news 2026/10/27 1:06:21

STM32G431六路互补PWM配置详解:死区时间计算与刹车功能调试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32G431六路互补PWM配置详解:死区时间计算与刹车功能调试

1. 为什么我要折腾六路互补PWM这个实验

如果你正在做无刷电机驱动、数字电源、逆变器或者任何需要三相桥式拓扑的项目,六路互补PWM几乎是绕不开的一道坎。我最近在基于STM32G431做一块无刷电机驱动板,板子打样回来之后第一件事就是验证TIM1能不能正常输出六路互补波形,死区时间能不能按预期插入,刹车保护能不能及时封波。这个实验看起来简单,但实际调起来坑不少,尤其是中心对齐模式下更新中断的触发时机、死区时间的计算、互补通道的极性配置这几个地方,稍不注意就会翻车。

所谓六路互补PWM,说白了就是三对互补信号,每对包含一个高边驱动和一个低边驱动,两者电平相反,中间插入一段死区时间防止上下管直通。STM32G431的高级定时器TIM1自带三对互补通道(CH1/CH1N、CH2/CH2N、CH3/CH3N),加上刹车输入和死区发生器,硬件层面就是为电机控制量身定做的。但硬件有归有,配置不对照样出不来波形,或者波形出来了但死区不对、极性反了、中断不触发,这些问题我在调试过程中基本都遇到了。

这篇文章适合谁看?如果你刚接触STM32的PWM配置,或者之前只用过普通PWM输出没碰过互补模式,又或者你已经配了互补PWM但波形总是不对,那这篇内容应该能帮你省下不少时间。我会从整体设计思路讲起,然后拆解关键配置细节,再给出完整的实操流程和参数计算过程,最后把我踩过的坑整理成排查表。所有代码基于STM32CubeMX生成的HAL库工程,芯片型号STM32G431CBU6,主频170MHz,定时器时钟170MHz。

2. 整体设计思路与方案选型

2.1 为什么选TIM1而不是其他定时器

STM32G431属于G4系列,定位就是电机控制和高精度电源,它内部有三个高级定时器:TIM1、TIM8和TIM20。这三个都支持互补输出、死区插入和刹车功能。我选TIM1的原因很简单:它是挂在APB2上的,时钟频率最高可以到170MHz,而且CubeMX对TIM1的支持最成熟,社区资料也最多。TIM8和TIM20虽然功能一样,但在某些封装上引脚复用不如TIM1灵活。

这里有个细节值得注意:G431的TIM1时钟源来自APB2总线,但并不是直接等于APB2频率。APB2的预分频器如果设为1,TIM1时钟就是170MHz;如果APB2预分频不为1,TIM1时钟会是APB2频率的2倍。我在CubeMX里把APB2预分频设为1,所以TIM1直接跑170MHz,这样PWM分辨率最高。

2.2 中心对齐模式的选择理由

PWM模式有边沿对齐和中心对齐两种。边沿对齐就是计数器从0数到ARR然后归零,波形不对称;中心对齐是计数器从0数到ARR再倒数回0,波形关于中心对称。做电机FOC控制必须用中心对齐,因为只有中心对齐才能在PWM周期中间触发ADC采样,采到的才是相电流的真实值。边沿对齐模式下,PWM周期内电流是变化的,采样时刻不同结果差很多。

中心对齐还有一个好处是谐波特性更好。边沿对齐的PWM频谱里除了基频和开关频率,还有很多低次谐波,中心对齐可以把这些谐波推高到开关频率附近,对电机噪音和EMI都有改善。代价是同样的ARR值下,中心对齐的实际PWM频率是边沿对齐的一半,因为一个完整周期需要上下各数一次。

2.3 死区时间的计算逻辑

死区时间是六路互补PWM最核心的参数之一。设得太小,上下管可能直通烧管子;设得太大,输出波形畸变严重,电机低速时尤其明显。死区时间的具体数值取决于你用的MOS管或IGBT的开关特性,包括开通延迟、关断延迟和驱动电路的传输延迟。

