首页 / 资讯中心 / 文章详情

基于STM32的SPWM三相逆变工程:死区配置与调频调压详解

基于STM32的SPWM三相逆变工程:死区配置与调频调压详解 ★ FEATURED ARTICLE
简介STM32F103C8T6搭配高级定时器TIM1以查表法生成三相六路互补SPWM信号相位互差120°并加入死区与恒压频比调频调压策略适用于三相电机驱动、变频器及逆变控制的学习与验证。资源包共149个文件压缩后约46.67MB结构上以C/H源码与KEIL5工程文件uvprojx/uvoptx为核心附带o/d/axf/map等编译调试产物以及SCVD、INI等仿真配置便于直接导入工程进行在线仿真同时提供SPWM原理PDF文档、正弦表生成工具xls/exe和死区计算器覆盖从正弦表生成到六路PWM输出、死区插入的完整链路。源码注释详细从TIM1定时器配置、正弦查表、死区插入到调频调压逻辑均有分步说明适合嵌入式初学者对照学习也可供电机控制开发者作为基础模板二次开发。目前已有8067人学习下载是兼顾原理讲解与实践验证的STM32 SPWM入门资料。1. 从单路正弦到三相互补SPWM 逆变的第一道门槛把三相桥式逆变电路从“能转”调到“稳转”的过程里卡我最久的不是 PID 也不是电流环而是死区时间设置错了相电流尖峰直接能把保护回路触发。这套基于 STM32F103C8T6 的 SPWM 工程解决了三相逆变里“载波生成、六路互补输出、死区插入、调频调压”四个环节的串接问题。它不是算法论文而是一份能在 KEIL5 里直接编译、仿真、下载到板子上跑的固件工程。适合正在做逆变器、变频器或三相无刷电机控制的开发者也适合想把高级定时器 TIM1 的互补输出、死区寄存器、相位累加器协同逻辑一次摸透的人。2. SPWM 生成与查表先把三相正弦表算对2.1 对称规则采样为什么比自然采样务实SPWM 的核心思想是用高频 PWM 的占空比去逼近一个正弦波。自然采样是在正弦波与三角波交点处切换输出理论上谐波最小但交点坐标需要解三角超越方程在 8 位或中低端 32 位 MCU 上逐个周期实时计算不现实而且输出脉冲不对称死区插入后波形更难看。工程上常用对称规则采样在每个载波周期中央或边界位置采样一次正弦值用这个值作为该 PWM 周期内的恒定占空比。它把问题从“找交点”变成“查正弦值”精度损失对绝大多数电机和逆变器应用足够。STM32 的中央对齐模式正好和对称规则采样匹配——计数器先向上、再向下计数一个完整周期在峰值处对齐更新事件发生在波谷和波峰。这样生成出来的 PWM 脉冲关于载波周期对称低次谐波更干净三相之间相位关系也更稳定。我一般把载波频率定在 18 kHz兼顾听觉噪声和功率管开关损耗。此时 SPWM 在一个 50 Hz 工频周期内约有 360 个 PWM 周期期望查表分辨率不低于 360 点。工程里取 1024 点每个周期约 20 个采样点角度分辨率约 0.35°已经相当平滑。2.2 在 KEIL5 工程里生成 1024 点正弦表正弦表用 float 在启动时算一次然后以 int16_t 存成带符号千分比值范围 -1000 到 1000。这样中断里查表后做整数乘法和除法不用浮点速度飞快。#define SINE_N 1024 /* 查表点数2 的幂便于用 取模 */ #define AMPL_NORM 1000 /* 正弦表满幅对应调制度 1.0 */ int16_t sineTbl[SINE_N]; void SPMW_GenerateSineTable(void) { for (int i 0; i SINE_N; i) { float rad 2.0f * 3.14159265358979f * i / SINE_N; sineTbl[i] (int16_t)(AMPL_NORM * sinf(rad)); } }这段代码把 sin 的计算集中到初始化阶段运行时不再碰浮点。生成后可以在 KEIL5 的 Watch 窗口里检查sineTbl[0]应为 0sineTbl[256]应为 -1000因为 256 / 1024 对应 90°sineTbl[512]应为 0sineTbl[768]应为 1000。如果符号方向不对后面调压逻辑会全反所以先确认这里。2.3 表格内存与运行期占空比计算1024 个 int16_t 占 2 KB三相如果需要独立三份就是 6 KBF103C8T6 有 20 KB RAM放得下。但如果把每一路都存满整个周期浪费且没必要因为三相之间只是固定 120° 相位差。工程里只保存一张正弦表另外两相的索引通过偏移 1/3 和 2/3 周期得到既能省 RAM又天然保证三相对称。运行期计算占空比的基本公式是clk center (int32_t)sineTbl[idx] * modDepth / AMPL_NORM;其中center是中央对齐模式下的中间值即 ARR / 2modDepth是调制深度范围 0 到 1000。这样 0% 调制时输出固定占空比 50%100% 调制时可以得到从 0 到接近 100% 变化的占空比。重点是用int32_t做乘法和除法避免负数右移带来的符号错误。若使用右移代替除法则必须先强转成带符号类型再移位否则负数结果会被补高位截断调压段波形会莫名其妙多出毛刺。3. 三相六路互补输出TIM1 通道分配与死区配置3.1 为什么 F103C8T6 上只有 TIM1 能干这活儿STM32F103C8T6 内部虽然有几个定时器但能输出“三相互补、可插死区”的只有高级定时器 TIM1。TIM1 的通道 CH1、CH2、CH3 各带一个互补输出 CH1N、CH2N、CH3N加起来正好六路结构上就是为三相桥式逆变准备的。TIM2、TIM3、TIM4 只能输出独立 PWM没有互补通道和死区寄存器。如果硬要拿普通定时器输出六路就需要外部逻辑电路搭死区一旦时序抖动就是桥臂直通安全性远不如硬件死区可靠。通道分配上一般把 U 相接 CH1/CH1NV 相接 CH2/CH2NW 相接 CH3/CH3N对应关系如下桥臂上管下管定时器输出UQ1Q2TIM1_CH1 / TIM1_CH1NVQ3Q4TIM1_CH2 / TIM1_CH2NWQ5Q6TIM1_CH3 / TIM1_CH3N接线顺序一旦搞错示波器上每一路看都是正弦但电机相序错上电直接反转或堵转。配置管脚时还要确认这些通道是重映射引脚还是默认引脚F103C8T6 上 TIM1 的默认输出脚和 5V 容忍范围要查芯片手册不能凭印象接。3.2 死区时间的 DTG 取值和实测对照死区时间必须大于功率管关断延迟和开通延迟的差值否则上下管瞬间直通。常见的 IGBT 模块需要 2-3 us低压 MOSFET 逆变可以做到 500 ns 到 1 us。工程里我按 72 MHz 时钟计算死区计数值 死区时间 x 72 MHz。2 us 就是 144 个计数器时钟。TIM1 死区由 BDTR 寄存器的 DTG 字段控制取值编码有点绕但 HAL 库封装得比较直接。配置结构体里填死区计数值和极性TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig {0}; sBreakDeadTimeConfig.DeadTime 144; /* 2us * 72MHz */ sBreakDeadTimeConfig.OffStateRunMode TIM_OSSR_ENABLE; sBreakDeadTimeConfig.OffStateIDLEMode TIM_OSSI_ENABLE; sBreakDeadTimeConfig.LockLevel TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTimePolarity TIM_DEADTIME_POLARITY_HIGH; HAL_TIM_PWM_ConfigDeadTime(htim1, sBreakDeadTimeConfig);DeadTime的编码取决于 72 MHz 分频它可能是直接计数也可能是一段分段映射具体数值用逻辑分析仪实测最靠谱。我的习惯是先填 144抓互补沿看实际死区再微调到驱动芯片需要的区间。死区过大问题不大但波形难看死区过小则是炸管的前兆所以宁可先给大一点稳定后再往下压。3.3 定时器初始化与 PWM 启动代码骨架TIM1 配置成中央对齐模式计数频率 72 MHz。要得到 18 kHz 载波中央对齐的 PWM 频率计算公式是 f clk / (2 x ARR)。所以 ARR 2000。#define PWM_ARR 2000 /* 对应 18kHz 载波(中央对齐) */ #define PWM_CENTER 1000 /* ARR 一半100%调制度时的中心占空比 */ void MX_TIM1_PWM_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; htim1.Instance TIM1; htim1.Init.Prescaler 0; htim1.Init.CounterMode TIM_COUNTERMODE_CENTERALIGNED1; htim1.Init.Period PWM_ARR - 1; htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter 0; HAL_TIM_PWM_Init(htim1); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse PWM_CENTER; sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; sConfigOC.OCIdleState TIM_OCIDLESTATE_SET; sConfigOC.OCNIdleState TIM_OCNIDLESTATE_RESET; HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_3); }启动时除了HAL_TIM_PWM_Start启动三个主通道还要确认 MOE 主输出使能位置位。HAL 库里HAL_TIM_PWM_Start会带起来但如果你在初始化代码里先切换了空闲状态或者刹车输入电平不对MOE 可能被清掉。启动后检查一下htim1.Instance-BDTR的 MOE 位这是输出能出去的前提。4. 调频调压实现相位步进与调制深度协同改4.1 V/F 曲线在数字域里怎么落地三相异步电机和永磁同步电机在开环调速时大多按恒压频比控制输出电压幅值与输出频率成固定比例低频时再补偿一段启动电压。在 SPWM 里输出频率由查表的走步速度决定输出幅度由调制深度决定两者在数字域里是独立的两个变量。调频率不能去改定时器 ARR。因为 ARR 一旦改动载波频率跟着变开关损耗和死区对应关系全乱。正确做法是保持 TIM1 配置固定在更新中断里改变相位累加器的步长。输出频率 f 与步长 step 的关系是step f x SINE_N / PWM_FREQ以 1024 点、18 kHz 载波为例50 Hz 输出时 step 50 x 1024 / 18000 2.844 个表项/载波周期。由于步长不是整数相位累加器至少要精度到 1/65536 个表项才能保证低频平滑无抖动。4.2 相位累加器与更新中断里刷新 CCR相位累加器用 32 位无符号整数高 16 位作为表索引低 16 位当作小数量。这样每个 PWM 更新中断只做加法、截断和查表速度极快可以在 18 kHz 中断里轻松跑完。volatile uint16_t modDepth 800; /* 调制深度0~1000 对应 0~1.0 */ uint32_t g_phaseAcc; uint32_t g_phaseStep; void SPMW_SetFreq(uint32_t freqHz) { /* freqHz 单位是 Hz例如 50 */ g_phaseStep (uint32_t)((uint64_t)freqHz * SINE_N * 65536UL / 18000UL); }频率设置接口单独抽出来避免在中断里做除法。void TIM1_UP_IRQHandler(void) { uint16_t idxA, idxB, idxC; int32_t ccrA, ccrB, ccrC; if (__HAL_TIM_GET_FLAG(htim1, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(htim1, TIM_FLAG_UPDATE); g_phaseAcc g_phaseStep; idxA (g_phaseAcc 16) (SINE_N - 1); /* 取高16位做表索引 */ idxB (idxA (SINE_N / 3)) (SINE_N - 1); idxC (idxA (SINE_N * 2 / 3)) (SINE_N - 1); ccrA PWM_CENTER (int32_t)sineTbl[idxA] * modDepth / 1000; ccrB PWM_CENTER (int32_t)sineTbl[idxB] * modDepth / 1000; ccrC PWM_CENTER (int32_t)sineTbl[idxC] * modDepth / 1000; __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, (uint16_t)ccrA); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_2, (uint16_t)ccrB); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_3, (uint16_t)ccrC); } }这段代码里(SINE_N - 1)代替取模成立的前提是 SINE_N 为 2 的幂。如果你把点数改成非 2 的幂必须改成% SINE_N否则索引会越界。ccrA的限幅依赖modDepth 1000和正弦表满幅 1000两者乘积最大为 1000000除 1000 后不会超过 PWM_CENTER 的余量。实际还要减去死区占用的时间后面会讲。4.3 调制深度上限、死区占用量和最小脉宽调压不是把调制深度直接拉满到 1000。中央对齐模式下一个 PWM 通道上管最大占空比不能达到 100%因为互补下管至少要维持一个死区时间。如果 ARR 是 2000中心是 10002 us 死区对应 144 个计数那么占空比上限是 (2000 - 144) / 2000 92.8%下限是 7.2%。所以调制深度上限要限制在 900 左右留出死区余量。更隐蔽的问题是窄脉冲消除。调制深度很低时正弦过零点附近的占空比可能只有几十个计数器周期此时上管导通脉宽比死区还小输出可能被死区吃掉产生非正弦的畸变。工程里最省事的办法是设置最小调制深度比如 100低于这个值直接按 100 算或者再结合死区补偿做修正。对于只做开环 V/F 调速的应用先把最小占空比限住实际运行后电流畸变能明显减少。5. 避坑清单SPWM 调试中最常见的五个翻车现场5.1 桥臂直通发热死区时间设错现象上电几秒内功率管明显发烫母线电流比空载额定期望大很多甚至直接触发过流保护。原因死区时间设置过短或根本没使能上管关断还未完成下管已经开通桥臂瞬间直通。常见的是把死区配置结构体填好但忘记调用HAL_TIM_PWM_ConfigDeadTime或者 DTG 编码理解错导致实际死区只有几百纳秒。解决先用逻辑分析仪抓一对互补通道实测死区。以 2 us 为目标逐次调整 DeadTime 参数再抓一次直到实测值落在 1.8-2.2 us 区间。上电前用限流电源电流限制到额定 1/3先验证波形再拉大功率。5.2 输出全无刹车输入和 MOE 位没配合好现象代码看起来正常CCR 寄存器也在变化但三个主输出和互补输出全部为低或者固定在高阻态。原因高级定时器的刹车输入 BKIN 控制 MOE 位上电瞬间如果刹车引脚浮空或被钳位到无效电平MOE 被清除所有 PWM 输出被禁止。这是高级定时器的硬件保护逻辑不是软件出错。解决检查 BKIN 引脚的外围电路给它一个确定电平空闲状态下不要让它误触发。初始化完成后读一次TIM1-BDTR确认 MOE 位为 1如果为 0查刹车事件标志并清除。我用 KEIL5 仿真时经常忽略这一步因为仿真器不模拟外部引脚板上一跑就现形。5.3 跳频瞬间电流尖峰相位累加器没有归零现象正在 50 Hz 运行直接把频率改成 30 Hz电机会瞬间冲一下母线电流出现尖峰。原因改变g_phaseStep时相位累加器仍然保留原来 50 Hz 的相位。新步长和旧相位值组合后输出正弦波发生跳变等效于叠加了一个阶跃信号。解决频率突变后把g_phaseAcc重新初始化为当前目标相位的对应值或者强制归零后从零相位开始继续跑。如果电机不允许断电重启就把频率变化做成斜坡每 1 ms 调整一次 step让相位连续过渡。我的工程里默认做法是在SPMW_SetFreq里同时把累加器清零虽然启动瞬间略慢但胜在可控。5.4 低压调制的削顶和输出畸形现象调制度降到 30% 以下时相电压波形已经不接近正弦顶部和底部削平电流谐波明显。原因调制深度过低时正弦过零点附近的占空比已经小于死区带来的最小脉宽输出被钳位另一方面查表值是整数千分比而实际脉宽精度有限小调制深度下量化误差占比升高。解决把调制深度下限约束住设置最小占空比对应值。如果应用要求宽范围调压改用单极性倍频调制或空间矢量 SVPWM但对 F103C8T6 而言 SVPWM 实时计算会吃不少资源看预算再选。单纯调电压的话把调制深度范围限制在 100 到 900 是一个稳妥的做法。5.5 占空比很小时波形缺失死区把窄脉冲吃掉现象示波器上看到某一相在正弦过零点附近偶尔少一个脉冲输出波形不连续低速时有明显抖动。原因上管导通脉宽可能比死区时间还短死区逻辑直接把这段时间吃掉输出变成缺失脉冲。尤其是中心对齐模式下ARR 大、死区大时更明显。解决限制最小 CCR 差值让上管导通时间至少大于 2 倍死区时间。中断里算出 ccrA 后判断是否小于下限值如果小于就取下限。另一种方案是使用死区补偿在相邻周期补足导通时间但补偿逻辑复杂对于开环调压不是首选。6. 一个验证技巧先把死区测准再谈调频调压6.1 用逻辑分析仪测死区的操作步骤逻辑分析仪采样率至少要 50 MS/s否则 2 us 死区边缘宽度本身就几个采样点测出来数值不可信。把 CH1 和 CH1N 两路探头接到上管和下管栅极信号或者 PWM 输出引脚注意共地。触发条件设为 CH1 上升沿捕捉一个周期。测量点看 CH1 下降沿到 CH1N 上升沿之间的时间间隔也就是上管关闭到下管打开的间隔。反过来下管关闭到上管打开的时间也要看一遍两个数值应该接近。如果相差大说明半桥驱动电路存在不对称延迟需要在软件里修正。我一般会同时抓 CH1N 下降沿到 CH1 上升沿记录两个死区时间取平均。如果两者都明显大于配置目标值比如 2 us 变成 3 us说明驱动电路开关延迟叠加进来了。这是正常现象软件死区要结合实际电路延迟来补偿不能只盯着寄存器值。6.2 示波器检查六路互补时序逻辑分析仪适合测时间示波器适合看幅值。六路输出对 COM 端分别测波形在 50 Hz、调制深度 800 的条件下三路主输出波形应互差 120°。查看时把示波器时间轴拉到一个 PWM 周期内放大观察死区。确认完死区后再验证调频调压效果。用示波器测相电压基波幅值改变modDepth时幅值应成比例变化改变g_phaseStep时输出周期应和设定频率一致。这一步可以编写一个小脚本设定频率 30、40、50、60 Hz对应记录电机空载电流和电压判断 V/F 曲线是否符合预期。我在调完这个工程后形成了一条习惯每次改死区参数或定时器配置都会重新抓一次互补波形确认 MOE 位和死区时间两条数据之后才上电接功率级。这套流程能让我排除至少七成硬件烧板风险。SPWM 看起来是“正弦表加定时器”的组合实际坑全藏在时序细节里希望这些经验能帮你在调试时少花几个晚上。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站