ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32实现单极性SPWM逆变器:原理、代码与实测对比

2026/9/28 1:32:28 拓冰建站 浏览量
STM32实现单极性SPWM逆变器:原理、代码与实测对比 我最早捣鼓逆变器那会儿干过一件蠢事直接用方波驱动一个工频变压器再接上台灯和一个小电机结果灯泡倒是亮了电机转得嗡嗡响整个板子的发热量也大得离谱。后来把驱动方式换成SPWM也就是正弦脉宽调制输出波形才真正走出正弦的样子。更花时间的其实是从双极性SPWM切到单极性SPWM的过程因为桥臂输出电压的形态一变后面的滤波参数、死区补偿、开关损耗全都跟着变。这篇文章我把整套过程重新捋一遍为什么方波不行、单极性SPWM的原理怎么理解、在STM32上如何一步步生成单极性SPWM、以及和双极性SPWM逐项实测对比最后再聊几个我调板时实际遇到的坑。1. 为什么方波驱动先被淘汰SPWM的动机1.1 方波逆变器的谐波账单方波逆变电路很简单单相全桥四个管子轮流开输出就是一个正负交替的方波负载两端看到的电压要么是正的母线电压要么是负的母线电压再用变压器升压就能得到220V左右的方波交流。空载测试时方波和正弦波都能让万用表显示差不多的有效值可一旦接上感性负载问题全出来了。方波里含有大量奇次谐波3次、5次、7次谐波的幅度都不小。普通阻性负载感受不明显但电机、变压器这类感性负载对谐波特别敏感谐波会在铁心里产生额外涡流损耗和磁滞损耗结果就是铁芯发热、噪声大、效率低。更麻烦的是很多设备内部有整流电路方波供电时整流滤波后的纹波也会明显恶化。简单说方波逆变器能“能出电”但出来的电质量很差。1.2 等面积法则SPWM想解决什么问题SPWM的核心逻辑可以用一句工程行话概括冲量相等而形状不同的窄脉冲加在惯性环节上时其效果基本相同。落在逆变器里就是让一串宽度按正弦规律变化的脉冲在经过LC滤波后能还原出干净的50Hz正弦电压。实现起来并不玄。先把一个正弦波周期分成很多等宽的小段每一段里用一个占空比可变的矩形波去替代这一小段正弦曲线。矩形波的脉冲宽度与该段正弦曲线的平均值成正比。只要分得足够细滤波后的输出就越是接近正弦。脉冲数越多可消除的谐波频率越高电感电容滤波就越轻松但开关管的损耗也会同步上升开关频率的选择本质上是在波形质量和损耗之间做平衡。到了这一步SPWM的框架就清楚了高频PWM负责“裁”正弦输出滤波负责“抹平”棱角。剩下的问题是用什么规则去决定每个脉冲的宽度以及桥臂的开关状态怎么配合。2. 单极性SPWM的调制原理与86.6%电压利用率推导2.1 单极性的“单”到底指什么单极性这个词指的是桥臂输出电压在半个工频周期内只有一种极性。以单相全桥逆变器为例负载接在H桥两个桥臂中点A和B之间Uab就是负载电压。双极性SPWM的工作方式是四个管子全部高频开关Q1和Q4同步导通Q2和Q3同步导通两组信号互补。任意时刻Uab要么等于正母线电压要么等于负母线电压波形在Vdc和-Vdc之间来回跳。单极性SPWM则不同正半周期里让左上管Q1一直导通左下管Q2一直关断只让右侧桥臂的Q4高频开关Q3保持关断。Q4导通时Uab等于VdcQ4关断时电流通过Q1和Q3的体二极管续流Uab被钳到0V。于是正半周期的Uab只在Vdc和0之间切换负半周期反过来Q2长通Q4长断Q3高频开关Uab只在-Vdc和0之间切换。所以单极性逆变对应的Uab是三电平波形也就是Vdc、0、-Vdc三种状态而双极性逆变只有Vdc和-Vdc两个电平。别小看这一个0电平它直接改变了滤波电感的电压纹波形态也让开关损耗和EMI特性和双极性明显不同后面我会逐项展开。2.2 自然采样与规则采样我为什么选中心对齐规则采样PWM脉冲宽度怎么由正弦波决定主流做法有两种自然采样和规则采样。自然采样是将正弦调制波和三角载波直接比较相交的时刻就是开关切换点获得的是最接近理想正弦的SPWM波形。但它的切换时刻需要解超越方程在单片机里实时计算负担很重。规则采样则是在载波周期的特定时刻对正弦波采样把采样值保持到下一个周期再和三角载波比较。最常用的是对称规则采样在三角载波的顶点或者谷底采样正弦值保持半个载波周期这样得到的脉冲关于载波峰谷对称。STM32的高级定时器做中心对齐PWM时计数器先增后减更新时刻正好对应三角载波的顶点特别适合直接配合对称规则采样软件里只需在更新中断里换一个新占空比即可。我在实际实现里没有用自然采样原因很实际STM32的中断为主实时计算交点太别扭而对称规则采样用一张查表就能解决计算量小、波形质量也够用。开关频率10kHz一个50Hz正弦周期分成200段每段1.8度这样的步进对绝大部分逆变场景已经足够细腻。2.3 三相线电压利用率86.6%的计算全过程这个话题在很多新手群里被反复问过因为搜索引擎一查“SPWM”最先跳出来的往往是一句话线性调制区里三相SPWM的线电压利用率最高只有86.6%。很多人拿单相单极性逆变去套这个结论疑惑为什么我明明能做到满调制。先推导这句话的来路。三相全桥逆变器是三个半桥结构每一相的输出端对直流母线中点而言最大只能输出幅度为Udc/2的相电压基波峰值。当调制比M等于1时也就是正弦调制波峰值正好等于三角载波峰值相电压基波峰值最大为Udc/2。三相线电压的基波峰值等于相电压基波峰值的根号3倍也就是根号3乘以Udc/2约等于0.866Udc。把这个数字和直流母线电压Udc做比值得到0.866写成分数就是86.6%。整个推算的关键就这两步相电压极限是Udc/2线电压是相电压的根号3倍。要区分的是这个86.6%是“三相系统、正弦波作调制波、不注入任何三次谐波”的前提下得出的结论。如果给调制波叠加三次谐波或者干脆用SVPWM线性区可以拓展到100%。而单相全桥逆变器没有根号3这一步单极性SPWM在全桥拓扑下基波电压峰值本来就能达到Udc所以不该拿86.6%来限制单相单极性方案。3. STM32实现单极性SPWM的落地步骤3.1 H桥的开关逻辑哪个管长通哪个管高频我用的是单相全桥拓扑四只N沟道MOS管搭成H桥。四个管子的逻辑关系必须在一开始就写清楚不然后面写代码必然乱。表单极性SPWM全桥开关逻辑半周期Q1 左上Q2 左下Q3 右上Q4 右下Uab状态正半周常通常断常断高频PWMVdc或0负半周常断常通高频PWM常断-Vdc或0注意负半周期Q3做高频PWM时Q4不是简单给低电平就完事。续流路径是靠Q4的体二极管完成的所以驱动电路要给够死区避免Q3和Q4在切换瞬间出现直通。我在板上用了一路带互补输出的高级定时器TIM1去驱动右侧桥臂Q3和Q4左侧桥臂Q1和Q2用两个GPIO按50Hz工频切换。左侧桥臂的开关频率只有50Hz对应的切换损耗几乎可以忽略也不需要加死区因为当Q1从导通切换到关断、Q2开始导通时右侧桥臂的PWM状态已经受控不会造出同桥臂上下管同时打开的直通路径。这个“左臂慢、右臂快”的非对称安排正是单极性方案省损耗的本质。3.2 正弦表生成与占空比换算我把一个50Hz正弦周期的正弦值预先算好存成一张查找表运行时不调用sin数学库只查表。这样做的原因很简单MCU里的浮点sin函数虽然能跑但每次都在中断里计算会让更新时刻抖动变大输出波形上表现为不该有的毛刺。查表把每次中断的计算开销压缩到一次内存读取加一次寄存器写入。表长取200项是因为我打算让PWM开关频率为10kHz10k除以50正好等于200每个PWM周期对应正弦表里的一项。用中心对齐模式后定时器在更新中断里换一次CCR值就完成一个正弦周期的200次采样。占空比换算要看单极性方案下的桥臂电压关系。正半周期输出电压Uab等于Vdc乘以右侧桥臂下管Q4的占空比d而期望的瞬时正弦值应该是母线电压乘以d所以占空比本身就是“0到调制峰值”之间的正弦映射不需要像半桥那样再做0.5偏置处理。建立查找表的代码逻辑大致如下#define LUT_SIZE 200 #define PWM_ARR 999 // ARR设为999配合中心对齐模式 #define MOD_INDEX 0.85f // 调制比先留一点余量 float angle_step 2.0f * 3.14159265f / LUT_SIZE; uint16_t lut[LUT_SIZE]; for (int i 0; i LUT_SIZE; i) { float sin_val sinf(angle_step * i); float duty MOD_INDEX * sin_val; // 正半周duty为正是Q4的占空比 if (duty 0.0f) duty -duty; // 负半周转成Q3的占空比 uint16_t ccr (uint16_t)(duty * (PWM_ARR 1)); if (ccr PWM_ARR) ccr PWM_ARR; // 限幅到合法范围 lut[i] ccr; }这里把正半周和负半周的表项统一存成绝对值方便在更新中断里根据半周期状态决定使用哪一路PWM输出。实际项目中还可以把正负半周分开处理逻辑更直白但内存占用多一倍。3.3 定时器配置与更新中断的节奏我用TIM1做主PWM定时器工作在中心对齐模式。中心对齐模式的特点是计数器从0递增到ARR再从ARR递减到0一个完整的递增递减周期对应一个PWM开关周期。这种模式下PWM脉冲两边对称谐波能量比边沿对齐模式更集中配合对称规则采样是绝配。要得到10kHz的开关频率假设定时器时钟为100MHz那么周期计数为100MHz除以10kHz等于10000因为一个开关周期包含递增和递减两段所以ARR等于计数周期的一半减1即4999。代码里需要按实际时钟计算但核心关系式是开关频率等于定时器时钟频率除以2倍的(ARR加1)。更新中断里做的事非常固定从正弦表里取出下一个CCR值写入TIM1的捕获比较寄存器同时根据索引位置判断是否需要切换左侧桥臂的GPIO状态。索引回到0时进入正半周索引等于LUT_SIZE一半时进入负半周。void TIM1_UP_IRQHandler(void) { static uint16_t idx 0; TIM1-CCR1 lut[idx]; if (idx 0) { // 正半周起始Q1长通Q2长断右侧桥臂只让Q4出PWM HAL_GPIO_WritePin(Q1_GPIO_Port, Q1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(Q2_GPIO_Port, Q2_Pin, GPIO_PIN_RESET); TIM1-CCER ~TIM_CCER_CC1E; // 关Q3输出 TIM1-CCER | TIM_CCER_CC1NE; // 开Q4输出 } else if (idx LUT_SIZE / 2) { // 负半周起始Q2长通Q1长断右侧桥臂换成Q3出PWM HAL_GPIO_WritePin(Q1_GPIO_Port, Q1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Q2_GPIO_Port, Q2_Pin, GPIO_PIN_SET); TIM1-CCER | TIM_CCER_CC1E; // 开Q3输出 TIM1-CCER ~TIM_CCER_CC1NE; // 关Q4输出 } idx; if (idx LUT_SIZE) idx 0; }工程上对更新中断的抖动很敏感更讲究的做法是用DMA把整张正弦表按更新事件自动搬到CCR寄存器让CPU完全离线。不过在学习和验证原理的阶段用中断查表完全够用等确定方案后再改DMA也不迟。3.4 一个很容易踩的坑输出引脚使能逻辑用高级定时器的互补输出做单极性SPWM时最容易犯的错误是只配置了CCER使能却没有考虑正负半周切换瞬间的输出状态。正半周结束的那一刻Q4正处在高频开关状态如果直接在中断里关掉Q4的输出并使能Q3那么下一拍右侧桥臂从“Q4工作、Q3关断”切换成“Q3工作、Q4关断”这中间必须依赖死区。高级定时器的死区寄存器要提前配好不能指望靠逻辑切换的瞬间能避免直通。另外一个细节是TIM1的CH1和CH1N默认是一对互补输出关掉其中一路寄存器后另一路仍然按CCR值产生PWM。我用CCER位控制两路输出的使能但要注意CCER的位操作不能影响其他通道。实际调试里我就因为用了整字赋值把死区配置顺带清掉过一次排查了半天才明白是初始化顺序的问题。4. 单极性与双极性SPWM的逐项实测对比4.1 输出电压波形两电平三电平的差别示波器直接看H桥中点对母线负极的电压双极性是正负交替的两电平方波单极性在正半周看到的是从0到Vdc跳变的脉冲负半周是从0到-Vdc跳变。真正有意义的差别在滤波之后的电流纹波。双极性SPWM中负载电感两端的电压在每个开关周期里都要摆动一次从Vdc到-Vdc的幅度电感电流纹波大单极性SPWM中正半周期电感电压在Vdc和0之间切换负半周期在-Vdc和0之间切换等效纹波频率提高了一倍纹波幅度却明显小。同样一只滤波电感单极性方案得到的电流波形干净得多。我实测过一组对比数据同样的母线电压340V、开关频率10kHz、负载2A双极性方案的电感电流纹波峰峰值约630mA单极性方案约320mA几乎正好差一半。这个结果不意外电感电流纹波正比于施加的伏秒积单极性把每个周期的电压摆幅砍了一半等效频率翻倍纹波自然大幅回落。4.2 开关损耗与温升开关管的损耗主要看高频开关次数。双极性方案四个管子全都在10kHz高频开关每一路MOS管的开关损耗直接累加单极性方案只有右侧桥臂两只管高频开关左侧桥臂以50Hz工频切换工频切换的损耗小到可以忽略。实测小功率200W的样机两只高频管的散热片温度明显高于低频管。双极性样机四个管子的温度比较平均但总体偏高单极性样机则是高频管局部发热集中所以在布局时我会把高频管的散热铜皮面积加大低频管可以适当缩减。需要说明的是单极性方案虽然开关损耗低但续流二极管在某个半周期内持续承担电流二极管反向恢复损耗不能忽略尤其在母线电压高、续流电流大的工况下高频管关断瞬间体二极管反向恢复造成的尖峰依然存在。选MOS管时要看体二极管的反向恢复特性必要时外并快恢复二极管。4.3 死区效应、共模干扰与EMI表现双极性SPWM的每个开关周期里桥臂中点都在正负母线之间大幅跳变负载两端共模电压变化剧烈通过寄生电容流入地的共模电流大EMI滤波器的压力也大。单极性方案在半周期内电平只在0和单侧母线之间跳变共模电压的变化幅度减半传导骚扰的实测值通常能低几个dB。死区效应方面双极性比较吃亏。死区时间里输出电压被钳位占空比丢失直接反映在基波幅值上而且丢失量随负载电流方向变化导致波形畸变更明显。单极性由于一个桥臂低频死区主要通过高频臂体现影响面小一些但在过零附近依然会出现脉冲过窄导致的平台失真这部分我在第6章单独讲。4.4 我应该选单极性还是双极性把对比结论整理成一张表方便直接选型对比维度单极性SPWM双极性SPWM输出电压电平数3电平Vdc/0/-Vdc2电平Vdc/-Vdc滤波电感纹波小等效纹波频率翻倍大一倍左右高频开关管数量2只管高频4只管高频开关损耗明显低高共模干扰较低较高死区影响过零附近仍需补偿全周期均有影响控制实现难度需要管理半周切换逻辑简单但损耗大对大多数单相逆变应用能选择单极性就选择单极性。双极性唯一的优势是控制逻辑简单适合快速验证原理。如果做的是大功率、高效率需求或者对EMI指标有强制要求单极性几乎是必须的。5. LC滤波参数与死区补偿中的工程细节5.1 电感量计算与谐振频率选择单极性SPWM的桥臂输出是一串高频脉冲必须经过LC低通滤波器才能得到平滑的正弦波。滤波器设计有两个关键点电感量决定纹波电流谐振频率决定衰减特性。第一确定电感量。流传最广的经验式是让电感电流纹波不超过额定电流的20%。单极性方案下纹波频率等于2倍的开关频率电感电流纹波峰值出现在占空比约等于0.5的位置简化工程估算式为L ≥ Udc_min / (8 × fsw × ΔIL_max)以我的样机为例母线最低电压Udc_min取280V开关频率fsw取10kHz额定负载电流2A限定纹波电流0.4A代入后L需要大于等于8.75mH实际取10mH。第二确定电容值。LC低通滤波器的截止频率fc一般取开关纹波频率的十分之一左右也就是1kHzfc 1 / (2π√(L × C)) 1000Hz反推电容当L为10mH时C约等于2.5μF实际取4.7μF。这个电容值在空载时会和电感形成谐振如果负载断开可能出现输出电压抬升的现象所以输出端硬件上要加泄放电阻软件上也应允许空载运行但不做过高升压。5.2 死区损失的补偿思路与其局限死区时间虽然只有一两微秒但在高频PWM下累计起来就是不可忽略的占空比丢失。死区期间输出电压被续流二极管钳位相当于实际输出脉冲宽度小于理想值。最直接的做法是在CCR值上做补偿根据负载电流方向在原有占空比上增加或减少一个等效死区占空比值。等效补偿量等于死区时间乘以开关频率。比如死区2微秒、开关频率10kHz对应占空比损失为2%那么就在电流为正采样的半个周期里把CCR增加约2%的ARR值电流为负时则减小。这个办法看起来简单实际做起来有两个坑。一是必须准确判断负载电流方向阻性负载好判断感性负载的电流滞后于电压开关切换瞬间电流方向可能已经变了判断错了反而加大畸变。二是死区时间不是固定值随母线电压、温度、驱动芯片延时漂移所以工程上经常要用闭环调节或者自适应补偿兜底。对学习阶段来说先把死区时间按手册保守设小一点再观察输出波形畸变是否可接受比一上来就做闭环补偿更划算。6. 调试验证中的三个典型问题与排查路径6.1 空载谐波还行接上负载波形就塌第一次上电我犯的错很典型空载时示波器看输出波形很漂亮正弦平滑无毛刺接上几十瓦的灯泡后波形立刻变钝THD明显上升。排查思路要按链路走先看母线电压在负载下有没有跌落再看桥臂输出脉冲是否还有正确的包络最后用电流探头量电感电流。我这边的根因是母线电容容量不足2A负载下母线电压纹波很大SPWM的占空比是按理想母线电压算的母线一跌输出电压自然塌。换大容量电解电容后波形恢复。这个案例提醒我SPWM调得好不好不只是调制算法的事母线供电质量同样决定输出波形。6.2 过零点出现不自然的平台单极性SPWM在正弦波过零附近占空比趋近于0高频PWM的脉冲宽度变得很窄。此时死区时间占整个脉冲周期的比例急剧上升输出平均电压被死区钳位得偏离理想值示波器上看正弦波过零附近会有一段平坦的“台阶”或者畸变。解决思路有两步第一步确认死区时间是否设置合理把死区从3微秒降到1微秒左右观察平台是否改善第二步引入前面讲的死区补偿先按电流方向做开环补偿看波形改善多少。如果仍不理想再考虑把过零附近的正弦表项进行局部修正。这个现象在双极性方案里更严重单极性已经算温和的了。6.3 载波频率与PWM分辨率打架想让波形更细腻第一反应是提高PWM开关频率。开关频率一高定时器计数周期变短ARR值变小每一条占空比对应的最小步进百分比变大PWM分辨率变差。我在一组对比测试里深有体会定时器时钟100MHz开关频率10kHz时ARR约4999CCR步进分辨率大约是万分之二开关频率提高到50kHz时ARR只剩999PWM分辨率掉到千分之一。分辨率不足时占空比只能按较大台阶变化滤波后波形就会叠加上明显的量化噪声。规避方法是在定时器时钟允许的范围内尽量用高时钟或者干脆把开关频率维持在一个折中值。我习惯用16kHz到20kHz既避开音频可听范围又保留足够分辨率实际效果比一味追求高频好得多。7. 跑了几周测试之后我再补充几句整个项目从头到尾调下来我最大的体会是单极性SPWM不是简单把双极性代码里的程序逻辑改一改就能直接用的。它改变的是桥臂电压的宏观形态连带把滤波器设计、损耗分布、死区补偿策略都改变了。如果只是照抄一段生成SPWM的代码不考虑半周切换和续流路径很可能会做出一个能输出正弦波、但效率和EMI都很差的方案。调试期间有几个习惯帮了大忙一是全程用隔离探头观察桥臂中点电压避免共地导致的测量失真二是在母线输入侧串一只限流灯泡再上电遇到直通短路时灯泡会限制电流保住了好几只MOS管三是每次调整完死区或者调制比都顺手记录示波器截图方便回看不同参数的波形差异。这些小习惯看起来不起眼但遇到问题时能帮你快速锁定位点比对着代码空想要高效得多。如果你也是第一次在STM32上做单极性SPWM建议按照本文的顺序走一遍先把H桥开关逻辑表贴在板子上再写正弦表和定时器程序空载观察波形最后再处理死区补偿和EMI。遇到平台失真和波形畸变时别急着换算法用示波器把桥臂电压、电感电流、母线电压三个信号抓出来对比往往答案就在其中一两个波形里。