STM32的死区时间通过BDTR寄存器的DTG位配置,计算公式分四段:

DTG[7:5]死区时间计算公式适用范围
0xxDT = DTG[7:0] × tDTS0~127×tDTS
10xDT = (64 + DTG[5:0]) × 2×tDTS128~254×tDTS
110DT = (32 + DTG[4:0]) × 8×tDTS256~504×tDTS
111DT = (32 + DTG[4:0]) × 16×tDTS512~1008×tDTS

其中tDTS是死区时间基准时钟周期,由TIM1的时钟分频决定。我用的MOS管是IRF3205配IR2104半桥驱动,查手册开通延迟约50ns,关断延迟约80ns,驱动芯片传输延迟约200ns。保守起见死区时间取500ns左右。tDTS我设为1/170MHz≈5.88ns,500ns/5.88ns≈85,落在第一段范围内,所以DTG直接填85即可。

2.4 刹车功能的预留设计

刹车输入(BKIN)是高级定时器的安全特性,当检测到刹车信号时,硬件自动把所有输出通道拉到预设的安全电平,不需要软件干预。我在设计时把BKIN引脚引出来接到过流比较器的输出,一旦电流超过阈值,比较器翻转,TIM1立即封波。这个响应速度是纳秒级的,比软件中断快得多。

刹车后的恢复策略有两种:自动恢复和手动恢复。自动恢复是刹车信号消失后PWM自动恢复输出,手动恢复需要软件清除刹车标志再重新使能输出。电机驱动场景我建议用手动恢复,因为过流往往是故障状态,自动恢复可能导致反复过流烧管。CubeMX里把Automatic Output State设为Disable就是手动恢复。

3. 核心配置细节与CubeMX实操要点

3.1 时钟树配置与定时器时钟确认

打开CubeMX后第一件事是配时钟树。G431的外部晶振我用的24MHz,经过PLL倍频到170MHz作为SYSCLK。关键一步是确认APB2预分频器设为1,这样TIM1的时钟就是170MHz。如果你把APB2分频设成2,TIM1时钟会变成170MHz(因为定时器时钟是APB2的2倍),但其他外设频率会变,容易搞混。

配置完成后在Clock Configuration页面可以看到TIM1的时钟标注,确认是170MHz。这个数值直接决定后续PWM频率和死区时间的计算精度,一定要先确认再往下走。

3.2 TIM1参数设置:中心对齐模式1

在Timers选项卡里选TIM1,Clock Source选Internal Clock。然后配置以下参数:

  • Prescaler:0,不分频,计数器时钟170MHz
  • Counter Mode:Center Aligned Mode 1
  • Counter Period (ARR):4250
  • Internal Clock Division:No Division
  • Repetition Counter:1
  • auto-reload preload:Enable

ARR为什么取4250?中心对齐模式下PWM频率 = 170MHz / (2 × ARR) = 170M / 8500 = 20kHz。20kHz是电机驱动的常用开关频率,高于人耳听觉范围,同时开关损耗可以接受。如果你做数字电源可能需要更高的频率,那就减小ARR值。

Repetition Counter设为1意味着每两次计数溢出才产生一次更新事件。中心对齐模式下计数器先向上溢出一次再向下溢出一次,Repetition Counter=1让更新中断在每个完整PWM周期结束时触发一次,而不是上下各触发一次。这个细节很关键,设错了中断频率会翻倍。

3.3 通道配置:PWM模式1与互补输出

TIM1的CH1、CH2、CH3配置为PWM Generation CH1 CH1N这种互补模式。每个通道的具体参数:

  • Mode:PWM mode 1
  • Pulse (CCR):初始值设ARR的一半,即2125,占空比50%
  • Output compare preload:Enable
  • Fast Mode:Disable
  • CH Polarity:High
  • CHN Polarity:High
  • Idle State:Reset

