☰
AI8051U用PWMA实现三相SPWM变频驱动:原理、死区与代码详解
2026/10/3 4:16:12 网站建设 项目流程

最近做电机变频项目,被朋友问AI8051U能不能出个三相SPWM例程。我一开始也犹豫,因为很多人觉得51内核跑SPWM太吃力,实际上AI8051U这颗芯片的PWMA外设相当能打:16位计数器、三对互补PWM输出、硬件死区插入、刹车保护寄存器一应俱全,拿来做三相SPWM变频驱动绰绰有余。这篇文章就记录我自己基于AI8051U的PWMA实现三相互补SPWM的过程,包括外设初始化、正弦表查表策略、载波频率和死区时间怎么定,以及调试中踩过的坑。适合准备用AI8051U做电机驱动、逆变器、变频电源,或者单纯想把PWMA玩明白的朋友参考,我会尽量把关键思路讲透,方便你直接移植到自己的板子上。

1. 项目背景与整体设计思路

1.1 为什么选择AI8051U跑三相SPWM

AI8051U是8051内核,但主频和片上外设已经完全不是传统89C52那个时代的水平。它工作在1T模式,主频最高可以跑到几十MHz,带USB下载、硬件乘除法、甚至TFPU浮点加速单元。最关键的还是PWM外设:和STC8H/STC32G系列的PWMA一脉相承,支持多路互补PWM、独立死区控制、刹车输入保护、比较输出、中心对齐模式,这些功能对三相逆变器来说是刚需。

跑SPWM的核心任务是:在每一个PWM周期里刷新三路比较值,让三个通道的占空比分别跟随正弦规律变化,同时彼此之间相差120度。对于8051内核来说,这要求中断响应够快、PWM外设支持高频率重载,否则占空比刷新不及时,波形就会失真。AI8051U的PWMA配合1T流水线,实测在中等载波频率下刷新三路CCR完全没有压力,这是它适合做这个例程的根本原因。

很多朋友可能觉得“三相SPWM不是STM32的活吗”?确实STM32生态成熟,但AI8051U的优势在于成本低、Keil环境熟悉、STC-ISP下载方便。对于三相风扇、水泵、小功率逆变器这类产品,一颗AI8051U就能搞定控制部分,不需要上ARM。所以我们下面所有设计,都以AI8051U的PWMA外设为主线展开。

1.2 SPWM变频驱动到底在干什么

SPWM,叫正弦脉宽调制,本质是“用一系列宽度按正弦规律变化的PWM脉冲,去等效一个正弦波”。变频驱动就是通过改变PWM输出正弦波的频率和幅值,来控制交流电机的转速和转矩。三相电机需要三相对称正弦电流,所以我们需要生成三路频率相同、幅值相同、但相位互差120度的正弦调制波。

放在PWM域里看,每一路正弦调制波就是一路占空比按正弦变化的PWM。母线直流电压经过三相桥逆变之后,在电机三相绕组上得到近似正弦的相电压,从而产生旋转磁场。基波频率决定电机同步转速,电压幅值按比例变化是为了维持磁通恒定,这就是V/F控制思想。我们的例程先做开环SPWM,不搞FOC,重点把三路互补信号的产生和变频逻辑跑通。

1.3 为什么非要互补输出:死区与防直通设计

三相逆变器的每一相都由上下两个功率管组成半桥,上管和下管的驱动信号必须互补。以U相为例,PWMA_CH1P驱动上管,PWMA_CH1N驱动下管,两者逻辑上不能同时导通,否则直通短路,瞬间烧管子。

但功率管存在开关时间,光靠软件保证“逻辑上互补”并不够。比如上管还没完全关断、下管就已经开通,哪怕只有几百纳秒的重叠,母线电压也可能直接穿过桥臂。这时候就需要硬件死区插入:在上下管切换之间强制插入一段“两个管子都关断”的盲区时间,确保安全。PWMA外设内部就有死区发生器,只要配置好DTG寄存器,它会在互补信号切换时自动插入死区,不需要软件在每个边沿去处理,这就避免了51单片机软件模拟PWM最容易出现的事故。

