☰
STM32编码器测速全链路拆解:从脉冲计数到速度闭环的工程实践
2026/10/27 9:17:31 网站建设 项目流程

1. 从脉冲到速度:编码器测速的完整工程链路拆解

编码器读到的到底是什么?这个问题看起来简单,但我在带新人的时候发现,十个人里有八个会卡在“我读到了数,但这个数怎么变成速度”这一步。编码器输出的本质是一串脉冲,而速度是一个物理量,中间隔着一整套信号处理、定时器配置、数学换算和滤波的工程链路。这篇文章就是把这根链路从头到尾拆开,讲清楚每一个环节在做什么、为什么这么做、以及实际调试时会踩哪些坑。

不管你是用STM32做电机控制、用ESP32做机器人底盘,还是用HC32F460做工业伺服,编码器测速的核心逻辑是相通的。区别只在于定时器的资源分配、中断的优先级安排、以及你对精度和实时性的取舍。我会以STM32的定时器编码器模式为主线来展开,因为这是最典型的场景,但涉及的原理和方法可以平移到任何MCU平台。

这篇文章适合谁看?如果你已经能点亮LED、能配置GPIO和定时器,但对编码器测速的完整流程还模模糊糊,那正好。如果你已经能读出脉冲数但速度波动大、低速测不准、高速溢出了,那这篇文章里的排查思路和参数计算方法应该能帮到你。我会尽量把每个环节的“为什么”讲透,而不是只给一段代码让你抄。

2. 编码器信号的本质:你读到的到底是什么

2.1 增量式编码器的三码道结构

先搞清楚硬件层面发生了什么。常见的增量式编码器有A、B、Z三路信号,这就是所谓的“3码道编码器原理”的核心。A相和B相是两路方波,它们的频率相同但相位差90度,这个相位差是判断方向的关键。Z相是零位信号,每转一圈输出一个脉冲,用来做绝对位置校准。

为什么是90度相位差而不是180度?因为90度能让你在任意时刻通过比较A和B的电平状态判断出旋转方向。如果是180度,两个信号完全反相,你只知道有脉冲,但分不清是正转还是反转。90度相位差产生四种组合状态:00、01、11、10,正转时按这个顺序循环,反转时按相反顺序循环。这就是四倍频计数的物理基础。

Z相的作用经常被忽略。很多人觉得有了A、B相就能测速和计位置了,Z相可有可无。但在实际工程中,Z相是消除累积误差的唯一手段。A、B相计数是相对的,你上电时不知道编码器在什么位置,只能从零开始累加。如果中间丢了脉冲或者受到干扰多计了脉冲,误差会一直累积下去。Z相每转一圈给你一个绝对参考点,你可以在Z相到来时把计数器清零或者校准,这样误差就不会跨圈累积。

2.2 脉冲、方向与四倍频计数

STM32的定时器编码器模式是硬件级别的解码器,它自动处理A、B相的相位关系,根据边沿变化自动加一或减一。你配置好之后,直接读CNT寄存器的值就是带方向的计数值,不需要自己写中断去判断方向。

这里有个关键选择:计数模式。STM32的编码器模式支持三种计数方式——只在A相边沿计数、只在B相边沿计数、在A和B的边沿都计数。第三种就是四倍频,因为A相有上升沿和下降沿,B相也有上升沿和下降沿,一个周期内总共四个边沿,每个边沿都触发计数,所以分辨率是单相脉冲数的四倍。

四倍频的好处是分辨率高,同样线数的编码器能测到更细的位置变化。但代价是同样的转速下计数频率翻了四倍,定时器的计数溢出风险也翻了四倍。如果你的编码器是1000线,四倍频后每转4000个计数,电机转速3000RPM时,计数频率就是3000/60*4000=200kHz。STM32的定时器在72MHz时钟下处理200kHz的计数完全没问题,但如果你用的是低速MCU或者高线数编码器,就要算一下定时器的处理能力了。

注意:四倍频模式下,A、B相的边沿都会触发计数,所以对信号的抖动非常敏感。如果编码器信号质量不好,边沿有毛刺,四倍频会把毛刺也计进去,导致计数偏大。这种情况下要么加硬件滤波,要么改用二倍频甚至单倍频模式。

2.3 从计数值到物理量的换算链路

读到的CNT值是一个整数,它代表的是“从上次清零到现在,编码器转过了多少个最小分辨单元”。要把它变成速度,需要经过三步换算。

