STM32 定时器编码器接口实战:正交解码测转速,电机闭环第一步

做电机控制,第一步往往是「知道转子转了没有、转多快、往哪转」。你在轴上贴个增量编码器,出来 A、B 两路相位差 90 度的方波。手动去读两个 IO 的电平变化、数边沿、判方向,中断能给你炸到怀疑人生,还容易漏脉冲。

STM32 的定时器自带编码器接口(Encoder Mode),硬件帮你数脉冲、判方向,CPU 只需要在想看的时候读一下计数器。这篇文章把正交解码原理、4 倍频、用定时器数脉冲算转速讲清楚,顺带把最常踩的坑列出来。

正交编码到底在算什么

增量编码器 A、B 两路方波,相位差 90 度。正转时 A 领先 B 90 度,反转时 B 领先 A 90 度。只看两路电平的跳变顺序,就能判断方向;数跳变次数,就能知道转了多少。

定时器编码器模式的逻辑是:把 A、B 两路接到同一个定时器的两个通道(TI1、TI2),在 A 的上升沿、下降沿、B 的上升沿、下降沿都计数。这样一个机械脉冲周期里数到 4 次边沿,叫4 倍频,分辨率直接翻四倍。

计数器是带符号的:正转递增,反转递减。溢出回绕也无所谓,读出来的值就是相对位移,减一下就知道这段时间转了多少。

CubeMX 与代码

CubeMX 里把对应定时器(TIM2~TIM5 多数支持)的 Combined Channels 选成 Encoder Mode。两个通道映射到 TI1、TI2,极性一般选 TI1FP1 和 TI2FP2(或都不反向,看编码器相位)。计数器周期设成足够大(比如 16 位就 65535,32 位定时器更大),避免短时间溢出。

c 复制代码
// 启动编码器接口
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);

// 主循环里读取
int32_t cnt = (int16_t)__HAL_TIM_GET_COUNTER(&htim2);  // 16位带符号

如果定时器是 32 位(如 TIM2、TIM5 部分型号),计数器直接当 int32_t 读,溢出周期更长,适合高速长时间累积。

怎么算转速

编码器的脉冲数对应机械角度。比如编码器每圈 1000 线,4 倍频后每圈 4000 个计数。测速就是「单位时间内的计数变化量 ÷ 每圈计数 ÷ 时间」:

c 复制代码
static int32_t last_cnt = 0;
static uint32_t last_tick = 0;

void measure_speed(void)
{
    int32_t cnt = (int32_t)__HAL_TIM_GET_COUNTER(&htim2);
    uint32_t now = HAL_GetTick();
    float dt = (now - last_tick) / 1000.0f;        // 秒
    int32_t delta = cnt - last_cnt;                 // 这段时间计数的变化
    float rpm = (delta / 4000.0f) / dt * 60.0f;     // 每圈4000计数

    last_cnt = cnt;
    last_tick = now;
    // rpm 就是当前转速
}

关键点:delta 要用带符号的减法。正转和反转的计数器是连续增减的,直接减就能得到净位移,不用分别数正负。用定时中断(比如每 10ms 调一次)周期性算,就得到平稳的转速反馈,这正是 FOC 电流环之外的速度环输入。

十个坑,测速乱基本都在这

  1. 两个通道接到同一个定时器。A、B 必须进同一个 TIM 的 CH1/CH2,用两个不同定时器各自数一路是数不出方向和 4 倍频的。

  2. 极性配反导致方向反。正转读出来是负、反转是正。这不是错,是编码器接线和极性设置的问题。把 TI1、TI2 极性都翻转,或者直接对读数取负,让正转对应正数即可。

  3. 计数器溢出没处理。16 位计数器从 65535 回 0 或 0 回 65535,直接减 delta 会算出巨大负值或正值。用带符号类型(int16_t 读再转 int32_t),回绕减法仍正确;或者定时清零、用增量累计。

  4. 每圈计数算错。编码器标 1000 线,你当 1000 计数算转速,漏了 4 倍频,转速算出来只有实际四分之一。标称线数 × 4 才是每圈计数,除非你特意关掉倍频。

  5. 测速周期和电机响应不匹配。10ms 测一次和 1 秒测一次,得到的转速噪声和延迟完全不同。慢速用长周期平滑,快速用短周期跟手,别一刀切。

  6. delta 用了无符号类型。cnt 是无符号读出来,相减结果被当成无符号,反转时溢出成大正数,转速符号全错。读出来先转成有符号再减。

  7. 编码器电源和地没共。编码器和 MCU 不共地,A、B 信号参考点漂移,电平识别错,计数乱跳。编码器 GND 必须和板子 GND 连起来。

  8. 长线传输没做差分或缓冲。编码器线拉几米,普通推挽信号受干扰,边沿抖动多计脉冲。距离远用 RS422 差分接收,或者至少加滤波和施密特触发。

  9. Z 相(零位)没利用做绝对归零。增量编码器只能数相对位移,上电不知道绝对角度。接 Z 相到定时器外部触发或 IO,转一圈对齐一次零位,解决累计漂移。

  10. 高速时计数溢出丢脉冲。电机转太快,单位时间脉冲数超过定时器计数能力或 MCU 读取太慢,漏数。提高定时器位数、缩短读取间隔,或评估编码器最高响应频率是否超 MCU 支持。

把这件事收一下

编码器接口是电机闭环的入口:硬件数脉冲判方向,4 倍频提分辨率,定时读计数器算转速。CPU 几乎不参与计数,只在测量周期去读一下差值。真正的麻烦不在配置,在方向极性、溢出回绕、还有每圈计数到底乘没乘 4。把这几点钉死,转速反馈就稳了,速度环才有靠谱的输入。

正反转只看 A、B 谁领先 90 度,4 倍频把分辨率翻四倍。读计数器用带符号减法吃溢出,每圈计数别忘乘 4,编码器接地要共,测速才不飘。

相关推荐
LCMICRO-1331084774620 分钟前
国产长芯微LD151完全P2P替代ADP151,是一款超低噪声、低压差线性稳压器
单片机·嵌入式硬件·fpga开发·硬件工程·电子工程师·线性稳压器·adp151
芯岭技术郦24 分钟前
XL2477 透传 WIFI 模组AT 指令说明
单片机·嵌入式硬件·个人开发·射频工程
@松26 分钟前
【无标题】STM32103FC8-DMA—直接存储区访问学习
stm32·嵌入式硬件·学习
深圳市恒锐丰科技杨生27 分钟前
SA8317E 2.7‑15V 单通道 H 桥电机驱动芯片|消费类低压直流有刷电机方案
嵌入式硬件·硬件工程
单片机仿真设计35 分钟前
【proteus仿真】基于 STM32 单片机六足机器人设计
stm32·单片机·嵌入式硬件·proteus·毕设
louis.D.1 小时前
gpio完整速查卡
单片机·嵌入式硬件
zcmodeltech1 小时前
垃圾发电厂沙盘模型控制系统设计与实现:多设备协同联动方案
java·大数据·数据库·人工智能·stm32·嵌入式硬件·制造
゛凌乱的记忆づ1 小时前
50元从零到成品:一套AI辅助的嵌入式实战入门教程——中断篇
单片机·嵌入式硬件
影寂ldy2 小时前
C# UDP+串口Modbus转发网关
单片机·网络协议·udp