2. SPWM生成的核心细节

2.1 正弦表与相位累加器:三相互差120度的两种实现

生成SPWM的占空比数据,最常用的做法是查表法。把正弦函数预先离散成一张表,运行时按索引取数值,换算成PWM比较值CCR。查表法比在中断里实时算sin快得多,是51内核做SPWM的基本盘。

相位差120度有两种实现方式。第一种最简单粗暴:准备三张正弦表,分别偏移1/3周期和2/3周期来存放数据,运行时就各自查各自的表。这种方式思路直白,但浪费存储空间,而且调制度、频率变化时三张表都要同步处理。

第二种是单张正弦表配合相位累加器。我实际用的是这种方法:在中断里维护一个16位累加器变量,每次中断累加一个步进值phase_step,再分别加上120度和240度对应的偏移量,得到三相的相位索引。因为360度对应65536,120度正好是65536/3约等于21845,240度是43690。这么做的好处是内存省、逻辑统一、频率可以连续调节,只需要中断里改动phase_step这个累加步进即可。

用代码表达就是:

u16 acc_U += phase_step; u16 acc_V = acc_U + 21845; u16 acc_W = acc_U + 43690; u8 idx_U = acc_U >> 8; u8 idx_V = acc_V >> 8; u8 idx_W = acc_W >> 8;

这里的右移8位相当于对一个256点的正弦表取相位索引,表正好一个周期对应256个点。而21845和43690这两个固定偏移,正好让三相之间保持120度电气角,这是整套代码里最核心的思路。

2.2 载波频率、表点数和输出频率的换算

很多初学者在这里卡壳,我多说几句。PWM本身有个开关频率,叫载波频率,比如16kHz。正弦波有个目标频率,比如电机要转50Hz。每个PWM周期我们从正弦表里取一个点去刷新占空比,那么一秒内取点的次数是载波频率,一个正弦周期分到的点数就是载波频率除以正弦频率。

如果固定正弦表点数N,比如256点,那么输出正弦波最高能达到的频率就是载波频率除以N。16kHz载波下,256点表最高只能出62.5Hz的正弦波。如果你的目标频率范围要到400Hz,要么降低查表分辨率,要么用相位累加器方式,让索引不严格按1递增,而是按phase_step的小数步进。

相位累加器方式不受“每周期必须凑整数个点”的限制,因为索引是从16位累加器右移来的,即使每次累加0.6个点,长期来看相位也是连续的。输出频率和步进的换算公式是:

phase_step = (f_target * 65536) / f_pwm;

比如目标50Hz、载波16kHz,那么phase_step = 50 * 65536 / 16000 = 204。这个数值不要求整除,频率分辨率完全够用。

2.3 调制度与86.6%的电压利用率问题

正弦表的输出范围决定了调制度M,也就是SPWM相电压峰值与母线电压一半的比值。调制度M等于1时,相电压基波峰值最大为Ud/2,其中Ud是直流母线电压。三相线电压基波峰值是相电压的根号3倍,所以最大线电压峰值是Ud/2乘以根号3,约等于0.866倍Ud,这就是大家热议的“SPWM线电压利用率最高只有86.6%”。

这个86.6%不是缺陷,而是正弦调制本身的数学特点。六步方波逆变能到100%,但谐波很大;SVPWM能接近100%同时谐波更小。在我们这个例程里,调制度M就是正弦表幅值相对半占空比的比例,M=1对应满调制。实际电机驱动时,一般会留点余量,设M在0.85到0.95之间,避免过调制导致波形畸变、电流谐波增大。

调制度在代码里体现为对正弦表数值的缩放。我建议把正弦表存成原始128幅值(0到255,128是中点),运行时通过amp变量缩放:

