4. 编码器的使用

学习本章时,配合《瑞萨RA系列FSP库开发实战指南》的 “GPT——通用PWM定时器” 章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。

在基础部分的编码器详解章节中,已经详细介绍了旋转编码器的结构、原理和参数,这一章节我们将介绍如何使用编码器对电机的速度和位置进行测量。

4.1. 增量式编码器倍频技术

首先来看一下增量式编码器的输出信号和它的信号倍频技术。增量式编码器输出的脉冲波形信号形式常见的有两种:

  1. 一种是占空比50%的方波,通道A和B相位差为90°;

  2. 另一种则是正弦波这类模拟信号,通道A和B相位差同样为90°。

对于第1种形式的方波信号,如果把两个通道组合起来看的话,可以发现A和B各自的上升沿和下降沿都能计数,至少在1/2个原始方波周期内就可以计数一次, 最多1/4个原始方波周期。这样计数频率就是原始方波信号的2倍或4倍,换句话说就是,将编码器的分辨率提高了2到4倍,具体如下图所示。

编码器倍频技术

图中的方波信号如果只看其中一个通道的上升沿,那计数频率就等于这个通道信号的频率。如果在通道A的上升沿和下降沿都进行计数,计数频率就是通道A的两倍,即2倍频。 如果同时对两个通道的上升沿和下降沿都计数,那计数频率就变成了原始信号的4倍,即4倍频。

假设有个增量式编码器它的分辨率是600PPR,能分辨的最小角度是0.6°,对它进行4倍频之后就相当于把分辨率提高到了600*4=2400PPR,此时编码器能够分辨的最小角度为0.15°。 编码器倍频技术还可用来扩展一些测速方法的速度适用范围。例如电机测速通常使用M法进行测量(M法在下节介绍),编码器4倍频后可以扩展M法的速度下限。

以上就是方波信号的编码器倍频技术,其实输出模拟信号的增量式编码器同样也可以倍频,不过这种倍频原理与方波完全不同,教程当中就不讲解了。

4.2. 常用测速方法简介

上一节提到了增量式编码器倍频技术可以扩展M法的测量范围,那么现在我们就来讲解下这个M法究竟是怎样测速的,以及简单介绍一些常用的编码器测速方法。 对于电机转速的测量,可以把增量式编码器安装到电机上,用控制器对编码器脉冲进行计数,然后通过特定的方法求出电机转速,常用的编码器测速方法一般有三种:M法、T法和M/T法。

  • M法:又叫做频率测量法。这种方法是在一个固定的定时时间内(以秒为单位),统计这段时间的编码器脉冲数,计算速度值。设编码器单圈总脉冲数为C, 在时间T0内,统计到的编码器脉冲数为M0,则转速n的计算公式为:

M法测速公式

公式中的编码器单圈总脉冲数C是常数,所以转速n跟M0成正比。这就使得在高速测量时M0变大,可以获得较好的测量精度和平稳性, 但是如果速度很低,低到每个T0内只有少数几个脉冲,此时算出的速度误差就会比较大,并且很不稳定。也有一些方法可以改善M法在低速测量的准确性, 上一节提到的增量式编码器倍频技术就是其中一种,比如原本捕获到的脉冲M0只有4个,经过4倍频后,相同电机状态M0变成了16个, 也就提升了低速下的测量精度。

  • T法:又叫做周期测量法。这种方法是建立一个已知频率的高频脉冲并对其计数,计数时间由捕获到的编码器相邻两个脉冲的间隔时间TE决定, 计数值为M1。设编码器单圈总脉冲数为C,高频脉冲的频率为F0,则转速n的计算公式为:

T法测速公式

