引言
在物联网(IoT)设备中,LED 控制是一个常见的应用场景。ESP32-S3 开发板提供了强大的 LED PWM 控制器(LEDC),可以用来控制 LED 的亮度以及其他需要 PWM 信号的应用。本文将详细介绍如何使用 ESP-IDF 框架中的 LEDC 驱动程序来控制 LED 的亮度。
环境准备
在开始之前,请确保你已经安装了 ESP-IDF 开发环境,并且熟悉基本的 ESP32 项目构建流程。如果你还没有安装 ESP-IDF,可以从 Espressif 官网 获取安装指南。
LED PWM 控制器概述
简介
LED 控制器 (LEDC) 主要用于控制 LED,也可产生 PWM 信号用于其他设备的控制。该控制器有 8 路通道,可以产生独立的波形,驱动 RGB LED 等设备。LEDC PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实现亮度和颜色渐变。
功能概览
设置 LEDC 通道:
- 定时器配置:指定 PWM 信号的频率和占空比分辨率。
- 通道配置:绑定定时器和输出 PWM 信号的 GPIO。
- 改变 PWM 信号:输出 PWM 信号来驱动 LED。可通过软件控制或使用硬件渐变功能来改变 LED 的亮度。
电源管理:
- LEDC 驱动不使用电源管理锁来防止系统进入 Light-sleep。可以通过配置
ledc_channel_config_t::sleep_mode
来选择 LEDC 外设电源域状态和 PWM 信号在睡眠期间的输出行为。
频率和占空比分辨率支持范围:
- LED PWM 控制器主要用于驱动 LED。该控制器 PWM 占空比设置的分辨率范围较广。例如,PWM 频率为 5 kHz 时,占空比分辨率最大可为 13 位。然而,这些参数取决于为 LEDC 定时器计时的时钟信号。
示例代码
我们将使用 ESP-IDF 提供的示例代码来演示如何使用 LEDC 驱动程序控制 LED 的亮度。以下是完整的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include "driver/ledc.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h"
static const char* TAG = "example";
#define LEDC_TIMER LEDC_TIMER_0 #define LEDC_MODE LEDC_LOW_SPEED_MODE #define LEDC_OUTPUT_IO (2) #define LEDC_CHANNEL LEDC_CHANNEL_0 #define LEDC_DUTY_RESOLUTION 10 #define LEDC_FREQUENCY (5000)
void app_main(void) { ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_MODE, .timer_num = LEDC_TIMER, .duty_resolution = LEDC_DUTY_RESOLUTION, .freq_hz = LEDC_FREQUENCY, .clk_cfg = LEDC_AUTO_CLK, }; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = { .speed_mode = LEDC_MODE, .channel = LEDC_CHANNEL, .timer_sel = LEDC_TIMER, .intr_type = LEDC_INTR_DISABLE, .gpio_num = LEDC_OUTPUT_IO, .duty = 0, .hpoint = 0, .flags = 0, }; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
while (1) { for (int duty = 0; duty <= 1023; duty++) { 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(10)); } for (int duty = 1023; duty >= 0; duty--) { 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(10)); } } }
|
代码解析
1. 包含头文件
1 2 3 4
| #include "driver/ledc.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h"
|
driver/ledc.h
:包含 LEDC 驱动程序的 API 声明。
esp_log.h
:包含日志记录功能。
freertos/FreeRTOS.h
和 freertos/task.h
:包含 FreeRTOS 任务管理功能。
2. 定义日志标签
1
| static const char* TAG = "example";
|
3. 定义 LEDC 配置参数
1 2 3 4 5 6
| #define LEDC_TIMER LEDC_TIMER_0 #define LEDC_MODE LEDC_LOW_SPEED_MODE #define LEDC_OUTPUT_IO (2) #define LEDC_CHANNEL LEDC_CHANNEL_0 #define LEDC_DUTY_RESOLUTION 10 #define LEDC_FREQUENCY (5000)
|
LEDC_TIMER
:使用的定时器编号。
LEDC_MODE
:速度模式,ESP32-S3 仅支持低速模式。
LEDC_OUTPUT_IO
:输出 PWM 信号的 GPIO 编号。
LEDC_CHANNEL
:使用的通道编号。
LEDC_DUTY_RESOLUTION
:占空比分辨率。
LEDC_FREQUENCY
:PWM 信号的频率。
4. 配置 LEDC 定时器
1 2 3 4 5 6 7 8
| ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_MODE, .timer_num = LEDC_TIMER, .duty_resolution = LEDC_DUTY_RESOLUTION, .freq_hz = LEDC_FREQUENCY, .clk_cfg = LEDC_AUTO_CLK, }; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
- 创建一个
ledc_timer_config_t
结构体,并设置定时器参数。
- 使用
ledc_timer_config
函数配置定时器。
5. 配置 LEDC 通道
1 2 3 4 5 6 7 8 9 10 11
| ledc_channel_config_t ledc_channel = { .speed_mode = LEDC_MODE, .channel = LEDC_CHANNEL, .timer_sel = LEDC_TIMER, .intr_type = LEDC_INTR_DISABLE, .gpio_num = LEDC_OUTPUT_IO, .duty = 0, .hpoint = 0, .flags = 0, }; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
- 创建一个
ledc_channel_config_t
结构体,并设置通道参数。
- 使用
ledc_channel_config
函数配置通道。
6. 循环改变 LED 亮度
1 2 3 4 5 6 7 8 9 10 11 12
| while (1) { for (int duty = 0; duty <= 1023; duty++) { 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(10)); } for (int duty = 1023; duty >= 0; duty--) { 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(10)); } }
|
- 在一个无限循环中,逐步增加和减少占空比,从而实现 LED 亮度的渐变。
- 使用
ledc_set_duty
设置新的占空比,并使用ledc_update_duty
使新配置生效。
- 使用
vTaskDelay
设置任务延迟,控制亮度变化的速度。
使用中断
LEDC 驱动还可以配置中断,在渐变完成时触发中断。以下是一个简单的示例,展示如何注册中断处理程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void IRAM_ATTR ledc_fade_isr(void* arg) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; ESP_LOGI(TAG, "Fade complete"); portYIELD_FROM_ISR(); }
void app_main(void) {
ledc_isr_register(ledc_fade_isr, NULL, ESP_INTR_FLAG_IRAM, NULL);
ledc_fade_func_install(0);
ledc_set_fade_with_time(LEDC_MODE, LEDC_CHANNEL, 1023, 1000); ledc_fade_start(LEDC_MODE, LEDC_CHANNEL, LEDC_FADE_NO_WAIT); }
|
7. 注册中断处理程序
1
| ledc_isr_register(ledc_fade_isr, NULL, ESP_INTR_FLAG_IRAM, NULL);
|
- 使用
ledc_isr_register
函数注册中断处理程序。
ledc_fade_isr
:中断处理函数。
ESP_INTR_FLAG_IRAM
:确保中断处理函数在 IRAM 中执行。
8. 启用渐变功能
1
| ledc_fade_func_install(0);
|
- 使用
ledc_fade_func_install
函数启用渐变功能。
9. 设置渐变参数并启动渐变
1 2
| ledc_set_fade_with_time(LEDC_MODE, LEDC_CHANNEL, 1023, 1000); ledc_fade_start(LEDC_MODE, LEDC_CHANNEL, LEDC_FADE_NO_WAIT);
|
- 使用
ledc_set_fade_with_time
函数设置渐变参数。
- 使用
ledc_fade_start
函数启动渐变。
总结
通过上述代码,我们可以轻松地在 ESP32-S3 上实现对 LED 的 PWM 控制,从而控制 LED 的亮度。这个示例展示了如何使用 ESP-IDF 提供的 LEDC 驱动程序进行基本的操作,包括定时器配置、通道配置和改变 PWM 信号。你可以根据实际需求,进一步扩展这个示例,比如控制多个 LED 或实现更复杂的渐变效果。
希望这篇博客笔记对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言。
参考文档