PWM模式1和模式2的区别在于有效电平的定义。模式1是计数器小于CCR时输出高电平,模式2是计数器小于CCR时输出低电平。互补通道CH1N的输出逻辑与CH1相反,但具体极性还受CCR和极性位共同影响。我统一用模式1加高极性,这样CH1高电平时CH1N低电平,逻辑直观。

这里有个容易搞混的地方:CH Polarity和CHN Polarity都设为High时,CH1和CH1N是严格互补的。如果把CHN Polarity设为Low,CH1N的极性会反转,导致CH1和CH1N可能同时为高,那就危险了。所以互补输出的两个极性位必须设成一样的。

3.4 死区配置与BDTR寄存器

在Break and Dead Time配置页里:

  • Dead Time:85
  • Automatic Output:Disable
  • Break Polarity:High
  • Break Filter:0
  • OSSR State:Enable
  • OSSI State:Enable
  • Lock Configuration:Off

OSSR和OSSI这两个位控制运行模式和空闲模式下输出引脚的状态。OSSR=Enable表示运行模式下当输出被禁用时,引脚由定时器控制而不是释放为高阻;OSSI=Enable表示空闲模式下同样保持控制。对于电机驱动,这两个都使能比较安全,避免引脚悬空导致误触发。

Lock Configuration我设为Off,方便调试阶段随时改寄存器。量产时可以设成Level 1或更高,防止寄存器被意外修改。

3.5 更新中断与NVIC配置

在NVIC Settings里勾选TIM1 update interrupt。这个中断在每个PWM周期结束时触发,用来做FOC算法计算或者更新CCR值。中断优先级我设为1,比SysTick低一点,避免影响系统时基。

中心对齐模式下更新中断的触发时刻是在计数器从ARR倒数到0的时候,也就是PWM周期的中点。这个时刻正好对应相电流采样的最佳时机,所以ADC触发通常也配在这个点。如果你用TIM1的TRGO触发ADC,TRGO源选Update Event,ADC就会在更新中断的同时启动转换。

4. 完整实操流程与代码实现

4.1 CubeMX工程生成与初始化代码

配置完成后生成MDK或STM32CubeIDE工程。生成的代码里MX_TIM1_Init函数包含了所有配置。我习惯在main函数里先调用HAL_TIM_PWM_Start和HAL_TIMEx_PWMN_Start分别启动主通道和互补通道:

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,这个容易写错。如果只启动了主通道没启动互补通道,CH1N引脚不会有输出。

然后启动更新中断:

HAL_TIM_Base_Start_IT(&htim1);

4.2 死区时间的实测验证方法

代码跑起来之后怎么验证死区时间对不对?最直接的方法是用示波器双通道同时抓CH1和CH1N的波形。把两个通道的探头分别接到PA8和PB13(TIM1_CH1和CH1N的默认引脚),触发方式设为CH1上升沿,时基调到100ns/div左右。

正常的波形应该是:CH1上升沿之后经过一段死区时间,CH1N才下降;CH1下降沿之后同样经过死区时间,CH1N才上升。两段死区时间应该相等,实测值应该接近500ns。如果死区时间明显不对,检查DTG值是否算错,或者tDTS是否按预期分频。

我实测下来DTG=85时死区时间约490ns,和理论值500ns差10ns左右,这个偏差来自引脚输出延迟和探头误差,完全可以接受。

4.3 占空比动态调整与CCR更新

电机控制需要实时调整占空比,也就是动态改CCR值。由于我使能了Output compare preload,写入CCR的新值不会立即生效,而是在下一个更新事件时才加载到影子寄存器。这样做的好处是避免PWM周期中间改CCR导致波形出现毛刺。

更新CCR的代码:

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, new_ccr1); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, new_ccr2); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, new_ccr3);

这三个值通常来自FOC算法的SVPWM计算输出。注意CCR值不能超过ARR,否则占空比会饱和到100%。我在代码里加了限幅保护:

if (new_ccr1 > ARR_VALUE) new_ccr1 = ARR_VALUE; if (new_ccr1 < 0) new_ccr1 = 0;