公式中的编码器单圈总脉冲数C和高频脉冲频率F0是常数,所以转速n跟M1成反比。从公式可以看出,在电机高转速的时候, 编码器脉冲间隔时间TE很小,使得测量周期内的高频脉冲计数值M1也变得很少,导致测量误差变大,而在低转速时,TE足够大, 测量周期内的M1也足够多,所以T法和M法刚好相反,更适合测量低速。

  • M/T法:这种方法综合了M法和T法各自的优势,既测量编码器脉冲数又测量一定时间内的高频脉冲数。在一个相对固定的时间内,计数编码器脉冲数M0, 并计数一个已知频率为F0的高频脉冲,计数值为M1,计算速度值。设编码器单圈总脉冲数为C,则转速n的计算公式为:

MT法测速公式

由于M/T法公式中的F0和C是常数,所以转速n就只受M0和M1的影响。电机高速时,M0增大,M1减小,相当于M法, 低速时,M1增大,M0减小,相当于T法。

4.3. 瑞萨RA的编码器接口简介

瑞萨RA拥有输入捕获功能,的接口,通过输入捕获功能, 我们可以测量高低电平脉冲的脉宽、信号的周期、频率和 PWM 占空比等。 在采集编码器信号上,可以使用这一功能来进行,关于定时器以及输入捕获的功能介绍和使用,请查看《瑞萨RA系列FSP库开发实战指南》 的 “GPT——通用PWM定时器” 章节中的 “28.5. GPT输入捕获功能详解” 一节。

我们重点关注在代码层面,编码器接口是如何实现信号采集和倍频的。 编码器信号与计数器方向和计数位置之间的关系,如下表所示。

计数方向与编码器信号的关系

这个表格将编码器接口所有可能出现的工作情况全都列了出来,包括它是如何实现方向检测和倍频的。虽然信息很全面但是乍看上去却不容易看懂。 首先需要解释一下,表中的TI1和TI2对应编码器的通道A和通道B,而TI1FP1和TI2FP2则对应反相以后的TI1、TI2。在对编码器接口进行计数的时候, 并不是单纯采集某一通道信号的上升沿或下降沿,而是需要综合另一个通道信号的电平。表中“相反信号的电平”指的就是在计数的时候所参考的另一个通道信号的电平, 这些电平决定了计数器的计数方向,也代表了电机的工作方向。

为了便于大家理解编码器接口的计数原理,我们将表中的信息提出转换成一系列图像。首先看下图,下图所展示的信息对应表格中“仅在TI1处计数”。 图中包含TI1、TI2两通道的信号,以及计数器的计数方向,其中TI1比TI2 提前 1/4个周期,以TI1的信号边沿作为有效边沿。 当检测到TI1的上升沿时,TI2为低电平,此时计数器向上计数1次,下一时刻检测到TI1的下降沿时,TI2为高电平,此时计数器仍然向上计数一次,以此类推。 这样就能把TI1的上升沿和下降沿都用来计数,即实现了对原始信号的2倍频。

编码器接口2倍频图解——向上计数

接下来看如下图像,图中同样包含TI1、TI2两通道的信号,以及计数器的计数方向,其中TI1比TI2 滞后 1/4个周期,以TI1的信号边沿作为有效边沿。 当检测到TI1的上升沿时,TI2为高电平,此时计数器向下计数1次,下一时刻检测到TI1的下降沿时,TI2为低电平,此时计数器仍然向下计数一次,以此类推。 这样同样是把TI1的上升沿和下降沿都用来计数,同样实现了对原始信号的2倍频,只不过变成向下计数了。

编码器接口2倍频图解——向下计数

以上两幅图像都是只以TI1的信号边沿作为有效边沿,并且根据TI2的电平决定各自的计数方向,然后判断计数方向就能得到编码器的旋转方向,向上计数正向,向下计数反向。 “仅在TI2处计数”也是同样的原理,在这里就不重复讲了。

最后如下图所示,下图所展示的信息对应表格中“在TI1和TI2处均计数”。这种采样方式可以把两个通道的上升沿和下降沿都用来计数,计数方向也是两个通道同时参考, 相当于原来仅在一个通道处计数的2倍,所以这种就能实现对原始信号的4倍频。

