4. PWM呼吸灯

任务目标

使用ESP-IDF的LEDC(LED Control)模块产生PWM信号,通过改变占空比控制LED亮度,实现平滑的呼吸灯效果。本案例以ESP32-C3开发板为例,GPIO8连接板载LED(或外接LED串联330Ω电阻至3.3V)。

核心实现

LEDC模块配置为低速通道,选择8位占空比分辨率(0~255),PWM频率设为5kHz(高于人眼闪烁感知阈值)。呼吸效果通过定时器中断逐步增减占空比实现,每步间隔10ms,步长1,完成一次由暗到亮再到暗的周期约5.1秒。

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

#define LED_GPIO         8
#define LEDC_MODE        LEDC_LOW_SPEED_MODE
#define LEDC_CHANNEL     LEDC_CHANNEL_0
#define LEDC_TIMER       LEDC_TIMER_0
#define PWM_FREQ_HZ      5000
#define PWM_RESOLUTION   LEDC_TIMER_8_BIT
#define DUTY_MAX         255
#define STEP_DELAY_MS    10
#define DUTY_STEP        1

static void ledc_init(void)
{
    // 定时器配置
    ledc_timer_config_t timer_conf = {
        .speed_mode      = LEDC_MODE,
        .timer_num       = LEDC_TIMER,
        .duty_resolution = PWM_RESOLUTION,
        .freq_hz         = PWM_FREQ_HZ,
        .clk_cfg         = LEDC_AUTO_CLK
    };
    ESP_ERROR_CHECK(ledc_timer_config(&timer_conf));

    // 通道配置
    ledc_channel_config_t channel_conf = {
        .gpio_num   = LED_GPIO,
        .speed_mode = LEDC_MODE,
        .channel    = LEDC_CHANNEL,
        .timer_sel  = LEDC_TIMER,
        .duty       = 0,
        .hpoint     = 0
    };
    ESP_ERROR_CHECK(ledc_channel_config(&channel_conf));
}

void app_main(void)
{
    ledc_init();

    uint16_t duty = 0;
    int8_t step = DUTY_STEP;  // 1: 渐亮, -1: 渐暗

    while (1) {
        // 更新占空比
        ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty));
        ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));

        vTaskDelay(pdMS_TO_TICKS(STEP_DELAY_MS));

        // 边界翻转方向
        if (duty >= DUTY_MAX) {
            step = -DUTY_STEP;
        } else if (duty == 0) {
            step = DUTY_STEP;
        }
        duty += step;
    }
}

编译与烧录

idf.py set-target esp32c3
idf.py build
idf.py -p /dev/ttyACM0 flash monitor

验证方法

  1. 烧录后观察LED:亮度应从完全熄灭平滑增至最亮,再平滑减至熄灭,循环往复。
  2. 使用逻辑分析仪或示波器测量GPIO8引脚,应看到频率约5kHz、占空比周期性变化的PWM波形,周期约5.1秒。
  3. 若LED不亮,检查GPIO8是否被板载其他外设占用(如ESP32-C3-DevKitM-1的GPIO8为RGB LED的绿色通道,需确认跳线或使用其他空闲GPIO)。

关键说明

  • 呼吸周期计算:255步×2(亮/暗)×10ms = 5.1秒。调整STEP_DELAY_MS或DUTY_STEP可改变呼吸速度。
  • LEDC通道与定时器资源有限(ESP32-C3有6个通道、4个定时器),多路PWM时需合理分配。
  • 若使用高亮度LED,建议降低DUTY_MAX至200左右,避免视觉上亮灭跳变感过强。