版本信息: ESP-ADF v2.7-65-gcf908721
本文档详细分析ESP-ADF中的显示/输出类外设实现机制,包括LCD、LED、WS2812、IS31FL3216和AW2013等外设的设计模式、接口规范、初始化流程和事件处理机制。ESP-ADF显示/输出类外设基于统一的外设框架设计,通过事件驱动模型实现显示和指示功能,为音频应用提供了丰富的视觉反馈能力和用户界面支持。
ESP-ADF显示/输出类外设主要负责提供视觉反馈和用户界面显示功能,将应用程序的状态和数据以可视化方式呈现给用户。主要功能包括:
显示/输出类外设是ESP-ADF外设子系统的重要组成部分,位于硬件驱动层和应用层之间:
LED外设是ESP-ADF框架中用于控制LED灯的外设组件,基于ESP32的LEDC(LED控制器)硬件模块实现。该外设提供了简单易用的API接口,支持PWM控制、闪烁效果、渐变效果等功能,并能在闪烁完成时发送事件通知应用程序。
LED外设主要由以下文件实现:
components/esp_peripherals/include/periph_led.h
components/esp_peripherals/periph_led.c
// LED外设初始化函数
esp_periph_handle_t periph_led_init(periph_led_cfg_t* config);
// LED闪烁控制函数
esp_err_t periph_led_blink(esp_periph_handle_t periph, int gpio_num, int time_on_ms, int time_off_ms, bool fade, int loop, periph_led_idle_level_t level);
// LED停止闪烁函数
esp_err_t periph_led_stop(esp_periph_handle_t periph, int gpio_num);
// LED事件类型
typedef enum {
PERIPH_LED_UNCHANGE = 0, // 无事件
PERIPH_LED_BLINK_FINISH, // LED闪烁完成时
} periph_led_event_id_t;
// LED空闲电平类型
typedef enum {
PERIPH_LED_IDLE_LEVEL_LOW, // 低电平输出
PERIPH_LED_IDLE_LEVEL_HIGH // 高电平输出
} periph_led_idle_level_t;
// LED配置结构体
typedef struct {
ledc_mode_t led_speed_mode; // LEDC速度模式,高速模式或低速模式
ledc_timer_bit_t led_duty_resolution; // LEDC通道占空比分辨率
ledc_timer_t led_timer_num; // 选择通道的定时器源(0-3)
uint32_t led_freq_hz; // LEDC定时器频率(Hz)
int gpio_num; // 可选,<0表示无效的GPIO编号
} periph_led_cfg_t;
// LED通道结构体(内部实现)
typedef struct {
int index; // 通道索引
int pin; // GPIO引脚编号
int high_level_ms; // 高电平持续时间(毫秒)
int low_level_ms; // 低电平持续时间(毫秒)
long long tick; // 时间戳
int loop; // 循环次数
bool is_high_level; // 当前是否为高电平
bool fade; // 是否启用渐变效果
bool stop; // 是否停止
int level; // 空闲电平
} periph_led_channel_t;
// LED外设主结构体(内部实现)
typedef struct periph_led {
ledc_mode_t led_speed_mode; // LEDC速度模式
ledc_timer_bit_t led_duty_resolution; // 占空比分辨率
ledc_timer_t led_timer_num; // 定时器编号
uint32_t led_freq_hz; // 频率(Hz)
QueueHandle_t led_mutex; // 互斥锁
periph_led_channel_t channels[MAX_LED_CHANNEL]; // LED通道数组
} periph_led_t;
LED外设提供了以下配置选项,通过periph_led_cfg_t
结构体设置:
LEDC_HIGH_SPEED_MODE
或LEDC_LOW_SPEED_MODE
LEDC_TIMER_10_BIT
表示10位分辨率(0-1023)LEDC_TIMER_0
到LEDC_TIMER_3
在调用periph_led_blink
函数时,还可以设置以下参数:
PERIPH_LED_IDLE_LEVEL_LOW
或PERIPH_LED_IDLE_LEVEL_HIGH
LED外设的初始化流程主要通过periph_led_init
和_led_init
函数实现。下面详细介绍这个初始化过程。
外设层初始化主要通过periph_led_init
函数(位于periph_led.c
)完成,主要包括以下步骤:
esp_periph_create
函数创建LED外设句柄periph_led_t
结构体内存// 文件:components/esp_peripherals/periph_led.c
esp_periph_handle_t periph_led_init(periph_led_cfg_t *config)
{
// 1. 创建外设句柄
esp_periph_handle_t periph = esp_periph_create(PERIPH_ID_LED, "periph_led");
//check periph
// 2. 分配内部数据结构
periph_led_t *periph_led = audio_calloc(1, sizeof(periph_led_t));
//check periph_led
// 3. 设置配置参数
periph_led->led_speed_mode = config->led_speed_mode;
periph_led->led_duty_resolution = config->led_duty_resolution;
periph_led->led_timer_num = config->led_timer_num;
periph_led->led_freq_hz = config->led_freq_hz;
// 4. 创建互斥锁
periph_led->led_mutex = mutex_create();
// 设置默认频率(如果未指定)
if (periph_led->led_freq_hz == 0) {
periph_led->led_freq_hz = 5000;
}
// 5. 初始化通道数组
memset(&periph_led->channels, -1, sizeof(periph_led->channels));
// 6. 注册回调函数
esp_periph_set_data(periph, periph_led);
esp_periph_set_function(periph, _led_init, _led_run, _led_destroy);
return periph;
}
当LED外设被添加到外设集合并启动时,会调用_led_init
函数(位于periph_led.c
),该函数负责初始化LEDC定时器和渐变功能:
// 文件:components/esp_peripherals/periph_led.c
static esp_err_t _led_init(esp_periph_handle_t self)
{
// 验证LED外设
VALIDATE_LED(self, ESP_FAIL);
// 获取LED外设数据
periph_led_t *periph_led = esp_periph_get_data(self);
// 配置LEDC定时器
ledc_timer_config_t ledc_timer = {
.duty_resolution = periph_led->led_duty_resolution, // PWM占空比分辨率
.freq_hz = periph_led->led_freq_hz, // PWM信号频率
.speed_mode = periph_led->led_speed_mode, // 定时器模式
.timer_num = periph_led->led_timer_num // 定时器索引
};
// 设置高速通道的定时器0配置
ledc_timer_config(&ledc_timer);
// 安装LEDC渐变功能
ledc_fade_func_install(0);
return ESP_OK;
}
下图展示了LED外设初始化的流程:
LED外设的销毁流程主要通过_led_destroy
函数实现,该函数在外设被停止时由ESP外设框架调用。下面详细介绍销毁过程。
LED外设销毁主要通过_led_destroy
函数(位于periph_led.c
)完成,主要包括以下步骤:
ledc_stop
函数停止输出esp_periph_stop_timer
函数停止LED闪烁定时器ledc_fade_func_uninstall
函数卸载LEDC渐变功能periph_led_t
结构体占用的内存// 文件:components/esp_peripherals/periph_led.c
static esp_err_t _led_destroy(esp_periph_handle_t self)
{
// 获取LED外设数据
periph_led_t *periph_led = esp_periph_get_data(self);
// 1. 停止所有LED通道
for (int i = 0; i < MAX_LED_CHANNEL; i++) {
periph_led_channel_t *ch = &periph_led->channels[i];
if (ch->index > 0 && ch->pin > 0) {
ledc_stop(periph_led->led_speed_mode, ch->index, ch->level);
}
}
// 2. 停止定时器
esp_periph_stop_timer(self);
// 3. 卸载渐变功能
ledc_fade_func_uninstall();
// 4. 销毁互斥锁
mutex_destroy(periph_led->led_mutex);
// 5. 释放内存
audio_free(periph_led);
return ESP_OK;
}
下图展示了LED外设销毁的完整流程:
LED外设的事件处理机制相对简单,主要包括以下几个方面:
LED外设的_led_run
函数实现非常简单,仅返回ESP_OK
,不处理任何事件。这表明LED外设本身不会对接收到的事件进行处理:
// 文件:components/esp_peripherals/periph_led.c
static esp_err_t _led_run(esp_periph_handle_t self, audio_event_iface_msg_t *msg)
{
return ESP_OK;
}
当调用periph_led_blink
函数控制LED闪烁时,会进行以下初始化步骤:
_find_led_channel
函数查找指定GPIO对应的通道或可用通道// 文件:components/esp_peripherals/periph_led.c
esp_err_t periph_led_blink(esp_periph_handle_t periph, int gpio_num, int time_on_ms, int time_off_ms, bool fade, int loop, periph_led_idle_level_t level)
{
// 获取LED外设数据
periph_led_t *periph_led = esp_periph_get_data(periph);
// 1. 查找可用通道
periph_led_channel_t *ch = _find_led_channel(periph_led, gpio_num);
if (ch == NULL) {
return ESP_FAIL;
}
// 2. 配置LEDC通道
ledc_channel_config_t ledc_channel_cfg = {
.channel = ch->index,
.duty = 0,
.gpio_num = gpio_num,
.speed_mode = periph_led->led_speed_mode,
.timer_sel = periph_led->led_timer_num,
};
ledc_channel_config(&ledc_channel_cfg);
// 3. 设置闪烁参数
ch->pin = gpio_num;
ch->tick = audio_sys_get_time_ms();
ch->loop = loop;
ch->fade = fade;
// 根据空闲电平设置高低电平时间
if (level == PERIPH_LED_IDLE_LEVEL_LOW) {
ch->is_high_level = false;
ch->high_level_ms = time_on_ms;
ch->low_level_ms = time_off_ms;
} else {
ch->is_high_level = true;
ch->high_level_ms = time_off_ms;
ch->low_level_ms = time_on_ms;
}
ch->stop = false;
ch->level = level;
// 4. 启动定时器
esp_periph_start_timer(periph, portTICK_RATE_MS, led_timer_handler);
return ESP_OK;
}
LED闪烁的核心是通过定时器处理函数led_timer_handler
实现的,该函数会定期检查每个LED通道的状态并根据配置进行切换:
// 文件:components/esp_peripherals/periph_led.c
static void led_timer_handler(xTimerHandle tmr)
{
// 获取外设句柄和数据
esp_periph_handle_t periph = (esp_periph_handle_t) pvTimerGetTimerID(tmr);
periph_led_t *periph_led = esp_periph_get_data(periph);
// 加锁保护通道访问
mutex_lock(periph_led->led_mutex);
// 遍历所有LED通道
for (int i = 0; i < MAX_LED_CHANNEL; i++) {
periph_led_channel_t *ch = &periph_led->channels[i];
// 跳过无效或已停止的通道
if (ch->pin < 0 || ch->stop == true) {
continue;
}
// 检查是否完成所有循环
if (ch->loop == 0) {
// 停止LED并发送完成事件
ledc_stop(periph_led->led_speed_mode, ch->index, ch->level);
esp_periph_send_event(periph, PERIPH_LED_BLINK_FINISH, (void *)ch->pin, 0);
ch->stop = true;
continue;
}
// 处理低电平到高电平的切换
if (!ch->is_high_level && audio_sys_get_time_ms() - ch->tick > ch->low_level_ms) {
// 减少循环计数(如果需要)
if (ch->loop > 0) {
ch->loop--;
}
// 切换到高电平
if (ch->fade) {
// 带渐变效果
ledc_set_fade_with_time(periph_led->led_speed_mode, ch->index,
pow(2, periph_led->led_duty_resolution) - 1,
ch->high_level_ms);
ledc_fade_start(periph_led->led_speed_mode, ch->index, LEDC_FADE_NO_WAIT);
} else {
// 无渐变效果
ledc_set_duty(periph_led->led_speed_mode, ch->index,
pow(2, periph_led->led_duty_resolution) - 1);
ledc_update_duty(periph_led->led_speed_mode, ch->index);
}
// 更新状态和时间戳
if (ch->low_level_ms > 0) {
ch->is_high_level = true;
}
ch->tick = audio_sys_get_time_ms();
}
// 处理高电平到低电平的切换
else if (ch->is_high_level && audio_sys_get_time_ms() - ch->tick > ch->high_level_ms) {
// 减少循环计数(如果需要)
if (ch->loop > 0) {
ch->loop--;
}
// 切换到低电平
if (ch->fade) {
// 带渐变效果
ledc_set_fade_with_time(periph_led->led_speed_mode, ch->index, 0, ch->low_level_ms);
ledc_fade_start(periph_led->led_speed_mode, ch->index, LEDC_FADE_NO_WAIT);
} else {
// 无渐变效果
ledc_set_duty(periph_led->led_speed_mode, ch->index, 0);
ledc_update_duty(periph_led->led_speed_mode, ch->index);
}
// 更新状态和时间戳
if (ch->high_level_ms > 0) {
ch->is_high_level = false;
}
ch->tick = audio_sys_get_time_ms();
}
}
// 解锁
mutex_unlock(periph_led->led_mutex);
}
下图展示了应用程序控制LED闪烁的流程,包括闪烁启动和定时器处理过程:
LED外设定义了两种事件类型:
这些事件定义在periph_led.h
文件中:
// 文件:components/esp_peripherals/include/periph_led.h
typedef enum {
PERIPH_LED_UNCHANGE = 0,
PERIPH_LED_BLINK_FINISH,
} periph_led_event_id_t;
LED外设主要在以下情况下触发事件:
led_timer_handler
函数中触发PERIPH_LED_BLINK_FINISH
事件:// 文件:components/esp_peripherals/periph_led.c (led_timer_handler函数片段)
if (ch->loop == 0) {
// 停止LED并发送完成事件
ledc_stop(periph_led->led_speed_mode, ch->index, ch->level);
esp_periph_send_event(periph, PERIPH_LED_BLINK_FINISH, (void *)ch->pin, 0);
ch->stop = true;
continue;
}
下图展示了LED外设事件的触发和处理流程:
下面是一个简单的LED外设使用示例,展示了基本的初始化和控制流程:
#include "esp_log.h"
#include "esp_peripherals.h"
#include "periph_led.h"
static const char *TAG = "LED_EXAMPLE";
void app_led_simple_example(void)
{
// 1. 初始化外设管理器
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
// 2. 配置LED外设
periph_led_cfg_t led_cfg = {
.led_speed_mode = LEDC_HIGH_SPEED_MODE,
.led_duty_resolution = LEDC_TIMER_10_BIT,
.led_timer_num = LEDC_TIMER_0,
.led_freq_hz = 5000,
};
// 3. 初始化LED外设
esp_periph_handle_t led_handle = periph_led_init(&led_cfg);
// 4. 启动LED外设
esp_periph_start(set, led_handle);
// 5. 控制LED闪烁 - GPIO 22闪烁5次
// 参数: 外设句柄, GPIO引脚, 亮时间(ms), 灭时间(ms), 渐变效果, 循环次数, 空闲电平
periph_led_blink(led_handle, 22, 300, 700, true, 5, PERIPH_LED_IDLE_LEVEL_LOW);
// 等待LED闪烁完成
vTaskDelay(5000 / portTICK_PERIOD_MS);
// 6. 清理资源
esp_periph_stop(set, led_handle);
esp_periph_set_destroy(set);
}