简介:这份资源面向参加电子设计竞赛或从事电机控制开发的工程师与学生,提供基于STC8H1K28单片机的三相直流无刷电机(BLDC)驱动完整源码方案,采用带HALL传感器的换相方式,并支持电位器调速与捕捉中断换相,适合需要快速搭建BLDC控制原型、理解有霍尔换相策略的读者参考。压缩包共15个文件,约162KB,包含C语言源码、头文件、HEX烧录文件、Keil工程文件(uv2、opt、bak等)以及两份PDF说明文档,分别对应中功率与大功率两种硬件方案,便于对照不同功率等级的设计差异。目前已有1450人学习下载,说明该方案在竞赛与教学场景中具有一定参考价值。读者可从中获取完整的电机换相逻辑、中断处理流程与调速实现思路,并借助PDF文档理解硬件参数与调试要点,适合作为BLDC控制入门与赛题方案复现的实践素材。
1. 从竞赛台架到量产思维:STC8H 带 HALL 的 BLDC 控制到底在做什么
很多做 BLDC 驱动的朋友第一次拿到 STC8H 加三相 HALL 的方案,都会觉得“不就是六步换相嘛,能有多难”。真把板子焊出来、电机转起来,才发现低速抖动、高速失步、换相点对不准、电流采样一塌糊涂。STC8H 作为 8051 内核里少见的带 16 位硬件乘法器和高级 PWM 定时器的型号,在竞赛和低成本量产里被大量用来做三相无刷电机驱动,尤其是带 HALL 的方波控制方案,几乎是电赛和毕业设计的标配。
这个标题背后其实包含三层东西:一是 STC8H 这颗 MCU 怎么把三相桥的六路 PWM 和 HALL 捕获配合起来;二是带 HALL 的 BLDC 控制策略,也就是经典的六步换相加双闭环;三是竞赛场景下对响应、成本、可靠性的取舍。适合谁看?如果你手上有 STC8H 的板子、电机带三个 HALL、想从零把闭环跑通,或者你已经能转但想搞清楚换相表和 PID 参数怎么调,这篇就是按这个路径写的。下面不聊虚的,直接按“硬件资源怎么用 → 换相和调速怎么写 → 坑在哪 → 怎么验证”推下去。
2. STC8H 的 PWM 与 HALL 捕获:把定时器资源算清楚再动手
2.1 为什么选 STC8H 而不是普通 8051 做三相 BLDC
普通 8051 做 BLDC 最大的问题是 PWM 资源不够灵活,很多型号只有两路 PCA,凑六路互补带死区非常勉强。STC8H 系列带高级 PWM 定时器,常见做法是用 PWMA 的六路输出通道,配合死区寄存器直接生成三相桥需要的六路带死区 PWM,硬件上省掉一堆逻辑门。另一个关键是它带 16 位硬件乘法器,速度环和电流环里做 PID 运算时,不用软件模拟乘法,中断里算得快很多,这对提高 PWM 频率和降低换相延迟很实际。
选型时我一般看三个点:PWM 通道数是否够六路且能互补、有没有硬件死区、HALL 输入能不能触发捕获或外部中断。STC8H 的 PWMA 支持互补输出和死区,HALL 三根线接到带外部中断的 IO 上,用边沿触发读状态,这套组合在竞赛里足够用。要注意的是,STC8H 不同封装和型号的 PWMA 通道映射到引脚不一样,画板前一定对着手册把 PWMA 的通道和引脚对应关系确认一遍,别等板子回来才发现六路 PWM 少一路。
2.2 六路 PWM 的初始化:周期、死区、互补输出怎么设
下面这段是 PWMA 初始化的核心逻辑,按 24kHz PWM 频率、系统时钟 24MHz 来算。不同主频改周期寄存器就行,逻辑不变。
// STC8H PWMA 初始化:三相六路互补 PWM,带死区 // 系统时钟 24MHz,PWM 频率 24kHz -> 周期计数值 1000 void PWMA_Init(void) { PWMA_PS = 0x00; // 通道引脚映射,按实际板子改 PWMA_CCER1 = 0x00; // 先关通道 PWMA_CCER2 = 0x00; PWMA_ENO = 0x00; PWMA_PSCRH = 0; // 预分频 0 PWMA_PSCRL = 0; PWMA_ARRH = (1000 >> 8); // 周期 1000,24MHz/1000 = 24kHz PWMA_ARRL = (1000 & 0xFF); // 死区时间:假设 1us,按主频折算 PWMA_DTR = 24; // 死区寄存器,具体值按手册公式算 // 通道 1/2/3 配置为 PWM 模式,互补输出 PWMA_CCMR1 = 0x68; // PWM 模式 1,预装载使能 PWMA_CCMR2 = 0x68; PWMA_CCMR3 = 0x68; PWMA_CCER1 = 0x55; // 使能 CH1/CH2/CH3 及互补 PWMA_CCER2 = 0x55; PWMA_ENO = 0x3F; // 使能六路输出 PWMA_BKR = 0x80; // 主输出使能 PWMA_CR1 = 0x01; // 使能计数器 }逻辑说明:ARR 决定 PWM 频率,死区寄存器 DTR 决定上下桥臂切换时的空白时间,太小会直通炸管,太大会让低速波形畸变。参数上,24kHz 是个折中值,人耳可听范围边缘,MOS 开关损耗也能接受。死区我一般从 0.5us 到 1us 起调,用示波器看上下桥臂波形确认没有交叠。注意 PWMA 的通道和引脚映射寄存器 PWMA_PS 必须和实际 PCB 走线一致,这个填错的表现是电机完全不转或者只抖一下。
2.3 HALL 三根线怎么接、怎么读、怎么和换相对应
HALL 传感器输出三路方波,电机每转一圈产生六个状态,每个状态对应一个 60 度扇区。常见接法是 HALL 的 U、V、W 分别接三个带外部中断的 IO,配置成上升沿和下降沿都触发,中断里读三路电平拼成 1 到 6 的状态值。STC8H 的外部中断数量有限,如果不够,也可以用定时器轮询,但换相延迟会大一些,高速时容易失步。
// HALL 状态读取,返回 1~6,0 表示非法状态 unsigned char HALL_Read(void) { unsigned char s = 0; if (HALL_U) s |= 0x01; if (HALL_V) s |= 0x02; if (HALL_W) s |= 0x04; switch (s) { case 0x03: return 1; // 0011 case 0x02: return 2; // 0010 case 0x06: return 3; // 0110 case 0x04: return 4; // 0100 case 0x05: return 5; // 0101 case 0x01: return 6; // 0001 default: return 0; // 000 或 111 非法 } }逻辑说明:HALL 的编码顺序取决于传感器安装位置和电机相序,上面这个映射是常见的一种,但不同电机可能相反。参数上,如果读出来状态跳变顺序是 1-5-4-3-2-6 这种,说明相序反了,把其中两根 HALL 线对调或者改映射表即可。注意上电时先读一次 HALL 状态,如果返回 0,说明接线或供电有问题,这时候不要急着开 PWM,否则可能因为换相表错乱导致电机堵转发热。
3. 六步换相加双闭环:从能转到转得稳的代码路径
3.1 换相表怎么写才不会让电机抖
六步换相的核心是一张表:当前 HALL 状态决定哪两相通电、哪一相悬空、PWM 加在哪一相。常见做法是用一个 6 元素的数组存每步的 PWM 通道和方向,换相时直接查表改 PWMA 的通道使能和比较值。下面这张表是按 HALL 状态 1 到 6 对应的换相动作,具体到 STC8H 上就是设置哪几路输出有效、哪一路做 PWM 斩波。
| HALL 状态 | 导通相 | PWM 相 | 悬空相 | 说明 |
|---|---|---|---|---|
| 1 | A+ B- | A | C | 扇区 1 |
| 2 | A+ C- | A | B | 扇区 2 |
| 3 | B+ C- | B | A | 扇区 3 |
| 4 | B+ A- | B | C | 扇区 4 |
| 5 | C+ A- | C | B | 扇区 5 |
| 6 | C+ B- | C | A | 扇区 6 |
实现时不要每次换相都重新初始化整个 PWMA,那样延迟大。我一般只改 CCER 和比较值,把对应通道打开、关掉,PWM 占空比由速度环输出决定。换相发生在 HALL 中断里,中断里只做查表和改寄存器,PID 计算放在定时器中断里,两者分开,避免换相延迟被 PID 运算拖长。
3.2 速度环和电流环的参数怎么整定
竞赛方案里常见的是速度环加电流环双闭环,但很多低成本板子没有电流采样,那就只做速度环加限流保护。有电流采样时,电流环用 PI,速度环用 PI 或 PID。STC8H 的 16 位乘法器在这里派上用场,PID 运算可以全用整型,避免浮点拖慢中断。
// 增量式 PI 速度环,整型运算 int Speed_PI(int target, int actual) { static int err_last = 0, out = 0; int err = target - actual; int delta = KP * (err - err_last) + KI * err; out += delta; if (out > OUT_MAX) out = OUT_MAX; if (out < 0) out = 0; err_last = err; return out; // 返回占空比 }逻辑说明:KP 决定响应快慢,KI 消除静差。参数上,我一般先把 KI 置零,只加 KP 到电机能稳速但略有超调,再加 KI 把静差压下去。注意积分限幅一定要做,否则启动瞬间误差大,积分饱和会导致占空比冲到头,电机猛冲一下再回落。电流环的采样点要避开 PWM 开关瞬间,通常在 PWM 周期中点触发 ADC,这个时序在 STC8H 上用 PWMA 触发 ADC 来做比较稳。
3.3 启动、调速和刹车的状态机怎么组织
BLDC 带 HALL 的启动比无感简单很多,因为一开始就知道转子位置。常见状态机是:上电读 HALL → 如果状态合法,直接按当前状态给一步换相 → 开 PWM 从低占空比起步 → 速度环接管 → 收到停止命令先降占空比再关桥。刹车可以用下桥全开做短接制动,但要注意电流,竞赛里一般用减速停。
// 简化状态机 typedef enum { IDLE, START, RUN, BRAKE } MotorState; MotorState state = IDLE; void Motor_Task(void) { switch (state) { case IDLE: if (start_cmd) { state = START; } break; case START: if (HALL_Read() != 0) { Commutate(HALL_Read()); // 先给一步 SetDuty(50); // 低占空比 state = RUN; } break; case RUN: if (stop_cmd) { state = BRAKE; } break; case BRAKE: SetDuty(0); PWMA_BKR = 0x00; // 关主输出 state = IDLE; break; } }逻辑说明:启动时先给一步再开 PWM,避免不知道转子位置时乱换相。参数上,起步占空比从 5% 到 10% 起,看电机能不能顺利转起来,太小可能堵转,太大启动电流冲击。注意刹车状态关主输出后要等电机停稳再允许重新启动,否则反电动势可能干扰 HALL 读数。
4. 避坑与排查:STC8H 带 HALL 方案最容易翻车的五个点
4.1 电机不转或只抖一下
现象:上电后电机发出嗡嗡声但不连续旋转,或者只抖一下就停。原因通常是换相表顺序和 HALL 实际状态不匹配,或者 PWMA 通道映射和 PCB 不一致。解决:先用调试器读 HALL_Read 的返回值,手动转动电机看状态是否按 1 到 6 循环跳变,如果不是,调整 HALL 映射或对调 HALL 线序;再确认 PWMA_PS 和实际引脚对应,用示波器看六路输出有没有波形。
4.2 低速抖动、高速失步
现象:低速时电机一顿一顿,高速时突然停转或电流猛增。原因:低速抖动多半是换相延迟固定,PWM 频率偏低;高速失步是换相中断响应不过来,或者速度环输出饱和。解决:提高 PWM 频率到 20kHz 以上,换相中断里只做最少的寄存器操作,把 PID 移到定时器中断;高速时检查速度环输出是否长期顶到 OUT_MAX,如果是,说明限流或电压不够,需要重新匹配电机和电源。
4.3 上下桥臂直通炸管
现象:一开 PWM 就炸 MOS 或者电流异常大。原因:死区时间不够,或者互补输出配置错误,上下桥臂同时导通。解决:用示波器看上下桥臂驱动波形,确认切换时有明显空白区;死区从 1us 起调,确认无误后再往下减;检查 PWMA_CCER 和 PWMA_BKR 配置,确保互补输出和主输出使能正确。
4.4 HALL 中断丢步导致换相错乱
现象:电机运行中突然反转或卡顿,复位后又正常。原因:HALL 中断被其他高优先级中断打断,或者中断里执行时间太长,导致换相错过。解决:把 HALL 中断设为高优先级,中断里只读状态和查表改寄存器;如果用了 ADC 中断做电流采样,把 ADC 中断优先级降低,或者用 PWMA 触发 ADC 减少 CPU 干预。
4.5 速度环振荡或响应迟钝
现象:给定速度后电机转速来回波动,或者加速很慢。原因:PID 参数不合适,或者速度反馈计算周期和 PWM 周期不匹配。解决:先调 KP 到临界振荡再减半,再加 KI;速度反馈用 HALL 中断计数在固定定时器周期里算,比如每 10ms 算一次,和 PID 周期一致;注意积分限幅和输出限幅都要做,避免饱和。
5. 用示波器和串口把控制策略验证到位
5.1 三个必须看的波形和对应判断
调 BLDC 不能只靠听声音,我一般会看三个波形。第一是 HALL 三路方波,确认状态跳变顺序和换相表一致,边沿干净没有毛刺。第二是相电压波形,看六步换相的梯形波是否对称,如果某一相明显畸变,说明该相驱动或换相有问题。第三是母线电流波形,启动和加载时看冲击电流,判断限流和死区是否合理。这三个波形用四通道示波器一次就能看完,比反复改代码猜问题快得多。
5.2 串口上位机怎么把转速和占空比实时画出来
竞赛调试时我会在 STC8H 上开一个串口,每 20ms 把目标转速、实际转速、占空比、HALL 状态打成一帧,上位机用简单脚本画曲线。这样调 PID 时能直接看到超调和振荡,不用反复烧录。帧格式用文本就行,比如“T:1000 A:980 D:350 H:3”,解析简单,也不占太多带宽。
// 串口上报,20ms 一次 void Report_Task(void) { if (report_tick >= 20) { report_tick = 0; printf("T:%d A:%d D:%d H:%d\r\n", target_speed, actual_speed, duty, hall_state); } }逻辑说明:printf 在 8051 上比较重,如果串口波特率不高,可以改成手动拼字符串或者用 DMA。参数上,20ms 对应 50Hz 刷新,看动态响应够用;波特率 115200 下一帧十几个字节,占用时间很短,不影响控制中断。
5.3 从竞赛台架到实际负载要改什么
竞赛时电机空载或轻载,参数调好就能跑。换到实际负载,比如带减速箱或者风扇叶,惯量和阻力都变了,速度环的 KP 和 KI 要重新整定,通常 KP 要加大,KI 略减。另外实际负载下启动电流更大,限流阈值要按母线电流有效值设,别按峰值设,否则一加载就触发保护。我自己的习惯是每次换负载都先用手转一下电机感受阻力,再上电从低占空比慢慢加,看电流和转速是否线性,确认没问题再跑闭环。这套流程看起来笨,但比直接上闭环炸管再回头查省时间。希望帮到你。
本文还有配套的精品资源,点击获取