1. PWM 基础概念:从波形到实战
各位工程师朋友,咱们今天聊聊 PWM。说实在的,这玩意儿在嵌入式领域太常见了,但很多人对它的理解就停留在「调占空比」这个层面。我刚开始做硬件那会儿也这样,直到有一次电机驱动板在启动瞬间烧了 MOSFET,才逼着我重新把 PWM 的底细摸清楚。
PWM,全称 Pulse Width Modulation,脉冲宽度调制。说白了,就是让数字信号输出一串宽度可调的方波。你想想看,单片机引脚只能输出 0 或 1,但通过快速切换高低电平,再配合外部电路,就能模拟出连续变化的模拟效果。嗯,这里要注意,PWM 本身不是模拟信号,它是数字信号,但它的平均效果是模拟的。
核心一句话:PWM 是用数字引脚「骗」出模拟效果的经典手段。频率决定响应速度,占空比决定输出幅度。
1.1 占空比与频率——两个最关键的参数
先讲占空比(Duty Cycle)。它定义了一个周期内高电平时间占总周期的百分比。比如 50% 占空比,就是一半时间高、一半时间低。我习惯用公式记:
占空比 D = T_on / T_period × 100%
频率(Frequency)呢,就是每秒有多少个完整的 PWM 周期。单位是 Hz。频率的倒数就是周期:
周期 T = 1 / f
举个例子:1 kHz 的 PWM,每个周期是 1 ms。如果占空比是 30%,那高电平就持续 300 μs,低电平 700 μs。
| 参数 | 定义 | 典型范围 | 我踩过的坑 |
|---|---|---|---|
| 占空比 | 高电平时间 / 周期 | 0% ~ 100% | 0% 和 100% 时,有些驱动芯片会异常 |
| 频率 | 每秒周期数 | 几十 Hz ~ 几百 kHz | 电机用太低频率会啸叫,LED 用太高会发热 |
| 分辨率 | 占空比可调步进 | 8-bit ~ 16-bit | 8-bit 只有 256 级,调光时肉眼可见跳变 |
警告:我曾经在一个项目中把 PWM 频率设到 200 kHz 去驱动一个普通直流电机,结果电机没转,MOSFET 先冒烟了。频率太高,电感的阻抗变大,电流上不去,但开关损耗却成倍增加。选频率一定要看负载特性。
1.2 典型应用场景——PWM 到底能干啥
PWM 的应用场景,我归纳成三大类。每一类我都吃过亏,分享出来你们少走弯路。
电机调速
这是 PWM 最经典的应用。通过改变占空比,改变电机两端的平均电压,从而控制转速。占空比越大,平均电压越高,转速越快。
- 频率选择:我一般选 1 kHz ~ 20 kHz。低于 1 kHz 电机可能会发出人耳可闻的嗡嗡声,高于 20 kHz 开关损耗明显上升。
- 避坑指南:我曾经用 50 Hz 去调一个直流无刷电机,结果电机没转起来,反而一直在抖动。后来查资料才知道,BLDC 需要更高的 PWM 频率来维持换相逻辑的稳定。
- 启动抖动:这是咱们这门课的核心话题。电机在启动瞬间,PWM 从 0% 突然跳到目标占空比,电流冲击很大,容易造成机械抖动甚至损坏驱动电路。
LED 调光
LED 调光靠的是人眼的视觉暂留效应。PWM 频率足够高时,人眼就看不到闪烁,只感觉到亮度变化。
- 频率要求:至少 100 Hz 以上,我习惯用 1 kHz。低于 60 Hz 人眼能明显感觉到闪烁,长时间看会头晕。
- 分辨率要求:调光应用对分辨率要求高。8-bit 只有 256 级,在低亮度区域跳变很明显。我建议至少用 10-bit 或 12-bit 的定时器。
- 避坑指南:有一次我做舞台灯光控制器,用了 200 Hz 的 PWM,结果摄像机一拍全是条纹。后来才知道,摄像机的帧率和 PWM 频率产生了差拍效应。解决办法是把 PWM 频率提高到 20 kHz 以上,或者与摄像机帧率同步。
开关电源
开关电源(DC-DC 转换器)的核心就是 PWM 控制。通过调整占空比来稳定输出电压。
- 频率范围:通常在几十 kHz 到几 MHz。频率越高,电感和电容可以越小,但开关损耗也越大。
- 占空比限制:开关电源的占空比一般不会做到 0% 或 100%,会留一个最小和最大限制。我见过一个电源设计,占空比跑到 95% 以上,结果电感饱和了,输出直接掉下去。
- 启动抖动:开关电源在启动时,PWM 占空比从 0 逐渐增大,这个过程叫软启动。如果软启动时间太短,输出电容充电电流过大,可能触发过流保护。
我的经验:不管用 PWM 驱动什么负载,启动阶段一定要做软启动处理。占空比从 0 开始,以一定斜率逐渐增加到目标值。这个斜率怎么算?看负载的电气时间常数。电机看 L/R,LED 看热时间常数,电源看输出电容的充电时间。
1.3 PWM 波形生成原理——定时器是怎么干的
大多数 MCU 都用定时器来生成 PWM。原理很简单:定时器从 0 计数到 ARR(自动重装载值),然后归零重新开始。计数过程中,比较寄存器 CCR 的值和当前计数值做比较:
- 计数值 < CCR:输出高电平
- 计数值 ≥ CCR:输出低电平
这样,ARR 决定了 PWM 的频率,CCR 决定了占空比。
// 以 STM32 为例,配置 TIM2 输出 PWM
// 频率 = 72 MHz / (PSC+1) / (ARR+1)
// 假设 PSC=71, ARR=999, 则频率 = 72M / 72 / 1000 = 1 kHz
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500; // 占空比 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
这段代码配置了一个 1 kHz、50% 占空比的 PWM。ARR=999,CCR=500,正好一半。如果你想改占空比,直接改 TIM_Pulse 的值就行。
注意:启动抖动往往发生在定时器刚使能、PWM 引脚从高阻或固定电平切换到 PWM 波形的瞬间。如果此时 CCR 的值还没稳定,或者定时器计数相位不确定,第一个脉冲的宽度就可能异常。这就是咱们后面要重点排查的问题。
1.4 本章知识体系
下面这张图把 PWM 的核心概念串起来了。我画图时习惯把「频率」和「占空比」放在中心,因为它们决定了 PWM 的一切行为。
从这张图能看出来,不管你是做电机控制、LED 照明还是电源设计,最终都会遇到同一个问题——启动抖动。频率选错了会抖,占空比突变会抖,定时器初始化顺序不对也会抖。咱们这门课,就是要把这些抖动的原因一个一个揪出来,再给出对应的排查和解决手段。