☰
六路互补PWM死区配置与波形验证:STM32高级定时器调试指南
2026/10/27 1:10:46 网站建设 项目流程

做电机驱动、逆变器、UPS这类功率变换项目,六路互补PWM这关是绕不过去的。不管你是刚接触FOC的小白,还是已经在调BLDC的老手,这路波形如果处理不好,轻则电机抖动噪音大,重则上下桥臂直通炸管子。我这篇文章就围绕“验证六路互补PWM”这个实验,把完整的原理、配置、实测方法和踩坑实录都梳理出来,手把手带你过一遍这套调试流程。

这篇文章适合正在做三相逆变器、无刷直流电机(BLDC)或永磁同步电机(PMSM)驱动方案的工程师,也适合学校实验室里刚接触STM32高级定时器的学生。通过这篇内容,你会搞明白互补PWM为什么需要死区、六路信号之间的相位关系怎么验证、STM32的寄存器该怎么配,以及示波器上哪些现象是正常的、哪些是危险的。

1. 六路互补PWM的整体设计思路

1.1 从H桥到三相全桥的进化

先说清楚六路互补PWM是从哪来的。单相H桥有四个开关管,一对桥臂输出一个交流电;三相逆变器是把三个H桥拼在一起,共用母线,这就是三相全桥拓扑。三个桥臂各有一对上下管,总共六个开关管。

上管打开时下管必须关断,下管打开时上管必须关断,这就是“互补”关系的来源。而所谓“六路”PWM,就是三个桥臂的上管和下管各一路控制信号,总计六路。在整个PWM周期里,任意时刻某个桥臂只能有一只管子导通,否则就构成母线短路。

真正的电机控制场景下,这三对互补PWM可不是简单复制成三份。三相定子绕组需要相位差120°的正弦波电流,对应到PWM上就是三对互补波形的占空比按正弦规律变化,并且三对波形之间相差120°电角度。验证这个相位关系是实验的关键环节。

1.2 为什么一定要加死区时间

理想情况下,上下管互补切换是瞬间完成的。实际功率器件不是理想开关,MOS管有关断延迟时间(td_off),栅极驱动器也有传输延迟差异。

如果上管还没完全关断,下管就已经开始导通,这时候母线正负极就通过两管直连了。实测中,哪怕只有几百纳秒的重叠导通,母线电容储存的能量也能瞬间烧毁MOS管。死区时间的本质就是在互补信号切换之间插入一段“双方都关断”的空白期,让上管可靠关断后再让下管导通。

这一点必须实测确认,不能只看逻辑。示波器上看互补波形,切换沿处有一段水平空档,那就是死区。关于死区时间的计算和设定,后面会专门展开讲。

1.3 实验验证的目标拆解

这个实验要验证的东西,我总结成四个检查点:

  • 互补关系:每个桥臂的上下管波形在任意时刻电平相反(死区期间除外)。
  • 死区时间:切换沿处的高电平空缺宽度与寄存器设定值一致。
  • 频率与占空比:六路输出频率相同,占空比跟随配置值变化。
  • 相位关系:三对波形依次延迟120°,这是三相控制的核心。

这四个点通过示波器逐项确认后,才算真正验证通过。

2. 实验环境搭建与工具选型

2.1 主控芯片选型的关注点

并不是任意一个单片机都能输出带死区的互补PWM。普通定时器PWM输出引脚只有一个,要产生互补输出,必须选用带“高级定时器”的芯片,典型代表是STM32的TIM1和TIM8。

高级定时器内置了死区发生器、互补输出通道和刹车保护功能。这意味着死区时间、互补极性、故障关断这些逻辑全在硬件里完成,不需要CPU实时干预。如果你用的MCU没有高级定时器,只能软件做死区,那又别论了,但做工业级产品我不会用这种方案。

我实验时用的板子是STM32F103C8T6最小系统板,几块钱一片,TIM1就是高级定时器,完全够用。如果你手头有GD32、MM32这类国产替系列芯片,同样有高级定时器,操作逻辑大同小异。

2.2 功率级与栅极驱动电路的组合方案

主控输出的3.3V PWM信号根本没能力直驱大功率MOS管。标准方案是主控接栅极驱动芯片,再驱动MOS管或IGBT。

栅极驱动我常用IR2101S或EG2131这类半桥驱动芯片,一路驱动芯片正好驱动一个桥臂的上下管。这种芯片内部的死区逻辑配合外部电阻,还可以再加一道硬件死区保障。三相全桥需要三颗这样的驱动芯片。母线电压我这套实验平台用的24V,MOS管选的额定电压100V的N-MOS,余量留了四倍,实验安全性高很多。

