ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32G431六路互补PWM与死区时间验证实战

STM32G431六路互补PWM与死区时间验证实战

1. 六路互补PWM到底在解决什么问题

如果你正在调无刷电机驱动、数字电源或者三相逆变器,大概率绕不开一个词:六路互补PWM。听起来像是六个通道各发各的波形,实际上它是一套有严格时序约束的“组合拳”——三对互补通道,每对包含一个高边开关信号和一个低边开关信号,两者在理想情况下完全反相,中间还必须插入一段死区时间,防止上下桥臂直通炸管。

我这次实验用的是STM32G431,主控芯片自带TIM1高级定时器,专门为电机控制这类场景设计。标题里写的是“验证”,意思就是我不打算只跑个例程看波形出来就完事,而是要把互补性、死区、中心对齐模式、刹车保护这几个关键点全部测一遍,确认这套配置能不能直接搬到真实的驱动板上。

先说清楚这套东西适合谁看:如果你刚开始接触STM32的PWM配置,或者已经会点灯但没碰过互补输出,这篇文章能帮你把概念和实操串起来;如果你已经在做电机驱动,但死区时间总是拍脑袋填、互补波形偶尔抽风,那下面的排查思路和参数计算过程应该对你有用。我尽量不堆寄存器手册原文,而是把“为什么这么配”讲透,让你换一颗芯片也能自己推出来。

核心关键词先摆出来:PWM、六路互补、TIM1、STM32G431、死区时间。这几个词贯穿全文,后面每个章节都会围绕它们展开。

2. 为什么选TIM1而不是普通定时器

2.1 高级定时器的互补输出硬件机制

STM32G431属于G4系列,主打混合信号和电机控制。它内部有三个高级定时器:TIM1、TIM8、TIM20。普通定时器比如TIM2、TIM3也能输出PWM,但它们的通道之间是独立的,没有硬件级的互补配对和死区插入功能。你要用普通定时器做互补,只能靠软件在中断里翻转两个IO,不仅占用CPU,死区时间还极不稳定,电机低速时抖动会非常明显。

TIM1的每个通道都有一个对应的互补通道,比如CH1对应CH1N,CH2对应CH2N,CH3对应CH3N。这三个互补对正好组成六路输出。硬件内部有一个死区发生器,你只需要往BDTR寄存器的DTG位写一个值,硬件就会自动在高边关断和低边开通之间插入延迟,延迟精度直接跟定时器时钟挂钩,不受中断响应时间影响。

注意:CH1N、CH2N、CH3N这三个互补输出默认不是随便就能用的,必须把BDTR寄存器的MOE位(主输出使能)置1,否则引脚上不会有波形。很多人第一次配完发现只有CH1有输出,CH1N死活不动,八成就是MOE没开。

2.2 中心对齐模式对电机驱动的意义

PWM模式有边沿对齐和中心对齐两种。边沿对齐就是计数器从0数到ARR然后归零,波形上升沿固定在周期起点。中心对齐则是计数器先向上数到ARR,再向下数回0,一个完整周期内对称。对于电机驱动和数字电源来说,中心对齐有几个实际好处:

第一,生成的PWM谐波频谱更干净。边沿对齐会在开关频率及其整数倍产生较大谐波,中心对齐相当于对波形做了对称化处理,谐波能量被分散,EMI表现更好。第二,中心对齐模式下,ADC采样时机可以安排在计数器过零或者达到ARR的时刻,这时候开关管状态稳定,采样到的电流噪声最小。第三,三相互补PWM在中心对齐下,任意时刻总有一相处于有效状态,空间矢量调制的实现更自然。

我这次实验用的是中心对齐模式1,计数器在向上计数和向下计数时都产生比较匹配。配置的时候要注意,中心对齐下PWM频率的计算公式跟边沿对齐不一样:

中心对齐PWM频率 = 定时器时钟 / (2 × ARR × 预分频系数)

假设TIM1挂载在170MHz时钟上,预分频设为0,ARR设为4249,那么PWM频率就是170MHz / (2 × 4250) = 20kHz。这个频率刚好在听觉范围边缘,电机驱动常用。如果你用边沿对齐,同样的ARR会得到40kHz,翻了一倍,所以切换模式时ARR必须重新算。

2.3 引脚分配与复用功能确认

