这篇文章适合谁?

你刚点亮第一颗 LED,想让它「呼吸」起来;或者你在电机、舵机资料里反复看到 PWM,却始终没建立起直观印象。全文不推导公式,只用一张动图和两个生活类比把原理讲透。

一、为什么需要 PWM?GPIO 只会「开」和「关」

单片机的引脚是个「数字选手」,只会输出两种电压:0 V(低电平)和 3.3 V(高电平)。它没办法直接输出 1.7 V 这种「中间电压」。

那问题来了:想让 LED 半亮、想让电机半速、想让舵机停在 45°,怎么办?

答案是用「时间上的平均」换「电压上的中间值」——快速地开、关、开、关。只要切换得足够快,从 LED 亮度到电机转速,表现出来就是一个稳定的「中间效果」。这种技术就叫 PWM(Pulse Width Modulation,脉冲宽度调制)

一个生活类比

电灯的「开关」你每秒拨一次,灯就是一闪一闪;但你每秒拨 1000 次,人眼就「 averaging 」出了一个固定的亮度——拨开的时间越长,灯越亮。PWM 干的就是这件事,「拨开关的节奏」由定时器自动完成,完全不用 CPU 参与。

二、看懂动图:占空比才是主角

盯着下面这张动图看几秒,注意三个信息:方波的形状、蓝颜色「高电平」所占的比例、右侧 LED 的亮度——它们是联动的:

PWM 占空比与 LED 亮度动图演示

  • 高电平占整个周期的时间比例,就叫占空比(Duty Cycle),用 D 表示;
  • D = 60% 时,平均电压 ≈ 60% × 3.3 V ≈ 2.0 V,LED 看起来就是六成亮;
  • 动图里占空比从 10% 一路变到 90%,亮度跟着爬升——这就是呼吸灯的全部原理

那么「平均」是怎么发生的?下图给出了更工程的说法:如果接一个 RC 低通滤波(或者接上电机/LED 这类天然「惯性」负载),毛刺的方波就会被「抹平」成一条直流:

PWM 占空比决定平均电压

三、三个关键词:频率、占空比、分辨率

关键词含义新手记忆点
频率 f每秒重复多少个「开-关」周期决定「闪不闪」:LED 调光建议 ≥ 1 kHz,否则肉眼可见频闪
占空比 D一个周期内高电平的时间占比决定「多亮 / 多快」:呼吸灯就是在不断改 D
分辨率占空比能分多少级,由计数器位数决定8 位 → 256 级,12 位 → 4096 级,越多「渐变」越细腻

三个量的关系可以这么理解:频率保证「看不见闪」,占空比负责「调多亮」,分辨率决定「能调多细」

四、在 STM32 上怎么用?

STM32 的定时器(TIM)就是为 PWM 而生的硬件。最小配置只需要想清楚三个寄存器的对应关系:

PWM 频率  = 定时器时钟 / (PSC + 1) / (ARR + 1)
占空比 D  = CCR / (ARR + 1)
  • PSC(预分频):把 72 MHz 的时钟先「降档」;
  • ARR(自动重装载):决定一个周期数多少个节拍 → 和 PSC 一起决定频率;
  • CCR(捕获比较寄存器)改它就是改占空比。呼吸灯程序本质上就是一个循环里让 CCR 从 0 加到 ARR,再从 ARR 减到 0。

用 CubeMX 配置时:选一个 TIM → Channel 设为 PWM Generation CHx → 填 PSC/ARR → 生成代码,然后只需要一行核心调用:

HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);   // 启动 PWM 输出
 
for (uint16_t d = 0; d < 1000; d++) {        // 渐亮
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, d);
    HAL_Delay(1);
}

验证小技巧

没有示波器也能验证:把 PWM 输出接到 LED 上,烧录后看灯是否平滑呼吸。如果看到明显闪烁,先检查频率是不是算低了(比如只有 10 Hz)。

五、新手最常踩的三个坑

  1. 频率设太低:LED 调光低于 100 Hz 会看到闪烁,电机低于几 kHz 会「啸叫」。LED 用 1 kHz 起步,电机用 1020 kHz 起步。
  2. 以为 PWM 引脚随便选:PWM 功能挂在特定定时器的特定通道上(看数据手册的 AF 复用表),不是每个 GPIO 都能输出 PWM。
  3. 直接用 PWM 驱动大电流负载:引脚只能拉/灌几十 mA,带电机、灯带要经三极管/MOS 管/驱动芯片中转,并加续流二极管。

六、小结

  • PWM = 用极快的开关 + 占空比,在数字引脚上「调」出模拟效果;
  • 占空比决定平均电压:Vavg = D × VCC
  • STM32 上:PSC/ARR 定频率,CCR 定占空比,改占空比就能做呼吸灯、调速、控舵机。

下一篇我们会继续用「按键」这个最简单的外设,讲讲硬件里另一个新手必经之路——按键消抖:为什么按一下却被识别了多次