STM32控制TT马达实战:从原理到代码,轻松实现智能小车驱动
2026/9/8 20:07:07 网站建设 项目流程

我一直觉得,STM32入门这件事,最大的坎儿不是寄存器、不是HAL库,而是手里没有一样能实实在在动起来的东西。流水灯点得再花哨,那也只是在“证明单片机会喘气”。直到你接上一个TT马达,让轮子转起来,让小车跑起来,那种“我真的在做一个系统”的感觉才算是到位了。

这个教程我拖了很久没写,主要是觉得TT马达太基础,随便搜搜到处都是资料。但真到自己上手带项目才发现,越是这种“人人都讲”的东西,越容易在细节上翻车。供电不足导致复位、PWM频率不对导致电机嗡嗡叫但不转、驱动芯片选错直接烧IO——这些坑,网上教程很少给你讲透。今天这篇就针对TT马达,从硬件原理到手写驱动代码,再到实际调试中会踩的坑,一次性说完。

1. 项目概述与核心思路拆解

1.1 我们到底在做一件什么事

TT马达,也叫TT减速直流电机,是各种智能小车、扫地机器人、机械臂底座项目里最常用的执行机构。它的核心结构是“直流电机本体 + 齿轮减速箱”,通过减速箱把电机的高转速降下来,同时增大输出扭矩,让小车能真正“爬”起来而不是原地打滑空转。

这次项目的目标很明确:用STM32控制一个TT马达,实现三件事——让马达转起来、控制转速、控制方向。听起来简单,但背后涉及的知识点其实覆盖了STM32非常核心的几个外设:GPIO的输出模式配置、定时器的PWM输出、以及外部中断或者编码器接口(如果选带编码器的TT马达)的速度反馈。

我用的开发环境是STM32CubeMX + Keil MDK,芯片型号选了STM32F103C8T6,也就是大家常说的“蓝丸”核心板。选这颗芯片没什么特别理由,就是资料多、便宜、皮实,新手折腾坏了也不心疼。如果你用的是F407或者G031,思路完全一样,配置界面稍作调整即可。

1.2 为什么用TT马达而不是其他电机

很多初学者会纠结:直流电机种类那么多,有刷的、无刷的、步进的、舵机,为什么教程都选TT马达?

无刷直流电机的转速方向和有刷直流电机其实是两个方向的门槛。无刷电机控制精度高、寿命长,但是需要专门的驱动电路,也就是电调,还要处理换相逻辑,学习曲线陡。步进电机定位精准,但又重又慢,驱动模块也贵,而且对新手来说“转多少度”这种控制方式反而不如“转多快”直观。

TT马达属于有刷直流电机,控制逻辑最简单:给正电压就正转,给负电压就反转,电压大小决定转速。配合一颗几块钱的TB6612或者L298N驱动模块,再加上STM32的PWM,就能实现“电压可调 + 方向可换”的全功能控制。这也是为什么几乎所有智能小车教程都拿它当入门首选。

1.3 系统整体方案设计

整个系统的硬件链路是这样的:

  • STM32的GPIO输出方向控制信号(决定电机正转还是反转);
  • STM32的定时器通道输出PWM信号(决定电机转速);
  • PWM信号和方向信号共同进入电机驱动芯片TB6612;
  • TB6612根据输入信号,输出对应大小和方向的电压给TT马达;
  • TT马达带动减速齿轮,输出扭矩驱动车轮。

软件层面,我用HAL库来做,整体流程是:CubeMX初始化时钟和GPIO,配置一个定时器的PWM通道,然后在主循环里通过改变比较寄存器(CCR)的值来改变占空比,再通过两个普通IO的电平组合来切换方向。

这个方案的好处是把“逻辑控制”和“功率驱动”完全分离。STM32的IO口只能输出3.3V、最大20mA左右的电流,根本带不动TT马达启动时可能达到几百毫安的电流。中间加了一级驱动芯片,既保护了MCU,又让功率路径更干净。

2. TT马达硬件原理解读与驱动方案对比

2.1 TT马达的电气特性与引脚定义

先搞清楚TT马达本身是什么脾气,后面接线才不会懵。

标准的TT马达工作电压通常有两个规格:3V-6V和6V-12V。智能小车套件里最常见的其实是3-6V的版本,使用两节或者四节AA电池供电,空载转速一般在200-300RPM左右(取决于减速比,常见1:48)。堵转电流可能达到1A以上,正常负载运行时电流大约在100-300mA之间。

TT马达只有两个引脚,没有正负之分,因为直流电机本身就是对称的——你给它正向电压它就正转,反过来接就反转。这也是它有刷电机做方向控制的原理:通过H桥电路切换供电极性。

