搞过步进电机的朋友应该都有体会:电机本身没什么神秘的,真正绕人的是“怎么把脉冲发得又准、又稳、还能按需要变速”。很多新手一上来就把步进电机的脉冲交给delay延时循环去刷,结果要么电机一顿一顿,要么高速直接丢步,最后开始怀疑人生。这篇教程我拿STM32F103C8T6最小系统板搭配TB6600驱动器来跑42步进电机,核心不是让你复制代码,而是把定时器PWM的来龙去脉讲清楚,包括定时器时钟怎么算、PWM频率和转速怎么换算、为什么加减速能救回你一半的机械和噪声问题。看完之后,你不仅能跑起来一个电机,还能自己动手改速度曲线,拓展到XY平台甚至小型CNC运动控制。
1. 项目背景与整体方案设计
1.1 这套组合能干什么
先定位一下:F103C8T6是最常见、最便宜的Cortex-M3内核Blue Pill小系统板,TB6600是几十块钱一块的步进电机驱动器。这两个搭在一起,能覆盖的场景其实很广:小型传送带、旋转工作台、丝杠滑台、3D打印机改造、激光雕刻机XY轴驱动,都能做。这套系统的控制方式本质上是“开环脉冲控制”:单片机负责发脉冲,驱动器负责把脉冲翻译成电机线圈里的电流波形,电机按脉冲一步步转。
这样做最大的优势是控制逻辑简单、成本低、在速度不激进的应用里非常可靠。代价是系统不带反馈,一旦低频共振或加速太快导致丢步,系统自己完全不知道。所以整个项目的核心目标可以概括为一句话:把脉冲发得足够准,把速度曲线安排得足够合理,让电机在开环条件下也不丢步。
1.2 为什么选定时器PWM而不是软件刷GPIO
这点我要多说两句,因为它是整个项目的认知基础。步进电机需要的输入,说白了就是一串方波脉冲:一个脉冲走一步,脉冲频率决定速度,脉冲总数决定位置。于是问题就坍缩成两个:怎么产生准确频率的方波,怎么让这个频率按计划变化。方案对比一下:
| 实现方案 | 频率精度 | CPU占用 | 可扩展性 | 适用场景 |
|---|---|---|---|---|
| delay + GPIO翻转 | 差,受中断干扰 | 极高 | 多路后直接崩溃 | 不推荐 |
| 定时器PWM | 好,硬件连续输出 | 极低 | 可多路同时输出 | 匀速/产品化推荐 |
| 输出比较翻转 + 中断 | 很好,逐拍可控 | 中等 | 适合加减速控制 | 加减速场景推荐 |
| 定时器 + DMA | 很好 | 几乎没有 | 适合复杂速度曲线 | 进阶推荐 |
用软件delay刷GPIO最大的问题不是精度,而是“抖动”和“堵塞”。如果你在主循环里顺便处理按键、OLED刷新、串口打印,任何一个耗时的函数都会让下一次脉冲延后,电机瞬时速度就变得毛躁。用定时器PWM之后,脉冲波形完全由硬件产生,主程序去忙别的,电机的节奏一点不受影响。这就是我标题里强调“定时器PWM高级功能”的原因:在这个项目里,定时器不是用来点灯的,而是整条运动控制链路的节拍器。
1.3 整体信号链路
整个系统信号流大致是这样的:
STM32的定时器/GPIO → PUL(步进脉冲)、DIR(方向)、EN(使能) → TB6600驱动器 → A+、A-、B+、B- 四根线 → 42步进电机
这套方案中,单片机完全不需要、也不应该直接驱动电机线圈。42步进电机的相电流一般在0.5A到2A之间,STM32的引脚最多承受几毫安到二十毫安,硬接必烧。TB6600在这里的角色就是“功率翻译官”:把低压逻辑信号转成微步进电流波形,同时承担过流、过温、低压保护。理解了这条链路,后面所有接线和参数设置都不会跑偏。
2. 硬件接线与TB6600配置
2.1 拨码开关与细分设置
TB6600模块上面通常有几组拨码开关,其中一组负责细分,一组负责电流。先说明一个残酷现实:市面上卖的“TB6600模块”很多都是各家二次设计的产品,拨码开关定义并不完全统一。我手里有过五六种不同版本的蓝色铝壳板,S1到S3对应细分的逻辑至少见过三种。所以第一步永远是“看你手上那块板的丝印说明”,而不是照抄网上的表。
不过大体的规律是接近的。常见版型里细分和每转脉冲数的关系如下,仅供参考:
| 细分模式 | 每转脉冲数(以1.8°电机为例) | 适用场景 |
|---|---|---|
| 1(整步) | 200 | 速度最快但振动大 |
| 1/2 | 400 | 入门体验 |
| 1/4 | 800 | 小幅改善平滑度 |
| 1/8 | 1600 | 常用推荐 |
| 1/16 | 3200 | 更平滑但高速偏弱 |
我在项目里默认用1/8细分。并不是细分越大越好:细分提高了位置分辨率和低速平滑度,但代价是同样的转速需要更高的脉冲频率,而TB6600的输入脉冲上限和STM32中断能力都是有限的。1/8在大多数42步进场景下是甜点值。
电流设置同样要看丝印。以额定电流1.7A的42步进电机为例,设置到1.5A到1.7A之间即可,不要贪大。电流太大电机温升会很难看,步进电机长时间过流工作可能烧断线圈。
2.2 接线:共阳还是共阴
TB6600信号输入端一般是六个端子:PUL+、PUL-、DIR+、DIR-、EN+、EN-。这六个端子本质是光耦输入,没有绝对极性,只要你能在光耦两端建立足够的电流方向就行。最常用的接法是“共阳”:
| STM32F103C8T6 | TB6600端子 | 说明 |
|---|---|---|
| PA8(TIM1_CH1,PWM输出) | PUL- | 步进脉冲输入 |
| 板载5V | PUL+ | 共阳接法 |
| PB5(推挽输出) | DIR- | 方向控制 |
| 板载5V | DIR+ | 共阳接法 |
| PB10(推挽输出,可选) | EN- | 使能控制 |
| 板载5V | EN+ | 共阳接法 |
注意:共阳接法里脉冲是“低电平有效”,也就是PWM拉低时光耦导通。这个细节不影响脉冲频率,但影响你对占空比的理解。如果你改用共阴接法,则PUL-接GND、PUL+接STM32引脚,此时高电平有效。
DIR这块我踩过一个坑:不要在脉冲正在发的时候瞬间切换方向。很多驱动器手册都规定方向信号要提前于脉冲边沿至少几微秒稳定。我自己的习惯是:先停脉冲,再改DIR,然后延时20微秒左右,再重新发脉冲。虽然多数情况下不这么做也“能用”,但为了长期可靠,这个顺序值得养成习惯。
EN引脚有些人直接不接。TB6600模块在EN悬空时多数默认是使能状态,也就是电机锁定、能保持力矩。只有当你需要“脱机”释放电机力矩、可以手动转动电机轴时,才需要把EN拉到有效电平。我建议还是把EN接上,因为在调试机械结构时,动不动就要手动转一下轴,有EN控制会方便很多。
2.3 电源和滤波电容
电机电源接到TB6600的VCC端子,电压范围一般是9V到42V,具体看模块丝印。电流建议按电机额定电流的1.5倍以上预留。我常用的42电机在1.7A额定电流、24V供电下,电源至少买5A的开关电源。千万别用小变压器加整流毛刺电压直接怼上去,驱动器内部逻辑很容易被干扰得乱跳。
另外在TB6600电源端子上并联一个470uF到1000uF的电解电容,是新手最容易忽略的一步。别小看这个电容:步进电机在换相瞬间会有很大的电流脉动,如果电源线距离较长,线缆电感会让母线电压产生尖峰,轻则噪声大,重则驱动器过压保护甚至烧管。电容尽量靠近驱动器电源引脚焊接,效果最明显。STM32和驱动器之间的地要不要共?因为TB6600信号端自带光耦隔离,逻辑地和功率地理论上可以不共地。但大量实战经验表明,共一下会更稳定,或者说共地之后排查问题的变量少一个。如果你的控制板供电来自独立USB口,而驱动电源是24V开关电源,建议把STM32的GND和开关电源GND连起来,避免光耦输入的共模电压漂移导致误触发。
3. 定时器PWM核心原理与高级功能
3.1 定时器时钟从哪来
要算PWM频率,先搞清楚定时器时钟。在F103C8T6上:
- TIM1挂在APB2总线上,系统时钟72MHz时,TIM1时钟就是72MHz。
- TIM2、TIM3、TIM4挂在APB1总线上。APB1的频率默认是36MHz,但STM32有个规则:当APB1预分频系数不等于1时,定时器时钟是APB1频率的2倍。默认CubeMX配置下APB1=36MHz、预分频系数=2,所以TIM2/3/4的时钟同样是72MHz。
这个“×2”经常坑新手。有人把TIM2的时钟当成36MHz去算,结果PWM频率差一倍。PWM频率公式是:
f_PWM = f_timer / ((PSC+1) × (ARR+1))
注意PSC和ARR寄存器值都要加1,因为计数器从0开始。这是新手最容易出错的地方:在CubeMX里填写PSC=71、ARR=999,实际分频系数是72乘以1000。
3.2 把PWM频率换算成电机转速
步进电机的转速公式:
转速(RPM) = 脉冲频率 / (每转步数) × 60 = 脉冲频率 / (200 × 细分) × 60
举例:配置PSC=71,ARR=999,TIM1时钟72MHz,PWM频率=72e6/(72×1000)=1000Hz,也就是每秒1000个脉冲。电机如果是1.8度、200步/转,1/8细分时每转需要1600个脉冲,于是转速=1000/1600×60=37.5RPM。如果你想跑120RPM,脉冲频率需要120/60×1600=3200Hz,反推ARR+1=1e6/3200约等于312,ARR填311即可。
这个换算建议直接写成一个函数放在工程里,后面加减速要用。我一般在代码里固定PSC=71,让计数时钟变成1MHz,这样ARR的值和脉冲周期微秒数直接对应。严格来说,计数时钟为1MHz时f=1e6/(ARR+1),ARR=999时正好1kHz;快速估算时直接f约等于1e6/ARR也够用,但写精确公式更不容易出错。
3.3 定时器的“高级功能”逐个说
标题里我强调“高级功能”,这里把定时器上几个容易被忽略、但在本项目真正发挥价值的特性拆开讲。
预装载(影子寄存器):ARR、CCR这些关键寄存器,在使能预装载后不会立刻生效,而是等到下一个更新事件统一装载。这非常重要。如果你直接往ARR写一个新值而没开预装载,它可能在某次计数的任意时刻生效,周期就突然乱了一拍,电机就会“咯噔”一下。所以CubeMX里Auto Reload Preload一定要开,我还习惯把CCR的预装载也打开。代价是修改频率后最多延迟一个完整脉冲周期才生效,这对步进控制完全没有问题。
更新事件和更新中断:PWM的每个完整周期结束,计数器从ARR回到0,就产生一次更新事件。顺着这个事件可以精确统计“已经发了多少个脉冲”,从而在指定步数后自动停止。很多运动控制场景本质上就是“发N个脉冲”,更新中断是完成这个任务最简单的工具。
输出比较翻转模式(OC Toggle):它和PWM模式不一样。PWM模式里周期由ARR决定,而在OC Toggle模式下,你把比较值CCR当作“下一次翻转的时刻”,每次比较匹配电平就翻转一次。这种模式特别适合做加减速,因为你可以每一次翻转都安排一个精确到定时器时钟周期的时间间隔,不受ARR整体长度的限制。当然,16位定时器在超长间隔时有限制,后面细说。
重复计数器(REP):这是高级定时器TIM1的专利。普通定时器每次更新都触发一次更新事件,TIM1可以设置重复计数,让它每N+1次更新才触发一次更新事件。比如你想每发完100个脉冲再通知CPU一次,就不用每个周期都进中断,能进一步降低CPU占用。这个在长距离运动里很实用。
DMA:定时器可以和DMA联动。典型玩法是把一整条速度曲线的ARR值放进数组,DMA在每次更新事件时自动把下一个ARR值送到寄存器,CPU完全不用管。这在输出复杂加减速曲线时几乎是神器,后面加减速部分我会给一个框架例子。
中心对齐模式、死区、刹车输入:这几个对纯步进脉冲生成来说不是必须,但一旦你的应用升级到“直接用STM32的MOS管驱动电机”,比如DC电机H桥、BLDC无刷电机,或者做成小型斩波驱动,这些就全用上了。中心对齐模式输出的PWM关于周期对称,谐波和电磁干扰更小;死区防止上下桥臂直通;刹车输入BKIN可以在硬件故障时瞬间封死输出。了解它们的存在,至少在你下一次设计驱动电路时知道方向。
3.4 代码里写死还是灵活配置
工程实践中,我建议在CubeMX里把定时器时钟结构固定,再写几个“寄存器级”的小工具函数实现动态换挡,而不是每次都用完整的HAL函数。原因很简单:HAL_SetCompare之类的调用方便,但在中断高频调用时会有不少状态检查的额外开销。自己写一个几行的宏或函数,比如:
#define TIM1_SET_PWM_FREQ(hz, clk_hz) \ do { \ uint32_t _a = ((clk_hz) / (hz)) - 1; \ __HAL_TIM_SET_AUTORELOAD(&htim1, _a); \ __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, _a / 2); \ } while(0)把ARR和CCR成对更新,既保证50%占空比,又不会让输出出现毛刺。后面加减速的每个节拍都靠这种小工具函数来执行。
4. CubeMX配置与基础代码实现
4.1 CubeMX工程配置步骤
这一节直接给步骤,减少大家翻菜单的时间:
- 新建工程,选择STM32F103C8Tx,确认封装LQFP48。
- RCC里把HSE设为Crystal/Ceramic Resonator。
- Clock Configuration页面,把HCLK调整为72MHz,CubeMX会自动配置PLL和总线分频。确认APB1=36MHz、APB2=72MHz,这样定时器时钟都能得到72MHz。
- TIM1配置:Clock Source选Internal Clock,Channel1选PWM Generation CH1。参数Prescaler填71,实际分频72;Counter Period填999;Auto Reload Preload打开;Pulse填500;PWM Mode选Mode1;CH Polarity保持High。
- 如果要用更新中断做脉冲计数,在NVIC Settings勾选TIM1 update interrupt。
- GPIO配置:PA8不需要额外配置,定时器PWM引脚会自动复用;PB5和PB10设置为Output Push Pull,可以起别名DIR、EN方便阅读。
- Project Manager里选MDK-ARM,生成工程。
这里解释一下为什么把PSC固定71:让计数时钟变成1MHz以后,ARR=999就是1kHz脉冲,ARR=311就是约3.2kHz脉冲,ARR和频率的换算非常直观。加减速时用整数运算就能完成,不用浮点,F103跑浮点太亏了。
4.2 让电机先转起来
生成工程后,在main.c里加几行:
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_RESET); // 使能,注意极性看模块 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 改变速度:ARR=311时脉冲频率约3.2kHz __HAL_TIM_SET_AUTORELOAD(&htim1, 311); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 155);如果你照上面的配置没出错,电机应该会匀速转起来。如果电机只啸叫不转,或者转向不对,多半是相线顺序问题,对调A相或B相其中一组即可。
4.3 指定步数的运动
实际应用里很少让电机无限转,一般要“走多少步、停在哪”。用更新中断做脉冲计数最简单:
volatile uint32_t steps_remaining = 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM1) { if (steps_remaining > 0) { steps_remaining--; if (steps_remaining == 0) { HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1); } } } }启动运动时设置steps_remaining = N并开始PWM即可。这里有一个细节:更新中断发生在每个脉冲周期结束,所以计数是精确的。但如果你在加速过程中动态改变ARR,更新事件仍然会在每个完整周期结束触发,计数不受影响。这种“指定步数就停”的写法已经能支撑一个简单的点动控制面板,再加上DIR的延时切换,就可以实现一个正反转小机构。
5. 速度控制与加减速算法
5.1 为什么必须加减速
很多人从静止直接让电机高速运行,结果发现:电机不仅声音难听,还经常走到一半停住。原因是步进电机是同步电机,转子通过磁力跟随定子磁场旋转,磁场转速过快,转子惯性跟不上,就会丢步。一旦丢步,开环系统没有办法自己纠正,位置就永久错了。
加减速的本质,是让电机在低速度下建立惯量,再逐步提高到目标速度;减速时逐步降低速度,让机械机构平滑停止。一个经验值是:在几百到几千步/秒这个区间,合理加减速能把噪声和丢步概率降低一个数量级。
5.2 梯形加减速参数计算
梯形速度曲线是最常用的加速策略:启动速度v0,线性加速到vmax,匀速,再线性减速到vstop。需要确定的三个参数是v0、vmax、加速度a。
以我的常用参数为例:42电机,1/8细分,1600步/转。
- 启动速度v0 = 200步/秒,约7.5RPM,足够低
- 目标速度vmax = 3200步/秒,约120RPM
- 加速度a = 4000步/秒²
加速时间t_acc = (vmax - v0) / a = (3200 - 200) / 4000 = 0.75秒。加速段总步数n_acc = v0 × t_acc + 0.5 × a × t_acc²,代入计算就是200×0.75 + 0.5×4000×0.75²,约等于1275步。也就是说,前1275步内把速度从200逐步线性提高到3200,然后以3200匀速走,最后再花1275步左右减速到0。减速段参数可以单独设置,通常比加速稍平缓,让机械机构更稳。
注意:这套计算里的单位全是“步”,不是角度也不是毫米。后续如果你给电机配上丝杠推动平台,位移就变成“步数×导程/细分数”。先把步层面的数学算清楚,上层换算只是乘法。
5.3 用PWM模式实现逐拍换频
如果采用PWM模式,最简单的变速方法就是把每个“拍”的ARR值按照曲线更新。比如每次更新后,根据当前速度查表或者用公式计算下一个ARR:
static uint32_t current_arr = 719; // 对应约1.39kHz启动 const uint32_t min_arr = 312; // 对应约3.2kHz目标速度 // 在一个定时器更新中断或系统节拍中断中: if (accelerating && current_arr > min_arr) { current_arr -= accel_step; // 按固定步长减小ARR,也就是提高频率 __HAL_TIM_SET_AUTORELOAD(&htim1, current_arr); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, current_arr / 2); }这个方法的优点是直观,缺点是每一步的时间精度没那么严格:你每次只改下一个周期的ARR,脉冲周期本身不会中途突然变,但也做不到精确控制单独一步的时间。对于绝大多数教学和原型项目,它已经足够好了。
5.4 想要更精准:输出比较翻转模式
我实际做加减速更喜欢用OC Toggle模式,尤其在要求每一步时间都很准的场景。原理是:计数器自由连续运行,每次比较匹配时引脚电平翻转,同时你在中断里把下一次翻转的时间提前写好。这样每一步的脉冲宽度完全由比较值决定,精度是单个定时器时钟周期。
关键公式:
比较间隔 = 定时器时钟频率 / (2 × 目标步率)
为什么有个2?因为每步是一个完整脉冲,需要翻转两次,低到高、高到低。以PSC=71、计数时钟1MHz为例,目标步率1000步/秒,比较间隔=1e6/(2×1000)=500个计数。中断伪代码如下:
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM1) { if (step_budget == 0) { HAL_TIM_OC_Stop(&htim1, TIM_CHANNEL_1); return; } // 下一次翻转时刻 = 当前计数 + 按当前速度算出的间隔 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, __HAL_TIM_GET_COUNTER(&htim1) + current_interval); step_budget--; } }把current_interval按加减速曲线一步步改小就是加速,改大就是减速,这样就能得到非常平滑的速度曲线。这里提醒一下:TIM1是16位定时器,计数器到65535后回绕。如果你的current_interval很大,也就是低速场景,“当前计数+间隔”可能跨越一个甚至多个回绕周期,比较逻辑会出错。解决办法有两个:一是限制最低步率,让间隔不超过65536个计数;二是改用32位的TIM2做同样的OC Toggle。我的工程里通常用TIM2做低速型号,TIM1做高速PWM输出,各管一段,互补使用。
5.5 进阶:用DMA自动执行速度曲线
最后说一个很多人不太敢用、但真正体现“高级”的功能——DMA。思路是这样:把一条完整加减速曲线的ARR序列提前放到一个数组里,然后启动定时器更新事件的DMA请求,硬件会在每个更新事件自动把数组的下一项写入寄存器,CPU从头到尾不需要干预。
const uint16_t speed_profile[PROFILE_LEN] = { /* 预先算好的ARR序列 */ }; HAL_DMA_Start(&hdma_tim1_up, (uint32_t)speed_profile, (uint32_t)&TIM1->ARR, PROFILE_LEN); __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);好处不仅是省CPU,更在于时序是硬件级别的确定性,不受中断优先级影响。你可以在同样的时间里驱动好几路电机,每个都用各自DMA通道送自己的速度曲线。这是从“能跑”迈向“产品化”的关键一步。第一次用的时候,建议先在目标板上实测DMA搬运完成中断,确认曲线走完后再去控制停止动作,别上来就接电机。
6. 常见问题排查与调试心得
6.1 一张排查速查表
我自己调试这个项目时遇到过、以及在社群看到过的典型问题,整理成这张表:
| 现象 | 可能原因 | 排查/解决 |
|---|---|---|
| 电机完全不动且没有声音 | PUL没有脉冲输出,或EN关断 | 用逻辑分析仪看PA8;查EN电平 |
| 电机啸叫但不转 | 相线接线错误或一相没接好 | 对调A相或B相;检查四根相线 |
| 电机不锁定,手轻轻转动轴 | EN未使能,或驱动电源没上 | 查EN电平和驱动器电源电压 |
| 电机锁定,但手动转轴很重 | 正常现象,正在保持力矩 | 需要手动调节时拉EN脱机 |
| 电机转起来一顿一顿 | 动态改ARR时机不对,或缺少加减速 | 确认预装载开启;加加减速 |
| 电机高速丢步 | 加速度太陡,或电流不足 | 降低a;提高驱动电流;换大电源 |
| 发几个脉冲就停 | 更新中断里计数逻辑有误 | 检查steps_remaining和PWM_Stop调用位置 |
| PWM频率和计算不符 | PSC或ARR忘加1,时钟树误判 | 重新核对PSC+1和ARR+1 |
| 电机瞬间发热严重 | 驱动电流拨码设太高 | 按电机额定电流调整驱动电流 |
6.2 示波器和逻辑分析仪怎么用
调试步进电机驱动,示波器不一定人人都有,但逻辑分析仪几十块钱一个,建议备一个。重点测两个场景:
一是PUL引脚的波形。看脉冲频率是否与设定一致,脉冲宽度是否稳定。如果你看到波形“忽宽忽窄”,说明PWM在某个周期被中断打扰过,或者在PWM模式下动态改ARR的时机不对。
二是DIR切换时PUL的状态。我习惯逻辑分析仪同时抓三个通道:PUL、DIR、EN。切换方向时确认PUL先停、DIR变化后至少稳定几十微秒、再恢复PUL。这个顺序在波形上一眼就能看出来,比人肉盯着电机转不转靠谱得多。
6.3 我踩过的几个坑
写这篇文章之前,我把这个项目又从头搭了一遍,有几个细节特别想给读者说。
第一个是EN引脚极性。我最初默认“高电平使能”,结果电机扭矩全无,折腾了很久。不同模块的EN有效电平定义完全不同,有的是高电平禁用,有的是低电平禁用,还有的是悬空即禁用。接好线之后第一件事,不是跑程序,而是先用万用表确认EN引脚在你想让它使能时的电平是否符合模块手册。
第二个是更新中断里的函数调用时间不要拖太长。在PWM模式下,更新中断是每个脉冲触发一次,如果最高3200Hz,那么一次中断只有约312微秒的预算。如果你在中断里做printf、调大延时,下一个更新事件就会被堵住,速度曲线直接糊掉。实际项目里我会在中断里只做最基本的计数和标志位设置,串口打印全部挪到主循环。
第三个是电源上电顺序。实践中最稳的做法是:先让STM32完成初始化,GPIO状态确定,再让驱动器输出有效,也就是把EN从脱机状态切到使能状态。如果反过来,驱动器先上电而STM32还在复位,GPIO可能处于不确定电平,驱动器有可能收到莫名其妙的脉冲,电机就会“啪”地跳一下。哪怕你的现象不明显,也建议在初始化代码里把EN默认拉到无效电平,再在所有外设配置完成后再放开。
最后再分享一个习惯
每次调步进电机,我永远从最低速开始加,先确认每根信号线的逻辑关系,再逐步提高速度。别一上来就奔着120RPM去,那只会让你在“是驱动坏了还是代码坏了”之间浪费一个下午。这套F103C8T6+TB6600的方案,本质上是一个非常典型的“定时器驱动运动控制”教学模型,只要能掌握脉冲生成、定时器高级功能、加减速这三板斧,后面再去玩闭环、多轴联动就有了扎实的基础。如果教程里的某个参数和你手上的板子对不上,别硬抄,先看丝印、量电平,再回头对着参数表调整。