s16 delta = (s16)(sin_tab[idx] - 128); u16 ccr = mid_value + ((delta * amp) >> 7);

amp默认128就对应M=1,amp减半就是M=0.5。这样调速和调电压可以分开,做V/F控制时改amp就行。

2.4 死区时间怎么定:计算公式与工程整定

死区时间太小,防不了直通;死区时间太大,输出波形畸变增加、电流谐波变大、还会让有效电压降低。工程上先看功率管的开关参数:比如你用的MOS管开通延迟30ns、关断延迟100ns,那死区至少大于两者之差再留50%安全余量,常见家用电机驱动用1us到3us比较多。

PWMA的死区时间是通过BKR寄存器里的DTG位配置的,具体公式在STC8H/STC32G系列手册里写得很清楚:DTG的取值不同段位,对应不同的死区倍率。比如不预分频的情况下,死区时间近似等于DTG数值乘以PWM时钟周期。假设PWM时钟是48MHz,周期约20.8ns,想要1us死区,DTG大约取48。如果死区还嫌不够,可以用DTG高位选择更大的倍率档位,拉长到几微秒。

我实际调试时习惯先设大一点,比如2us,用示波器看上下管的栅极波形有没有同时导通区域,确认安全后再逐步减小,直到输出电流波形和谐波都能接受。这个“从大到小逼近”的方法,比一上来就按理论值计算要稳妥得多,因为你还要考虑驱动芯片的传输延迟和PCB走线带来的误差。

3. PWMA外设初始化与代码实现

3.1 引脚分配与最小硬件系统

AI8051U的PWMA输出脚一般是P2组以及P1组,具体映射要看封装和引脚切换寄存器。以典型方案来规划:PWMA_CH1P/CH1N驱动U相上下桥,PWMA_CH2P/CH2N驱动V相,PWMA_CH3P/CH3N驱动W相。三路互补PWM经过驱动芯片IR2101或EG3002,再接到三相MOSFET桥臂。

驱动芯片选型方面,IR2101这种自举半桥驱动芯片很常见,只需要一路供电,用PWM互补信号直接驱动。需要注意的是,6路PWM信号进入驱动芯片之前,最好串100欧姆到220欧姆的栅极电阻,抑制振铃。母线电容要在功率板就近放置,否则开关瞬间的电压尖峰很容易击穿MOS管。

我测试时用的是一块24V直流供电的三相无刷驱动板,MOS管用低电压大电流的N-MOS,驱动芯片用三片IR2101。AI8051U核心板和功率板分开,信号线尽量短,共地必须可靠,不然PWM高电平噪声会影响内部ADC和复位。

3.2 PWMA寄存器配置流程

寄存器配置是整个例程的地基。下面以STC8H/STC32G系列兼容的寄存器命名方式来写,AI8051U的寄存器基本沿用这套体系,具体名称和中断号以官方数据手册和头文件为准。

第一步配置时钟和预分频。假设系统时钟48MHz,边沿对齐模式,想要16kHz载波,那么ARR加1乘上PSC加1应等于3000。比如PSCR=0,ARR=2999。如果PWM时钟太高导致ARR太大不好处理,也可以加大预分频。代码如下:

PWMA_PSCRH = 0x00; PWMA_PSCRL = 0x00; // 预分频为0,PWM时钟=系统时钟 PWMA_ARRH = 0x0B; PWMA_ARRL = 0xB7; // ARR=2999,载波=48M/(2999+1)=16kHz PWMA_CR1 = 0x01; // 向上计数模式,计数器使能

第二步配置三个通道为PWM模式1。比较寄存器CCR的计数值小于ARR时,输出有效电平。具体要设置CCMR1/2/3里的OCxM位,同时使能CCER1里的三路输出以及三路互补输出:

