
1. 六路互补PWM到底在做什么先把概念说清楚。六路互补PWM指的是三对、共六路PWM信号每一对里两路波形在理想情况下完全反相也就是常说的“互补”。这三对信号通常对应三相桥式电路的三个桥臂每个桥臂有上下两个开关管上管开的时候下管必须关下管开的时候上管必须关。如果这两路信号同时导通那就是直通电源正负极之间被短路轻则烧管子重则炸板子。所以六路互补PWM的核心价值不是“输出六路波形”这么简单而是用硬件机制保证同一桥臂上下管不会同时导通。这个硬件机制就是死区时间——在上下管切换的瞬间强制插入一段两路都关断的空白期等一个管子彻底关断之后再让另一个管子开通。我这次实验用的是STM32G431高级定时器TIM1。选它的原因很直接TIM1是高级控制定时器自带互补输出和死区插入功能还有刹车输入是做电机驱动、数字电源这类应用的标配。通用定时器虽然也能输出PWM但互补和死区要靠软件或者外部逻辑去凑既占CPU又不可靠。这个实验适合谁看如果你正在做无刷电机驱动、三相逆变、数字电源或者单纯想搞明白STM32的高级定时器互补输出怎么配那这篇内容可以直接拿去参考。如果你只是想让一个LED呼吸灯闪起来那用不到六路互补普通PWM就够了不必杀鸡用牛刀。我先把这次实验的目标列出来后面所有配置都围绕这几个目标展开用TIM1的CH1/CH1N、CH2/CH2N、CH3/CH3N输出三对互补PWM中心对齐模式方便后续做电机FOC控制死区时间可调先设一个保守值实测波形后再优化用示波器验证六路波形的互补关系和死区是否真实存在留出刹车输入接口为后续故障保护做准备。2. 方案选型与整体设计思路2.1 为什么是TIM1而不是普通定时器STM32G431属于G4系列主打混合信号和电机控制。它内部有几个定时器TIM1和TIM8是高级控制定时器TIM2/3/4是通用定时器TIM6/7是基本定时器。高级定时器和通用定时器最大的区别就在互补输出这块。通用定时器每个通道只有一个输出引脚你要做互补得自己再找一个引脚用软件在中断里翻转或者外挂反相器。软件翻转的问题在于延迟不确定中断响应有抖动死区时间根本没法精确控制。外挂反相器倒是硬件反相了但死区还得另外加RC延迟或者逻辑门电路复杂不说温度漂移也会影响死区精度。TIM1就不一样了。它每个通道自带一个互补输出引脚比如CH1对应PA8CH1N对应PB13具体引脚要看封装和复用表。死区时间由BDTR寄存器里的DTG位段控制纯硬件生成精度是定时器时钟周期级别不受中断和软件影响。刹车输入也是硬件直接封波响应速度是纳秒级软件根本追不上。所以做六路互补只要芯片里有高级定时器就优先用它。这是最省事、最可靠的路子。2.2 中心对齐模式的选择理由PWM有两种常见对齐方式边沿对齐和中心对齐。边沿对齐就是计数器从0数到ARR然后归零重新开始。PWM的跳变都发生在计数器的起点或者终点波形不对称。这种模式配置简单适合LED调光、简单电机调速这类对波形对称性没要求的场景。中心对齐是计数器先从0数到ARR再从ARR倒数回0形成一个三角波。PWM的跳变发生在三角波的上升沿和下降沿波形关于中心对称。这种模式有两个好处一是谐波分布更优电机运行更平稳噪音更小二是配合ADC采样时可以在计数器过零或者峰值时触发采样避开开关噪声。我做这个实验是为了后续的无刷电机驱动所以直接上中心对齐。虽然配置稍微麻烦一点但后面省事。2.3 死区时间的计算逻辑死区时间不是随便填一个数就完事它跟你的开关管特性、驱动电路、母线电压都有关系。算少了直通风险还在算多了输出波形畸变电机效率下降。死区时间的下限由开关管的关断时间决定。比如一个MOS管数据手册里写关断延迟时间td(off)是100ns下降时间是50ns那从栅极信号撤去到漏极电流真正截止大概需要150ns。这150ns就是死区时间的理论下限。实际取值要留余量通常取1.5到2倍也就是225ns到300ns。死区时间的上限由输出波形质量决定。死区期间桥臂中点电压由负载电流方向决定如果死区太长输出电压和理想值偏差就大低速时尤其明显电机会有转矩脉动。STM32的BDTR寄存器里DTG是一个8位字段分几种编码方式。以G431为例定时器时钟假设是170MHz死区时间计算公式分几段DTG[7:5]0xx时死区时间 DTG[7:0] × tDTSDTG[7:5]10x时死区时间 (64 DTG[5:0]) × 2 × tDTSDTG[7:5]110时死区时间 (32 DTG[4:0]) × 8 × tDTSDTG[7:5]111时死区时间 (32 DTG[4:0]) × 16 × tDTS。tDTS是死区时钟周期通常等于定时器时钟周期。170MHz下tDTS约5.88ns。如果我要300ns死区用第一段公式DTG值取300/5.88≈51也就是0x33。这个值后面在代码里会体现。3. 核心配置细节与实操要点3.1 时钟与定时器基础配置G431的主频可以跑到170MHz。我用的是外部晶振加PLL配置成170MHz。TIM1挂在APB2总线上APB2的时钟也是170MHz所以定时器时钟就是170MHz不需要额外倍频。这里有个细节要注意STM32的定时器时钟并不总是等于APB总线时钟。如果APB预分频系数是1定时器时钟等于APB时钟如果APB预分频系数大于1定时器时钟是APB时钟的2倍。G431的APB2预分频通常设为1所以TIM1时钟就是170MHz。这个在CubeMX里会自动算好但你要心里有数不然算死区时间的时候会差一倍。PWM频率我设的是20kHz。这个频率高于人耳听觉范围电机运行不会有啸叫。计算ARR值中心对齐模式下PWM频率 定时器时钟 / (2 × ARR × (PSC1))。PSC取0则ARR 170MHz / (2 × 20kHz) 4250。所以ARR设为4250-14249。3.2 互补输出与死区配置在CubeMX里TIM1的Channel1、Channel2、Channel3都设为“PWM Generation CHx CHxN”模式。这个选项就是同时使能主输出和互补输出。CH1对应PA8CH1N对应PB13CH2对应PA9CH2N对应PB14CH3对应PA10CH3N对应PB15。具体引脚以你用的封装为准G431的引脚复用比较灵活CubeMX会自动分配。死区时间在CubeMX的Break and Dead-Time Settings里配置。Dead Time设为51对应前面算的约300ns。Automatic Output自动输出要开启否则需要软件使能MOE位才能输出。Off State Selection选“When inactive, CHxN is driven to inactive level”这样刹车时互补输出会被强制到安全电平。刹车功能我暂时没接实际信号但配置里把刹车输入使能了刹车极性设为高电平有效滤波设为0。后面做故障保护时只要把刹车引脚接到过流比较器的输出上一旦过流硬件立刻封波不需要软件干预。3.3 中心对齐模式的寄存器设置CubeMX里Counter Mode选“Center Aligned mode 1”。模式1和模式2的区别在于输出比较中断的触发时机模式1在计数器向上计数和向下计数都触发模式2只在向下计数触发。对于PWM输出本身两者波形一样只是中断行为不同。我用模式1方便后续在中断里做电流采样。中心对齐模式下比较值CCR决定占空比。占空比 CCR / ARR。初始我设CCR为ARR的一半也就是2125输出50%占空比。这样示波器上看上下管波形对称容易判断死区。3.4 输出使能与保护机制TIM1有个MOEMain Output Enable位在BDTR寄存器里。这个位不清零PWM不会输出。CubeMX生成的代码里HAL_TIM_PWM_Start之后还需要调用__HAL_TIM_MOE_ENABLE或者用HAL_TIMEx_PWMN_Start这类函数。我踩过的坑是只调了HAL_TIM_PWM_Start结果主输出有波形互补输出没波形查了半天才发现MOE没开。刹车输入触发后MOE会被硬件清零PWM立刻停止输出。要恢复输出得先清除刹车标志再重新置位MOE。这个流程在刹车中断里做后面做保护时会详细说。4. 完整实操流程与波形验证4.1 CubeMX配置步骤打开CubeMX选STM32G431CBU6我用的芯片型号。系统时钟配置成170MHz具体是HSE 8MHz经过PLL倍频。TIM1配置如下Clock Source: Internal ClockChannel1: PWM Generation CH1 CH1NChannel2: PWM Generation CH2 CH2NChannel3: PWM Generation CH3 CH3NCounter Settings: Prescaler0, Counter ModeCenter Aligned mode 1, Period4249PWM Generation Channel1: ModePWM mode 1, Pulse2125, Output compare preloadEnable, Fast ModeDisable, CH PolarityHigh, CHN PolarityHigh, Idle StateResetBreak and Dead-Time: Dead Time51, Automatic OutputEnable, Off State SelectionInactive, Break InputEnable, Break PolarityHigh, Break Filter0生成代码后在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_TIM_MOE_ENABLE(htim1);注意最后那句MOE使能少了它互补输出不会出来。4.2 示波器实测波形分析我用的是四通道示波器先看CH1和CH1N这一对。探头接PA8和PB13地夹接板子地。触发设在CH1上升沿时基调到5us/div。实测波形CH1高电平期间CH1N是低电平CH1下降沿之后CH1N并没有立刻上升而是等了一段时间才上升。这段两路都是低电平的时间就是死区。用光标测量大约300ns和设计值吻合。再看CH1和CH2的关系。中心对齐模式下CH1和CH2的相位差是120度。20kHz对应周期50us120度就是16.67us。示波器上CH2的上升沿确实比CH1滞后约16.7us。CH3再滞后16.7us。三路波形均匀分布说明中心对齐和比较值设置正确。有个现象要注意死区期间桥臂中点电压不是零而是由负载电流方向决定的。如果负载是感性电流不能突变会通过续流二极管续流中点电压被钳位到母线正极或者负极。所以死区不仅影响波形还影响输出电压。做电机控制时这个非线性要在算法里补偿。4.3 死区时间的实测与调整前面算的300ns是理论值实际电路里驱动芯片也有延迟。我用的栅极驱动芯片开通延迟约200ns关断延迟约150ns。这意味着即使MCU输出没有死区驱动芯片本身也会引入不对称延迟。所以实际死区要加上这个差值。我把死区从300ns逐步加大到500ns观察波形。300ns时上下管切换处有轻微重叠说明驱动芯片的关断延迟比开通延迟长需要额外死区。加到500ns后重叠消失波形干净。但再加大到800ns输出波形明显畸变占空比偏离50%较多。所以最终定在500ns对应DTG值约85也就是0x55。这个调整过程说明一个道理死区时间不能只按MCU手册算要把驱动电路和开关管的延迟一起考虑。最可靠的方法就是示波器实测看切换点有没有重叠或者畸变。4.4 刹车功能验证刹车输入我暂时用按键模拟。把刹车引脚配置成上拉输入按键按下时拉低。在刹车中断里翻转一个LED表示刹车触发。实测按下按键六路PWM立刻停止输出所有输出引脚变成配置的Idle State。松开按键清除刹车标志重新使能MOEPWM恢复输出。整个过程硬件响应没有软件延迟。这里有个细节刹车中断里必须清除刹车标志位否则MOE无法重新置位。清除标志用__HAL_TIM_CLEAR_FLAG(htim1, TIM_FLAG_BREAK)。这个标志在SR寄存器里不清的话下次刹车中断不会触发。5. 常见问题与排查技巧实录5.1 互补输出没有波形怎么办这是最常见的问题。排查顺序如下先确认MOE位是否置位。用调试器看BDTR寄存器的bit15如果是0说明主输出没使能。调用__HAL_TIM_MOE_ENABLE即可。确认互补输出引脚是否配置正确。有些引脚默认功能不是TIM1_CH1N需要在CubeMX里检查引脚复用。确认HAL_TIMEx_PWMN_Start是否调用。只调HAL_TIM_PWM_Start不会启动互补输出。如果主输出有波形但互补没有检查CHN Polarity设置。如果设成Low互补输出极性反了示波器上可能看不到预期波形。5.2 死区时间不准确怎么排查死区时间偏差大通常有几个原因定时器时钟不是预期的频率。检查APB预分频和PLL配置确认TIM1时钟。DTG值算错。对照参考手册的公式重新算注意不同DTG[7:5]区间的公式不一样。驱动芯片延迟没算进去。用示波器实测切换点根据重叠情况调整DTG值。探头接地不良。高频下探头地线太长会引入振铃影响测量。用弹簧地针代替鳄鱼夹。5.3 中心对齐模式下占空比不对中心对齐模式下占空比 CCR / ARR。但要注意如果CCR大于ARR输出会一直是高电平如果CCR为0输出一直是低电平。我一开始把CCR设成ARR1结果波形一直是高查了半天才发现是CCR超了。另外中心对齐模式下PWM频率 定时器时钟 / (2 × ARR × (PSC1))。这个2是因为计数器上下都数。如果你按边沿对齐的公式算频率会差一倍。5.4 刹车触发后无法恢复输出刹车触发后MOE被硬件清零同时刹车标志置位。要恢复输出必须清除刹车标志重新置位MOE如果刹车输入还在有效电平MOE无法置位必须先解除刹车信号。我遇到过一次按键松开后PWM没恢复原因是刹车标志没清。在中断里加上清除标志的代码后正常。5.5 常见问题速查表现象可能原因排查方法互补输出无波形MOE未使能检查BDTR bit15互补输出无波形未调用PWMN_Start检查启动代码死区时间偏差大定时器时钟不对确认APB预分频死区时间偏差大DTG计算错误对照手册重算占空比不对CCR超过ARR检查CCR值刹车后不恢复刹车标志未清清除TIM_FLAG_BREAK波形有振铃探头接地不良用弹簧地针6. 实操心得与后续扩展这个实验做完我最深的体会是六路互补PWM的难点不在配置而在验证。CubeMX点几下就能生成代码但波形对不对、死区够不够、刹车灵不灵必须用示波器一处处看。我见过太多人代码跑通了就以为万事大吉结果上电一跑就炸管子就是因为没实测死区。另一个心得是死区时间要留余量但不能过头。余量不够直通风险还在余量太大输出畸变。我的做法是先按理论值算然后实测切换点逐步加大到重叠消失再稍微加一点余量最后定值。这个过程花不了多少时间但能避免很多返工。后续这个平台可以往几个方向扩展。一是把刹车输入接到过流比较器上做过流保护二是加入ADC采样在中心对齐的过零点触发做电流环控制三是把PWM频率提高到40kHz配合更快的驱动芯片减小电流纹波。这些扩展都建立在六路互补PWM稳定输出的基础上所以先把这一步做扎实后面才走得顺。最后分享一个小技巧调试PWM时把示波器的余辉模式打开可以同时看到波形的长期稳定性和瞬态抖动。如果余辉模式下波形边缘模糊说明有抖动可能是时钟不稳定或者中断干扰。这个技巧在调电机驱动时特别有用能提前发现很多隐藏问题。