简介:一套基于STM32F103C8T6单片机的三相SPWM逆变电源完整设计资源,涵盖Altium Designer硬件工程与Keil软件源码,面向嵌入式、电力电子方向的学习者,也可供逆变器项目开发者作为参考模板,解决三相SPWM逆变电源从原理图设计、PCB布局到程序调试的完整工程需求。资源包共384个文件,整体约30.42MB,其中包括原理图、PCB封装库、C/H源码文件,PDF芯片资料与参考论文,Excel元件清单,Word硬件结构框图和程序流程图,以及编译生成的HEX固件,文件类型覆盖从硬件参考到软件烧录的各个环节。目前已有598人学习下载。配套的实物照片能辅助硬件对照,Keil5源码工程采用标准库编写,易于阅读和二次开发,结合定时器、ADC与SPWM调制实现三相逆变输出,适合课程设计、毕业设计或工程预研时快速搭建和验证方案,学习价值与复用性都比较高。
1. 三相SPWM逆变电源板:从STM32到强电输出的一整条链路
第一次做逆变电源的人,最容易卡住的位置往往不在电路本身:原理图能看懂,可一旦要把三相SPWM驱动信号变成能带负载的强电输出,母线电容、驱动芯片、死区时间和软件保护便全部耦合在一起。这套以STM32F103C8T6单片机为控制核心的三相SPWM逆变电源板,交付物是Altium Designer完成的原理图、PCB、BOM三件套,外加软件源码和文档资料,正好把"主控生成SPWM → 驱动隔离 → 三相桥功率变换 → 滤波输出"这条链路完整打通。对做变频器预研、伺服驱动器或UPS的电源工程师来说,能省掉从头起工程的时间,直接在这个骨架上改电压等级和功率;对需要完整毕业设计落地的单片机方向学生,它也是一份从算法到PCB都可复现的工程样例。F103C8T6虽然只有64KB Flash,但72MHz主频配上带互补输出与刹车功能的高级定时器,做中低频三相SPWM逆变是性价比非常稳的组合。
2. SPWM 调制原理与 STM32F103C8T6 的定时器实现路径
2.1 面积等效与查表法:SPWM 生成的核心思路
SPWM(正弦脉冲宽度调制)的物理基础是面积等效原理:在半个正弦波周期内,把正弦曲线分成若干个小区间,每个区间用一个宽度与该区间正弦面积成正比的矩形脉冲替代。只要脉冲序列的基波分量与原正弦一致,经过 LC 低通滤波后就能还原出正弦波。工程上几乎不会实时计算三角函数,普遍做法是预先生成一个正弦表,运行时按载波周期查表更新比较寄存器,也就是查表法。
三相与单相的本质区别在于相位。A 相为零相位起点,B 相滞后 120°,C 相滞后 240°,对应查表就是同一个正弦表分别用 0、1/3 周期、2/3 周期的索引偏移量取值。调制方式选中心对齐 PWM:计数器从 0 增至 ARR 再从 ARR 减至 0,输出脉冲前后沿对称,输出谐波含量低于边沿对齐模式,三相逆变的主流做法都用它。标准的 256 点正弦表,半周期 128 点,B 相偏移量理论值为 85.33。常见做法是直接取 85,但相位误差会随波形累积,更严谨的方案在 2.4 节给出。
2.2 高级定时器 TIM1:互补输出、死区与刹车三个硬件基础
STM32F103C8T6 的 TIM1 和 TIM8 属于高级控制定时器,CH1/CH2/CH3 可以配置成互补输出,恰好对应三相桥的三个半桥。互补输出的意义在于:同一桥臂上下两个功率管不能同时导通,否则直流母线会直接短路烧毁器件,所以必须在两路互补波形之间插入死区时间。TIM1 的硬件死区发生器通过 DTG 寄存器实现,不依赖中断响应速度,比在中断里软件翻转可靠得多。
| PWM 信号 | 定时器通道 | F103C8T6 引脚 | 连接目标 |
|---|---|---|---|
| A 相上管 | TIM1_CH1 | PA8 | A 桥臂驱动 HIN |
| A 相下管 | TIM1_CH1N | PB13 | A 桥臂驱动 LIN |
| B 相上管 | TIM1_CH2 | PA9 | B 桥臂驱动 HIN |
| B 相下管 | TIM1_CH2N | PB14 | B 桥臂驱动 LIN |
| C 相上管 | TIM1_CH3 | PA10 | C 桥臂驱动 HIN |
| C 相下管 | TIM1_CH3N | PB15 | C 桥臂驱动 LIN |
刹车功能是第二个关键点。TIM1 的 BKIN 引脚或软件刹车事件可以把所有 PWM 输出强制置为预设电平,硬件响应在纳秒到微秒级,远快于"先进中断再关 PWM"的软件路径。过流、过压这类故障信号通过比较器直接接 BKIN,比软件保护快一个数量级。第三个能力是 DMA 请求:三相 SPWM 每个载波周期要更新三个比较寄存器,用 DMA 从正弦表连续搬运后,CPU 只负责调整调制比和处理故障,主循环还能跑 LCD 或者 Modbus 帧收发逻辑。
2.3 最小可运行的 SPWM 内核代码与死区参数整定
下面是这套板载软件里 TIM1 产生三相 SPWM 的最小配置框架,核心代码都贴出来:
// 系统时钟72MHz,TIM1挂在APB2上,定时器时钟72MHz void TIM1_SPWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; // 1. PA8/PA9/PA10为CH1/CH2/CH3,PB13/PB14/PB15为互补通道 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_TIM1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); // 2. 分频72,计数时钟1MHz;中心对齐模式,ARR=99 // PWM频率 = 1MHz / (2 * (ARR+1)) = 5kHz TIM_TimeBaseStructure.TIM_Prescaler = 71; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; TIM_TimeBaseStructure.TIM_Period = 99; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // 3. 三个通道PWM模式1,互补输出全部使能 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = 50; // 初始占空比50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC2Init(TIM1, &TIM_OCInitStructure); TIM_OC3Init(TIM1, &TIM_OCInitStructure); // 4. 死区配置:DTG[7:0]=36时,死区≈36*13.9ns≈500ns // DTG=72时进入第二档,(64+8)*2*13.9ns≈2us TIM_BDTRInitStructure.TIM_DeadTime = 36; // MOS管先按500ns起步 TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Disable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Disable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable; TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low; TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure); // 5. 使能主输出、更新中断和NVIC TIM_Cmd(TIM1, ENABLE); TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }死区这块特别容易改错:DTG 从 0 到 63 时死区时间等于DTG × 13.9ns,DTG=36 对应约 500ns;想从 2us 调回 1us,DTG 不是简单的 144 改 72,而是 72 进入第二档公式,算出约 2us。上电前务必用示波器实测死区再合母线电,这一步永远不要跳。调制比抬升靠修改TIM_Pulse,从 50 改到 80 后占空比变大,逆变器输出基波幅值升高,这就是"调压"的本质。
提示:初次上电时在 BKIN 引脚上留一个外部跳线到地,方便测试刹车功能,确认 MOE 能可靠拉低所有 PWM 输出后再进入带载测试。
2.4 三相相位用 32 位相位累加器消除 120° 取整误差
固定 256 点表时,B 相偏移 256/3=85.33,取 85 之后每个载波周期会有约 0.33 个量化单位的相位误差,几百个周期后三相相位差就不是 120°。更稳的做法是用 32 位相位累加器,120° 对应的累加量是0xFFFFFFFF / 3,这样相位差是精确的,只是取值时把高字节作为查表索引:
#define TABLE_SIZE 256 static const uint16_t sin_table[TABLE_SIZE] = { // 由脚本生成,200~0之间的一圈正弦值 }; volatile uint32_t phase_acc = 0; // 120°/240°在32位累加器中的精确偏移 #define PHASE_B ((uint32_t)(0xFFFFFFFF / 3)) #define PHASE_C ((uint32_t)(0xFFFFFFFF * 2 / 3)) void TIM1_UP_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM1, TIM_IT_Update); phase_acc += PHASE_STEP; // PHASE_STEP = 基波频率 * 2^32 / 载波频率 uint8_t idx = (phase_acc >> 24) & 0xFF; TIM_SetCompare1(TIM1, sin_table[idx]); TIM_SetCompare2(TIM1, sin_table[(idx + (PHASE_B >> 24)) & 0xFF]); TIM_SetCompare3(TIM1, sin_table[(idx + (PHASE_C >> 24)) & 0xFF]); } }PHASE_STEP决定输出基波频率:载波 5kHz、要出 50Hz 时,PHASE_STEP = 50 * 4294967296 / 5000 ≈ 42949673。每次累加一个固定步进,查表索引就是累加器高 8 位,频率连续可调且不会因为取整丢相位。这个写法在 F103C8T6 上开销很小,整型加法加移位,72MHz 跑 20kHz 载波也毫无压力。注意sin_table用uint16_t还是uint8_t存储影响不大,但比较寄存器赋值前要确认正弦表中心值对应 50% 占空比,否则初始输出可能直接饱和。
3. Altium Designer 三件套落地:原理图、PCB 与 BOM 的工程化要点
3.1 原理图设计:主控最小系统、驱动隔离与采样通道
这套板子的原理图在 Altium Designer 里主要分成四个模块:STM32F103C8T6 最小系统、三相驱动与功率桥、电压电流采样调理、防护与电源转换。最小系统里最容易出错的是晶振电路。负载电容计算公式是CL = (C1*C2)/(C1+C2) + Cs,Cs 为引脚寄生电容约 2~5pF。8MHz 晶振配两个 20pF 电容是经验值,等效负载电容约 10pF 加寄生后接近 12~15pF,起振后用示波器夹 XO 引脚确认幅度不低于 0.4 倍 VDD,否则要调整电容。F103C8T6 的 NRST 加 100nF 去耦电容,BOOT0 下拉到地,VDDA 通过磁珠或 10Ω 电阻接 3.3V 并加 1uF+100nF 两级去耦,这些都是老生常谈但漏掉一个就会出诡异复位。
驱动部分值得花最多时间。三相 SPWM 板常用两类驱动方案,选型差异直接影响 BOM 成本和 PCB 面积:
| 方案 | 代表器件 | 供电方式 | 适用功率 | 设计要点 |
|---|---|---|---|---|
| 自举驱动 | IR2101S、IR2110S | 单 15V 电源 + 自举电容 | 300W 以下 | 自举电容选 10uF 钽电容并联 100nF,死区要大于自举电容充电时间 |
| 隔离驱动 | TLP250、HCPL3120 + 隔离电源 | 多路隔离电源 | 300W 以上 | 需独立驱动绕组或隔离电源模块,成本高但抗干扰强 |
自举方案在低功率板里最常见:一个 IR2101S 管一个桥臂,三颗芯片加一路 15V 电源就能驱动整个三相桥。自举电容与下管导通时间相关,载波频率低到下管导通时间不足时,上管会因电容欠压关断不彻底,这是低频大占空比时波形畸形的常见原因。IR2110S 的 VB 和 VS 之间建议并一颗快恢复二极管如 US1M,防止桥臂中点负压把上管驱动拉死。采样通道方面,母线电压用电阻分压后经运放跟随到 ADC,输入范围不要超 3.3V;相电流用 ACS712 霍尔传感器或采样电阻加 INA240 差分放大器,温度保护用 NTC 贴在 MOS 散热器上。这些信号进 ADC1 后,注意别跟 DMA 循环搬运的缓冲区冲突,否则会出"数据怎么老是旧的"的怪问题。
3.2 PCB 布局:强电功率环路、地平面分割与异形封装
三相逆变电源板的 PCB 工作环境比普通数字板恶劣很多,Altium 布局时我会按下面几个约束顺序处理。第一,功率回路面积必须最小化:上管→负载→下管构成的高频电流环路,包围面积一旦偏大,环路电感会带来很高的 di/dt 尖峰,MOS 的 Vds 应力成倍上抬。布局上让母线电容紧贴三相桥的直流输入端子,能明显压低尖峰。第二,控制地与功率地单点连接:STM32 和驱动芯片的信号地只在母线电容负极处一点相连,不要用大面积铜皮把两个地直接铺成一体,否则强电侧噪声会直接灌进 MCU 的参考地。
第三,驱动芯片要紧贴 MOS 栅极。IR2101S 输出到被驱动管的栅极、源级回路越短越好,栅极电阻放在驱动芯片侧,串 10Ω 到 22Ω 阻尼电阻抑制振铃。完全没有栅极电阻或阻值过小,关断瞬间的 Vgs 尖峰会超过 ±20V 直接打穿栅氧化层。第四,SPWM 信号线加粗并被地包住,从 STM32 到 IR2101S 的 HIN/LIN 走太长容易被强电侧耦合干扰,串 100Ω 电阻是常规保险。第五,晶振远离功率 MOS 和电感,8MHz 晶振如果放在功率电感附近,停振或频率偏移很难排查,这类故障比软件 bug 难定位得多。
功率 MOS 的散热焊盘在 Altium 里做封装时,可以用铜区域配合过孔阵列实现异形封装:在 PCB 库编辑器中用放置填充或铜区域画出焊盘外形,再放一排规则排列的过孔到背面铜箔,注意用 Paste Mask Expansion 控制钢网开孔比例,防止贴片时锡膏挤出短路。两层板在 4.5cm 见方内能装下整套系统,功率地尽量整片填充;功率超 500W 建议四层板,中间两层分别做地和电源平面,散热焊盘过孔阵列可以直接扩大散热面积。
3.3 BOM 选型参数:电压余量、电流与封装命名陷阱
BOM 是 Altium 工程里最容易出交付问题的环节。三相 SPWM 板的关键器件选型给出一个可参考的规格范围:
| 器件 | 推荐参数 | 说明 |
|---|---|---|
| 母线电解电容 | 450V/220uF × 2 | 220V 交流整流后母线约 311V,耐压留 1.5 倍裕量 |
| 三相整流桥 | 35A/1000V | 留足浪涌电流冲击余量 |
| 功率 MOS | 500V/20A 或 600V/15A | 对应 220V 输入、300W 级输出 |
| 自举二极管 | US1M,1A/1000V 快恢复 | 用普通 1N4007 在 20kHz 下会过热 |
| 栅极电阻 | 10Ω~22Ω,1/4W | 开关速度与 EMI 的折中 |
| 光耦(隔离方案) | HCPL3120 | 峰值输出电流 2A,驱动大栅容 MOS 不拖尾 |
BOM 导出时注意 Altium 的封装命名习惯:贴片阻容用英制时 0603 对应公制 1608,库封装的注释没写清R0603还是C1608,发给 SMT 厂很容易大批量错料。另一个坑是电感类元件,BOM 里必须同时给感量和额定电流,只写"10mH"不写饱和电流的话,带载后电感饱和,SPWM 输出波形立刻变形。Altium 的 BOM 模板可以加一列Manufacturer Part Number,出给采购时保留这一列能少往返好几次。
4. 软件源码关键模块:正弦表生成、死区补偿与保护联动
4.1 用脚本生成三相正弦表与载波比的选取逻辑
单片机里手敲 256 个正弦值不现实,这套工程的软件源码里通常用一个 Python 脚本生成数组,再粘贴进 C 文件。常见的生成逻辑如下:
import math N = 256 # 表长度 amp = 80.0 # 幅值,留出调制比余量 offset = 99.0 # 中心值,对应50%占空比 for i in range(N): s = offset + amp * math.sin(2 * math.pi * i / N) print(int(round(s)), end=', ') if (i + 1) % 16 == 0: print()中心值 99 而不是 100,是因为 ARR=99 时占空比按compare / ARR计算,表幅值若到 100 会出现一段平台饱和,调制线性区变窄。幅值取 80 时最大调制比约(99+80)/(2*99)≈0.9,实际控制在 0.8 以下最稳妥。
载波比的选取要同时看基波频率和开关损耗,下面给出一组常用对照:
| 基波频率 | 载波频率 | 载波比 N | 说明 |
|---|---|---|---|
| 50Hz | 5kHz | 100 | 同步调制,三相对称性好 |
| 50Hz | 10kHz | 200 | 谐波更小,开关损耗更高 |
| 400Hz | 20kHz | 50 | 中频输出应提高载波,避免载波比过小 |
同步调制下 N 必须是 3 的整数倍才能保证三相严格对称,50Hz/5kHz 组合载波比 100 恰好满足。F103C8T6 的 64KB Flash 在完整工程里约占用 30~40KB,正弦表用const uint8_t存储可以省一半空间,换来的查表速度损失可以忽略。
4.2 死区时间对 THD 的影响与软件补偿手段
死区是保护性的,但它本身会带来非线性。死区时间内桥臂上下管全关断,负载电流只能靠续流二极管续流,输出电压相比理想 SPWM 会损失一个与电流方向相关的电压误差。死区越长,基波损失越大,谐波畸变越明显。实验里常见的现象是空载时三相波形很漂亮,一带感性负载波形开始出现削顶和毛刺,很多情况下不是电源的问题,而是死区效应随负载电流加大被放大了。
对这套软件源码而言,死区补偿通常有两种做法。一种是在调制波上叠加补偿量:检测相电流方向,电流为正时该相占空比补偿 +Δ,电流为负时补偿 -Δ,Δ 对应死区时间内损失的伏秒数。这套板子如果没有加电流传感器,就用第二种方案:把死区时间调到器件允许的极限,比如快速 MOS 管 500ns 到 1us,前提是实测确认不直通。IGBT 则要留 2~5us,不能照搬 MOS 的参数。想给毕业设计加分的,可以在源码里保留一个deadtime_compensation的开关变量,对比开和关两种情况下输出电压总谐波失真的差异,这一部分作为展示理解深度很有说服力。
4.3 过流过压保护与刹车中断的联动流程
F103C8T6 的 TIM1 刹车输入可以接管全部 PWM 输出。软件里的保护联动通常分两级:母线过压、过流这类快速故障,用模拟比较器输出一个低电平到 BKIN 引脚,PWM 被硬件立即拉到无效电平,同时刹车标志置位,MCU 在主循环或刹车中断里记录故障类型并点亮故障灯。软件上的常见 bug 是刹车后没有正确复位 MOE 位,导致告警解除后 PWM 输出一直为零。正确做法是重新初始化 BDTR 寄存器并执行TIM_CtrlPWMOutputs(TIM1, ENABLE)。
掉电保护也建议放在软件里做:母线电压通过 ADC 实时监控,当电压低于设定阈值时,主循环置一个stop_flag,SPWM 中断里查到这个标志后把三相对比寄存器全部设为 50% 占空比或直接调用刹车,同时关闭驱动芯片使能。这里要特别注意中断与主循环共享的标志位必须加volatile修饰,否则开编译器优化后会出现"故障明明发生了但软件毫无反应"的诡异现象。复位刹车后,要等母线电容放电到安全电压再重新使能主输出,全硬件方式可以用一个继电器或 PNP 三极管控制驱动电源实现,软件上则加一段延时等待,避免二次浪涌冲击。
提示:正式并入电网或带电机负载之前,先用纯电阻负载做短路测试,确认过流保护动作时间在几十微秒以内,再上感性负载。
5. 带载调试时,验证三相 SPWM 波形的 4 个必查项
5.1 母线电压、占空比与输出线电压的理论对照
上电后先把示波器探头夹在母线电容正负极,确认母线电压稳定在期望值。然后单独测驱动芯片输入侧的 SPWM 信号,此时占空比应随着正弦表按固定周期变化。再用万用表在滤波电容后量交流电压,验证调压关系。母线 311V、调制比 0.8 时,输出相电压有效值约为311/2 * 0.8 / √2 ≈ 88V,如果实测偏差超过 10%,先回头查调制比和死区补偿,再查 LC 滤波参数,不要一上来就怀疑功率管。
5.2 同一桥臂上下管驱动波的死区实测
死区检查是不可跳过的一步。示波器两个通道分别夹同一桥臂上下管的栅源极,注意使用差分探头或隔离探头,不能直接用普通探头同时测两个高压参考点。时基放到 1us/div,观察两个波形的上升沿和下降沿之间是否有一段"同时为低"的时间。发现波形交叉重叠立即断电,把 DTG 调大。调试时死区宁大勿小,MOS 从 2us 往 500ns 调要一路观察栅极波形确认没有直通风险,再改下一个参数。
5.3 三相相位关系用双通道或李萨如图验证
只测一相 PWM 无法确认三相是否正确。常见做法用双通道示波器测滤波前的线电压 Uab,能看到阶梯状 SPWM 波形,然后分别测 Ubc、Uca,三个波形的基波相位差必须是 120°。更快的办法是示波器 XY 模式看李萨如图:Uab 与 Ubc 接两个输入,正常相位差 120° 会看到一个旋转的椭圆;如果看到一条直线,说明两路相位差不是 0° 就是 180°,优先检查查表偏移量是不是被写成了同一个值。
5.4 输出滤波参数不当的特征与补救
滤波电感感量不足的典型特征是空载正弦度好、带载后输出毛刺变大;电感太大则输出电压随负载跌落明显,铁芯还容易饱和。常用经验值:载波 10kHz、输出 50Hz、额定电流 2A 时,滤波电感取 3~8mH,滤波电容取 2.2~4.7uF 薄膜电容,不能用电解电容。调试中看到稳态正弦波上叠加明显的高频开关纹波,先并一颗 100nF 薄膜电容,而不是盲目加大电感。最后调正弦度时,把示波器带宽限制在 20MHz,用短线地夹缩短测量回路,能避免把共模噪声误判为纹波,这是测开关电源输出时最容易被忽略的一步。
本文还有配套的精品资源,点击获取