
STMicroelectronics的STM32系列在电机控制项目里几乎是绕不开的选择而TT马达直流减速电机又是小车、机械臂、智能窗帘这类入门项目里最常见的那颗“心脏”。很多新手在点亮LED之后第一次卡住的地方往往就是“电机怎么不转”“怎么一转单片机就重启”“怎么调速”。这篇教程我从选型、驱动电路、PWM调速到问题排查一步步拆开讲全是实际项目里验证过的经验想给正在做STM32小车或者直流电机相关项目的朋友一个可以直接照着做的完整参考。1. 先搞懂TT马达从参数到驱动原理1.1 TT马达是什么为什么项目里总用它TT马达并不是某个厂商的型号而是国内创客圈对一类“130电机塑料齿轮减速箱”组合的通俗叫法。它通常由一个空心杯直流电机和一个多级塑料齿轮减速箱组成输出轴转速比电机本体转速低很多但扭矩明显增大。这种马达广泛用在智能小车、循迹车、避障车、机械臂关节等入门项目里价格便宜、结构简单、控制逻辑直白非常适合用来学习电机驱动和PWM控制。与步进电机、伺服电机相比TT马达的优点是“只要给电就转、换向就反着转”不需要编码器反馈除非你自己加霍尔或者编码器版本控制难度低。缺点是精度和定位能力基本为零转速和负载关系也比较松。所以它适合做“让轮子转起来”“让窗帘拉起来”这类不需要精确位置控制的应用而不适合做需要准确定位的关节控制。1.2 关键电气参数电压、转速、电流怎么看市面上常见的TT马达分1:48、1:90、1:120等几种减速比同一个电机本体在额定电压下减速比越大输出转速越低、扭矩越大。选型时主要看三个参数额定电压大多数TT马达额定电压3V-6V最常用的是3V和6V两种。3V版本接3.3V-4.2V能跑6V版本建议用6V-7.4V供电超过额定电压太多会缩短寿命甚至烧毁。空载转速比如1:48减速比、6V额定电压的空载转速大约在200-300rpm左右这个值决定了小车跑多快。堵转电流这是新手最容易忽略的参数。TT马达堵转电流通常在0.5A-2A之间具体看规格书或者用万用表实测。堵转电流决定了驱动电路和电源的选型如果选了个只能输出几百毫安的LDO去带电机一堵转系统必崩。补充一个我自己的经验选马达不能只看空载参数要看应用场景。如果做小型循迹小车1:48的6V版本很合适如果做爬坡车或者机械臂扭矩优先选1:120甚至更高减速比如果做风扇、云台这类高速应用可以选1:20以下低减速比甚至直接不加减速箱。1.3 为什么STM32的GPIO不能直接驱动马达很多新手第一次接电机都会想STM32的GPIO不是能输出高电平吗直接把电机接到引脚上不就行了答案是不行原因有两个。第一是电流能力不够。STM32的单个GPIO输出电流一般只有20mA左右而TT马达正常工作电流动辄几百毫安堵转时更是超过1A。强行接上去的结果就是GPIO被拉垮、电压跌落、单片机复位甚至引脚烧毁。第二是电机是感性负载。电机绕组在断电瞬间会产生反向电动势电压可能冲到几十伏甚至更高这种尖峰直接灌进GPIO引脚非常容易把单片机干掉。所以电机驱动必须要有隔离、扩流和保护措施。用生活化的类比来解释GPIO就像一个只能背20斤东西的人TT马达是一台需要背200斤货物的机器。你不能让这个人直接去扛得给他配一台叉车这“叉车”就是电机驱动模块或H桥电路。2. 驱动方案选型L298N、TB6612FNG还是分立H桥2.1 常见驱动模块横评给STM32配TT马达主流方案有三大类集成电机驱动模块、分立H桥电路、以及带逻辑的驱动芯片。我把最常见的几种放一起对比方案典型代表输入电压范围最大持续电流控制复杂度适用场景L298N模块经典L298N5V-12V2A实际1A左右建议降额低学习、大电流电机、不介意压降TB6612FNG模块TB6612FNG2.7V-5.5V逻辑/6V-13.5V电机单路1.2A低小车主驱、TT马达最佳搭档DRV8833模块DRV88332.7V-10.8V单路1.5A低低压TT马达、锂电池供电分立H桥自己搭三极管/MOS管H桥由元件参数决定取决于元件高学习原理、特殊电压需求集成PWM芯片L9110S2.5V-12V单路0.8A低微型TT马达、方案预算有限L298N是很多教材里的常客但它的缺点是压降大约1.5V-2V6V供电到电机端只剩4V多电机明显没力。而且模块体积大、功耗高不太适合现在的小车项目。TB6612FNG的MOS管导通压降非常小效率高尺寸也小是我做小车主驱时的首选。DRV8833则更适合3.3V逻辑直接控制不需要额外电平转换。2.2 我为什么推荐TB6612FNG作为入门首选TB6612FNG是东芝出品的一颗双路H桥驱动芯片一路H桥就能驱动一个TT马达一颗芯片刚好驱动两个马达完美匹配两轮差速小车。它的优势有几个导通压降小6V输入、负载电流0.5A时压降约0.1V-0.3V电机端电压几乎等于电源电压扭矩损失小。控制信号电平兼容3.3V和5VSTM32的GPIO可以直接驱动不用额外加电平转换。内置续流二极管不需要外接反接保护二极管省事很多。待机电流极低控制引脚拉低可以让芯片进入待机模式降低整机功耗。实际接线时TB6612FNG有四个控制引脚AIN1、AIN2、PWMA、STBY对应一路电机另一路BIN1、BIN2、PWMB对应另一路。AIN1/AIN2控制旋转方向PWMA控制PWM占空比调速STBY需要置高才能工作。控制逻辑很简单正转就是AIN11、AIN20反转就是AI N10、AIN21停止就是两个都为0刹车是两路都置1也能实现急停。2.3 关于续流保护和反电动势的坑如果用电机的朋友自己搭H桥或者买裸芯片而不是模块一定要记住续流二极管这件事。电机在断电瞬间会产生反向电动势如果不加续流路径这个电压尖峰可能击穿MOS管或三极管。集成驱动模块TB6612、DRV8833、L298N内部都做了处理裸芯片或分立元件就必须在每个输出端对电源正极加一个快恢复二极管或者用MOS管体二极管但性能不如外置二极管。我自己踩过这个坑第一次用两片L9110S裸芯片驱动TT马达没加任何保护结果马达正反切换时芯片三番五次烧掉示波器一看尖峰直接干到20多伏。后来加装续流二极管和瓷片电容问题彻底消失。3. 硬件连接与电路设计细节3.1 接线图和引脚分配以STM32F103C8T6最小系统板 TB6612FNG模块 两个TT马达为例推荐这样接线TB6612引脚STM32引脚说明PWMAPA0TIM2_CH1A路电机PWM调速AIN1PA1A路方向控制1AIN2PA2A路方向控制2PWMBPA3TIM2_CH2B路电机PWM调速BIN1PA4B路方向控制1BIN2PA5B路方向控制2STBYPA6待机控制置高使能VM7.4V或6V电源正极电机电源VCC3.3V逻辑电源GNDGND共地与STM32共地关于引脚选择有个细节PWM引脚必须选在定时器通道上PA0对应TIM2_CH1、PA1对应TIM2_CH2注意这里PA1同时是通道2如果PWM和方向引脚冲突就容易出Bug。我习惯把两路PWM放在同一个定时器的不同通道上这样两个电机的PWM频率天然一致调速同步性更好。3.2 电源设计为什么电机一转单片机就重启这是新手问得最多的一个问题“我的代码没问题一上电程序能跑但电机一转整个系统就重启”。十有八九是电源设计出了问题。TT马达启动瞬间电流非常大如果STM32和电机共用一个LDO或一块小功率电池电机的启动压降会把系统电压拉到单片机的复位门槛以下从而触发复位。解决办法有三条建议都做给电机单独供电。单片机用USB的5V或者一路稳压电机用电池直接供电两边只在GND处连通。跳线帽那种两路共地的设计其实是正确的千万不要把电机电源和逻辑电源完全隔离因为控制信号需要共同参考地。在电机电源两端并联一个大容量电解电容220uF-470uF耐压高于电源电压再并一个104瓷片电容。电解电容负责吸收瞬态的电流冲击瓷片电容滤高频干扰。尽量选内阻小的电池。普通碳性电池内阻大带动电机瞬间电压跌落特别明显镍氢、锂聚合物电池内阻小表现就好很多。3.3 逻辑电源的另一个隐藏问题3.3V还是5VTB6612FNG的逻辑电源引脚VCC可以接3.3V也可以接5V。接3.3V时STM32的3.3V GPIO高电平可以直接识别接5V时如果STM32引脚是3.3V容限GPIO输出的高电平不能被芯片正确识别为逻辑高。所以最简单稳妥的方案是VCC直接接3.3V与STM32的VD D一致无需任何电平转换。还有DRV8833这种芯片逻辑电源范围比较宽就更不用担心了。如果手头只有L298N模块它的逻辑部分很多模块已经自带了5V稳压输出直接用模块上的5V给逻辑部分供电也行但注意别拿这个5V去给STM32供电模块的5V稳压能力通常不够强。4. 代码实现从GPIO点动到PWM平滑调速4.1 用GPIO实现正反转最小可运行代码先写一个最简单的控制程序让马达正转1秒、停1秒、反转1秒。这里用HAL库演示标准库思路一样只是寄存器操作更直接。// 定义方向引脚 #define AIN1_PIN GPIO_PIN_1 #define AIN2_PIN GPIO_PIN_2 #define AIN1_PORT GPIOA #define AIN2_PORT GPIOA void Motor_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_1 | GPIO_PIN_2; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); } void Motor_Forward(void) { HAL_GPIO_WritePin(AIN1_PORT, AIN1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(AIN2_PORT, AIN2_PIN, GPIO_PIN_RESET); } void Motor_Reverse(void) { HAL_GPIO_WritePin(AIN1_PORT, AIN1_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(AIN2_PORT, AIN2_PIN, GPIO_PIN_SET); } void Motor_Stop(void) { HAL_GPIO_WritePin(AIN1_PORT, AIN1_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(AIN2_PORT, AIN2_PIN, GPIO_PIN_RESET); } int main(void) { HAL_Init(); Motor_Init(); while (1) { Motor_Forward(); HAL_Delay(1000); Motor_Stop(); HAL_Delay(500); Motor_Reverse(); HAL_Delay(1000); Motor_Stop(); HAL_Delay(500); } }注意一个小细节如果使用TB6612还要把STBY引脚拉高否则芯片始终处于待机状态电机怎么都不转。很多新手在排查“代码没问题但电机不动”时就是卡在STBY没置高。4.2 PWM调速定时器配置与占空比计算方向用GPIO控制后速度就要靠PWM了。STM32的定时器可以输出PWM核心是配置三个参数预分频器PSC、自动重载值ARR和比较值CCR。PWM频率由PSC和ARR共同决定$$上 PWM频率 \frac{定时器时钟}{(PSC1) \times (ARR1)}$$以STM32F103为例TIM2挂在APB1上时钟为72MHz。想得到20kHz的PWM频率听不到噪声的频率可以设PSC71ARR49计算得到$$上 \frac{72MHz}{(711) \times (491)} \frac{72MHz}{3600} 20kHz$$占空比就是CCR/(ARR1)的比值。CCR25时占空比50%CCR49时占空比接近100%。HAL库的配置代码如下TIM_HandleTypeDef htim2; void PWM_Init(void) { __HAL_RCC_TIM2_CLK_ENABLE(); htim2.Instance TIM2; htim2.Init.Prescaler 71; // PSC71, 72MHz/721MHz htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 49; // ARR49, PWM频率1MHz/5020kHz htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim2); TIM_OC_InitTypeDef sConfigOC {0}; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 25; // CCR25, 初始占空比50% sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); }此时把PWMA引脚接到PA0TIM2_CH1上写CCR值就能调速度__HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 10); // 占空比约20% __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 40); // 占空比约80%4.3 两轮差速小车的转向逻辑最简实现两轮差速小车用两个TT马达分别驱动左右轮转向原理很简单左轮转右轮不转就向右转左轮慢右轮快就向右转弯两个轮子以不同速度转动即可实现不同半径的转向。整套逻辑就一行左轮PWM、右轮PWM分别给不同CCR值。我当时做小车时把速度抽象成一层接口方便以后加调速或循迹void Set_LeftMotor(int speed) // speed: -100 ~ 100 { if (speed 0) { Motor_Left_Direction(1); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, speed * 49 / 100); } else { Motor_Left_Direction(0); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, (-speed) * 49 / 100); } }这里把CCR范围映射到-100到100负数就切换方向。然后转向就变成“左轮给70、右轮给30”这种直观操作后续加红外循迹、超声波避障只需要改speed参数不用动底层硬件逻辑。5. 常见问题与排查技巧实录5.1 电机不转从硬件到软件的系统排查电机不转是最常见的问题我一般按这个顺序排查基本能在几分钟内定位排查项操作常见原因供电电压用万用表量VM和GND之间电压电池没电、电源线断、降压板限流使能引脚确认STBY已接高电平STBY悬空或没配置芯片待机控制逻辑单独给AIN1高电平、AIN2低电平看是否转动IN1/IN2接线反了或者逻辑配反PWM引脚确认PWM引脚选的是定时器通道对应引脚引脚和定时器通道不匹配共地确认电机电源地、STM32地、模块地是否连在一起地没共、控制信号回不了路烧录状态单独写个GPIO点灯程序验证引脚输出正常引脚被复用、初始化顺序问题如果芯片发热、有异响多半是堵转或者输出短路立即断电摸一下芯片温度避免扩大损坏。5.2 电机一转MCU就复位或者串口打印乱码这个问题前面在电源设计里讲过本质是电源跌落。但还有另一种可能开关电源的纹波大地线细电机的电磁干扰串进了STM32的复位电路或串口线路。处理思路是电机线上加磁环或者绕几圈磁珠滤掉高频干扰。STM32的复位引脚外部加一个104对地电容增强抗干扰能力。串口线用双绞线或者屏蔽线缩短走线长度。终极办法把电机电源和逻辑电源的地星型连接不要让大电流的地和小信号的地混在一起走。我用充电池给六通道小车供电时电机启动瞬间电压从7.4V拉到6.2V系统没复位但PWM输出抖动明显最后在电池两端加了470uF电解电容问题彻底解决。5.3 PWM调速时有啸叫声或者转速不稳PWM频率太低比如几百赫兹会让人耳听到高频啸叫同时也容易导致电机转速波动。推荐把PWM频率设在15kHz到20kHz以上超过人耳听觉上限就不容易听到噪声。但要注意频率太高时电机的感性阻抗变大实际电流会下降速度反而上不去。折中下来20kHz对TT马达是一个很稳的选择。如果转速明显不稳定比如周期性忽快忽慢除了PWM频率问题还要检查脉冲是否平滑。定时器CCR突变太快会导致电机电流突变最好是做一个加速度限制比如设定转速变化量每50ms只允许变化5%这样启动更顺对机械齿轮箱也友好很多。int target_speed 80; // 目标速度 int current_speed 0; while (1) { if (current_speed target_speed) { current_speed current_speed 5 target_speed ? target_speed - current_speed : 5; Set_LeftMotor(current_speed); Set_RightMotor(current_speed); } HAL_Delay(50); }5.4 ST-Link 烧录时提示 no target found 的常见场景不少人在做完电机接线后突然发现程序烧不进去了提示类似“no stm32 target found”或者“error: no stm32 target found! if your product embeds debug authentication”。这种情况往往不是单片机坏了而是你的复位电路或电源受到了电机干扰导致调试接口不稳定。先断开电机电源、单独用USB给开发板供电再试一次如果恢复了就说明是供电问题。另外还要检查SWDIO和SWCLK两根调试线有没有被GPIO复用掉。我自己就遇到过切换电机方向瞬间SWD断开后来在SWDIO线上串了33欧电阻同时在调试口和地之间加了一个104电容再也没出现过烧录失败的问题。5.5 关于IO驱动能力和PWM通道的误区这里再强调一个很容易被忽视的点STM32的GPIO驱动能力是有限的但很多人以为“GPIO输出高电平就能驱动电机模块的逻辑输入端”。如果是TB6612这种CMOS输入输入阻抗高GPIO直接驱动没问题。但如果你的驱动模块是L298N的旧版输入电路带了LED指示灯和大阻值上拉单片机GPIO带载能力不够时电压会被拉低造成逻辑不可靠。这时候可以用开漏输出加上拉电阻的方式提高驱动能力或者加一个74HC245芯片做缓冲。PWM通道的复用也非常容易翻车PA0默认是TIM2_CH1但如果你把PA0复用成了ADC或者普通GPIOPWM信号根本出不来。遇到PWM输出异常第一件事就是查复用功能配置和GPIO_AF是否正确。最后分享一个我自己的工程习惯TT马达听起来简单但要把两个马达跑得协调、稳定其实考验的是电源设计和代码架构。我现在做小车类项目永远先把底层电机驱动抽象好方向、速度、加速度限制都封装成独立函数应用层只关心“往哪走、走多快”不关心寄存器细节。这样后面加循迹、加遥控、加蓝牙逻辑都是同一套代码扩展起来非常快。另外一个实用建议是调试电机控制时不要直接用全速20kHz先降到1kHz左右用示波器看波形对不对确认PWM频率和占空比匹配后再切回20kHz。否则在代码有Bug的情况下高频率PWM会对电机造成很激烈的冲击长时间运转容易烧驱动。TT马达配合STM32做小项目的上限其实很高从两轮差速小车到小型机械臂、从鱼缸喂食器到智能窗帘底层都是这一套“方向控制PWM调速”的组合。把这套基础打牢后续玩什么花样都不慌。