目录
- ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之LED)
- 简介
- 模块概述
- 功能定义
- 架构位置
- 核心特性
- LED外设分析
- LED外设概述
- LED外设功能特点
- 常见应用场景
- LED外设架构图
- LED外设API和数据结构
- 公共API
- 事件类型
- 配置结构体
- 内部数据结构
- LED外设配置选项
- LED外设初始化流程
- 外设层初始化过程(periph_led.c)
- LED外设完整初始化时序图
- LED外设销毁流程
- 销毁流程分析
- 销毁流程时序图
- LED外设事件处理机制
- 事件处理函数特点
- LED闪烁控制流程
- LED定时器处理函数
- LED闪烁控制流程图
- 事件类型
- 事件触发机制
- 事件处理特点
- 事件处理流程图
- LED外设典型使用示例
ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之LED)
版本信息: ESP-ADF v2.7-65-gcf908721
简介
本文档详细分析ESP-ADF中的显示/输出类外设实现机制,包括LCD、LED、WS2812、IS31FL3216和AW2013等外设的设计模式、接口规范、初始化流程和事件处理机制。ESP-ADF显示/输出类外设基于统一的外设框架设计,通过事件驱动模型实现显示和指示功能,为音频应用提供了丰富的视觉反馈能力和用户界面支持。
模块概述
功能定义
ESP-ADF显示/输出类外设主要负责提供视觉反馈和用户界面显示功能,将应用程序的状态和数据以可视化方式呈现给用户。主要功能包括:
- 状态指示(LED指示灯、状态灯等)
- 用户界面显示(LCD屏幕显示文本、图形等)
- 视觉效果(WS2812彩色灯带、IS31FL3216和AW2013 LED矩阵等)
- 音频可视化(音频频谱显示、节奏灯光效果等)
架构位置
显示/输出类外设是ESP-ADF外设子系统的重要组成部分,位于硬件驱动层和应用层之间:
核心特性
- 多种显示设备支持:支持LCD、LED、WS2812、IS31FL3216和AW2013等多种显示和指示设备
- 统一控制接口:所有显示/输出外设使用统一的初始化和控制接口
- 丰富的显示效果:支持开关控制、亮度调节、颜色变化、动画效果等多种显示功能
- 与音频处理集成:可与音频处理模块协同工作,实现音频可视化效果
- 低功耗设计:支持设备休眠和唤醒管理,优化功耗表现
- 事件驱动模型:通过事件机制实现显示状态变化的通知和处理
LED外设分析
LED外设概述
LED外设是ESP-ADF框架中用于控制LED灯的外设组件,基于ESP32的LEDC(LED控制器)硬件模块实现。该外设提供了简单易用的API接口,支持PWM控制、闪烁效果、渐变效果等功能,并能在闪烁完成时发送事件通知应用程序。
LED外设主要由以下文件实现:
- 头文件:
components/esp_peripherals/include/periph_led.h
- 实现文件:
components/esp_peripherals/periph_led.c
LED外设功能特点
- PWM控制:利用ESP32的LEDC模块,通过PWM(脉冲宽度调制)信号控制LED亮度
- 闪烁效果:支持设置LED的开启时间和关闭时间,实现闪烁效果
- 渐变效果:支持LED亮度的渐变变化,实现平滑的明暗过渡
- 多通道支持:最多支持8个LED通道同时控制
- 事件通知:当LED闪烁完成时,会发送事件通知应用程序
常见应用场景
- 状态指示:显示系统运行状态,如电源状态、网络连接状态等
- 警告提示:通过闪烁或特定模式的亮灭来提示用户注意
- 用户界面反馈:对用户操作提供视觉反馈
- 氛围照明:通过控制LED的亮度和颜色创造特定的氛围
- 调试辅助:在开发过程中用作调试指示器
LED外设架构图
LED外设API和数据结构
公共API
// 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外设配置选项
LED外设提供了以下配置选项,通过periph_led_cfg_t
结构体设置:
- led_speed_mode: LEDC速度模式,可选
LEDC_HIGH_SPEED_MODE
或LEDC_LOW_SPEED_MODE
- led_duty_resolution: LEDC占空比分辨率,如
LEDC_TIMER_10_BIT
表示10位分辨率(0-1023) - led_timer_num: LEDC定时器编号,可选
LEDC_TIMER_0
到LEDC_TIMER_3
- led_freq_hz: LEDC定时器频率,单位Hz,默认为5000Hz
- gpio_num: 可选参数,指定默认的GPIO引脚,<0表示无效
在调用periph_led_blink
函数时,还可以设置以下参数:
- gpio_num: 指定要控制的GPIO引脚
- time_on_ms: LED亮的时间,单位毫秒
- time_off_ms: LED灭的时间,单位毫秒
- fade: 是否启用渐变效果
- loop: 闪烁循环次数,<0表示无限循环
- level: 空闲电平,可选
PERIPH_LED_IDLE_LEVEL_LOW
或PERIPH_LED_IDLE_LEVEL_HIGH
LED外设初始化流程
LED外设的初始化流程主要通过periph_led_init
和_led_init
函数实现。下面详细介绍这个初始化过程。
外设层初始化过程(periph_led.c)
外设层初始化主要通过periph_led_init
函数(位于periph_led.c
)完成,主要包括以下步骤:
- 创建外设句柄:调用
esp_periph_create
函数创建LED外设句柄 - 分配内部数据结构:分配
periph_led_t
结构体内存 - 设置配置参数:设置LED速度模式、占空比分辨率、定时器编号和频率
- 创建互斥锁:创建用于保护LED通道访问的互斥锁
- 初始化通道数组:将所有通道的引脚设置为-1(无效)
- 注册回调函数:设置初始化、运行和销毁回调函数
// 文件: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外设销毁流程
LED外设的销毁流程主要通过_led_destroy
函数实现,该函数在外设被停止时由ESP外设框架调用。下面详细介绍销毁过程。
销毁流程分析
LED外设销毁主要通过_led_destroy
函数(位于periph_led.c
)完成,主要包括以下步骤:
- 停止所有LED通道:遍历所有通道,对有效通道调用
ledc_stop
函数停止输出 - 停止定时器:调用
esp_periph_stop_timer
函数停止LED闪烁定时器 - 卸载渐变功能:调用
ledc_fade_func_uninstall
函数卸载LEDC渐变功能 - 销毁互斥锁:释放用于保护LED通道访问的互斥锁
- 释放内存:释放
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外设的_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;
}
LED闪烁控制流程
当调用periph_led_blink
函数控制LED闪烁时,会进行以下初始化步骤:
- 查找可用通道:调用
_find_led_channel
函数查找指定GPIO对应的通道或可用通道 - 配置LEDC通道:设置通道、占空比、GPIO引脚等参数
- 设置闪烁参数:设置高电平时间、低电平时间、循环次数等参数
- 启动定时器:启动定时器处理LED状态变化
// 文件: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闪烁的核心是通过定时器处理函数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闪烁的流程,包括闪烁启动和定时器处理过程:
事件类型
LED外设定义了两种事件类型:
- PERIPH_LED_UNCHANGE:LED状态未改变事件
- PERIPH_LED_BLINK_FINISH: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完成指定循环次数的闪烁后,在
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外设只发送事件到应用程序,不接收和处理来自应用程序的事件
- GPIO信息传递:事件触发时会将对应的GPIO引脚号作为参数传递,便于应用程序识别是哪个LED触发了事件
事件处理流程图
下图展示了LED外设事件的触发和处理流程:
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);
}