
简介这是一套基于STM32F103微控制器和IR2104半桥驱动芯片的三相逆变器系统设计方案面向电力电子与嵌入式方向开发者聚焦直流转三相交流过程中的SPWM波形生成、死区时间控制、三相桥式电路构建与反馈调节机制等关键问题。压缩包共一百五十六个文件大小约二点四四兆字节以Keil工程资源为主包含STM32标准外设库头文件与C源码、汇编启动文件、工程配置及编译生成文件并附有说明文档便于对照源码理解设计细节。目前已有133人学习下载适合正在学习逆变器控制算法、准备课程设计或项目开发的读者参考。资源内可查看SPWM调制与死区设置的软件逻辑以及IR2104半桥驱动电路与反馈环路的配合实现帮助快速上手三相逆变器开发理解从算法到电路落地的完整流程。1. 先把三相逆变器的控制难点说透调试三相逆变器时最先暴露问题的地方往往不是功率管烧毁而是上下桥臂直通。STM32F103 主频 72MHz做 SPWM 调制绰绰有余但真正决定系统能否长期稳定运行的是死区时间控制能不能在每个开关周期都可靠生效。IR2104 半桥驱动负责把 PWM 信号转换成高低边 MOS 管的驱动电压但它自己不产生死区死区必须由 MCU 的互补 PWM 输出保证。这个设计把 SPWM 波形生成、死区时间控制、三相桥式逆变电路和反馈调节机制串成一条完整链路适合做电机驱动、变频电源、UPS 或三相 DC-AC 电源的工程师参考。下面按从调制算法到闭环控制的顺序拆开讲。2. SPWM 生成算法与 STM32F103 定时器映射2.1 对称规则采样 SPWM 比实时计算更适合嵌入式SPWM 的核心是正弦波与三角载波比较。实时计算正弦值在 10kHz 载波下虽然来得及但会占用大量 CPU 时间留给反馈调节的余量就不够了。常见做法是离线生成一张正弦表运行时查表并映射到定时器的比较寄存器。对称规则采样在三角载波顶点处采样调制波计算量小且输出脉冲关于载波周期中心对称谐波性能比不对称采样略好适合三相逆变器这种需要三路同步调制的场景。载波频率一般选 10kHz 到 20kHz。频率太高IR2104 自举电容的充电时间不足频率太低输出滤波器体积变大。我一般把载波设在 15kHz 左右既在听觉范围之外又留足裕量。正弦表长度取 768 点不仅相位分辨率高而且能被 3 整除三相相位间隔天然准确。如果想进一步压低谐波三电平 NPC 是另一个方向但对 STM32F103 和驱动电路复杂度的要求都会上一个台阶。2.2 TIM1 的互补通道映射关系与定时器初始化STM32F103 里只有高级定时器能输出带死区的互补 PWM。以常见的 STM32F103C8T6 最小系统为例TIM1 的 CH1/CH2/CH3 分别对应 U、V、W 相上桥臂反相通道 CH1N/CH2N/CH3N 对应下桥臂。如果是高密度型号TIM8 也可以作为第二组输出但三相桥用一个 TIM1 就够了。通道映射关系如下TIM1 通道上桥臂输出下桥臂输出对应相CH1 / CH1NCH1CH1NU 相CH2 / CH2NCH2CH2NV 相CH3 / CH3NCH3CH3NW 相初始化时要把主输出使能TIM_CtrlPWMOutputs(TIM1, ENABLE)打开否则即使TIM_Cmd使能引脚上也不会有波形。下面是基于标准外设库 V3.5.0 的 TIM1 基础配置TIM_TimeBaseInitTypeDef sTB; TIM_OCInitTypeDef sOC; TIM_BDTRInitTypeDef sBD; sTB.TIM_Prescaler 72 - 1; // 72MHz / 72 1MHz 计数时钟 sTB.TIM_CounterMode TIM_CounterMode_CenterAligned1; sTB.TIM_Period 32; // 中心对齐1MHz / (2*(321)) ≈ 15.15kHz sTB.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM1, sTB); sOC.TIM_OCMode TIM_OCMode_PWM1; sOC.TIM_Pulse 0; // 运行时由 SPWM 更新 sOC.TIM_OutputState TIM_OutputState_Enable; sOC.TIM_OutputNState TIM_OutputNState_Enable; sOC.TIM_OCPolarity TIM_OCPolarity_High; sOC.TIM_OCNPolarity TIM_OCNPolarity_High; sOC.TIM_OCIdleState TIM_OCIdleState_Reset; sOC.TIM_OCNIdleState TIM_OCNIdleState_Reset; TIM_OC1Init(TIM1, sOC); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);这里最容易弄错的是中心对齐模式下的频率公式。计数器先向上再向下一个完整周期等于 2 倍的TIM_Period1所以 PWM 频率不是1MHz / (321)而是1MHz / (2*(321))。TIM_Prescaler71只是把 72MHz 分频成 1MHz 计数时钟不会改变死区时基这一点到第 3 章死区配置时尤其重要。2.3 三相互差 120° 的调制波生成方法三相输出电压要求相位互差 120°。查表实现时如果表长度取 256间隔 85.33 无法整除会引入固定相位误差。所以我把表长度取为 768每相间隔正好 256 点。在定时器更新中断里按索引更新三个比较寄存器#define SPWM_TABLE_SIZE 768 #define TIM_PERIOD 32 // 初始化时生成正弦表偏移到 0~1 范围 for (int i 0; i SPWM_TABLE_SIZE; i) { float v 0.5f 0.5f * sinf(2.0f * 3.14159265f * i / SPWM_TABLE_SIZE); sin_table[i] (uint16_t)(v * (TIM_PERIOD - 2)); } // 更新中断中按调制度计算三相比较值 uint32_t u_idx phase_acc; uint32_t v_idx phase_acc SPWM_TABLE_SIZE / 3; uint32_t w_idx phase_acc 2 * SPWM_TABLE_SIZE / 3; uint32_t cmp_u (uint32_t)(sin_table[u_idx % SPWM_TABLE_SIZE] * modulation_index); uint32_t cmp_v (uint32_t)(sin_table[v_idx % SPWM_TABLE_SIZE] * modulation_index); uint32_t cmp_w (uint32_t)(sin_table[w_idx % SPWM_TABLE_SIZE] * modulation_index); TIM_SetCompare1(TIM1, cmp_u); TIM_SetCompare2(TIM1, cmp_v); TIM_SetCompare3(TIM1, cmp_w); phase_acc PHASE_STEP;sin_table里的值在 0 到TIM_PERIOD-2之间目的是给比较值留出 1~2 个计数余量避免占空比极端情况。modulation_index是 0~1 的调制度它最终决定输出线电压基波幅值。PHASE_STEP与基波频率的关系是在中心对齐模式下更新事件频率为2*fc所以基波频率 2*fc * PHASE_STEP / SPWM_TABLE_SIZE。表运行时用phase_acc累加配合查表可以平滑改变频率不会出现相位跳变。代码用sinf生成表实际工程可以提前用脚本生成 const 数组避免运行时依赖浮点数学库。3. IR2104 半桥驱动与死区时间控制落地3.1 IR2104 自举电路的工作条件IR2104 的高端驱动电压来自自举电容。PWM 低电平期间VS 被拉到功率地VCC 通过自举二极管给电容充电高电平期间电容为高端驱动电路供电。这就要求每个开关周期都有足够的低电平时间给电容充电载波频率越高充电时间越短。自举电容取值通常用Cbs Qg / dVbs估算Qg 是上管栅极电荷dVbs 是允许的电压跌落。以 IRF540 为例Qg 约 70nC允许跌落 0.3V计算得到约 233nF取 470nF 比较稳妥。电容太小会触发 IR2104 的欠压锁定导致 HO 输出关断电容太大则充电时间常数过大启动时可能出现高端驱动不足。我一般用 470nF 陶瓷电容并联一个 0.1µF 高频电容放在 IR2104 的 VB 与 VS 之间离芯片尽量近。如果母线电压较高自举二极管需要选快恢复管不能用普通整流二极管。3.2 死区时间由定时器生成而不是靠软件延时软件插入延时看起来可行但中断响应、指令乱序和不同编译优化都会让死区时间抖动严重时出现某些周期没有死区。正确做法是使用 TIM1 的 BDTR 寄存器由硬件在互补输出之间强制插入死区。STM32F103 的死区步长tDTS来自定时器内部时钟由TIM_ClockDivision控制与计数器预分频无关。当 APB2 为 72MHz 且CKDDIV1时tDTS 1/72MHz ≈ 13.89ns。设置 1µs 死区时DTG 72即二进制0b01001000落在DTG[7:5]0b010区间死区时间等于(64 DTG[4:0]) * tDTS也就是(648)*13.89ns ≈ 1µs。所以TIM_DeadTime 0x48。注意不要把计数器的 1MHz 周期 1µs 当成死区步长那样会算偏一个数量级。TIM_BDTRInitTypeDef sBD; sBD.TIM_OSSRState TIM_OSSRState_Enable; sBD.TIM_OSSIState TIM_OSSIState_Disable; sBD.TIM_LOCKLevel TIM_LOCKLevel_1; sBD.TIM_DeadTime 0x48; // (648) / 72MHz 1us sBD.TIM_Break TIM_Break_Enable; sBD.TIM_BreakPolarity TIM_BreakPolarity_High; sBD.TIM_AutomaticOutput TIM_AutomaticOutput_Disable; TIM_BDTRConfig(TIM1, sBD); TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE);这里把刹车功能也打开了过流保护时会用到。死区寄存器给出的是定时器输出引脚上的死区经过 IR2104 输入到输出的传播延迟后实际 MOSFET 栅极上的死区会变化。尤其是 HIN 到 HO 的延迟与 LIN 到 LO 的延迟有差异所以最后必须用示波器实测。3.3 三相桥式逆变电路接线与电源处理三相桥由三个半桥组成每个半桥用一个 IR2104。IR2104 的 HIN 接 TIM1_CH1LIN 接 TIM1_CH1NSD 引脚接高电平或接保护 GPIO。HO 驱动上管栅极LO 驱动下管栅极HS 输出点接上管源极和下管漏极也就是半桥中点。栅极串联 10Ω 到 22Ω 电阻抑制栅极驱动振荡。母线电容放在桥臂附近用 220µF 电解电容并联 0.1µF 薄膜电容吸收开关瞬态电流。电源地的处理是很多 stm32f103 最小系统方案容易踩坑的地方。MCU 的逻辑地和功率地必须在母线电容负极单点连接如果形成环路IR2104 会因地弹产生闩锁轻则重启重则烧驱动芯片。工程包里看到的stm32f10x_tim.c、stm32f10x_rcc.c是标准外设库 V3.5.0 的源文件建议编译选项里打开 MicroLIB中断里的浮点运算会更省资源。4. 反馈调节机制从 ADC 采样到 PI 调压4.1 输出电压采样与 ADC 触发时刻闭环控制需要实时知道输出电压。逆变器输出是 PWM 方波经 LC 滤波后的正弦波不能直接接入 STM32F103 的 ADC。常见做法是先用电阻分压把电压降到 ADC 范围然后用运放差分电路去除共模再叠加 1.65V 偏置把双极性正弦波抬到 0~3.3V。如果要求电气隔离需要线性光耦或隔离放大器但带宽要跟上 10kHz 的 PWM 开关频率普通光耦不够用。采样时刻直接影响反馈精度。用 TIM1 的 TRGO 触发 ADC 注入组转换可以在 PWM 中心附近采样避开开关瞬态。核心配置如下ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_TRGO); ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE); ADC_ExternalTrigConvCmd(ADC1, DISABLE);配置后不再用软件启动 ADC每次定时器更新事件自动发起注入组转换。注入组转换完成后读结果即可获得输出电压瞬时值。对 50Hz 基波一个周期内有几百个采样点相位延迟很小适合做电压闭环。4.2 增量式 PI 调节器实现反馈调节的本质是把输出电压采样值与参考值比较通过 PI 控制器调整调制度。增量式 PI 只需要保存上一次误差计算量小适合在定时器更新中断里执行。typedef struct { float kp; float ki; float out_min; float out_max; float err_last; float out; } PI_Controller; float PI_Update(PI_Controller *pi, float ref, float fb) { float err ref - fb; float delta pi-kp * (err - pi-err_last) pi-ki * err; pi-out delta; if (pi-out pi-out_max) pi-out pi-out_max; if (pi-out pi-out_min) pi-out pi-out_min; pi-err_last err; return pi-out; }kp决定对误差变化速度的响应ki决定对累积误差的修正。整定时先把ki设为 0逐步增加kp到输出电压开始振荡再把kp乘 0.6然后加入ki消除稳态误差。out_min和out_max限制在 0.1 到 0.9避免过调制导致谐波激增。注意 PI 输出的是调制度要作用到第 2 章的modulation_index上而不是直接改定时器周期。4.3 过流保护与故障封锁快速保护不能等软件判断要利用 TIM1 的刹车输入。当输出过流比较器或电流采样运放给出故障电平时BKIN引脚立即关断互补 PWM不需要 CPU 参与。配置中TIM_AutomaticOutput_Disable的作用是刹车信号消失后 PWM 不会自动恢复必须由软件清理标志并重新使能 MOE防止故障未排除时反复重启。参数典型值说明SPWM 载波15kHz高于音频低于自举驱动极限死区时间1µs根据 MOSFET 关断延迟调整自举电容470nF按上管 Qg 估算电流保护阈值1.2 倍额定电流比较器输出接 TIM1_BKIN输出滤波电感1mH ~ 3mH与负载功率和基波频率有关刹车动作后要刹车标志清掉再通过TIM_CtrlPWMOutputs(TIM1, ENABLE)重新打开输出。SD 引脚同样能关断 IR2104但软件响应需要几百纳秒到几微秒与硬件刹车不在一个量级所以优先级最高的保护路径应该是 BKIN 引脚。5. 用示波器验证 SPWM 与死区时间的一个实用技巧SPWM 和死区配置完成后光看程序里的寄存器值还不够IR2104 的输入到输出有传播延迟必须实测 MOSFET 栅极信号。把示波器 CH1 接 U 相上桥臂栅极CH2 接下桥臂栅极地线用接地弹簧而不是长地线夹否则地线环路电感会造成测量波形振铃甚至误判为桥臂直通。触发方式设为上升沿触发在 CH1时基先放到 10µs 看整个 PWM 周期再放大到 1µs/div。用光标测量下桥臂关断沿到上桥臂导通关沿之间的低电平区间这个区间就是实际死区时间。注意要分别测上桥臂先关断和下桥臂先关断两种情况因为 IR2104 高低侧传输延迟不同死区会不对称。如果示波器有逻辑运算功能把 CH1 和 CH2 做AB运算可以直接看到两路同时为高的时间。正常情况下为零出现毛刺说明死区配置不够或者探头地线环路造成串扰。另一个实用做法是触发模式设为 Normal触发电平放在母线电压一半连续抓 100 个开关周期记录最小死区值。STM32F103 的 DTG 寄存器只是定时器输出点的死区经过 IR2104 后还要扣除高低侧延迟差在低温或大电流下死区会进一步缩小所以留 20% 裕量是合理的。记录不同母线电压和温度下的死区边缘抖动把存储深度开到最大统计 100 个周期的最小死区值再决定是否在 PI 输出侧加入死区补偿。本文还有配套的精品资源点击获取