4.4 刹车功能的测试与恢复流程

刹车功能测试很简单:把BKIN引脚(TIM1_BKIN默认是PB12)短接到地或拉高(取决于Break Polarity设置),观察PWM输出是否立即被封。我设的是High极性,所以给BKIN一个高电平,六路输出应该全部变成Idle State设定的电平。

刹车触发后会在SR寄存器里置位BIF标志,恢复流程是:

__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_BREAK); __HAL_TIM_MOE_ENABLE(&htim1);

第一句清刹车标志,第二句重新使能主输出。注意MOE位(Main Output Enable)在刹车触发时会被硬件自动清除,必须软件重新置位才能恢复输出。如果你用的是自动恢复模式,这两句就不需要了,但我不建议自动恢复。

4.5 更新中断服务函数的编写

更新中断里做两件事:一是触发ADC采样,二是执行FOC计算。简化版的ISR:

void TIM1_UP_TIM16_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE) != RESET) { __HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE); // 触发ADC或执行FOC foc_calculation(); } }

注意G431的TIM1更新中断和TIM16共用中断向量,所以函数名是TIM1_UP_TIM16_IRQHandler。这个在启动文件里可以查到,写错了中断不会进。

5. 常见问题与排查技巧实录

5.1 互补通道没有输出怎么办

这是最常见的问题。排查顺序如下:

排查项检查方法解决方法
MOE位是否使能读BDTR寄存器bit15调用__HAL_TIM_MOE_ENABLE
互补通道是否启动检查是否调用了HAL_TIMEx_PWMN_Start补上启动代码
引脚复用是否正确CubeMX里看引脚颜色重新配置GPIO
刹车是否触发读SR寄存器BIF位清标志并重新使能MOE
OSSR/OSSI设置检查BDTR配置都设为Enable

我遇到过一次互补通道没输出,查了半天发现是刹车引脚悬空导致BIF被置位,MOE被硬件清除了。把BKIN引脚加上下拉电阻之后就正常了。

5.2 死区时间实测与理论值偏差大

如果实测死区时间和计算值差很多,先确认tDTS的分频设置。CubeMX里Internal Clock Division如果设成了2或4,tDTS会相应变大,死区时间就跟着变大。我建议设成No Division,让tDTS=1/170MHz,计算最直观。

另一个可能的原因是探头和示波器带宽不够。测量500ns级别的死区时间,示波器带宽至少100MHz,探头要用1x档而不是10x档,否则上升沿被拉缓,测出来的死区时间偏大。

5.3 更新中断频率不对

中心对齐模式下更新中断的频率应该是PWM频率,即20kHz。如果你发现中断频率是40kHz,那说明Repetition Counter设成了0。设成1之后,每两个计数溢出才产生一次更新事件,中断频率就对了。

还有一种情况是中断根本不进。检查NVIC里TIM1 update interrupt有没有使能,中断优先级有没有被其他中断屏蔽,以及启动文件里的中断函数名是否匹配。

5.4 PWM波形有毛刺或抖动

波形毛刺通常来自几个方面:一是CCR更新时没有使能preload,新值立即生效导致占空比突变;二是死区时间太小,上下管切换时出现直通;三是电源去耦不足,开关瞬间电源跌落导致输出异常。

我的经验是CCR preload必须开,死区时间宁大勿小,电源引脚旁边至少放100nF加10uF的组合去耦。如果毛刺还在,用示波器的单次触发模式抓一下毛刺出现的时刻,看是否和更新中断或刹车信号相关。

5.5 刹车后无法恢复输出

刹车后恢复不了,八成是BIF标志没清或者MOE没重新使能。还有一个小概率情况是Lock Configuration设成了Level 1以上,BDTR寄存器被锁定,MOE写不进去。调试阶段把Lock设为Off,量产再根据需求锁定。

