1. 六路互补PWM到底在解决什么问题
如果你手上有一块STM32G431的开发板,或者正在折腾无刷电机驱动板,那你大概率绕不开一个东西:六路互补PWM。听起来像是某种高级玩法,其实说白了,它就是让三个桥臂、六个开关管按照严格的时序轮流导通和关断,从而驱动电机旋转或者控制功率变换。但问题在于,如果只是简单地输出六路PWM,上下桥臂同时导通的那一瞬间,电源和地之间就相当于直接短路了,管子炸掉是分分钟的事。所以互补PWM的核心价值,就是在上下桥臂之间插入一段“谁都不导通”的死区时间,让开关管安全切换。
我这次做的实验,目标很明确:用STM32G431的TIM1高级定时器,输出三对互补PWM,也就是六路信号,并且验证死区时间是否按预期生效、互补关系是否正确、中心对齐模式下的波形是否稳定。为什么选TIM1而不是普通定时器?因为只有高级定时器才自带互补输出和死区插入功能,普通定时器如TIM2、TIM3虽然也能输出多路PWM,但互补和死区需要外部电路或者软件模拟,既不可靠也不精确。STM32G431的TIM1支持三对互补通道(CH1/CH1N、CH2/CH2N、CH3/CH3N),正好对应三相桥的六个开关管,这也是它在电机控制领域被广泛使用的原因。
这个实验适合谁看?如果你刚开始接触STM32的PWM配置,或者正在做电机驱动、电源变换相关的项目,又或者你之前只用过单路PWM输出、对互补和死区概念还比较模糊,那这篇内容应该能帮你把思路理顺。我会从定时器的基础配置讲起,一步步到死区时间的计算、中心对齐模式的选择、波形的实测验证,最后再聊聊实际调试中容易踩的坑。整个过程我会尽量用大白话解释,不堆砌寄存器手册里的原话,而是告诉你每一步为什么要这么做。
在开始之前,先明确一个概念:互补PWM不是简单地把一路PWM反相。反相只是逻辑上的取反,而互补输出是定时器硬件层面的一对通道,它们共享同一个计数器,但输出极性相反,并且可以在切换边缘插入死区。这个区别很关键,因为如果你用软件反相,死区时间很难精确控制,而且CPU占用率会很高。硬件互补则完全由定时器自动处理,你只需要配置好死区寄存器,剩下的交给硬件就行。
2. TIM1高级定时器的互补输出架构拆解
2.1 为什么普通定时器做不了六路互补
STM32的定时器分几类:基本定时器(TIM6、TIM7)只能计数,通用定时器(TIM2到TIM5)可以输出PWM但通道之间独立,高级定时器(TIM1、TIM8)才有互补输出和死区插入。这个分类不是随便定的,而是由硬件设计决定的。高级定时器内部有一个专门的“刹车和死区”模块,它接收来自通道的原始PWM信号,然后生成一对互补信号,并在两者之间插入可编程的死区延迟。通用定时器没有这个模块,所以它的通道只能各自独立输出,无法保证上下桥臂之间的安全切换。
你可以把高级定时器想象成一个交通指挥员,它不仅告诉车辆什么时候走,还确保在换方向之前有一段所有车都停下的时间。通用定时器就像六个独立的路口信号灯,各自按自己的节奏亮,没有协调机制。所以做电机驱动或者同步整流这类需要严格时序控制的应用,必须用高级定时器。
2.2 互补通道的硬件映射关系
STM32G431的TIM1有三对互补通道,分别对应:
| 通道对 | 主输出引脚 | 互补输出引脚 | 典型用途 |
|---|---|---|---|
| CH1/CH1N | PA8 | PA7 | 桥臂A上管/下管 |
| CH2/CH2N | PA9 | PA10 | 桥臂B上管/下管 |
| CH3/CH3N | PA11 | PA12 | 桥臂C上管/下管 |
这里有个细节需要注意:PA8是CH1的主输出,PA7是CH1的互补输出。但PA7默认功能是TIM1_CH1N还是TIM3_CH2?在STM32G431上,PA7的复用功能需要查数据手册确认。我实际配置的时候,用的是CubeMX,它会自动帮你处理引脚复用,但如果你直接写寄存器,就要先使能AF功能并选择正确的AF编号。G431的PA7对应TIM1_CH1N的AF是AF6,这个在参考手册的复用功能表里能查到。
另外,互补输出引脚不是随便选的,它们和主输出引脚有固定的配对关系。你不能把CH1的互补输出映射到任意引脚上,只能用芯片设计好的那几个。所以画PCB的时候,引脚分配要提前规划好,不然后面改起来很麻烦。
2.3 死区时间在硬件里是怎么实现的
死区时间的本质是一段延迟。当主输出从低变高时,互补输出不会立刻从高变低,而是等一段时间再变;反过来也一样。这段延迟由TIM1的BDTR寄存器里的DTG位控制,它是一个8位的字段,但并不是直接写时间值,而是通过一个分段公式来计算。
具体来说,DTG的值决定了死区时间的长度,计算公式分几段:
- 当DTG[7:5]=0xx时,死区时间 = DTG[7:0] × T_dts
- 当DTG[7:5]=10x时,死区时间 = (64 + DTG[5:0]) × 2 × T_dts
- 当DTG[7:5]=110时,死区时间 = (32 + DTG[4:0]) × 8 × T_dts
- 当DTG[7:5]=111时,死区时间 = (32 + DTG[4:0]) × 16 × T_dts
其中T_dts是定时器的时钟周期,通常等于1/72MHz或者1/144MHz,取决于你的时钟配置。这个公式看起来有点绕,但实际用CubeMX的时候,你直接填想要的死区时间,它会自动算出DTG的值。不过理解这个公式还是有用的,因为有时候你需要手动微调,或者验证CubeMX算出来的值对不对。
我一般会先确定开关管的死区需求。比如常用的IRF3205或者IRF540,开通延迟和关断延迟加起来大概在几百纳秒级别,所以死区时间通常设在500ns到1us之间。如果死区太小,上下管可能直通;如果太大,输出波形失真,电机效率下降。这个需要根据具体管子的数据手册来定,不能拍脑袋。
3. 从CubeMX配置到寄存器落地的完整链路
3.1 时钟树配置:TIM1的时钟从哪来
在配置TIM1之前,先要确保它的时钟源正确。STM32G431的TIM1挂在APB2总线上,APB2的时钟默认来自SYSCLK。如果你用CubeMX,在Clock Configuration页面可以看到APB2的时钟频率。假设系统时钟是170MHz,APB2预分频器设为1,那TIM1的时钟就是170MHz。但定时器的实际计数时钟还要看TIM1的预分频器(PSC)设置。
我一般会把PSC设成0,也就是不分频,让定时器以最高频率计数。这样PWM的分辨率最高,死区时间的调节也最精细。如果PSC设大了,虽然计数频率降低,PWM周期容易做长,但死区时间的步进也会变大,可能调不到你想要的精确值。所以除非PWM频率特别低,否则PSC尽量设小。
举个例子:如果TIM1时钟是170MHz,PSC=0,那计数周期是1/170MHz≈5.88ns。要生成20kHz的PWM,ARR值应该是170MHz/20kHz=8500。这个分辨率足够高,死区时间也能精确到几纳秒。
3.2 中心对齐模式的选择理由
PWM有两种计数模式:边沿对齐和中心对齐。边沿对齐就是计数器从0数到ARR,然后归零,波形是单边不对称的。中心对齐则是计数器从0数到ARR,再倒着数回0,波形是对称的。对于电机驱动来说,中心对齐模式有几个好处:
第一,它产生的谐波频谱更干净。边沿对齐的PWM在开关频率及其倍频处有较大的谐波分量,而中心对齐把这些分量分散了,电机的噪音和发热都会小一些。第二,中心对齐模式下,PWM的占空比更新可以在计数器归零或者达到ARR时进行,避免了边沿对齐中可能出现的脉冲宽度不一致问题。第三,对于互补输出,中心对齐能让死区时间在波形的两侧对称分布,上下管的导通时间更均衡。
当然,中心对齐也有代价:同样的ARR值,中心对齐的PWM频率是边沿对齐的一半。因为计数器要上下数两次才完成一个周期。所以如果你要20kHz的PWM,ARR要设成时钟频率/(2×20kHz)。这个在配置的时候别搞错了,不然频率会差一倍。
3.3 死区时间的计算与寄存器写入
假设我用的是IRF540N,查数据手册:开通延迟时间td(on)约10ns,上升时间tr约30ns,关断延迟时间td(off)约25ns,下降时间tf约20ns。最坏情况下,一个管子从导通到完全关断需要td(off)+tf=45ns,另一个管子从关断到完全导通需要td(on)+tr=40ns。所以死区时间至少要大于45ns,但实际中为了留余量,通常设100ns到500ns。
我这次实验设的是200ns。TIM1时钟170MHz,T_dts=5.88ns。200ns/5.88ns≈34。34在0到63之间,所以用第一段公式:DTG[7:0]=34,即0x22。写入BDTR寄存器的DTG字段即可。
在CubeMX里,你直接在Dead Time栏填200ns,它会自动算出DTG值。但你要注意,CubeMX填的是纳秒还是微秒,单位别搞错。我见过有人填了200以为单位是ns,结果实际是200us,波形完全不对。
3.4 刹车功能的配置要点
刹车功能是高级定时器的一个安全机制。当刹车引脚检测到有效电平时,定时器会立刻把输出置为安全状态(通常是高阻或者低电平),防止功率管在故障时继续导通。这个功能在电机驱动里非常重要,因为一旦出现过流或者短路,软件响应可能来不及,硬件刹车能在几个时钟周期内切断输出。
我这次实验虽然没接刹车引脚,但配置的时候还是把刹车功能使能了,并且设置了自动恢复模式。这样即使刹车触发,只要故障消失,PWM会自动恢复输出,不需要软件干预。刹车极性也要注意,默认是高电平有效还是低电平有效,要根据你的驱动电路来定。如果搞反了,正常工作时刹车一直触发,PWM根本出不来。
4. 实测波形的验证方法与异常分析
4.1 用示波器看互补波形的基本设置
验证六路互补PWM,最直接的工具就是示波器。我用的是四通道示波器,先看CH1和CH1N这一对。探头接PA8和PA7,地线夹好。示波器设置:时基先设成10us/div,电压档位5V/div,触发方式选CH1上升沿触发。
正常波形应该是:CH1高电平时,CH1N低电平;CH1低电平时,CH1N高电平。但在两者切换的边缘,会看到一段两者都是低电平的区域,这就是死区。死区时间应该和你配置的200ns一致。如果死区时间明显不对,或者根本没有死区,那就要检查BDTR寄存器的配置。
我实测的时候,第一次波形出来,死区时间只有几十纳秒,远小于200ns。查了半天,发现是CubeMX里Dead Time的单位选成了微秒,我填了200,实际是200us,但示波器上看到的却是几十纳秒,这明显矛盾。后来才发现,是我看错了示波器的时基,实际死区是200ns,只是波形太密,肉眼看着像几十纳秒。把时基放大到100ns/div,才看清楚死区的真实宽度。
4.2 中心对齐模式下波形不对称的排查
中心对齐模式下,理论上PWM波形应该是对称的。但我实测发现,CH1的高电平时间比低电平时间略长,不对称。一开始怀疑是死区时间导致的,但死区只影响切换边缘,不影响整体占空比。后来查资料才知道,中心对齐模式下,如果ARR值是奇数,计数器上下数的时候,中间那个点会多计一个数,导致波形不对称。解决办法很简单:把ARR设成偶数。
这个坑很隐蔽,因为CubeMX不会提醒你ARR的奇偶性。如果你用示波器看波形,发现占空比总是差那么一点点,不妨检查一下ARR的值。我后来把ARR从8501改成8500,波形立刻对称了。
4.3 互补输出引脚无信号的常见原因
有时候配置都对了,但互补引脚就是没波形。这种情况我遇到过几次,原因各不相同:
第一种,互补输出没使能。TIM1的CCER寄存器里,除了要使能主输出(CCxE),还要使能互补输出(CCxNE)。CubeMX里对应的选项是“Complementary Output”,要勾上。
第二种,刹车功能误触发。如果刹车引脚悬空或者电平不对,定时器会一直处于刹车状态,输出被强制关断。解决办法是检查刹车引脚的电平,或者暂时禁用刹车功能。
第三种,引脚复用没配置。PA7默认不是TIM1_CH1N,需要设置AF6。如果你用CubeMX,它会自动配置;如果直接写寄存器,别忘了先使能GPIO时钟和AF功能。
第四种,死区时间设得太大。如果死区时间超过了PWM周期的一半,互补输出可能一直处于无效状态。这种情况比较极端,但确实发生过。检查一下DTG值,确保死区时间远小于PWM周期。
4.4 用定时器中断验证PWM周期
除了示波器,还可以用定时器的更新中断来验证PWM周期。TIM1在每次计数器溢出或者归零时会产生更新中断。在中断里翻转一个GPIO,然后用示波器测这个GPIO的频率,应该等于PWM频率。这个方法的好处是不需要额外的引脚,而且能验证定时器的计数逻辑是否正确。
我一般会在中断里加一个计数器,每1000次中断翻转一次GPIO。这样GPIO的频率就是PWM频率的1/1000,更容易用普通万用表或者低频示波器测量。如果测出来的频率和预期不符,那就要检查PSC和ARR的计算是否正确。
5. 实际调试中那些手册不会告诉你的坑
5.1 死区时间与开关管发热的关系
死区时间设得太小,上下管直通,管子会瞬间发热甚至炸掉。这个大家都知道。但死区时间设得太大,也会导致管子发热,这个很多人不知道。原因是:死区期间,电机的续流二极管会导通,产生压降和损耗。死区时间越长,二极管导通的时间越长,损耗越大。所以死区时间要恰到好处,既要避免直通,又要尽量减少续流损耗。
我一般会先用一个较大的死区时间(比如1us)让电机转起来,然后用示波器观察上下管的驱动波形,逐步减小死区时间,直到刚好不直通为止。然后再留20%到30%的余量。这个过程需要耐心,但能帮你找到最优值。
5.2 互补PWM对电源纹波的影响
六路互补PWM工作时,三个桥臂的开关动作会在电源线上产生纹波。如果死区时间不对称,或者PWM相位不对,纹波会更大。我实测发现,中心对齐模式下,三个桥臂的PWM相位如果错开120度,电源纹波明显小于三者同相的情况。这是因为错相后,三个桥臂的电流需求不会同时达到峰值,电源的瞬时负载更平稳。
STM32的TIM1本身不支持三相互补PWM的自动错相,需要你在配置时手动设置每个通道的比较值,或者用定时器的重复计数功能来实现。这个在电机驱动里很常见,但如果你只是做简单的PWM输出,可能不需要考虑。
5.3 调试时如何安全地观察波形
调试功率电路的时候,安全永远是第一位的。我一般会先用低压电源(比如12V)给桥臂供电,并且串联一个限流电阻。这样即使发生直通,电流也被限制在安全范围内,不会炸管。等波形验证无误后,再逐步提高电压。
另外,示波器的探头地线要接对。如果你把地线夹在桥臂的输出端,而输出端不是真正的“地”,可能会引入干扰甚至短路。最好用差分探头,或者确保示波器的地和电路的地是同一个参考点。
5.4 从实验到实际项目的过渡建议
这个实验验证的是TIM1的六路互补PWM输出,但实际项目中,你还需要考虑更多东西:比如电流采样、过流保护、速度闭环控制等。我的建议是,先把PWM输出调稳,确保死区时间、频率、占空比都符合预期,然后再逐步加入其他功能。不要一上来就把所有东西都堆在一起,出了问题很难定位。
另外,STM32G431的TIM1还支持PWM输入捕获、编码器接口等功能,这些在电机控制里也很有用。如果你后续要做无刷电机的六步换相或者FOC控制,这个实验是一个很好的起点。把互补PWM和死区搞明白了,后面的路会顺很多。
我在实际使用中发现,CubeMX生成的代码虽然方便,但有些细节它不会帮你处理。比如死区时间的单位、ARR的奇偶性、刹车引脚的电平,这些都需要你自己检查。所以即使是用CubeMX,也要养成看参考手册的习惯,知道每个配置项背后的硬件行为是什么。这样出了问题,你才能快速定位,而不是盲目地改参数。
最后再分享一个小技巧:如果你手头没有示波器,可以用一个简单的RC低通滤波器把PWM变成模拟电压,然后用万用表测量。虽然精度不高,但能大致判断PWM有没有输出、占空比大概是多少。这个方法在紧急调试的时候挺管用,尤其是当你只需要确认PWM是否工作时。