第一步,把计数值转换成角度或位移。公式是:角度 = CNT / (线数 × 倍频数) × 360度。比如1000线编码器四倍频,CNT=4000时对应360度。如果是丝杆传动,位移 = CNT / (线数 × 倍频数) × 丝杆导程。

第二步,把角度或位移除以时间,得到角速度或线速度。这里的时间是你两次采样之间的间隔,通常由定时器中断或者系统滴答定时器来提供。

第三步,把单位转换成你需要的格式。RPM、度每秒、米每秒、毫米每秒,看你的应用场景。

这三步看起来简单,但每一步都有坑。第一步的坑在于线数和倍频数的确认——有些编码器标称的线数是A相脉冲数,有些标的是四倍频后的计数,搞错了速度就差四倍。第二步的坑在于采样周期的稳定性——如果你用系统滴答定时器做采样,但中断优先级没安排好,采样周期会抖动,速度就会波动。第三步的坑在于单位换算时的整数除法——如果你用整数运算,除不尽的部分会被截断,低速时误差会很大。

3. STM32定时器编码器模式配置实战

3.1 定时器选型与引脚分配

STM32的定时器中,TIM1、TIM2、TIM3、TIM4、TIM5、TIM8都支持编码器模式。其中TIM2和TIM5是32位定时器,其他是16位。如果你的编码器线数高、转速快,建议优先用32位定时器,因为16位定时器在四倍频下很容易溢出。

引脚方面,每个定时器的通道1和通道2分别接A相和B相。比如TIM3的CH1在PA6,CH2在PA7;TIM4的CH1在PB6,CH2在PB7。具体引脚要看你的芯片型号和数据手册,不同封装的引脚映射可能不一样。

Z相的处理有两种方案。一种是接普通GPIO,配置外部中断,在中断里清零计数器。另一种是接定时器的通道3或通道4,利用定时器的输入捕获功能。前者的优点是灵活,任何GPIO都行;后者的优点是硬件自动处理,不占CPU。我一般推荐用外部中断,因为Z相每转才来一次,中断频率很低,对CPU负担可以忽略,而且代码逻辑更清晰。

实操心得:配置编码器模式时,GPIO要设置为浮空输入或上拉输入。如果你的编码器是集电极开路输出,必须加上拉电阻,否则读不到高电平。上拉电阻一般选4.7k到10k,太小了功耗大,太大了上升沿变缓,高速时可能丢脉冲。

3.2 编码器模式的寄存器级配置

STM32的编码器模式配置其实不复杂,关键是几个寄存器的设置要到位。我用TIM3来举例,假设A相接PA6,B相接PA7,Z相接PB0。

首先开时钟:RCC->APB1ENR |= RCC_APB1ENR_TIM3EN,同时开GPIOA和GPIOB的时钟。

然后配置GPIO:PA6和PA7设为浮空输入,PB0设为上拉输入(Z相通常需要上拉)。

接下来配置TIM3。关键寄存器是SMCR(从模式控制寄存器)和CCMR1(捕获比较模式寄存器)。

SMCR的SMS位设为001,表示编码器模式1,也就是根据TI1和TI2的电平变化计数。CCMR1的CC1S和CC2S都设为01,表示通道1和通道2都配置为输入,映射到TI1和TI2。CC1P和CC2P设为0,表示不反相。CC1E和CC2E设为1,使能捕获。

ARR(自动重装载寄存器)设为65535,让计数器自由计数。CNT初始值设为0。CR1的CEN位设为1,启动定时器。

这样配置完之后,TIM3->CNT的值就会随着编码器的旋转自动增减。正转时增加,反转时减少。你不需要写任何中断代码来处理A、B相,硬件全帮你做了。

3.3 溢出处理与计数范围扩展

16位定时器的CNT范围是0到65535,四倍频下1000线编码器转16圈就会溢出。如果你的应用需要连续计位置,必须处理溢出。

处理溢出的标准做法是在定时器更新中断里维护一个高位计数器。每次CNT溢出时,如果是向上溢出就高位加一,向下溢出就高位减一。最终的位置值 = 高位计数器 × 65536 + CNT。

但这里有个细节:编码器模式下,CNT的溢出方向取决于旋转方向。正转时CNT从65535跳到0,是向上溢出;反转时CNT从0跳到65535,是向下溢出。STM32的TIMx_CR1寄存器的DIR位可以读出当前计数方向,你可以在更新中断里读DIR位来判断是哪种溢出。

