STM32CubeMX实战:基于定时器编码器模式实现直流电机精准测速与方向控制

张开发
2026/6/11 11:51:54 15 分钟阅读
STM32CubeMX实战:基于定时器编码器模式实现直流电机精准测速与方向控制
1. 硬件准备与接线指南搞电机控制最头疼的就是硬件连接我第一次玩编码器电机时光接线就折腾了大半天。咱们这次用的主角是带AB相霍尔编码器的直流减速电机减速比1:30意味着输出轴转1圈电机内部要转30圈。编码器是13位的所以电机每转一圈会产生8192个脉冲2的13次方。这里有个坑要注意很多新手会忽略减速比对脉冲数的影响实际计算转速时要记得把减速比算进去。驱动模块选的是经典的L298N这玩意儿就像电机的方向盘——PWM控制速度IN1/IN2控制方向。我的独门接线技巧是电机A/B相直接接PE9和PE11定时器1的编码器接口L298N的ENA接PWM输出我用的是TIM5_CH2IN1/IN2接普通GPIOPC4/PC5关键三个地线必须接在一起电机电源地、L298N地、STM32地提示实验室里80%的编码器问题都是接地不良导致的如果发现读数跳变先检查所有地线是否可靠连接电源方面建议用3节锂电池串联12V实测带负载时电压会降到11V左右正好适合电机工作。千万别直接用开发板的5V供电我有次偷懒这么干结果电机一转开发板就重启后来发现是电流不够。2. CubeMX定时器配置详解打开CubeMX就像玩拼图每个参数都得严丝合缝。先配置TIM1的编码器模式在Parameter Settings里选Encoder ModeCounter Period设20000这个值决定了计数范围滤波器我一般设15能有效消抖分频系数保持默认1分频这里有个血泪教训第一次我把Counter Period设成65535结果电机高速旋转时计数器溢出导致读数错误。后来改成20000并配合溢出中断稳如老狗。具体原理就像汽车里程表超过最大值就归零我们需要记录这个翻表次数。PWM配置更讲究TIM5-PWM频率 100Hz TIM5-Pulse 1500 (占空比50%)为什么选100Hz太低了电机会咔咔响太高了L298N发热严重。实测100Hz时电机运转最平滑而且不会让驱动模块烫手。3. 编码器数据处理核心代码代码部分我封装成了Motor模块先看头文件里的关键定义#define RR 30u // 减速比 #define RELOADVALUE __HAL_TIM_GetAutoreload(htim1) #define COUNTERNUM __HAL_TIM_GetCounter(htim1) typedef struct _Motor { int32_t lastAngle; // 上次角度 int32_t totalAngle; // 累计角度 int16_t loopNum; // 溢出计数 float speed; // 转速(RPM) }Motor;这个结构体设计暗藏玄机用loopNum记录溢出次数配合totalAngle就能实现32位扩展计数再也不怕高速旋转时数据丢失。速度计算才是重头戏看这个公式motor.speed (float)(motor.totalAngle - motor.lastAngle)/(4*13*RR)*6000;解释下各部分含义4AB相编码器4倍频13编码器位数(2^138192PPR)RR减速比306000将10ms采样周期转换为分钟(1000ms/10ms*60s)中断处理才是灵魂所在我优化过的版本长这样void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htimhtim6){ // 10ms定时中断 int16_t pluse COUNTERNUM - RELOADVALUE/2; motor.totalAngle pluse motor.loopNum * RELOADVALUE/2; // 转速计算 motor.speed (motor.totalAngle - motor.lastAngle)*60.0f/(4*13*RR*0.01); motor.lastAngle motor.totalAngle; } else if(htimhtim1){ // 编码器溢出中断 if(COUNTERNUM 10000) motor.loopNum; else if(COUNTERNUM 10000) motor.loopNum--; __HAL_TIM_SetCounter(htim1, 10000); } }4. 调试技巧与性能优化调试编码器就像中医把脉得会望闻问切。分享几个实用技巧示波器诊断法用示波器看AB相信号正常应该是90度相位差的正弦波。如果波形畸变可能是电源干扰或接线问题。串口绘图大法用J-Scope或SerialPlot实时显示转速曲线比看原始数据直观多了。我常用的数据格式printf(%.1f\n,motor.speed); // 直接输出浮点数参数调优经验值滤波器值低速时设15高速时设5采样周期高速电机用5ms低速用20ms死区补偿正反转切换时加50ms延时性能优化方面我总结了三板斧中断优化把编码器中断优先级设为最高防止被其他中断打断计算优化用移位代替乘除比如/8192改成13滤波算法加个一阶低通滤波代码超简单motor.speed 0.9*last_speed 0.1*current_speed;最后说说常见坑点电机反转时转速为负值UI显示时要取绝对值低速时可能出现抖动可以设置个最小阈值长时间运行要注意loopNum溢出我遇到过连续转1小时后数据异常的bug5. 进阶应用从测速到控制掌握了测速只是第一步接下来可以玩更刺激的——PID控制。先透露个简单的位置式PID实现typedef struct { float Kp,Ki,Kd; float error,last_error,integral; }PID; float PID_Calculate(PID* pid, float target, float feedback){ pid-error target - feedback; pid-integral pid-error; float output pid-Kp*pid-error pid-Ki*pid-integral pid-Kd*(pid-error-pid-last_error); pid-last_error pid-error; return output; }使用时只需要PID speed_pid {0.5, 0.01, 0.2}; float pwm PID_Calculate(speed_pid, target_speed, motor.speed); __HAL_TIM_SET_COMPARE(htim5, TIM_CHANNEL_2, pwm);实测效果空载时速度波动能控制在±2RPM以内带负载变化时响应时间约0.3秒。当然PID参数整定又是另一个深坑建议先用Ziegler-Nichols方法初步整定再微调。

更多文章