简介:STC 51单片机驱动三相无刷电机(BLDC)的完整工程资源,面向嵌入式入门开发者、电机控制爱好者及课程设计人群,解决低成本单片机实现电机换相与调速的实战问题。压缩包共收录17个文件,以C源码、头文件、Keil工程文件为主,同时包含Hex固件、列表/目标文件及工程备份,并附带三相无刷电机驱动电路PDF,便于软硬件对照学习,整体仅145KB,轻量易用,目录结构清晰。目前已有2129人学习并下载,内容实用性获得验证。资料覆盖带霍尔传感器与无感两种控制方案:带霍尔方案采用捕捉中断换相,无感方案基于反电动势检测,均支持电位器调速,适合从六步换相入门到进阶调试。借助源码可快速理解PWM输出、换相逻辑与中断处理,结合电路图可掌握MOSFET驱动与功率电路设计要点,是一份可直接烧录验证的电机驱动参考设计。
1. STC 51 单片机驱动三相无刷电机:方案、资源和硬约束
STC 51 单片机驱动三相无刷电机,不是一个纸面项目。多数人第一反应是上 FOC 算法,但手里只有一块 STC8 时,跑 FOC 既缺硬件加速,三相电流同步采样也不方便。我一般先做“无感方波电调”:用 STC8 内置比较器检测反电动势过零点,触发六步换相,功率级用三相全桥配半桥驱动芯片。这个方案成本低、可复现,启动逻辑、堵转保护、调速也都能在 51 内核上跑通。适合课程设计、小功率风机、桌面电调这类场景。前提是认清硬件边界:PWM 频率做到 20kHz 左右,母线电压控制在 12V 到 24V,MOSFET 选型留足两倍耐压余量,剩下的问题基本都出在换相时序上。
2. 三相无刷电机驱动模型:为什么无感方波是 51 单片机最稳的切入路径
2.1 先想清楚:用 51 做三相无刷驱动,第一选择不是 FOC
无感 FOC 在 STM32 上已经是很成熟的方案,但它对 MCU 的要求是:至少两路 ADC 同时采样相电流,PWM 和 ADC 要硬件触发同步,电流环要跑到 10kHz 以上,速度环和位置估算还要吃掉大量周期。51 内核没有硬件除法器做 Clarke/Park 变换,靠软件逐周期算电流环,外设同步又不如 ARM 方便,整体做下来代码复杂度和调试成本都会高很多。
无感方波电调就不一样。它的核心是检测“没通电那一相”的反电动势过零,然后每 60° 电角度换一次相。检测只需要一个比较器,换相只需要一组带死区的互补 PWM。STC8 系列恰好把这两样都放在片内:高级 PWM 模块可以输出三相六路互补带死区的波形,片上模拟比较器可以直接接反电动势采样网络。这等于为六步换相方案量身定做。
2.2 六步换相:一张导通表说清楚电机怎么转
三相无刷电机的定子有三相绕组,转子是永磁体。要让转子连续转起来,必须按转子位置依次给两相通电,第三相悬空。转子每转过 60° 电角度,就切换一次通电状态,一圈下来就是六步。这六步的通断关系如下表。
| 步序 | 上桥导通 | 下桥导通 | 悬浮相 | 用悬浮相反电动势检测位置 |
|---|---|---|---|---|
| 1 | A | B | C | C |
| 2 | A | C | B | B |
| 3 | B | C | A | A |
| 4 | B | A | C | C |
| 5 | C | A | B | B |
| 6 | C | B | A | A |
这里的 A、B、C 是电机三相线,顺序只是逻辑定义。如果电机反转,把 1 和 4、2 和 5、3 和 6 对调即可,也可以直接交换任意两根电机线。程序里真正要维护的是一张六行六列的换相表,每行对应 PWM 通道和普通 IO 的输出组合,后面第 4 章会直接给代码。
表格里值得注意的一点:同一步里,上桥和下桥不能是同一相。比如步序 1 是 A 上桥、B 下桥,如果同时把 A 下桥也打开,母线直接就短路了。这就是死区存在的原因。
2.3 反电动势过零检测:悬浮相电压和中点比一比
反电动势过零检测的原理,可以从电机端电压方程看。对于星形连接的三相绕组,任意一相端电压可以写成:
悬浮相电压 ≈ 三相中点电压 + 该相反电动势
悬浮相没有电流,所以电阻压降和电感压降都为 0,这一点是方案成立的前提。当转子转到某个角度时,悬浮相反电动势恰好为 0,此时悬浮相端电压就等于三相中点电压。如果拿一个比较器,把正端接悬浮相分压后的电压,负端接三相中点分压后的电压,比较器翻转的时刻就是反电动势过零时刻。
这个过零时刻比“该换相的真正时刻”提前 30° 电角度。检测到过零后,需要再等 30° 才执行下一次换相。这个延时不是固定的时间长度,而是和电角速度成反比,程序里要按当前转速换算成定时器计数值。低速时 30° 对应的时间长,高速时对应的时间短。
2.4 调制策略:高边斩波是 STC 驱动电路最稳的调法
六步换相只解决“哪两相通电”,没解决“通多大电”。三相无刷电机的调速,本质是调节通电相的电压,常用做法是 PWM 调制。调制策略有两种,全斩波和高边斩波。
全斩波是同一时刻把上桥、下桥两个管子同时 PWM,优点是通流能力均匀,但续流路径复杂,反电动势检测时刻很难选。高边斩波是上桥管走 PWM,下桥管常通,PWM 关断期间靠下桥续流。高边斩波时,悬浮相电压在 PWM 导通期间相对干净,过零检测更容易做准,也不需要额外的高端电流检测。STC8 的 PWMA 模块支持独立设置每个通道的占空比,正好适合高边斩波:三个高边通道给 PWM,三个低边通道作为普通 IO 输出常电平。
PWM 频率一般选 16kHz 到 24kHz,高于人耳听觉范围,也避开 AM 收音机频段。死区时间从 0.5μs 到 1.5μs 都常见,具体取决于 MOSFET 栅极电荷和驱动芯片的灌拉电流能力。
3. 硬件电路落地:三相全桥、栅极驱动和反电动势采样
3.1 功率级:MOSFET 与栅极驱动芯片怎么选
三相全桥总共六只 MOSFET。我做这类板子的习惯是:先用母线电压定 MOSFET 耐压,再考虑导通阻抗。比如 12V 系统选 30V 或 40V 耐压的管子,24V 系统直接上 60V 或 75V 耐压档。PWM 关断瞬间会有一到两倍母线电压的尖峰,耐压余量不足是炸管第一大原因。
STC8 的 IO 灌拉电流只有 20mA 左右,直接驱动 MOSFET 栅极既慢又不够力。功率 MOSFET 的栅极电荷 Qg 通常在 10nC 到 50nC 之间,要让栅极电压在几百纳秒内完成翻转,需要专用的栅极驱动芯片。三相全桥每相用一个半桥驱动,比如 IR2101、IR2104、EG2104 这类芯片,一颗管一相高边和低边,硬件结构统一。
IR2101 这类半桥驱动的高边供电靠自举电容。自举电容一端接 VB,一端接 VS,通常用 4.7μF 加 100nF 并联。自举二极管必须用快恢复型,比如 US1M 或 UF4007,不能用整流桥用的 1N4007,否则高边打开时电压恢复速度跟不上。
3.2 反电动势采样:分压网络、虚拟中点与比较器
反电动势采样不能把母线电压直接送进 STC 的比较器。STC8 的模拟比较器输入范围到不了 24V,必须先分压。我在每个相线对地接两个 10kΩ 电阻,分压中点引出到比较器正端。三相中点如果电机内部没有引出,就用三个阻值相等的电阻在板子上搭一个虚拟中点,虚拟中点电压通过另一个 10kΩ 电阻接到比较器负端。
这个网络的关键是三个相的分压系数必须一致,虚拟中点的三个电阻也必须是同一个批次,否则比较器翻转时刻会引入固定偏差,表现为电机高速运行时换相点左右漂移。分压电阻后面再各并一只 1nF 到 10nF 的瓷片电容做低通滤波,可以滤掉 PWM 开关噪声。电容不能选太大,RC 时间常数会带来相位延迟,高速时影响明显,后面第 5 章会算这个账。
3.3 电流采样与保护电路
过流保护在无感方波电调里必须单独处理。最简单的做法是在三相下桥公共端到地之间串一个采样电阻,阻值 0.01Ω 到 0.05Ω,功率按最大电流的平方乘以阻值留两倍余量。采样电阻两端电压经过运放放大后,一路进 STC 的 ADC 做平均电流监控,一路接比较器做硬件过流阈值。硬件过流比较器动作后直接关断 PWM 输出,比软件中断快一个量级。
母线电容也不可省。PWM 开关时电机绕组电感会产生很高的 di/dt,母线电压会被顶起来。母线正负极之间要放一个大电解电容,容量按 100μF 到 1000μF 之间选,再看高频再并 100nF 陶瓷电容。没有这组电容,MOSFET 的雪崩击穿电压被反复冲击,寿命明显缩短。
3.4 12V/24V 驱动板的最小器件参数表
下表的参数以 12V 到 24V 母线、额定电流 5A 以内为基准,是我个人常用的起步组合,不追求极限参数,优先保证能稳定跑起来。
| 器件 | 推荐参数 | 说明 |
|---|---|---|
| MOSFET | 60V 耐压,Qg 20nC 以内,Rds(on) 10mΩ 左右 | 24V 母线留 2.5 倍余量 |
| 驱动芯片 | IR2101/EG2104 半桥驱动,三片 | 每片负责一相 |
| 自举电容 | 4.7μF X7R + 100nF 并联 | 高边开关频率高时必须并小电容 |
| 自举二极管 | US1M 或 UF4007 | 快恢复,反向恢复时间低于 100ns |
| PWM 频率 | 16kHz 到 20kHz | 避开音频,减少铁损 |
| 死区时间 | 0.5μs 到 1.5μs | 按实际驱动芯片延迟整定 |
| 电流采样 | 0.02Ω/2W 采样电阻 + 运放 | 硬件比较器做过流快速关断 |
| 反电动势采样 | 10kΩ 分压 + 1nF 低通 | 三相对称,虚拟中点阻值要一致 |
| 母线电容 | 470μF 电解 + 100nF 陶瓷 | 吸收 PWM 换流尖峰 |
4. 程序实现:六步换相状态机、开环启动与闭环切换
4.1 换相状态机与 STC PWM 输出映射
先写最底层的换相函数。以 STC8H 系列为例,PWMA 模块有六路输出通道,每三路对应一组互补 PWM。换相表里每一行的含义是:哪一路高边给 PWM,哪一路低边拉高,其余四路全部关断。
// 以 STC8H8K64U 为例,寄存器名以厂家头文件为准 // 数组按六步换相顺序排列,每行对应 A上B下、A上C下、B上C下、B上A下、C上A下、C上B下 static const uint8_t comm_table[6][6] = { {1, 0, 0, 0, 1, 0}, // 步0: A上桥PWM,B下桥常通,C悬空 {1, 0, 0, 0, 0, 1}, // 步1: A上桥PWM,C下桥常通,B悬空 {0, 1, 0, 0, 0, 1}, // 步2: B上桥PWM,C下桥常通,A悬空 {0, 1, 0, 1, 0, 0}, // 步3: B上桥PWM,A下桥常通,C悬空 {0, 0, 1, 1, 0, 0}, // 步4: C上桥PWM,A下桥常通,B悬空 {0, 0, 1, 0, 1, 0}, // 步5: C上桥PWM,B下桥常通,A悬空 }; void comm_set_step(uint8_t step) { uint8_t i; // 换相前先关断全部 PWM 输出,给死区留出时间 PWMA_ENO = 0x00; // 按换相表设置 PWMA 各通道占空比或普通 IO 电平 for (i = 0; i < 6; i++) { if (comm_table[step][i]) { if (i < 3) { // 高边通道: 交给 PWMA, 以固定占空比输出 PWM_ENABLE_CH(i); PWMA_CCR1 = current_duty; // 当前占空比 } else { // 低边通道: 普通IO拉高常通 LOW_SIDE_IO_HIGH(i - 3); } } else { if (i < 3) { PWM_DISABLE_CH(i); } else { LOW_SIDE_IO_LOW(i - 3); } } } // 死区延时后重新使能 PWM delay_us(1); PWMA_ENO = 0x3F; }代码里 PWMA_ENO 是整个 PWM 模块的输出使能开关,关断后六个引脚全部变高阻,避免换相瞬间出现上桥和下桥同时导通。current_duty是全局调速变量,对应高边 PWM 占空比。低边三路不用 PWM,直接用普通 IO 拉高或拉低。
需要注意不同 STC 型号的寄存器命名有差异,比如 STC8A、STC8H 的 PWMA 通道使能位定义就不同,移植时要翻一遍对应系列的数据手册。
4.2 开环启动流程:对齐、强制换相、升频
三相无刷电机静止时,反电动势为 0,无法用反电动势过零检测判断转子位置。所以启动必须分成三步:先对齐,再开环拖动,最后切闭环。
对齐就是随便给某一相通电一段时间,让转子被磁力拉到固定位置。接着按换相表顺序强制切换,每步等待固定时间,时间逐次缩短,相当于用一个“假的反电动势信号”把电机硬拖起来。当转速升到反电动势足够大、过零检测结果稳定后,才切到真正的闭环。
void motor_start(void) { uint32_t step_interval_us; uint16_t step_count; // 1. 对齐: 固定给第0步通电 200ms,让转子吸附到确定位置 comm_set_step(0); delay_ms(200); // 2. 开环拖动: 初始每步 5ms,约等于200Hz换相频率 step_interval_us = 5000; step_count = 0; while (step_count < 120) { // 每步结束切换下一相 comm_set_step((step_interval_us % 6)); // 按序换相 delay_us(step_interval_us); // 每换一相,间隔缩短,表示转速提升 if (step_interval_us > 800) { step_interval_us -= 80; } step_count++; } // 3. 此时反电动势应足够明显,交给过零检测闭环 closed_loop_enable(); }这段代码的逻辑是“先给一个固定初始换相频率,之后逐步缩短换相周期”。初始 5ms 每步对应 200Hz 换相频率,四极电机大概是 300rpm 上下。升频步进 80μs 会让电机在几百毫秒内接近目标转速,太快会失步,太慢会在低速区停留过久,PWM 占空比低、反电动势检测又不可靠。
启动过程是否顺利,很大程度上取决于初始占空比。占空比太小,转子扭矩不足拖不起来;占空比太大,电流冲击大但不转。一般从 10% 到 15% 开始试,逐次加 5% 观察。
4.3 过零检测与 30° 换相延时
闭环阶段的核心是过零检测中断。比较器正端接悬浮相分压,负端接虚拟中点,电流为零的悬浮相反电动势过零时比较器翻转,触发中断。中断里只记录“发生过零”这件事,并启动一个 30° 延时定时器,延时到后再调用comm_set_step()换相。
// 比较器中断服务程序,检测到反电动势过零 void CMP_isr(void) __interrupt { uint16_t delay_count; // 清除 STC 比较器中断标志位,避免同一窗口重复进中断 CMPCR1 &= ~0x01; // 关闭比较器中断,在换相完成前不响应新过零 CMPCR1 &= ~0x80; // 30° 电角度对应的延时 = 电机换相周期的 1/6 // comm_period 是当前一步的周期,由前一步测得的两次换相间隔得到 delay_count = comm_period / 6; set_comm_delay_timer(delay_count); }这里的关键是,过零时刻和换相时刻相差 30° 电角度,而一次 60° 换相周期正好是 6 等分里的 1 份,所以延时计数直接取当前换相周期的 1/6。comm_period建议由定时器测两次换相中断之间的实际间隔,而不是用查表值,因为负载变化时转速会波动,用实测周期算延时更准确。
比较器中断必须在换相后重新打开,而且只能在一个合适的窗口内打开。下一节会说这个窗口怎么控制。
4.4 堵转保护与调速
堵转保护用软件计数做就行。做法是维护一个“过零超时计数器”,每次换相时清零,比较器捕捉到过零就重新赋值。如果连续多次换相都没有出现新的过零,说明电机已经失步或堵转,此时立即关断 PWM,进入故障态,避免母线电流持续过大把 MOSFET 烧掉。
// 在换相定时中断里执行 void comm_timer_isr(void) __interrupt { zero_cross_timeout++; // 超过 5 个换相周期没有检测到过零,判定堵转 if (zero_cross_timeout > 5) { PWMA_ENO = 0x00; // 关断全部 PWM 输出 motor_state = MOTOR_FAULT; return; } }调速方面,无感方波的调速就是调整current_duty。把目标转速换算成占空比,再用简单的斜率限制做加减速,防止占空比突变。速度闭环要等反电动势过零稳定后,把换相频率换算成实际转速,再做一个 PI 调节器输出占空比修正量。
5. 调试与参数调优:示波器看点、相位延迟补偿和比较器开窗
5.1 示波器验证换相波形
上电后第一件事是把示波器探头夹在电机某一相线上,看端电压波形。正常闭环运行时应看到连续的梯形波,六个阶梯清晰可辨,三相波形互差 120°。如果梯形波高电平只有很短一段,说明换相表顺序不对,电机转不利索。如果波形里夹着明显毛刺,首先怀疑死区时间太小或母线电容不足,用示波器接地环短接探头尖端,把噪声源逐个排除。
反电动势过零点在悬浮相电压波形的中点附近,拿另一根探头同时测比较器输出引脚,能看到比较器翻转沿正好落在悬浮相电压过零处。若翻转时刻偏离,问题多半出在分压电阻不对称或 RC 滤波相位延迟上。
5.2 启动失败与高速抖动的排查表
常见问题基本就集中在启动和高速两个阶段,按下面这个表排查效率最高。
| 现象 | 可能原因 | 检查项 |
|---|---|---|
| 上电后电机只抖一下 | 对齐时间不够或初始占空比太小 | 对齐时间加到 300ms,初始占空比提到 15% |
| 启动“咔咔”响但不转 | 开环升频过快,转子跟不上 | 降低升频步进,比如从每次减 80μs 改为减 40 |
| 低速正常,高速抖或停 | 反电动势 RC 滤波延迟导致换相滞后 | 减小滤波电容,或按转速查表提前换相 |
| 一通电就过流保护 | 采样电阻选得过大或比较器阈值太低 | 量采样电阻压降,算实际电流 |
| 用 Proteus 仿真时正常,实板不转 | 分压网络参数不一致,虚拟中点偏移 | 用万用表量三个相分压点电压是否相同 |
Proteus 仿真里反电动势检测是理想化的,实板上分压电阻、比较器失调电压、PCB 布线寄生参数都会带来偏差。仿真能验证的是状态机逻辑和启动流程,不能替真实电机验证过零检测。
5.3 反电动势检测的相位补偿
RC 低通滤波会带来相位滞后。一阶 RC 的滞后角近似为 arctan(ωRC),其中 ω 是角频率。举个例子,10kΩ 电阻配 4.7nF 电容,RC 时间常数约 47μs,当换相频率 800Hz 时,滞后角约 13°。这意味着检测到的过零时刻比真实过零晚了 13°,如果还按 30° 延时换相,实际换相点变成 43°,扭矩会下降。
补偿办法是软件按转速查表。我一般把转速分成十几挡,每挡对应一个“过零后延时值”修正系数,高速挡直接减小延时。
// 按换相频率分挡,返回过零后需要补偿的定时器计数值 uint16_t phase_comp_count(uint16_t comm_freq_hz) { uint8_t index; // 300Hz 到 3000Hz 分成 13 挡 index = (comm_freq_hz - 300) / 200; if (index >= 13) { index = 12; } // phase_comp_table 由实测得到,数值随 RC 参数变化 return phase_comp_table[index]; }这个查表数组不建议纯理论计算,因为它还包含比较器响应延迟、PWM 关断导致检测点偏移等非线性因素。最稳的做法是闭环跑稳后,用示波器看实际过零点和比较器翻转沿的时间差,填进表格。
5.4 实测技巧:用定时器给比较器开检测窗口
过零检测最怕的干扰来自 PWM 换流瞬间。每次 PWM 开关,端电压上都会出现高频振铃,如果比较器一直开着,一次误触发就可能让换相时序彻底错乱。我在实际项目里给比较器加了一个“开窗”机制,思路很简单。
每个换相周期有 60° 电角度,过零点只会出现在这 60° 的中间区域,前后各有一段不会出现过零。程序在换相完成后启动一个窗口定时器,等到换相后的 1/6 周期再打开比较器中断,检测到过零后立刻关闭比较器,直到下一次换相前重新开窗。这样比较器只在反电动势过零最可能出现的时刻工作,PWM 开关噪声造成的误触发被挡在窗口外。
窗口宽度要足够覆盖过零点的全部可能位置。负载变化会导致过零时刻前后偏移,我通常留 1/3 个换相周期宽度,也就是 20° 电角度左右,前后各 10° 的裕量。这个值既不会漏检测,也不会让窗口宽到把相邻换相继流噪声收入。
本文还有配套的精品资源,点击获取