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
验证方法
- 烧录后观察LED:亮度应从完全熄灭平滑增至最亮,再平滑减至熄灭,循环往复。
- 使用逻辑分析仪或示波器测量GPIO8引脚,应看到频率约5kHz、占空比周期性变化的PWM波形,周期约5.1秒。
- 若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左右,避免视觉上亮灭跳变感过强。