2. PWM 基础原理:占空比、频率与生成方式

PWM,全称 Pulse Width Modulation,脉宽调制。说白了,就是通过快速开关信号,让负载“感觉”到的是一个平均电压。我刚开始接触嵌入式时,总觉得这玩意儿不就是个方波嘛,有啥好讲的?后来调电机、调LED亮度,才发现里面的门道真不少。

这一节,咱们把 PWM 的三个核心参数——占空比、频率、分辨率——彻底讲透,再聊聊硬件 PWM 和软件 PWM 怎么选。

2.1 占空比与频率:PWM 的两个基本维度

先看一张最基础的 PWM 波形图,理解这两个参数到底在描述什么。

PWM 波形:占空比与频率示意 周期 T = 1 / 频率 高电平时间 t_on t_off 占空比 = t_on / T × 100% t_on t_off

占空比(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 的核心知识体系,方便你对照复习:

PWM 核心知识体系 PWM 脉宽调制 占空比 & 频率 PWM 分辨率 生成方式 高电平时间 t_on 周期 T = 1/f 8/10/12/16 位 调节步长 = V_max / 2^n 硬件 PWM 软件 PWM 选择建议 硬件 PWM:优先选择,不占 CPU,稳定可靠 软件 PWM:引脚不够或临时调试时使用,注意 CPU 负载 分辨率:够用就好,别盲目追求高位

2.6 避坑指南

最后,分享几个我踩过的坑:

  • 频率选错导致电机啸叫:我曾经把 PWM 频率设成 1kHz,电机发出刺耳尖叫声。后来查到人耳敏感区在 2~4kHz,把频率提到 20kHz 以上就听不到了。
  • 分辨率不够导致 LED 闪烁:用 8 位 PWM 调 LED 灯带,低亮度时肉眼可见闪烁。原因是低占空比下,脉冲太窄,LED 来不及完全熄灭就再次点亮。解决办法:提高频率或增加分辨率。
  • 软件 PWM 导致系统卡死:这个前面提过,中断里做太多事,主循环被饿死。后来我加了个标志位,中断里只置位,主循环里处理,才算解决。