## 一、正交信号与测速原理 增量式编码器输出两路相位差 90° 的方波信号:A 相(Channel A)和 B 相(Channel B)。旋转方向不同,A 与 B 的相位关系也不同:正转时 A 领先 B 90°,反转时 B 领先 A 90°。STM32 定时器编码器接口通过检测两个信号的边沿组合,可以在每个完整周期内产生 **2 倍或 4 倍频** 的计数脉冲,从而获得更高的分辨率。 在 4 倍频模式下,定时器的计数方向由 A、B 相电平状态决定,硬件自动完成鉴相与计数,无需软件干预。要实现速度测量,核心思路是: - 配置定时器为编码器模式(Slave Mode: Encoder Mode 1/2/3) - 开启定时器更新中断或使用定时器溢出计数 - 周期读取计数寄存器(TIMx->CNT),并换算为转速 ## 二、STM32 定时器编码器模式原理 STM32 的通用定时器(TIM2~TIM5)和高级定时器(TIM1、TIM8)都支持编码器接口。其本质是将定时器的两个输入通道 TI1 和 TI2 作为编码器输入,内部通过一个异或门和边沿检测逻辑,将 A/B 相信号转换为计数脉冲和方向信号。 关键寄存器与配置项: - **TIMx_CCMR1**:设置通道输入捕获映射,选择 IC1F 滤波和分频 - **TIMx_SMCR**:设置 Slave Mode 为 Encoder Mode(101、110、111 分别对应模式 1、2、3) - **TIMx_CR1**:设置计数器方向(由硬件自动控制)和使能计数 - **TIMx_ARR**:设定自动重装值,用于决定计数范围(通常设为 65535 或自定义值) - **TIMx_CNT**:当前计数值,代表位置(或速度差) 模式选择: - **Encoder Mode 1**(SMCR=101):只在 TI1 边沿计数,TI2 决定方向 - **Encoder Mode 2**(SMCR=110):只在 TI2 边沿计数,TI1 决定方向 - **Encoder Mode 3**(SMCR=111):在 TI1 和 TI2 的双边沿计数(4倍频),最常用 ## 三、CubeMX 配置步骤 本文以 STM32F103 系列为例,使用 TIM4 的通道1(PA6)和通道2(PA7)。 1. **设置时钟**:RCC 开启 HSE,主频配置为 72MHz。 2. **配置定时器 TIM4**: - Combined Channels: `Encoder Mode` - Prescaler: `0`(编码器输入不需要分频) - Counter Period: `65535` - Input Filter: `0`(可根据实际情况设为 10~20 用于滤波) - Encoder Mode: `Encoder Mode TI1 and TI2`(即 4 倍频) 3. **使能中断**:NVIC Setting 中勾选 TIM4 global interrupt(若需要计算溢出次数,必须开启)。 4. **生成代码**:在 main.c 中初始化 TIM4,并启动编码器模式。 ## 四、完整代码示例 ```c /* main.c 中相关代码 */ #include "tim.h" volatile int16_t encoder_raw = 0; // 当前计数值(有符号) volatile int32_t encoder_total = 0; // 累计位置数 void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef *htim) { // GPIO 初始化已在 CubeMX 生成,此处无需额外处理 } void Start_Encoder(void) { HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL); __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE); // 使能更新中断 } void TIM4_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE); // 根据方向调整累计值。计数器递增溢出时,CNT 回到 0;递减下溢时,CNT 回到 ARR if (__HAL_TIM_GET_COUNTER(&htim4) > 32768) { encoder_total -= (htim4.Init.Period + 1); } else { encoder_total += (htim4.Init.Period + 1); } } } float Get_Speed_By_Encoder(float dt) { // dt 为两次采样的时间间隔(秒) int16_t current_cnt = (int16_t)__HAL_TIM_GET_COUNTER(&htim4); int32_t delta = current_cnt - encoder_raw; encoder_raw = current_cnt; // 需要考虑定时器重装造成的跳变,但增量编码器在短间隔内不会超过 ARR/2 if (delta > 32767) delta -= 65536; else if (delta < -32768) delta += 65536; encoder_total += delta; // 换算线速度或 RPM,需知道编码器线数和减速比 // 假设编码器每转 N 线,4倍频后有 4*N 个计数。则转速(rpm) = delta / (4*N) / dt * 60 float rpm = (float)delta / (4.0f * 1000) / dt * 60.0f; // 示例:1000线编码器 return rpm; } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM4_Init(); Start_Encoder(); while (1) { // 每隔固定时间(如10ms)调用一次 float rpm = Get_Speed_By_Encoder(0.01f); // 转速可输出到串口或用于 PID 控制 HAL_Delay(10); } } ``` ## 五、测速换算与注意事项 - **计数范围**:TIM4 是 16 位计数器,ARR 设为 65535,CNT 在 0~65535 间循环。读取时要强制转换为 int16_t 才能正确解析有方向的值。 - **溢出处理**:若采样周期较长,必须开启更新中断,并记录溢出次数。上述代码中通过判断 CNT 值在中断中的大小近似判断方向,但更稳妥的方法是利用 `TIM_CR1` 的 DIR 位或将计数器配置为自动重装到 0xFFFF 再配合方向寄存器。 - **滤波**:在电机运行中会有机械抖动或电磁干扰,建议在 CubeMX 中设置 Input Filter(建议 20~50),可有效避免误计数。 - **4倍频与分辨率**:4倍频计数获得最高分辨率,但计数频率也更高。若电机转速很快,需确认定时器时钟与计数器位数是否够用。对于 16 位定时器,若计数脉冲频率超过 65536 CPU 时钟(72MHz),会溢出丢失。此时可考虑降低分频或改用双倍频模式。 - **接线注意**:A、B 相必须接在定时器对应通道上,且要共地。OC 输出通常需要上拉电阻,推荐使用 10kΩ 上拉到 3.3V。 - **代码兼容性**:若使用 HAL 库,需要确保 `HAL_TIM_Encoder_Start` 在 `MX_TIM4_Init` 之后调用,并注意 `TIM_CHANNEL_ALL` 的写法对不同型号芯片可能存在区别。 ## 六、扩展:M 法/T 法测速对比 - **M 法(测频法)**:固定采样时间,计数脉冲个数。适用于中高速,测速精度高。 - **T 法(测周法)**:测量相邻脉冲间隔时间,适用于低速。 - **STM32 编码器模式天然支持 M 法**,但也可以结合定时器输入捕获实现 T 法测速。在实际项目中,可设定长短两个采样周期,低速用 T 法,高速用 M 法,实现宽范围高精度测速。 ## 七、总结 STM32 的硬件编码器接口将繁琐的正交解码工作交给定时器外设,开发者只需读取计数值并做简单换算,即可获得精确的位置与速度信息。通过合理配置滤波、溢出处理和采样周期,能够稳定应对各类电机测速场景。希望本文的配置思路与示例代码能帮助你快速上手。