STM32G431的TIM1通道分布在PA8、PA9、PA10、PA11和PB13、PB14、PB15等引脚上。具体哪几个引脚能用作CH1N、CH2N、CH3N,必须查数据手册的复用功能表。我这次用的是PA8(CH1)、PA9(CH2)、PA10(CH3)作为三路高边,PB13(CH1N)、PB14(CH2N)、PB15(CH3N)作为三路低边。这样分配的好处是同一组互补对在物理上相邻,PCB走线短,干扰小。

配置GPIO的时候,模式要选复用推挽输出,速度建议设为Very High,因为20kHz的PWM边沿需要足够的驱动能力。上下拉根据你的栅极驱动芯片输入阻抗来定,一般选无上下拉或者下拉,避免上电瞬间误导通。

3. 死区时间到底怎么算才不炸管

3.1 死区时间的物理意义与计算过程

死区时间就是高边关断之后、低边开通之前的那段“空窗期”。如果没有这段延迟,上下两个MOS管会同时导通,电源直接对地短路,瞬间电流能到几百安培,管子直接炸。死区时间太短,炸管风险高;太长,输出波形畸变严重,电机效率下降、发热增加。

死区时间的计算跟定时器时钟和BDTR寄存器的DTG配置有关。STM32G431的TIM1时钟我设的是170MHz,死区发生器有几种步进模式:

DTG[7:5]步进死区时间范围
0xx1×tDTS0~127×tDTS
10x2×tDTS128~254×tDTS
1108×tDTS256~504×tDTS
11116×tDTS512~1008×tDTS

其中tDTS = 1/170MHz ≈ 5.88ns。假设我的MOS管栅极驱动芯片是IR2104,开通延迟120ns,关断延迟90ns,MOS管本身开通时间50ns,关断时间30ns。最坏情况是高边关断最慢、低边开通最快,那么需要的死区时间至少是:

死区时间 ≥ 高边关断时间 - 低边开通时间 + 安全裕量

高边关断总延迟 = 90ns + 30ns = 120ns,低边开通总延迟 = 120ns + 50ns = 170ns。等等,这里低边开通比高边关断还慢,那理论上不需要死区?不对,实际电路中驱动芯片的传播延迟和MOS管的开关时间都有离散性,而且温度变化会影响参数。稳妥起见,我取最坏情况下的差值再加上100ns裕量。

实际计算:高边关断最快可能只要60ns,低边开通最快可能只要80ns,那高边还没完全关断低边就开了,直通风险就来了。所以死区时间要覆盖“高边关断最慢”和“低边开通最快”之间的时间差。我最终取200ns作为死区时间,对应DTG值:

200ns / 5.88ns ≈ 34,落在0xx范围,DTG[7:5]=000,DTG[4:0]=34,即DTG=0x22。

3.2 死区时间对输出波形的实际影响

配置好死区之后,我用示波器抓了CH1和CH1N的波形。理想情况下,CH1上升沿和CH1N下降沿之间应该有200ns的延迟,反过来CH1下降沿和CH1N上升沿之间也有200ns延迟。实测下来,上升沿延迟约208ns,下降沿延迟约196ns,误差在几个ns级别,完全可接受。

但这里有个坑:死区时间会“吃掉”一部分有效占空比。假设你设置占空比为50%,由于死区插入,实际高边导通时间会比理论值少200ns。在20kHz频率下,周期是50us,200ns只占0.4%,影响很小。但如果你的PWM频率提高到100kHz,周期只有10us,200ns就占2%了,电机低速时可能会感觉到转矩脉动。所以高频应用下死区时间要尽量压缩,前提是保证不直通。

实操心得:调死区的时候,先用示波器双通道同时看高边和低边的栅极驱动信号,确认没有交叠。然后用手摸MOS管,如果发现某个管子异常发热,大概率是死区不够。但注意安全,高压板子不要直接用手碰,用热成像仪看。

3.3 互补输出的极性配置陷阱

TIM1的每个通道都有极性控制位,CC1P控制CH1的极性,CC1NP控制CH1N的极性。默认情况下,CH1高电平有效,CH1N也是高电平有效。但很多栅极驱动芯片是低电平有效的,比如IR2104的HIN和LIN都是高有效,但有些芯片是低有效。如果你接的是低有效驱动芯片,就需要把CC1P和CC1NP都设为1,让输出反相。

这里最容易出错的地方是:互补输出的“互补”关系是在考虑极性之后仍然成立。也就是说,不管你怎么设极性,CH1和CH1N在任意时刻都不能同时有效。我建议配置完之后,一定要用示波器确认一遍,不要只看代码里写了互补就以为万事大吉。