注意:更新中断的频率取决于转速和线数。1000线编码器四倍频,3000RPM时CNT的溢出频率是3000/60*4000/65536≈3Hz,完全在可处理范围内。但如果你用的是10000线编码器,溢出频率就是30Hz,中断负担就上来了。这种情况下建议换32位定时器,或者降低倍频数。

4. 速度计算的三种方法与选型对比

4.1 M法测速:固定时间读计数

M法是最直观的方法:每隔固定时间T读一次CNT,两次读数之差就是这段时间内的脉冲数,除以T就是速度。

公式:速度 = (CNT_now - CNT_last) / (线数 × 倍频数 × T)

M法的优点是实现简单,只需要一个定时中断。缺点是低速时精度差,因为T时间内可能只读到几个脉冲,甚至一个脉冲都没有,量化误差很大。比如T=10ms,编码器1000线四倍频,转速1RPM时,10ms内只转了1/6000圈,对应0.67个计数,四舍五入后要么是0要么是1,速度误差可能达到50%。

M法适合高速场景。一般来说,当T时间内至少有几十个脉冲时,M法的精度就够用了。你可以根据最低转速要求来反推T的大小:T ≥ 期望精度 × 线数 × 倍频数 / (最低转速/60)。

4.2 T法测速:固定脉冲测时间

T法反过来:记录两个相邻脉冲之间的时间间隔,速度 = 1 / (时间间隔 × 线数 × 倍频数)。

T法的优点是低速时精度高,因为低速时脉冲间隔大,时间测量相对准确。缺点是高速时精度差,因为高速时脉冲间隔很小,定时器的计时分辨率不够。比如72MHz定时器,1us分辨率,转速3000RPM时脉冲间隔是1/(3000/60*4000)=5us,只有5个计时单位,量化误差20%。

T法适合低速场景。实现上需要用输入捕获功能,每次脉冲边沿触发捕获,记录CCR值,两次捕获之差就是脉冲间隔。

4.3 M/T法:兼顾高低速的混合方案

M/T法结合了M法和T法的优点:在固定时间内同时记录脉冲数和最后一个脉冲到采样点的时间,用脉冲数除以实际时间。这样高速时脉冲数多,精度靠脉冲数保证;低速时脉冲数少,但时间测量精确,精度靠时间保证。

M/T法的实现比前两种复杂,需要同时用到定时器中断和输入捕获。但在宽速度范围内要求高精度的场合,M/T法是最优解。

方法适用速度范围精度特点实现复杂度资源占用
M法高速高速好,低速差低一个定时中断
T法低速低速好,高速差中输入捕获
M/T法全速段全速段都好高定时中断+输入捕获

实操心得:大多数电机控制应用其实用M法就够了,因为电机正常工作时转速不会太低。只有在需要精确位置控制或者极低速运行的场合,才需要上T法或M/T法。我一般先用M法调通,如果低速指标不满足再换M/T法。

5. 从速度到PWM:闭环控制中的关键环节

5.1 速度环的控制周期选择

速度环的控制周期直接影响控制效果。周期太短,速度计算精度不够,而且CPU负担重;周期太长,响应慢,负载变化时速度波动大。

经验值是控制周期取速度环带宽的5到10倍。比如你希望速度环带宽是100Hz,控制周期就取1ms到2ms。对于一般的直流电机或步进电机,1ms到10ms的控制周期都算合理。

控制周期还和编码器线数有关。线数低的时候,周期太短会导致每个周期只读到几个脉冲,量化噪声大。这时候要么加长周期,要么用T法。

5.2 PWM调速与编码器反馈的配合

PWM调速的基本原理是改变占空比来改变电机平均电压。STM32的定时器可以很方便地输出PWM,比如TIM1的CH1在PA8输出PWM,配置好ARR和CCR就能调占空比。

但PWM和编码器配合时有几个坑。第一个坑是PWM频率的选择。频率太低,电机有啸叫,而且电流纹波大;频率太高,MOS管开关损耗大,驱动器的死区时间影响也大。一般有刷直流电机用10kHz到20kHz,无刷电机用20kHz到40kHz。

第二个坑是PWM和编码器采样的同步。如果PWM更新和速度采样不同步,速度反馈会有周期性波动。好的做法是在PWM周期结束时触发速度采样,这样每次采样的时间点相对于PWM波形是固定的。

第三个坑是死区时间。如果用的是H桥驱动,上下桥臂之间必须留死区时间,否则会直通短路。死区时间会损失一部分有效电压,低速时影响特别明显。你可以在速度环里加一个死区补偿,根据电流方向补偿掉死区损失。

