ARTICLE DETAIL

资讯详情

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

STM32驱动TT马达实战:从PWM调速到硬件接线与调试全解析

STM32驱动TT马达实战:从PWM调速到硬件接线与调试全解析 1. 项目概述为什么TT马达是STM32入门绕不开的坎先聊一个几乎所有学STM32的人都会遇到的场景手头有一块开发板想做个智能小车或者机械臂第一件事就是让电机转起来。但很多人在这一步就卡住了——不是电机不转就是转速不可控或者一上电复位。这个看似简单的外设恰恰是理解 PWM、GPIO、定时器、电源设计之间协同工作的最佳切入点。TT马达也叫TT减速电机本质上是一个带减速齿轮箱的直流电机常见的是3V到6V供电空载转速从几十转到两三百转不等视减速比而定。因为便宜、结实、接口就两根线所以成了教学和DIY项目里最常用的执行机构。配合轮胎、编码器或者连杆能做出小车底盘、云台、传送带这些典型结构。这篇教程要解决的问题很直接用STM32驱动TT马达实现正转、反转和可调节的速度控制。我会从硬件连线开始到驱动芯片选型再到用定时器产生PWM调速信号最后补充我实际调试中踩过的一些坑。不管是准备做毕设小车还是第一次接触电机控制顺着这篇文章走一遍应该能少走不少弯路。这里先说一下我刚接触这个话题时的一个认知偏差我以为电机控制核心是代码后来才发现硬件层面如果处理不好代码写得再漂亮也是白搭。所以这篇文章会花不少篇幅讲硬件和驱动逻辑这部分理解透了后面的代码就是水到渠成的事。2. 硬件准备与核心原理拆解2.1 TT马达的基本参数与选型要点TT马达最核心的参数有三个供电电压、空载转速、减速比。市面上最常见的型号工作电压是3V到6V标称6V时空载转速大约160到320RPM转每分钟具体看减速比常见的有1:48、1:120、1:220等。减速比越大转速越慢但扭矩越大能拉的负载越重。选型时的建议是这样的如果是做普通两驱小车1:48是比较平衡的选择速度快一点如果做机械臂或者需要精确角度的项目用1:220甚至更高减速比会更稳因为扭矩大不容易被负载憋停。另外要注意TT马达的轴是圆轴还是D型轴D型轴配联轴器或者轮子时不容易打滑如果买的是圆轴版装轮子前要自己磨一个平面出来这个细节在买配件时容易被忽略。从电学角度看TT马达内部就是一个永磁直流电机加一个减速齿轮组。直流电机等效电路模型很简单一个电阻R串联一个感应电动势源E反电动势R是绕组电阻E和转速成正比。这个模型解释了为什么电机堵转时电流很大——因为转速为零时反电动势为零电流只受绕组电阻限制。TT马达绕组电阻一般在几个欧姆到十几欧姆6V供电时堵转电流可能到几百毫安甚至1A这个数值直接决定你选驱动芯片还是三极管驱动。2.2 从GPIO到H桥电机方向控制的本质逻辑要把直流电机转起来最简单粗暴的做法是用一个继电器或者MOS管控制通断——通电就转断电就停。但这种方式只能控制启停不能控制反转因为改变旋转方向需要改变流过电机的电流方向这就要用到H桥电路。H桥得名于它的电路画法四个开关元件组成一个“H”形拓扑电机接在中间横臂上。四个开关分别称为Q1左上、Q2右上、Q3左下、Q4右下。当Q1和Q4导通时电流从电源正极流经Q1、电机、Q4到地电机正转当Q2和Q3导通时电流方向反转电机反转。这里有个关键点Q1Q2或者Q3Q4绝对不能同时导通。一旦对角线方向的开关同时导通电源直接通过两个开关短路到地这个叫“直通”轻则驱动芯片过温保护重则烧毁MOS管和芯片。所以用集成驱动芯片时它内部会做互锁逻辑但如果你用分立元件搭H桥这个时序问题要靠代码和硬件双重保证。STM32的GPIO本身驱动能力有限推挽输出时一般也就输出几毫安到二十毫安共地后最多能点个LED不可能直接驱动TT马达这种几百毫安级别的负载。所以GPIO到马达之间必须有功率放大环节。这个环节在工程上有两种主流做法用集成的电机驱动芯片比如L298N、TB6612FNG、DRV8833或者用分立元件搭建NPN三极管加MOS管组合等。入门阶段强烈建议用集成芯片省心而且出问题的概率低得多。2.3 核心驱动方案对比L298N、TB6612FNG、DRV8833怎么选市面上的电机驱动模块价格差异很大从几块钱的DRV8833模块到几十块钱的L298N模块都有。我分别用过这三款说下实际感受方便你选型时有参考。项目L298NTB6612FNGDRV8833供电范围5V-12V逻辑部分5V2.7V-5.5V2.7V-10.8V最大连续电流2A实际1A左右稳定1.2A1.5A导通压降较大约1.5V-2V小约0.5V小约0.5V发热情况大常需散热片小小接口复杂度多需跳线帽精简精简适用场景大功率电机/教育演示小型直流电机/智能车小型直流电机/微型机器人L298N是很多教程里的“启蒙芯片”原因是它太经典了资料多而且耐造。但实际用起来有一个非常坑的点导通压降太大。如果输入6V经过L298N之后实际到电机两端的电压可能只有4V多这意味着输出给电机的有效电压被吃掉了不少。而且L298N模块上自带的5V稳压输出电流很小给STM32供电时容易不够用所以通常需要单独给开发板供电。TB6612FNG是目前智能车竞赛和DIY项目里的主流选择。它体积小、压降低、发热少STBY引脚可以在需要时让电机进入待机状态功耗控制比较好。在6V供电下输出到电机的电压基本接近6V同样一颗TT马达用TB6612比用L298N转得更快更有劲。DRV8833是TI的产品特点是供电范围宽、电流足够、保护功能齐全内置过流保护、热关断和欠压锁定。模块上通常带PWM输入和相位输入两种控制模式用法比较灵活。如果你是新手第一款驱动芯片选TB6612FNG或者DRV8833都不会错别选L298N它能用但是体验真的不怎么样。3. 接线与电路连接别让电源成为隐形炸弹3.1 STM32与驱动模块的标准接法以我最常用的接线方案为例STM32F103C8T6最小系统板 TB6612FNG模块 两个TT马达组成一个两驱小车底盘。TB6612FNG模块上有一个电源输入处理电路。VM引脚接电机电源正极这个电压范围是2.7V到5.5V所以我用两节18650锂电池串联标称7.4V就不合适了会用一节锂电池加一个升压模块或者直接用一个5V的稳压电源模块供电。VCC引脚接逻辑电源一般是3.3V或者5V我接STM32的3.3V因为TB6612的逻辑电平兼容3.3V。信号线方面每个电机通道有两根控制线加一根PWM线。以A通道为例AIN1PWMA、AIN2分别接STM32的两个GPIO这两根线决定电机方向PWMA接定时器的PWM输出引脚决定电机速度STBY接3.3V让芯片退出待机模式。B通道同理。关键的接线注意事项VBAT和VM的电源地和STM32的GND必须共地。也就是说电源模块的负极、TB6612的GND、STM32的GND三者要连在一起。否则控制信号没有共同参考点会出现不稳定现象比如电机时转时不转、PWM调速没反应、或者是GPIO电平被干扰导致误动作。这个问题我在早期调试时忽略过后来发现只要把GND连好一大半诡异问题都消失了。还有一个细节电源去耦电容不能省。电机是感性负载换向瞬间会产生反向感应电动势也就是电压尖峰。如果电源线上没有电容吸收这个尖峰可能窜到STM32的电源轨导致复位甚至损坏芯片。制动时或者PWM频率较高时这种尖峰更明显。所以务必在电机电源输入端并一个100uF电解电容再并一个0.1uF陶瓷电容电容尽量靠近驱动模块的电源引脚。3.2 电源方案与电流估算选电源不是随便找个充电头TT马达的启动电流比额定电流大得多因为电机从静止到转动需要克服静摩擦力和惯性。一般启动瞬间电流能达到额定电流的3到5倍。如果电源供电能力不足电压会被拉低出现“电机还没转STM32先复位”的经典故障。简单估算一下单个TT马达额定电流约150mA到250mA堵转电流可能到500mA甚至更高。两个电机同时正转启动瞬间峰值可能到1A到2A。所以最小系统如果只用一个USB口供电USB口典型输出500mA肯定是撑不住的。我用的是18650电池组或者3节AA电池盒或者5V2A以上的USB电源模块单独给电机供电STM32开发板用另一路供电或者用带稳压的电源模块直接从电池取电转5V给开发板。电源选择上的另外一个坑是千万别用充电宝的USB口直接驱动TT马达。充电宝的保护逻辑会检测输出电流如果启动瞬间电流超过它的阈值它会直接关断输出表现就是电机顿一下然后彻底停摆。如果一定要用充电宝只能给开发板供电电机部分单独接电池。3.3 按键和LED的辅助电路设计为了演示控制效果我在调试板上加了两个按键一个正转一个反转和一个LED指示PWM输出状态。按键接法是GPIO输入上拉模式按下时引脚接地读取到低电平。LED串联1k电阻接GPIO和GND之间高电平点亮。这部分电路没什么难度但能帮你快速验证逻辑是否正确按键按下时电机能否从静止平滑启动PWM占空比变化时电机转速是否线性变化。实际上这些辅助外围是调试效率提升的关键不然改一次参数就要重新编译下载一次浪费时间不说还容易把问题混在一起。4. PWM调速原理与定时器配置4.1 占空比、频率和平均电压的直观理解TT马达的转速控制本质上靠的是改变电机两端的平均电压。让PWM信号以高频率切换导通和关断电机因为机械惯性和绕组电感的存在不会像LED那样跟随PWM闪烁而是会平滑地输出一个平均电压。占空比指一个PWM周期内高电平时间占整个周期的比例。占空比50%时理想情况下电机两端平均电压是电源电压的50%实际因为死区、晶体管压降等因素会略低。调速的体验大概是这样占空比10%时电机几乎不转或者转得很弱30%到60%之间线性度比较好80%以上基本接近全速超过90%增益变得不明显。PWM频率是一个需要斟酌的参数。频率太低比如几十赫兹电机会发出明显的“嗡嗡”声转动也会一顿一顿的因为周期太快电机还没转过一个角度就又断电了。频率太高比如几十千赫兹开关损耗变大驱动芯片发热增加但电机运行更平滑安静。对TT马达来说10kHz到20kHz是比较常规的选择。我习惯用10kHz因为在这个频率下电机噪声小驱动芯片的开关损耗也还能接受。4.2 TIM定时器PWM模式配置细节STM32的定时器PWM模式核心逻辑是“计数器CNT在0到ARR之间循环计数当CNT小于CCR时输出有效电平当CNT大于等于CCR时输出无效电平”。这样通过修改CCR的值就能改变占空比而ARR决定了PWM周期。举个例子芯片主频72MHzTIM2挂载在APB1总线上分频系数默认是1因为APB1时钟配置为36MHz时会自动倍频到72MHz。如果我要10kHz的PWM那么定时器时钟 72MHz分频系数PSC 72 - 1 71这样定时器计数频率变成1MHz自动重装载值ARR 100 - 1 99这样PWM频率为1MHz / 100 10kHz此时CCR的范围是0到99占空比分辨率约为1%如果是F103系列用到APB1上的定时器TIM2、TIM3、TIM4要注意APB1时钟和定时器时钟之间的倍频关系——APB1分频系数不为1时定时器时钟是APB1的2倍。很多人在这里配置错导致频率和预期不一致。PWM通道和GPIO的映射关系也很重要。以TIM2的CH1为例如果是STM32F103C8T6PA0可以复用为TIM2_CH1。所以配置GPIO时要把PA0设置为复用推挽输出模式AF_PP并且配置为复用功能AF1F103系列TIM2的复用号是AF1不过HAL库的GPIO_InitStruct.Alternate字段需要看具体型号手册F1系列没有这个字段F4和L4系列才有。4.3 HAL库配置的关键代码解析我习惯用STM32CubeMX生成初始化代码然后自己写控制逻辑。CubeMX里配置TIM2的步骤如下选择定时器TIM2勾选Channel1为PWM Generation CH1。设置Prescaler为71Counter Period为99。打开自动重装载预装载Auto Reload Preload。设置PWM初始脉冲值为0也就是CCR初始为0。生成代码。生成的HAL库代码里PWM启动的核心函数是这样的HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1);这个函数启动了定时器的PWM输出。硬件上一旦这个函数执行PA0引脚就会持续输出PWM波形。设置占空比的函数是__HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, duty);其中duty的取值范围是0到99对应ARR。duty50时占空比为50%电机两侧平均电压约为电源电压的一半。有一个容易被忽略的细节如果修改ARR的值来改变PWM频率最好在修改后重新调用__HAL_TIM_SET_AUTORELOAD(htim2, new_arr)并等待更新事件完成。否则可能造成PWM波形瞬时异常电机出现抖动。4.4 为什么推荐用TIM_CHx而不是简单的GPIO翻转有人可能会问直接用GPIO_WriteBit轮流输出高电平和低电平配合delay延时不也能模拟PWM吗对于LED呼吸灯这种低速应用确实可以。但对于电机调速基于定时器硬件的PWM有不可替代的优势第一定时器PWM由硬件产生占空比和频率都是精确的CPU不需要在中断里不停地翻转引脚。如果用delay模拟当主程序还在执行其他任务或者被中断打断时PWM波形就会失真电机转速跟着不稳定。第二定时器PWM的占空比更新可以做到“安全同步”。在使能了预装载的情况下CCR值在计数周期边界才会生效不会出现一个PWM周期内波形忽然变宽变窄的情况。这对电机控制非常重要因为不平衡的PWM波形会导致电机振动和噪声。第三多个通道的PWM可以同时对多个电机进行控制。比如TIM3的CH1和CH2分别控制左右两个电机时钟基准是同一个定时器两边天然同步对做差速转向非常有利。5. 完整代码实现与调试过程5.1 实现功能与代码整体框架这一节的所有代码都基于STM32F103C8T6最小系统板使用STM32CubeIDE或者Keil MDK均可核心逻辑不依赖具体IDE。我实现的功能如下按下KEY1按键电机正转速度从0平滑加速到目标占空比。按下KEY2按键电机反转速度从0平滑加速到目标占空比。串口打印当前占空比和电机状态方便调试观察。检测到按键长按时电机停止PWM输出占空比归零。整体代码框架包括主循环轮询按键、PWM控制函数、ADC读取电位器可选三部分。实际工程中我建议把PWM控制封装成一个独立模块比如Motor.c和Motor.h这样后期替换驱动芯片或者扩展功能时只需要改这一层的API即可。5.2 核心代码逐步解析先看主函数初始化部分int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); MX_USART1_UART_Init(); uint16_t duty 0; uint8_t direction 0; // 0:stop, 1:forward, 2:reverse while (1) { if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) GPIO_PIN_RESET) { direction 1; } else if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) GPIO_PIN_RESET) { direction 2; } if (direction 1) { HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_RESET); if (duty TARGET_DUTY) { duty 1; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, duty); } } else if (direction 2) { HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_SET); if (duty TARGET_DUTY) { duty 1; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, duty); } } else { HAL_GPIO_WritePin(AIN1_GPIO_Port, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(AIN2_GPIO_Port, AIN2_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); duty 0; } HAL_Delay(10); } }这段逻辑很简单但有两个真实的细节值得注意第一duty每次加1相当于每次循环占空比增加1%配合10ms的延时从0到80%需要800ms也就是0.8秒完成加速过程。这个平滑加速的设计是为了减小启动电流冲击避免电源被瞬间拉垮同时保护齿轮箱。第二方向切换时我先写了对应方向的GPIO电平再逐步增加占空比。这样做的原因是如果从正转直接切到反转时电机两端电压瞬间反向电流会非常大可能触发驱动芯片过流保护。实际项目中方向切换前最好先让PWM归零等待约50ms让电机停下再设置相反方向。上面的代码为了简单起见没有做这个延时但实际调试时建议加上。5.3 定时器初始化函数的解读用CubeMX生成的初始化函数的核心部分长这样static void MX_TIM2_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; htim2.Instance TIM2; htim2.Init.Prescaler 71; htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 99; htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim2); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_1); HAL_TIM_MspPostInit(htim2); }TIM_OCMODE_PWM1这个比较重要当CNT小于CCR时输出有效电平。如果改成PWM2模式输出极性就反了占空比的定义也会跟着变。用PWM1模式时CCR越大高电平时间越长平均电压越高电机越快。HAL_TIM_MspPostInit在HAL库里负责把GPIO复用功能初始化好。如果你是在CubeIDE里手动修改初始化代码千万不要漏掉这一步否则PA0引脚不会映射到TIM2_CH1功能上。5.4 串口调试与日志打印实现调试时用串口看参数变化非常重要。初始化USART1后用简单的方式输出调试信息printf(Duty: %d, Direction: %d\r\n, duty, direction);如果重定向printf到串口需要在工程里实现fputc函数int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF); return ch; }我实测下来把占空比、电流、速度这些数据通过串口可视化出来比用示波器还直观。特别是在调PID或者做匀速控制时串口曲线图能让你一眼看出震荡和超调。5.5 结合HG7881驱动模块的实现变体如果你手头是HG7881模块它的控制逻辑其实和TB6612很类似有IN1、IN2和PWM引脚有的模块叫PWMA/PWMB。配置代码只需要改GPIO引脚定义PWM通道复用逻辑不变。HG7881的优势是价格更低、模块体积更小劣势是逻辑电平只能支持到5V不能直接用3.3V需要检查模块上是否有电平转换电路。没有的话要加逻辑电平转换器或者选STM32的5V容忍引脚。6. STM32常见调试问题与排查技巧6.1 “error: no stm32 target found”问题处理很多人在下载程序时遇到error: no stm32 target found这个问题在STM32调试中非常常见。出现这个提示通常说明调试器无法和芯片建立连接。可能的原因和排查顺序如下BOOT0引脚状态不对。如果BOOT0被拉高芯片会进入系统存储器Boot模式而不是Flash启动。此时用ST-LINK连接时调试器可能能检测到芯片但无法正常下载应用。解决办法是检查BOOT0跳线帽确保它接GND。芯片进入了低功耗模式或者被硬件看门狗卡死。我在调电机项目时遇到过一次程序里开了独立看门狗但喂狗逻辑放错了位置导致芯片反复复位ST-LINK根本连不上。解决办法是用ST-LINK Utility的“Connect under reset”选项下载时强制复位芯片。接线问题。ST-LINK的SWDIO、SWCLK、GND、3.3V四根线是否连接正确线序反了或者接触不良都会导致找不到目标。调试器的固件版本太旧。ST-LINK的固件偶尔需要升级用STM32CubeProgrammer的固件升级功能刷一下就好。6.2 Keil UV5中C51和STM32工程共存问题很多朋友的电脑上同时装着Keil C51和Keil MDK两个版本都能打开但新建工程时死活找不到STM32芯片选项。这个问题的本质是Keil MDK用于ARM芯片的器件库没有安装或者安装路径被C51版本覆盖了。解决办法是去Keil官网下载对应的Device Family Pack比如Keil.STM32F1xx_DFP.2.x.x.pack双击安装即可。如果装完还是找不到把Keil彻底卸载重装先装MDK再装C51或者反过来总之避免两个版本共用同一个安装目录。6.3 电机抖动、转速不稳的排查步骤电机抖动、转速不稳这类问题在PWM调试中最容易遇到几乎所有人都经历过。排查思路按顺序来第一步确认PWM频率和占空比设置是否合理。把频率调到10kHz到20kHz之间占空比从小到大缓慢增加观察电机是否有从静止到启动的平滑过渡。如果电机在低占空比时剧烈抖动多半是频率太低或者扭矩不足调整减速比大的电机试试。第二步检查电源电压是否稳定。用万用表测量电机供电电压空载和满载时的变化。如果满载时电压下跌超过0.5V说明电源内阻偏大或者线太细换粗一点的杜邦线或者直接焊接。第三步驱动芯片的STBY引脚是否被拉高。很多新手忘了把TB6612的STBY接高电平导致芯片一直处于待机状态PWM信号进去了但电机没反应。这个问题在排查时非常容易忽略。第四步检查共地。如果GND没有连在一起你会发现PWM信号虽然正常但电机完全不受控制或者有随机震动。这是新手最隐蔽的坑之一。6.4 死区电压现象与处理即使占空比调到很小比如5%部分TT马达依然不会转动。这是因为直流电机存在启动电压阈值电机两端的电压必须超过这个阈值才能克服静摩擦力启动。这个现象叫“死区电压”在不同的电机上表现不同大致在额定电压的10%到20%之间。如果要实现低速平稳控制方案有三个一是更换更高减速比的电机增大低速扭矩二是用更高的PWM频率并配合更低占空比让电机获得更平滑的平均电压三是在启动瞬间给一个短时的高占空比脉冲把电机“踢”起来然后回落到目标低速。第三种方案在实际项目中非常常用尤其是做循迹小车时需要极低速通过弯道。6.5 常用问题速查表故障现象可能原因排查方向电机完全不转电源未接通/驱动芯片未使能检查VM电压、STBY电平、GND共地电机只能正转不能反转AIN1/AIN2电平逻辑错误交换控制信号接线确认方向定义转速不可调PWM信号未接或GPIO复用配置错误用示波器或万用表频率档量PWM引脚电机突然停转过一会又转驱动芯片过温保护降低占空比或者加散热片一启动就MCU复位电源瞬间电压跌落单独给电机供电加去耦电容串口输出乱码波特率不匹配/共地问题确认波特率设置检查GND连接电机有明显的齿轮异响减速齿轮磨损/电机负载过大更换电机或减轻负载7. 进阶应用与扩展思路7.1 用正交编码器测量TT马达转速TT马达配一个带编码器的版本就是常见的“TT马达带霍尔编码器”模块。编码器输出两路正交信号A相和B相通过检测两路信号的相位关系可以判断电机旋转方向通过计数脉冲数可以计算转速。STM32的定时器可以配置为Encoder Mode硬件上自动解码正交信号。把定时器配置成编码器模式后计数器的值会随着电机旋转自动增减CPU只需要读取CNT寄存器就能知道位置变化。用这种方式做闭环速度控制、轮式里程计比靠PWM开环控制可靠得多。这也是从“能转”到“控得住”的分水岭。7.2 结合HAL库的ADC实现速度闭环要让电机在实际负载变化时保持恒定速度开环PWM是不够的。一个简单的速度闭环方案是用ADC读取电位器设定目标速度用编码器读取实际速度两者误差经过PID计算后修正PWM占空比。HAL库中ADC多通道采样有两种方式一种是软件触发逐次转换简单但CPU占用高另一种是定时器触发ADC DMA把采样结果自动搬运到内存数组几乎不占用CPU资源。做电机控制时我强烈建议用后者因为速度环控制周期可以做到毫秒级而软件触发在复杂任务下很难保证稳定周期。7.3 两轮差速小车的运动学基础如果你做的是两驱小车最基础的运动控制是差速转向两个电机速度相同则直行左轮速度大于右轮则右转反之左转。到这一层控制逻辑就从“单电机启停”升级到“双电机协同”了。用STM32控制两个电机需要两个定时器输出两路PWM。我通常用TIM2的CH1和CH2输出左轮右轮各自的PWM方向控制用4个GPIO。代码架构上把“设置某轮速度比例”抽象成一个函数之后做循迹、避障、遥控功能时只需要调用这个函数不用再碰底层寄存器。7.4 移植到STM32CubeIDE与VSCode环境如果你还在用旧版的标准外设库建议尽早迁移到HAL库。新建工程的流程是配置好CubeMX工程后用CubeIDE打开或者生成Makefile后导入VSCode配合arm-none-eabi-gcc工具链编译。VSCode插件比如“Arm Embedded Debugger”可以配合OpenOCD做调试体验已经接近IDE了而且启动速度快很多。迁移过程中最容易出错的是外设初始化代码的命名差异。标准库的GPIO_InitTypeDef到HAL库的GPIO_InitTypeDef虽然结构体名字一样但字段名有变化比如标准库的GPIO_Mode到HAL库是Mode。这类错误编译时就能发现不算大问题。8. 实操总结与避坑经验分享8.1 从零到一完整复现的清单如果是第一次照着这篇教程做我建议完整走一遍下面的清单避免漏掉关键步骤准备硬件STM32F103C8T6最小系统板、两个TT马达、一个TB6612FNG驱动模块、一个ST-LINK下载器、按键和LED若干、杜邦线若干、5V电源模块。在STM32CubeMX里创建工程配置TIM2的CH1为PWM模式配置按键输入GPIO配置一个串口用于调试。接线驱动模块的VM接5V电源VCC接3.3VSTBY接3.3VAIN1/AIN2接两个GPIOPWMA接PA0确保所有GND共地。编译下载用串口助手观察PWM占空比的数值变化同时用示波器或者万用表的频率档确认PA0引脚确实有PWM波形。逐步增加占空比观察电机是否转动、转向是否正确、噪声是否正常。如果以上都正常再接入编码器或者做双电机差速控制进入进阶阶段。8.2 我踩过的三个最典型的坑第一个坑是电源问题。当初我用一个9V电池直接给L298N供电再用L298N模块上的5V输出给STM32供电结果电机一启动STM32就复位。后来用万用表一量9V电池在电机启动瞬间被拉到了4V多STM32的供电电压直接跌到复位阈值以下。从那以后我的电源原则变成了电机电源和逻辑电源物理隔离至少不能用同一路直接并联。第二个坑是PWM频率的选择。一开始我在F103上用了1kHz的PWM结果电机发出刺耳的滋滋声转速也控制不平滑。后来把频率提高到10kHz之后噪声几乎听不到了转速曲线也流畅很多。第三个坑是驱动芯片的使能引脚。用TB6612的时候我一度以为STBY就是电源意思随便接了个GND导致芯片一直待机电机怎么都不转。查了半天才发现是STBY必须拉高才能让输出级工作。这种低级错误恰恰说明看数据手册是多么重要。8.3 后续可以继续深入的方向这篇教程写完你已经能够实现“用STM32让TT马达转起来并且控制方向”。在这个基础上值得继续深入的方向有这么几个一是PID速度闭环控制让电机在负载变化时依然保持恒定速度二是多电机协同控制比如麦克纳姆轮底盘的四个电机需要更高维度的运动解算三是接入无线遥控或者视觉模块把电机控制接到实际应用场景里。每一步推进本质上都是同一条学习路线从硬件原理出发理解信号链路上的每一个环节再回到代码里用抽象接口去掌控它。希望这篇教程能帮你把这个起步走稳。
返回列表