51单片机三相步进电机单三拍控制:相序、定时器与丢步排查
简介这份资源是《单片机原理及应用》课程设计报告书面向电子信息、自动化等专业的学生及单片机初学者围绕AT89C51控制三相单三拍步进电机展开完整设计。压缩包内仅含1个doc文档大小约1.58MB可直接用于课程设计参考、答辩准备或自学对照。报告完整梳理了三相单三拍、双三拍及单双六拍的运行原理与30度、15度步距角差异并给出以键盘输入步数3至27步、正反转指示灯、键盘锁定、DS18B20过热保护报警为核心的系统方案覆盖方案论证、硬件设计、软件设计、调试与技术小结等章节附录还附有源程序代码与电路图。目前已有95人学习适合需要从原理推导到电路搭建、再到程序调试全流程参照的读者可据此快速理解脉冲分配与方向控制的实现思路减少自行摸索的试错成本。1. 从「电机嗡嗡响却不转」说起三相单三拍到底难在哪做单片机课设的人几乎都会撞上三相步进电机一块 STC89C52 或者任意一块 51 开发板配一块 ULN2003 驱动板代码照抄网上两相四线的例程下载进去电机是响了转子却只在原地抖轴一点不转。把线序调换一遍又变成反转或者干脆堵转。这类问题九成不出在单片机本身而出在三个变量上——相序表、每拍通电时间、绕组公共端接法。拆开「三相单三拍」这个说法三相指定子有三个独立绕组单三拍指每一步只给其中一相通电按 A→B→C→A 循环三拍走完一个电周期。它和三相六拍A→AB→B→BC→C→CA是同一台电机的两种励磁方式差别只在步距角和转矩。单三拍的相序表只有三个元素控制代码最短适合刚上手、或者只需要低速小转矩的场合。适合往下读的人有三类正在做单片机课程设计、需要一份能一次跑通的三相步进电机程序的人手上有一台三相反应式或混合式电机、想用最小硬件成本驱动它的嵌入式新手以及电机已经转起来了但想搞清楚为什么转速上不去、为什么一加速就丢步的工程师。后面几章把相序、换算公式、驱动电路、定时器参数和排错顺序一层层拆开。2. 三相单三拍的相序、步距角与驱动电路选型2.1 单三拍、双三拍、六拍的相序差异上电顺序是三相步进电机的全部秘密。定子三相绕组记作 A、B、C把它们的控制位放在单片机同一个端口上用低三位的 0/1 表示通电状态就能把三种励磁方式各写成一个数组。// 低三位 P1.0 / P1.1 / P1.2 分别对应 A / B / C 三相 // 某位为 1 表示该相导通为 0 表示该相断电 unsigned char code Wave_1x3[3] {0x01, 0x02, 0x04}; // 单三拍 A→B→C unsigned char code Wave_2x3[3] {0x03, 0x06, 0x05}; // 双三拍 AB→BC→CA unsigned char code Wave_6[6] {0x01, 0x03, 0x02, 0x06, 0x04, 0x05}; // 六拍 A→AB→B→BC→C→CA0x01是0000 0001只有 A 相通电0x03是0000 0011A 相和 B 相同时通电0x06、0x05同理。单三拍每一步只有一相绕组通电转子被单个磁场齿拉过去下一个脉冲换到下一相转子再往前挪一格。它的优点是代码量最小、驱动管只需要 3 路缺点是每步只有一相出力转矩只有双三拍的七成左右低速时低频振荡也更明显电机容易「一顿一顿」。双三拍和单三拍的步距角完全一样区别在于每一步都是两相同时通电合成磁场更强转矩更大代价是任何时刻都有两相在耗电发热和电源压力都上去一截。六拍是两者的混合序列A→AB→B→BC→C→CA 走完六拍才回到起点步距角减半运行最平稳代价是同样的转速需要两倍的脉冲频率。提示三种励磁方式的接线完全相同改的只是相序表。如果代码跑起来发热明显先检查是不是把单三拍的三个元素写成了双三拍的值。2.2 步距角、每转步数与转速的换算步距角是每来一个脉冲转子实际转过的机械角度它不是电机的固定常数而是随励磁方式变化。公式是θ 360° / (z × N) 每转步数 S 360° / θ z × N 转速 n(r/min) 60 × f / S其中 z 是转子齿数N 是一个电周期里的拍数单三拍取 3六拍取 6f 是每秒发出的脉冲数也就是换相频率。励磁方式每周期拍数 N40 齿转子步距角每转步数 Sf200 Hz 时转速单三拍33.0°120100 r/min双三拍33.0°120100 r/min三相六拍61.5°24050 r/min表格里能直接看出一个反直觉的结论同样的换相频率六拍的转速只有单三拍的一半。很多人调六拍程序时觉得「变慢了」其实脉冲频率没变只是每转需要的步数翻倍了。反过来如果目标转速是 300 r/min单三拍只需要 f 300 × 120 / 60 600 Hz而六拍要 1200 Hz后者对单片机定时器和驱动电路的要求高得多。z 的取值一定要查电机铭牌或实测常见的小型三相反应式步进电机是 40 齿或 50 齿。50 齿配单三拍步距角就是 2.4°每转 150 步凭感觉猜齿数会直接导致转速和位移量全错。2.3 驱动电路选型ULN2003、达林顿管还是分立 MOSFET单片机 IO 口一般只能给出十几毫安而步进电机单相绕组动辄需要几百毫安中间必须加功率级。选哪一级取决于电机相电流。ULN2003 是最省事的一档内部是 7 路达林顿管每路持续电流 500 mA、峰值 600 mA输入端直接接单片机 IO输出端灌电流。接线方式是电机公共端接正电源A/B/C 三相分别接 OUT1/OUT2/OUT3单片机对应引脚输出高电平时ULN2003 把该相拉到地绕组得电。它自带的续流二极管能吸收绕组关断时的反电动势这是它比直接用三极管省心的地方。相电流超过 500 mA 就得上分立方案三相各用一只 N 沟道 MOSFET注意选逻辑电平、导通电阻小的型号栅极串 100 Ω 电阻、对地并 10 kΩ 下拉漏极接绕组源极接地绕组两端反向并一只快恢复二极管做续流。这套电路把电流能力做到几安培没有压力但 PCB 上的地线和散热要单独规划。工业设备上更常见的做法是用外置三相步进驱动器接口就三根线脉冲 PUL、方向 DIR、使能 ENA。单片机只需要输出脉冲和方向电流环、细分和斩波全交给驱动器。ENA 通常是低电平使能、高电平释放释放状态下绕组断电电机可以用手自由转动这个脚在需要手动调整机构位置的场合很有用平时不用就直接短接到地省一根线。注意无论用哪种驱动级电机电源和单片机电源必须共地且电机侧要加大容量电解电容常见 470 μF 以上吸收换相瞬间的电流冲击否则单片机会因为电源波动反复复位。3. 51 单片机驱动三相单三拍的最小可跑程序3.1 硬件接线与限流电阻先确定电机的引线定义。三相电机常见是 3 根引出线对应 A、B、C 三相公共端已经从内部引出或需要自行短接两根同名端也有 4 线机型其中一根是公共端。判断方法很简单用万用表电阻档两两测量三根线之间阻值彼此接近的就是三相绕组剩下那根与它们都导通的就是公共端。公共端的接法决定单片机输出电平的极性。如果公共端接 12 V共阳接法那么各相拉低才通电ULN2003 正好适配如果公共端接地共阴接法就需要 PNP 或高边驱动。搞反了的表现是电机完全没反应但摸驱动管会明显发热。限流电阻怎么定设电源 12 V、绕组直流电阻 100 Ω则静态电流 120 mA在 ULN2003 能力范围内可以不加电阻。如果绕组电阻只有 30 Ω直接接 12 V 就是 400 mA接近 ULN2003 上限且发热严重这时要么降低电源电压要么串限流电阻。串电阻会牺牲高速性能因为电阻上的压降随电流增大而增大真正可用的力矩下降所以更好的做法是改用恒流斩波驱动器。3.2 用查表法写相序切换相序切换有三种写法实际项目里推荐查表法。直接写三行 if-else 在单三拍里还看得过去一旦换成六拍就要写六个分支正反转还得再写一遍出错概率高。查表法把相序编码放进code区烧进 Flash不占 RAM正转就是索引递增反转就是索引递减改成六拍只需要换一个数组和循环长度。端口操作要遵循「读—改—写」原则。如果 P1 口上还挂着数码管或者按键直接P1 Wave_1x3[idx]会把其他引脚一起清零造成显示闪烁或误触发。正确写法是先把低三位清掉再或上相序值P1 (P1 0xF8) | Wave_1x3[idx]; // 0xF8 1111 1000只保留高五位3.3 定时器初值计算与完整 C51 程序换相的节奏必须由硬件定时器给不能用delay()软件延时。软件延时期间单片机什么都做不了按键响应不了速度也调不细。以 12 MHz 晶振、12T 模式的 51 单片机为例一个机器周期是 1 μs定时器 0 工作在方式 116 位重装值算法是重装值 65536 - t TH0 重装值 / 256 TL0 重装值 % 256要 200 Hz 的换相频率就是每步间隔 t 5000 μs重装值 65536 - 5000 60536 0xEC78于是TH0 0xEC、TL0 0x78。#include reg52.h unsigned char code Wave_1x3[3] {0x01, 0x02, 0x04}; // 单三拍 A→B→C unsigned char idx 0; // 当前拍号 void Timer0_Init(void) { TMOD 0xF0; TMOD | 0x01; // T0 方式 116 位定时器 TH0 0xEC; // 5000us 12MHz - 200 步/秒 TL0 0x78; ET0 1; // 允许 T0 中断 EA 1; // 开总中断 TR0 1; // 启动 T0 } void Timer0_ISR(void) interrupt 1 { TH0 0xEC; // 方式 1 必须软件重装 TL0 0x78; P1 (P1 0xF8) | Wave_1x3[idx]; // 只改低三位不动其他外设 idx; if (idx 3) idx 0; // 三拍循环 } void main(void) { P1 0xF8; // 上电三相全断电防止锁死 Timer0_Init(); while (1) { } }中断服务函数里的重装是必需的方式 1 不会自动重载漏掉这一行电机会先按 5 ms 走一步、之后按最长溢出周期约 65 ms爬行表现为「启动正常但越来越慢」。idx用无符号字符型比较用 3而不是 3即使因为中断嵌套导致idx意外越界也能自恢复。若要反转把自增改成自减、并在idx 0时赋值为 2 即可不需要另写一份相序表。方向切换时有一个细节直接在中断里改变方向变量可能在两拍之间插入一个非法状态。稳妥做法是先把当前相断电、置位一个方向标志再从步骤起点重新开始循环。4. 转速调节、加减速曲线与失步排查4.1 从目标转速反推定时器重装值调转速不要靠试直接算。已知每转步数 S 和目标转速 n换相频率 f n × S / 60步间隔 t 1/f重装值 65536 - tt 以 μs 计12 MHz 12T 条件下。目标转速每转步数换相频率步间隔 t重装值TH0/TL060 r/min120120 Hz8333 μs572030xDF73100 r/min120200 Hz5000 μs605360xEC78300 r/min120600 Hz1667 μs638690xF97D600 r/min1201200 Hz833 μs647030xFCBF晶振不是 12 MHz 时机器周期要跟着变。比如 11.0592 MHz 晶振在 12T 模式下一个机器周期约 1.085 μs同样的重装值实际间隔会偏大 8.5%转速会偏低。要么按 1.085 折算要么在定时器里跑 1T 模式STC 系列支持可通过烧录软件或寄存器切换把分辨率提上去。频率越低每个计数单位的相对误差越大1.2 kHz 时 1 μs 的偏差不到 0.2%100 Hz 时同样偏差会被放大到 1% 左右所以低速段不用太纠结精度。4.2 梯形加减速为什么直接给高频一定丢步步进电机的转矩随脉冲频率上升而下降。转子从静止起步时如果第一拍就是 600 Hz转子还来不及跟上第一个磁场位置第二拍就来了电机只会原地抖动或者出现响亮的「咯咯」声这就是丢步。所有能跑得稳的方案都是从低频起步、逐步加速。最简版本是线性加速每次换相中断把步间隔减去一个固定量#define STEP_INT_MIN 1700u // 最快步间隔约 1.7ms对应 588Hz #define STEP_INT_MAX 8000u // 起步步间隔 8ms对应 125Hz #define STEP_ACC 40u // 每个中断把间隔缩短 40us unsigned int step_int STEP_INT_MAX; // 当前步间隔单位 us void Timer0_ISR(void) interrupt 1 { static unsigned char idx 0; unsigned int reload; // 先按当前步间隔装载下一次定时 reload 65536u - step_int; TH0 (unsigned char)(reload 8); TL0 (unsigned char)(reload 0x00FF); // 换相 P1 (P1 0xF8) | Wave_1x3[idx]; idx (idx 1) % 3; // 一阶线性加速到顶后保持匀速 if (step_int STEP_INT_MIN) { step_int - STEP_ACC; } }这段代码里三个宏是全部可调参数。STEP_INT_MAX决定起步有多柔设得太小比如直接 2000就相当于没有加速过程照样丢步设得太大比如 20000起步会很慢短行程里几乎全是加速段定位时间反而变长。STEP_INT_MIN是速度上限它受电机转矩、电源电压和驱动级共同限制只能实测确定逐步减小这个值直到电机开始出现明显的失步声再退回 20% 留余量。STEP_ACC决定加速快慢值越大加速越猛也越容易丢步一般取STEP_INT_MAX的 0.5% 到 2% 之间。这个一阶写法省内存、易理解缺点是减速段没做停下时电机是「硬停」的惯量大或负载重时会冲过目标位置。真正生产用的做法是按步数分段加速段走 N1 步匀速段按剩余步数扣掉加速和减速步数减速段镜像加速曲线。梯形曲线做起来代码量翻倍S 曲线更复杂需要精确停位时才值得投入。4.3 丢步、抖动、发热的定位顺序出问题时按下面的顺序查能覆盖绝大多数现场故障。第一步查相序。让电机以 10 Hz 左右的极低频率单步运行用手摸转子应该能明显感到每次脉冲都「咯」地转一小格。如果只是抖动没位移说明相序错了把任意两相调换再试。这一步用的是单三拍三个元素循环最容易判断。第二步查共振。单三拍在 100 到 200 Hz 附近容易出现低频共振表现是电机噪声突然变大、机身明显晃动。解决方式有两种加快速度跳过这个频段或者切换到六拍运行因为六拍步距角减半同转速下脉冲频率翻倍共振点会移出工作区。第三步查电流和电压。用万用表量相绕组两端电压通电相应该接近电源电压减去驱动管饱和压降。如果只有零点几伏说明驱动管没导通或者公共端接错。如果电机转得动但明显没劲多半是电源电流不够换更大功率的电源再测。第四步查散热。ULN2003 和 MOSFET 连续工作时温度应该只是温热烫手说明电流超限或占空比过高。单三拍任意时刻只有一相通电占空比本来就是 1/3比六拍低散热压力更小如果单三拍都烫手基本可以确定是限流没做好。注意调试期间不要长时间让电机停在「单相通电」状态不动。绕组持续通电不换相电流全部转成热量小型步进电机十几秒就能烫到 60 ℃ 以上还有可能退磁。程序里最好加一个空闲超时超过 1 秒没有换相就把三相全部断电。5. 进阶非阻塞换相状态机与细分、闭环的取舍前面的写法把换相逻辑塞在定时器中断里加减速判断也在中断里做一旦要同时处理按键、串口或者传感器采样中断函数就会膨胀最坏情况是中断执行时间超过步间隔导致定时器重装丢失、电机突然快一下。工业代码里的常见做法是把「什么时候换相」和「换到哪一拍」拆开定时器只负责置一个标志位主循环根据标志位推进状态机。volatile bit step_tick 0; // 定时器中断置位主循环清零 void Timer0_ISR(void) interrupt 1 { unsigned int reload 65536u - step_int; TH0 (unsigned char)(reload 8); TL0 (unsigned char)(reload 0x00FF); step_tick 1; // 只做标记不做逻辑 } void main(void) { P1 0xF8; Timer0_Init(); while (1) { if (step_tick) { step_tick 0; P1 (P1 0xF8) | Wave_1x3[idx]; idx (idx 1) % 3; UpdateSpeed(); // 加速、减速、限位判断都在这里做 } } }中断执行时间从几十微秒压到几微秒主循环腾出来的时间可以跑按键扫描、数码管动态显示或串口收发。要注意step_tick必须声明为volatile否则编译器可能把它优化进寄存器主循环永远读不到变化。细分是另一条路。单三拍每步 3° 或者 2.4°分辨率有限低速时一格一格跳很明显。外置驱动器可以通过细分把一整步拆成 8、16、32 个微步代价是同样的转速需要成倍的脉冲频率51 单片机的定时器在 12 MHz 下很难稳定输出几十 kHz 的脉冲。这时候要么换 STM32 这类主频高、带 PWM 和 DMA 的单片机要么把脉冲交给驱动器内部的振荡器单片机只给方向和使能。细分不增加绝对定位精度它改善的是低速平顺性和噪声别指望靠细分解决丢步。闭环是最后一道保险。加装编码器后单片机可以对比发出的脉冲数和编码器反馈的实际步数一旦差值超过阈值就判断为丢步降速重试或者报警。三相单三拍本身转矩小、抗扰能力弱在负载变化大的场合闭环带来的可靠性提升比换更贵的开环电机更明显。代价是需要额外的编码器、计数器资源和一套位置比较逻辑课设级别通常用不到产品级别则几乎是标配。选型上给一个粗略的判断低速、小负载、成本敏感用单三拍加 ULN2003 就够需要平稳、有定位精度要求切六拍或者上细分驱动器负载波动大、不允许丢步直接上闭环。三种方案的控制代码骨架完全一致差的只是相序表长度和反馈环的有无。本文还有配套的精品资源点击获取