ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32控制TT马达全指南:PWM调速、H桥驱动与闭环控制

2026/9/6 8:49:18 拓冰建站 浏览量
STM32控制TT马达全指南:PWM调速、H桥驱动与闭环控制 TT马达这名字一出来玩过智能小车、做过电赛或者课程设计的兄弟估计都秒懂。这货在STM32项目里出镜率实在太高了——便宜、皮实、接线简单拧上轮子就能跑是绝大多数两轮差速小车、机械臂关节、云台原型机的首选动力源。我之前在好几个项目里都用TT马达做执行机构从最基础的PWM调速到后来配合编码器做闭环踩过的坑和攒下的经验都不少。这篇就把STM32控制TT马达这事从头到尾捋一遍从硬件选型、驱动原理到CubeMX配置和HAL库代码包括那些文档里不会写但实际一定会遇到的坑一次性给你讲透。先说清楚TT马达是个什么东西。TT马达其实是玩具级直流减速电机的一种统称得名于早期流行的TT Motor型号后来成了这种带塑料减速箱、输出轴直径3mm、工作电压3-6V的直流电机的代名词。它和有刷直流电机是同一个物种内部有电刷换向通过改变施加在电机两端的电压大小和极性来控制转速与方向。与无刷直流电机相比TT马达结构简单、控制门槛低不需要电子换相器两根线就能转。代价是寿命和效率不如无刷电刷磨损后会有碳粉积累但几块钱一个的玩意坏了直接换不心疼。控制TT马达的核心就两件事调速和换向。调速一般用PWM脉宽调制换向靠H桥电路。STM32通过定时器输出PWM信号经过驱动芯片放大后驱动电机整个过程在嵌入式项目里属于最基础的执行机构控制但就是这最基础的一环很多新手栽了跟头。这篇文章就围绕这件事把硬件方案、原理讲解、工程配置和排障经验都摊开来讲。1. 驱动方案选型为什么不能直接用STM32引脚接电机新手最容易犯的第一个错误就是试图用STM32的GPIO直接驱动TT马达。我当年也干过这事结果就是STM32芯片烧了。这里必须把原理讲清楚——TT马达堵转电流轻松超过1A正常运行时也有几百毫安而STM32的GPIO引脚输出能力只有几个毫安级别灌电流和拉电流都有限制。用GPIO直接推电机相当于让一个只能提5公斤重物的人去扛一吨的货结果就是芯片内部烧毁。所以中间必须加驱动电路。常见的方案有三种分立元件搭H桥用四个三极管或MOS管搭桥成本极低但电路复杂还需要考虑死区、续流二极管等问题新手不建议。电机驱动芯片TB6612、L298N、DRV8833等。这是最主流的方案芯片内部集成了H桥只需要STM32给出逻辑信号就能控制方向和PWM调速。集成电机驱动模块比如带稳压和接口的Arduino电机扩展板方便是方便但体积大而且很多模块的使能脚设计不利于高频PWM。实际项目里我推荐TB6612FNG或者DRV8833这类低压驱动芯片。TB6612是东芝出的额定电流1.2A峰值3.2A对TT马达来说绰绰有余。它内部是两个H桥可以同时驱动两个TT马达正好满足两轮差速小车的需求。价格也不贵几块钱一片零散玩家直接用现成的TB6612模块更方便模块上已经把去耦电容和逻辑电平转换都做好了。L298N这个老前辈也不是不能用但它的压降大饱和压降能达到2V以上6V电池供电经过L298N再到电机可能只剩4V不到TT马达转速会明显掉。而且L298N用的是达林顿管开关速度慢高频PWM下发热严重。我实测下来同样6V供电TB6612驱动下TT马达的空载转速比L298N明显高发热也小得多。所以如果不是手头正好有L298N模块新设计直接跳过它上TB6612。DRV8833是TI的内置电流限制和过热保护耐压低一些但静态功耗和体积更优。三者对比下来给TT马达配驱动首选TB6612或DRV8833具体看手里有什么和板子上的封装空间。2. PWM调速原理与定时器配置细节2.1 PWM调速的本质TT马达是直流电机它的转速大致与端电压成正比。PWM调速的本质不是改变电压的大小而是通过改变一个周期内导通和关断时间的比例来改变平均电压。频率足够高的时候电机的机械惯性会平滑掉电压的波动转速反映的是占空比对应的平均电压。举个直观的例子一个TT马达工作电压6V用PWM输出频率10kHz、占空比50%的信号平均电压就是3V。电机绕组有电感电流不会瞬间跳变再加上转子有转动惯量所以电机不会一顿一顿地转而是平稳地以近似3V电压对应的转速运行。占空比越高平均电压越高转速越快。这里有个关键参数PWM频率。频率太低比如50Hz电机就会明显一顿一顿伴随尖锐的啸叫声。频率太高驱动芯片的开关损耗会上升MOS管或晶体管来不及完全导通就再次关断效率反而下降。对TT马达来说经验值在1kHz到20kHz之间我习惯用10kHz。这个频率下听不到人耳可感知的啸叫驱动芯片的开关损耗也控制得不错。如果用的驱动芯片是DRV8833这种高频优化的可以用20kHz甚至更高。2.2 CubeMX中的定时器配置STM32的PWM输出依靠定时器实现。主流做法是用高级定时器TIM1或TIM8、通用定时器TIM2-TIM5具体看工程设计。以最常见的STM32F103C8T6为例我用CubeMX配置一个PWM输出到PA8TIM1_CH1的过程是这样的打开CubeMX选择芯片型号后先把PA8引脚设置为TIM1_CH1模式。然后在Timers里找到TIM1设置时钟源为Internal ClockChannel1为PWM Generation CH1。参数设置上Prescaler (PSC) 71 Counter Period (ARR) 999这样的参数在72MHz定时器时钟下PWM频率就是72MHz / (711) / (9991) 1kHz。如果想让PWM频率达到10kHz把PSC设为7ARR设为899即72MHz / (71) / (8991) 10kHz。这里别被PSC和ARR的公式绕晕了记住一句话最终PWM频率 定时器时钟 / (PSC1) / (ARR1)。PSC负责把高频时钟分频到PWM这个档位ARR决定一个完整PWM周期包含多少个计数单位。占空比则通过比较寄存器CCR设置CCR的值在0到ARR之间CCR/ARR就是占空比。ARR的选择要注意分辨率。如果ARR太小比如只有99那么占空比只能以1%的步进调整电机转速的手感会比较粗糙。我一般把ARR设在999或者更大这样占空比可以精确到0.1%级别。有人会问PWM频率和分辨率能不能两全受限于定时器时钟频率PSC和ARR此消彼长。频率越高ARR就得越小分辨率就越低。10kHz下ARR只能到899分辨率约0.11%这已经很高了。对电机控制来说2%甚至5%的占空比步进都够用所以不用担心。2.3 HAL库PWM输出代码CubeMX生成工程后PWM初始化代码已经有了接下来就是开启PWM输出并动态修改占空比。最核心的调用有两个HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, duty_cycle);第一行启动PWM输出第二行设置占空比对应的CCR值。比如ARR999要输出50%占空比duty_cycle设为500即可。在实际项目里我会把电机速度控制封装成一个函数void motor_set_speed(TIM_HandleTypeDef *htim, uint32_t channel, uint8_t speed_percent) { if(speed_percent 100) speed_percent 100; uint32_t arr __HAL_TIM_GET_AUTORELOAD(htim); uint32_t ccr (uint32_t)(arr * speed_percent / 100.0f); __HAL_TIM_SET_COMPARE(htim, channel, ccr); }这样在主逻辑里调用motor_set_speed(htim1, TIM_CHANNEL_1, 70)就是70%转速代码可读性好很多。还有一点要注意TIM1和TIM8是高级定时器在CubeMX里生成代码后默认的PWM输出并不一定会直接出来因为高级定时器还需要设置主输出使能。在HAL库代码里通常表现为调用HAL_TIM_PWM_Start后用示波器却测不到波形。解决方案是在初始化代码中加上一行HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1);然后手动加上__HAL_TIM_MOE_ENABLE(htim1);MOE是Main Output Enable高级定时器特有必须在启动PWM后使能。很多人第一次用TIM1做PWM查了好半天波形就是没有就是漏了这个。这一点在ST的参考手册里有明确说明但实际开发中太容易被忽略了。如果是STM32G4、H7系列新版本HAL库在初始化时自动处理了MOE但还是建议查看生成的代码确认一下。用通用定时器TIM2-TIM5就没有这个问题所以我建议新手第一次做PWM调速项目直接用TIM2或TIM3练手避免在MOE这个坑上卡住。3. 方向控制与H桥逻辑不止是GPIO高低电平PWM只解决了调速的问题方向控制还得靠H桥。TT马达改变转向的原理很简单调换电机两端电源极性。H桥就是实现这个功能的四开关网络——对角导通是一组另一组对角导通则是反方向的电流路径。以TB6612为例它有两个输入引脚控制一个通道的方向AIN1和AIN2。逻辑关系如下AIN1AIN2电机行为01正转或前进10反转或后退00刹车同侧制动11惰行滑行电机自由转动和PWM结合的接法有两种。第一种是单路PWM方向引脚PWMA引脚接PWM信号AIN1和AIN2接GPIO方向信号。方向逻辑决定电流方向PWM占空比决定平均电压大小。第二种是双路PWMAIN1和AIN2都输入PWM信号一个为0另一个非0时就转动占空比差值决定转速和方向。TT马达驱动一般用第一种就够了逻辑清晰代码也简单。速度配合方向的完整控制函数可以这样写void motor_control(int16_t speed) { if(speed 0) { HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_RESET); motor_set_speed(htim1, TIM_CHANNEL_1, speed); } else { HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_SET); motor_set_speed(htim1, TIM_CHANNEL_1, -speed); } }这里speed是带符号的16位整数正值前进负值后退。这种封装在后续做差速控制、遥控小车、循迹小车时非常方便。关于刹车和惰行很多人不在意但实际调试小车时区别很大。刹车模式下H桥让电机两端短路电机变成发电机阻力大、立即停下惰行模式则电机自由旋转靠摩擦力慢慢停。如果是循迹小车高速冲线后需要立刻转向一定要用刹车否则小车会因为惯性冲出赛道。我在以前的项目里就是因为图省事转向时让电机惰行结果小车转弯半径大得离谱后来改成先刹车150ms再转向才恢复正常。另外TT马达的反电动势问题也值得一说。电机转动时转子在磁场中切割磁感线会产生与外加电压方向相反的反电动势。电机转速越高反电动势越大。PWM关断期间电机绕组的电流不能突变续流二极管会提供一个通路否则驱动芯片会被高压击穿。TB6612内部已经集成了续流二极管所以模块方案不用担心。如果自己用分立元件搭H桥记得在电机两端并联续流二极管或者RC吸收电路方向别接反否则等于短路。4. 电源设计与共地问题电机一启动单片机就复位的元凶这个问题的典型现象是程序下载好上电跑起来一启动电机STM32直接复位重启或者LCD屏幕闪烁、传感器读数异常。这是嵌入式项目里非常经典的现象根因在于电源。TT马达启动瞬间电流非常大我实测过有些堵转状态下电流能到2-3A。如果单片机系统和电机共用一个电源比如一块锂电池或者一个USB供电口电机一启动就把母线电压拉低单片机供电电压跌落到复位阈值以下自然就重启了。USB供电更惨电压跌落过大还可能触发PC端USB口过流保护直接断连。解决思路有三个层级物理隔离单片机用单独的稳压电路供电电机电源独立。这是最彻底的办法很多正式产品也是这么做的。电源去耦在电机电源端并一个大容量电解电容470uF以上靠近驱动芯片再接0.1uF陶瓷电容吸收瞬态冲击。TT马达启动电流虽然大但持续时间短大电容能扛住大部分冲击。降低启动冲击软件上做软启动即PWM占空比从0逐渐增大到目标值而不是一步到位直接给目标占空比。这个在代码里实现非常容易void motor_soft_start(uint8_t target_speed) { for(uint8_t i 0; i target_speed; i) { motor_set_speed(htim1, TIM_CHANNEL_1, i); HAL_Delay(1); } }软启动看似简单效果却非常明显。TT马达启动电流大的原因在于启动瞬间转子静止反电动势为零绕组相当于纯电阻电流只受绕组电阻限制。瞬间给满压电流就是堵转电流。软启动让平均电压缓慢上升电流冲击被削平对电源系统的冲击小一个数量级。但软启动也有代价就是响应变慢。如果是比赛需要爆发力就不适合用太慢的软启动折中方案是快速爬坡比如1ms从0到70%再微调。共地问题也一样关键。STM32的GND和驱动芯片的逻辑GND、电机电源的GND必须连在一起否则H桥的输入信号没有参考地逻辑信号会抖动甚至完全无法判定。有的新手把单片机的地和电机电源的地分开想着隔离结果驱动板根本不工作或者逻辑逻辑混乱。TT马达和驱动板之间没有做光耦隔离的前提下共地是必须的。真正的隔离需要光耦或隔离电源不是简单分开接地就能实现的。5. 常见问题与排查经验速查5.1 电机不转检查供电测量电机电源端的电压如果只有一两伏基本就是电源被拉垮了按第四节的方法解决。检查方向逻辑如果AIN1和AIN2同时为高或同时为低电机处于惰行或刹车状态。用万用表量一下这两个引脚的电压。检查PWM是否输出用示波器看PWM引脚。如果用的是TIM1/TIM8高级定时器检查是否漏了__HAL_TIM_MOE_ENABLE。这一条看着基础实际排查时花费的时间往往最多。我见过不下十次这类求助最后发现就是漏了高级定时器使能。建议第一时间查这个。5.2 电机单向转无法反转方向引脚可能没有正确切换或者驱动芯片的某个H桥臂损坏。用万用表量AIN1和AIN2在代码切换前后的电平变化如果电平变化正常但电机仍是单方向怀疑驱动芯片烧了。TB6612这种芯片模块也不贵换一个试试。另一个隐形坑是代码里的逻辑错误——方向引脚复用比如同一个GPIO被其他外设占用初始化后模式被改写。排查时先确认GPIO初始化的模式和当前实际状态一致。5.3 电机启动时单片机复位按上一节方案逐条排查先用大电容顶一下同时软件软启动。绝大多数情况下这两个手段组合就能解决。如果还不行检查所有GND是否可靠连接特别是电池负极端到驱动板GND、STM32 GND到驱动板逻辑GND这两条线用粗一点的杜邦线直接连。5.4 电机转速不稳忽快忽慢TT马达属于有刷电机电刷与换向器接触电阻在不断变化天然存在转速波动。6V供电、PWM占空比不变的情况下空载和带载的转速差异本身就很大。如果转速波动明显异常先检查电源电压是否稳定再看PWM频率是否处于电机和驱动芯片的合适区间。如果项目对转速稳定性要求高比如精密循迹、视觉跟随光靠开环PWM是不够的必须加编码器做闭环。TT马达有配套的带霍尔编码器版本带有AB两相输出可以测转速和方向。STM32用定时器编码器模式或者外部中断加定时器测量脉冲再配合PID控制器调节PWM占空比就能把转速稳定在目标值附近。5.5 电机发热严重或驱动芯片烫手TT马达本身的铜损和铁损会在高负载下发热但发热到烫手的地步就不正常了。先看电流用万用表串入供电回路测工作电流如果超过1A长时间运行要么负载太大要么电压超出额定值太多。驱动芯片烫手则多半是PWM频率太高导致开关损耗过大或者电机堵转时间太久。TT马达堵转保护很弱别让它长时间堵转。5.6 驱动模块输入信号正常但电机剧烈抖动这种情况常常出现在快速切换方向时。如果代码里从正转直接切到反转没有中间刹车延时H桥的换向瞬间电流会很大冲击电源和驱动芯片电机也可能剧烈抖动。我习惯在方向切换前加一个短暂刹车段void motor_reverse(uint8_t target_speed) { // 先刹车 HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_RESET); HAL_Delay(10); // 再反向启动 motor_control(-target_speed); }这个10ms的刹车段既能防止电流冲击也能让电机更快进入反向稳定状态。5.7 JTAG/SWD引脚被占用导致无法调试有朋友把PA13、PA14、PA15、PB3、PB4这些复用成GPIO控制电机方向结果程序下载后调试器连不上芯片。STM32默认的SWD引脚是PA13SWDIO和PA14SWCLK如果被用作普通GPIO调试口就失效了。遇到这种问题要么在代码里别占用这些引脚要么下载前按住复位键尝试连接再快速烧录。我个人的建议是方向控制引脚绕开调试引脚别给自己埋雷。6. 进阶扩展从开环控制到闭环控制如果只是让TT马达转起来前面的内容已经完全够用。但实际项目里往往不止于此——小车能不能走直线、机械臂能不能准确停在某个角度、云台能不能稳定跟随这些都是开环PWM解决不了的。TT马达升级闭环有两个方向加编码器测速带霍尔编码器的TT马达通过两路正交信号可以算出转速和方向。STM32定时器编码器模式可以直接接收正交信号自动计数省去外部中断开销。转速反馈之后经典增量式PID就能上int32_t pid_update(int32_t target_speed, int32_t current_speed) { static int32_t integral 0; static int32_t last_error 0; int32_t error target_speed - current_speed; integral error; int32_t derivative error - last_error; last_error error; return (int32_t)(kp * error ki * integral kd * derivative); }PID参数需要实际调试先从P开始加上I消除静差最后D抑制超调。这个调参过程很有玄学色彩但本质上就是不断观察阶跃响应曲线微调。实测下来TT马达在轻载情况下调好PID后转速稳定度比开环高一个数量级直线行驶的偏移明显改善。加电流/位置闭环在测速的基础上再加电流环或者位置环比如编码器计数到目标值停转可以做精准定位。比如旋转平台项目要求电机带动转盘准确停在某个角度这就需要在编码器模式下用位置控制闭环。在没有编码器的情况下也可以用反电动势测速做廉价的闭环方案。电机两端并联一个分压电路PWM关断期间续流阶段检测反电动势从而估算转速。这个方案精度一般但胜在不需要额外传感器和齿轮某些场景下够用。我个人做过的TT马达闭环项目中印象最深的是给一台循迹小车做速度和转向PID。开环版本循迹时车子走S形左右摆动很大因为两个电机的个体差异和地面摩擦不均匀导致转速不一致。加了编码器闭环之后直线段跑得非常稳PID一旦调好那种“丝滑”的直线感是开环永远给不了的。如果你的项目需要较长时间的稳定运行强烈建议直接上带编码器的TT马达别回头在开环上浪费时间。7. 实操项目两轮差速小车控制框架最后放一个完整的参考框架我做过的一个两轮差速小车项目两个TT马达电机采用TB6612驱动PWM调速加方向控制核心代码段如下// 电机1PB0→AIN1, PB1→AIN2, PA8→PWMA (TIM1_CH1) // 电机2PB4→BIN1, PB5→BIN2, PA9→PWMB (TIM1_CH2) void motor1_control(int16_t speed) { if(speed 0) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); speed -speed; } __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, (uint32_t)(speed * 10)); } void motor2_control(int16_t speed) { if(speed 0) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); speed -speed; } __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_2, (uint32_t)(speed * 10)); } // 差速转向left_speed 和 right_speed 的范围是 -100 到 100 void differential_drive(int16_t left_speed, int16_t right_speed) { motor1_control(left_speed); motor2_control(right_speed); }回调函数里可以挂编码器中断或者遥控信号处理。整个框架最核心的逻辑就这些剩下的循迹、避障、遥控、视觉跟随都是在它上面加逻辑。差速转向本质上很简单左右轮速度相同是直线一个快一个慢就转弯。但注意转弯时内侧轮如果速度过大会打滑导致实际轨迹和理论轨迹偏差巨大。我一般限制最大速度差在60%以内或者转弯时主动减速都能获得更稳定的效果。如果要做得更像样加MPU6050陀螺仪做角速度闭环转向精度会有质的提升——但这已经是另一个话题了。8. 关于ST-Link调试时的额外提醒说一个我踩过好几次的坑ST-Link连接报错error: no stm32 target found!。这事跟电机驱动关系不大但做小车调电机时太常见了——因为程序里意外禁用了SWD引脚。STM32有个JTAG/SWD引脚复用问题GPIO配置里如果把这些引脚设置成普通输出调试器下次就连接不上芯片了。我的处理经验是写程序之前先在驱动引脚规划表上明确标注PA13/PA14SWDIO/SWCLK、PA15、PB3、PB4尽量避让不要用作电机方向控制。如果已经中招了几个恢复手段按住板子上的复位键不放点击烧录后松手或者用ST-Link Utility的Connect under reset模式。注意得先用调试器的NRST脚连到板子的NRST否则这个功能也不灵。这个坑在电机控制项目中尤其容易触发因为电机控制需要多个GPIO扳着指头数引脚的时候很容易把SWD引脚用掉。引以为戒。9. 结尾的个人经验小结最后说一点个人体会。TT马达在STM32项目里就像一把万能的螺丝刀便宜够用什么都能拧两下。但越简单的器件越考验基本功电源设计、PWM参数、H桥逻辑、共地处理每一环出问题都会导致整个系统不工作。很多新手栽跟头不是STM32代码写不出来而是硬件上的细节没处理好。我的建议是从最简单的单电机转起来开始用示波器看PWM波形用万用表量电压和电流一步步验证完整条链路再叠加方向控制、多电机差速、闭环PID。基础打扎实了后面做再复杂的机器人项目也不会发怵。TT马达只是起点但当你知道怎么把一个转动的电机变成精确可控的执行机构时能做的事情就完全不一样了。