编码器接口4倍频图解

4.4. 减速电机编码器测速实验

在本实验中主要使用M法对直流减速电机进行测速。

4.4.1. 硬件设计

本实验用到的减速电机与减速电机串口控制例程的相同,所以电机、开发板和驱动板的硬件连接也完全相同,只加上了编码器的连线。

编码器接口部分原理图

4.4.2. 软件设计

4.4.2.1. 编程思路

  1. 配置定时器可以输出PWM控制电机

  2. 配置定时器可以读取编码器的计数值

  3. 配置基本定时器可以产生定时中断来计算速度

4.4.2.2. 新建工程

我们直接在基础部分的直流有刷章节的 “200_Motor_BDC_PWM_Control” 例程的基础上修改程序。

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “200_Motor_BDC_PWM_Control”, 然后将工程文件夹重命名为 “203_Motor_BDC_Encoder_SpeedMeasurement”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “200_Motor_BDC_PWM_Control”, 然后将工程文件夹重命名为 “203_Motor_BDC_Encoder_SpeedMeasurement”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “encoder” 文件夹, 再进入 “encoder” 文件夹里面新建源文件和头文件:“bsp_encoder.c” 和 “bsp_encoder.h”。 另外还需要在 “gpt” 文件夹下面新建源文件和头文件 “bsp_gpt_timing.c” 和 “bsp_gpt_timing.h” 。 工程文件结构如下。

文件结构
203_Motor_BDC_Encoder_SpeedMeasurement
├─ ......
└─ src
   ├─ beep
   │  ├─ bsp_beep.c
   │  └─ bsp_beep.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ encoder
   │  ├─ bsp_encoder.c    //新建文件
   │  └─ bsp_encoder.h    //新建文件
   ├─ gpt
   │  ├─ bsp_gpt_pwm_output.c
   │  └─ bsp_gpt_pwm_output.h
   │  ├─ bsp_gpt_timing.c           //新建文件
   │  └─ bsp_gpt_timing.h           //新建文件
   ├─ key
   │  ├─ bsp_key_irq.c
   │  └─ bsp_key_irq.h
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ motor_controls
   │  ├─ bsp_motor_control.c
   │  └─ bsp_motor_control.h
   └─ hal_entry.c

4.4.2.3. FSP配置

下面以野火启明6T2开发板为例来讲解相关的 FSP 配置。

根据原理图所示,需要配置 PB06 和 PB07 引脚作为输入捕获引脚, 在“Stacks”配置页面中加入 GPT,按如下图所示的配置进行设置。

图

需要注意的是,因为“Input”里面的各类配置项目若是展开来的话篇幅比较长,因此在图中并未展开出来, 要注意勾上以下几个选项:

  • 展开 Input->Count Up Source 并勾上(在TI1和TI2处均上升计数,两个通道同时参考,相当于4倍频采集):
    • GTIOCA Rising Edge while GTIOCB Low

    • GTIOCA Rising Edge While GTIOCB High

    • GTIOCA Falling Edge While GTIOCB Low

    • GTIOCA Falling Edge while GTIOCB High

    • GTIOCB Rising Edge while GTIOCA Low

    • GTIOCB Rising Edge while GTIOCA High

    • GTIOCB Falling Edge While GTIOCA Low

    • GTIOCB Falling Edge while GTIOCA High

  • 展开 Input->Capture A Source 并勾上(正转情况下进行中断,目的是实时更新方向):
    • GTIOCA Rising Edge While GTIOCB Low

    • GTIOCA Falling Edge While GTIOCB High

    • GTIOCB Rising Edge While GTIOCA High

    • GTIOCB Falling Edge While GTIOCA Low

  • 展开 Input->Capture B Source 并勾上(反转情况下进行中断,目的是实时更新方向):
    • GTIOCA Rising Edge While GTIOCB High

    • GTIOCA Falling Edge While GTIOCB Low

    • GTIOCB Rising Edge While GTIOCA Low

    • GTIOCB Falling Edge While GTIOCA High

