ARTICLE DETAIL

资讯详情

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

STM32定时器编码器模式测速:从原理到代码实现

STM32定时器编码器模式测速:从原理到代码实现 在调试一款带霍尔编码器的直流减速电机时我一开始图省事直接用STM32的外部中断去数脉冲A相接PA0B相接PA1每次上升沿进一次中断计数。低速空转的时候看起来挺正常一旦电机转速拉起来示波器上还能看到完整的方波可单片机数出来的脉冲数却开始对不上方向判断更是稀烂。后来我查了一圈才发现问题不在硬件而在测速方案本身——STM32定时器自带编码器模式硬件自动完成脉冲计数和方向判别根本不占CPU。这篇博文就把我从原理到代码完整走一遍说说怎么用STM32定时器的编码器接口模式配合光电码盘或者霍尔编码器实现电机转速的精准测量。顺便先澄清一个容易踩的搜索坑这里说的编码器是旋转编码器/码盘不是深度学习里那个自编码器也不是Transformer里的编码器结构。1. 编码器测速的底层逻辑为什么测脉冲比测电压靠谱1.1 编码器到底在测什么很多刚开始做电机控制的朋友会问测转速为什么不直接量电机两端的电压毕竟电压高转速就快。这个思路在没有负载的空转场合勉强能用但一旦带上负载电机电流增大、内阻压降变大同样的电压实际转速会掉很多。更别说PWM调速时占空比和平均电压的关系还会受到MOS管压降、死区时间等因素的干扰想做精准测速基本不现实。编码器的思路完全绕开了电压这个间接量。增量式编码器输出的是一组方波脉冲每个脉冲对应电机轴转过的一个固定角度。只要数出单位时间内的脉冲个数就能反推出转速。换句话说测的是角度变化量对时间的变化率这是位置量的微分物理意义直接、抗负载干扰能力强。这也是工业上几乎所有伺服电机都要装编码器的原因。从大类上看编码器分增量式和绝对式两种。增量式输出A、B两路方波掉电后不知道轴在什么位置但胜在便宜、接口简单绝大多数直流电机测速场景都用它。绝对式编码器比如磁编码器AS5047P通过SPI直接读出一个绝对角度值掉电不丢失位置但成本高、接线多一般用来做精确的位置闭环普通测速项目用不上。本文后面说的都是增量式。1.2 AB相与倍频转一圈为什么能数出这么多脉冲增量式编码器最核心的输出是A、B两路方波两路信号频率相同、相位相差90度。正是这90度的相位差让单片机可以判断电机到底是正转还是反转如果A相超前B相90度是正转反过来B相超前A相就是反转。判断方法有两种一种是在A相上升沿读B相电平另一种是把AB相接在定时器的编码器模式上由硬件自动判断计数方向后者正是STM32定时器最擅长的活。有了AB相之后计数分辨率还能翻倍。如果只在A相的上升沿计数叫1倍频在A相上升沿和下降沿都计数就是2倍频如果在A、B两相的上升沿和下降沿都计数就是4倍频。4倍频之后单位时间内的计数个数变成原始脉冲频率的4倍能分辨的角度精度也提高到原来的4倍。举个例子EC11旋转编码器就是鼠标滚轮里那种通常一圈输出20个脉冲4倍频后一圈能数到80个计数。而小车底盘常见的带码盘电机码盘线数是13电机本身转一圈码盘输出13个完整的A相周期如果电机还带减速箱、减速比30:1那么电机输出轴转一圈A相输出的脉冲就是134倍频后就是13×452再乘以减速比30输出轴一圈总共能数到1560个计数。这个数值很重要后面算转速全靠它。1.3 为什么我不建议用外部中断做编码器测速刚上手时我用外部中断测速方法是A相接外部中断引脚每次A相跳变沿触发中断在中断里读B相电平判断方向再累加计数器。这个方法在低速下确实能跑但有两个硬伤。第一CPU负担太重。假设电机输出轴转速为3000RPM经过减速比30后电机本体转速是90000RPM每秒1500转。按单圈52个脉冲算A相每秒产生78000个脉冲也就是要进78000次中断。STM32F103主频72MHz处理一次中断即使只花1微秒也要占用接近全部CPU时间别说还要跑控制算法和显示连系统调度都会被拖垮。第二容易丢脉冲。中断里读写寄存器需要时间如果两次跳变间隔小于中断响应时间脉冲就丢了。高速时A、B两相的跳变间隔可能只有几百纳秒外部中断方案在这个尺度上基本不可靠。定时器的编码器模式则完全不同。AB相接在定时器的CH1、CH2引脚上后计数是由硬件完成的不需要进中断不存在丢脉冲的问题。CPU只是周期性读取计数寄存器的值来算速度负载极低。所以我的结论很简单只要是用STM32做编码器测速第一选择永远是定时器的编码器模式不是外部中断。2. 硬件选型与接线EC11、码盘电机和霍尔编码器怎么选2.1 三种常见编码器怎么选提到编码器测速很多人第一个搜到的是EC11旋转编码器。EC11确实是编码器但它天生是给手动旋钮用的旋转速度不快机械寿命以万次旋转计不适合装在电机上长时间高速旋转。它有20个脉冲每圈4倍频后是80个计数用来做音量旋钮、菜单选择、数控面板的输入没有问题但拿它测电机转速很快就会磨损而且没有码盘和减速箱配合转速分辨率也远远不够。真正适合电机测速的有两类。第一类是光电码盘电机也叫光栅码盘测速模块。码盘是一片带黑白相间条纹的塑料圆盘安装在电机转轴上光电对管一边发光一边接收码盘旋转时交替遮挡光线输出方波。这种方案在小车底盘电机里非常常见便宜、信号干净码盘线数常见的13、20、36都有。第二类是霍尔编码器电机比如网上常见的JGA25-370电机。霍尔元件贴在电机内部或尾部磁环随电机轴旋转输出两路正交方波。相比光电码盘霍尔方案不怕灰尘油污信号稳定性更好输出已经是规整的方波直接接单片机即可不用额外整形电路。我现在项目里用的就是JGA25-370减速比30:1编码器线数13实测信号质量比光电码盘省心不少。两类电机的选型要点可以看下面的表。类型输出信号优点缺点常见场景EC11旋转编码器A/B正交方波便宜、接线简单、有定位感不适合高速连续旋转人机交互旋钮、手动调节光电码盘电机A/B正交方波便宜、无接触、信号干净怕油污灰尘遮挡小车底盘测速、传送带定位霍尔编码器电机A/B正交方波抗污染、寿命长、信号规整价格略贵要求较高的测速闭环项目绝对式磁编码器SPI或I2C输出绝对角度掉电保留位置、精度高成本高、驱动复杂精确位置控制、机械臂关节2.2 接线里最容易翻车的三件事编码器接线看着就四根线电源、地、A相、B相但最容易出问题的地方恰恰在这里。第一是共地。编码器的GND必须和STM32的GND连在一起否则A、B相的电平参考基准都不一样单片机读到的信号不是高就是低根本没法判断。这个坑我见过太多新手踩尤其是电机驱动板和单片机分开供电的时候经常忘记把电源的负极连到一起去。第二是上拉电阻。很多编码器模块的输出是开漏结构模块上如果没有自带上拉电阻A、B相在无输出时是悬空的电平不确定计数会乱跳。这种情况下需要在A、B相到3.3V之间各接一个4.7k到10k的上拉电阻。买模块的时候先确认一下板子上有没有排阻没有的话自己飞线焊两个。第三是信号电平。常见编码器模块用5V供电输出高电平也是5VSTM32的IO是3.3V容忍直接接3.3V引脚的GPIO一般问题不大但为了稳妥最好加一级电阻分压或者用电平转换芯片。另外A相和B相互换会导致方向反这只是接线问题不是代码问题真遇到方向反了把A、B对调一下即可。2.3 电气上的小细节电源滤波与PWM干扰电机是出了名的干扰源。电机启动瞬间电流可能是正常工作的好几倍会在电源线上拉出很大的纹波如果编码器和单片机共用一组电源纹波会直接窜进A、B信号里导致误计数。我的标准做法是电机电源和逻辑电源分开各自独立供电。电机电源两端并联一个100uF电解电容和一个104陶瓷电容吸收启动电流冲击编码器模块和STM32共用3.3V逻辑电源也在电源引脚附近放104电容。如果板子空间允许A、B信号线上再串一个几十欧的电阻配合STM32定时器的输入滤波功能基本能把绝大多数毛刺过滤掉。另外PWM驱动线和编码器信号线不要平行走线。电机的PWM驱动线上电流变化很快会通过寄生电容耦合到旁边的信号线上。实测中我发现同样的代码信号线和PWM线绑在一起走转速值偶尔会跳一下分开走之后跳变基本消失。3. 定时器编码器模式配置CubeMX参数逐项拆解3.1 为什么编码器模式比输入捕获省心STM32定时器的编码器模式本质上是一个由外部信号自动控制加减计数的计数器。A相接CH1B相接CH2定时器会根据A、B两相的相位关系自动决定计数器是向上加还是向下减。这样方向判断、脉冲计数、倍频这些工作全部由硬件完成CPU只需要定期读一下CNT寄存器的值。对比输入捕获模式编码器模式的优势非常明显。输入捕获需要自己配置两个通道捕获上升沿时间戳然后软件计算频率和方向代码量大而且捕获中断同样有CPU占用问题。编码器模式只需要把定时器配置成编码器模式启动定时器然后直接读计数器值就行了代码量少一个数量级。不过要注意不是所有定时器都能做编码器模式。STM32F103上TIM1、TIM2、TIM3、TIM4、TIM8都支持编码器模式但TIM6、TIM7这种基本定时器不行。选型的时候看清楚工程里如果有多个电机要测速尽量选一个定时器管一个电机避免资源冲突。3.2 CubeMX里的关键参数怎么填在STM32CubeMX里配置编码器模式的路径是选择定时器比如TIM3然后看Combined Channels组合通道这个选项默认是Disabled把它改成Encoder Mode编码器模式。改成编码器模式后下方会自动出现几个参数这里逐个说明。第一个是Encoder Mode。有TI1、TI2和TI1 and TI2三个选项。TI1表示只在A相跳变沿计数等效1倍频TI2表示只在B相跳变沿计数也是1倍频TI1 and TI2表示A、B两相的跳变沿都计数等效4倍频。做测速项目直接选TI1 and TI2分辨率最高方向判断也最稳。第二个是Period自动重装载值。编码器模式下这个值决定了计数器的计数范围。因为CNT是16位寄存器最大能数到65535通常直接把Period填成65535让计数器自由转圈。注意编码器模式下计数器不会自动清零数到65535后继续递增会回绕到0这个特性后面代码里要处理。第三个是Input Filter输入滤波。这是抗干扰的关键参数默认是0也就是不过滤。实际电机环境里建议设置一个非零值比如0x0F。它的原理是只有当信号电平持续稳定多个采样周期后才认为信号有效能滤掉窄毛刺。具体滤波强度要根据定时器时钟频率和信号频率来定采样频率越高滤波效果越弱采样频率越低滤波越强但可能滤掉正常信号。对编码器信号来说0x0F是个比较稳妥的起步值。第四个是Prescaler预分频系数编码器模式下保持0即可不需要分频。3.3 配置好之后还要做什么CubeMX配置完生成代码后只是把定时器的寄存器初始化好了还需要在代码里启动它。HAL库的启动函数是HAL_TIM_Encoder_Start(htim3, TIM_CHANNEL_ALL);第二个参数传TIM_CHANNEL_ALL表示同时启动编码器模式使用的两个通道。启动之后CNT寄存器就会随着A、B相信号自动增减不需要额外中断。启动之后还要注意两点。第一初始计数值要清零。上电瞬间CNT可能是一个随机值用__HAL_TIM_SET_COUNTER(htim3, 0)把计数器清零。第二读取计数值要趁早。因为CNT是16位的如果两次读取间隔太长CNT回绕造成的误差会很大。以单圈1560个脉冲算高速旋转时每秒可能产生几万个计数如果100ms才读一次CNT早就转了好几圈根本没法算速度。所以读取周期要控制在10ms甚至更短或者用定时器级联组成32位计数器后者适合要求更高的场合。4. 测速算法三选一M法、T法、M/T法的适用边界4.1 M法测频法适合高速的数脉冲M法也叫测频法做法很简单固定一个时间窗口比如10ms统计这个窗口内编码器输出的脉冲个数N然后用公式算出转速。M法的公式推导很直观。电机的输出轴转一圈编码器产生的计数总数是P 码盘线数 × 倍频系数 × 减速比对于一个13线、4倍频、减速比30的电机P 13 × 4 × 30 1560。如果采样窗口是T秒单位是秒窗口内数到N个计数那么转速n单位RPM等于n N / (P × T) × 60代入T0.01、P1560就得到n N × 6000 / 1560 N × 3.846也就是说10ms窗口内每数到1个计数转速大约是3.846RPM。这个换算系数可以直接写在代码里省去每次运算的麻烦。M法的误差来源主要在于低速时脉冲个数少。比如转速只有10RPM时10ms窗口内大约只有0.26个计数要么数到0、要么偶尔数到1算出来的速度在0和3.8RPM之间跳变相对误差极大。所以M法天生适合中高速场景速度越低精度越差。热词里提到的STM32测频法指的就是M法。它的优点是实现简单、代码量小中高速下精度完全够用是大多数直流电机测速项目的首选。4.2 T法测周期法低速场景的救命稻草T法也叫测周期法思路和M法反过来不是数固定时间内的脉冲数而是测量相邻两个脉冲之间的时间间隔。脉冲间隔越长说明转速越慢间隔越短转速越快。T法的实现通常借助输入捕获功能。配置定时器一个通道为输入捕获捕获上升沿每次进入捕获中断时读取捕获寄存器得到上一次上升沿到本次上升沿之间的定时器时钟周期数ΔT。如果定时器时钟频率是F比如72MHz那么两个脉冲之间的真实时间就是ΔT/F秒。转速的计算公式是n 60 / (ΔT / F × P_origin)这里的P_origin是电机输出轴转一圈产生的原始脉冲数不是4倍频后的计数而是倍频系数为1时的脉冲数。比如13线、减速比30的电机原始脉冲数是13×30390那么n 60 / (ΔT / 72e6 × 390) 60 × 72e6 / (ΔT × 390)代入ΔT72000得到n约等于153.8RPM也就是说这个转速下脉冲间隔大约1ms。T法的优势在低速非常明显速度越低脉冲间隔越长测得的ΔT数值越大精度越高。但速度一高脉冲间隔变短ΔT太小测出的转速误差反而变大。比如转速3000RPM时脉冲间隔只有几微秒ΔT只有几百个时钟周期任何一点时钟误差都会被放大。T法还有个实现上的坑单个脉冲间隔会带来比较大的噪声抖动实际使用中通常测多个脉冲的时间间隔平均或者直接测固定数量的脉冲所需的总时间这样更稳定。4.3 M/T法宽范围测速的正解M法和T法各有短板M法高速准低速差T法低速准高速差。工程上如果要求从低速到高速全范围都比较准就要上M/T法。M/T法的核心思路是在一个固定闸门时间Ts内同时记录两个数据一是这个窗口内完整的脉冲个数M二是从窗口开始到窗口结束期间第一个脉冲和最后一个脉冲之间的精确时间ΔT。然后把M和ΔT关联起来算频率。简化版的M/T法公式可以写成n (M - 1) / (ΔT × P_origin) × 60这里的ΔT是窗口内最后一个脉冲的时间戳减去第一个脉冲的时间戳单位是秒。这样算出的转速不受窗口固定时间Ts的误差影响因为实际测量时间用的都是硬件时间戳精度很高。M/T法在STM32上的实现比前两个复杂一些。通常需要两个定时器配合一个做编码器计数另一个做时间测量还要合理处理脉冲时间戳的记录时机。如果项目对测速精度要求很高比如做伺服或者闭环速度控制M/T法才值得上。普通小车底盘、风机、水泵这类场景M法加一个合适的滤波算法已经足够没必要把系统搞复杂。4.4 换算公式与减速比为什么P值是最重要的参数不管用哪种算法最后都绕不开单圈总脉冲数P这个参数。它的准确与否直接决定了测速的绝对精度。拿JGA25-370电机为例商品信息里写的基本参数是减速比30:1编码器线数13。但这里有几个地方容易理解错。第一编码器线数13指的是电机本体高速端转一圈编码器输出13个A相脉冲周期。电机输出轴低速端转一圈高速端其实转了30圈所以输出轴一圈对应的原始脉冲数是13×30390。第二4倍频之后输出轴一圈的总计数是390×41560。这个1560就是我在代码里用的P值。第三如果你的测速对象不是电机的输出轴而是轮子那还要再乘一个轮子相关参数。比如轮子直径是65mm轮子转一圈前进的距离是π×65≈204.2mm换算线速度的公式是v n / 60 × π × D其中D是轮径n是轮轴转速v的单位和D一致。提示买电机时一定仔细看商品详情页的减速比和编码器线数不同批次可能不一样。如果资料没写可以用手摇电机输出轴一圈观察编码器输出波形的脉冲个数来实测推算。5. 完整代码走读从定时器初始化到转速换算5.1 CubeMX自动生成的定时器初始化在CubeMX里配置好TIM3为编码器模式后生成的初始化代码大概是下面这个样子。我用的芯片是STM32F103RCT6HAL库版本是1.8.0其他芯片和库版本的寄存器结构基本一致。/* tim.c 中生成的代码 */ void MX_TIM3_Init(void) { TIM_Encoder_InitTypeDef sConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim3.Instance TIM3; htim3.Init.Prescaler 0; htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 65535; htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; sConfig.EncoderMode TIM_ENCODERMODE_TI12; sConfig.IC1Polarity TIM_ICPOLARITY_RISING; sConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler TIM_ICPSC_DIV1; sConfig.IC1Filter 15; sConfig.IC2Polarity TIM_ICPOLARITY_RISING; sConfig.IC2Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler TIM_ICPSC_DIV1; sConfig.IC2Filter 15; if (HAL_TIM_Encoder_Init(htim3, sConfig) ! HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(htim3, sMasterConfig); }这里面最关键的参数就是TIM_ENCODERMODE_TI12它对应4倍频模式。IC1Filter和IC2Filter都设成了15这是前面说的输入滤波能过滤掉A、B相上的毛刺信号。5.2 定时中断读取与速度计算启动编码器定时器之后再配置一个基础定时器TIM6做10ms周期中断用来周期性读取CNT寄存器并计算转速。/* main.c 中的启动代码 */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM3_Init(); MX_TIM6_Init(); HAL_TIM_Encoder_Start(htim3, TIM_CHANNEL_ALL); HAL_TIM_Base_Start_IT(htim6); while (1) { /* 主循环可以干别的事情速度值在中断里更新 */ } }速度计算的核心逻辑放在TIM6的中断回调函数里。这里有一个关键点读取CNT时先把uint16_t强转成int16_t再处理。这样做的原因是CNT在编码器模式下既可能向上加也可能向下减直接读回的无符号数在正反转切换时很难处理转成int16_t后差值的正负就自然代表了计数方向而且只要两次采样间隔内的计数变化不超过32767就不会出错。/* encoder.c 中的速度计算 */ #define ENCODER_LINES 13 #define GEAR_RATIO 30 #define PULSES_PER_REV (ENCODER_LINES * 4 * GEAR_RATIO) // 1560 static int16_t last_cnt 0; static float speed_filtered 0.0f; volatile float motor_speed_rpm 0.0f; volatile int16_t motor_speed_dir 1; void Motor_Speed_Calc(void) { int16_t now_cnt (int16_t)__HAL_TIM_GET_COUNTER(htim3); int16_t delta now_cnt - last_cnt; last_cnt now_cnt; if (delta 0) { motor_speed_dir 1; } else { motor_speed_dir -1; delta -delta; } /* 10ms窗口RPM delta * 6000 / 1560 */ float speed_raw (float)delta * 6000.0f / (float)PULSES_PER_REV; /* 一阶低通滤波系数0.2能明显抑制转速波动 */ speed_filtered 0.8f * speed_filtered 0.2f * speed_raw; motor_speed_rpm speed_filtered; }/* stm32f1xx_it.c 或回调文件中 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM6) { Motor_Speed_Calc(); } }5.3 方向判断与数据滤波方向判断有两种做法。上面代码里是用两次计数的差值正负来判断方向简单直观。另一种更可靠的做法是直接读定时器的计数方向标志位if (READ_REG(htim3.Instance-CR1) TIM_CR1_DIR) { motor_speed_dir -1; } else { motor_speed_dir 1; }这个方法的优点是实时性更好不受采样周期影响缺点是代码可读性稍差。实际项目里两种我都用过如果采样周期稳定在10ms量级差值法完全够用。滤波这一步千万不要省。电机旋转本身就有机械波动编码器输出脉冲也不可能绝对均匀直接拿原始计算值做控制速度曲线会像锯齿一样跳。我惯用的一阶低通滤波系数0.2到0.3之间比较合适10ms采样周期下滤波时间常数大约是40到50ms既能平滑掉高频抖动又不会让响应慢到跟不上转速变化。如果需要更平滑可以叠一个滑动平均但注意会增加延迟。6. 实测数据与踩坑实录转速值为什么一直在跳6.1 计数器溢出这个隐藏的大坑第一次用编码器模式跑起来我发现一个诡异的现象电机正转的时候转速偶尔会突然跳成一个巨大无比的值然后又恢复正常。查了半天才发现是计数器溢出问题。CNT是16位寄存器最大值65535。电机高速旋转时10ms窗口内产生的计数可能达到好几千但绝对值不会超过65535所以10ms读取一次不会溢出。问题出在电机只反转不转正、停在某个位置附近抖动或者代码运行到读取语句前被更高优先级中断打断导致两次读取CNT的间隔远大于10msCNT已经回绕了一圈甚至几圈。解决思路有两条。最简单是保证读取周期稳定别在中断里放耗时的操作。更彻底的方案是用32位计数把两个定时器级联起来或者读取时做特殊处理比如用临界区保护。对绝大多数项目保证10ms定时中断稳定触发就够了这条坑我列出来主要是提醒你别在主循环里也去读CNT算速度两个地方抢着读很容易踩到溢出问题。6.2 电机一启动转速值乱跳噪声惹的祸有一次我把编码器模块直接固定在电机尾端离驱动板PWM输出线很近结果一启动电机转速值就无规律乱跳测得的正反转方向也是乱变。用示波器看A、B相波形能看到在正常方波的边沿上叠加了很多几十纳秒宽的小毛刺。这些毛刺被定时器的编码器模式当成有效跳变沿计数器就被无规律地加减转速自然乱跳。我的处理办法分三步。第一步把CubeMX里IC1Filter和IC2Filter从0改到15毛刺被过滤掉大半。第二步在编码器电源引脚旁边加104电容进一步吸收高频噪声。第三步把PWM驱动线和编码器信号线物理分开重新布线。三步做完示波器上的毛刺基本消失转速值也稳定了。注意输入滤波不是越大越好。滤波值太大会把正常的高频脉冲一起滤掉导致计数丢失。建议先用电机的最大转速算一下A相的最高频率再根据定时器时钟频率选择滤波值原则是滤波采样频率至少是信号最高频率的5倍以上。6.3 正转时偶尔报负速度方向判断的边界情况还有一个很典型的坑电机明明一直在正转方向标志却偶尔变成负的持续一小段时间又变回来。出现这个现象有两个常见原因。一是编码器A、B相接线接触不良信号偶尔丢一拍硬件计数方向就错了二是电源纹波导致计数器误判。排查的时候先用示波器看A、B相有没有异常抖动再看供电是否稳定。如果硬件没问题就要在软件里做方向死区处理只有连续若干次采样都是反向才真正切换方向标志。这个方法可以避免单次误判导致的方向抖动。另外一个方向相关的小技巧是不用改代码直接交换A、B两根线的接线就能改变方向判断逻辑。系统联调的时候如果发现方向反了先别急着改代码把A、B对调一下往往更快。6.4 定时器资源冲突与开发环境坑编码器模式占用了定时器的CH1和CH2这个定时器就不能再输出PWM了。同一个定时器既做编码器计数又做PWM驱动硬件上就是不支持的。所以在系统设计阶段就要把定时器资源分配好。我常用的分配方案是定时器用途说明TIM1PWM输出高级定时器用于电机驱动频率20kHzTIM3编码器模式左轮CH1接A相CH2接B相TIM4编码器模式右轮CH1接A相CH2接B相TIM610ms定时中断基础定时器只做周期触发这样左右轮各占一个定时器PWM另用高级定时器互不干扰。如果用到的电机数量更多就得考虑定时器不够用的情况这时才需要回头评估外部中断方案的可行性把低速的编码器放在中断里高速的放在定时器模式里。另外提一个开发环境的小问题。有些读者会遇到Keil装了却识别不到STM32芯片或者ST-Link连接不上目标板。这个多数是ST-Link驱动没装好、或者开发板没共地的问题。用ST-Link调试编码器项目时务必确认编码器、电机驱动板和ST-Link三者的GND都连在一起否则调试器可能时好时坏编码器信号更是没法看。6.5 实测数据M法和T法在不同转速下的表现为了直观展示算法选型的差异我在同一个JGA25-370电机上分别用M法和T法测了一组数据采样周期都是10ms参考转速用高精度光学转速表标定。目标转速M法测量值T法测量值说明约30RPM27~35RPM波动29.4~30.6RPM稳定M法10ms窗口内脉冲约2~3个跳动明显约150RPM148~153RPM稳定146~159RPM波动两者都可用T法抖动略大约600RPM598~602RPM稳定590~620RPM波动高速下T法脉冲间隔太短误差放大约1500RPM1495~1505RPM稳定明显偏慢T法在当前配置下已基本不可用这组数据印证了前面的理论推导低速用T法准高速用M法稳。如果只打算用一种算法个人建议M法配一阶低通滤波因为大部分电机测速应用都在中高速区间M法实现简单、稳定性好低速误差通过滤波也能缓解大半。真要做宽范围高精度测速再考虑上M/T法。最后再分享一个我实际调试中觉得特别实用的小技巧把算出来的转速值通过串口以固定频率发到上位机画波形或者直接通过虚拟示波器插件看曲线调试效率比盯着串口助手的数字高太多了。你能直观看到电机从启动、加速到稳定的完整过程也能马上发现滤波参数调得合不合适强烈建议在做闭环控制之前先把这个测速链路调稳。
返回列表