4. PWM调光原理:PWM周期与占空比概念、定时器配置(ARR/CCR寄存器)、软件PWM与硬件PWM对比

说到LED调光,PWM绝对是绕不开的核心技术。我刚开始做照明项目时,总觉得PWM不就是高低电平切换嘛,有啥难的?结果第一次做呼吸灯效果,灯就闪得跟警车似的。后来才明白,PWM这东西,周期和占空比这两个参数,差一点都不行。

4.1 PWM周期与占空比:两个决定一切的参数

PWM的全称是脉冲宽度调制。说白了,就是让一个信号在高低电平之间来回跳变。你想想看,LED灯接在高电平上就亮,低电平上就灭。如果切换得足够快,人眼就反应不过来,看到的亮度就是平均亮度。

这里有两个关键参数:

  • 周期(Period):一个完整的高低电平循环的时间。单位通常是微秒或毫秒。
  • 占空比(Duty Cycle):高电平时间占整个周期的百分比。

举个例子。周期是1ms,高电平时间是0.5ms,那占空比就是50%。灯看起来就是半亮。如果高电平时间变成0.8ms,占空比80%,灯就更亮一些。

核心公式:

占空比 = 高电平时间 / 周期 × 100%

平均电压 = 高电平电压 × 占空比

我在项目中遇到过一个问题:用100Hz的PWM调光,结果手机摄像头一拍,画面在疯狂闪烁。这是因为100Hz的周期是10ms,刚好落在摄像头采样频率的敏感区。后来我把频率提到1kHz以上,问题就解决了。

经验之谈:人眼对50Hz以下的闪烁比较敏感,超过100Hz基本就感觉不到了。但如果你要配合摄像头或传感器,建议PWM频率至少1kHz以上。我一般习惯用1kHz到5kHz之间,既省电又稳定。

4.2 定时器配置:ARR和CCR寄存器

在MCU里实现PWM,靠的就是定时器。我常用的STM32系列,定时器里有两个关键寄存器:

  • ARR(Auto-Reload Register):自动重装载寄存器。它决定了PWM的周期。
  • CCR(Capture/Compare Register):捕获/比较寄存器。它决定了占空比。

工作原理是这样的:定时器从0开始向上计数,一直数到ARR的值,然后归零重新开始。在计数的过程中,当计数值小于CCR时,输出高电平;当计数值大于等于CCR时,输出低电平。

嗯,这里要注意:ARR和CCR的值,直接决定了PWM的频率和占空比。

频率计算公式:

PWM频率 = 定时器时钟频率 / (ARR + 1) / 预分频系数

占空比计算公式:

占空比 = CCR / (ARR + 1) × 100%

举个例子。假设定时器时钟是72MHz,预分频系数设为72-1,ARR设为999-1,那么:

PWM频率 = 72MHz / 72 / 1000 = 1kHz
占空比范围:0% ~ 100%,对应CCR值 0 ~ 999

配置代码大概长这样(以STM32为例):

// 定时器2配置为PWM模式
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

// 设置预分频和周期
TIM_TimeBaseStructure.TIM_Prescaler = 71;      // 72MHz / 72 = 1MHz
TIM_TimeBaseStructure.TIM_Period = 999;        // 1MHz / 1000 = 1kHz
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

// 配置PWM通道
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);

避坑指南:我曾经在配置ARR时忘了减1,结果频率算出来总是差一点。ARR的值是0到N,实际周期是N+1个时钟周期。同样,CCR的值也是从0开始算的。这个细节很容易被忽略,但差之毫厘谬以千里。

4.3 软件PWM与硬件PWM对比

做PWM调光,有两种实现方式:软件PWM和硬件PWM。我两种都用过,各有各的适用场景。

对比项 软件PWM 硬件PWM
实现方式 用定时器中断或延时,手动翻转IO口 利用MCU内置的定时器PWM外设
CPU占用 高,中断频繁,CPU被大量占用 极低,外设独立运行,几乎不占CPU
精度 受中断响应时间影响,精度有限 高,由硬件计数器保证
频率上限 较低,受CPU主频和中断开销限制 高,可达MHz级别
多通道支持 困难,需要软件管理多个通道时序 容易,一个定时器可输出多个独立PWM
适用场景 资源紧张、对精度要求不高的场合 大多数照明应用,尤其是多通道调光

软件PWM说白了就是「用CPU硬扛」。我在一个低成本项目里用过,MCU只有8个引脚,没有硬件PWM外设。当时用定时器中断,每100微秒进一次中断,在中断里判断当前是应该拉高还是拉低IO口。结果CPU几乎全花在PWM上了,其他功能基本跑不动。

硬件PWM就舒服多了。配置好定时器,它自己就跑了,CPU可以腾出手来处理通信、传感器、算法这些正事。我现在的项目,只要MCU有硬件PWM,我绝对不用软件模拟。

我的建议:

  • 如果MCU有硬件PWM外设,优先使用硬件PWM。省心、省力、效果好。
  • 如果实在没有硬件PWM,或者引脚不够用,才考虑软件PWM。但要做好CPU负载评估。
  • 软件PWM的频率不要超过1kHz,否则CPU基本就废了。

你想想看,一个照明系统往往要控制好几路LED,每路都要独立调光。如果用软件PWM,每路都要一个定时器中断,CPU忙得跟陀螺似的。硬件PWM一个定时器就能输出4路甚至更多,互不干扰,多好。

我记得有一次做RGB调光项目,三路PWM分别控制红绿蓝。用硬件PWM,配置好三个通道的CCR值,颜色就稳定了。如果用软件PWM,三路中断互相抢时间,颜色一直在抖,调了半天都调不准。

所以,我的结论很明确:能做硬件PWM就别用软件PWM。除非你真的是资源受限到极致,否则硬件PWM带来的稳定性和低CPU占用,绝对值回票价。