GPT的“Input”选项卡的属性描述

GPT属性描述:“Input”

属性

描述

Count Up Source

选择计数器向上计数的外部源。

如果某个外部向上计数源被选中,该定时器将在该源的驱动下进行计数, 而不是计数 PCLKD 周期。

Count Down Source

选择计数器向下计数的外部源。

如果某个外部向下计数源被选中,该定时器将在该源的驱动下进行计数, 而不是计数 PCLKD 周期。

Start Source

选择外部源来启动定时器。

Stop Source

选择外部源来停止定时器。

Clear Source

选择外部源来清除定时器。

Capture A Source

选择外部源来触发一个捕获 A 事件。

Capture B Source

选择外部源来触发一个捕获 B 事件。

Noise Filter A Sampling Clock Select

GTIOCA 输入噪声滤波器的采样时钟选择。

Noise Filter B Sampling Clock Select

GTIOCB 输入噪声滤波器的采样时钟选择。

另外还需要额外创建一个 GPT 定时模块 g_timer_gpt0 ,这部分比较简单,主要配置一个周期中断来 计算周期内的电机速度,在本例程中配置成200ms,并开启中断。

图

4.4.2.4. GPT初始化

初始化 GPT 模块用于输入捕获的函数如下。 在这个函数中,我们先初始化 GPT 模块,然后调用 R_GPT_InfoGet 函数获取 GPT 的配置信息, 主要是为了获取计数器的计数周期,即 GPT 一个周期的计数次数,并保存到全局变量 period 中。

最后使能输入捕获,并启动 GPT 定时器进行下一步采集。

bsp_encoder.c-GPT初始化
timer_info_t info;                    // 定时器信息,包含时钟频率等
uint32_t period;    //输入捕获计数器的计数周期

/* 初始化编码器 */
void initEncoder(void)
{
   fsp_err_t err = FSP_SUCCESS;

   /* 打开编码器 */
   err = R_GPT_Open(&encoder_ctrl, &encoder_cfg);
   assert(FSP_SUCCESS == err);

   /*获取当前参数*/
   (void) R_GPT_InfoGet(&encoder_ctrl, &info);
   period = info.period_counts;

   /* 使能输入捕获 */
   R_GPT_Enable(&encoder_ctrl);

   /* 启动 GPT 定时器 */
   R_GPT_Start(&encoder_ctrl);
}

4.4.2.5. GPT中断回调函数

GPT 输入捕获中断回调函数如下, 此代码定义了一个编码器中断回调函数 encoder_callback,用于处理编码器定时器在不同事件下的响应逻辑。

  • TIMER_EVENT_CAPTURE_A 分支表示正转方向的捕获事件,当捕获 A 事件发生时,将方向标志 flag 设置为 true,表明当前是正转方向,并通过当前捕获值 p_args->capture 加上溢出计数(overflow_count)乘以定时器周期(period)来更新脉冲周期 pulse_period

  • TIMER_EVENT_CAPTURE_B 分支表示反转方向的捕获事件,当捕获 B 事件发生时,将方向标志 flag 设置为 false,表明当前是反转方向,并以同样的方式更新 pulse_period

  • TIMER_EVENT_CYCLE_END 分支处理定时器溢出事件,每当定时器周期结束时,增加溢出计数器 overflow_count,以确保在周期超过计数范围时,捕获值仍能正确计算。

bsp_encoder.c-GPT中断回调函数
/* 编码器中断回调函数 */
void encoder_callback(timer_callback_args_t *p_args)
{
   switch (p_args->event)
   {
      /* 捕获事件:正转计数(捕获A事件) */
      case TIMER_EVENT_CAPTURE_A:
            flag = true; // 设置为正转方向
            //更新计数值
            pulse_period = p_args->capture + (overflow_count * period);
            break;

      /* 捕获事件:反转计数(捕获B事件) */
      case TIMER_EVENT_CAPTURE_B:
            flag = false; // 设置为反转方向
            //更新计数值
            pulse_period = p_args->capture + (overflow_count * period);
            break;

      /* 定时器溢出事件 */
      case TIMER_EVENT_CYCLE_END:
            overflow_count++; // 增加溢出计数
            break;

      default:
            break;
   }
}