人手一个的TT马达其实分两种:不带编码器的(纯动力马达)和带编码器的(带AB相霍尔编码器,一般在电机尾部多出一块小PCB)。编码器版本虽然贵几块钱,但如果你后面想做闭环速度控制,比如让小车走直线,那编码器就是必需品。本次教程先讲不带编码器的版本,把PWM调速和方向切换讲透,编码器的使用放到下一篇详说。

2.2 电机驱动的核心:H桥到底在干什么

方向控制靠的就是H桥电路。这个名字很形象——四个开关管组成一个“H”形状,电机接在中间横杠上。

具体工作原理不复杂:

  • 当左上和右下两个开关管导通时,电流从左边流向右边,电机正转;
  • 当右上和左下两个开关管导通时,电流从右边流向左边,电机反转。

注意绝对不能把同一侧的两个开关管同时导通,那就直接短路了,业界称之为“直通(shoot-through)”,轻则烧管子,重则起火。

我们自己用分立元件搭H桥不是不行,但性能必然拉胯,尤其是死区控制、过流保护这些细节根本做不到位。所以项目里一律推荐直接用集成驱动芯片——目前市面主流的型号有两个:

驱动芯片工作电压峰值电流导通压降接口逻辑适用场景
L298N5V-35V2A高(约2V左右)5V TTL大功率电机、双电机小车
TB66122.7V-5.5V1.2A低(约0.3V)3.3V/5V均可TT马达、小功率直流电机

L298N是经典老将,缺点是导通压降太大——2V左右的压降意味着明明给了6V电源,到了电机端可能只有4V,白白浪费电能。而且它的逻辑电平要求5V,跟STM32的3.3V IO直接对接不太友好,通常还得加电平转换或者用5V容忍引脚硬怼。

TB6612就是当前TT马达项目的首选,供电范围覆盖3V-6V,正好匹配TT马达工作电压。导通压降只有0.3V左右,发热小。逻辑输入兼容3.3V,STM32的IO可以直连,不用任何电平转换电路。模块体积也很小,面包板和洞洞板都能轻松放得下。

2.3 PWM调速的原理与选择

电压高低决定电机转速这个道理大家都懂,但STM32并没有“直接输出可变电压”的功能,只能输出数字信号——要么高电平要么低电平。调速就需要靠PWM(脉冲宽度调制)来模拟。

PWM的精髓在一个“占空比”上,一个周期里高电平所占的比例就是占空比。在一定频率下,虽然IO口输出的是0到3.3V跳动方波,但电机的机械惯性会滤掉这种快速波动,等效于接收了一个平均电压,平均电压 = 3.3V × 占空比。

我举个具体数字帮助理解:假设频率是10kHz,一个周期就是100微秒,如果高电平占30微秒,那占空比就是30%,等效电压就是3.3V × 30% ≈ 1V。换到电机上,即使系统供电是6V,驱动器输出的等效电压也会相应调整,速度自然就降下来了。

这里就引出了PWM频率选择的学问。如果频率太低,比如50Hz,电机会一冲一冲地转,伴随明显的“滋滋”或者“嗡嗡”声响,这就是典型的脉动转矩。但频率也不能太高,因为驱动芯片有开关损耗,频率高了芯片发热增加,电机电感也会对高频信号产生阻抗,导致实际电流反而上不去。

TT马达这种小电机的甜点频率在10kHz到20kHz之间。我用的是10kHz,驱动芯片不热,电机声音也小。不要听网上有些人说“频率随便设个1kHz就行”,那只能让电机转,转起来的噪音会让你怀疑人生。

3. 实操过程:从CubeMX配置到代码实现

3.1 CubeMX引脚分配与定时器选择

硬件连接先定下来。我用的引脚分配如下,不一定是最优解,但你照着接一定没问题:

  • TB6612的PWMA接STM32的PA8(TIM1_CH1);
  • TB6612的AIN1接PB0;
  • TB6612的AIN2接PB1;
  • TB6612的STBY接3.3V(一直使能);
  • TB6612的VM接6V(4节AA电池或2节锂电池);
  • TB6612的VCC接3.3V(逻辑电源);
  • TB6612的GND跟STM32共地(非常重要,不共地一切白搭)。

打开STM32CubeMX,芯片选STM32F103C8T6,时钟配置为外部8MHz晶振,然后PLL倍频到72MHz主频。这一步很关键,因为PWM输出的实际频率跟定时器的时钟源直接相关。

PA8对应的TIM1_CH1,TIM1是挂在APB2总线上的高级定时器,时钟频率在72MHz主频下就是72MHz。设置PWM频率10kHz,换算预分频器(Prescaler)和自动重装载值(Counter Period)的过程是这样的:

  • 预分频值PSC = 72MHz / 10kHz / ARR - 1;
  • 为了让占空比调节有细腻度,我期望ARR值在100-1000之间,取整后选择PSC = 72 - 1,ARR = 100 - 1。

