编码器与 STM32:正交解码测速实战指南
👁 5 阅读 · 2026-08-14 · STM32 编码器
增量式编码器通过正交信号精确反馈电机位置与速度,而 STM32 的定时器正交解码器(Encoder Interface)可直接对 AB 相脉冲进行硬件计数,极大降低 CPU 负载。本文从正交信号原理出发,讲解 STM32 定时器编码器模式的配置流程,并给出完整的 CubeMX 初始化代码与测速换算方法,帮助开发者快速实现高精度测速。
## 一、正交信号与测速原理
增量式编码器输出两路相位差 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 的硬件编码器接口将繁琐的正交解码工作交给定时器外设,开发者只需读取计数值并做简单换算,即可获得精确的位置与速度信息。通过合理配置滤波、溢出处理和采样周期,能够稳定应对各类电机测速场景。希望本文的配置思路与示例代码能帮助你快速上手。