PWMA_CCMR1 = 0x68; // CH1为PWM模式1,具体值参考手册 PWMA_CCMR2 = 0x68; // CH2为PWM模式1 PWMA_CCMR3 = 0x68; // CH3为PWM模式1 PWMA_CCER1 = 0x3F; // 使能CH1/CH2/CH3及其互补输出

第三步配置死区和主输出使能。BKR寄存器里同时有MOE主输出使能和DTG死区位。我设定死区对应约1us,把DTG设为48附近,具体数值要按自己的PWM时钟验算:

PWMA_BKR = 0xB0; // MOE=1使能主输出,DTG配置死区,具体位段见手册 PWMA_CCR1H = 0x05; PWMA_CCR1L = 0xDC; // 初始CCR1设一个中点附近值 PWMA_CCR2H = 0x05; PWMA_CCR2L = 0xDC; PWMA_CCR3H = 0x05; PWMA_CCR3L = 0xDC;

这样一个基础的三相互补PWM就起来了。此时用示波器看CH1P和CH1N,应该能看到一对互补且带死区的方波,把另外两个通道也打开,能看到三路错开120度的PWM。如果到这里正常,就可以进入SPWM刷新阶段。

3.3 SPWM更新中断与三相信号生成代码

在PWM更新中断里刷新三路CCR,是SPWM的主战场。采用边沿对齐模式时,计数器上溢产生更新中断,中断频率等于载波频率16kHz,这个频率下8051内核处理几次查表和乘法运算没有问题。如果使用中心对齐模式,注意更新事件是PWM频率的两倍,中断负荷会翻倍,后续调谐时要注意。

中断里的完整流程是:更新三个相位累加器,取三个索引,查正弦表,按当前调制度换算成CCR值,写入三个比较寄存器。参考代码:

volatile u16 phase_acc; volatile u16 phase_step; volatile u8 amp; // 调制度,128为满调制 void PWMA_ISR(void) interrupt 13 // 中断号以实际头文件为准 { u16 accU = phase_acc; u16 accV = accU + 21845; u16 accW = accU + 43690; u8 idxU = (u8)(accU >> 8); u8 idxV = (u8)(accV >> 8); u8 idxW = (u8)(accW >> 8); u16 mid = 1500; // ARR=2999时,中点比较值 s16 d; d = (s16)(sin_tab[idxU] - 128); PWMA_CCR1H = (u8)((mid + ((d * (s16)amp) >> 7)) >> 8); PWMA_CCR1L = (u8)(mid + ((d * (s16)amp) >> 7)); d = (s16)(sin_tab[idxV] - 128); PWMA_CCR2H = (u8)((mid + ((d * (s16)amp) >> 7)) >> 8); PWMA_CCR2L = (u8)(mid + ((d * (s16)amp) >> 7)); d = (s16)(sin_tab[idxW] - 128); PWMA_CCR3H = (u8)((mid + ((d * (s16)amp) >> 7)) >> 8); PWMA_CCR3L = (u8)(mid + ((d * (s16)amp) >> 7)); phase_acc = accU + phase_step; }

正弦表可以提前用PC端生成,256点,第i点值为128 + 127 * sin(2 * π * i / 256),存成const unsigned char数组。这里我把相位累加和索引计算放在中断里,而phase_step和amp由主循环按目标频率更新,实现变速变调。这样做的好处是中断只做机械的查表更新,逻辑简单,不容易出错。

写CCR的时候要先写高字节再写低字节,避免更新过程中计数器匹配到中间值产生毛刺。这个顺序在STC的高位字节预装载机制下尤其重要,我刚开始没注意,偶尔会出现一两个异常窄脉冲,排查了很久才发现是写入顺序问题。

3.4 V/F曲线与基础调速逻辑

三相异步电机的特点是磁场由电压和频率共同决定。如果只变频不变压,频率拉高后磁通会下降,转矩不够;频率降低时电压太高又会饱和,电流飙升。所以开环SPWM驱动异步电机,必须做V/F控制:输出电压幅值随目标频率近似线性变化,低频时再适当补偿死区和线路压降。

