4. 编码器的使用¶
学习本章时,配合《瑞萨RA系列FSP库开发实战指南》的 “GPT——通用PWM定时器” 章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。
在基础部分的编码器详解章节中,已经详细介绍了旋转编码器的结构、原理和参数,这一章节我们将介绍如何使用编码器对电机的速度和位置进行测量。
4.1. 增量式编码器倍频技术¶
首先来看一下增量式编码器的输出信号和它的信号倍频技术。增量式编码器输出的脉冲波形信号形式常见的有两种:
一种是占空比50%的方波,通道A和B相位差为90°;
另一种则是正弦波这类模拟信号,通道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的计算公式为:
公式中的编码器单圈总脉冲数C是常数,所以转速n跟M0成正比。这就使得在高速测量时M0变大,可以获得较好的测量精度和平稳性, 但是如果速度很低,低到每个T0内只有少数几个脉冲,此时算出的速度误差就会比较大,并且很不稳定。也有一些方法可以改善M法在低速测量的准确性, 上一节提到的增量式编码器倍频技术就是其中一种,比如原本捕获到的脉冲M0只有4个,经过4倍频后,相同电机状态M0变成了16个, 也就提升了低速下的测量精度。
T法:又叫做周期测量法。这种方法是建立一个已知频率的高频脉冲并对其计数,计数时间由捕获到的编码器相邻两个脉冲的间隔时间TE决定, 计数值为M1。设编码器单圈总脉冲数为C,高频脉冲的频率为F0,则转速n的计算公式为:
公式中的编码器单圈总脉冲数C和高频脉冲频率F0是常数,所以转速n跟M1成反比。从公式可以看出,在电机高转速的时候, 编码器脉冲间隔时间TE很小,使得测量周期内的高频脉冲计数值M1也变得很少,导致测量误差变大,而在低转速时,TE足够大, 测量周期内的M1也足够多,所以T法和M法刚好相反,更适合测量低速。
M/T法:这种方法综合了M法和T法各自的优势,既测量编码器脉冲数又测量一定时间内的高频脉冲数。在一个相对固定的时间内,计数编码器脉冲数M0, 并计数一个已知频率为F0的高频脉冲,计数值为M1,计算速度值。设编码器单圈总脉冲数为C,则转速n的计算公式为:
由于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倍频。
接下来看如下图像,图中同样包含TI1、TI2两通道的信号,以及计数器的计数方向,其中TI1比TI2 滞后 1/4个周期,以TI1的信号边沿作为有效边沿。 当检测到TI1的上升沿时,TI2为高电平,此时计数器向下计数1次,下一时刻检测到TI1的下降沿时,TI2为低电平,此时计数器仍然向下计数一次,以此类推。 这样同样是把TI1的上升沿和下降沿都用来计数,同样实现了对原始信号的2倍频,只不过变成向下计数了。
以上两幅图像都是只以TI1的信号边沿作为有效边沿,并且根据TI2的电平决定各自的计数方向,然后判断计数方向就能得到编码器的旋转方向,向上计数正向,向下计数反向。 “仅在TI2处计数”也是同样的原理,在这里就不重复讲了。
最后如下图所示,下图所展示的信息对应表格中“在TI1和TI2处均计数”。这种采样方式可以把两个通道的上升沿和下降沿都用来计数,计数方向也是两个通道同时参考, 相当于原来仅在一个通道处计数的2倍,所以这种就能实现对原始信号的4倍频。
4.4. 减速电机编码器测速实验¶
在本实验中主要使用M法对直流减速电机进行测速。
4.4.1. 硬件设计¶
本实验用到的减速电机与减速电机串口控制例程的相同,所以电机、开发板和驱动板的硬件连接也完全相同,只加上了编码器的连线。
4.4.2. 软件设计¶
4.4.2.1. 编程思路¶
配置定时器可以输出PWM控制电机
配置定时器可以读取编码器的计数值
配置基本定时器可以产生定时中断来计算速度
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”选项卡的属性描述
属性 |
描述 |
---|---|
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 定时器进行下一步采集。
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,以确保在周期超过计数范围时,捕获值仍能正确计算。
/* 编码器中断回调函数 */
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 用于将转轴速度转换为输出轴速度,需与实际减速比一致,否则会导致输出值偏差。
/* 定时器最大计数值 */
#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_period 和 pulse_period 的差值计算脉冲周期变化, 进而使用宏定义 SHAFT_SPEED 和 OUTPUT_SPEED 完成速度计算。 需要注意的是,flag 用于标识电机方向,速度和方向的计算均依赖于正确的脉冲周期数据。
/* 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. 编程思路¶
配置定时器可以输出PWM控制电机
配置定时器可以读取编码器的计数值
配置基本定时器可以产生定时中断来计算速度
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”选项卡的属性描述
属性 |
描述 |
---|---|
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 定时器进行下一步采集。
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,以确保在周期超过计数范围时,捕获值仍能正确计算。
/* 编码器中断回调函数 */
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,用于根据传入的参数计算编码器的速度,单位为圈每秒。 该宏通过将输入的参数除以总分辨率再乘时间系数,得到每秒的旋转速度。
/* 编码器接口倍频数 */
#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_period 和 pulse_period 的差值计算脉冲周期变化, 进而使用宏定义 SHAFT_SPEED 和 OUTPUT_SPEED 完成速度计算。 需要注意的是, flag 用于标识电机方向,速度和方向的计算均依赖于正确的脉冲周期数据。
/* 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. 下载验证¶
连接好电机、电机驱动板和开发板,编译并下载程序后,复位开发板使程序重新运行,启动电机,并调节占空比,观察电机的速度是否正确。