第四章 PWM原理与调试:频率、占空比与死区时间

各位工程师,咱们今天聊聊PWM。说实话,PWM这东西看着简单,但真正调起来,坑还真不少。我见过不少新手,上来就写代码,结果电机要么嗡嗡响不动,要么直接烧管子。嗯,咱们一步步来。

4.1 PWM频率与占空比的计算

先搞清楚两个核心参数:频率和占空比。

频率决定了PWM波形的快慢。对于电机控制,频率太低,电机会抖动、发出刺耳噪音;频率太高,开关损耗增大,驱动器发热严重。我个人习惯,直流有刷电机用10kHz-20kHz,步进电机用20kHz-40kHz,无刷电机(BLDC)用16kHz-20kHz。你想想看,20kHz以上人耳就听不到了,电机安静很多。

占空比就是高电平时间占整个周期的比例。占空比50%,意味着一半时间导通,一半时间关断。电机转速和占空比基本成正比——当然,实际还要考虑死区、管压降等因素。

举个例子:

假设PWM频率 f = 20kHz
周期 T = 1/f = 50μs
如果占空比 D = 60%
则高电平时间 Ton = T × D = 50μs × 0.6 = 30μs
低电平时间 Toff = T - Ton = 20μs

我在项目中遇到过一个问题:客户要求电机在1%占空比下启动,结果电机根本不转。为什么?因为占空比太低,有效电压不足以克服静摩擦力。后来我加了个软启动策略,从10%开始慢慢往上加,问题就解决了。

小技巧: 实际调试时,用示波器看PWM波形,别光靠代码算。代码里的占空比和实际波形可能差几个百分点,尤其是高频率下。

4.2 定时器配置

PWM的核心是定时器。以STM32为例,高级定时器TIM1和TIM8支持互补输出和死区插入,最适合电机控制。

配置步骤其实不复杂:

  1. 选择定时器时钟源:一般用内部时钟,比如72MHz
  2. 设置预分频器(PSC):降低时钟频率到合适范围
  3. 设置自动重装载值(ARR):决定PWM频率
  4. 设置比较值(CCR):决定占空比
  5. 配置输出模式:PWM1或PWM2,极性选择
  6. 使能互补输出和死区:如果驱动H桥或三相逆变器

代码示例(STM32 HAL库):

// 定时器初始化结构体
TIM_HandleTypeDef htim1;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 72 - 1;        // 72MHz / 72 = 1MHz
htim1.Init.Period = 50 - 1;           // 1MHz / 50 = 20kHz
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim1);

// PWM通道配置
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 25;                 // 占空比 25/50 = 50%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);

// 启动PWM
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);  // 互补输出

这里有个细节:预分频器和自动重装载值怎么搭配?我建议先确定目标频率,再反推参数。比如要20kHz,定时器时钟1MHz,那么ARR = 1MHz / 20kHz - 1 = 49。说白了,ARR+1就是计数值,决定了频率。

注意: 修改占空比时,要确保CCR值不超过ARR。我曾经遇到过CCR写成了ARR+1,结果PWM输出直接变成了常高电平,电机全速运转,差点把负载甩飞出去。嗯,血的教训。

4.3 死区时间设置

死区时间,这是电机驱动里最容易忽略但又最关键的一环。

为什么要死区?因为MOS管或IGBT开关需要时间。如果上管还没完全关断,下管就导通了,那就直通短路——电流瞬间飙升,管子直接冒烟。我刚开始做驱动时,就烧过好几个MOS管,后来才明白死区的重要性。

死区时间怎么算?

死区时间 ≥ 关断延迟时间 + 开通延迟时间 + 安全裕量

一般MOS管的关断延迟在50ns-200ns,开通延迟在20ns-100ns。安全裕量我习惯加50%。所以:

死区时间 ≈ (200ns + 100ns) × 1.5 = 450ns

实际工程中,我常用500ns-1μs。频率越高,死区时间要越短,否则占空比损失太大。比如20kHz下,1μs死区占整个周期的2%,意味着最大占空比只能到98%。

STM32配置死区:

// 死区时间寄存器设置
// 以TIM1为例,BDTR寄存器的DTG[7:0]位
// 死区时间 = DTG × Tdtg,其中Tdtg = 1/定时器时钟
// 假设定时器时钟1MHz,Tdtg = 1μs
// 要设置500ns死区,DTG = 0.5μs / 1μs = 0.5 → 取整为1
// 实际死区 = 1 × 1μs = 1μs

// HAL库配置
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 1;    // 死区时间1μs
sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);
关键点: 死区时间不是越大越好。太大,占空比损失严重,电机输出力矩下降;太小,有直通风险。我一般先设1μs,用示波器看波形,确认没有直通后,再逐步减小到500ns左右。

4.4 PWM波形测量与验证

代码写完了,参数设好了,怎么知道对不对?用示波器看。

测量步骤:

  1. 探头连接:地线夹接GND,探头接PWM输出引脚。注意地线要短,否则会引入噪声。
  2. 设置时基:根据频率调整。20kHz周期50μs,时基设10μs/div比较合适。
  3. 触发设置:边沿触发,上升沿,触发电平设在1.65V(3.3V系统的一半)。
  4. 测量参数:频率、占空比、上升时间、下降时间、死区时间。

验证要点:

  • 频率是否准确:示波器测出的频率和设定值偏差应在1%以内
  • 占空比是否线性:从0%到100%逐点测试,看实际占空比和设定值是否一致
  • 死区时间是否足够:用示波器的光标测量两路互补信号之间的间隔
  • 波形是否干净:有没有过冲、振铃、毛刺

我遇到过最典型的问题:示波器一看,PWM波形上有高频振荡。后来发现是探头地线太长,形成了环路天线。换成短地线弹簧夹,波形立马干净了。你想想看,有时候问题不在电路,在测量方法。

调试建议: 先空载测波形,确认无误后再接电机。接电机后波形可能会有变化,因为电机是感性负载,会产生反电动势。这时候要检查续流二极管是否正常工作。

最后说一句:PWM调试,说白了就是频率、占空比、死区三个参数的平衡。频率高了开关损耗大,低了噪音大;占空比大了力矩足但发热多;死区长了安全但效率低。没有绝对正确的参数,只有最适合你应用的参数。多试几次,多看看波形,慢慢就有感觉了。