2. PWM 基础原理:占空比、频率与生成方式
PWM,全称 Pulse Width Modulation,脉宽调制。说白了,就是通过快速开关信号,让负载“感觉”到的是一个平均电压。我刚开始接触嵌入式时,总觉得这玩意儿不就是个方波嘛,有啥好讲的?后来调电机、调LED亮度,才发现里面的门道真不少。
这一节,咱们把 PWM 的三个核心参数——占空比、频率、分辨率——彻底讲透,再聊聊硬件 PWM 和软件 PWM 怎么选。
2.1 占空比与频率:PWM 的两个基本维度
先看一张最基础的 PWM 波形图,理解这两个参数到底在描述什么。
占空比(Duty Cycle),就是高电平时间占整个周期的百分比。50% 占空比,意味着信号一半时间高、一半时间低。我习惯用 LED 亮度来理解:100% 占空比就是最亮,0% 就是灭,中间值就是不同亮度。
频率(Frequency),就是 1 秒内信号重复了多少个周期。单位是 Hz。频率决定了 PWM 的“细腻程度”——频率太低,LED 会闪烁,电机可能会抖动;频率太高,开关损耗会变大。
关键公式:
- 周期 T = 1 / 频率 f
- 占空比 D = t_on / T × 100%
- 平均电压 V_avg = V_max × D
2.2 PWM 分辨率:你能调多细?
分辨率,决定了占空比的调节步长。举个例子:8 位 PWM,分辨率就是 256 级(0~255)。你想想看,如果只有 4 位(16 级),调 LED 亮度时就会一档一档地跳,很不平滑。
我在项目中遇到过用 8 位 PWM 调呼吸灯,效果还行。但后来做高精度电机控制,8 位就不够用了——速度波动肉眼可见。换成 16 位 PWM,立马顺滑。
| 分辨率 | 级数 | 典型应用 | 我的建议 |
|---|---|---|---|
| 8 位 | 256 | LED 调光、风扇调速 | 够用,但别要求太高 |
| 10 位 | 1024 | 舵机控制、简易电机 | 性价比不错 |
| 12 位 | 4096 | 精密电机、音频生成 | 我常用的选择 |
| 16 位 | 65536 | 高精度伺服、仪器仪表 | 性能过剩,看需求 |
注意:分辨率越高,定时器计数频率要求也越高。比如 16 位 PWM 在 100kHz 频率下,定时器时钟需要跑到 100kHz × 65536 ≈ 6.55MHz。别盲目追求高分辨率,够用就行。
2.3 硬件 PWM vs 软件 PWM:怎么选?
这是新手最容易纠结的问题。我直接说结论:能用硬件 PWM,就别用软件 PWM。但有些场景,软件 PWM 反而是唯一选择。
硬件 PWM
由 MCU 内部的定时器/计数器模块直接生成。CPU 只需要配置好寄存器,剩下的硬件自己搞定。
- 优点:不占 CPU、频率稳定、分辨率高、支持多通道同步
- 缺点:引脚固定、通道数有限、灵活性差
- 典型芯片:STM32 的 TIM 模块、ESP32 的 LEDC、AVR 的 Timer
软件 PWM
用 GPIO + 定时器中断,在中断服务函数里手动翻转电平。
- 优点:任意引脚都能用、通道数不受限、灵活
- 缺点:吃 CPU、频率受限、多路时抖动大
- 典型场景:引脚不够用、临时调试、低频应用
我的经验:做产品原型时,先看芯片数据手册的定时器资源。如果硬件 PWM 通道够用,直接上。不够用?可以考虑用 PCA9685 这类外部 PWM 驱动芯片,比软件 PWM 靠谱得多。
2.4 软件 PWM 实现示例(仅供理解)
虽然我不推荐在产品里用软件 PWM,但理解它的原理,能帮你更清楚硬件 PWM 在做什么。下面是一个极简的软件 PWM 伪代码:
// 伪代码:软件 PWM 实现
// 假设定时器每 10us 触发一次中断
uint8_t duty_cycle = 128; // 0-255, 50% 占空比
uint8_t counter = 0;
void timer_isr() {
counter++;
if (counter < duty_cycle) {
GPIO_SetPin(HIGH); // 输出高电平
} else {
GPIO_SetPin(LOW); // 输出低电平
}
if (counter >= 255) {
counter = 0; // 周期结束
}
}
看到了吗?每次中断都要做比较、翻转 GPIO。如果同时跑 4 路软件 PWM,中断里就要做 4 次比较。CPU 基本就被占满了。我早期做项目时就这么干过,结果主循环里的传感器读取全卡死了……后来老老实实换了硬件 PWM。
2.5 本章知识结构图
下面这张图总结了 PWM 的核心知识体系,方便你对照复习:
2.6 避坑指南
最后,分享几个我踩过的坑:
- 频率选错导致电机啸叫:我曾经把 PWM 频率设成 1kHz,电机发出刺耳尖叫声。后来查到人耳敏感区在 2~4kHz,把频率提到 20kHz 以上就听不到了。
- 分辨率不够导致 LED 闪烁:用 8 位 PWM 调 LED 灯带,低亮度时肉眼可见闪烁。原因是低占空比下,脉冲太窄,LED 来不及完全熄灭就再次点亮。解决办法:提高频率或增加分辨率。
- 软件 PWM 导致系统卡死:这个前面提过,中断里做太多事,主循环被饿死。后来我加了个标志位,中断里只置位,主循环里处理,才算解决。