
1. 从一块十几块钱的蓝色小板说起小功率逆变器为什么值得用STM32认真做一遍如果你手上正好有一块STM32F103C8T6最小系统板又对逆变器这个东西有点好奇那这篇内容基本就是写给你的。小功率逆变器通常指输出在100W到500W这个区间的DC-AC变换装置输入多为12V或24V直流输出220V/50Hz交流。它不算什么高精尖的电力电子设备但麻雀虽小五脏俱全——SPWM生成、H桥驱动、死区控制、ADC采样、过流保护、电压闭环该有的全都有。拿它来练手比单纯点个灯、跑个FreeRTOS要扎实得多。我之所以推荐用STM32来做这件事核心原因有三个。第一STM32F103C8T6这颗芯片的定时器资源足够丰富TIM1和TIM8都是高级定时器自带互补输出和死区插入功能生成SPWM波形几乎是专业对口。第二它的ADC采样速度和外设联动能力比如定时器触发ADC、DMA搬运可以做到和PWM周期严格同步这对逆变器的电压闭环控制非常关键。第三生态成熟标准库、HAL库、寄存器开发三条路都走得通ST-Link调试器便宜又好用Keil、PlatformIO、STM32CubeIDE随便挑。但我要先说清楚一件事小功率逆变器看着简单真正做起来烧管子、炸电容、波形畸变、带载掉压这些问题一个都不会少。这篇内容不会只给你一个能跑就行的Demo而是把我在实际调试中踩过的坑、想明白的原理、以及那些文档里不会写的细节尽量摊开来讲。适合有基本单片机基础、想从点灯进阶到做点真东西的朋友也适合正在做相关毕业设计或项目验证的工程师参考。2. 逆变器的骨架H桥、SPWM和那颗被低估的F103C8T62.1 小功率逆变器的拓扑选择为什么是H桥而不是别的小功率逆变器的主流拓扑有两种推挽和H桥。推挽适合低压输入比如12V升到220V变压器初级中心抽头两个MOS管交替导通结构简单但变压器利用率低管子耐压要求是输入电压的两倍。H桥则是四个开关管组成桥臂对角导通实现正负交替变压器初级不需要中心抽头利用率高控制灵活。我选的是H桥原因很直接STM32的高级定时器天生就是为互补PWM设计的TIM1的CH1/CH1N、CH2/CH2N、CH3/CH3N三对互补通道正好可以驱动H桥的四个管子用两对就够了留一对备用或做其他用途。而且H桥的死区控制是硬件级别的配置好BDTR寄存器里的死区时间就不用担心上下管直通。后级H桥的四个开关管我用的是IGBT型号是常见的FGA25N120或者类似的1200V/25A管子。有人会问小功率为什么不用MOS管MOS管导通电阻低开关速度快但耐压做高之后导通电阻会急剧上升而且体二极管反向恢复特性在桥式电路里容易引发问题。IGBT虽然开关速度慢一点但耐压高、电流大、驱动简单在小功率逆变器这个场景下反而更省心。当然如果你做的是几十瓦的微型逆变器低压MOS管也完全可以。2.2 STM32F103C8T6的资源账哪些外设是刚需哪些是锦上添花先把这颗芯片的资源盘清楚。F103C8T6是Cortex-M3内核72MHz主频64KB Flash20KB RAM。对于小功率逆变器来说这个配置是够用的但前提是你得把资源用在刀刃上。刚需外设清单TIM1高级定时器生成四路SPWM带互补输出和死区插入。这是整个方案的核心没有它就得用软件模拟CPU占用率会很难看。ADC112位采样输出电压和电流。我用的是定时器触发ADCDMA搬运的方式采样点和PWM周期严格对齐避免采样到开关噪声。TIM2或TIM3通用定时器做系统时基比如1ms的调度 tick或者用来做按键扫描、LED指示。USART1调试串口printf输出关键变量调波形的时候没有串口基本等于瞎子摸象。GPIO驱动继电器、风扇、指示灯以及读取按键。锦上添花的外设TIM4可以做输入捕获测量输出频率做频率闭环。I2C或SPI接OLED屏或者外部EEPROM显示状态或保存参数。独立看门狗IWDG逆变器这种强干扰环境看门狗是保命的。这里有个细节要注意F103C8T6的PB0引脚不能做按键输入因为它在某些封装里和别的功能复用内部有特殊电路。我在第一版硬件上就踩了这个坑按键死活读不到换了PB1才正常。如果你在画板子尽量避开PB0做普通IO。2.3 SPWM生成的核心逻辑查表法还是实时计算SPWM的生成方式有两种主流做法查表法和实时计算法。查表法就是预先算好一个正弦表比如256个点放在Flash里定时器每次中断或者DMA请求时按顺序取出一个值赋给比较寄存器。优点是CPU占用极低波形稳定缺点是频率和调制比调整不够灵活改频率就得重新算表或者改定时器周期。实时计算法是在每个PWM周期里用数学公式算出占空比比如用sin()函数或者查表加插值。优点是灵活频率、调制比、甚至波形类型都能实时改缺点是对CPU有一定压力72MHz的M3跑50Hz的SPWM载波频率20kHz每秒中断20000次每次算一个sin值其实也扛得住。我最终用的是查表加定时器周期调整的方案。正弦表用256点16位精度放在Flash里。载波频率固定20kHz通过改变定时器的ARR值来调整输出频率。这样既保证了波形质量又保留了频率调整的灵活性。具体来说TIM1的时钟是72MHz中央对齐模式ARR值设为1800那么PWM频率就是72MHz/(2*1800)20kHz。输出频率50Hz意味着每个正弦周期有20000/50400个PWM周期256点的正弦表每个点大约对应1.56个PWM周期用简单的线性插值就能平滑过渡。注意查表法的正弦表精度直接决定输出波形的THD。我建议至少用128点256点更稳妥。表里的数值要归一化到ARR的一半左右留出死区时间的余量。3. 从原理图到PCB硬件设计里那些看起来对但实际会炸的细节3.1 最小系统板的选型与供电隔离市面上常见的STM32F103C8T6最小系统板十几块钱一块拿来验证逻辑没问题但直接用在逆变器上要慎重。原因有两个一是板载的AMS1117线性稳压器在12V输入下发热严重二是没有做电源隔离逆变器的开关噪声很容易通过电源耦合回MCU导致复位或者ADC采样异常。我的做法是最小系统板只负责逻辑部分用独立的5V电源供电这个5V来自一个隔离型DC-DC模块比如B0505S或者类似的。逆变器的12V主电源和MCU的5V电源在电气上完全隔离地线也分开最后在一点通过磁珠或者0欧电阻连接。这样做的成本增加不到十块钱但稳定性提升非常明显。如果你非要用最小系统板自带的稳压器那至少要在12V输入端加一个LC滤波并且把AMS1117换成开关型稳压芯片比如MP1584或者LM2596。不过我还是建议隔离这是花小钱办大事。3.2 H桥驱动电路光耦隔离还是自举驱动H桥的四个IGBT需要驱动电路。常见方案有两种光耦隔离驱动和自举驱动。光耦隔离驱动用TLP250或者HCPL3120这类光耦每个管子一个输入侧接STM32的PWM输出输出侧接IGBT的栅极。优点是隔离彻底上下管之间不需要担心共地问题缺点是光耦有传输延迟而且需要独立的隔离电源给每个光耦供电电路复杂。自举驱动用IR2110或者IR2104这类半桥驱动芯片一片就能驱动一个桥臂的上下管。优点是电路简单只需要一路电源缺点是自举电容在低频或者大占空比下可能充电不足导致上管驱动电压不够。我选的是IR2110加自举电容的方案因为小功率逆变器的占空比不会太极端自举电容用1uF的钽电容配合快恢复二极管实测在20kHz载波下驱动很稳定。但要注意IR2110的逻辑输入是5V兼容的STM32的3.3V输出可以直接驱动不需要电平转换。如果你用的是IR2104它自带死区插入但死区时间是固定的不如STM32的BDTR寄存器灵活。提示IR2110的VCC和VDD要分别供电VCC是驱动供电通常12V到15VVDD是逻辑供电5V或者3.3V。很多人只给VCC供电忘了VDD结果芯片不工作查半天查不出来。3.3 电流采样与过流保护别等炸管了才想起来电流采样是逆变器保护的核心。我用的是霍尔电流传感器型号是ACS712或者类似的串在H桥的直流母线或者输出回路上。ACS712输出的是模拟电压零点在VCC/2灵敏度是66mV/A对于5A量程的型号。STM32的ADC采样这个电压换算成电流值。过流保护的逻辑是ADC采样值超过阈值立即关闭PWM输出。这里的关键是响应速度。如果等软件轮询ADC再判断再关PWM可能已经过去几十微秒管子已经炸了。我的做法是用ADC的模拟看门狗功能或者用比较器直接触发定时器的刹车输入BKIN。STM32的TIM1有刹车功能一旦BKIN引脚被拉低硬件立即关闭PWM输出不需要CPU干预。这个响应速度是纳秒级的比软件快几个数量级。具体配置把电流传感器的输出接到一个比较器的输入端比较器的另一端接参考电压比较器输出接TIM1的BKIN引脚。一旦过流比较器翻转BKIN拉低TIM1硬件关断。同时比较器输出也可以接到一个外部中断让CPU知道发生了过流做后续处理。3.4 PCB布局功率地和信号地必须分开PCB布局是逆变器设计里最容易被忽视但最致命的部分。我见过太多人原理图画得漂漂亮亮PCB一塌糊涂结果波形惨不忍睹。核心原则就一条功率地和信号地分开最后单点连接。功率地是H桥、母线电容、电流传感器这些大电流路径的地信号地是MCU、晶振、ADC参考这些弱信号的地。如果混在一起大电流在地线上的压降会直接叠加到ADC采样上导致采样值跳动闭环控制根本没法做。具体做法在PCB上划两个区域功率区域和信号区域各自铺地最后在母线电容的负极附近用一根粗铜线或者0欧电阻连接。H桥的走线要短而粗母线电容要尽量靠近H桥减小寄生电感。晶振要靠近MCU下面不要走任何信号线。ADC的参考电压引脚要加滤波电容最好用独立的LDO供电。4. 软件框架标准库、HAL库还是寄存器以及SPWM的具体实现4.1 开发环境搭建Keil、PlatformIO和STM32CubeIDE的取舍开发环境这块我三个都用过说说各自的适用场景。Keil MDK是传统方案资料多调试方便ST-Link支持好。缺点是界面老旧代码补全弱而且需要安装芯片包。如果你在学校或者公司里大家都用Keil那就随大流遇到问题好找人问。STM32CubeIDE是ST官方的免费基于Eclipse集成了CubeMX配置工具。优点是图形化配置外设生成初始化代码省去很多查手册的时间。缺点是Eclipse的调试体验不如Keil流畅而且生成的HAL库代码比较臃肿。PlatformIO是VS Code插件适合喜欢现代化开发环境的人。库管理方便支持多种框架编译速度快。缺点是对ST-Link的支持需要额外配置有时候会识别不到。我最终用的是Keil加标准库。原因很简单标准库的代码量小执行效率高而且我对F103的寄存器比较熟出了问题容易定位。HAL库虽然抽象层次高但在逆变器这种对时序敏感的场景下HAL的层层封装反而可能引入不确定性。注意如果你用Keil识别不到ST-Link先检查驱动装了没有。ST-Link的驱动有独立版本也有集成在Keil里的版本建议装独立的ST-Link Utility它自带驱动装完之后Keil一般就能识别了。另外Keil的Debug设置里要选ST-Link Debugger不是ST-Link Cortex-M Debugger这两个容易搞混。4.2 定时器配置中央对齐模式、死区时间和刹车功能TIM1的配置是整个软件的核心。我用的是中央对齐模式Center-Aligned Mode因为这种模式下PWM波形对称谐波特性比边沿对齐好而且ADC采样点可以安排在计数器过零点或者峰值点避开开关瞬间的噪声。具体配置步骤使能TIM1时钟和GPIO时钟配置CH1、CH1N、CH2、CH2N、CH3、CH3N对应的引脚为复用推挽输出。设置TIM1的预分频器PSC为0自动重装载寄存器ARR为1800中央对齐模式1时钟分频因子为1。配置通道1和通道2为PWM模式1使能互补输出设置死区时间。死区时间根据IGBT的开关特性来定FGA25N120的关断延迟大约200ns死区时间设500ns到1us比较稳妥。BDTR寄存器的死区时间计算公式是DTG[7:0]乘以tDTStDTS是定时器时钟周期72MHz下是13.9ns。设DTG72死区时间就是1us。使能刹车功能配置BKIN引脚为输入刹车极性为低电平有效刹车后输出状态为高阻或者低电平根据你的驱动电路来定。使能TIM1的预装载寄存器这样修改CCR值时不会立即生效而是在下一个更新事件时生效避免波形抖动。4.3 正弦表生成与SPWM查表逻辑正弦表的生成我用Python脚本算好直接生成C数组。256个点每个点的值是sin(2pii/256)归一化到0到ARR之间再乘以调制比。调制比一般取0.8到0.9留出余量给死区和电压调整。const uint16_t sin_table[256] { 900, 922, 944, 966, 988, 1010, 1032, 1054, // ... 省略中间数据 900, 878, 856, 834, 812, 790, 768, 746 };查表的逻辑放在TIM1的更新中断里。每个PWM周期中央对齐模式下更新事件发生在计数器溢出和过零时所以是每个PWM周期两次中断根据当前的正弦表索引取出两个相邻的值做线性插值然后写入CCR1和CCR2。CCR1控制CH1/CH1N这一对CCR2控制CH2/CH2N这一对。CH1和CH2的相位差180度所以CH2的索引是CH1索引加128256点表的一半。频率调整通过改变ARR值实现。比如要输出60HzARR值改为72MHz/(22000060/50)1500。但ARR改了之后正弦表的步进也要相应调整否则输出频率不对。我的做法是维护一个相位累加器每次中断累加一个步进值步进值等于256乘以输出频率除以载波频率。这样ARR固定只改步进值频率调整更平滑。4.4 ADC采样与闭环控制定时器触发加DMA搬运ADC采样必须和PWM同步否则采样到的是开关噪声不是真实的输出电压。我的做法是用TIM1的更新事件触发ADCADC转换完成后触发DMADMA把数据搬到内存数组里。这样CPU完全不用管采样只需要在需要的时候读取数组里的值。具体配置ADC1的规则通道设置为外部触发触发源选TIM1的TRGO事件。TRGO配置为更新事件。DMA设置为循环模式缓冲区大小和采样通道数一致。采样时间设为71.5个ADC周期因为霍尔传感器的输出阻抗不低采样时间太短会导致采样不准。闭环控制我用的是简单的PI控制器。目标电压是220V有效值采样回来的是瞬时值先做平方和累加算出一个周期内的有效值然后和设定值比较PI控制器的输出调整调制比。调制比变了正弦表的乘数就变了输出电压跟着变。PI参数我一开始用试凑法Kp从0.1开始Ki从0.01开始慢慢加。后来发现用临界比例度法更快先把Ki设为0Kp加到系统开始振荡记下此时的Kp和振荡周期然后Kp取一半Ki取0.8倍周期倒数的两倍。这样调出来的参数基本能用。5. 调试实录从波形畸变到带载掉压的完整排查链路5.1 空载波形正常带载就畸变地线环路在作怪第一版硬件调试的时候空载输出波形很漂亮正弦波干净THD用示波器看大概3%左右。但一接上负载哪怕只有50W的灯泡波形立刻畸变顶部和底部被削平像方波一样。排查过程是这样的先怀疑是PI参数问题把Kp和Ki都调小波形稍微好一点但还是畸变。然后怀疑是母线电容不够加了一倍电容改善不明显。接着用示波器看H桥的直流母线电压发现带载后母线电压有明显的纹波而且纹波频率是输出频率的两倍100Hz这是典型的单相逆变器二倍频纹波。但真正的问题不在这里。我用差分探头看H桥输出对地的电压发现带载后地线上有几十毫伏的高频噪声频率和开关频率一致。这说明功率地和信号地之间的隔离没做好开关电流在地线上的压降耦合到了ADC采样上导致PI控制器误判调制比乱调波形就畸变了。解决办法重新画了一版PCB把功率地和信号地严格分开单点连接。连接点选在母线电容的负极用一根2mm宽的铜线。同时ADC的参考电压引脚加了10uF和100nF的滤波电容霍尔传感器的输出加了RC低通滤波截止频率设在大约10kHz。改完之后带载波形和空载基本一致THD在5%以内。提示如果你没有差分探头可以用两个普通探头做数学运算但要注意共地问题。更好的办法是买一个隔离探头虽然贵但调逆变器的时候能省很多时间。5.2 死区时间设小了上下管直通的惨痛教训第二版硬件调试的时候我为了追求效率把死区时间从1us改到了200ns。理论上FGA25N120的关断延迟是200ns200ns的死区应该够。但实际跑起来带载到100W的时候H桥的一个桥臂炸了管子直接短路。拆下来测发现上下管的栅极驱动波形有重叠重叠时间大约100ns。这说明200ns的死区在实际电路中不够因为驱动电路的传输延迟、管子的开关时间离散性、以及温度影响都会让实际死区比设定值小。教训死区时间不能卡着管子的关断延迟设要留至少2到3倍的余量。我后来把死区时间设回1us并且在软件里加了死区时间自适应调整——根据母线电压和负载电流动态调整死区时间。轻载时死区小一点重载时死区大一点。这样既保证了安全又减少了死区带来的波形畸变。5.3 输出频率漂移晶振精度和定时器配置的双重检查有一批板子做出来之后发现输出频率不是精确的50Hz而是49.8Hz到50.2Hz之间漂移。用示波器测TIM1的PWM频率发现载波频率也有漂移。先查晶振。F103C8T6最小系统板上的晶振是8MHz的无源晶振负载电容是20pF。用频率计测晶振输出发现实际频率是7.998MHz到8.002MHz之间波动。这个精度对于串口通信没问题但对于逆变器的频率精度要求一般要求±0.5%以内就有点勉强。解决办法有两个一是换更高精度的晶振比如10ppm的温补晶振二是用STM32内部的HSI时钟虽然精度更差但可以通过外部参考信号校准。我选的是换晶振成本增加两块钱频率稳定在49.99Hz到50.01Hz之间。另外定时器的配置也要检查。中央对齐模式下ARR的值和实际PWM频率的关系是fPWM fCLK / (2 * ARR)。如果ARR是1800fCLK是72MHzfPWM就是20kHz。但如果时钟配置有问题比如倍频系数不对fCLK不是72MHz那PWM频率就会偏。用示波器测一下PWM引脚的实际频率和理论值对比就能确认。5.4 过流保护误触发比较器阈值和滤波的配合过流保护用的是比较器加TIM1刹车。调试的时候发现带载启动的瞬间过流保护经常误触发导致逆变器启动失败。用示波器看电流传感器的输出发现启动瞬间有一个尖峰幅度超过了阈值但持续时间很短大概几微秒。这是典型的浪涌电流。逆变器带感性负载或者容性负载启动时电流会有一个冲击。如果过流保护响应太快就会误触发。解决办法是在比较器前面加一个RC低通滤波截止频率设在大约1kHz把尖峰滤掉。同时在软件里加一个启动延时启动后的前100ms内过流阈值放宽到1.5倍等启动完成后再恢复正常阈值。这样既保证了保护功能又避免了误触发。6. 那些让方案真正成熟的工程细节6.1 散热设计小功率也不能忽视小功率逆变器虽然功率不大但H桥的开关损耗和导通损耗加起来在满载时也有十几瓦。如果不加散热片IGBT的结温会很快超过150度然后热击穿。我的做法是给四个IGBT加一个铝制散热片散热片的热阻要小于2度每瓦。同时在散热片上贴一个NTC热敏电阻接到STM32的ADC做温度监测。温度超过80度时降额运行输出功率降到一半超过100度时直接关机保护。风扇的话小功率逆变器可以用自然冷却但如果外壳封闭最好加一个小风扇5V的涡轮风扇就够了噪音也不大。6.2 电磁兼容输入输出滤波器的设计逆变器的开关频率是20kHz谐波会传导到输入和输出线上影响其他设备。输入侧加一个共模电感加X电容和Y电容的EMI滤波器输出侧加一个LC低通滤波器截止频率设在大约2kHz把20kHz的开关谐波滤掉。LC滤波器的参数计算截止频率f 1/(2pisqrt(L*C))。要滤掉20kHz截止频率设2kHzL取1mHC取6.3uF。实际用1mH的电感和两个3.3uF的CBB电容并联。电感要用铁硅铝磁环饱和电流要大于最大输出电流。6.3 参数保存与掉电恢复EEPROM还是Flash模拟逆变器的输出电压、频率、保护阈值这些参数需要掉电保存。F103C8T6没有内置EEPROM但可以用Flash模拟。ST官方有Flash模拟EEPROM的库但用起来比较麻烦。我直接用了一个外部的I2C EEPROM型号AT24C02两块钱一个容量256字节够用了。保存策略是参数修改后延时1秒再写入EEPROM避免频繁写入。写入时先写一个备份区再写主区读取时先读主区校验失败再读备份区。这样即使写入过程中掉电也不会丢失参数。6.4 固件加密保护你的劳动成果如果你打算把这个方案做成产品固件加密是必须的。F103C8T6支持读保护RDP可以通过设置选项字节来启用。启用读保护后通过调试器无法读取Flash内容只能整片擦除。但要注意启用读保护后如果程序跑飞了想重新烧录就得先擦除整片比较麻烦。更高级的加密可以用外部加密芯片或者把关键算法放在外部存储器里通过加密通信读取。但对于小功率逆变器这种成本敏感的产品读保护基本够用了。7. 关于这套方案我最后想说的几件事这套小功率逆变器的方案我从第一版炸管到最后一版稳定运行前前后后改了五版硬件软件重构了三次。最大的体会是逆变器这个东西原理不难难的是工程细节。地线怎么走、死区怎么设、保护怎么加、散热怎么做每一个细节都可能决定成败。如果你正在做类似的项目我的建议是第一版硬件不要追求小体积把该留的测试点都留出来该加的滤波都加上先跑通再优化。软件上先把开环跑通波形对了再加闭环闭环稳了再加保护。不要一上来就想着把所有功能都做进去那样出了问题你根本不知道是哪里的问题。另外调试的时候一定要有示波器最好是四通道的能同时看PWM、电流、电压和温度。没有示波器调逆变器就像闭着眼睛修车全靠猜。这套方案的完整工程文件包括原理图、PCB、源代码和正弦表生成脚本我整理好放在了文末的下载链接里。代码是基于标准库的Keil工程可以直接编译下载。硬件是Altium Designer格式方便你直接改。如果你在复现的过程中遇到问题欢迎在评论区交流我看到都会回。