资讯动态

STM32三相SPWM六路互补输出:调频调压与死区耦合的工程实现

发布时间:2026/10/26 8:48:58 来源:尧图企业网站定制
简介面向嵌入式初学者与电机控制方向学习者的STM32实战资源围绕STM32F103C8T6高级定时器TIM1用查表法生成三相六路互补SPWM信号相位互差120°支持死区插入与调频调压并以恒压频比方式驱动三相电机适合作为电机驱动入门与课程设计的参考工程。压缩包共149个文件、约46.67MB以c与h源码、Keil工程文件uvprojx/uvoptx、hex与axf固件、map与lst编译产物为主另附SPWM原理学习文档、正弦表生成工具与死区计算器便于边看代码边理解波形生成逻辑。程序在KEIL5下编写并带详细注解可在线仿真观察六路互补输出波形帮助读者掌握死区时间设置、正弦表查表与调压调频的实现细节。目前已有8068人学习下载适合对照源码与工具逐步复现并排查问题。1. 三相 SPWM 六路互补输出为什么“调频调压”不是改两个参数那么简单很多人第一次在 STM32F103C8T6 上做三相 SPWM直觉是定时器配好中心对齐模式三路 PWM 出来再取反得到六路频率改 ARR、电压改 CCR收工。真跑起来才发现互补输出的死区一旦加上占空比和输出电压的关系就不再是线性的调频时载波比跟着变正弦表步进也得跟着换六路里任何一路相位错半个周期电机或逆变桥立刻抖给你看。这套东西的难点从来不在“会不会配定时器”而在于调频、调压、死区、互补这四件事互相耦合动一个牵三个。把这份资源里的实现逻辑拆开看能搞清楚 STM32 的高级定时器到底是怎么把“三相正弦”这件事硬件化掉的以及为什么工程上宁可多算一张表也不肯在中断里现算 sin。2. 高级定时器六路互补输出到底是谁在硬件里完成的2.1 中心对齐模式与三相互补的硬件映射关系STM32F103C8T6 上能直接吐六路互补 PWM 的只有 TIM1 和 TIM8 这类高级定时器通用定时器 TIM2/3/4 做不到带死区的互补对。原因在硬件结构高级定时器的每个通道都有 OCx 和 OCxN 两个输出中间夹着一个死区发生器 BDTR互补关系是硅片里连好的不是软件取反。这一点决定了整个方案的骨架——三相六路必须落在 TIM1 的 CH1/CH1N、CH2/CH2N、CH3/CH3N 上三对互补正好对应逆变桥的三个半桥。计数模式选中心对齐CMS 位非零计数器从 0 数到 ARR 再数回 0比较值 CCR 在上升和下降两个方向各匹配一次输出波形天然关于中点对称。对 SPWM 来说这不是可选项中心对齐让每路 PWM 的脉宽以周期中心为轴分布三相互补的相位关系才稳定边沿对齐模式下互补对的死区插入点会偏谐波明显变差。常见的实现思路是把 ARR 设成载波周期的一半计数实际开关频率 72MHz / (2 × ARR)这个 2 倍关系是很多人第一次算频率时翻车的地方。三相互补的相位差靠什么保证不是靠三个定时器同步而是同一个定时器的三个通道共用同一个计数器 CNTCH1/CH2/CH3 的 CCR 值分别对应三相正弦在 0°、120°、240° 的采样点。硬件上它们天然同相锁定软件只需要保证三张或一张偏移索引的正弦表对齐。这是单定时器方案相对三定时器方案最大的优势不存在同步误差也就没有三相不平衡的隐患。2.2 死区发生器 BDTR为什么死区时间必须用硬件而不是软件延时死区的作用是防止同一半桥的上下管直通。软件延时的做法是在关断一路后插一段空循环再开另一路问题在于中断响应、编译优化、主频波动都会让这段延时不可控血泪经验是软件死区在低频时看着没事一上调频就炸管。硬件死区发生器挂在 BDTR 寄存器上死区时间由 DTG[7:0] 位编码从几个 ns 到十几 us 分档插入位置精确到时钟周期且与 CCR 更新完全解耦。DTG 的编码不是线性的这是必须讲清的一个细节。以 72MHz 时钟为例DTG 低段按 t_dtg DTG × t_DTS 计算高段带偏移和倍率具体分档在参考手册里有表。工程上一般先定死区时间比如 1us 量级视功率管开关速度而定再反查 DTG 值而不是随手填一个数。填错档位的现象很典型示波器上看互补对之间要么没有间隔要么间隔大得离谱导致有效占空比被吃掉一大块输出电压上不去。// TIM1 死区与刹车配置寄存器级便于看清每一位的作用 TIM1-BDTR 0; TIM1-BDTR | (0x1C 0); // DTG[7:0]按 72MHz、t_DTS1/72us 反查得到的死区分档 TIM1-BDTR | (1 11); // OSSR运行模式下关闭后输出空闲电平 TIM1-BDTR | (1 12); // OSSI空闲模式下输出空闲电平 TIM1-BDTR | (1 13); // BKE使能刹车输入过流保护用 TIM1-BDTR | (1 15); // MOE主输出使能缺这一位六路全哑这段配置里最容易漏的是 MOE 位。高级定时器的输出使能是独立的一级开关CCER 里把通道配成输出、BDTR 里死区也设了只要 MOE 没置位六个引脚就是死的示波器一片平。排查时先看 MOE再看 CCER 的 CCxE/CCxNE最后才怀疑正弦表这个顺序能省掉大量时间。DTG 那一位的具体数值必须对着时钟树和手册分档表算代码里写死一个魔数而不写注释是后面接手的人最想骂人的地方。2.3 CCR 预装载与更新事件调压时波形为什么不能“撕裂”调压的本质是改 CCR。但 CCR 不能在计数器跑到一半时随便改否则当前这个 PWM 周期的上升沿和下降沿会来自两个不同的比较值脉宽畸变输出上表现为毛刺。高级定时器的解法是 CCR 预装载OCxPE 位写进 CCR 的值先进影子寄存器等更新事件 UEV 到来中心对齐模式下在计数器到 0 时才整体生效。三路 CCR 在同一个 UEV 时刻一起换三相正弦的相位关系不会被打乱。这里有个设计取舍值得说透。如果每个 PWM 周期都更新一次 CCR正弦表的步进就固定为 1载波比 表长 / 每周期的步进调频时要么改表长要么改步进前者占 Flash、后者要重算。资源里的做法是把正弦表按固定点数存好调频时改 ARR 同时改查表步进用定点累加器做相位累加调压时改一个全局的幅度系数去乘表值。相位累加器是这套方案的核心数据结构// 相位累加 幅度缩放放在 UEV 中断里执行 static uint32_t phase_acc 0; // 32 位相位累加器高 16 位作查表索引 static uint32_t phase_step 0; // 每个 PWM 周期的相位增量决定输出频率 static uint16_t amplitude_q8 256; // Q8 定点幅度256 表示满幅 void TIM1_UP_IRQHandler(void) { if (TIM1-SR TIM_SR_UIF) { TIM1-SR ~TIM_SR_UIF; phase_acc phase_step; // 相位连续累加频率无跳变 uint16_t idx phase_acc 16; // 取高 16 位做表索引 uint16_t a (sin_table[idx] * amplitude_q8) 8; // 调压乘幅度系数 uint16_t b (sin_table[(idx TAB_120) TAB_MASK] * amplitude_q8) 8; uint16_t c (sin_table[(idx TAB_240) TAB_MASK] * amplitude_q8) 8; TIM1-CCR1 a; TIM1-CCR2 b; TIM1-CCR3 c; // 预装载UEV 后统一生效 } }相位累加器的好处是频率分辨率极高phase_step 加 1输出频率只变一点点而且相位永远连续调频过程中不会出现波形跳变。代价是中断里要做三次乘法和查表72MHz 下这点开销完全扛得住但如果把 sin() 现算放进来一次浮点三角函数就够中断超时了。幅度用 Q8 定点而不是浮点也是同一个考虑Cortex-M3 没有 FPU软浮点在中断里是灾难。TAB_120 和 TAB_240 是 120°、240° 对应的表偏移用掩码取模避免除法这些细节决定了这套代码能不能在 72MHz 上稳定跑满开关频率。3. 调频调压的耦合正弦表、载波比与相位累加器怎么配合3.1 载波比变化时正弦表点数为什么不能死守一个值SPWM 的载波比是开关频率与输出基波频率之比。固定正弦表点数、靠改 ARR 调频载波比会随输出频率漂移低频时载波比高、谐波远高频时载波比掉下来低次谐波逼近基波滤波器和电机都难受。资源里的处理思路是让 phase_step 与 ARR 联动——输出频率 f_out 满足 f_out f_pwm × phase_step / 2^16而 f_pwm 72MHz / (2 × ARR)两个式子一联立给定目标 f_out 就能反算出 phase_step。这样正弦表点数不变载波比随频率自然变化在常用频段内保持在合理区间。死守一个载波比、强行改表长的做法也有但表长一变Flash 占用和查表掩码全要跟着改代码复杂度陡增收益在多数应用里不明显。这里的取舍是用相位累加器换掉“表长必须整除”的约束把调频的自由度从表结构里解放出来。我一般会这么理解——正弦表是“波形模板”相位累加器是“播放速度”两者解耦之后调频调压才互不干扰。3.2 调压的两种路径改幅度系数还是改直流母线调压有两条路软件改幅度系数上面代码里的 amplitude_q8或者硬件调直流母线电压。前者响应快、无额外器件但占空比整体压低后死区占用的相对比例变大低压输出时线性度变差这是 SPWM 的固有短板。后者线性度好但需要可调电源成本和体积上去了。资源走的是软件调幅路线适合中小功率、对低压段精度要求不极端的场景。改幅度系数时要注意 CCR 的下限。死区已经吃掉了每个边沿附近的一小段如果 amplitude_q8 压得太低算出来的 CCR 值落进死区区间实际输出直接归零表现为“调压调到某个点以下就断输出”。工程上的做法是给 CCR 设一个下限钳位并在幅度系数上做标定把有效调压范围映射到可用区间。这个坑不踩一次很难想到因为示波器上看波形是“突然没了”而不是“慢慢变小”。3.3 六路互补的相位校验怎么确认三相互差真的是 120°代码写完不等于相位对。验证方法很直接把三对互补输出接到示波器看 CH1 与 CH2 的基波过零点间隔是否等于 1/3 个基波周期CH2 与 CH3 同理。更省事的做法是在调试阶段临时把 amplitude_q8 设成满幅、输出频率设低比如几 Hz用示波器的余晖或长存储抓三相包络肉眼就能看出相位是否均匀。如果三相互差不对先查 TAB_120/TAB_240 的偏移量是否按表长正确换算再查掩码是否覆盖了表长最后才怀疑中断里的更新顺序。提示相位校验一定要在死区使能的状态下做。死区会改变每路的有效脉宽但不会改变基波相位如果关掉死区校验完再打开容易掩盖互补对配置本身的错误。4. 避坑与排查这套方案最容易翻车的几个点4.1 六路全哑示波器一片平现象定时器配置看起来全对CCER 也开了六个引脚没有任何输出。原因BDTR 的 MOE 主输出使能位没置位或者刹车输入 BKE 使能后 BKIN 引脚被拉低触发了刹车。解决先确认 MOE1再检查 BKIN 引脚电平调试阶段可临时关闭 BKE 排除刹车干扰量产前必须恢复并接好过流保护。4.2 调频时波形跳变、电机异响现象改输出频率的瞬间波形相位不连续负载上有明显抖动。原因直接改 ARR 而没同步改 phase_step或者相位累加器在改频率时被清零。解决频率切换只改 phase_stepphase_acc 保持连续累加ARR 的修改走预装载等 UEV 生效避免当前周期被截断。4.3 低压段输出突然消失现象幅度系数往下调输出先变小后突然归零。原因CCR 计算值落进死区区间有效脉宽被死区完全吃掉。解决给 CCR 设下限钳位标定有效调压区间必要时在低压段适当减小死区或提高开关频率。4.4 三相互差不是 120°且随频率漂移现象低频时相位看着还行频率一高三相就不均匀。原因TAB_120/TAB_240 的偏移量按固定表长写死但查表索引的掩码或步进在调频时没跟着一致处理导致偏移错位。解决偏移量统一用“表长/3”的整数表达索引取模用同一个掩码调频只动 phase_step 不动表结构。4.5 中断里现算 sin() 导致输出频率上不去现象开关频率一提高波形畸变甚至中断丢拍。原因Cortex-M3 无 FPU软浮点三角函数在中断里耗时过长。解决正弦值全部预存成表中断里只做查表、乘法和移位幅度用 Q8/Q15 定点杜绝浮点运算进入中断路径。5. 设计取舍为什么这套结构值得照着源码走一遍把整条链路串起来看这套方案的核心取舍其实只有一句话把“波形生成”从软件计算降级成“查表 相位累加 硬件互补”软件只负责每个 PWM 周期更新三个 CCR其余全部交给 TIM1 的硬件结构。这个取舍带来的好处是确定的——死区精度由硅片保证、三相同步由共用计数器保证、调频调压解耦由相位累加器保证代价也同样确定——正弦表占 Flash、调压线性度受死区限制、载波比随频率漂移。值不值得照着源码走一遍取决于你想验证什么。如果只是想“让六路出波形”抄配置就够但如果你想搞清楚高级定时器在 SPWM 场景下每一级硬件到底承担了什么就得把 BDTR 的 DTG 分档、CCR 预装载的生效时机、UEV 中断与相位累加器的配合这三处对着手册逐位看一遍。这三处是整套方案里“软件想管但管不了、硬件默默兜住”的地方也是这类资源最有价值的部分——它把寄存器级的细节和工程上的标定经验放在了一起而不是只给一份能跑的工程。验证方法上我习惯先断开功率级只测六路 GPIO 波形确认互补对间隔等于设定死区、三相互差 120°、调频时相位连续、调压时占空比线性变化四项全过再上功率管。这个顺序看起来慢但比上电炸管之后再回头查寄存器快得多。从那以后我每次碰高级定时器的互补输出都强制先把 MOE、DTG、CCER 三位在示波器上验一遍再往下走后悔药这东西能不吃就不吃。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