第四章 PWM生成与死区控制:高级定时器配置、互补PWM输出、死区时间计算与设置
好,咱们进入第四章。这一章,说白了就是FOC的“动力输出”环节。你想想看,前面算了一大堆电流、电压、角度,最后靠什么去驱动电机?就是靠PWM。而PWM玩得转不转,直接决定了你的电机是安静地转,还是“滋滋”尖叫,甚至直接炸管。
我个人习惯把这一章叫做“玩火”的章节。为什么?因为PWM配置不好,轻则效率低下,重则MOS管直接冒烟。我刚开始做FOC驱动时,就吃过这个亏——死区时间设得太短,上下管直通,那味道,至今难忘。
4.1 高级定时器:FOC的“心脏起搏器”
FOC控制离不开高级定时器。普通定时器只能输出简单的PWM,但高级定时器能干三件大事:
- 互补PWM输出:一个通道输出两路相反的信号,控制上下桥臂
- 可编程死区插入:防止上下管同时导通
- 刹车与故障保护:出问题时能瞬间关断所有输出
我常用的STM32高级定时器是TIM1和TIM8。它们有6个通道,正好对应三相逆变器的6个MOS管。配置时,我一般会这样设置:
// 以TIM1为例,配置CH1、CH2、CH3为互补输出
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
// 1. 时基配置:PWM频率20kHz
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; // 中央对齐模式
TIM_TimeBaseStructure.TIM_Period = 4200 - 1; // 168MHz / 4200 = 40kHz,中央对齐等效20kHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
// 2. 输出比较配置:PWM模式1
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; // 占空比初始为0
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; // 互补通道极性
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
4.2 互补PWM输出:上下管的“默契配合”
互补PWM,说白了就是一对信号:一个高时另一个必须低。但实际电路中,MOS管关断需要时间。如果你让上管关断的同时立刻打开下管,那瞬间上下管都处于半导通状态——这就是直通,电流会像短路一样飙升。
所以我们需要死区时间。死区就是一段“谁都别导通”的空白期。我见过新手把死区设成0,结果一上电就炸管。嗯,这里要注意:死区不是越小越好,也不是越大越好。
互补PWM的配置要点:
- 极性设置:主输出和互补输出的极性可以独立配置。我一般都用高电平有效
- 空闲状态:当定时器停止时,输出引脚的电平状态。我习惯设为低电平,确保MOS管关断
- 刹车功能:硬件故障时,可以立即将输出强制为安全状态
避坑指南:我曾经在一个项目中,把互补输出的极性设反了。结果上管导通时下管也导通,死区完全没起作用。后来用示波器一看,两路信号完全同相,当场就明白了。所以配置完后,一定要用示波器看波形,别偷懒。
4.3 死区时间计算:不是拍脑袋定的
死区时间怎么算?很多人直接抄别人的参数,这是不对的。死区时间取决于三个因素:
- MOS管的关断延迟:数据手册里会有td(off)参数
- 驱动芯片的传播延迟:隔离驱动芯片也有延迟
- 电路寄生参数:布线电感、栅极电阻等
我一般这样估算:
// 死区时间估算公式
// 假设MOS管关断延迟 td_off = 50ns
// 驱动芯片延迟 t_prop = 100ns
// 安全裕量 50%
// 死区时间 = (50 + 100) * 1.5 = 225ns
// 实际配置时取整,比如250ns
// STM32高级定时器的死区寄存器是8位,单位取决于时钟
// 如果定时器时钟168MHz,一个计数周期约6ns
// 250ns / 6ns ≈ 42个计数周期
TIM_BDTRInitStructure.TIM_DeadTime = 42; // 约250ns
实际调试时,我建议这样操作:
- 先设一个保守值,比如500ns
- 用示波器观察上下管栅极波形,确保死区存在
- 逐步减小死区,直到波形开始出现“毛刺”或“尖峰”
- 回退到上一个安全值,再加20%裕量
4.4 死区配置实战:BDTR寄存器详解
STM32高级定时器的死区控制,核心是TIMx_BDTR寄存器。这个寄存器里藏着不少门道:
| 位段 | 名称 | 说明 |
|---|---|---|
| DT[7:0] | 死区时间 | 8位数值,具体时间取决于时钟分频 |
| LOCK[1:0] | 锁定配置 | 防止运行时误修改,我一般设为1级锁定 |
| OSSI | 空闲模式输出 | 定时器停止时,输出是否保持 |
| OSSR | 运行模式输出 | 刹车时输出状态 |
| BKE/BKP | 刹车使能和极性 | 故障信号输入配置 |
配置代码示例:
// BDTR寄存器完整配置
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; // 锁定,防止误改
TIM_BDTRInitStructure.TIM_DeadTime = 42; // 死区时间
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable; // 使能刹车功能
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRInit(TIM1, &TIM_BDTRInitStructure);
// 最后别忘了使能主输出
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
我的经验:LOCK位一定要设。我曾经在调试时不小心写错了寄存器,导致死区被清零,差点炸管。设了LOCK后,至少能保证运行时不会被意外修改。另外,刹车功能建议接上硬件过流信号,这是最后一道防线。
4.5 波形验证:别信代码,信示波器
代码写完了,配置也做了,但你怎么知道死区真的生效了?我告诉你,唯一可靠的方法就是看波形。
验证步骤:
- 把PWM占空比设到50%
- 用双通道示波器同时测量上下管的栅极
- 放大时间轴,看上升沿和下降沿之间是否有空白
- 测量死区时间是否和配置一致
我遇到过最坑的一次:代码里配了250ns死区,示波器一看只有50ns。查了半天,发现是定时器时钟分频设错了,导致死区时间计算偏差。所以,永远不要相信你的计算,要相信示波器。
好了,这一章的内容就这些。PWM生成和死区控制,是FOC从理论走向实践的关键一步。配置好了,电机才能安静、高效地转起来。下一章我们会讲电流采样,那是FOC的“眼睛”,咱们到时候见。