3. PWM呼吸灯

PWM呼吸灯 学习卡片

任务目标

使用ESP32的LEDC(PWM)外设驱动LED,实现从熄灭到最亮再回到熄灭的平滑呼吸效果。通过本案例理解两个核心概念:占空比(一个PWM周期内高电平时间占比)和频率(每秒PWM周期数)。

所需硬件与接线

  • ESP32开发板 × 1
  • LED × 1(颜色不限)
  • 限流电阻 × 1(220Ω~1kΩ均可)
  • 面包板与跳线若干

接线方式:将LED阳极(长脚)串联电阻后连接至GPIO2,阴极(短脚)接GND。

核心原理

ESP32的LEDC控制器可产生硬件PWM信号。本案例设置PWM频率为5kHz(高于人眼闪烁感知阈值,LED亮度稳定无闪烁),分辨率8位(占空比范围0~255)。呼吸效果通过定时改变占空比实现:占空比从0逐步增至255(渐亮),再从255逐步减至0(渐暗),循环往复。每次调整间隔10ms,使亮度变化平滑连续。

完整代码

#include <stdio.h>
#include "driver/ledc.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define LED_GPIO        2
#define PWM_FREQ_HZ     5000    // PWM频率:5kHz
#define PWM_RES_BITS    8       // 分辨率:8位,占空比0~255
#define PWM_MAX_DUTY    ((1 << PWM_RES_BITS) - 1)  // 255
#define STEP_DELAY_MS   10      // 每级亮度间隔

static const char *TAG = "pwm_breath";

void app_main(void)
{
    // 1. 配置LEDC定时器
    ledc_timer_config_t timer_conf = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .timer_num = LEDC_TIMER_0,
        .duty_resolution = PWM_RES_BITS,
        .freq_hz = PWM_FREQ_HZ,
        .clk_cfg = LEDC_AUTO_CLK
    };
    ESP_ERROR_CHECK(ledc_timer_config(&timer_conf));

    // 2. 配置LEDC通道
    ledc_channel_config_t channel_conf = {
        .gpio_num = LED_GPIO,
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .channel = LEDC_CHANNEL_0,
        .intr_type = LEDC_INTR_DISABLE,
        .timer_sel = LEDC_TIMER_0,
        .duty = 0,
        .hpoint = 0
    };
    ESP_ERROR_CHECK(ledc_channel_config(&channel_conf));

    ESP_LOGI(TAG, "PWM呼吸灯启动: 频率=%dHz, 分辨率=%d位", PWM_FREQ_HZ, PWM_RES_BITS);

    while (1) {
        // 渐亮:占空比从0增加到255
        for (int duty = 0; duty <= PWM_MAX_DUTY; duty++) {
            ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty));
            ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
            vTaskDelay(pdMS_TO_TICKS(STEP_DELAY_MS));
        }
        // 渐暗:占空比从255减小到0
        for (int duty = PWM_MAX_DUTY; duty >= 0; duty--) {
            ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty));
            ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
            vTaskDelay(pdMS_TO_TICKS(STEP_DELAY_MS));
        }
    }
}

编译与烧录

  1. 在ESP-IDF环境中创建项目,将上述代码保存为项目主目录下的 main.c(替换模板默认文件)。
  2. 在项目根目录执行 idf.py set-target esp32 设置目标芯片。
  3. 执行 idf.py build 编译项目。
  4. 连接开发板,执行 idf.py -p /dev/ttyUSB0 flash monitor 烧录并打开串口监视器(Windows下端口号请替换为实际设备名,如COM3)。

验证方法与判定标准

  • 视觉验证:烧录成功后,LED应呈现平滑的渐亮→渐暗循环效果,每个完整周期约5.1秒(256级×2×10ms)。若LED闪烁而非平滑变化,说明PWM频率过低,应提高 PWM_FREQ_HZ 至1kHz以上。
  • 串口日志:串口监视器应输出 PWM呼吸灯启动: 频率=5000Hz, 分辨率=8位,表明初始化成功。
  • 概念验证:修改 STEP_DELAY_MS 为100后重新编译烧录,可观察到呼吸速度明显变慢(周期约51秒),直观感受占空比变化速率对效果的影响;修改 PWM_FREQ_HZ 为50后烧录,可看到LED明显闪烁,直观理解频率对视觉连续性的作用。

常见问题

  • LED不亮:检查GPIO2接线是否正确,确认LED极性(长脚接电阻到GPIO2,短脚接GND)。
  • 编译报错:确认项目已正确初始化ESP-IDF环境,且代码文件位于main目录下。
  • 亮度跳变不连续:检查 STEP_DELAY_MS 是否过小(小于1ms可能导致任务调度不稳定),建议保持10ms以上。