放在例程里,主循环可以根据目标频率f_target计算phase_step,同时计算对应的amp。一个简单的V/F系数:比如额定50Hz对应满调制度amp=128,15Hz对应amp约38,那么可以写成:

f_target = 30; // 目标频率,单位Hz phase_step = (u16)((u32)f_target * 65536 / 16000); amp = (u8)((u32)f_target * 128 / 50); if (amp < 20) amp = 20; // 最低励磁电压补偿

低频补偿可以给一个最低占空比底值,防止电机低速时电压太小无法建立磁场。实际调试时可以先用示波器看三相输出线电压波形,再配合电流钳表观察相电流是否接近正弦。如果电流波形有明显尖峰,多半是死区补偿不足或调制度配得不合适。

4. 常见问题与排查技巧实录

4.1 波形异常:相位差错误和极性反了怎么查

我遇到的第一个问题是三路PWM相位完全一致,示波器上U、V、W三通道重叠成一条线。检查思路从最简单的开始:先看相位累加的偏移量是否真的写进了中断代码。我在代码里设置了21845和43690偏移,但编译优化和头文件里u16变量定义不当,导致加偏移后溢出结果异常,三相索引差为0,自然就没有相位差。

第二个常见问题是某一路的P和N信号反了。PWMA的CCER寄存器里有CCxNP位,可以控制互补输出的有效极性。如果你发现上管和下管的死区是“上管导通结束后立即下管开通”,按逻辑没错,但实际驱动波形看起来像非互补,那就要查CCxNP设置。最直观的办法是把CH1P和CH1N同时接到示波器两通道开余辉显示,正常的互补波形应该像两把梳子互相咬合,中间有清晰死区空隙。

有时候还会出现上面相正常、下面相滞后半拍的诡异波形。这种多半不是PWM外设问题,而是驱动芯片某一相的上管供电没接好,自举电容失效导致上管驱动波形不对。我在DEBUG板上遇到过一次,排查半天,最后发现是IR2101的自举电容虚焊。建议检查PWM信号之前,先确认驱动芯片供电和自举电路都正常。

4.2 管子发烫甚至烧毁:死区与驱动电路的坑

功率管发烫,第一个要怀疑的就是死区时间不够。在小电流下可能看不到明显问题,一旦负载加大,上下管微直通导致瞬间大电流,管子内部结温迅速上升。我用示波器量过不下五次死区,发现理论计算值1us在某些栅极电阻和MOS管组合下仍然偏小,尤其是栅极电容大、关闭时间长的管子。

判断死区是否足够的经验方法:让母线电压升高到正常工作电压,同时用电流探头观察母线电流。如果每个PWM周期都出现一个窄而高的电流尖峰,并且尖峰位置正好在开关切换点附近,那大概率是死区不足。把死区时间往大调一档,比如从1us调到2us,再观察尖峰是否明显减小。同时注意驱动电阻也会影响开关速度,栅极电阻从10欧改成100欧,开关变慢,所需死区时间也要跟着加大。

另一个让MOS管发烫的原因是互补极性配置反了,导致上下管逻辑错位,实际的“死区”出现在错误的边沿上。这个问题比死区不足更隐蔽,因为波形看起来也有空隙,但两个管子在某个切换瞬间仍然会直通。此时用示波器直接测上管源极到下管漏极之间的压降,或者用差分探头量半桥中点波形,会看到异常的下冲。检查顺序依然是先恢复CCR为固定值输出,再用示波器确认每一相P/N电平极性。

4.3 电机嗡嗡叫却不转:启动参数设置不合理

电机通电之后只嗡嗡响但不转,在开环SPWM驱动异步电机时很常见。原因主要有三类:输出基波频率太低导致启动转矩不足,调制度太低导致电机端电压太低,启动瞬间频率和电压不匹配导致磁场建立失败。

