2026/9/7 2:39:01

STM32驱动TT马达:PWM调速与TB6612FNG驱动原理及调试全解析

STM32驱动TT马达:PWM调速与TB6612FNG驱动原理及调试全解析 做小车、做云台、做一个简单的机械臂我遇到的第一类电机基本都是TT马达。它便宜、耐造、拆装方便跟STM32搭配起来刚好把GPIO、定时器、PWM和功率驱动这几个嵌入式核心外设一次过完一遍。这篇笔记不打算只讲“怎么接线、怎么敲代码”我尽量把原理也揉进去——为什么电机不转、为什么发烫、为什么抖这些坑我都踩过写出来给后来人省点时间。不管你是刚点亮LED想进阶的新手还是已经写过一些外设驱动、准备做两轮差速小车的同学这篇内容都能让你少走弯路。1. TT马达的核心特征与选型考虑1.1 TT马达到底是个什么结构TT马达从外观上看就是一个圆柱形电机加上一组减速齿轮箱输出轴上还能直接套轮子或者码盘。很多人叫它“TT Motor”其实这名字更多是来自智能小车套件里的习惯叫法本质就是一台小功率有刷直流电机。内部结构上它比无刷电机简单得多永磁体提供定子磁场转子上绕线圈通过换向器和电刷把电流送进线圈让转子持续旋转。减速齿轮箱的作用是把电机的高转速、低扭矩转换成低转速、高扭矩的输出所以TT马达额定转速一般都在几十转到三百多转每分钟这个区间搬动几十到两三百克的车身很轻松。正是因为“有刷”这个属性TT马达的控制特别直接通上额定电压就转反接两根线就反转电压越高转速越快。代价是电刷和换向器之间会有机械磨损寿命比无刷电机短。不过对学习和项目验证来说这根本不是问题几块钱一个坏了直接换肉不疼。1.2 为什么TT马达是STM32入门首选我见过不少新手上来就纠结“要不要直接上无刷电机配电调”我的建议是如果是第一次用单片机控制电机老老实实从TT马达开始。原因有三点。第一驱动逻辑简单。有刷直流电机只需要控制电压大小和极性一个PWM波加一个H桥直驱芯片就搞定不涉及三相换相、反电动势检测这种复杂逻辑适合把注意力放在STM32本身的配置和编程上。第二减速箱带来的扭矩对DIY小车来说刚好够用配合橡胶轮和电机支架几个螺丝就能搭出一个能跑的底盘成就感来得快。第三TT马达有带编码器的版本一般是霍尔编码器或光电码盘后期做转速闭环、里程计、差速转向都够用。也就是说从最基础的开关控制到PID调速它都能一路撑下来。拿STM32F103C8T6这种几十块钱的板子来说TT马达刚好匹配它的外设资源一个定时器输出PWM一个定时器做编码器计数剩下几个GPIO控制方向。整套驱动流程跑通之后你其实已经把MCU控制物理世界的最基础闭环掌握了再去看无刷、步进、伺服这些电机思路会顺畅很多。1.3 不要忽略的电气参数和实测数据TT马达常见标称电压是3V到6V我平时用的比较多的是6V版本配两节或者四节镍氢电池都行。用实验室稳压电源实测6V供电下空载电流大约100毫安上下带个普通橡胶轮空转不会超过200毫安但把输出轴捏住堵转电流会瞬间飙到500毫安甚至更高。这个数据很重要——它直接决定了你不能拿STM32的GPIO去怼电机。STM32的GPIO输出能力大概在3.3V、几毫安到二十毫安级别连空载都喂不饱更别说堵转。就算有人抱着“我直接从5V引脚取电给电机”的侥幸心理堵转时那几百毫安的电流也会把开发板供电拉垮轻则电压跌落导致复位重则烧板载稳压芯片。所以我一直强调驱动电路不是可选项是必须项。2. 驱动方案与原理从H桥到TB6612FNG2.1 为什么不能省掉驱动芯片很多人第一次尝试是直接把电机两根线接在STM32引脚上结果电机纹丝不动甚至把引脚烧了。这里有两个原因。第一电机的启动电流和堵转电流远超MCU的IO驱动能力。第二电机本质是感性负载通断瞬间会产生反向电动势这个电压尖峰可能超过MCU耐压值直接击穿引脚内部的保护二极管。更麻烦的一个问题是“反转”。要让直流电机反转必须让电流从反方向流过电枢也就是把电机两端电压极性对调。一个独立的GPIO只能输出高或低没法输出负电压所以光靠单片机引脚根本无法实现双向转动。这些需求指向同一个解决方案H桥驱动电路。2.2 H桥的基本工作逻辑H桥的电路结构只有四个开关管电机接在桥臂中间外观上像字母H所以叫H桥。四个开关Q1、Q2在上半部分Q3、Q4在下半部分电机两端分别接在左桥臂和右桥臂之间。想让电机正转就打开左上角Q1和右下角Q4电流从电源正极流过Q1、电机、Q4回到负极想让电机反转就换成右上角Q2和左下角Q3导通电流方向反过来。如果让Q1和Q3、Q2和Q4这种同侧上下两个开关同时导通就是直接把电源短路这叫“直通”轻则跳闸重则烧管子在实际驱动芯片里都会通过逻辑互锁来防止这种状态。除了正转和反转H桥还能实现“刹车”和“滑行”刹车是同侧两个管子导通把电机两端短接利用反电动势形成制动力矩滑行则是四个管子全关电机自由停转。从控制角度讲方向由两个开关组合决定速度则由PWM占空比决定。而TB6612FNG这类驱动芯片本质就是把这一整套H桥、死区逻辑、保护电路全部集成好的成品你只需要通过两个GPIO选方向一个PWM调速度。2.3 TB6612FNG与L298N、分立MOS管的选型对比做TT马达驱动市面上主流的方案无非三种L298N模块、TB6612FNG模块、自己搭分立MOS管H桥。我三个都用过简单说说差别。L298N是很多老教程里的常客驱动能力强但它是双极型晶体管方案饱和压降大1A以上电流时发热严重而且模块巨大放小车上很占地方。分立MOS管方案成本最低但你需要自己处理逻辑电平转换、栅极驱动、防直通和散热问题对新手不太友好。TB6612FNG是东芝的MOS管驱动芯片压降只有零点几伏静态电流低体积小驱动能力在1.2A左右驱动TT马达这种小功率电机绰绰有余。它还有一个好处逻辑电源只需要3.3V就能工作刚好匹配STM32的电平。如果你的电机功率更大或者准备以后升级成带减速箱的较大直流电机也可以考虑DRV8833或者A4950这类芯片引脚逻辑类似但具体细节还是要以数据手册为准。总体来说STM32配TT马达TB6612FNG是综合体验最好的选择。2.4 TB6612FNG引脚说明与接线逻辑TB6612FNG有14个引脚正面看依次是VM电机电源、VCC逻辑电源、GND、PWMA、AIN1、AIN2、STBY、BIN1、BIN2、PWMB、GND、GND、BO1、AO1、AO2、BO2还有些模块会引出BO1/AO1这种电机输出口不同模块丝印略有差异接线前务必看清自己模块的引脚标注不要想当然。接线逻辑上VM接电池正极VCC接3.3V或者5V逻辑电源GND必须和STM32GND、电池GND共地。PWMA接STM32定时器的PWM输出通道AIN1和AIN2接两个普通GPIOSTBY是待机引脚低电平时芯片停止工作因此必须拉高才能正常输出。电机接AO1和AO2。ITE控制逻辑是一个真值表我整理成了表格STBYAIN1AIN2PWMA电机状态Lxxx停止HHLH正转全速HLHH反转全速HHLPWM正转调速HLHPWM反转调速HHHx刹车HLLx滑行这里要特别提醒一下AIN1和AIN2同为高是刹车同为低是滑行这两个状态很容易被新手忽略。我在调试时一度以为输出低电平就是不转结果电机是处于滑行状态被外力推动还是会缓慢转动。区分清楚这两者对做精确定位的小车很重要。3. STM32侧PWM和GPIO的配置方案3.1 PWM调速的原理和参数选择PWM调速的本质是改变电机两端电压的有效值。占空比越高一个周期内高电平占的时间越长电机获得的平均电压越接近电源电压转速就越快。STM32的定时器输出PWM时大致原理是计数器不停从0计到ARR自动重装载值比较寄存器CCR里存一个阈值计数器值小于CCR时输出高电平到达CCR后输出低电平计数器溢出后清零重新计数。所以改变CCR就改变了占空比ARR决定了PWM频率。对于TT马达这种小直流电机PWM频率在1kHz左右是多数人验证过的甜点区。频率太低会听到电机“咔哒咔哒”的顿挫声速度控制也不线性频率太高比如20kHz以上虽然听不见声音但驱动芯片的开关损耗会上升电机反而更容易发热。我一般习惯把定时器配成72MHz主频预分频PSC设成71自动重装载ARR设999这样PWM频率就是72MHz/(711)/(9991)1kHz非常干净。3.2 CubeMX配置流程走一遍用STM32CubeMX配定时器很方便我以STM32F103C8T6为例说明关键步骤。首先在Pinout界面选好单片机型号打开TIM2勾选Channel1的PWM Generation CH1。然后在Parameter Settings里把Prescaler设成71Counter Period设成999Pulse初始设成0这样PWM周期是1kHz初始占空比0%。打开Clock Configuration确认APB1总线频率是72MHz这样不会数错分频系数。方向控制引脚我用PA6和PA7配置成GPIO_Output初始电平设成Low。这个初始状态很有讲究最好在程序上电时先把方向引脚确定下来再开PWM。否则在MCU复位到main函数执行这段极短的时间内引脚是浮空状态如果驱动芯片STBY被意外拉高电机可能猛地抽动一下虽说不至于烧毁但吓人一跳是难免的。3.3 驱动代码的结构设计思路我习惯把电机驱动封装成一个独立模块不把逻辑全堆在main.c里。在dc_motor.h里定义方向枚举和结构体结构体成员包括PWM定时器句柄、通道号、方向引脚号和速度范围。这样做的好处是以后从两路电机扩展成四路复制粘贴结构体再改引脚就行不需要每个main文件挖一次五脏六腑。控制函数最好统一成一个接口比如motor_set_speed(Motor_Handle_t *motor, int speed)speed范围从-1000到1000正数正转负数反转0表示刹车。函数内部先判断speed的符号再根据符号设置方向引脚电平最后用__HAL_TIM_SET_COMPARE(motor-htim, motor-channel, abs(speed))更新占空比。这里必须注意一个顺序先改方向引脚再更新PWM避免方向切换瞬间出现瞬间反转的毛刺。4. 完整接线与代码实操记录4.1 接线清单与共地原则实际动手接线时我建议把电源路径和逻辑路径分开理清楚。电源路径是电池正极到TB6612的VM电机两端到驱动芯片的AO1、AO2逻辑路径是STM32的3.3V到TB6612的VCCPWM、方向引脚到对应输入。核心的一步是共地电池负极、TB6612的GND、STM32的GND必须接到同一个参考点否则控制信号没有回路驱动芯片无法正确识别高低电平。我常用的接线组合是这样STM32F103C8T6开发板TB6612FNG引脚连接目标VM6V电池正极或5V电源正极VCCSTM32 3.3VGND电池负极、STM32GNDPWMAPA6TIM2_CH1AIN1PB0AIN2PB1STBY3.3VAO1TT马达正极AO2TT马达负极在面包板上飞线时尤其是电池线的杜邦线我吃过不少亏有些劣质杜邦线内芯细满载时压降能到0.5V以上电机转速明显偏低。后面我改用粗一点的杜邦线或者直接用电线焊接问题就没了。如果手头有大电容建议在VM和GND之间焊一个470uF电解电容并联一个0.1uF瓷片电容可以有效平抑堵转瞬间的电压跌落和反向电动势尖峰。4.2 一个可以直接上手的工程代码下面这套代码片段的思路是上电后先让电机以50%占空比正转3秒再反转3秒然后刹车2秒循环执行方便你在没有示波器的情况下验证方向和PWM是否正常。我基于STM32 HAL库编写CubeMX生成工程后只需要补上这些逻辑。/* dc_motor.h */ #ifndef __DC_MOTOR_H #define __DC_MOTOR_H #include main.h typedef enum { MOTOR_STOP 0, MOTOR_FORWARD, MOTOR_BACKWARD } Motor_Dir_t; typedef struct { TIM_HandleTypeDef *htim; uint32_t channel; GPIO_TypeDef *in1_port; uint16_t in1_pin; GPIO_TypeDef *in2_port; uint16_t in2_pin; } Motor_Handle_t; void Motor_Init(Motor_Handle_t *motor, TIM_HandleTypeDef *htim, uint32_t channel, GPIO_TypeDef *in1_port, uint16_t in1_pin, GPIO_TypeDef *in2_port, uint16_t in2_pin); void Motor_SetSpeed(Motor_Handle_t *motor, int speed); #endif/* dc_motor.c */ #include dc_motor.h void Motor_Init(Motor_Handle_t *motor, TIM_HandleTypeDef *htim, uint32_t channel, GPIO_TypeDef *in1_port, uint16_t in1_pin, GPIO_TypeDef *in2_port, uint16_t in2_pin) { motor-htim htim; motor-channel channel; motor-in1_port in1_port; motor-in1_pin in1_pin; motor-in2_port in2_port; motor-in2_pin in2_pin; HAL_TIM_PWM_Start(htim, channel); __HAL_TIM_SET_COMPARE(htim, channel, 0); } void Motor_SetSpeed(Motor_Handle_t *motor, int speed) { if (speed 1000) speed 1000; if (speed -1000) speed -1000; if (speed 0) { HAL_GPIO_WritePin(motor-in1_port, motor-in1_pin, GPIO_PIN_SET); HAL_GPIO_WritePin(motor-in2_port, motor-in2_pin, GPIO_PIN_RESET); } else if (speed 0) { HAL_GPIO_WritePin(motor-in1_port, motor-in1_pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(motor-in2_port, motor-in2_pin, GPIO_PIN_SET); } else { /* 这里选择刹车而不是滑行 */ HAL_GPIO_WritePin(motor-in1_port, motor-in1_pin, GPIO_PIN_SET); HAL_GPIO_WritePin(motor-in2_port, motor-in2_pin, GPIO_PIN_SET); } __HAL_TIM_SET_COMPARE(motor-htim, motor-channel, (uint32_t)(speed 0 ? speed : -speed)); }/* main.c 中的调用示例 */ Motor_Handle_t motor1; void Motor_Test_Demo(void) { Motor_Init(motor1, htim2, TIM_CHANNEL_1, GPIOB, GPIO_PIN_0, GPIOB, GPIO_PIN_1); Motor_SetSpeed(motor1, 500); /* 50%占空比正转 */ HAL_Delay(3000); Motor_SetSpeed(motor1, -500); /* 50%占空比反转 */ HAL_Delay(3000); Motor_SetSpeed(motor1, 0); /* 刹车 */ HAL_Delay(2000); }这个代码我已经在F103开发板上实际跑过稳定性和可靠性没什么问题。使用时的注意点有三处一是HAL_TIM_PWM_Start之后要立刻设占空比为0防止上电瞬间输出高电平让电机全速转一下二是方向引脚的电平改变必须赶在PWM更新之前三是speed为0时建议用刹车而不是滑行这样小车停止后不会因为惯性溜车。4.3 调试过程的现场记录第一次上电时我踩过一个很蠢的坑STBY引脚悬空了。TB6612的STBY内部没有上拉悬空时电平不定芯片有时候工作有时候不工作表现出来就是电机偶发抖动一下然后完全没动静。当时我拿着万用表量了半天VM、VCC都正常最后才怀疑到STBY头上。从那以后我拿到模块的第一件事就是把STBY直接焊到3.3V省心。还有一次电池电压在7V左右四节镍氢直接接VM发现电机转速明显偏低一量VM电压只有4.8V。原因是电池内阻加上杜邦线压降启动瞬间掉压很严重。我在VM和GND之间加了一个470uF电容问题立刻改善。不要小看这个电容它对整个系统的稳定性帮助非常大尤其是后面加上编码器测速后你会发现示波器上PWM信号的毛刺少了很多。5. 常见问题与排查技巧实录5.1 电机完全不转或者只会抖不会转排查这类问题我习惯按信号路径从后往前查先看驱动芯片的供电再查逻辑输入最后看PWM和GPIO配置。第一步用万用表量TB6612的VM和VCC确认电压都正常特别是共地必须接好。第二步检查STBY是否为高电平很多人在这栽过。第三步查AIN1和AIN2的电平组合是否符合预期。如果方向引脚设置不对比如两个都是低电平电机就处于滑行状态你用手去转它转得动但程序里它不会自己跑。第四步用逻辑分析仪或示波器探PWM引脚确认确实有波形输出。如果PWM输出正常但电机依然不动就要怀疑驱动芯片本身是否损坏换一块模块试试。电机“嗡嗡”响但就是堵着转不起来这种多半是PWM频率太低或者占空比太小。低于3%的占空比很多TT马达的静摩擦力矩都克服不了直接不转。解决办法是把PWM频率调到1kHz附近同时让初始占空比从10%甚至20%起步。5.2 电机能转但转速明显无力转速偏低先说一个最容易被新手忽略的情况电机电源是从STM32开发板的5V引脚取的。开发板的5V往往来自USB供电或者稳压芯片最大输出电流本来就有限电机一启动就把5V拉垮轻则转速骤降重则整板复位。正确做法是给电机准备独立的电池电源比如两节18650或者4节AA电池然后电池正极接TB6612的VM。还有一种情况是杜邦线太细太长电池到VM之间的线路压降过大。我实测过普通杜邦线在500mA电流下确实能掉0.3V左右电机稍微带点负载转速下降非常明显。建议电源线用稍微粗一点的硅胶线把长度控制在10厘米以内。最后别忘了检查电池本身是不是没电了充满的电池和快没电的电池驱动同一个电机那是完全两种表现。5.3 驱动芯片发热系统偶发复位TB6612正常工作时会有一点温热但如果烫手就要考虑是不是接线有短路、电机堵转时间太长、PWM频率过高导致开关损耗增大。另一个常见问题是反电动势干扰。电机在换向和制动瞬间会产生很高的电压尖峰这个尖峰如果顺着电源线窜回开发板ESD和复位就都来了。处理方法是在VM和GND之间加足够容量的储能电容同时把电机电源线和信号线分开走不要在面包板上让它们并排飞线。如果你的系统里同时有舵机或者编码器也要注意电源拓扑舵机启动瞬间的电流脉冲很容易影响相邻的逻辑电路。我在做四轮小车时就遇到过编码器读数偶发跳变的问题后来在驱动电源端加了电容、把编码器线换成双绞线问题才彻底解决。5.4 ST-Link下载失败no stm32 target found这个问题在STM32调试中太经典了报错提示大体是“Error: No STM32 target found! If your product embeds Debug Authentication, please...”之类。遇到这个报错时第一反应不要慌按照顺序排查。先把ST-Link的四根线SWDIO、SWCLK、GND、3.3V确认接对排除接触不良。然后检查STM32的BOOT0跳线如果BOOT0接高电平芯片会进入系统存储器引导模式此时SWD口有时连接不稳定。更隐蔽的一个原因是你之前在程序里把SWDIO和SWCLK两个引脚复用成了普通GPIO导致调试器无法连接芯片。解决办法是按住开发板上的复位键在IDE里点击下载当IDE提示正在连接目标时瞬间松开复位键多试几次一般能抢在程序运行前把新固件下载进去。如果还是不行用串口ISP方式把BOOT0拉高通过串口烧录一个清空程序的固件然后再恢复BOOT0为低电平SWD口就回来了。5.5 抖动、异响和后续的测速闭环有些TT马达配的是编码器版本输出轴后面带码盘和两个霍尔传感器能输出A、B两路正交信号。这时候你用STM32的一个定时器配置成编码器模式就能直接读出转速和方向。我在做两轮差速小车的时候就用TIM4的编码器模式加TIM2的PWM输出配合增量式PID调节左右电机速度最终实现了直线行驶走个四五米偏移量很小。这里想提醒一句闭环控制的前提是开环驱动先跑得稳。如果开环状态下电机转速就不稳定、抖动明显直接上PID只会让参数调得怀疑人生。先解决PWM频率、供电和接线这些硬件问题让开环驱动的电机转起来稳定、有力再谈PID参数整定。这是我做了好几版小车之后最深的体会。方向确认、PWM占空比固定测试、编码器计数是否线性这些步骤一步步来问题会少很多。写在最后的几点实验体会TT马达看起来只是一个简单的小电机但把它玩明白其实把嵌入式里很重要的一整条链路都走了一遍GPIO控制方向、定时器输出PWM、功率驱动芯片选型、电源完整性和地回路设计再加后面的编码器测速和PID闭环。每一个环节拿出来都是工程上躲不开的硬功夫。我个人在实际操作中最受用的一个习惯是调试电机驱动时永远先把占空比固定成一个中间值比如30%到50%只去切换方向引脚确认方向逻辑和刹车逻辑都正确了再开始动态调速。这样排查问题的时候变量少了很多不会一会儿怀疑PWM、一会儿怀疑GPIO乱成一锅粥。另外一点接线布线时一定要在脑子里把“电源路径”和“信号路径”分开两者只在共地点交汇这样绝大多数干扰和掉压问题都能在设计阶段避免掉。如果你正准备做自己的第一台小车或者想把手里的TT马达用起来建议就按这篇文章的步骤走一遍。别看步骤细碎等你真正跑通“正转—反转—刹车”这三个动作后面做编码器测速、里程计、差速转向都会顺理成章。希望这篇笔记能帮你少踩几个我当年踩过的坑。