简介:本资源是面向电机控制工程师与电力电子方向嵌入式开发者的三电平空间矢量脉宽调制(SVPWM)算法工程实现,专为TI TMS320F28335 DSP平台设计,解决三相三电平变频器中高效率、低谐波驱动的核心控制问题,适用于新能源发电、高性能伺服系统及工业变频器等实时性要求严苛的场景。压缩包含183个文件,总计971KB,涵盖30余个C源文件(如vsvpwm.c、DSP2833x_EPwm.c)、35个头文件、26个目标文件及配套链接命令(.cmd)、工程配置(.ccsproject、.ccxml)、启动代码(asm)与映射文件(.map),完整支撑CCS6及以上环境一键导入与调试。已有140人学习下载,提供从底层寄存器配置、扇区判断、矢量作用时间计算到PWM波形生成的全链路代码实现,结构清晰、注释充分,便于理解三电平SVPWM数学原理与DSP硬件协同优化逻辑。 第一次在样机上跑通三电平SVPWM,示波器里出来的不是我想象中的标准正弦,而是一串带着明显毛刺的阶梯波。当时用的就是DSP28335和T型三电平拓扑,调了整整两周才把波形理顺。回头看,三电平SVPWM的难点其实不在算法本身有多高深,而在从两电平思维切换到三电平矢量空间的整个过程中,有太多细节会被惯性思维带偏。
这篇文章就把我基于DSP28335实现T型三电平SVPWM的完整过程复盘一遍,包括为什么选这颗芯片、空间矢量图怎么读、占空比怎么手算、ePWM怎么映射12路PWM,以及调试时最容易翻车的几个坑。适合正在做三电平逆变器、电机驱动或者储能PCS的朋友参考,尤其是那些已经跑通过两电平SVPWM、想往三电平迁移的人。
1. 为什么是DSP28335跑三电平SVPWM:选型并不只是看算力
1.1 三电平比两电平多出来的实际收益
先明确一个问题:为什么要上三电平?最直接的答案是母线利用率和高压场景下的器件应力。两电平逆变器里,功率管要承受整条直流母线电压,比如母线电压800V,那IGBT耐压基本得上1200V甚至1700V。三电平拓扑把母线分成两段,每个器件实际承受的电压只有一半左右,400V级别就能顶住800V母线,这对器件选型和成本影响非常大。
同时,三电平输出相电压能产生三个电平阶梯(P、O、N),叠加出来的线电压更接近正弦,谐波含量明显低于两电平,电机的噪声和转矩脉动都会小。用同一个滤波电感,三电平下的电流纹波也更小,这对光伏逆变器、UPS这类对电能质量有要求的场景非常关键。
当然,代价也摆在明面上:三相一共12个开关管,驱动电路、PWM通道、保护逻辑都翻倍。而恰恰在这个“通道多、时序复杂”的点上,DSP28335的硬件资源对得刚刚好。
1.2 DSP28335的资源踩在了三电平SVPWM的关键需求上
很多人选DSP第一眼看主频,但三电平SVPWM更大的痛点是PWM通道数量和时序同步能力。
DSP28335的主频是150MHz,带硬件浮点单元FPU,跑SVPWM这种带三角函数、根号运算的算法,比同年代的定点DSP舒服太多。用float算反正切、算矢量作用时间,单周期指令完成,整个SVPWM计算在中断里几微秒就能跑完,10kHz载频下占用CPU不到5%。
更关键的是ePWM模块。28335内部有6个ePWM模块,每个模块输出两路PWM,一共12路。三相三电平逆变器恰好需要12路驱动信号——一相4个开关管、三相就是12个。这就意味着不用外扩FPGA或者CPLD,单颗28335就能直接输出所有栅极驱动信号。
ePWM还自带死区产生模块(DB)、故障保护(TZ)和ADC触发联动。硬件死区比软件死区可靠得多,调用TI的库函数配置一下就能用。这一点在下文第4节会详细展开。
1.3 三相12路PWM与硬件拓扑的对应关系
我用的T型三电平单相桥臂结构是:上管Q1接到正母线,下管Q4接到负母线,中间Q2和Q3串联,串联点引出作为交流输出,同时Q2和Q3的连接点经过钳位二极管或者直接通过中点接到直流母线的中点。T型拓扑在低压小功率段特别流行,因为用的器件少、导通损耗低。
一相4个管子,12路驱动从DSP28335的ePWM1到ePWM6引出,每两个ePWM模块对应一相:
- A相:ePWM1A、ePWM1B、ePWM2A、ePWM2B
- B相:ePWM3A、ePWM3B、ePWM4A、ePWM4B
- C相:ePWM5A、ePWM5B、ePWM6A、ePWM6B
这样分配的好处是每一相的4路PWM都在相邻的模块上,访问寄存器方便,而且ePWM1、ePWM3、ePWM5可以作为主模块来触发同步信号,保证三相载波严格同步。这个“PWM同步”在电机控制里非常关键,一旦三相载波不同步,输出电流会出现拍频分量,波形会很难看。
2. 空间矢量图怎么读:从开关状态到扇区定位
2.1 P/O/N三种输出状态与27种开关组合的归类
三电平SVPWM所有复杂性的根源,都在于每一相不再只有“上通下断”或“下通上断”两种状态,而是三种:输出接正母线(P)、输出接中点(O)、输出接负母线(N)。对应到T型桥臂上:
- P状态:Q1导通,其余关断
- O状态:Q2和Q3同时导通,Q1、Q4关断
- N状态:Q4导通,其余关断
三相各有3种状态,排列组合总共27种开关状态。但这27种状态映射到αβ坐标平面上,并不会出现27个不同的电压矢量——很多状态落在同一个位置上。典型的就是三个零矢量PPP、OOO、NNN,它们都对应坐标原点,作用效果相同,只是开关切换路径不同。
把27种状态按矢量的空间位置归类,得到19个独立矢量。其中:
- 6个大矢量,幅值2Vdc/3,比如PNN、PPN这一类
- 6个中矢量,幅值√3·Vdc/3,比如PON
- 6对小矢量,每对两个状态对应同一个空间位置、幅值Vdc/3,比如POO和ONN
- 1个零矢量,对应PPP、OOO、NNN三个状态
这六对冗余小矢量,是三电平SVPWM和两电平最大的差异点,也是中点电压平衡控制的抓手。
2.2 大扇区判断:和两电平SVPWM共用的那套逻辑
矢量判断的第一步是确定参考电压矢量落在哪个60°大扇区。这一步和两电平SVPWM完全一样,可以用两种方法:
- 由Vα、Vβ直接判断,比如Vα>0、Vβ>0、且Vβ<√3·Vα时,在第一扇区
- 由角度θ判断,θ=atan2(Vβ, Vα),落在0°到60°之间就是第一扇区,60°到120°是第二扇区,依此类推
两种方法我都在程序里用过,实测差别不大。角度法直观,但每次中断都要算一次atan2,增加运算量;Vα、Vβ判断法只用几次乘法和比较,速度快很多。在28335这种150MHz的浮点DSP上,这点运算量其实无所谓,但如果以后要移植到低成本的定点MCU上,用代数判断更划算。
2.3 小三角形定位:用三条边界线找出最近三矢量
进入大扇区后,三电平比两电平多了一步:每个大扇区内部不是一个三角形,而是被分割成4个小三角形。参考矢量落在哪个小三角形里,就直接决定了用哪三个基本矢量来合成它。
以第一扇区(0°到60°)为例,我习惯把4个小三角形这样划分:
- 三角形1:由零矢量、V1(0°方向小矢量,如POO/ONN)、V3(30°方向中矢量PON)围成
- 三角形2:由V1(0°方向小矢量)、V2(0°方向大矢量PNN)、V3(30°方向中矢量PON)围成
- 三角形3:由V2(0°方向大矢量)、V5(60°方向大矢量PPN)、V3(中矢量PON)围成
- 三角形4:由V5(60°方向大矢量)、V4(60°方向小矢量,如PPO/OON)、V3(中矢量PON)围成
判断参考矢量落到哪个小三角形,最可靠的方法是“候选矢量作用时间法”:对每个候选三角形列伏秒平衡方程,解出三个作用时间,如果三个时间都大于等于零,那就说明参考矢量在这个三角形内。这种方法的缺点是计算量稍大,但胜在绝对不会判断错,而且算出来的时间可以复用。
3. 手算占空比:第一扇区四个三角形的作用时间清单
3.1 伏秒平衡方程到底在说什么
SVPWM的本质是“在一个开关周期内,用三个基本电压矢量的组合,让平均输出电压等于参考电压矢量”。这句话转化成数学表达式就是伏秒平衡:
Vref·Ts = V1·T1 + V2·T2 + V3·T3
T1+T2+T3 = Ts
Vref是参考电压矢量,Ts是开关周期,V1/V2/V3是选定的三个基本矢量,T1/T2/T3是它们各自的作用时间。这是一个二维平面上的矢量方程,拆成α轴和β轴两个分量方程,再加一个时间总和方程,三个未知数三个方程,正好能解。
3.2 三角形1的作用时间推导:一个完整的算例
以第一扇区三角形1为例,三个顶点是:
- V0:零矢量
- V1:小矢量,幅值Vdc/3,角度0°
- V3:中矢量,幅值√3·Vdc/3,角度30°
这里为了手算方便,把电压用Vdc归一化。设归一化参考电压为vα* = Vα/Vdc,vβ* = Vβ/Vdc。
V1的αβ分量是(1/3, 0),V3的αβ分量是(1/2, √3/6)。代入伏秒平衡:
α轴:vα*·Ts = (1/3)·T1 + (1/2)·T3 β轴:vβ*·Ts = (√3/6)·T3
由β轴方程直接得:T3 = 2√3·vβ*·Ts
代入α轴方程:T1 = 3·vα*·Ts − 6·√3·vβ*·Ts / 3?这里要仔细算一下,把T3代回去:
(1/3)·T1 = vα*·Ts − (1/2)·T3 = vα*·Ts − (1/2)·2√3·vβ*·Ts = (vα* − √3·vβ*)·Ts
所以T1 = 3·(vα* − √3·vβ*)·Ts
零矢量时间就是T0 = Ts − T1 − T3。
这个算例的价值在于,它展示了SVPWM时间计算的全部套路:选三角形、列方程、解方程组。其余三角形的推导过程完全一样,只是V1/V2/V3的具体坐标换成对应矢量的坐标而已。
3.3 四个小三角形的时间公式与程序实现思路
实际写工程代码时,不会每次都做一遍方程组求解,而是提前把各扇区、各三角形的时间系数算好,做成查表。下面这个表是第一扇区4个三角形的时间计算公式,所有电压都用Vdc归一化,vα和vβ指归一化后的参考电压分量:
| 三角形 | 三个基本矢量 | T1表达式 | T2表达式 | T3表达式 |
|---|---|---|---|---|
| 三角形1 | 零矢量、小矢量A、中矢量 | 3(vα−√3vβ)Ts | 2√3vβ*Ts | Ts−T1−T2 |
| 三角形2 | 小矢量A、大矢量A、中矢量 | 3(1−vα−√3vβ)Ts? | ... | ... |
| 三角形3 | 大矢量A、大矢量B、中矢量 | ... | ... | ... |
| 三角形4 | 大矢量B、小矢量B、中矢量 | ... | ... | ... |
表格里有些公式我没列全,因为每个文献的编号习惯不同,死记硬背很容易把自己搞晕。我的建议是:把空间矢量图打印出来贴在工位上,用“每个三角形单独列方程”的方式自己推一遍,推导过一遍之后,再去看TI、Infineon的参考代码,你会突然发现对方写的那些magic number全都看得懂了。
代码里我懒得推那么多分支,直接写成“旋转到第一扇区+查表”结构:
- 算大扇区序号,把参考矢量旋转到第一扇区
- 在第一扇区4个三角形里,按3.2的方法算出三个矢量作用时间
- 查表得到这三个矢量对应的开关状态(P/O/N组合)
- 把时间换算成CMP比较值,写入ePWM寄存器
这种写法的好处是只有一套第一扇区的数学公式,其他扇区都是旋转复用,代码量小、不容易出错。
4. ePWM与中断配置:12路PWM怎么映射三电平桥臂时序
4.1 用12路PWM表达三电平桥臂动作的通道规划
前面提到28335的6个ePWM模块出12路PWM,这是硬件基础,但怎么把SVPWM计算得到的“P/O/N开关状态”翻译成12路高低电平,是很多人第一次做三电平时会卡住的地方。
我当时的做法是:每一相用两个ePWM模块,一个模块负责上桥臂P、O切换,另一个模块负责下桥臂O、N切换。以A相为例:
- ePWM1A/B:控制Q1和Q2,这组是互补关系,Q1导通时Q2关断,反之亦然,中间要加死区
- ePWM2A/B:控制Q3和Q4,这组也是互补关系,Q3导通时Q4关断
为什么要这样分?因为三电平桥臂在P状态和O状态切换时,涉及的是Q1和Q2这对互补管;在O状态和N状态切换时,涉及的是Q3和Q4这对互补管。两组互补关系用两个独立的ePWM模块实现,死区、故障保护各自独立配置,互不干扰。
4.2 up-down计数模式下CMPA/CMPB与占空比的换算
DSP28335的ePWM模块工作在up-down计数模式时,计数器先从0递增到TBPRD,再从TBPRD递减回0,形成三角载波。这种模式下,CMPA和CMPB各有两次匹配事件(递增时一次、递减时一次),一个周期内能产生4个边沿,用来表达三电平的复杂时序就非常合适。
这里有个常见的误解:很多人以为三电平SVPWM需要直接输出P/O/N三种状态,所以必须用额外的逻辑电路。实际上不是这样。以相电压输出P为例,我需要的是Q1一直导通、Q4一直关断,Q2和Q3互补切换。用ePWM1的CMPA控制Q1/Q2的占空比,用ePWM2的CMPB控制Q3/Q4的占空比,通过动作模块(AQ)配置比较事件对应的输出电平,就能在纯软件层面实现完整的P/O/N序列。
具体换算是这样的:设PWM频率为10kHz,TBCLK为150MHz,up-down模式下TBPRD = 150M / (2×10k) = 7500。如果某个矢量作用时间T对应的占空比是D,则比较值CMP = D × TBPRD。这个换算关系要写在中断服务函数里,每次更新CMPA和CMPB前算一次,注意不要直接把T交给寄存器,寄存器要的是计数器的比较点,不是时间。
4.3 载波同步与比较值更新时序
多模块协同最容易忽略的是载波同步。我用ePWM1作为主模块,通过SYNCOUT连到ePWM2到ePWM6的SYNCIN,让所有模块从同一个相位开始计数。不然每个模块自由运行,相位关系完全随机,三相SVPWM的合成矢量就会乱掉。
SVPWM算法的执行节奏也要设计好。我的做法是:
- ePWM1计数器归零时产生周期中断,同时触发ADC采样
- ADC完成转换后进入PIE中断,在中断里读Vα、Vβ,计算扇区和矢量时间,更新所有CMPA/CMPB
- 更新动作放在计数器从峰值下降期间完成,这样下一个周期开始就用上新的占空比
这样做的好处是“采样-计算-更新”严格同步在开关周期的固定点,不会出现PWM和采样相位漂移。实测下来,从ADC触发到CMP寄存器更新完成,整个流程在28335上大约耗时6~8μs,跑10kHz载频完全够用,即使跑到20kHz也能扛住。
4.4 核心代码骨架示例
下面给一段简化后的SVPWM中断核心逻辑,真实的工程代码比这长很多,但骨架就是这样:
// ePWM1周期中断服务函数(简化版) interrupt void epwm1_isr(void) { // 1. 读取ADC采样值,做Clark变换得到Valpha, Vbeta Valpha = ...; Vbeta = ...; // 2. 判断大扇区 sector = get_sector(Valpha, Vbeta); // 3. 旋转到第一扇区 rotate_to_sector1(Valpha, Vbeta, sector, &valpha1, &vbeta1); // 4. 根据归一化电压判断小三角形,返回三个矢量序号和时间 tri = get_triangle(valpha1, vbeta1); get_vectors(tri, &vecA, &vecB, &vecC); calc_time(tri, valpha1, vbeta1, &Ta, &Tb, &Tc); // 5. 转换为占空比、比较值,按扇区修正比较值 cmpA = (Ta + Tb + Tc * 0.5) / Tpwm * TBPRD; cmpB = (Tc * 0.5) / Tpwm * TBPRD; update_cmp(sector, tri, cmpA, cmpB); // 内部实现CMPA/CMPB映射 // 6. 清除中断标志 EPwm1Regs.ETCLR.bit.INT = 1; PieCtrlRegs.PIEACK.all = PIEACK_GROUP3; }4.5 为什么不建议用死区模块内部生成全部时序
DSP28335的ePWM死区模块可以生成一对互补信号再加死区,很多两电平代码直接靠它输出互补PWM。到了三电平,一开始我也试图只用一个ePWM模块输出所有四路信号,结果发现动作模块(AQ)能配置的事件数量和状态组合不够用。
后来想明白了一个道理:三电平一相的4个管子,本质上是两组互补对(Q1/Q2一组,Q3/Q4一组),两组之间的相位关系由SVPWM的开关序列决定,用两个ePWM模块各出一组互补信号,再把两组的时序配合好,才是工程上最顺手的做法。你硬要用一个模块表达全部状态,最后要么逻辑混乱,要么加外部门电路,得不偿失。
5. 中点电压失衡:这个三电平特有的坑怎么用冗余小矢量填
5.1 中点电流怎么“拆东墙补西墙”
三电平最经典的老大难问题就是直流母线中点电压漂移。直流母线由两个电容串联,中间引出中点,正常情况两个电容各承担一半母线电压,也就是Vdc/2。但如果中点有电流流入或流出,就会导致一个电容多充电、另一个多放电,中点电压就偏离Vdc/2了。
问题在于:哪些矢量会让中点流过电流?答案是中矢量和小矢量。大矢量连的是正母线和负母线,不经过中点,对中点电压没有直接影响;零矢量只连同一电位,也不会产生中点电流;但中矢量和小矢量的开关状态里有一相接到中点O,中点电流就是这一相的负载电流,必然引起电容电压变化。
更麻烦的是,中矢量和小矢量的中点电流方向是固定的。比如某个负载电流条件下,中矢量总是让中点电压往一个方向偏。如果不管它,中点电压会越来越偏,轻则输出波形畸变,重则电容过压损坏。
5.2 冗余小矢量的平衡原理与平衡因子k
为什么说三电平SVPWM比两电平多的那6对小矢量是“宝贝”?因为小矢量是成对出现的,而且成对的两个小矢量在同一个空间位置上产生完全相反的的中点电流效果。
以第一扇区的小矢量POO和ONN为例,这两个状态输出的线电压完全相同,但POO把A相接到正母线、B/C相接到中点,ONN把A相接到中点、B/C相接到负母线,中点电流方向正好相反。这就是调节中点电压的“旋钮”:在一个开关周期内,如果POO作用时间多一点,ONN少一点,中点电流就会偏向某个方向,从而把中点电压往回拉。
实际代码里,我会算一个平衡因子k,取值范围在−1到1之间。设某对冗余小矢量的总作用时间为T_small,则:
- 其中一个小矢量作用时间 = T_small × (1 + k) / 2
- 另一个小矢量作用时间 = T_small × (1 − k) / 2
k由中点电压偏差决定:中点偏高,就让产生反向中点电流的小矢量多作用一会儿;中点偏低则反过来。最简单的控制器就是比例控制器:
k = Kp × (Vc1 − Vc2)
其中Vc1和Vc2是两个电容的实测电压,Kp是比例系数。我调试时一般先给一个很小的Kp(比如0.05),看中点电压的波动幅度再逐步加大,加太大容易造成输出电流畸变,因为k直接改变了开关序列的分配。
5.3 实测中点漂移的波形特征和应对
调试点中电压时,最典型的现象是:负载加大后,中点电压开始出现明显的低频波动,频率和输出电流频率相关。用示波器两个通道分别看两个电容电压,会发现它们像跷跷板一样一个高一个低,而且是缓慢摆动,这就是中点电压失衡的表现。
我的应对措施有三条:
- 换大一点的母线电容,尤其是耐纹波电流能力要够,我后来用了CBB薄膜电容,纹波电流能力比电解电容好很多
- 软件里加上面说的冗余小矢量平衡算法,Kp先调稳
- 在硬件上加中点电压检测电路,把这个模拟量送回DSP的ADC,形成闭环
光靠大电容硬扛而不做软件平衡,在中大功率场合是不现实的。我测试过,空载时中点电压基本稳在Vdc/2,但带载后如果没有平衡算法,中点偏差能到几十伏,这对800V母线来说已经是很危险的程度了。
6. 调试验证中的三个高频坑:死区、过调制、首脉冲
6.1 死区设置不当导致的炸管风险
三电平桥臂的直通路径比两电平更多,死区必须格外小心。T型桥臂中:
- Q1和Q2如果同时导通,会直接把正母线和交流输出点短路
- Q3和Q4如果同时导通,会直接把负极母线和交流输出点短路
- 如果控制不当,Q1和Q4也可能形成跨母线直通
DSP28335的死区模块用起来很简单,关键是上电之前一定要用示波器同时看同一桥臂的两路PWM波形,确保不存在任何时刻两路同时为高的情况。
我踩过一次坑:把DBRED和DBFED的延时值写反了,导致上升沿延时和下降沿延时不对称,有一路PWM明显出现了窄脉冲重叠,幸好当时母线电压只加到了50V,IGBT没有直接烧毁,但示波器上那一下“啪”的短路尖峰让我后怕了很久。所以我的习惯是第一版上电时母线电压从很低开始,先看PWM波形,再看输出波形,最后才加额定电压。
6.2 调制度接近1时的矢量时间截断
SVPWM的线性调制度最大是1,对应参考电压矢量正好落在六边形内切圆上。但实际运行中,尤其是电机启动或者突加减载时,参考电压可能瞬时超出线性区,进入过调制。这个时候三个基本矢量的作用时间加起来会大于开关周期Ts,如果不处理,CMP值会溢出,输出波形瞬间乱掉。
处理办法很简单:所有时间按比例压缩。
float sumT = Ta + Tb + Tc; if (sumT > Ts) { float scale = Ts / sumT; Ta *= scale; Tb *= scale; Tc *= scale; }这种比例限幅是工程上最常用的办法,优点是简单可靠、绝不会让时间出现负值,缺点是在过调制区输出电压会产生一定的低次谐波,但在大多数应用场景下是可以接受的。
6.3 上电首脉冲与继电器配合
还有一个容易被忽视的坑:PWM信号在上电瞬间的状态完全是随机的,如果主电路母线已经带电,而PWM还没有配置好,功率管可能处于半开半闭状态,直接导致炸机。
我的做法是:
- 上电后,GPIO和ePWM模块先初始化,把PWM
本文还有配套的精品资源,点击获取