我的处理方法是设置一个最低启动频率,比如5Hz,启动时直接以5Hz对应的电压和步进输出,而不是从0Hz慢慢爬。主循环里判断当前频率小于5Hz就强制为5Hz,等电机建立可靠转速后再逐步升频。另外启动阶段适当增大最低励磁电压,就是前面提到的amp下限,帮助电机更可靠地建立磁场。

如果是相序错误,电机也会振动但不转,或者转向不对。调换任意两相PWM输出即可改变转向。比如把CCR1和CCR2对应的两相交换,电机就会反转。注意不要在带载运行时直接换相,最好停机后再改。

4.4 程序跑飞与中断卡死:优先级和重入问题

SPWM中断频率设置在16kHz时,AI8051U的CPU负载通常能承受,但如果主循环里有大段其它任务,或者中断处理代码写得太长,容易触发中断重入问题。C51编译器默认的interrupt函数不会自动重入,一旦主循环或更高优先级中断里调用了与中断相同的代码段,就可能造成变量冲突。

我建议把相位更新需要的临时变量全部放在中断函数内部定义,不要和主循环共用局部变量。相位累加器、全局的标志位都要加volatile修饰,防止编译器优化导致读取不到最新值。中断里避免调用自定义延时不释放CPU的函数,也不要调用printf之类重量级库函数,否则载波一高就崩。

如果出现PWM停止输出现象,先检查是否进了看门狗复位。开看门狗后,主循环必须及时喂狗,而喂狗操作不能放在中断里,否则主循环死循环时看门狗形同虚设。我调试时经常在修改主循环逻辑后忘记喂狗,导致偶发复位,PWM重新初始化后波形看起来“抖动”甚至没有输出。排除方法是在主循环最开始关狗,确认系统稳定再开狗。

4.5 实测排查问题速查表

我把自己调试过程中遇到的问题整理成一张表,方便对照排查。

现象可能原因排查方法
三相PWM波形完全重叠相位累加偏移丢失检查21845/43690是否真正加到累加器,直接打印acc值对比
上下管直通烧管死区时间太小或极性反先调大DTG到2us以上,再核对CCxNP极性
电机不转但有嗡嗡声启动频率太低或电压不足设置最低启动频率5Hz,调低V/F曲线起点
输出基频不准phase_step计算错误用示波器测U相PWM周期,反向计算phase_step核对
PWM中断卡死中断重入或死循环临时关中断跑主循环定位,检查是否有库函数调用
载波频率似有拍频表点和频率不同步用相位累加器后不会有此问题,确认step是小数累加
某一路上下桥臂波形缺失驱动芯片自举供电不良检查自举电容和上管电源,逐路独立测试

另外还有一个容易忽略的坑:AI8051U的引脚切换寄存器。默认引脚和PWM外设复用位置不一定是对应的,必须阅读芯片手册的引脚说明,正确配置PWMA对应的引脚切换位。我第一次画板子时默认以为PWM在P2口,结果发现实际上需要额外使能,浪费了整整一个晚上。这个点新手特别容易踩,强烈建议画板前就核对官方封装引脚表。

最后分享一个小经验

三相SPWM看起来代码不多,真正难的是把相位关系和死区逻辑想清楚。我最开始用固定三张正弦表,代码笨重还容易错,后来改成单表+相位累加器之后,整个项目清爽了一大截,调频率调电压都变成了简单的数值更新。如果你只是想快速验证PWMA输出,可以先不接功率管,直接让PWM输出通过电阻分压后用示波器看波形;等波形对了再接驱动板和电机,这样能少烧很多管子。AI8051U这个例程后面还可以扩展成SVPWM、闭环速度控制,甚至配合TFPU做实时运算,先把开环SPWM跑稳,再慢慢往深处走。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询