4.4.2.6. 宏定义代码

该代码定义了一些与编码器相关的参数和计算宏,用于解析编码器脉冲信号并计算速度。

特别注意:
  • ENCODER_TOTAL_RESOLUTION 的计算依据编码器的倍频模式决定,如果倍频设置有误,速度计算可能不准确。

  • REDUCTION_RATIO 用于将转轴速度转换为输出轴速度,需与实际减速比一致,否则会导致输出值偏差。

bsp_encoder.h-宏定义代码
/* 定时器最大计数值 */
#define TIMER_MAX_COUNT 65536U

/* 编码器接口倍频数 */
#define ENCODER_MODE                         TIM_ENCODERMODE_TI12

/* 编码器物理分辨率 */
#define ENCODER_RESOLUTION                  16.00f

/* 经过倍频之后的总分辨率 */
#if (ENCODER_MODE == TIM_ENCODERMODE_TI12)
#define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 4)  /* 4倍频后的总分辨率 */
#else
#define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 2)  /* 2倍频后的总分辨率 */
#endif

/* 减速电机减速比 */
#define REDUCTION_RATIO                     30.00f

// 计算转轴速度的宏定义
#define SHAFT_SPEED(param)   ((float)(param) / ENCODER_TOTAL_RESOLUTION)

// 计算输出轴速度的宏定义
#define OUTPUT_SPEED(param)  ((float)(SHAFT_SPEED(param)) / REDUCTION_RATIO)

4.4.2.7. GPT计数器中断回调函数

该代码是 GPT 定时器的中断回调函数,用于处理编码器的脉冲周期变化,计算转轴和输出轴的速度, 并通过打印函数输出方向、有效计数值以及速度信息。 通过 last_pulse_periodpulse_period 的差值计算脉冲周期变化, 进而使用宏定义 SHAFT_SPEEDOUTPUT_SPEED 完成速度计算。 需要注意的是,flag 用于标识电机方向,速度和方向的计算均依赖于正确的脉冲周期数据。

bsp_encoder.c-GPT计数器中断回调函数
/* GPT定时器中断回调函数 */
void gpt0_timing_callback(timer_callback_args_t *p_args)
{
   static uint32_t last_pulse_period = 0; // 上一次的脉冲周期
   static uint32_t new_period = 0; // 上一次的脉冲周期
   static float shaft_speed = 0.00f;
   static float output_speed = 0.00f;

   if (TIMER_EVENT_CYCLE_END == p_args->event)
   {
      if (pulse_period > 0 && pulse_period != last_pulse_period)
      {

            // 更新最近速度
            new_period = (pulse_period - last_pulse_period);

            // 转轴速度计算(单位:转/秒)
            shaft_speed = SHAFT_SPEED(new_period) * 5.0f;
            // 输出轴速度计算(单位:转/秒)
            output_speed = OUTPUT_SPEED(new_period) * 5.0f;

            // 打印方向和速度
            if (flag)
            {

                  MOTOR_PRINT("方向:正转\n");
            }
            else
            {
                  MOTOR_PRINT("方向:反转\n");
            }

               MOTOR_PRINT("单位时间内有效计数值:%ld \n", new_period);
               MOTOR_PRINT("转轴处速度:%.2f 转/秒\n", shaft_speed);
               MOTOR_PRINT("输出轴速度:%.2f 转/秒\n", output_speed);
            }

         last_pulse_period = pulse_period; // 更新目前脉冲周期
      }
}

4.4.3. 下载验证

