3、PWM(脉宽调制)原理:用Python控制LED亮度
各位同学,今天我们来聊聊PWM。说白了,这就是个「假装模拟」的数字把戏。
你想想看,单片机引脚只能输出高电平(3.3V或5V)和低电平(0V)。那怎么让LED半亮呢?直接给一半电压?不行,数字电路没这功能。
嗯,这里有个巧妙的办法——快速开关。让LED在极短的时间内,一会儿全亮,一会儿全灭。只要开关频率够快,人眼就反应不过来,看到的亮度就是「平均亮度」。
我在项目中遇到过这种情况:用普通IO口控制LED呼吸灯,频率设低了,肉眼能看到闪烁,特别掉档次。后来把频率提到1kHz以上,效果就顺滑了。
3.1 PWM的三个核心参数
要理解PWM,记住三个词就够了:
- 频率(Frequency):一秒内开关多少次。单位Hz。频率太低会闪烁,太高浪费算力。
- 周期(Period):一次开关的总时间。周期 = 1 / 频率。
- 占空比(Duty Cycle):高电平时间占整个周期的百分比。0%就是全灭,100%就是全亮。
举个例子:频率1kHz,周期就是1ms。如果占空比50%,那就是0.5ms高电平,0.5ms低电平。LED看起来就是半亮。
核心公式:
平均电压 = 高电平电压 × 占空比
比如5V系统,占空比30%,平均电压就是5 × 0.3 = 1.5V
3.2 用Python生成PWM信号
我建议用Python的 time 模块来模拟PWM。虽然实际嵌入式开发会用硬件PWM模块,但用软件模拟能帮你彻底搞懂原理。
来看代码:
import time
import RPi.GPIO as GPIO # 树莓派GPIO库,没有硬件也能学原理
# 配置引脚
LED_PIN = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)
def software_pwm(pin, frequency, duty_cycle, duration):
"""
软件模拟PWM
:param pin: GPIO引脚号
:param frequency: PWM频率(Hz)
:param duty_cycle: 占空比(0-100)
:param duration: 运行时间(秒)
"""
period = 1.0 / frequency
high_time = period * (duty_cycle / 100.0)
low_time = period - high_time
cycles = int(duration * frequency)
for i in range(cycles):
GPIO.output(pin, GPIO.HIGH)
time.sleep(high_time)
GPIO.output(pin, GPIO.LOW)
time.sleep(low_time)
# 让LED以50%占空比呼吸5秒
software_pwm(LED_PIN, 1000, 50, 5)
GPIO.cleanup()
小提示: 实际用的时候,time.sleep() 精度有限。高频PWM(比如10kHz以上)建议用硬件PWM模块。我在做汽车尾灯项目时,用的就是STM32的硬件定时器,精度到纳秒级。
3.3 控制LED亮度:从暗到亮
理解了基础,我们来做个呼吸灯效果——LED从暗到亮,再从亮到暗,循环变化。
说白了就是不断改变占空比。从0%慢慢增加到100%,再降回来。
import time
import RPi.GPIO as GPIO
LED_PIN = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)
def breathing_led(pin, frequency=1000, step=2, delay=0.02):
"""
呼吸灯效果
:param step: 每次占空比变化量
:param delay: 每步间隔时间
"""
while True:
# 从暗到亮
for duty in range(0, 101, step):
software_pwm(pin, frequency, duty, delay)
# 从亮到暗
for duty in range(100, -1, -step):
software_pwm(pin, frequency, duty, delay)
# 注意:这里复用了上面的software_pwm函数
# 实际项目中建议用线程或异步方式,避免阻塞
try:
breathing_led(LED_PIN)
except KeyboardInterrupt:
GPIO.cleanup()
避坑指南: 我曾经在调试时发现LED亮度变化不平滑,检查半天发现是 step 设太大了。步长设小一点(比如1或2),变化会更细腻。另外,delay 值决定了呼吸速度,别设太小,否则人眼跟不上。
3.4 硬件PWM vs 软件PWM
做课程设计时用软件PWM没问题,但实际产品中,我强烈建议用硬件PWM。为什么?
| 对比项 | 软件PWM | 硬件PWM |
|---|---|---|
| 精度 | 受系统调度影响,不稳定 | 定时器硬件控制,精准 |
| 频率上限 | 一般不超过10kHz | 可达MHz级别 |
| CPU占用 | 高,一直占用CPU | 几乎不占CPU |
| 多通道支持 | 难,需要多线程 | 轻松支持多路 |
| 适用场景 | 学习、低频控制 | 工业、汽车、高频 |
你想想看,汽车尾灯流水效果里,每个LED都要独立控制亮度。如果用软件PWM,CPU得忙死。硬件PWM模块一启动,CPU就可以去干别的事了。
3.5 实际项目中的PWM配置建议
以汽车尾灯为例,我一般这样配置:
- 频率: 200Hz ~ 1kHz。太低会闪烁,太高LED驱动芯片可能响应不过来。
- 占空比分辨率: 8位(0-255)或10位(0-1023)。8位够用,10位更细腻。
- 渐变步长: 每次变化1-3个占空比单位,配合20-50ms的延时。
记住: PWM的精髓不是「调电压」,而是「调时间比例」。这个思路在电机控制、舵机控制、音频输出等领域都能用。
好了,这一章就到这里。下一章我们会把PWM和流水灯结合起来,做出真正的「呼吸流水」效果。到时候你就知道,为什么汽车尾灯看起来那么有质感了。