这样算下来:实际PWM频率 = 72MHz / 72 / 100 = 10kHz,完美。占空比的分辨率就是1%,也就是100档,对TT马达调速完全够用。

生成代码前,记得在TIM1的Configuration页面里把Channel1设置为PWM Generation CH1,然后在Parameter Settings里把Pulse设置为50(初始占空比50%)。

3.2 HAL库PWM代码实现

生成工程后,代码修改量其实非常小。先看PWM的启动部分,这个必须加在初始化完成之后:

/* 启动PWM输出 */ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

注意HAL_TIM_PWM_Start只是启动了PWM波形输出,但此时比较寄存器里的值还是CubeMX里设置的初始值。这个时候电机应该是维持在一个固定速度转,还不能调速,我们需要在程序运行过程中不断改变比较值。

调节占空比的核心代码就一行:

/* 设置占空比,第二个参数取值范围0-100 */ __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty_cycle);

这个宏的本质就是修改TIM1->CCR1寄存器的值。PWM频率是固定的,每个周期内高电平时间的长短完全由CCR1决定,所以把CCR1从0改成100,就等于占空比从0%变成100%,电机从停止到全速。

方向控制就更简单了,就是两路GPIO的配合:

/* 正转 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); /* 反转 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); /* 刹停(两端同时拉低/拉高等效于短路电机) */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);

这里有个细节值得多说一句。很多人以为停转就是把PWM占空比设为0,这其实不对。占空比为0等于电机两端没有电压,电机靠惯性继续转,这叫“滑行”。真正要做到快速停止,需要把AIN1和AIN2同时拉高,也就是刹车。H桥两侧下管导通,电机两端短路,转子的动能被绕组电阻快速消耗,电机很快就停了。

如果你做的是小车项目,想让它精准停在某条线前或者某个地点附近,用滑行大概率会冲过头,必须做刹停处理。

3.3 一个完整的电机测试Demo

理论说再多不如跑起来看效果。写一个简单的测试程序:电机先正转,占空比从0平滑加到100,再降回来,然后反转重复一遍。这个过程能看到转速的平滑变化,也能验证PWM通道和方向控制是否同时正常工作。

