ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32编码器测速全解析:从硬件接线到代码实现

STM32编码器测速全解析:从硬件接线到代码实现 简介STM32编码器测速代码是一套面向嵌入式开发者和电子设计竞赛参赛者的实用工程基于STM32F1系列如C8T6实现增量编码器信号采集与电机速度测量覆盖定时器编码器模式、GPIO输入、中断处理及转速换算等关键环节。压缩包共184个文件以.c/.h源码、编译生成的.o/.d依赖文件及Keil工程设计文件uvprojx/uvoptx为主另有链接脚本、map映射和axf镜像等整体仅4.75MB可通过Keil直接打开并重新编译。该工程已吸引10779人学习适合正在做控制类项目或备战电赛的开发者作为参考模板。示例工程能够帮助快速理解编码器测速的完整流程包括正反转判断、脉冲计数和RPM换算也可在此基础上移植到其他STM32型号或结合PID实现闭环速度控制省去从零搭建外设配置的时间工程中的标准外设库调用和硬件抽象逻辑同样具有学习价值。 搞嵌入式这几年我接触最多的传感器之一就是编码器尤其在电机测速和闭环控制这块STM32编码器几乎可以说是标配方案。今天这篇不聊虚的直接说透STM32编码器测速代码的完整思路从硬件接线、CubeMX配置到代码实现、调试排坑一次性讲完。如果你刚接触单片机不久或者正在做小车、云台、机械臂这类项目这篇文章应该能帮你少走不少弯路。我会把原理讲得尽量接地气代码也都是工程里直接可用的水准不是那种网上抄来抄去的残缺Demo。我自己在实际项目里用这套方法调过好几套电机驱动方案踩过的坑也会一并列出来。1. 项目整体设计与思路拆解1.1 为什么优先选定时器编码器模式而不是外部中断STM32测速这件事很多人第一反应是用外部中断数脉冲也就是把编码器A相接到某个GPIO上上升沿触发一次中断计数器加一。这个方法听上去简单实际用起来问题不少电机高速旋转时脉冲频率很容易到几千赫兹甚至几十千赫兹如果每一路脉冲都进中断CPU大量时间都花在反复压栈出栈上主循环、通信、控制算法全都会被拖慢。更麻烦的是A、B两相的相位关系也需要自己软件判断方向稍微处理不及时方向就判断错了。真正合理的方法是使用STM32的定时器编码器接口模式。这个模式是硬件自动工作的定时器内部会同时监测A相和B相的电平变化不仅能自动判断方向还能对上升沿和下降沿都进行计数实现四倍频。整个过程不占用CPU计数全部由硬件完成。这意味着哪怕A、B相频率很高代码也不会被脉冲中断打爆。对于电机转速测量这种场景这就是最合适的方案。我最早做电机小车的时候也图省事用过外部中断后来电机转速一上去串口打印数据和PID计算开始肉眼可见地卡顿换到编码器模式之后CPU占用率一下降下来整个控制周期稳定很多。如果你打算做闭环控制编码器模式基本上是绕不开的。1.2 增量式编码器测速的核心原理先把编码器本身说清楚。常见的增量式编码器输出两路方波信号分别叫A相和B相两路信号相位差正好90度。电机正转时A相超前B相90度反转时B相超前A相90度定时器就是通过检测这个相位关系来判断旋转方向的。为什么能四倍频因为A、B两路信号相加每个完整正交周期里一共有四个跳变沿A上升沿、A下降沿、B上升沿、B下降沿。定时器编码器模式对这四个边沿全部计数所以物理上编码器每输出一个脉冲计数器实际会增加4。假设编码器每圈输出13个脉冲PPR13那转一圈计数器实际累计的数字是13×452。这个知识在后面计算转速时非常关键很多人算错转速就是因为忘了乘4。增量式编码器通常还有一路Z信号每转一圈输出一个脉冲一般用来做机械零点校准。测速场景基本用不上Z相只需要A、B两路就够了。知道这些原理之后再去配置STM32就清楚多了。2. 硬件连接与工程初始化配置2.1 编码器与STM32的接线细节STM32的编码器接口模式并非所有引脚都支持必须把编码器的A、B两相接到同一个定时器的两个输入捕获通道上。常用的是TIM2、TIM3、TIM4、TIM5这些通用定时器。比如我用TIM3那A相接PA6也就是TIM3_CH1B相接PA7也就是TIM3_CH2对应关系一定不能弄错。选定时器的时候优先选中型以上型号的32位定时器例如TIM2或TIM5视具体型号而定这样计数器范围更大溢出处理压力会小很多。但很多入门板子代码都用TIM316位也完全够用只要做好溢出处理就行。接线方面编码器一般有VCC、GND、A、B四根线。电源电压必须确认清楚常见编码器有3.3V和5V两种供电规格输出电平也对应不同。如果编码器是5V供电输出高电平5V直接接到STM32的3.3V引脚上长期运行有烧毁IO的风险最好用电平转换芯片或者电阻分压处理一下。供电一定要跟STM32共地否则信号参考电位不一致计数必然乱七八糟。还有一个容易忽略的点是上拉电阻。很多开漏输出的编码器模块官方原理图里建议MCU内部开启上拉。在CubeMX配置GPIO或者定时器输入引脚时可以把引脚的上拉打开防止信号悬空时产生抖动脉冲。如果编码器线比较长布线环境有电机驱动之类的强干扰源建议A、B线上各加一个1kΩ到10kΩ的上拉电阻到3.3V同时并联一个几十皮法的电容滤波能明显减少误计数。我之前在电机驱动板旁边走线没做任何滤波编码器数据在低速时乱跳加了两颗小电容之后立刻干净了。2.2 CubeMX配置定时器编码器模式的要点现在大部分项目都用STM32CubeMX生成初始化代码配置编码器模式非常直观但有几个关键参数设置错了会直接影响测速结果。定时器配置页面里需要把Combined Channels设置为Encoder Mode也就是编码器接口模式。编码器模式下面有几种选项TI1、TI2、TI1 and TI2。我一般选TI1 and TI2这样A、B两相的边沿都会参与计数实现四倍频。如果选TI1或者TI2只对一相信号计数相当于二倍频精度会打折。输入极性Polarity选择上A、B两相都设置成Rising Edge上升沿触发就可以硬件会自动处理两个通道的组合边沿。但有的编码器A、B相位相反或者接线接反了此时计数方向相反解决方法有两个一是把编码器A、B线对调二是在CubeMX里把某个通道的极性改成Falling Edge效果等同于软件对调A、B相。我个人的习惯是硬件上直接交换A、B接头因为这样最直观不容易留下隐患。自动重载值ARR根据定时器位数来设置。16位定时器设置为65535这样计数器计数范围就是从0到65535超过这个范围会产生更新事件。脉冲计数周期也就是定时器时钟分频设置为最低预分频器PSC设为0保证每个边沿都能被硬件捕获不需要任何分频。这些参数配置完成后生成代码然后自己在用户代码里启动编码器模式就行了。3. 核心代码实现与测速计算3.1 编码器初始化与计数值读取CubeMX生成的代码已经完成了定时器参数的静态初始化但编码器模式还需要在运行时启动。在主循环开始之前调用HAL的编码器启动函数HAL_TIM_Encoder_Start(htim3, TIM_CHANNEL_ALL);注意这里第二个参数必须是TIM_CHANNEL_ALL不是某一个单独通道。新手容易在这里踩坑只启动了TIM_CHANNEL_1导致计数始终不对。启动之后读取当前计数值用下面这行int16_t encoder_count (int16_t)__HAL_TIM_GET_COUNTER(htim3);这里有一个非常关键的细节__HAL_TIM_GET_COUNTER返回的是uint32_t但16位定时器的计数器实际是16位有符号数。对于正交编码器模式计数器的值是有方向性的正转时递增反转时递减。如果直接把返回值当作uint32_t处理反转时的负数值会被解读成65535附近的大正数后续计算转速就会出严重问题。正确做法是把读取值强制转换成int16_t类型让负数正确还原。同理如果用了32位定时器则转换成int32_t。读完之后记得清零计数器保证每次采样周期内的计数值是增量值__HAL_TIM_SET_COUNTER(htim3, 0);这两行代码构成了完整的单次采样逻辑采样开始时计数值从0起步采样结束时读走当前的累计值再清零得到的就是一个固定周期内的脉冲增量。这个“定时读取-清零”的模式是所有编码器测速代码的骨架。3.2 转速换算公式与单位转换拿到了固定周期内的计数值增量之后需要换算成实际物理转速。这里的公式是整个测速代码最核心的部分我直接给出通用版本再举一个实际例子。假设PPR是编码器每圈输出的脉冲数比如常见的13线编码器PPR13四倍频模式下每圈总计数 4 × PPR采样周期T的单位是秒一个周期内读到的计数值增量是ΔN那么转速RPM圈/分钟的计算公式是RPM ΔN × 60 / (4 × PPR × T)举个例子一个13线编码器四倍频后每圈计数52采样周期取了20毫秒也就是0.02秒读到的ΔN是260。那转速就是RPM 260 × 60 / (4 × 13 × 0.02) 15600 / 1.04 ≈ 15000 RPM等等这个结果有点奇怪。260增量在0.02秒内每秒增量就是13000除以每圈52每秒就是250圈每分钟250×6015000转。电机的空载转速确实可以达到这个数量级但如果是带减速箱的直流电机那这里的转速是电机轴的转速最终输出轴的转速还要除以减速比。比如减速比是30那输出轴转速就是15000/30500转/分钟。很多小车测速算出来数值大得离谱原因就是漏了减速比这点千万要记住。如果小车测速还需要线速度那就要结合轮子直径。线速度v 输出轴转速RPM_out × π × 轮子直径D / 60。我用的是直径65mm的轮子RPM_out是500那线速度就是500×3.14×0.065/60≈1.7米/秒。把这些公式封装成一个函数每次采样直接把计数值传进去返回速度主程序用起来就很舒服。3.3 计数器溢出保护与中断处理16位定时器在编码器模式下计数器范围是-32768到32767。当电机高速运转时如果两次采样之间的间隔过长计数增量完全有可能超过32767导致计数器溢出读到的数值瞬间变成负数或者大幅跳变。解决这个问题通常有两种思路。第一种思路是提高采样频率保证每次采样周期内计数增量不超过32767。如果最大转速对应的每周期计数是5000那20ms采样周期完全来得及读取不会溢出。这种方法简单有效也是大多数项目的默认做法。采样周期太短也有问题下面会详细说。第二种思路是使用定时器的更新中断在计数器溢出时通过中断服务函数把溢出的次数记录下来跟当前计数值一起合成一个32位扩展计数。如果用的是16位定时器初始化开启更新中断__HAL_TIM_ENABLE_IT(htim3, TIM_IT_UPDATE); HAL_NVIC_EnableIRQ(TIM3_IRQn);然后在中断回调里处理volatile int32_t overflow_count 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM3) { // 根据计数方向判断溢出是向上溢出还是向下溢出 if (__HAL_TIM_IS_TIM_COUNTING_DOWN(htim3)) { overflow_count--; } else { overflow_count; } } }读取计数值的时候就合成32位总量int32_t total_count (int32_t)(overflow_count 16) (int16_t)__HAL_TIM_GET_COUNTER(htim3);这个方法适合高速编码器、长采样周期的场景。但说实话多数小车主控场景用第一种方法就够了额外增加溢出中断反而增加了代码复杂度和潜在的排查难度。如果你的电机转速不是极端高优先把采样周期调短就完了。4. 常见问题与排查技巧实录4.1 典型故障现象与处理对照表实际调试编码器测速时我遇到过的问题基本可以整理成一张表新手按图索骥就能节省大量时间。故障现象可能原因解决办法转速数值始终为0编码器模式未启动或A/B没有接到定时器通道检查HAL_TIM_Encoder_Start是否调用核对引脚对应关系正反转方向相反A、B相接反或极性配置反了交换A、B线或在CubeMX中调整极性数值剧烈跳动静止也计数信号毛刺、共地不良、电源纹波检查供电和共地加RC滤波开启内部上拉速度比实际值大一倍编码器模式选成了单通道而非TI1 and TI2确认配置为Encoder Mode TI1 and TI2速度数值偏大好几倍漏算了减速比或倍频系数确认4倍频确认减速比运行时CPU占用率高仍在用外部中断数脉冲切换到定时器编码器模式数值在中速时偶尔突变采样周期过长16位计数器溢出缩短采样周期或增加溢出中断处理这些故障里最隐蔽的是供电共地问题。编码器模块如果由独立的5V电源供电而5V电源的地和STM32的地没有接在一起A、B信号在MCU眼里完全是一堆不确定电平因为TTL电平判定必须以同一个参考地为基准。我实际遇到过编码器静止不动但计数持续跳变的情况排查了半天最后发现是编码器模块和单片机各用各的电源适配器地线没连。把两个电源的GND连通之后数据立刻稳定了。4.2 用逻辑分析仪验证正交信号波形当编码器数值异常但接线和代码看起来都没问题时强烈建议用逻辑分析仪直接抓A、B两相的波形。逻辑分析仪在这里的价值和示波器类似价格便宜、上手简单适合观察低速数字信号。把A相接分析仪通道0B相接通道1转动电机或者手动拧编码器轴看波形是不是正常的正交方波。正常的正交信号应该是这样的A相和B相都有清晰的方波上升沿和下降沿两路频率相同相位差90度。如果看到波形有大量毛刺或者边沿抖动严重说明信号完整性有问题需要检查屏蔽和滤波。如果波形正常但计数不对问题就在STM32配置上。如果波形缺相或者完全没有脉冲先查编码器供电和接线。逻辑分析仪能直接把问题定位在硬件还是软件省得盲猜。我自己有一次怎么调代码都计数不对用逻辑分析仪一看A相波形是对的B相输出恒定高电平根本不是方波。最后发现是编码器内部的B相输出引脚虚焊重新补焊后问题彻底消失。这种问题如果不用仪器光看代码和配置能排查一整天。另外如果采样到的速度曲线毛刺多测速结果在PID控制里会导致输出抖动这时可以在速度计算后加一阶低通滤波。滤波系数alpha取0.1到0.3之间平滑效果比较好。但注意滤波会增加相位滞后系数不能太大PID参数也要相应调整。4.3 环境与工程层面的几个小坑除了编码器本身的问题环境配置也容易卡住新手好几天。比如下载程序时提示Error: No STM32 Target Found这个和编码器没什么直接关系多半是调试器没识别到芯片。检查一下ST-Link或J-Link是否有单独供电连接线是否牢固BOOT0引脚是否被拉高MCU供电是否正常。还有一个容易被忽略的问题是芯片的Debug接口引脚被复用为其他功能如果把SWDIO和SWCLK配成了普通GPIO就会导致调试器连接不稳定。Keil环境方面如果之前装了C51版本的Keil再装MDK时要注意安装路径不能带中文且需要分别安装对应的芯片包。编译报错No STM32 Target Found这种情况还得检查工程是否选对了Device型号。如果你的板子是APM32这类国产兼容芯片程序一般可以直接用STM32工程编译下载但需要安装对应的芯片支持包选型时也要额外确认引脚定义是否完全一致。编码器测速代码虽然只是整个控制系统里的一小块但它的稳定性直接决定了速度闭环能不能正常工作。我在实际项目里的体会是先花半小时用逻辑分析仪确认硬件信号没问题再写代码配置定时器最后再计算和滤波这样顺序走下来效率最高。不要上来就堆代码出了问题反而要回头查硬件。如果只是在做实验验证可以把测到的速度通过串口打印出来边转电机边看数据你会很快建立起对编码器测速的直观感觉。本文还有配套的精品资源点击获取
返回列表