2.3 示波器是这实验的关键工具

验证PWM波形绕不开示波器。关于示波器的选择,我想多说两句,因为这块的经验太多人踩坑了。

至少要有两个通道,最好四通道。两通道意味着你一次只能看一个桥臂的上下管波形,然后轮换着看另外两相;四通道可以一次性把三对波形的相位关系全抓到,效率一下子就上来了。

带宽至少要50MHz,实测20kHz PWM是绰绰有余的。但是如果你想细看死区几十纳秒的沿细节,带宽越高失真越小。我用的四通道100MHz数字示波器,测死区精度足够。

有个容易忽略的点是探头补偿。示波器探头上有补偿电容调节孔,先把探头接到示波器自带的1kHz方波校准端,调成标准方波再测。不然你测出来的死区时间误差会很大,甚至波形边缘出现假振铃。

3. 核心参数计算与寄存器配置

3.1 理解定时器时钟树的来龙去脉

参数计算之前,先搞明白PWM的底层来源。STM32F103的TIM1挂在APB2总线上,来自主时钟的72MHz。这个时钟先经过预分频器(PSC),再经过计数器计数(ARR),最终决定PWM频率。

PWM输出频率的计算公式是这样的:

F_PWM = 主频 / ((PSC + 1) × (ARR + 1))

主频=72MHz时,如果我想要20kHz的PWM频率,可以取PSC=0,那么(ARR+1)=72000000/20000=3600,即ARR=3599。

为什么选20kHz?这里有一个理论支撑:20kHz已超出人耳可听范围(20Hz~20kHz)。电机开关频率低于这个值,运行时会发出刺耳的电磁噪音。20kHz意味着每个PWM周期是50μs,死区几μs只占周期百分之几,效率损失非常小。如果在实验初期调低到10kHz,会明显听到电机有“咝咝”的啸叫,那其实是PWM频率落在可听范围内,不是电机故障。

3.2 死区时间的计算与寄存器写入

死区时间设置的依据是功率器件的开关特性。芯片数据手册上会给出典型开通延迟(td_on)和关断延迟(td_off),两者之差就是直通风险窗口。栅极驱动电阻也会影响这个时间。

保守的做法是死区时间取器件开关延迟之和的3~5倍。比如我用的MOS管关断延迟约120ns,开通延迟约80ns,那么理想死区至少200ns,安全起见取1μs。留足裕量之后,1μs的代价是PWM周期50μs里占2%的效率损失,换来的是炸管风险大幅降低。

STM32 TIM1死区时间寄存器是TIMx_BDTR,其中DTG位域的控制逻辑并不直接写入纳秒数,而是分段映射。我的实验以72MHz主频为例,逆推了一下死区值,实际效果很理想:

  • Tdts对应的是定时器时钟周期,72MHz下约为13.89ns。
  • DTG位域高三位是000,属于模式0,直接死区时间就是DTG值乘以Tdts。
  • 需要1μs死区,就要写入DTG=72,72×13.89ns≈1μs。

我习惯在初始化代码里把死区值写明确并加注释,防止换一批芯片后主频变了,死区时间在不知情的情况下跟着变。

3.3 寄存器级别初始化代码解析

下面是我在实验里实际用过的TIM1初始化代码,我贴的是简化版,屏蔽了中断处理部分,只保留核心配置逻辑。

void TIM1_PWM_Init(uint16_t arr, uint16_t psc, uint16_t dtg) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; TIM_TimeBaseStructure.TIM_Prescaler = psc; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); /* 三对互补通道的模式都设置为PWM1,比较值与手动配置 */ 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 = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC2Init(TIM1, &TIM_OCInitStructure); TIM_OC3Init(TIM1, &TIM_OCInitStructure); /* 六路主输出使能,必须写,否则高级定时器不输出 */ TIM_CtrlPWMOutputs(TIM1, ENABLE); /* 死区时间写入BDTR寄存器 */ TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Disable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_DeadTime = 72; TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; TIM_BDTRInit(TIM1, &TIM_BDTRInitStructure); TIM_ARRPreloadConfig(TIM1, ENABLE); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable); }

注意TIM_CtrlPWMOutputs这一行,高级定时器区别于普通定时器的一个坑就在这里。即使前面都配好了,不调用这个函数,主输出就是不出来,波形一直是低电平。

补一句比较值怎么设置。上面代码里TIM_Pulse先置为0,实际运行中通过TIM_SetCompare1(TIM1, 300)这类函数动态更新占空比。比如ARR=3599时,比较值300表示占空比约8.3%,比较值3300表示占空比约91.7%。