5.3 速度环PID参数整定实战

速度环通常用PI控制器就够了,D项在速度环里容易引入噪声。整定顺序是先P后I,P从0开始慢慢加,加到速度有轻微振荡,然后回调到振荡消失。I从0开始加,加到速度能快速跟踪设定值且没有稳态误差。

但编码器测速的噪声会影响PID整定。如果速度反馈噪声大,P不能加太大,否则输出抖动。这时候要么加低通滤波,要么提高速度计算精度。

低通滤波的截止频率一般取速度环带宽的2到5倍。比如速度环带宽100Hz,低通滤波截止频率取200Hz到500Hz。滤波太狠会引入相位滞后,降低系统稳定性。

注意:低通滤波会引入延迟,延迟会降低相位裕度。如果你发现加了滤波之后系统反而更容易振荡,说明滤波截止频率太低了。这时候要么提高截止频率,要么降低P增益。

6. 常见问题排查与避坑指南

6.1 脉冲计数不准确的原因分析

脉冲计数不准是最常见的问题,表现是速度显示波动大、位置累积误差大、或者正反转计数不对称。

原因一:信号干扰。编码器信号线如果和电机线捆在一起走,电机的PWM噪声会耦合到编码器信号上,导致多计或漏计。解决办法是编码器线用屏蔽线,屏蔽层单端接地,并且和电机线分开走线。

原因二:上拉电阻不合适。集电极开路输出的编码器需要上拉电阻,阻值太大会导致上升沿变缓,高速时来不及达到高电平就被下一个边沿触发了。阻值太小功耗大,而且可能超过编码器输出管的灌电流能力。一般4.7k到10k是安全范围。

原因三:GPIO配置错误。编码器模式的GPIO必须配置为输入模式,不能配置为复用输出。有些人在CubeMX里配置的时候没注意,把引脚配成了其他功能,结果读不到信号。

原因四:定时器时钟没使能。这个是最低级的错误,但新手经常犯。配置寄存器之前一定要先开时钟,否则写进去的值不生效。

6.2 低速测不准与高速溢出的对策

低速测不准的根本原因是量化误差。M法在低速时每个采样周期只读到几个脉冲,甚至零个脉冲,速度计算出来要么是零要么跳变。对策有三个:加长采样周期、改用T法或M/T法、或者用位置差分代替速度计算。

位置差分是另一种思路:不直接算速度,而是算位置的变化量,然后用位置的变化量除以时间。这和M法本质一样,但如果你对位置做了滤波,速度也会平滑一些。

高速溢出的表现是速度突然变成负值或者跳变到很大。原因是CNT溢出后没有正确处理。对策是用32位定时器、降低倍频数、或者加溢出中断维护高位计数。

还有一个隐蔽的坑:编码器线数标称值和实际值不符。有些厂家标的线数是电机轴转一圈的脉冲数,但编码器可能装在减速器后面,实际脉冲数要乘以减速比。如果你按标称值算,速度会差减速比倍。

6.3 常见问题速查表

现象可能原因排查方法解决方案
速度波动大信号干扰、采样周期不稳示波器看编码器波形屏蔽线、稳定采样周期
低速时速度为零量化误差检查采样周期内脉冲数加长周期或改用T法
高速时速度跳变CNT溢出未处理检查CNT是否溢出用32位定时器或加溢出中断
正反转计数不对称A、B相接线反了交换A、B相交换接线或改计数模式
位置累积误差大丢脉冲或Z相未校准检查Z相信号加Z相校准
速度差固定倍数线数或倍频数配置错误核对编码器手册修正线数和倍频数

实操心得:排查编码器问题时,示波器是最有用的工具。先看A、B相的波形是否干净,边沿是否陡峭,相位差是否90度。如果波形有问题,先解决硬件问题,软件怎么调都没用。如果波形没问题,再查软件配置。

7. 不同MCU平台的移植要点

7.1 HC32F460的PWM与编码器配置差异

HC32F460是华大的MCU,在工业控制里用得不少。它的定时器架构和STM32不太一样,编码器模式配置的寄存器名和位定义都不同。

HC32F460的定时器叫TIMERA和TIMERB,编码器模式需要用TIMERA的编码器功能。配置流程和STM32类似,但寄存器操作更繁琐一些。PWM配置方面,HC32F460的CCU6模块功能很强,但配置起来比STM32的定时器复杂。