连接好电机、电机驱动板和开发板,编译并下载程序后,复位开发板使程序重新运行,启动电机,并调节占空比,观察电机的速度是否正确。

图

4.5. 步进电机编码器测速实验

在本实验中主要使用M法对直流减速电机进行测速。

4.5.1. 硬件设计

本实验用到的步进电机与步进电机串口控制例程的相同,所以电机、开发板和驱动板的硬件连接也完全相同,只加上了编码器的连线。 在本工程中使用启明6T2开发板的编码器接口进行测速,原理图如下。

编码器接口部分原理图

4.5.2. 软件设计

4.5.2.1. 编程思路

  1. 配置定时器可以输出PWM控制电机

  2. 配置定时器可以读取编码器的计数值

  3. 配置基本定时器可以产生定时中断来计算速度

4.5.2.2. 新建工程

我们直接在基础部分的直流有刷章节的 “300_Motor_Stepper_PWM_Control” 例程的基础上修改程序。

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “300_Motor_Stepper_PWM_Control”, 然后将工程文件夹重命名为 “301_Motor_Stepper_Encoder_SpeedMeasurement”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “300_Motor_Stepper_PWM_Control”, 然后将工程文件夹重命名为 “301_Motor_Stepper_Encoder_SpeedMeasurement”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “encoder” 文件夹, 再进入 “encoder” 文件夹里面新建源文件和头文件:“bsp_encoder.c” 和 “bsp_encoder.h”。 另外还需要新建 “gpt” 文件夹,并在该文件夹下面新建源文件和头文件 “bsp_gpt_timing.c” 和 “bsp_gpt_timing.h” 。 工程文件结构如下。

文件结构
203_Motor_BDC_Encoder_SpeedMeasurement
├─ ......
└─ src
   ├─ beep
   │  ├─ bsp_beep.c
   │  └─ bsp_beep.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ encoder
   │  ├─ bsp_encoder.c              //新建文件
   │  └─ bsp_encoder.h              //新建文件
   ├─ gpt
   │  ├─ bsp_gpt_timing.c           //新建文件
   │  └─ bsp_gpt_timing.h           //新建文件
   ├─ key
   │  ├─ bsp_key_irq.c
   │  └─ bsp_key_irq.h
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ motor_controls
   │  ├─ bsp_motor_control.c
   │  └─ bsp_motor_control.h
   └─ hal_entry.c

4.5.2.3. FSP配置

下面以野火启明6T2开发板为例来讲解相关的 FSP 配置。

根据原理图所示,需要配置 PE08 和 PE09 引脚作为输入捕获引脚, 在“Stacks”配置页面中加入 GPT,按如下图所示的配置进行设置。

图

需要注意的是,因为“Input”里面的各类配置项目若是展开来的话篇幅比较长,因此在图中并未展开出来, 要注意勾上以下几个选项:

  • 展开 Input->Count Up Source 并勾上(在TI1和TI2处均上升计数,两个通道同时参考,相当于4倍频采集):
    • GTIOCA Rising Edge while GTIOCB Low

    • GTIOCA Rising Edge While GTIOCB High

    • GTIOCA Falling Edge While GTIOCB Low

    • GTIOCA Falling Edge while GTIOCB High

    • GTIOCB Rising Edge while GTIOCA Low

    • GTIOCB Rising Edge while GTIOCA High

    • GTIOCB Falling Edge While GTIOCA Low

    • GTIOCB Falling Edge while GTIOCA High

  • 展开 Input->Capture A Source 并勾上(正转情况下进行中断,目的是实时更新方向):
    • GTIOCA Rising Edge While GTIOCB Low

    • GTIOCA Falling Edge While GTIOCB High

    • GTIOCB Rising Edge While GTIOCA High

    • GTIOCB Falling Edge While GTIOCA Low

  • 展开 Input->Capture B Source 并勾上(反转情况下进行中断,目的是实时更新方向):
    • GTIOCA Rising Edge While GTIOCB High

    • GTIOCA Falling Edge While GTIOCB Low

    • GTIOCB Rising Edge While GTIOCA Low

    • GTIOCB Falling Edge While GTIOCA High