3.4 利用STM32CubeMX快速生成工程

有些朋友喜欢用STM32CubeMX图形化配置,我实验初期也用它快速搭过框架。在CubeMX里选择TIM1,把Channel1、Channel2、Channel3的模式都设为PWM Generation CHx and CHxN(互补输出),然后在Break and Dead Time settings里设置死区时间。

注意CubeMX里死区时间单位是ns,界面框会让你直接输入数字,底层自动帮你换算成BDTR值。输入1000代表1μs,生成代码后再核对一下寄存器值,确认换算逻辑没问题。

我个人最终倾向于寄存器方式,因为经验上调试中修改比较值以及分析波形时,寄存器方式逻辑更透明,出了诡异问题能少绕弯子。CubeMX适合快速起步,寄存器适合深度排错,两者不矛盾。

4. 实操过程与六路波形验证

4.1 硬件接线与上电检查

我的实验用板是STM32F103C8T6,选用的管脚对应TIM1通道,实验具体接线如下:

信号MCU引脚去往备注
TIM1_CH1PA8IR2101S的高边输入HIN桥臂A上管
TIM1_CH1NPB13IR2101S的低边输入LIN桥臂A下管
TIM1_CH2PA9IR2101S的高边输入HIN桥臂B上管
TIM1_CH2NPB14IR2101S的低边输入LIN桥臂B下管
TIM1_CH3PA10IR2101S的高边输入HIN桥臂C上管
TIM1_CH3NPB15IR2101S的低边输入LIN桥臂C下管
母线电源-桥臂MOS管直流母线24V
示波器夹子-GND接地要就近接

上电顺序有讲究。先给MCU烧录程序,确认主控板工作正常;再接通24V母线。第一次上电时,我习惯把母线电源限流调小,比如限流1A,即使有错误也不至于炸管,这个习惯帮我避免了两次事故。

栅极驱动芯片的地必须和MCU共地,很多初学者漏了这一步,导致高低边逻辑乱跳,波形完全不可控。

4.2 示波器上验证互补关系

示波器通道1接TIM1_CH1引脚,通道2接TIM1_CH1N引脚。触发源选在通道1,触发方式设置为下降沿,时基先放到50μs/格。

正常情况下,你会看到上下两个波形接近镜像,但不是完全对称。高电平宽度之差正好是死区时间。通道1从高变低后,通道2还要保持一段低电平才变高;通道2从高变低后,通道1也要保持一段低电平。这个双双低电平的区间,就是死区。

给一个我更推荐的观察方式:示波器打开“数学运算”功能,用通道1加通道2的“与”逻辑或用“下降沿延时”测量功能,直接读出死区时间数值。如果算出来的死区在0.9μs到1.1μs之间,和设定的1μs一致,这组波形就验证通过了。

4.3 三对波形的相位差验证

三相电机的核心是相位差120°。验证PWM的时候,不能只看单独一个桥臂,还得看三对波形之间的相位关系。

示波器四通道分别接CH1、CH2、CH3,同时观察三个上管波形。设置时基50μs/格,你会看到三个波形形状相同但互相错开。PWM周期是50μs,120°相位差对应的时间偏移是50μs除以3,约16.67μs。也就是说,通道1的上升沿出现后,通道2的对应上升沿要晚约16.67μs,通道3再晚16.67μs。

如果这里测出来时间偏移不对,说明比较值更新时机有误,通常是对齐/边沿对齐模式选择出了问题。做FOC算法的大忌是六路PWM虽然都有输出,但相位关系全是乱的。

4.4 上管和下管不能简单看同一路信号

有些学习板为了简化接线,直接把互补输出的N通道悬空,用上管信号逻辑取反来控制下管。这种做法虽然能让电机转起来,但丢失了硬件死区的意义。

我建议实验验证时一定要把CH1和CH1N都接上,不光是看波形,还要接功率级的输入实测。有些问题只在带载情况下显现,空载时示波器看着教科书级完美,一带电机就出毛刺。

5. 调试过程实录与常见问题排查

5.1 上电立即炸管:典型的主输出未关断问题

我调试初期最惨烈的一次经历是上电以后开始配置寄存器,程序还没运行到主输出使能那段,MOS管就冒烟了。

原因很简单:STM32刚上电时IO引脚默认是浮空输入状态,对于栅极驱动芯片来说,输入悬空就等于随机电平。IR2101S的LIN输入一旦悬空被拉高,下管直接导通,再叠加母线电源一开,若上管也被意外拉导通,瞬间就直通了。