移植的时候要注意时钟树的差异。HC32F460的定时器时钟源和STM32不同,分频系数的计算方式也不一样。如果你从STM32移植代码到HC32F460,时钟配置部分基本要重写。

7.2 ESP32的PCNT模块与编译速度优化

ESP32有专门的PCNT(脉冲计数器)模块,硬件级别支持编码器计数,不占用CPU。PCNT支持四倍频、方向检测、上限下限报警,功能比STM32的定时器编码器模式还全。

用PCNT的好处是CPU完全不用管计数,只需要定期读PCNT的计数值就行。缺点是PCNT的通道数有限,ESP32只有8个PCNT单元,多路编码器的时候不够用。

ESP32的编译速度是另一个话题。用Arduino框架编译ESP32项目,第一次编译可能要几分钟。加快编译速度的方法有:用PlatformIO代替Arduino IDE、开启编译缓存、减少不必要的库依赖、用ccache。这些方法能显著缩短编译时间,调试的时候体验好很多。

7.3 跨平台移植的注意事项

跨平台移植编码器代码,核心是抽象出硬件相关的部分。我一般会定义一个编码器接口结构体,包含初始化、读计数、读方向、清零这几个函数指针。上层速度计算和PID控制完全不依赖具体硬件,移植的时候只需要重写底层驱动。

这样做的另一个好处是方便单元测试。你可以在PC上模拟一个编码器,给定任意的计数序列,测试速度计算和PID逻辑是否正确,不需要实际硬件。

实操心得:移植的时候最容易出错的是计数方向。不同MCU的编码器模式对正反转的定义可能相反,STM32正转CNT增加,但某些MCU可能正转CNT减少。移植后一定要先验证方向,否则速度环会变成正反馈,一上电就飞车。

8. 调试工具与实测经验分享

8.1 用示波器看编码器波形

示波器是调试编码器的第一工具。把探头接到A相和B相,看波形是否干净、边沿是否陡峭、相位差是否90度。如果波形有毛刺,说明有干扰,先解决硬件问题。

看波形的时候要注意时间基准。低速时脉冲间隔大,用ms级时间基准;高速时脉冲间隔小,用us级时间基准。如果波形在高速时变形严重,说明编码器输出能力不够或者线太长,需要考虑加缓冲器或者缩短线长。

Z相波形也要看。Z相应该在A、B相特定状态时出现,通常是A、B都为高的时候。如果Z相位置不对,位置校准就会出错。

8.2 串口打印调试数据的技巧

串口打印是最常用的调试手段,但打印速度有限,不能每个控制周期都打印。我一般用两种方式:一种是降频打印,每100个控制周期打印一次;另一种是缓冲打印,把数据存到数组里,等攒够一批再一次性打印。

打印的数据要包括:设定速度、实际速度、PWM占空比、编码器原始计数。这四个数据能覆盖大部分调试场景。如果速度环振荡,看设定速度和实际速度的相位关系;如果速度上不去,看PWM占空比是否饱和;如果计数异常,看原始计数的变化。

注意:串口打印本身会占用CPU时间,如果打印太频繁,会影响控制周期的稳定性。调试的时候可以接受,但正式运行时要关掉打印或者降低频率。

8.3 实际项目中的参数记录与复盘

每个项目的编码器参数、速度环参数、滤波参数都应该记录下来。下次做类似项目的时候,这些参数就是起点,能省很多调试时间。

我一般会记录:编码器型号和线数、倍频数、采样周期、速度环P和I、滤波截止频率、PWM频率、死区时间。这些参数组合起来,基本能复现一个项目的速度控制效果。

复盘的时候重点看:低速精度是否满足要求、高速是否稳定、负载变化时恢复时间多长、有没有异常噪声。这些指标决定了你的参数是否真的合适。

9. 写在最后

编码器测速这件事,原理不复杂,但细节很多。从信号质量到定时器配置,从计数模式到速度算法,从PID整定到滤波设计,每个环节都有坑。我见过太多人卡在某个环节上,明明代码看起来没问题,但速度就是不对。

我的经验是:先保证硬件信号干净,再保证定时器配置正确,然后验证计数方向,最后调速度算法。这个顺序不能乱,跳步的话后面会花更多时间排查。

还有一点:不要迷信高线数编码器。线数高确实分辨率高,但对信号质量、定时器资源、CPU处理能力的要求也高。很多应用用500线甚至250线就够了,配合四倍频和合适的采样周期,精度完全能满足要求。选型的时候根据实际需求来,不要盲目堆参数。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询