GPT的“Input”选项卡的属性描述

GPT属性描述:“Input”

属性

描述

Count Up Source

选择计数器向上计数的外部源。

如果某个外部向上计数源被选中,该定时器将在该源的驱动下进行计数, 而不是计数 PCLKD 周期。

Count Down Source

选择计数器向下计数的外部源。

如果某个外部向下计数源被选中,该定时器将在该源的驱动下进行计数, 而不是计数 PCLKD 周期。

Start Source

选择外部源来启动定时器。

Stop Source

选择外部源来停止定时器。

Clear Source

选择外部源来清除定时器。

Capture A Source

选择外部源来触发一个捕获 A 事件。

Capture B Source

选择外部源来触发一个捕获 B 事件。

Noise Filter A Sampling Clock Select

GTIOCA 输入噪声滤波器的采样时钟选择。

Noise Filter B Sampling Clock Select

GTIOCB 输入噪声滤波器的采样时钟选择。

另外还需要额外创建一个 GPT 定时模块 g_timer_gpt0 ,这部分比较简单,主要配置一个周期中断来 计算周期内的电机速度,在本例程中配置成200ms,并开启中断。

图

4.5.2.4. GPT初始化

初始化 GPT 模块用于输入捕获的函数如下。 在这个函数中,我们先初始化 GPT 模块,然后调用 R_GPT_InfoGet 函数获取 GPT 的配置信息, 主要是为了获取计数器的计数周期,即 GPT 一个周期的计数次数,并保存到全局变量 period 中。

最后使能输入捕获,并启动 GPT 定时器进行下一步采集。

bsp_encoder.c-GPT初始化
timer_info_t info;                    // 定时器信息,包含时钟频率等
uint32_t period;                      //输入捕获计数器的计数周期

/* 初始化编码器 */
void initEncoder(void)
{
   fsp_err_t err = FSP_SUCCESS;

   /* 打开编码器 */
   err = R_GPT_Open(&encoder_ctrl, &encoder_cfg);
   assert(FSP_SUCCESS == err);

   /*获取当前参数*/
   (void) R_GPT_InfoGet(&encoder_ctrl, &info);
   period = info.period_counts;

   /* 使能输入捕获 */
   R_GPT_Enable(&encoder_ctrl);

   /* 启动 GPT 定时器 */
   R_GPT_Start(&encoder_ctrl);
}

4.5.2.5. GPT中断回调函数

GPT 输入捕获中断回调函数如下, 此代码定义了一个编码器中断回调函数 encoder_callback,用于处理编码器定时器在不同事件下的响应逻辑。

  • TIMER_EVENT_CAPTURE_A 分支表示正转方向的捕获事件,当捕获 A 事件发生时,将方向标志 flag 设置为 true,表明当前是正转方向,并通过当前捕获值 p_args->capture 加上溢出计数(overflow_count)乘以定时器周期(period)来更新脉冲周期 pulse_period

  • TIMER_EVENT_CAPTURE_B 分支表示反转方向的捕获事件,当捕获 B 事件发生时,将方向标志 flag 设置为 false,表明当前是反转方向,并以同样的方式更新 pulse_period

  • TIMER_EVENT_CYCLE_END 分支处理定时器溢出事件,每当定时器周期结束时,增加溢出计数器 overflow_count,以确保在周期超过计数范围时,捕获值仍能正确计算。