4. 从CubeMX配置到代码落地的完整流程

4.1 CubeMX里的关键配置项

我用STM32CubeMX做初始化配置,选好G431芯片后,在Timers里找到TIM1,Clock Source选Internal Clock,Channel1、Channel2、Channel3都选PWM Generation CHx CHxN。这样六路输出就都使能了。

下面是几个必须手动改的参数:

  • Counter Mode:选Center Aligned Mode 1
  • Prescaler:0
  • Counter Period (ARR):4249
  • Pulse (CCR1/2/3):初始设为2125,也就是50%占空比
  • PWM Mode:PWM Mode 1
  • Dead Time:34
  • Automatic Output:Enable
  • Break Input:根据你的刹车电路配置,我暂时禁用

注意:CubeMX里Dead Time那个框填的是DTG寄存器的原始值,不是纳秒。很多人直接填200,结果死区时间变成200×5.88ns≈1.18us,电机直接啸叫。填之前一定先算好。

4.2 生成代码后的手动补充

CubeMX生成的代码只完成了初始化,要让PWM真正跑起来,还需要在main函数里调用:

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3); HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);

注意互补通道要用HAL_TIMEx_PWMN_Start,不是普通的HAL_TIM_PWM_Start。这两个函数内部都会检查MOE位,如果BDTR配置里MOE没使能,它们会帮你置位。但如果你用的是LL库或者直接写寄存器,就必须手动设置TIM1->BDTR |= TIM_BDTR_MOE。

4.3 动态调整占空比与死区

实际运行中,占空比需要根据电机控制算法实时调整。修改CCR寄存器的函数是:

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, new_pulse);

这个操作在中心对齐模式下是安全的,因为影子寄存器会在更新事件时才把新值载入。但如果你在中断里频繁修改,要注意更新事件的频率。中心对齐模式下,更新事件发生在计数器过零和达到ARR时,一个周期两次。

死区时间一般不需要动态调整,但如果你用的是不同型号的MOS管,或者温度变化很大,可以考虑在运行时根据温度传感器读数微调DTG值。不过这个操作比较危险,建议只在实验室阶段尝试。

5. 实测波形分析与常见故障排查

5.1 互补波形和死区的示波器实测

我用四通道示波器同时抓了CH1、CH1N、CH2、CH2N的波形。在50%占空比下,CH1和CH1N的上升沿/下降沿交错,中间的死区平台清晰可见。CH2和CH1之间相差120度,CH3再差120度,三相互补关系正确。

有一个细节值得注意:中心对齐模式下,CH1的脉冲是居中对称的,也就是说高电平时间均匀分布在周期中心两侧。这跟边沿对齐下脉冲靠左或靠右完全不同。如果你用边沿对齐的思维去看中心对齐波形,可能会误以为占空比不对。

5.2 常见问题速查表

现象可能原因排查方法
CH1N无输出MOE位未使能检查BDTR寄存器bit15
上下管同时导通死区时间太短示波器看栅极信号交叠
PWM频率不对中心对齐公式未换算重新计算ARR
波形抖动严重中断优先级冲突检查是否有高优先级中断打断
电机低速啸叫死区时间过长减小DTG值,观察发热
刹车后无法恢复刹车中断未清除检查SR寄存器BIF位

5.3 刹车保护功能的验证

TIM1的刹车输入(BKIN)可以在故障时立刻封锁六路输出,保护功率级。我配置了PB12作为刹车输入,触发极性为高电平。测试时用一个按钮把PB12拉高,示波器上六路波形瞬间消失,MOE位被硬件清零。松开按钮后,需要手动重新使能MOE才能恢复输出。

这个功能在真实驱动板上非常关键,比如过流保护信号可以直接接到BKIN引脚,一旦过流,硬件在几百纳秒内封锁PWM,比软件中断快得多。但要注意,刹车输入有滤波配置,如果滤波太强,响应会变慢;滤波太弱,容易误触发。我一般设滤波长度为8个时钟周期,兼顾抗噪和速度。

6. 从实验板到真实驱动板的注意事项

6.1 栅极驱动电路的匹配问题

实验板上我用的是IR2104半桥驱动,它的输入逻辑是HIN和LIN不能同时为高,否则输出会直通。虽然TIM1已经保证了互补和死区,但IR2104内部还有约500ns的传播延迟,这个延迟会叠加在死区时间上。所以实际死区时间 = TIM1死区 + 驱动芯片延迟差。如果驱动芯片的高边和低边延迟不一致,死区效果会被削弱。

