
PWM(Pulse Width Modulation,脉冲宽度调制) 是一种通过改变数字信号的“高电平持续时间”来模拟连续电压的技术。由于单片机或微控制器的 GPIO 引脚通常只能输出两种状态高电平1,低电平0,如果想要调节 LED 亮度、电机转速或舵机角度等连续变化的量,直接输出数字信号是无法实现的。PWM 通过极高频率地快速切换高低电平,利用人眼的视觉暂留(或电机的机械惯性),实现等效的“模拟电压”输出。
PWM 通过快速切换引脚的高低电平来工作,周期:完成一次高低电平切换的时间,占空比:在一个周期内高电平所占的百分比,例如:50% 占空比表示高低电平各占一半时间,树莓派 Pico 上几乎所有 GPIO 引脚都可以配置为 PWM 输出,每个引脚都关联到一个 PWM 通道。
周期(Period, T): 一次完整的“高电平 + 低电平”所持续的时间。
频率(Frequency, ): 一秒内PWM信号重复次数,公式为 f=T1 在控制LED亮度时,频率通常设置为 100Hz~1000Hz以上,避免感知闪烁
占空比(Duty Cycle): 在一个周期内,高电平持续时间占总周期时间的百分比。
0% 占空比:引脚全程输出 0V,LED 熄灭。
50% 占空比:一半时间 3.3V,一半时间 0V,等效平均电压为 $3.3\text{V} \times 50\% = 1.65\text{V}$,LED 半亮
100% 占空比: 引脚全程输出 3.3V,LED 最亮
控制led呼吸示例,可使用 MicroPython 内置的 machine.PWM 和 machine.Pin 类即可轻松实现呼吸灯(LED 亮度逐渐变亮再变暗)。默认对应接线: GPIO 25 是 Pico 板载的 LED,也可以外接 GPIO 其他引脚。占空比范围: MicroPython 的 PWM 占空比设为 16 位精度,数值范围是 0(0%)到 65535(100%)。
import time
from machine import Pin, PWM
# 初始化 PWM,使用板载 LED(GPIO 25)
# 如果使用的是 Pico W,可尝试使用 PWM(Pin("LED")) 或外接 GPIO 引脚
led_pwm = PWM(Pin(25))
# 设置 PWM 频率为 1000Hz (1kHz)
led_pwm.freq(1000)
try:
while True:
# 逐渐变亮:占空比从 0 增加到 65535
for duty in range(0, 65536, 1000):
led_pwm.duty_u16(duty)
time.sleep_ms(15)
# 逐渐变暗:占空比从 65535 减少到 0
for duty in range(65535, -1, -1000):
led_pwm.duty_u16(duty)
time.sleep_ms(15)
except KeyboardInterrupt:
# 退出时关闭 PWM 输出
led_pwm.deinit()ESP32 拥有专用的 LEDC(LED Control) 硬件外设模块,支持高精度的 PWM 输出。在 Arduino ESP32 框架中,使用 ledcSetup、ledcAttachPin 和 ledcWrite 接口。
硬件引脚: 下面代码中使用 GPIO 2引脚来控制LED
分辨率设置: 设置为 8 位分辨率(8-bit),占空比数值范围是 0(0%)到 255(100%)。
#include <Arduino.h>
// 定义使用的 GPIO 引脚
const int ledPin = 2; // ESP32 开发板板载 LED 通常在 GPIO 2
// PWM 参数配置
const int pwmChannel = 0; // 使用 PWM 通道 0 (ESP32 有 16 个 independent 通道)
const int freq = 5000; // 设置 PWM 频率为 5000Hz (5kHz)
const int resolution = 8; // 设置分辨率为 8 位,即数值范围 0 - 255
void setup() {
// 配置 PWM 通道、频率和分辨率
ledcSetup(pwmChannel, freq, resolution);
// 将 PWM 通道绑定到指定 GPIO 引脚
ledcAttachPin(ledPin, pwmChannel);
}
void loop() {
// 逐渐变亮:占空比从 0 增加到 255
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
ledcWrite(pwmChannel, dutyCycle);
delay(10);
}
// 逐渐变暗:占空比从 255 减少到 0
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
ledcWrite(pwmChannel, dutyCycle);
delay(10);
}
}