解决方式有两个必须同时做:第一,在栅极驱动输入前对地加下拉电阻(10kΩ),保证MCU还没初始化的时间段里栅极驱动低电平输入;第二,程序一启动先把刹车信号拉起来,或者在主函数最开始就配置TIM1的MOE主输出使能为0,全部初始化完成后再置1。

炸管的问题是整个实验中最危险最花钱的问题,这一条务必重视。

5.2 只有三路输出:被忽略的N通道使能位

有次实验六路波形只出来三路,CH1、CH2、CH3都有,CH1N、CH2N、CH3N全是低电平。查来查去,最终发现是结构体里TIM_OutputNState一直没配置,默认输出状态被关掉了。

对比代码就一目了然:

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; // 这一行漏了就只输出三路

TIM_OCInitStructure是库函数方式的老黄历写法,但原理是通用的。如果你用寄存器方式初始化,检查CCER寄存器里CC1NE位有没有置1,低电平和这个位是强关联的。

5.3 死区时间偏大的影响

有段时间我把死区设成5μs,电机低速转动时明显能听到“嗒嗒”的异响,但波形看着没问题。效率测量对比后发现输出功率比预期低了约10%。

这就是死区时间过大的副作用。死区越大,每个周期里两个管子都关断的时间越长,实际输出电压相对理想值的损失就越大。这个现象在低电压大电流的电机驱动上尤其明显。

要做死区补偿的话,原理上是在软件算法里对电压矢量做修正,根据电流方向前移或者后退PWM前沿。这个属于比较进阶的内容,实验初期没必要上,先把死区值控制在合理范围内更重要。

5.4 波形毛刺和振铃不是芯片问题

示波器上看到波形上升沿有振铃,很多初学者误以为是芯片时序有问题。我排查下来,大部分情况来自两个物理因素:探头接地线太长,或者开关管快速切换时回路寄生电感与电容产生谐振。

第一个因素最容易验证。把示波器探头换成弹簧地线,接地端紧贴测试点附近,如果振铃明显变小,就是探头带来的假象。第二个因素需要改善PCB布局,让母线电容尽可能靠近MOS桥臂。实验中用的面包板由于寄生电感大,毛刺严重是正常的,放宽心态。

5.5 互补PWM常见问题速查表

把调试中容易碰到的问题和排查方向整理成一个表格,方便你对照定位。

故障现象优先怀疑方向检查手段
上电即短路或炸管IO上电默认电平、未使能MOE栅极驱动输入对地下拉;主循环最先关主输出
只有三路低电平输出N通道使能未配置检查CCER的CCxNE位,或结构体OutputNState
互补波形没有死区BDTR死区寄存器值为0读取TIMx_BDTR寄存器,确认DTG非零
波形振铃大探头地线过长、PCB寄生电感大换弹簧接地探针;优化功率回路布局
某一路占空比异常比较值更新时机问题检查是否在需要使用影子寄存器时开了预装载
死区值与设定不一致主频估计错误、分频配置有误用示波器频率功能实测PWM频率反推主频

6. 从实验台到产品级的几个改进方向

实验波形验证通过,说明你的PWM配置基础已经扎实了,但距离能上产线的固件还有几个坎要过。

第一,增加刹车保护功能。高级定时器上的刹车输入(BKIN)并不是摆设。产品上得过流保护、过压保护、电机驱动故障信号,直接接到刹车输入脚。一旦故障信号触发,芯片硬件直接将PWM输出全部置为安全状态,响应时间在硬件级纳秒级别,而软件中断响应最快也要几微秒。这个时间差就能区分是管子保住了还是冒烟了。

第二,开启定时器中断做逐周期电流保护。母线电流通过采样电阻采集后送入比较器,比较器输出接BKIN。也可以直接用ADC采样,在定时器更新中断里做限流判断,超出阈值就关闭PWM输出。

第三,考虑自适应死区时间。如果项目对效率极致追求,可以根据母线电压和负载电流动态调整死区时间。小型电机上差异不明显,大功率驱动上死区损耗不容忽视。

实验做到这一步,离实际的驱动器产品就比较接近了。剩下FOC、SVPWM那些算法层面的东西,是基于这套可靠的PWM底层逐个叠加的。

我个人在实际操作中的体会是:六路互补PWM验证虽然步骤琐碎,但一次扎实的验证能让你后续调FOC算法时少走好多弯路。PWM底层如果没有踩踏实,算法跑得再花哨,最终都会在各种奇怪的现象里翻车。建议你将实验记录数据保存成模板,以后换主控平台直接照这个流程做验证,省下的时间非常可观。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询