另外注意,如果刹车信号一直存在,即使清了BIF,MOE使能后硬件会立即再次触发刹车。所以恢复前要确认刹车源已经消失。

6. 实测波形分析与参数优化建议

6.1 六路波形的相位关系验证

用四通道示波器抓CH1、CH1N、CH2、CH2N的波形,可以看到CH1和CH2之间相差120度,CH1N和CH1互补,CH2N和CH2互补。中心对齐模式下,三路PWM的相位关系是关于周期中心对称的,这和边沿对齐的相位分布不一样。

验证相位关系的方法是测量CH1上升沿到CH2上升沿的时间差。20kHz下周期是50us,120度对应50us×120/360≈16.67us。实测值在这个附近就说明相位正确。

6.2 死区时间对输出波形的影响

死区时间会略微降低有效占空比。高边导通时间实际是CCR值减去死区时间,低边导通时间也是CCR值减去死区时间。在50%占空比时影响不大,但在接近0%或100%占空比时,死区时间会导致输出无法达到极限值。

如果应用需要接近100%的占空比,可以考虑在极端区域动态减小死区时间,或者用死区补偿算法在CCR里加上死区偏移。我在低速电机驱动里用了简单的补偿:高边CCR加死区值,低边CCR减死区值,实测波形对称性明显改善。

6.3 开关频率与死区时间的权衡

开关频率越高,死区时间占PWM周期的比例越大,波形畸变越严重。20kHz下500ns死区占周期的1%,影响很小;如果开关频率提到100kHz,500ns就占5%,低速时电机转矩脉动会很明显。

所以高频应用要选开关速度更快的MOS管,把死区时间压到100ns以内。但死区时间太小又有直通风险,需要根据管子的实际开关特性仔细权衡。我的建议是先用示波器测出管子的实际开通和关断延迟,然后死区时间取关断延迟的1.5倍左右,留足裕量。

6.4 中心对齐模式下的ADC采样时机

中心对齐模式下,计数器在ARR处向下溢出时触发更新中断,这个时刻PWM输出处于周期中点,三相下管全部导通,相电流在采样电阻上产生的压降最稳定。所以ADC采样应该紧跟在更新中断之后启动,转换时间要远小于死区时间,否则采到的值已经变了。

我用的ADC采样时间设为2.5个ADC时钟周期,总转换时间约1us,远小于500ns死区时间加PWM周期,采样精度足够。如果ADC转换太慢,可以降低分辨率或者提高ADC时钟。

7. 个人实操体会与后续扩展方向

这个实验做完之后我最大的体会是:六路互补PWM的配置本身不复杂,但细节特别多,任何一个参数设错都可能导致波形异常甚至烧管。死区时间的计算、MOE位的管理、刹车标志的清除这三件事必须搞清楚,否则调试时会很痛苦。

后续我打算在这个基础上加三电阻电流采样,配合TIM1的TRGO触发ADC,实现完整的FOC电流环。再往后是加上过流比较器的硬件刹车通路,把保护响应时间压到最短。如果你也在做类似的项目,建议先把六路互补波形调稳,确认死区时间和相位关系都正确,再往上叠算法,这样出问题的时候排查范围小很多。

最后分享一个小技巧:调试PWM的时候把示波器的余辉模式打开,可以很直观地看到波形的稳定性和抖动情况。如果余辉模式下波形边缘模糊,说明有抖动,需要查时钟源或者CCR更新逻辑;如果边缘清晰锐利,说明PWM输出很干净,可以放心往下做。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 1:06:18

工控人必知:字节与16进制详解及高低字节浮点数转换实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:06:13

滤波电路设计详解:RC/LC/EMI滤波与实际电感并联谐振实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:04:37

芯片时序路径分析实战:从PrimeTime到SDC签核

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:02:55

Matlab滤波器设计实战:从需求分析到Simulink部署的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:02:09

嵌入式I2C调试实战:从物理层到设备树的全栈排障指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:02:02

STM32+IMU实时姿态解算实战:Mahony滤波与上位机闭环调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华