bsp_encoder.c-GPT中断回调函数
/* 编码器中断回调函数 */
void encoder_callback(timer_callback_args_t *p_args)
{
   switch (p_args->event)
   {
      /* 捕获事件:正转计数(捕获A事件) */
      case TIMER_EVENT_CAPTURE_A:
            flag = true; // 设置为正转方向
            //更新计数值
            pulse_period = p_args->capture + (overflow_count * period);
            break;

      /* 捕获事件:反转计数(捕获B事件) */
      case TIMER_EVENT_CAPTURE_B:
            flag = false; // 设置为反转方向
            //更新计数值
            pulse_period = p_args->capture + (overflow_count * period);
            break;

      /* 定时器溢出事件 */
      case TIMER_EVENT_CYCLE_END:
            overflow_count++; // 增加溢出计数
            break;

      default:
            break;
   }
}

4.5.2.6. 宏定义代码

该代码定义了一些与编码器相关的参数和计算宏,用于解析编码器脉冲信号并计算速度。 首先定义了编码器的工作模式和物理分辨率。ENCODER_MODE 被设为 TIM_ENCODERMODE_TI12,表示使用编码器的 4 倍频模式。 然后,定义了编码器的物理分辨率 ENCODER_RESOLUTION,其值为 1000.0f,表示每圈编码器的脉冲数。 根据编码器的工作模式,计算出经过倍频后的总分辨率:如果使用 4 倍频模式,总分辨率为物理分辨率的 4 倍; 如果使用 2 倍频模式,总分辨率为物理分辨率的 2 倍。最后,定义了一个宏 OUTPUT_SPEED,用于根据传入的参数计算编码器的速度,单位为圈每秒。 该宏通过将输入的参数除以总分辨率再乘时间系数,得到每秒的旋转速度。

bsp_encoder.h-宏定义代码
/* 编码器接口倍频数 */
#define ENCODER_MODE                         TIM_ENCODERMODE_TI12

/* 编码器物理分辨率 */
#define ENCODER_RESOLUTION                     1000.0f

/* 经过倍频之后的总分辨率 */
#if (ENCODER_MODE == TIM_ENCODERMODE_TI12)
#define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 4)  /* 4倍频后的总分辨率 */
#else
#define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 2)  /* 2倍频后的总分辨率 */
#endif

// 计算速度的宏定义
#define OUTPUT_SPEED(param)  ((float)param / ENCODER_TOTAL_RESOLUTION)

4.5.2.7. GPT计数器中断回调函数

该代码是 GPT 定时器的中断回调函数,用于处理编码器的脉冲周期变化,计算转轴和输出轴的速度, 并通过打印函数输出方向、有效计数值以及速度信息。 通过 last_pulse_periodpulse_period 的差值计算脉冲周期变化, 进而使用宏定义 SHAFT_SPEEDOUTPUT_SPEED 完成速度计算。 需要注意的是, flag 用于标识电机方向,速度和方向的计算均依赖于正确的脉冲周期数据。

bsp_encoder.c-GPT计数器中断回调函数
/* GPT定时器中断回调函数 */
void gpt0_timing_callback(timer_callback_args_t *p_args)
{
   static uint32_t last_pulse_period = 0; // 上一次的脉冲周期
   static uint32_t new_period = 0; // 上一次的脉冲周期
   static float output_speed = 0.00f;

   if (TIMER_EVENT_CYCLE_END == p_args->event)
   {
      if (pulse_period > 0 && pulse_period != last_pulse_period)
      {
            // 更新最近速度
            new_period = (pulse_period - last_pulse_period);

            // 速度计算(单位:转/秒)
            output_speed = OUTPUT_SPEED(new_period) * 5.0f;

            // 打印方向和速度
            if (!flag)
            {

                  MOTOR_PRINT("方向:正转\n");
            }
            else
            {
                  MOTOR_PRINT("方向:反转\n");
            }

               MOTOR_PRINT("单位时间内有效计数值:%ld \n", new_period);
               MOTOR_PRINT("电机转速:%.2f 转/秒\n", output_speed);
            }

         last_pulse_period = pulse_period; // 更新目前脉冲周期
      }
}

4.5.3. 下载验证

连接好电机、电机驱动板和开发板,编译并下载程序后,复位开发板使程序重新运行,启动电机,并调节占空比,观察电机的速度是否正确。

图