while (1) { /* 正转加速 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); for (uint16_t duty = 0; duty <= 100; duty++) { __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty); HAL_Delay(10); } /* 正转减速 */ for (uint16_t duty = 100; duty > 0; duty--) { __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty); HAL_Delay(10); } /* 刹车1秒 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); HAL_Delay(1000); /* 反转加速 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); for (uint16_t duty = 0; duty <= 100; duty++) { __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty); HAL_Delay(10); } HAL_Delay(2000); }

这个Demo跑起来后,你用手捏住电机轴应该能感觉到扭矩在持续变化,松开后转速随占空比线性起伏。如果一切正常,那恭喜你,STM32控制直流电机最核心的部分已经拿下了。

4. 常见问题与调试心得实录

4.1 电机不转,排查顺序很重要

但凡新手做这个项目,十个有九个卡在“电机不转”上,然后就开始胡乱换线、乱试。我的建议是,不要急,按照顺序一步步排查。

首先量一下TB6612的VM和GND之间的电压。很多新手用电脑USB口或者直接从STM32的5V引脚给驱动模块供电,这是最典型的坑。TT马达启动瞬间的电流可以飙到1A以上,而USB口的输出能力一般只有500mA,电压直接掉到4V以下,电机当然不转。供电一定要用独立的电池组,或者质量靠谱的DC-DC模块。

接着看逻辑部分。TB6612的VCC引脚接3.3V了吗?STBY有没有拉到高电平?我见过太多人把这个使能脚悬空,然后电机死活不转。STBY是待机控制脚,低电平会让芯片进入待机状态,所有输出都不工作,必须接高电平才正常。

然后是控制信号有没有真的送进芯片。用示波器或者万用表测一下AIN1和AIN2的电压,跟代码逻辑对不对得上。如果没示波器,万用表也能看个大概:正转时AIN1应该是3.3V,AIN2是0V,反转就反过来。如果这两个信号都不对,就别赖驱动芯片,问题出在STM32的GPIO配置上,回去看看CubeMX是不是把引脚模式配成ADC或者复用功能了。

4.2 电机转起来之后,噪音大得像拖拉机

跑通之后,第二个常见的坑就是电机振动和噪音。这个时候先不要怀疑电机坏了,大概率是PWM频率太低了。

我试过用1kHz的频率驱动,电机转是转,但那个“嗡嗡”声跟电锯差不多,用手摸电机外壳都能感觉到明显的振动。把频率提升到10kHz以上,声音立刻细腻很多,听起来只是电机换向的轻微“沙沙”声。

如果提高频率后噪音还压不下去,那就要检查PWM和电源之间的滤波电容了。TT马达这种带换向器的有刷电机,电刷在换向瞬间会产生大量火花和反向电动势噪声,不仅干扰自身运行,还会沿着电源线往STM32那边窜。解决方法是电机引脚两端直接并联一个100nF的陶瓷电容,再并一个10uF-100uF的电解电容。这个操作能极大改善系统稳定性,很多诡异的重启、死机问题都是这个噪声引起的。

4.3 电机能转,但速度不受控制或者完全跑飞

如果你发现代码里改了占空比,电机速度根本不变,或者ICU直接跑飞,多半是PWM初始化没生效。

HAL库有几个坑是新手常踩的。比如CubeMX里明明开启了TIM1的PWM通道,但生成的代码中漏掉了自动启动的代码。解决方法是手动调用HAL_TIM_PWM_Start。

还有更隐蔽的:用了错误的定时器句柄。比如你配置的是TIM2,结果代码里操作的是htim1,系统不会报错,但PWM就是出不来。追查这种问题非常痛苦,建议从一开始就养成习惯——每次改动后在初始化代码周围加注释,标明用的是哪个定时器哪个通道。

4.4 一个容易被忽略的问题:共地

这是嵌入式开发里最常见也最容易被忽视的细节——所有模块的地必须连在一起,才能以同一个电压参考点为基准工作。

如果你发现一个奇怪的现象:STM32单独下载程序正常,跟电脑通信正常,但一接上电机电源,整个系统就乱套——要么随机复位,要么串口数据乱码。先别去查代码逻辑,拿万用表量一下驱动模块的GND和STM32的GND之间是不是0V。如果不是,把它们接上,90%的妖魔鬼怪瞬间消失。

共地问题还会引发一个更隐蔽的症状:电机转动时STM32偶尔复位。这是因为电机电流经“地回路”倒灌到了单片机的地线上,造成地电位被抬高。用独立的地线把功率地和逻辑地分开走,回到电源端再单点汇接,能有效避免这个问题。

4.5 实测下来的电源选择和速度感受

项目调试了一段时间,最后说说实际使用的电源方案。我测试了三种供电方式:

  • 2节AA电池串联(3V):能转,但堵转时速度掉得厉害,明显供不上劲;
  • 4节AA电池串联(6V):最理想的工作状态,加速有力,调速线性度也好;
  • 3.7V锂电池单节:能用,但转速偏低,驱动力不太够,适合做玩具级别的小车。

所以如果你做的是双马达后驱小车,建议直接上4节AA电池,或者两节3.7V锂电池串联再通过降压模块稳压。记住电源的额定电流最好在2A以上,否则电机同时启动时电压跌落会非常明显。

最终的项目实测结论是这样的:STBY接3.3V常开,AIN1、AIN2控制方向,PWMA输入10kHz的PWM信号控制等效电压,TT马达整套系统运行稳定,调速响应非常灵敏——从最低转速到最高转速,目测响应时间不到100毫秒,足够满足绝大多数小车项目的需求。

5. 从驱动到闭环:下一步可以怎么玩

如果这篇文章你已经完全消化了,TT马达在你手里应该已经能顺畅地正反转调速了。接下来值得往三个方向延伸一下。

第一个方向是做双电机差速控制。小车有两个后轮、两个TT马达,你需要用第二个定时器的另一个通道输出PWM,再配合两路GPIO控制另一个驱动芯片。差速转向的核心逻辑是:两个轮子转速不同,车身自然转弯。这里你会遇到一个问题——两个电机的机械特性不可能完全一致,同样的占空比转速也不一样,所以要加入编码器反馈来统一转速。

第二个方向就是带编码器的TT马达。电机尾部那个多出来的霍尔编码器每转一圈会产生固定数量的脉冲(常见1圈约11或20个脉冲,经过减速箱之后,轮子转一圈大约对应几百个脉冲)。用STM32的定时器输入捕获模式去测脉冲频率,就能反算出车轮转速,从而做PID闭环控制。

第三个方向是把控制搬到无线端。用ESP8266或者蓝牙模块接收手机指令,STM32解析指令后控制电机转速和方向。这一步做完,一台可以手机遥控的智能小车就成型了。

按我个人的经验,从“让电机转起来”到“根据指令稳定地转到指定转速”,中间需要经历的概念跨度其实很大,涉及定时器捕获、中断服务函数、PID整定、串口协议解析等一整套知识链。但你把这篇文章的基础打扎实了,后面的每块砖都能一块块垒上去。别急,慢慢来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询