
做三相SPWM逆变器这事儿我一直觉得是嵌入式电源方向最值得亲手做一遍的项目。它不像点个灯、跑个串口那么简单也不像纯做理论分析那么虚而是把单片机定时器、功率电子、模拟采样、PCB布局、仿真验证全串在一起任何一个环节掉链子示波器上立刻给你颜色看。这篇帖子就围绕“STM32F103C8T6从零搭建三相SPWM逆变器”展开把我从原理图设计、MATLAB仿真到PCB打样、源码调试整条链路踩过的坑和验证过的方法原原本本写出来。无论你是刚学会用CubeMX的小白还是想把手头项目从方波升级成SPWM的老手这篇内容都能让你少走不少弯路。先说清楚这个项目做出来是什么一块以STM32F103C8T6为核心的驱动板输出六路带死区的SPWM波形驱动三相全桥IGBT或MOSFET母线电压经过逆变后得到三相互差120°的交流输出。整个系统包含控制板、驱动电路、功率板三部分配合MATLAB/Simulink仿真验证算法最后用PCB实现。开源资料里常见的方案很多但大多数要么只讲仿真要么直接甩源码真正把仿真、原理图、PCB、代码串起来讲透的很少这篇就是奔着“一条龙复现”去的。1. 整体方案设计与思路拆解1.1 为什么选STM32F103C8T6做逆变器控制核心很多人在选型时会纠结做逆变器是不是得上DSP或者F28027、F28035这类专用控制芯片。确实工业变频器和伺服驱动里TI的C2000系列是绝对主流因为它的PWM外设带死区产生、故障封锁、硬件比较器性能很强。但自己做项目、做毕设、做产品原型STM32F103C8T6完全够用而且有非常现实的优势。第一成本低、货源足。蓝色pill板或者最简核心板十几块钱一片就算国产替代型号也完全兼容烧了不心疼。第二生态成熟。Keil、IAR、STM32CubeMX随便用网上参考资料多到爆炸遇到问题基本都能搜到答案。第三硬件资源刚好够用。STM32F103C8T6有TIM1和TIM8两个高级定时器其中TIM1自带6路互补PWM输出支持死区插入和刹车功能这几乎就是为三相逆变器准备的。72MHz主频跑10kHz~20kHz的开关频率中断里更新正弦表查表值CPU占用率很低。有人会问F103C8T6只有64KB Flash、20KB RAM跑SPWM够不够我实际测试正弦表用uint16_t数组存256点三个通道共用一个表、相位差通过查表偏移实现占用不到1KB Flash中断里做简单的乘法和加法RAM占用也极低。整个工程编译完不到30KB非常宽裕。1.2 三相SPWM逆变器整体架构与信号流向三相SPWM逆变器的系统框图本质上就是“弱电控制强电”的典型结构。信号流向从STM32的TIM1输出六路PWM开始经过隔离驱动芯片常见IR2101、EG2134、IR2110进行电平转换和功率放大再去控制三相全桥六个开关管的导通与关断。母线直流电压比如310V由220V交流整流得到或者48V、24V低压测试经过三相桥的开关动作在输出端合成出三相对称的交流电压。这里必须理解一个核心逻辑逆变器不是直接“产生”交流电而是通过高速开关直流母线电压让负载端看到平均电压呈正弦变化。SPWM正弦脉宽调制的思路就是让PWM脉冲的占空比按正弦规律变化脉冲宽度大则输出电压高脉冲宽度小则输出电压低再经过LC滤波或者负载本身的感性特性把高频开关分量滤掉就得到了平滑的正弦波。我做这个项目时控制板和功率板是分开设计的。控制板就是一块STM32F103C8T6最小系统板再加上信号调理电路功率板包含三相全桥、驱动芯片、母线电容、电压电流采样。这样的好处是调试时可以分开供电先用直流稳压电源给控制板供电在线调试PWM波形确认六路波形和死区都没问题再逐步加母线电压。这个习惯救了我很多次强烈建议你也这么做。1.3 两电平与三电平方案怎么选选两电平还是三电平拓扑是很多人查资料时看到“两电平逆变器与三电平逆变器区别与时序分析图”这个热搜词后会产生的疑问。先说结论用STM32F103C8T6做三相逆变器老老实实做两电平就可以了三电平NPC拓扑不建议在这个平台上玩。两电平逆变器就是每个桥臂上下两个开关管输出端要么接母线正要么接母线负输出电压只有两个电平加上死区时间的影响实际上开关过程中有短暂的中间状态。控制简单驱动电路只需要三个半桥驱动芯片F103的TIM1一个定时器全搞定。三电平NPC逆变器每个桥臂有四个开关管和两个钳位二极管输出端可以接母线正、母线负、母线中点三个电平。输出波形质量更好谐波更小但驱动电路复杂得多还需要中点电位平衡控制F103的运算资源虽然勉强够但对新手来说难度直接翻倍。我做项目时也翻了不少三电平的时序分析图研究完就明白那是有中点电压控制需求的高压大功率场合用的方案低压小功率学习项目用两电平完全合理。2. 核心原理与关键参数计算2.1 SPWM生成思路硬件调制还是软件查表SPWM生成有两种主流方式理解清楚这两种方式的差别对你后续写代码会有本质帮助。第一种是硬件调制方式。用STM32的TIM1内部斜波三角载波和比较寄存器CCR值比较当计数值小于CCR时输出高电平大于CCR时输出低电平。所以只要在每个PWM周期更新CCR寄存器让它按正弦规律变化输出端就能得到SPWM波形。这种方式的关键是CCR更新必须准确、及时最好用定时器更新中断来更新占空比保证每个载波周期内只更新一次避免在PWM周期中间被改写导致波形毛刺。第二种是软件查表方式。提前在代码里存一张正弦表中断里取出当前角度对应的正弦值乘以一个缩放系数后直接赋给CCR。查表法计算量极小适合主频不高的MCU而且表可以预先在MATLAB或Python里生成好几乎不需要单片机做三角函数运算。我做项目时选的就是查表法配合定时器更新中断31250Hz的更新率在72MHz主频下毫无压力。2.2 载波比、调制度与死区时间的计算这几个参数是SPWM的核心必须自己动手算一遍不能只抄代码。载波比就是载波频率和调制波频率的比值也就是PWM开关频率除以输出正弦波频率。我做的是工频输出目标50Hz载波频率选10kHz或20kHz。选10kHz时载波比是200选20kHz时载波比是400。载波比越高输出波形谐波越小、电机的噪声越小但开关损耗越大、对驱动芯片和散热要求越高。我做实验用10kHz起步确认稳定后再往上提。调制度调制比M是正弦调制波的幅值与载波幅值的比值范围0到1。它直接决定输出相电压的基波幅值。对单极性SPWM输出相电压基波有效值大约是 ( U_{ph} M \times \frac{U_{dc}}{2\sqrt{2}} ) 。比如母线310V、M0.9时相电压有效值约为98V线电压约为170V这时候要得到220V线电压就需要通过升压或改变调制策略。计算清楚这个关系你就知道为什么逆变器输出“220V”没那么简单母线电压、调制比、变压器三者要配合。死区时间的计算也特别关键。死区就是同一桥臂上下管换流时必须保证一个管子完全关断后另一个才能导通。STM32F103的TIM1有专门的死区寄存器可以精确到纳秒级。死区时间要大于开关管的关断延迟时间。IR2101搭配常用的IRFP460、IRFP260等MOS管死区设0.5μs到2μs比较合理。我看过有些初学者的板子死区设得太大比如5μs导致输出波形畸变明显、电机电流噪声大设得太小又容易直通炸管。我用示波器实测过IGBT的关断延迟通常设1μs左右比较稳妥。2.3 三相正弦表的生成方法三相SPWM的关键就是要得到三相互差120°的正弦调制波。实际代码里不推荐直接用数学库算sin因为中断里算浮点sin会有比较大的开销。正确做法是提前生成一张正弦表。我用MATLAB生成256点正弦表然后把数据直接复制成C语言数组。核心代码如下% 生成256点12位精度的正弦表 N 256; sine_table zeros(1, N); for i 1:N sine_table(i) round(2048 2047 * sin(2 * pi * i / N)); end % 打印成C语言数组格式 fprintf(const uint16_t sin_table[%d] {\n, N); for i 1:8:N fprintf( ); for j i:min(i7, N) fprintf(%d, , sine_table(j)); end fprintf(\n); end fprintf(};\n);这里用2048做中点、2047做幅值是因为TIM1的ARR如果设为4095CCR寄存器是12位有效占空比正好可以从0到4095变化2048对应50%占空比也就是调制波过零点。三相的相位差通过查表偏移实现A相从表头开始B相偏移N/3个点C相偏移2N/3个点。因为N256N/3不是整数所以要特别处理。这里有个小坑256不能被3整除直接偏移会引入相位误差。我后来改用768点表这样每相偏移256点正好是整数。也可以用1024点表偏移341、682但769、341.33都不是整数所以768点最合适。如果你非要用256点表就把B相偏移85或者86点实际上误差也不大但做严格的三相对称输出时768点表更好。我最终用的是768点表。3. MATLAB仿真搭建实操3.1 Simulink模型怎么搭开始焊接之前强烈建议先用MATLAB/Simulink把系统仿真跑通。Simulink里搭三相SPWM逆变器并不复杂但很多人一上来就找现成的demo反而搞不清楚每个模块的作用。其实核心就几个模块。我用的是R2023b版本Simulink模型大致如下直流电源用“DC Voltage Source”母线电压先设低压比如100V便于观察。三相全桥可以用“Universal Bridge”模块桥臂数选3电力电子器件选IGBT/Diode。SPWM脉冲生成不用自己写S函数直接用Simulink自带的“PWM Generator (Three-phase, Two-level)”模块这个模块支持输入调制波三相正弦波和载波频率可以设置死区时间。三相调制波用三个“Sine Wave”模块幅值、频率按需设置相位分别设为0、-120°、-240°。负载侧接三相对称RL负载或者接一个“Asynchronous Machine”模块模拟电机负载。测量输出电压电流用“Three-Phase V-I Measurement”然后接“Scope”观察波形。仿真前要理解一个细节PWM Generator模块的载波频率要和后续STM32定时器的实际开关频率保持一致否则仿真的谐波分析结果无法指导实际调试。我仿真时开关频率设10kHz调制波50Hz仿真步长用固定步长步长设为1e-6秒仿真时间0.1秒这样能清晰看到两三个周期的输出波形。3.2 仿真参数设置与波形分析仿真最直观的收获是看三相相电压和线电压的波形以及滤波前后的变化。不接滤波器时相电压是典型的PWM脉冲序列边缘和母线电压相等。这时候你把它做FFT分析可以看到频谱里除了50Hz基波外还有大量以载波频率为中心的边带谐波。输出线电压则是两相相电压的差会看到阶梯状的SPWM波形幅值最高达到母线电压。接上LC滤波器我仿真用L5mH、C10μF输出端就能看到光滑的正弦波。需要特别注意的是LC滤波器的谐振频率公式是 ( f_{res} 1 / (2\pi\sqrt{LC}) )取5mH和10μF时谐振频率约712Hz远低于载波10kHz同时远高于基波50Hz能有效滤除高频分量且不会对基波造成明显衰减。这也是滤波器设计经验里常用的准则即谐振频率至少是基波的10倍且不超过载波的1/5到1/10。仿真中我还会故意加大死区时间比如从1μs加到5μs观察输出波形畸变。这个操作让我对死区效应有了直观认识死区时间越大输出电压基波幅值损失越大波形越接近“削顶”这在实际电路中是看不到这么明显的规律的。3.3 仿真和实测之间的差距仿真终究是“理想世界”它帮我们验证了算法和参数的正确性但和真实系统有几点明显差距。首先是开关管的导通压降和开关时间仿真里默认是理想开关实际用IGBT或MOS管存在导通压降和开关损耗会让输出电压略微降低、发热明显。其次是死区时间在仿真里可以精确控制但实际电路中驱动芯片的传播延迟、光耦或隔离电源的延时会让实际死区比设定值更大或更小。第三是母线电压的纹波仿真里直流源是理想电压源实际中母线电容容量不够或ESR太大时开关瞬间母线电压会剧烈波动严重时会导致过压击穿。所以我的观点是仿真结果能验证设计方向和参数范围但到了硬件阶段还是要以实测波形为准。把仿真里的调制比、开关频率、LC参数作为初始值再根据实测微调这个流程比纯仿真或者纯盲调都要高效得多。4. 从原理图到PCB布局布线与抗干扰4.1 控制板与驱动电路选型PCB设计是整个项目里最容易被低估的环节。很多人觉得原理图画完、PCB随便拉一下线就能工作实际上一块功率板如果布局布线不讲究轻则输出波形畸变、单片机死机重则炸管炸模块。我在这个项目里的做法是控制板用现成的STM32F103C8T6最小系统板自己做功率板。最小系统板网上十几块钱稳定性有保障而且坏了换一块就行完全不心疼。驱动电路选型方面IR2101是常用的半桥驱动芯片自带死区时间可以由外部电阻调整也能用MCU直接在PWM里插入死区。但我后来换成了EG2134因为它是三相半桥驱动一个芯片同时驱动三个半桥外围电路更简洁。EG2134内置死区逻辑即使MCU输出错误也能在一定程度上保护上管和下管不同时导通。对我这种经常手滑的人来说这个功能算多了一道保险。驱动电路要注意VCC和VB之间的自举电容这个电容提供上管驱动电压。容值一般选0.1μF到1μF之间而且要靠近芯片引脚放置。自举电容太小会造成上管驱动电压不足管子无法完全导通发热严重太大则充电时间太久频率高时可能来不及补充电荷。我实测10kHz开关频率下用1μF没问题20kHz时也稳定。4.2 PCB布局顺序和关键走线规则功率板PCB布局我总结了一条核心原则先画功率回路再画驱动电路最后画控制信号。电流大的回路面积要尽量小这是减少寄生电感的关键。三相全桥的布局做成“一字型”或“U字型”母线正极和母线负极之间要尽量靠近。以AD20为例我做双层板顶层放功率器件和驱动底层做地平面和信号走线关键规则如下功率走线母线电流和三相输出电流的走线宽度至少2mm以上1盎司铜厚下2mm走线大约能承受2A左右电流如果母线电流更大就开窗加锡或直接上铜排。信号走线PWM信号从单片机到驱动芯片的距离尽量短走线宽度0.3mm~0.5mm串接22Ω~100Ω的栅极电阻可以抑制振铃。地线处理功率地母线负极和信号地单片机地要单点连接通常在母线电容负极处汇合避免功率电流在信号地上产生压降干扰控制电路。数字地和模拟地如果分开也要用0欧电阻或磁珠汇接。安全间距强电区域和弱电区域之间间距做到2mm以上如果母线电压超过100V还要在PCB上开槽做物理隔离防止爬电。4.3 母线电容、采样电路与铜皮挖空技巧母线电容是逆变器PCB上最重要的元件之一。它的作用是在开关管高频通断时提供瞬态能量否则母线导线电感会感应出很高的尖峰电压可能导致开关管过压击穿。容量选择上小功率实验可以先用电解电容470μF~1000μF配合0.1μF~1μF的CBB电容并联。电解电容负责储能CBB电容负责滤高频纹波。CBB电容尽量靠近开关管放置这比容量大更重要。PCB上的例子母线正负极之间如果只用一个电解电容引脚长、走线长等效串联电感就大尖峰就高而把CBB薄膜电容紧贴IGBT封装放高频尖峰能显著下降。采样电路方面如果需要做闭环控制或者过流保护母线电流采样可以用采样电阻加差分放大器或者用电流互感器。低压小功率场景用采样电阻最简单。采样电阻要选低温漂、低感抗的类型比如锰铜电阻或者康铜电阻。采样信号走线要用差分走线远离功率开关管和栅极走线避免开关噪声耦合。关于“AD20中PCB板铜皮挖空”这个技术点主要是为了两块区域之间的隔离。我在功率板画完之后会在强弱电之间、以及高压区域下方铺地铜皮的地方做挖空处理降低寄生电容和爬电风险。操作步骤是在AD20里用“Polygon Pour Cutout”工具在需要隔离的区域画一个闭合多边形再重新铺铜这块区域就不会覆盖铜皮。另一个场景是芯片底部散热焊盘正下方有时为了减少寄生电容或者避开高压爬电也需要在底层对应区域挖空。5. STM32源码实现解析5.1 定时器配置与三相互补PWM输出源码的核心是TIM1的配置。TIM1是高级定时器可以输出三对互补PWM也就是CH1/CH1N、CH2/CH2N、CH3/CH3N正好驱动三相全桥。我用STM32CubeMX配置但也建议你手写一遍寄存器彻底理解过程。关键配置如下时钟源内部时钟APB2定时器时钟72MHz。预分频器PSC 0这样计数器时钟就是72MHz。自动重载值ARR 7199这样PWM频率就是72MHz / (01) / (71991) 10kHz。计数模式向上计数产生三角载波的上升沿效果。对称PWM中心对齐一般用中心对齐模式但我用向上计数配合更新中断同样能实现SPWM只是载波和调制波的相位关系和中心对齐略有差别不过对通用逆变器没影响。PWM模式1或模式2都可以关键是占空比和CCR值的关系要对。TIM1输出六路互补PWM时还要设置MOE主输出使能位TIM1_BDTR寄存器的MOE位忘记这一步是初学者最常见的错误现象就是PWM寄存器配好了但引脚没有输出。用CubeMX的话要在“Break and Dead-Time settings”里把MOE使能打开。5.2 死区设置与开环调压死区时间通过TIM1_BDTR寄存器的DTG[7:0]位配置。以72MHz时钟为例如果选择DTG100死区时间计算公式是 ( T_{DTS} 1/72MHz ≈ 13.9ns )死区时间大约为 ( 13.9ns \times 100 1.39μs )。如果直接用CubeMX在“Break and Dead-Time settings”里直接填1.4μs软件会自动算好。开环调压就是通过改变正弦调制波的幅值来控制输出交流电压。在查表法里就是改变CCR更新时的缩放系数。具体代码逻辑如下// 主循环或中断里根据调制度M计算当前相CCR // A相index_a (phase_index) % TABLE_SIZE // B相index_b (phase_index TABLE_SIZE / 3) % TABLE_SIZE // C相index_c (phase_index 2 * TABLE_SIZE / 3) % TABLE_SIZE uint16_t get_ccr_value(uint16_t index, uint16_t modulation_percent) { // modulation_percent: 0~1000 对应调制度0~1.000 uint32_t value sine_table[index]; value value * modulation_percent / 1000; if (value 4095) value 4095; return (uint16_t)value; }注意这个乘法和除法放在定时器更新中断里做计算量不大。如果觉得除法慢可以改用右移或者预计算的查表法优化。5.3 过流保护与故障处理做逆变器过流保护是保命设计。STM32F103的TIM1带刹车功能外部信号或软件都可以触发刹车让PWM输出立即变为安全状态全部关断或全部接入低电平。我把比较器的输出接到TIM1_BKIN引脚当母线电流超过阈值时硬件立即封锁PWM不需要软件干预这是最快的保护路径。软件层面也要处理故障标志位。在TIM1中断里检查刹车中断标志一旦触发就记录故障码、点亮故障灯、进入错误处理循环彻底停止输出。我还会用ADC采样母线电压如果母线电压超过设定值比如380V就主动关闭PWM防止电容过压或者管子雪崩击穿。故障恢复要设计成手动恢复不能自动重启否则故障状态持续存在时反复重启会烧管子。6. 调试过程中常见问题与排查6.1 一上电就炸管的几个原因炸管是每个做逆变器的人都经历过的痛。总结下来炸管原因基本逃不过下面几类。第一类最普遍死区时间不够或者没有死区上下管直通。即使是IGBT关断也需要时间上下管还没完全关断就开通母线电压直接短路瞬间大电流把管子炸裂。解决办法示波器双通道同时观察同一桥臂上下管的栅极波形确认死区时间。第二类是控制电源和功率电源上电时序问题。单片机还没正常工作时IO口状态不定驱动芯片可能输出异常信号导致全桥直通。解决办法是驱动芯片的使能引脚用硬件上拉到安全状态或者用RC延时电路保证单片机初始化完成后才使能驱动。第三类是栅极电阻过小或者没有栅极电阻导致开关瞬间栅极电压振铃严重管子误触发。串一个10Ω到100Ω的栅极电阻能明显改善具体阻值要根据管子栅极电荷和开关速度调节。6.2 波形异常排查思路上电后如果输出波形不对先别急着改代码按下面的顺序排查。第一步断开功率部分只给控制板供电用示波器看TIM1输出的六路PWM波形。检查频率是不是10kHz检查上下管互补波形之间的死区检查三对PWM之间的相位关系是否互差120°。第二步确认PWM幅值。STM32输出的是3.3V电平如果驱动芯片输入阈值设计不对可能无法识别。IR2101输入高电平阈值是2.4V左右3.3V可以驱动但有些驱动芯片需要5V电平这时候就要加电平转换电路。第三步接上驱动电路但不加母线电压空载测量驱动芯片输出端的波形。如果波形幅值不够检查自举电容是否正常、VB和VS之间的电压差够不够。第四步加低压母线电压比如用直流稳压电源调到10V带上假负载测量输出线电压波形。如果波形正常再逐步升高母线电压。这个循序渐进的过程能帮你把问题限定在某一级电路比一下子加310V再炸板高效得多。6.3 其他实用调试经验与避坑技巧调试过程中还有几个细节容易被忽略。第一示波器探头的地线夹子要尽量短否则测量高频开关波形时地线电感会和探头电容形成谐振看到振铃不是真实的电路问题。建议用弹簧地线示波器标配的短地弹簧。第二母线电压采样电阻的分压比要算准。用两个电阻分压比如100kΩ和10kΩ100V母线在ADC引脚得到约9V如果用3.3V的ADC必须再分压用100kΩ、10kΩ、3.3kΩ的组合或者直接用电压互感器隔离。高压侧采样时电阻耐压也要考虑单个0805贴片电阻耐压有限串联多个电阻分担电压是常规做法。第三调试串口必须留。 PCB上预留一个USART1的调试串口或者SWD接口这对调试帮助巨大。我习惯在代码里加一个简单的命令解析器可以实时修改调制度、开关频率、死区时间等参数不用每次都重新烧录固件。这个操作在调闭环或者做老化测试时特别方便。第四散热问题。低压小功率实验阶段MOS管可能不需要额外散热器但一旦母线电压和电流上来管子发热会非常快。建议功率管预留散热器封装位置用导热硅脂加螺丝固定。温度保护也可以用STM32内部温度传感器做超过设定温度直接刹车封锁输出。从MATLAB仿真到PCB打样再到STM32源码调试整个三相SPWM逆变器项目做下来我最大的感受是这个项目最难的并不是某个单一环节而是把每个环节的误差和风险都控制在可接受范围内。仿真里参数再完美实际电路里一个地线布局不合理就可能让波形面目全非STM32PWM配置再熟练驱动芯片自举电容选错也可能让上管无法导通。动手做之前不要指望一次成功按“控制板空载调试 → 驱动板不带母线调试 → 低压带载调试 → 升压验证”这个顺序逐步推进是能把炸管次数降到最低的。另外我还有个习惯软件里所有关键参数比如调制比、死区时间、过流阈值全部做成宏定义或者结构体变量方便调试时快速修改。这种“仿真验证算法、硬件验证实现、软件验证逻辑”三者互相印证的方法在这类功率电子项目里确实管用。后面如果你们感兴趣我可以再写一篇加装电压电流闭环和并网控制的内容让这个系统从开环实验变成真正可用的电源装置。