解决办法是选延迟匹配好的驱动芯片,或者在TIM1里预留更大的死区。我实测IR2104的高边延迟约680ns,低边延迟约520ns,差值160ns。加上我设置的200ns死区,实际有效死区约360ns,偏大但安全。如果换成延迟更小的驱动芯片,比如UCC27211,死区可以压缩到100ns以内。

6.2 PCB布局对PWM波形的影响

PWM频率20kHz,边沿上升时间约20ns,对应的频率分量可以到几十MHz。如果PCB走线太长或者回路面积太大,会产生振铃和辐射干扰。我的做法是:

  • 驱动芯片尽量靠近MOS管,栅极走线短而粗
  • 自举电容紧贴驱动芯片的VB和VS引脚
  • 高边和低边的功率回路分开走,最后在电源入口处单点汇合
  • 六路PWM信号线远离模拟采样线,必要时用地线隔离

踩过的坑:有一次我把PWM信号线和电流采样线平行走了5厘米,结果ADC采样值跳得厉害,电机低速时尤其明显。后来把采样线改成差分走线,并且远离PWM线,问题才解决。

6.3 热设计与死区时间的权衡

死区时间越长,体二极管续流时间越长,MOS管发热越严重。特别是在低速大电流工况下,死区期间的续流损耗可能占总损耗的30%以上。所以死区时间不是越大越好,要在直通风险和效率之间找平衡。

我的经验值是:对于20kHz到50kHz的电机驱动,死区时间取100ns到300ns比较合适。低压小功率场合可以取小一点,高压大功率场合取大一点。最终值一定要用示波器和热成像仪实测确认,不要只靠计算。

7. 几个容易被忽略的配置细节

7.1 重复计数器和更新事件的关系

TIM1有一个重复计数器RCR,在中心对齐模式下,更新事件发生的频率是PWM频率的1/(RCR+1)。如果你开了更新中断,RCR设得太大,中断响应会变慢。我一般设RCR=0,让每个PWM周期都产生更新事件,方便实时调整占空比。

但RCR=0时,更新中断频率等于PWM频率,20kHz下每50us进一次中断。如果你的中断服务程序太长,会占用大量CPU时间。这时候可以考虑用DMA来搬运CCR值,减少中断负担。

7.2 互补输出的关断状态配置

当MOE=0时,六路输出会进入空闲状态。空闲状态的电平由CR2寄存器的OISx和OISxN位决定。默认情况下,OISx=0表示空闲时输出低电平。但有些驱动芯片要求输入在关断时保持高电平才能确保MOS管关断,这时候就要把OISx设为1。

这个细节在调试刹车功能时特别重要。如果空闲电平设反了,刹车触发后MOS管反而导通,那就不是保护而是自杀了。

7.3 时钟源的稳定性

TIM1的时钟来自APB2总线,G431的APB2最高170MHz。如果你用了外部晶振,要确保晶振起振稳定后再启动PWM。我遇到过用内部RC振荡器时,PWM频率随温度漂移的问题,后来换成外部8MHz晶振,PLL倍频到170MHz,频率稳定性好了很多。

另外,如果系统里有其他高优先级中断频繁打断PWM更新,可能会导致占空比偶尔跳变。解决办法是把PWM更新中断的优先级设到最高,或者用DMA自动搬运数据。

8. 这套配置能直接用在哪些场景

六路互补PWM加死区,最典型的应用就是三相无刷电机驱动。无论是方波驱动还是正弦波驱动,六路互补都是基础。方波驱动下,每个电角度换相一次,六路输出按特定顺序通断;正弦波驱动下,三路占空比按正弦规律变化,互补路自动反相。

另一个场景是数字电源的同步整流。比如同步Buck电路,高边和低边MOS管需要互补PWM,死区时间防止直通。这时候PWM频率可能到几百kHz,死区时间要压缩到几十纳秒,对驱动芯片和布局要求更高。

还有一个不太常见但很有意思的应用:PWM背光调光。虽然背光一般只需要一路PWM,但如果你用推挽拓扑驱动背光,就需要两路互补。这时候死区时间可以设大一点,因为背光对效率不敏感,但对EMI有要求。

不管哪种场景,核心逻辑都是一样的:互补、死区、保护。把这三点吃透,换任何芯片都能快速上手。我在实际使用中发现,很多问题不是出在代码上,而是出在对硬件时序的理解偏差上。多抓波形、多算参数、多摸管子温度,比看十遍手册都管用。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进