跳到主要内容

脉冲宽度调制 (PWM)

1. 什么是 PWM?

PWM(Pulse Width Modulation,脉冲宽度调制) 是一种数字信号调制技术,可以用来“模拟”出连续变化的电压。

当需要控制设备输出的“强度”(例如调节 LED 亮度或电机转速),而不仅仅是简单的开/关状态时,PWM (Pulse Width Modulation,脉冲宽度调制) 技术是一种有效的解决方案。

PWM 信号本质上仍然是数字信号,只有 HIGH 和 LOW 两种状态,但通过控制高电平持续的时间比例,可以实现类似模拟输出的效果。

PWM

PWM 的核心概念包括:

  • 占空比 (Duty Cycle): 在一个 PWM 周期内,高电平(信号为 HIGH)持续的时间占整个周期的百分比。占空比直接影响输出的平均功率或强度。

    不同占空比产生不同的平均电压效果:

    • 占空比 0%:平均电压 ≈ 0V
    • 占空比 25%:平均电压 ≈ 0.825V (3.3V * 0.25)
    • 占空比 50%:平均电压 ≈ 1.65V (3.3V * 0.50)
    • 占空比 75%:平均电压 ≈ 2.475V (3.3V * 0.75)
    • 占空比 100%:平均电压 ≈ 3.3V
  • 频率 (Frequency): PWM 信号每秒钟重复一个完整周期的次数,单位是赫兹 (Hz)。选择合适的频率对于应用至关重要:对于 LED 调光,频率需足够高以避免人眼察觉闪烁;对于电机控制,频率会影响效率和噪音。

2. ESP32 上的 PWM 实现

ESP32 集成了专门的硬件模块用于生成 PWM 信号:

  • LEDC (LED Control) 外设: ESP32 的主要 PWM 生成器。虽然名为 LED 控制,但可产生通用的 PWM 信号。根据具体芯片型号,拥有 6 到 16 个独立通道,频率范围 1Hz-40MHz。
  • MCPWM (Motor Control PWM) 外设: 专门用于电机控制,包含死区控制等高级功能。其接口未被包含在标准的 Arduino ESP32 核心库中。

在 Arduino ESP32 编程环境中,主要通过以下两种方式来利用 LEDC 外设产生 PWM 信号:

  1. analogWrite(pin, value) 函数: 这是 Arduino 平台的标准 PWM 函数,ESP32 对其提供了良好支持,使用便捷。
  2. LEDC API 函数: 一系列如 ledcAttach(), ledcWrite(), ledcAttachChannel() 的函数,允许对 PWM 参数进行更细致和灵活的配置。

本节将通过控制外部 LED 的亮度来演示这两种 PWM 实现方法。

3. 搭建电路

需要使用的器件有:

  • LED * 1
  • 330Ω 电阻 * 1
  • 面包板 * 1
  • 导线
  • ESP32 开发板

按照下面接线图连接电路:

ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

接线图

4. 使用 analogWrite() 的 ESP32 PWM 示例代码

const int ledPin = 7;  // LED 连接的引脚号

void setup() {
}

void loop() {
// 亮度增加 (占空比值从 0 到 255)
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
analogWrite(ledPin, dutyCycle); // 设置 PWM 占空比值
delay(10); // 延时以控制变化速度
}
// 亮度减少 (占空比值从 255 到 0)
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
analogWrite(ledPin, dutyCycle); // 设置 PWM 占空比值
delay(10); // 延时以控制变化速度
}
}

代码解析

  1. 对于 analogWrite(),ESP32 的实现会自动处理引脚的 PWM 初始化,因此通常不需要显式调用 pinMode()

  2. loop() 函数:

    • 第一个 for 循环:

      for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++): 变量 dutyCycle 从 0 递增至 255。

    • 第二个 for 循环:

      for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) 变量 dutyCycle 从 255 递减至 0,使 LED 亮度逐渐减弱。

    • analogWrite(ledPin, dutyCycle);:

      将当前 dutyCycle 值(0-255)设为 ledPin 的 PWM 占空比,使 LED 亮度逐渐增加。

      提示

      在使用 analogWrite() 时,ESP32 会自动管理 LEDC 通道的分配,并设置默认的频率 和 8 位分辨率。

    • delay(10);:

      短暂延时,用于控制亮度变化速率。

5. 使用 LEDC API 的 ESP32 PWM 示例代码

const int ledPin = 7;  // LED 连接的 GPIO 引脚

const int frequency = 5000; // PWM 频率 5kHz
const int resolution = 8; // PWM 分辨率 8 位(0-255)
const int ledChannel = 0; // PWM 通道号

void setup() {
// 初始化 LED PWM 功能
ledcAttach(ledPin, frequency, resolution);

// 如果你想指定通道
// ledcAttachChannel(ledPin, frequency,resolution,ledChannel)
}

void loop() {
// 逐渐增加占空比,LED 变亮
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
ledcWrite(ledPin, dutyCycle); // 设置 PWM 占空比值
delay(10); // 延时 10ms,控制变化速度
}

// 逐渐减少占空比,LED 变暗
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
ledcWrite(ledPin, dutyCycle); // 设置 PWM 占空比值
delay(10); // 延时 10ms,控制变化速度
}
}

代码解析

  1. ledcAttach(ledPin, frequency, resolution);

    • ledPin:指定要用作 PWM 输出的 GPIO 引脚
    • frequency:PWM 信号的频率,单位为赫兹 (Hz)
    • resolution:分辨率位数,决定占空比的精度
  2. ledcWrite(ledPin, dutyCycle);:

    • 设置 LEDC 引脚 ledPin 的占空比为 dutyCycle
    • 数值范围取决于分辨率设置:8 位为 0-255(2⁸-1)

6. 拓展

请尝试实现:通过调节电位器旋钮来控制 LED 灯的亮度,使旋钮位置与灯光亮度相对应

接线图:

接线图

代码:

const int ledPin = 7;            // LED 连接的引脚
const int potentiometerPin = 8; // 电位器连接的引脚

int potentiometerValue; // 存储电位器读取值
int brightness; // 存储映射后的亮度值

void setup() {
}

void loop() {
// 1. 读取电位器的模拟值 (范围 0-4095)
potentiometerValue = analogRead(potentiometerPin);

// 2. 将 ADC 读数映射到 PWM 的范围 (0-255)
brightness = map(potentiometerValue, 0, 4095, 0, 255);

// 3. 用映射后的值设置 LED 亮度
analogWrite(ledPin, brightness);

delay(20);
}

代码解析:

  1. map(potValue, 0, 4095, 0, 255);:

    将电位器的 0-4095 范围线性映射到 PWM 的 0-255 范围,实现电位器旋转角度与 LED 亮度的对应关系。

    语法为:

    map(value, fromLow, fromHigh, toLow, toHigh)
    • value:要转换的输入值(这里是电位器读数)
    • fromLow, fromHigh:输入范围(0 到 4095)
    • toLow, toHigh:输出范围(0 到 255)

    工作原理:

    map() 函数按比例转换数值。例如:

    • 电位器值 0(0%位置)→ LED 亮度 0(0% 亮度)
    • 电位器值 2047(50%位置)→ LED 亮度 127(50% 亮度)
    • 电位器值 4095(100%位置)→ LED 亮度 255(100% 亮度)

    简单来说,电位器转到哪个位置,LED 就对应那个亮度。

7. 相关链接