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占用,绝对值回票价。