2026/10/3 14:56:47

AI8051U三相SPWM变频驱动实战:从PWMA配置到死区踩坑

AI8051U三相SPWM变频驱动实战:从PWMA配置到死区踩坑 最近在调AI8051U的PWM外设把三相SPWM变频驱动整套流程跑通了。这个例程用PWMA的3个通道做三相互补输出相位依次差120°配合硬件死区时间直接驱动三相全桥异步电机和PMSM都能拖起来转。网上关于AI8051U做SPWM的资料还不多正好把寄存器配置、正弦表生成、相位累加和实测踩坑一起整理出来给准备做变频器、逆变电源或者伺服驱动方向的朋友一个可以直接抄作业的底子。整个方案的核心就三件事用PWMA_CH1/CH2/CH3输出三路PWM各配一个互补通道再把三路占空比按照相位差120°的正弦规律刷新。但越简单的方案坑越藏在细节里——死区时间、中心对齐模式下的更新时机、正弦表精度哪一样没处理好波形都会直接给你脸色看。1. 三相SPWM变频驱动先弄明白这几件事再动手1.1 电机要的是正弦电压但功率电路只会开关三相异步电机或者永磁同步电机要转起来定子绕组上必须有相位差120°的正弦电压。真正的正弦电源当然好办但变频器要调速得把直流母线电压变成频率可调、幅值可调的三相交流。功率电路里我们手里只有MOS管或IGBT这些器件只会干两件事导通或者关断。所以唯一的办法就是高频开关用PWM脉冲序列去等效正弦波。电机绕组本身是个大电感电流是连续的相当于一个天然的低通滤波器。只要PWM载波频率足够高绕组里的电流就基本是平滑正弦。这就是SPWM的第一个要点PWM的占空比不是随手给的必须按正弦规律变化。把这个规律数字化就是一张正弦表表的点数代表一个输出周期内的刷新次数表中每个值对应当时刻的占空比。1.2 三相全桥为什么必须互补输出加死区每一相就是一个半桥上下两个管子串联在母线上。上管导通时桥臂中点接正母线下管导通时接负母线或者地。上下管绝对不能同时导通否则母线直接短路瞬间电流能把管子炸飞。理论上上管关断、下管导通可以无缝衔接但实际管子有延迟关断需要几十到几百纳秒开通往往更快。如果下管在上管还没完全关断时就开通就是直通。互补输出就是这个需求来的硬件上让两路输出电平互反中间插入一段可编程的死区——上管关断后等一段时间再让下管导通。PWMA这类高级定时器把死区发生器做在硬件里软件只需要写一个寄存器值不用在中断里折腾延迟。1.3 普通定时器和IO翻转为什么做不了这件事有人问用普通定时器加IO翻转能不能做SPWM。能做但上限很低。三路PWM要各自独立、还要互补、还要死区、还要在中断里高频更新占空比——普通定时器只有一路比较通道IO翻转软件开销巨大载波频率稍微一高CPU就忙不过来示波器上一看波形全是抖的。PWMA有3个带互补输出的通道、16位计数器、死区寄存器、刹车输入这些就是为电机驱动准备的。它的结构和STM32的TIM1高级定时器师出同门寄存器命名和配置思路都很接近玩过STM32的人上手会非常快。2. SPWM背后的关键参数调制比、86.6%和死区时间2.1 查表法怎么做SPWMSPWM实现方式主流有两种。一种是实时比较法用正弦参考波和三角载波实时比较生成PWM另一种是查表法把正弦波一个周期采样成N个点存成表每个载波周期取出对应点的值作为占空比。单片机方案九成用查表法因为在8051上实时算正弦函数代价太高了。表怎么生成以256点为例点索引i从0到255对应角度0°到360°表中值用这个公式tab[i] (0.5 0.5 * sin(2 * π * i / 256)) * 8192乘8192是为了放大成整数。注意那个0.5偏移量半桥电路没有负压输出正弦波必须抬一个直流分量让占空比在0到100%之间摆动而不是在正负之间摆动。这个直流分量经过电机绕组会被电感滤掉不会影响输出线电压放心用。2.2 86.6%的线电压利用率是怎么算出来的这个86.6%经常有人问我把推导写清楚。设直流母线电压为Vdc每相桥臂中点相对负极的电压在0和Vdc之间脉动。SPWM线性调制时调制比m正弦参考幅值除以载波幅值不能超过1。此时相电压基波的峰值为Uph_peak m × Vdc / 2m1时最大为Vdc/2为什么是Vdc/2因为桥臂输出平均电压在0.5×Vdc上下摆动正弦偏移的峰峰值是m×Vdc取一半才是幅值。三相线电压是两相电压之差幅值关系是√3倍Uline_peak √3 × Uph_peak ≤ √3 × Vdc / 2 ≈ 0.866 × Vdc这就是86.6%的来历同样一个直流母线SPWM线性调制能送出去的最大线电压峰值只有母线电压的86.6%。如果电机额定电压是380V那么直流母线至少要440V左右才够用。那剩下14%怎么抠回来SVPWM空间矢量调制通过在每个扇区重新分配零矢量作用时间等效调制比可以做到SPWM的2/√3≈1.1547倍线电压峰值能顶到Vdc利用率100%。做变频器想榨干母线电压下一步就往SVPWM走。我这个例程先用SPWM把整条链路跑通后面换SVPWM只需要改占空比刷新算法硬件不用动。2.3 死区时间选多大死区设置原则大于最坏情况下从“关断指令发出”到“管子真正关断”的总延迟再留一点余量但也不能太大。死区过大输出电压波形会削顶低次谐波增加电机能听到明显的“沙沙”声死区过小直通风险直接拉满。常见经验值参考功率器件典型死区备注低压小功率MOSFET几十W0.3~1μs开关快死区可以小中功率MOSFET百W到kW级1~2μs常见1.5μsIGBTkW级以上2~5μs拖尾电流长死区要大以PWMA_DTR寄存器为例它的结构和STM32 TIM1的DTG寄存器接近低7位直接乘定时器时钟周期。24MHz主频下1个计数周期约41.7ns想设2μs就写48也就是0x30。具体换算表要看数据手册记住“死区时间寄存器值×定时器时钟周期”这个关系就不会算错。3. AI8051U的PWMA初始化细节3.1 通道和引脚分配PWMA有4个通道但只有CH1~CH3带互补输出正好对应三相。通道分配如下通道输出互补输出对应相CH1PWMA_CH1PPWMA_CH1NU相0°CH2PWMA_CH2PPWMA_CH2NV相120°CH3PWMA_CH3PPWMA_CH3NW相240°CH1P和CH1N这一对接U相半桥的上下管栅极驱动输入CH2、CH3同理。引脚映射要查数据手册的引脚切换寄存器不同封装的可用引脚不一样。我这次把三对输出放在P1口和P2口的部分引脚旁边就是半桥驱动芯片的输入走线尽量短栅极驱动回路面积越小越好。3.2 载波频率怎么定载波频率PWM频率是SPWM最基础的设计参数选择时主要权衡三点听觉噪声低于20kHz人耳能听到电机和电感会啸叫。要么跑到20kHz以上要么接受可闻噪声开关损耗频率越高每个周期MOS管开关损耗越大散热压力越大分辨率占空比分辨率取决于计数器最大值ARR越大分辨率越高但ARR大了载波频率就降了具体计算中央对齐模式f_pwm 主频 / (预分频1) / (ARR1) / 2主频24MHz预分频为1ARR1499时f_pwm 24MHz / 1 / 1500 / 2 8kHz占空比分辨率是1500级约10.5位对一般电机驱动足够了。想上20kHz听不到啸叫ARR就得压到599以下分辨率降到约9.2位SPWM谐波会大一些。这就要看你更在意噪声还是波形质量了。我这例程用8kHz波形质量和散热都比较均衡电机有一点高频声但完全能接受。3.3 初始化代码与顺序初始化顺序有讲究先配预分频和重载值再配通道模式再配死区最后开主输出和计数器。千万别先把MOE主输出使能打开再去配通道那样上电瞬间输出引脚可能是不可控电平直接炸管。#define FOSC 24000000UL #define ARR_VAL 1499 // 8kHz载波 void PWMA_Init(void) { // 1. 预分频: 1分频 PWMA_PSCRH 0x00; PWMA_PSCRL 0x00; // 2. 自动重载值: 决定载波频率 PWMA_ARRH (unsigned char)(ARR_VAL 8); PWMA_ARRL (unsigned char)(ARR_VAL 0xFF); // 3. CH1~CH3 配成PWM模式1, 使能预装载 // OC1M[2:0]110, OC1PE1, 所以CCMR0x68 // 注意: 部分型号的CCMR寄存器把CH1/CH2拆成两个字节, 以头文件为准 PWMA_CCMR1 0x68; // CH1 PWM模式1 预装载 PWMA_CCMR2 0x68; // CH2 // CH3 同样处理, 寄存器名称可能是PWMA_CCMR3或CCMR2高字节 // 4. 使能3路输出及其互补输出, 高电平有效 PWMA_CCER1 0x55; // CH1/CH1N CH2/CH2N PWMA_CCER2 0x05; // CH3/CH3N // 5. 死区: 24MHz下每步41.7ns, 0x30约2us PWMA_DTR 0x30; // 6. 开主输出, 刹车配置按需扩展 PWMA_BKR 0x80; // MOE1 // 7. 中央对齐模式1 自动重载预装载 计数器使能 // CR1: ARPE1(0x80), CMS01(0x20), CEN1(0x01) PWMA_CR1 0xA1; // 8. 更新中断使能 PWMA_IER 0x01; }这段代码里的寄存器名称在不同型号上可能有细微差别比如CCMR1到底是同时管CH1/CH2还是每个通道一个寄存器。写代码前先把官方头文件打开对一遍别直接抄。原理一样只是寄存器拆分方式不同。4. 三相120°相位差的软件实现正弦表相位累加器4.1 先用Python把正弦表生成出来手算正弦表不现实用脚本生成最稳。下面这段Python输出256点、13位精度的表值域0到8192正好对应占空比0到100%import math N 256 SCALE 8192 for i in range(N): s math.sin(2 * math.pi * i / N) v int(round((0.5 0.5 * s) * SCALE)) v max(0, min(SCALE, v)) print(f0x{v:04X},, end) if (i 1) % 8 0: print()生成的256个值粘进Keil定义成code数组unsigned int code sin_tab[256] { 0x1000, 0x1047, 0x108F, 0x10D6, 0x111D, 0x1164, 0x11AB, 0x11F2, // ... 共256个 };4.2 120°相位差一个小技巧不用第二张表三相互差120°最笨的办法是做三张正弦表没必要。256个点对应360°120°就是256/3≈85.33个点240°就是170.67个点。取整#define PHASE_B 85 // 120° #define PHASE_C 171 // 240°在中断里取表时直接给索引加这两个偏移再按0xFF截断就能得到120°和240°的占空比。因为256是2的8次方0xFF这个操作比取模快得多在中断里这点开销值得抠。索引差85.33取整为85带来的相位偏差只有0.47°对电机驱动完全可以忽略。4.3 中断服务函数占空比刷新#define ARR_VAL 1499 #define SCALE_BITS 13 #define SCALE_VAL (1 SCALE_BITS) // 8192 unsigned long phase_acc; // 相位累加器, 高16位作为表索引 unsigned long phase_step; bit half_cycle_flag; void PWMA_ISR(void) interrupt 13 { unsigned int idx; unsigned int arr (unsigned int)(ARR_VAL 1); if (PWMA_SR1 0x01) { PWMA_SR1 0x01; // 中央对齐模式下, 计数器上溢和下溢各产生一次更新事件 // 一个PWM周期会有两次中断, 这里只在第二次进入时刷新, // 保证载波周期内三相占空比一致 if (half_cycle_flag 0) { half_cycle_flag 1; return; } half_cycle_flag 0; idx (unsigned int)(phase_acc 16); PWMA_CCR1 (unsigned int)(((unsigned long)sin_tab[idx] * (unsigned long)arr) SCALE_BITS); PWMA_CCR2 (unsigned int)(((unsigned long)sin_tab[(idx PHASE_B) 0xFF] * (unsigned long)arr) SCALE_BITS); PWMA_CCR3 (unsigned int)(((unsigned long)sin_tab[(idx PHASE_C) 0xFF] * (unsigned long)arr) SCALE_BITS); phase_acc phase_step; } }为什么用“高16位作索引”而不是单独一个递增变量因为相位累加器天然支持变频。输出频率不是靠改变表内跳进步长而是靠改变phase_step这个累加步进。累加器溢出一次就是完整一圈溢出周期等于2^32除以phase_step个中断周期输出频率就出来了。4.4 变频控制和V/F曲线输出频率f_out和相位步进的关系phase_step f_out × 2^32 / f_update其中f_update是实际刷新占空比的频率本例是载波8kHz。50Hz时phase_step 50 × 4294967296 / 8000 ≈ 26843546代码里freq_x10单位是0.1Hz用32位乘法近似void SetOutputFreq(unsigned int freq_x10) { // freq_x10 × 4294967296 / 8000 freq_x10 × 536870.912 // 近似为 ×536871, 频率范围0.1~400Hz内误差小于0.1% phase_step (unsigned long)freq_x10 * 536871UL; }只调频率不调电压不行。三相电机定子绕组是电感电压不变频率往下调电流会暴涨磁路饱和、绕组发热。所以变频器都有V/F曲线基频以下保持电压和频率的比值恒定。最简单的实现就是让调制深度和频率一起变——比如10Hz时调制深度给0.250Hz时给到1.0。例程里留一个全局变量mod_depth在中断里做一次乘法缩放占空比后面想加恒压频比控制直接改这个变量就行。注意调制深度超过1.0就是过调制波形会削顶这里先不涉及。5. 实测踩坑从死区直通到MOS管发热的排查链路5.1 坑一死区没生效上电就直通第一次上电我犯了个典型错误初始化代码里DTR写的是0x00想着先把波形跑出来再说。结果母线电流限制电阻直接烫手不到10秒就闻到糊味。用示波器同时测同一桥臂的CH1和CH1N栅极波形看到上下管驱动信号存在重叠区域——上管还没关完下管就开了这就是直通。排查链路我建议按这个顺序走别跳步先把母线断开只给控制电路供电测6路栅极驱动波形看同一桥臂两路信号有没有“同时为高”的时刻如果有检查死区寄存器值如果没有但母线仍然过流重点检查驱动芯片逻辑和MOS管是否已经损坏确认死区正常后串一个限流电阻或者白炽灯再上母线逐级加压把DTR改成0x30约2μs之后波形上出现了干净的死区窗口也就是两路都为低的平台母线电流恢复正常。这个坑基本每个人都会踩一次好在排查也不难就怕上来就直接满电压跑。5.2 坑二中央对齐模式的中断次数没算清楚这是我调试时最隐蔽的一个问题。第一次写ISR我只在更新中断里“每次进来就刷新占空比并累加相位”结果输出频率比设定值高了一倍。原因在于中央对齐模式一个PWM周期有两次更新事件上溢和下溢各一次中断频率实际是16kHz不是我以为的8kHz。相位步进按8kHz算实际以16kHz累加频率自然翻倍。解决办法就是4.3代码里的half_cycle_flag两次中断只做一次刷新。这个坑也提醒我写软件前一定要先把硬件的更新事件触发条件读透寄存器手册里关于更新事件那几行字值得反复看。5.3 坑三MOS管发热但波形看着没问题死区正常、没有直通带个小电机跑了一会儿MOS管还是烫。从前往后排查下来有三个隐藏点栅极驱动电压不足。自举电容太小100nF根本不够上管导通后期栅极电压跌落管子没完全导通进入线性区发热。换成1μF低ESR电容后明显改善。栅极电阻太大。最早用100Ω想抑制振铃结果开关过程被拉长开关损耗剧增。降到22Ω同时给关断回路并联一个二极管加速泄放发热才正常。载波频率过高。测试时为了静音试过32kHz开关损耗直接翻倍。这个功率级别还是回到8kHz加个小风扇稳定跑半小时温升在可接受范围。MOS管发热总结下来就三条线没完全导通是导通损耗开关过程太长是开关损耗直通会快速炸管。按这三条线查基本都能找到原因。5.4 故障保护别省刹车输入和逐波限流电机驱动必须要考虑故障过流、过压、堵转。PWMA的刹车输入BKIN可以在硬件层面直接关闭所有输出响应是纳秒级的比软件快得多。我把母线的过流比较器输出接到BKIN一旦电流超过阈值立即封波。软件里也要在过流中断里清MOE并置故障标志进入保护状态后需要人工复位才能重新启动避免反复触发把管子折磨坏。还有一个容易被忽略的细节PWM输出引脚到栅极驱动芯片之间串个几十欧电阻除了抑制振铃还能在驱动芯片异常时保护单片机IO。这个电阻不是随便加的它直接影响开关速度要和死区时间一起考虑改了一边另一边也要重新验证。6. 验证测试示波器、逻辑分析仪和带电机前的检查清单6.1 示波器和逻辑分析仪各看什么调SPWM逻辑分析仪和示波器分工不同。逻辑分析仪便宜8通道的几十块6路PWM加上过流信号、母线欠压信号全都能抓到适合看时序关系三相占空比是否按正弦变化、死区是否始终存在、故障封波是否及时。示波器看模拟量测相电压波形要注意探头地线要短最好用隔离通道用电流探头看相电流正弦度看母线电压是否稳定。直接测高压侧的相电压容易引入干扰也危险。看三相互差120°最直观的办法把三相PWM输出各自接一个RC低通滤波器比如10kΩ加100nF截止频率约160Hz然后示波器三通道同时看滤波后的包络——三个正弦波清晰可见彼此错开120°幅值一致。这个土办法没有探头隔离问题调试特别好用。6.2 带电机前的安全检查清单拿我的实际顺序作参考不装MOS管只测控制板和驱动板的逻辑波形确认6路PWM频率、死区、相位关系正确装MOS管不接母线测栅极驱动波形确认死区依然存在串白炽灯或限流电阻接母线给很低的输出频率10Hz和低调制深度观察母线电流确认无异常后直接上母线空载运行听声音、看电流再接电机负载观察启动电流和稳态电流每次改动代码后至少过一遍第3步不要直接全电压跑。这个习惯能省下不少管子钱。6.3 从SPWM到SVPWM、FOC的路还很长这套底层跑通之后往上加的东西很多。最直接的是换SVPWM算法把线电压利用率从86.6%提到接近100%电机在同样母线电压下能跑更高转速、输出更大转矩。再进一步就是电流环和速度环闭环那就需要电流采样——中心对齐PWM在这里的优势很明显计数器到达谷底0时三相下管刚好都导通母线电流等于某一相电流这个时刻采样电流噪声最小正好配合ADC同步触发。这是后面做FOC的基础。配套工程文件我按“正弦表生成脚本、PWMA初始化、三相查表刷新、V/F频率控制”四部分整理好了开发板到手就能直接对照跑。最后说点个人体会SPWM这套东西原理不复杂但每个坑都藏在时序细节里——死区、中心对齐的更新事件、自举电容、栅极电阻随便一个没处理到位波形图上都很难一眼看出来得靠电流、温度、声音这些物理量反馈才能发现。调试时别只盯示波器多用手背感觉一下管子的温度多听听电机的动静它们往往比波形更早告诉你问题出在哪。