ESP32-S3开发教程7:PWM呼吸灯
·
现在,我们来讲解PWM的相关知识
一、LEDC库函数
LEDC(LED PWM Controller)是 ESP32-S3 专门用于生成 PWM(脉冲宽度调制)信号的硬件外设,核心特点:
- 无需 CPU 干预:配置好后,硬件自动生成 PWM 波形,不占用 CPU 资源;
- 多通道:有 8 个通道(0-7),可同时控制 8 路 PWM(比如 8 个 LED 渐变、3 路电机调速);
- 高精度:占空比分辨率支持 1-16 位可调(比如 10 位对应 0-1023 级亮度);
- 仅支持低速模式:注意!ESP32-S3 只有LEDC_LOW_SPEED_MODE,没有高速模式。
二、完善CMakeList.txt
idf_component_register(SRCS "main.c"
PRIV_REQUIRES driver freertos
INCLUDE_DIRS ".")
三、完善mian.c
#include "driver/ledc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// ===================== PWM配置宏定义 =====================
#define LEDC_HS_TIMER LEDC_TIMER_0
#define LEDC_HS_MODE LEDC_LOW_SPEED_MODE // ESP32-S3仅支持低速模式
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0
#define LEDC_HS_CH0_GPIO 18 // LED引脚
#define LEDC_DUTY_RES LEDC_TIMER_10_BIT // 占空比分辨率:0-1023
#define LEDC_FREQUENCY 5000 // PWM频率:5000Hz
// ===================== PWM初始化函数 =====================
void ledc_pwm_init(void) {
// 1. 配置定时器
ledc_timer_config_t ledc_timer = {
.duty_resolution = LEDC_DUTY_RES,
.freq_hz = LEDC_FREQUENCY,
.speed_mode = LEDC_HS_MODE,
.timer_num = LEDC_HS_TIMER,
.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&ledc_timer);
// 2. 配置通道
ledc_channel_config_t ledc_channel = {
.channel = LEDC_HS_CH0_CHANNEL,
.duty = 0, // 初始占空比:0(LED灭)
.gpio_num = LEDC_HS_CH0_GPIO,
.speed_mode = LEDC_HS_MODE,
.hpoint = 0,
.timer_sel = LEDC_HS_TIMER
};
ledc_channel_config(&ledc_channel);
}
// ===================== PWM LED渐变任务函数 =====================
void pwm_led_task(void *arg) {
// 初始化PWM
ledc_pwm_init();
int duty = 0;
int step = 5; // 占空比步长
// 任务无限循环
while(1) {
// 设置占空比(0-1023)
ledc_set_duty(LEDC_HS_MODE, LEDC_HS_CH0_CHANNEL, duty);
ledc_update_duty(LEDC_HS_MODE, LEDC_HS_CH0_CHANNEL); // 必须调用,占空比才生效
// 渐变逻辑:递增到最大值后递减
duty += step;
if(duty >= 1023 || duty <= 0) {
step = -step;
}
vTaskDelay(pdMS_TO_TICKS(10)); // 渐变速度
}
}
// ===================== 主函数(仅保留任务创建) =====================
void app_main(void) {
// 创建PWM LED渐变任务
xTaskCreate(
pwm_led_task, // 任务函数
"pwm_led_task", // 任务名称
4096, // 任务栈大小(字节,PWM操作足够)
NULL, // 任务参数(无)
2, // 任务优先级(普通优先级)
NULL // 任务句柄(无)
);
}
3.1 核心宏定义详解
// PWM配置宏定义
#define LEDC_HS_TIMER LEDC_TIMER_0 // 选择LEDC定时器
#define LEDC_HS_MODE LEDC_LOW_SPEED_MODE // 速度模式(ESP32-S3仅支持低速)
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0 // 选择LEDC通道
#define LEDC_HS_CH0_GPIO 18 // LED连接的GPIO引脚
#define LEDC_DUTY_RES LEDC_TIMER_10_BIT // 占空比分辨率
#define LEDC_FREQUENCY 5000 // PWM频率(Hz)
- LEDC_HS_TIMER:选择 LEDC 定时器,指定用哪个硬件定时器生成 PWM 波形;可选值LEDC_TIMER_0~LEDC_TIMER_3(共四个定时器)。
- LEDC_HS_MODE:速度模式,ESP32-S3只支持低速模式,即LEDC_LOW_SPEED_MODE。
- LEDC_HS_CH0_CHANNEL:选择LEDC通道。用于指定用哪个通道输出 PWM 波形,可选值LEDC_CHANNEL_0~LEDC_CHANNEL_7(共8个通道)。每个通道必须绑定一个定时器,可以多个通道绑定同一个定时器,实现同频率不同占空比的 PWM。
- LEDC_HS_CH0_GPIO:PWM输出引脚。用于指定 PWM 波形从哪个 GPIO 引脚输出。并不是所有GPIO都支持LEDC输出,需要参考下图。要注意不要使用GPIO6~11,这些是SPI Flash 引脚,占用会导致系统崩溃。
- LEDC_DUTY_RES:占空比分辨率,用于设置占空比的 “精度等级”,决定了 PWM 有多少级可调。可选值LEDC_TIMER_1_BIT~LEDC_TIMER_16_BIT。这里我们使用了10位分辨率,占空比范围 0-1023(共 1024 级,对应 0%-100% 占空比)
- LEDC_FREQUENCY:PWM频率。用于设置 PWM 波形的 “每秒重复次数”,单位是赫兹(Hz)5000Hz(5kHz)即PWM 波形每秒重复 5000 次,LED 不会闪烁。
3.2 关键配置结构体讲解
LEDC 的配置分为两步:先配置定时器(决定 PWM 的频率和分辨率),再配置通道(决定 PWM 的输出引脚和初始占空比)。
3.2.1 定时器配置结构体:ledc_timer_config_t
ledc_timer_config_t ledc_timer = {
.duty_resolution = LEDC_DUTY_RES, // 占空比分辨率(复用宏定义)
.freq_hz = LEDC_FREQUENCY, // PWM频率(复用宏定义)
.speed_mode = LEDC_HS_MODE, // 速度模式(低速)
.timer_num = LEDC_HS_TIMER, // 定时器编号(复用宏定义)
.clk_cfg = LEDC_AUTO_CLK, // 时钟源(自动选择)
};
ledc_timer_config(&ledc_timer); // 应用定时器配置
逐成员拆解:

分辨率与频率的限制:分辨率越高,最大频率越低。公式:最大频率 = 80MHz / 2^分辨率。ESP32-S3的LEDC时钟默认80MHz:

如果设置的频率超过最大频率,PWM会无法正常工作。
3.2.2 通道配置结构体:ledc_channel_config_t
ledc_channel_config_t ledc_channel = {
.channel = LEDC_HS_CH0_CHANNEL, // 通道编号(复用宏定义)
.duty = 0, // 初始占空比(0:LED灭)
.gpio_num = LEDC_HS_CH0_GPIO, // PWM输出引脚(复用宏定义)
.speed_mode = LEDC_HS_MODE, // 速度模式(和定时器一致)
.hpoint = 0, // 高电平起始点(新手设为0即可)
.timer_sel = LEDC_HS_TIMER // 绑定的定时器(和定时器一致)
};
ledc_channel_config(&ledc_channel); // 应用通道配置
逐成员拆解:

3.3 核心API讲解
3.3.1 ledc_timer_config():配置定时器
esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);
- 作用:将定时器配置结构体应用到硬件,设置 PWM 的频率和分辨率;
- 参数:指向
ledc_timer_config_t结构体的指针(传地址,加&); - 返回值:
esp_err_t(ESP32 通用错误码,返回ESP_OK表示配置成功); - 调用时机:必须在
ledc_channel_config()之前调用(先有定时器,才能绑定通道)。
3.3.2 ledc_channel_config():配置通道
esp_err_t ledc_channel_config(const ledc_channel_config_t *channel_conf);
- 作用:将通道配置结构体应用到硬件,设置 PWM 的输出引脚和初始占空比;
- 参数:指向
ledc_channel_config_t结构体的指针(传地址,加&); - 返回值:
esp_err_t(返回ESP_OK表示配置成功); - 调用时机:必须在
ledc_timer_config()之后调用(先配置定时器,再绑定通道)。
3.3.3 ledc_set_duty():设置占空比
esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty);
- 作用:将新的占空比写入 “缓存”,但不会立即生效;
- 参数:1.
speed_mode:速度模式(LEDC_LOW_SPEED_MODE);2.channel:通道编号(LEDC_CHANNEL_0);3.duty:新的占空比(0-1023,对应 10 位分辨率); - 返回值:
esp_err_t(返回ESP_OK表示写入缓存成功); - 新手注意:仅调用这个函数,PWM 占空比不会变化,必须配合
ledc_update_duty()使用。
3.3.4 ledc_update_duty():更新占空比
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
- 作用:将缓存中的占空比更新到硬件,PWM 占空比立即变化;
- 参数:1.
speed_mode:速度模式(和ledc_set_duty()一致);2.channel:通道编号(和ledc_set_duty()一致); - 返回值:
esp_err_t(返回ESP_OK表示更新成功); - 新手必记:这是最容易漏写的函数!必须在
ledc_set_duty()之后调用,否则占空比不会生效。
3.4 任务函数
// ===================== PWM LED渐变任务函数 =====================
void pwm_led_task(void *arg) {
// 初始化PWM
ledc_pwm_init();
int duty = 0;
int step = 5; // 占空比步长
// 任务无限循环
while(1) {
// 设置占空比(0-1023)
ledc_set_duty(LEDC_HS_MODE, LEDC_HS_CH0_CHANNEL, duty);
ledc_update_duty(LEDC_HS_MODE, LEDC_HS_CH0_CHANNEL); // 必须调用,占空比才生效
// 渐变逻辑:递增到最大值后递减
duty += step;
if(duty >= 1023 || duty <= 0) {
step = -step;
}
vTaskDelay(pdMS_TO_TICKS(10)); // 渐变速度
}
}
在任务函数中,我们先初始化PWM,之后定义了初始的占空比步长。在一个无限循环中,我们设置并更新占空比,同时让占空比每循环一次增加一个步长,当亮度≥1023或者≤0时,将步长取反,这样LED的亮度就会朝相反方向变化。
最后,每循环一次等待10MS。
3.5 创建任务
// ===================== 主函数(仅保留任务创建) =====================
void app_main(void) {
// 创建PWM LED渐变任务
xTaskCreate(
pwm_led_task, // 任务函数
"pwm_led_task", // 任务名称
4096, // 任务栈大小(字节,PWM操作足够)
NULL, // 任务参数(无)
2, // 任务优先级(普通优先级)
NULL // 任务句柄(无)
);
}
四、编译烧录,查看现象

编译烧录成功后,可以看到LED在以规律的速度增加减少亮度,看起来就像在呼吸一样。
更多推荐


所有评论(0)