ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32控制TT马达:从H桥到PWM调速与编码器闭环

STM32控制TT马达:从H桥到PWM调速与编码器闭环 STM32这个小东西到底能带得动多大的电机这是很多刚接触嵌入式控制的朋友爱问的问题。答案是带不动但搭配上驱动模块它可以精确控制几乎任何直流电机——包括在各种小车套件里最常见的TT马达。这篇文章就围绕TT马达直流电机在STM32平台上的完整控制流程来聊从驱动芯片选型、H桥原理、CubeMX配置到HAL库代码实现再到带编码器版本的速度闭环思路最后把我实际踩过的坑一并说清楚。无论是做两轮差速小车、智能台灯、宿舍物联网设备还是凑一个基于STM32的毕业设计你都可以直接参考这套方案。1. 为什么入门小车总绕不开TT马达从选型逻辑说起1.1 TT马达到底是个什么东西TT马达严格来说并不是一个官方标准型号而是指一种常见微型直流减速电机模组通常由一颗130或180规格的空心杯直流电机加上一套塑料减速齿轮箱组成。它的外观特征是那个标志性的圆筒形黑色或透明外壳输出轴大多采用D型或者带有双轴头设计一边输出动力另一边可以接编码器磁环或码盘。TT马达的工作电压一般在3V到12V之间我手上这批额定6V空载转速根据减速比不同在80到300转/分钟之间浮动。这类电机之所以在STM32教学和竞赛小车中被大量使用并不是因为它性能有多强而是因为它的性价比和折腾空间实在太好了。十几块钱一个坏了不心疼扭矩虽然不强但推动一个几百克的小车绰绰有余结构足够结实学生们摔摔打打也没事。更重要的是选它做教学案例可以把「H桥控制方向」「PWM调速」「正交编码器测速」这些核心知识点全部串联起来学完它再去碰伺服电机、步进电机、无刷电机思路是相通的。1.2 普通版和带编码器版本怎么选买TT马达的时候你会发现有两个主流版本普通版和带编码器版。普通版只有两个电源引脚给上电压就转调换极性就反转控制逻辑最简单适合只想让小车跑起来、做基础开关控制和PWM调速的场景。带编码器版本在后盖上集成了一块霍尔传感器小板通常引出5根线电机正负极两根编码器电源VCC和GND两根再加一根信号输出线或者A、B两个正交信号。如果你用的是双减版即两个输出轴一般会在尾部轴套上安装一个6极或12极的磁环霍尔传感器检测磁极变化每一圈输出若干个脉冲。带编码器的版本贵几块钱但意义完全不同——有了它你就能够在STM32上读取电机实际转速实现闭环控制让小车在负载变化时依然能稳住速度。我的建议很直接如果不差这十几块钱直接买带编码器的版本。哪怕你现在只需要开环控制后面想做PID、做循迹、做避障速度控制都用得上。一步到位比以后再拆车换电机省心得多。1.3 电机驱动板的选择L298N、TB6612、DRV8833谁更合适STM32的GPIO输出电流只有几毫安到20毫安左右而TT马达正常工作时电流在100到300毫安之间堵转瞬间可能冲到1安培以上所以直接接IO口一定烧芯片。必须用驱动模块做功率放大。目前市面上最常见的是三款L298N、TB6612FNG、DRV8833。L298N是老古董了双H桥能驱动大电流电机但压降大约2V发热严重而且板子又大又重逻辑电平兼容性也一般在TT马达这种小功率场景下我是完全不推荐的除非你手头正好有现成的。TB6612FNG是我最常用的一款东芝出品内置LDO逻辑电压2.7V到5.5V电机电压最高15V单路连续驱动电流1.2A峰值3.2A。它的导通压降只有0.3V左右同样电压下电机实际获得的电压明显比L298N高转速更接近理想值发热也小得多。DRV8833是TI的芯片和TB6612定位接近但它的逻辑输入直接兼容1.8V到7V对于用3.3V供电的STM32来说更省心不需要像TB6612那样担心逻辑电压范围问题。此外DRV8833还内置电流限制和过热保护抗折腾能力更强。做一个简单总结驱动模块单路峰值电流逻辑电压压降适用建议L298N2A4.5~5.5V约2V大电流电机TT马达场景不推荐TB6612FNG3.2A2.7~5.5V约0.3VTT马达首选性能均衡DRV88332A1.8~7V约0.3V3.3V逻辑直连保护完善如果你用的是TB6612注意它的VM引脚接电池正极VCC引脚接3.3V或者5V供内部逻辑使用两个供电缺一不可。新手最容易在这里出问题——只接了VM没接VCC芯片完全没反应电机纹丝不动。2. H桥与PWM控制直流电机绕不开的两个核心概念2.1 为什么STM32的GPIO不能直接驱动电机很多人不理解为什么一个简单的电机控制非要经过驱动芯片这一层。除了前面说的电流不够还有一个核心原因直流电机的本质是一个感性负载而且它的反电动势在运行时会随着转速升高而升高。当你直接拿GPIO去驱动电机时电机在换向、启停瞬间产生的反向尖峰电压可能达到电源电压的数倍轻则干扰芯片逻辑重则直接把IO口打坏。驱动芯片另一个容易被忽略的作用是「电平隔离」。STM32的IO只输出3.3V逻辑电平但电机工作电压可能是6V、9V甚至12V两个不同的电压域之间如果直接相连逻辑电路会被彻底击穿。驱动芯片内部把逻辑控制和功率输出分开两边各自工作在自己的电压域里你只需要用3.3V的逻辑信号去控制低功率侧高功率侧独立供电这才是安全的连接方式。2.2 方向控制H桥的导通逻辑H桥之所以叫H桥是因为四个开关管两两一组在电路图上正好摆出一个字母H的形状电机横躺在中间那一横。当左上和右下两个开关导通时电流从左侧流进电机、右侧流出电机正转当右上和左下导通时电流方向反过来电机反转。两组开关绝对不能同时导通同一侧的上下两个管子否则等于把电源直接短接这个状态叫「直通」瞬间电流会烧掉驱动芯片。在TB6612上这四个开关被封装成了标准逻辑引脚你用两个引脚就能控制一组H桥IN1和IN2。具体逻辑是IN11 (高电平), IN20 (低电平)电机正转IN10, IN21电机反转IN1IN20电机自由停止滑行IN1IN21电机刹车短接制动这里有个值得留意的细节刹车和自由停止在物理效果上完全不同。自由停止时电机电源被切断转子依靠惯性继续转慢慢停下来刹车时电机两端被短接转子转动产生的反电动势形成涡流制动电机瞬间停下。如果你的小车需要精确停车要用刹车模式而不是自由停止。2.3 速度控制PWM占空比与电机转速的关系方向控制解决「往哪转」的问题速度控制解决「转多快」的问题。直流电机的转速近似正比于两端平均电压而PWM脉冲宽度调制就是用高频开关的方式去调节平均电压。假设供电6VPWM占空比50%相当于电机在6V和0V之间高速切换平均电压约3V转速大约减半。占空比和转速并不是严格的线性关系。因为在占空比非常低的时候电机上的电压不足以克服静摩擦和电枢电阻电机根本不转存在一个死区。比如我用6V供电时占空比低于15%左右电机基本处在蠕动或者静止状态占空比高于85%之后转速上升开始变得平缓因为反电动势接近供电电压电枢电流变小输出扭矩跟着下降。实际工程中我一般把占空比控制范围设在20%到90%之间低于下限用停止代替高于上限直接给满占空比。3. 接线与CubeMX配置把硬件跑通的关键一步3.1 以TB6612为例的完整接线清单我下面以TB6612搭配带编码器TT马达和STM32F103C8T6蓝板为例给出一套完整的接线方案。首先说主电源两节18650电池串联约7.4V或者四节五号电池约6V正极接TB6612的VM引脚负极接GND。这里有一个非常实用的建议电源的GND必须和STM32的GND共地否则逻辑信号没有参考地电机会完全不听使唤或者乱转。逻辑供电方面VCC引脚接STM32的3.3V输出即可或者5V取决于你的板子具体逻辑电压要求GND接公共地。控制信号部分左电机接AIN1、AIN2、PWMA三个引脚右电机接BIN1、BIN2、PWMB。我这里配置的是PA6 - AIN1PA7 - AIN2PA8 - PWMAPB0 - BIN1PB1 - BIN2PA9 - PWMB编码器部分每路电机引出一个霍尔信号线分别接到STM32的PB4和PB5上这两个引脚我后面会配上定时器的输入捕获功能用来做正交解码。编码器供电接3.3VGND共地。3.2 定时器PWM通道的选择思路在CubeMX里配置PWM很多人喜欢随便选一个定时器通道结果后来发现和其他功能冲突来回改引脚浪费时间。我的习惯是先看数据手册里的定时器通道映射表把PWM输出引脚选在和编码器、串口、I2C不冲突的引脚上。比如上面提到的PA8对应TIM1_CH1PA9对应TIM1_CH2。TIM1属于高级定时器它的PWM输出在有一些型号上需要额外注意重复计数寄存器设置否则波形出不来。如果不想碰这些麻烦也可以选普通定时器TIM2、TIM3、TIM4的PWM通道配置上更省心。以TIM2为例PA0是CH1PA1是CH2PA2是CH3PA3是CH4。PWM频率的选择也很关键。TT马达的机械时间常数比较长一般使用10kHz到20kHz的PWM频率就足够了。太高了驱动芯片的开关损耗增加太低了电机运转会产生明显的顿挫感和啸叫——那种「滋滋滋」的声音其实就是在音频范围内听着特别难受。我在实际调试时常用16kHz一方面超过了人耳最敏感的频段另一方面在驱动芯片的开关能力范围内。设置方法是在CubeMX的定时器配置里把Prescaler和Counter Period算好例如72MHz主频下Prescaler设为71Counter Period设为999则PWM频率是72MHz / (72 * 1000) 1kHz。要16kHz就把Period调到44左右Prescaler设为71得到72M / (72 * 45) ≈ 22.2kHz。实际算一下再填不要照抄别人的参数。3.3 GPIO模式配置细节方向控制引脚AIN1、AIN2、BIN1、BIN2在CubeMX里要配置成GPIO_Output模式初始电平可以设成Low。这里有一个很多人忽略的细节虽然把它们配成推挽输出Push-Pull是最常见的做法但TT马达驱动芯片的输入引脚一般内部带下拉电阻所以即使用开漏输出配合外部上拉也能工作但推挽是最稳妥的选择响应速度快驱动能力强。编码器信号引脚比较特别。霍尔编码器输出的信号有些是开漏结构必须配合上拉电阻才能读到正确的高电平。STM32的内部上拉电阻大约在30到50k欧姆左右在低速转动时完全够用。所以在CubeMX里配置编码器引脚时除了要选对复用功能后面会细说还要把GPIO的上拉Pull-up打开否则你会看到编码器读数极其不稳定或者干脆一只为0、另外一只莫名其妙乱跳。4. 代码实现正向反转、调速和刹车4.1 用HAL库封装一个电机控制结构体配置完CubeMX生成工程之后先别忙着堆代码我建议先把电机控制抽象成一个小结构体。这样做的好处是代码可读性高后面加电机、换引脚、扩展功能都方便。结构体定义大概长这样typedef struct { TIM_HandleTypeDef *htim; uint32_t Channel; GPIO_TypeDef *IN1_Port; uint16_t IN1_Pin; GPIO_TypeDef *IN2_Port; uint16_t IN2_Pin; GPIO_TypeDef *PWM_Port; uint16_t PWM_Pin; } Motor_TypeDef;然后写一个初始化函数把引脚关系和定时器通道填进去。这一步看起来多此一举但在代码量上来之后它的价值会非常明显。想象一下你写了五个控制小车的函数突然发现电机A和电机B接反了如果没有封装你得一个一个函数去改引脚有了结构体改一行初始化就行。初始化时记得用HAL_TIM_PWM_Start开启PWM输出通道同时把初始占空比设置为0防止上电瞬间电机突然窜动。4.2 基础动作实现正转反转停止封装完结构体就可以写核心的三个控制函数了。正转就是IN1置高、IN2置低然后设置PWM占空比void Motor_Forward(Motor_TypeDef *motor, uint16_t speed) { HAL_GPIO_WritePin(motor-IN1_Port, motor-IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(motor-IN2_Port, motor-IN2_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(motor-htim, motor-Channel, speed); }反转就是把IN1和IN2电平对调。停止分两种自由停止是IN1、IN2都置低刹车是两个都置高。这段逻辑本身不复杂但有一个细节值得展开讲——PWM占空比的值范围。在CubeMX里如果PWM的Period设置成了999那么占空比范围就是0到999对应0%到100%。不要在代码里直接把占空比写成像Arduino那样的0到255除非你明确知道你用的Period是255。这个错误我第一次写STM32时就犯过电机输出明显不足调了半天才发现是周期不匹配。4.3 多级调速和Soft Start开环控制下电机从0突加到最大占空比电流会瞬间飙升机械冲击也很大表现在小车上就是猛地一下窜出去。在竞赛和演示场景里这种突变的输出会让小车很难控制跑起来一卡一卡的。解决方法是做一个简单的软启动用循环把占空比逐步加到目标值void Motor_Speed_Ramp(Motor_TypeDef *motor, uint16_t target, uint16_t step_delay) { uint16_t current __HAL_TIM_GET_COMPARE(motor-htim, motor-Channel); while (current target) { current 1; __HAL_TIM_SET_COMPARE(motor-htim, motor-Channel, current); HAL_Delay(step_delay); } }这里的step_delay决定了加速的快慢。我测试下来的经验值是每步延时2到5毫秒这样既不会感觉反应迟钝又能明显减少起步冲击。减速过程同理也可以反着写一个Ramp Down。如果你是在做PID闭环控制这种软启动可以用来做平滑启动防止积分饱和和超调。注意软启动最好配合方向引脚先稳定、PWM后变化的时序即先设置IN1/IN2方向再开始PWM占空比爬坡。5. 进阶带编码器的TT马达与速度闭环5.1 编码器输出是什么信号带编码器的TT马达尾部有一个霍尔传感器小板磁环套在电机转轴上注意不是输出轴上所以编码器测出来的是电机高速侧的速度而不是经过减速齿轮之后的输出轴速度。做速度闭环时你需要把这个测出来的速度换算成输出轴的每分钟转速方法就是用测到的脉冲频率除以减速比再除以每圈脉冲数。以我常用的一个编码器TT马达为例减速比1:30磁环6极霍尔传感器每个机械周期输出2个脉冲因为霍尔信号是方波一个周期内高低电平各触发一次沿。所以电机输出轴旋转一圈编码器输出的脉冲数是30乘以2等于60个脉冲。如果你在一秒内数到了600个脉冲那么输出轴转速就是600 / 60 10转/秒也就是600RPM。这个换算关系务必想清楚否则后面PID的目标速度根本对不上。5.2 用定时器输入捕获读取速度读取速度最常用的方式是定时器输入捕获加中断计数。在CubeMX里把定时器设置成输入捕获模式上升沿触发中断然后在中断回调函数里累加一个全局变量。每隔固定的时间比如50毫秒把累加值读出来清空重新计数这个值除以时间间隔就是脉冲频率。我在实际使用中发现用中断计数在电机低速时没什么问题但高速时脉冲频率可能达到几千赫兹频繁进中断会增加CPU负载尤其在同时处理电机、串口、传感器的情况下可能引发一些时序问题。更优雅的方案是利用STM32定时器的编码器接口模式Encoder Mode。把两个带正交编码输出的信号接到同一个定时器的CH1和CH2上配置成Encoder Mode TI1 and TI2定时器的计数器就可以直接跟随电机转动的方向和角度自动加减计数完全不占用CPU时间。当然TT马达的编码器多数情况下只输出单路信号用定时器的编码器接口模式去解单路信号需要手动把两路信号合并或者只接一路捕获精度会受一些影响。对于大多数小车项目直接用外部中断EXTI计数就够了而且代码容易理解。拿来学习的效果反而比一上来就用高大上的编码器模式更好。5.3 PID速度闭环的思路有了速度反馈就能实现闭速度环。PID控制的思路不复杂设定一个目标转速把实际转速和目标转速的偏差送入控制器控制器输出一个PWM占空比的调整量。比例项P负责快速响应偏差积分项I负责消除静态误差比如负载增加导致的转速跌落微分项D负责抑制超调。对TT马达这种小惯性系统我通常从PD控制开始调也就是把积分项去掉看看稳态误差能不能接受。调参顺序是先设一个较小的P让系统稳定不震荡再逐步增大P直到开始出现轻微震荡然后回退20%左右接着加一点I让稳态误差归零最后加一点点D让阶跃响应没那么冲。这里有一个经验值供参考对常见的4轮差速小车用50毫秒的采样周期P大概在30到80之间I在0.1到0.5之间D可以先设为0。不要一上来就追求完美的PID参数先让闭环在工作点附近稳定然后再调整目标速度看响应曲线。很多人在仿真里调参用得飞起一到真车上就崩原因就是真车的摩擦力、供电电压波动、齿轮间隙带来的非线性都在变。在实践中一点一点加参数比任何仿真都靠谱。6. 实测中容易踩的坑从电机不转到上位机读不到数6.1 电机不动先查这些遇到电机不转90%的情况不是代码问题而是硬件连接或者供电问题。我总结了排查顺序按这个顺序来基本十分钟内能找到问题第一步用万用表量驱动板的VM和GND之间有没有正常的电机供电电压。很多电池盒的开关接触不良或者电池没电了电压直接掉了电机自然不动。第二步量VCC引脚有没有电压。TB6612如果VCC没接芯片内部逻辑完全不工作你输入引脚给再高的电平也没反应。第三步检查共地。用万用表蜂鸣档量一下驱动板GND和STM32的GND是不是通的不通的话一切控制信号都是浮空状态。第四步把PWM占空比手动设成最大值排除占空比太小导致的死区问题。如果以上都正常再看代码。用调试器在Motor_Forward函数里打断点确认IN1、IN2引脚的电平确实被拉高和拉低了。如果你发现电平正常但电机依然不转那就查驱动芯片的引脚有没有虚焊。有一种很经典的情况电机线插在驱动板的端子排上没插紧看起来插进去了实际上只接触了一半时断时续电机表现为偶尔抖动但不持续旋转。6.2 PWM频率对噪音和扭矩的影响有一段时间我做的两轮小车总在低速时发出尖锐的啸叫声一开始以为是电机齿轮的问题后来用示波器一看PWM频率只有500Hz正好落在人耳最敏感的频段里。把频率提到16kHz之后啸叫立即消失电机的低速扭矩也更稳定了。但频率不是越高越好。驱动芯片的栅极驱动能力有限频率太高的话开关损耗会变得明显芯片发热加剧同时电机的电感感抗在高频下更大电流纹波虽然减小了但等效平均电流也会跟着下降输出扭矩会打折。综合来看10kHz到20kHz是一个比较甜点的区间具体用多少可以结合你的驱动芯片手册来选。DRV8833可以支持到100kHz以上的PWM但对TT马达来说没这个必要。6.3 供电不稳导致的复位和异常TT马达启动瞬间电流很大如果供电电源内阻高比如质量差的电池或者太细的杜邦线电压会被瞬间拉低。STM32的3.3V稳压器在输入电压跌落到某个阈值以下时会触发掉电复位表现就是小车一加速就重启、代码跑着跑着突然回到main函数开头。这种情况排查起来很容易被误判成代码逻辑问题。我遇到过一次代码加了很多调试打印小车每次启动都会死机重启后来用示波器一看VDD在电机启动瞬间跌落到了2.8V。解决方案有几个一是加一个大一点的大容量电容例如在电源输入端并联一个470uF的电解电容和0.1uF的陶瓷电容吸收启动瞬间的电流尖峰二是缩短电池到驱动板的导线长度换成粗一点的硅胶线三是在软件层面让电机逐个启动不要两个电机同时从0猛加速。这里讲一个实用组合一个1000uF电解电容和一颗100nF瓷片电容并联在STM32电源入口处成本极低却能解决大量莫名其妙的问题。记住电容耐压要超过电源最高电压否则电容本身会炸。6.4 编码器读数异常的排查思路编码器读数不对常见的有三种表现。第一种是某一侧电机转动时读数恒为0优先检查霍尔传感器的供电和共地。编码器小板上的VCC接3.3VGND必须和STM32共地我曾经因为忘记共地编码器输出信号一直浮在中间电平计数完全不工作。第二种是两个方向读数没有区别也就是反向时读数不会减少。这种情况通常是你在中断里只做了加计数没有考虑方向判断。使用外部中断加旋转方向检测的方法如果编码器有两路信号通过判断A路和B路的相位关系来确定方向如果只有单路信号就需要外加一个方向引脚来判断电机转向。第三种是读数抖动转动一圈读了比理论值多或者少很多多半是信号毛刺导致的。处理办法是在中断里加简单的消抖比如连续两次触发间隔小于某个时间就认为是毛刺直接忽略或者使用定时器的输入滤波功能在CubeMX里配置数字滤波器。曾经有个项目堵转时编码器读数猛然暴涨追踪后发现是电机电流尖峰感应出的电磁干扰被串入了编码器信号线。解决办法很简单把编码器信号线和电机电源线分开走线不要绑在一起问题立刻消失。这个在画PCB和整理飞线时都要留意。7. 从模块到整车一套有效的控制思路把单个TT马达控制好只是第一步真正有意思的是把它们组合起来做完整的应用。两轮差速小车的运动学逻辑是左轮和右轮同速同向转动小车直线前进两轮反向转动小车原地旋转两轮同向不同速小车走弧线。这个逻辑用封装的电机控制函数非常好实现只需要分别控制两个电机的方向和速度不需要额外的运动学方程对新手非常友好。如果你要做的不是小车而是其他场景比如智能台灯的亮度调节、鱼缸的自动投喂、宿舍灯的远程控制核心逻辑也是一样的用STM32的PWM输出控制一个执行器通过传感器或者无线模块获取信息形成一个闭环系统。TT马达在这里面扮演的角色就是一个结构简单、反馈明确、适合用来验证整个嵌入式控制思路的「标准负载」。从这个角度说如果你能把TT马达在STM32上控制得明白通透后面迁到其他电机、其他平台你会发现套路高度相似先看驱动芯片手册确认逻辑引脚怎么控制再看有无反馈信号有反馈就做闭环先开环跑通再逐步加入速度环、位置环。整个学习曲线在TT马达这里被压得很短收益却很高。最后分享一个我自己的习惯无论做多小的项目我都会在上位机上打印出当前PWM占空比、实际转速和供电电压这三个值。你不需要一次看懂太多但当项目出问题时这份数据记录往往能帮你快速定位是供电问题、控制问题还是机械问题。磨刀不误砍柴工嵌入式控制这一行数据和实物永远比感觉可靠。
返回列表