版本信息: ESP-ADF v2.7-65-gcf908721
本文档详细分析ESP-ADF中外设子系统(esp_peripherals组件)的事件机制,包括事件的产生、传递、处理流程以及相关API的使用方法。ESP-ADF外设子系统采用事件驱动架构,通过统一的事件接口实现外设状态变化的通知和命令的分发,为应用程序提供了灵活且可扩展的外设管理能力。
ESP-ADF外设事件机制是连接外设与应用程序的桥梁,主要负责:
事件机制是esp_peripherals组件的核心部分,位于整个外设子系统的中心位置,连接各类外设模块与应用层:
// 发送外设事件(任务上下文)
esp_err_t esp_periph_send_event(esp_periph_handle_t periph, int event_id, void *data, int data_len);
// 发送外设命令(任务上下文)
esp_err_t esp_periph_send_cmd(esp_periph_handle_t periph, int cmd, void *data, int data_len);
// 从中断上下文发送外设命令
esp_err_t esp_periph_send_cmd_from_isr(esp_periph_handle_t periph, int cmd, void *data, int data_len);
// 注册外设事件处理回调
esp_err_t esp_periph_set_register_callback(esp_periph_set_handle_t periph_set_handle, esp_periph_event_handle_t cb, void *user_context);
// 注册外设的事件接口
esp_err_t esp_periph_register_on_events(esp_periph_handle_t periph, esp_periph_event_t *evts);
// 获取外设集合的事件接口
audio_event_iface_handle_t esp_periph_set_get_event_iface(esp_periph_set_handle_t periph_set_handle);
// 获取外设集合的事件队列
QueueHandle_t esp_periph_set_get_queue(esp_periph_set_handle_t periph_set_handle);
typedef struct {
void *source; // 事件源(通常是外设句柄)
int source_type; // 事件源类型(通常是外设ID)
int cmd; // 命令或事件ID
void *data; // 事件数据
int data_len; // 事件数据长度
bool need_free_data; // 是否需要释放数据
} audio_event_iface_msg_t;
typedef struct esp_periph_event {
void *user_ctx; // 用户上下文数据
esp_periph_event_handle_t cb; // 事件回调函数
audio_event_iface_handle_t iface; // 事件接口句柄
} esp_periph_event_t;
typedef esp_err_t (*esp_periph_event_handle_t)(audio_event_iface_msg_t *event, void *context);
ESP-ADF外设事件系统采用基于队列的事件流转机制,主要包括以下几个关键环节:
事件可以由以下几种方式产生:
事件发送有三种主要方式:
事件分发主要通过以下机制实现:
事件处理包括两个层次:
esp_err_t esp_periph_send_event(esp_periph_handle_t periph, int event_id, void *data, int data_len)
{
// 1. 检查外设事件接口是否已注册
if (periph->on_evt == NULL) {
return ESP_FAIL;
}
// 2. 构造事件消息
audio_event_iface_msg_t msg;
msg.source_type = periph->periph_id; // 设置事件源类型为外设ID
msg.cmd = event_id; // 设置命令/事件ID
msg.data = data; // 设置事件数据
msg.data_len = data_len; // 设置数据长度
msg.need_free_data = false; // 默认不释放数据
msg.source = periph; // 设置事件源为外设句柄
// 3. 如果注册了回调函数,则调用回调
if (periph->on_evt->cb) {
periph->on_evt->cb(&msg, periph->on_evt->user_ctx);
}
// 4. 将事件发送到事件队列
return audio_event_iface_sendout(periph->on_evt->iface, &msg);
}
static esp_err_t process_peripheral_event(audio_event_iface_msg_t *msg, void *context)
{
// 1. 获取事件源外设
esp_periph_handle_t periph_evt = (esp_periph_handle_t) msg->source;
esp_periph_handle_t periph;
esp_periph_set_t *sets = context;
// 2. 遍历外设列表,查找匹配的外设
STAILQ_FOREACH(periph, &sets->periph_list, entries) {
// 3. 检查外设ID是否匹配,且外设状态正常
if (periph->periph_id == periph_evt->periph_id
&& periph_evt->state == PERIPH_STATE_RUNNING
&& periph_evt->run
&& !periph_evt->disabled) {
// 4. 调用外设的run函数处理事件
return periph_evt->run(periph_evt, msg);
}
}
return ESP_OK;
}
外设事件系统中的状态管理主要体现在以下几个方面:
ESP-ADF外设系统中的事件类型主要分为两大类:
系统事件是由ESP-ADF框架定义的通用事件,包括:
每种外设都有自己特定的事件类型,例如:
事件在ESP-ADF外设系统中的流向如下:
ESP-ADF外设系统提供了两级回调机制:
通过esp_periph_set_register_callback
注册,处理所有外设的事件:
esp_periph_set_register_callback(periph_set, app_periph_callback, app_context);
// 回调函数实现
esp_err_t app_periph_callback(audio_event_iface_msg_t *event, void *context) {
// 根据event->source_type和event->cmd处理不同类型的事件
switch(event->source_type) {
case PERIPH_ID_BUTTON:
// 处理按键事件
break;
case PERIPH_ID_SDCARD:
// 处理SD卡事件
break;
// 其他外设事件处理...
}
return ESP_OK;
}
每个外设模块内部的run函数处理特定于该外设的命令和事件:
esp_err_t button_periph_run(esp_periph_handle_t periph, audio_event_iface_msg_t *msg) {
// 处理按键特定的命令
switch(msg->cmd) {
case BUTTON_PRESSED:
// 处理按键按下
break;
case BUTTON_RELEASED:
// 处理按键释放
break;
// 其他命令处理...
}
return ESP_OK;
}
外设事件机制依赖以下模块:
以下模块依赖外设事件机制:
以下是使用外设事件机制的基本示例:
#include "esp_peripherals.h"
#include "periph_button.h"
// 外设事件回调函数
static esp_err_t periph_event_handler(audio_event_iface_msg_t *event, void *context)
{
if (event->source_type == PERIPH_ID_BUTTON) {
if (event->cmd == BUTTON_PRESSED) {
ESP_LOGI(TAG, "BUTTON_PRESSED");
// 执行按键按下的操作
} else if (event->cmd == BUTTON_RELEASED) {
ESP_LOGI(TAG, "BUTTON_RELEASED");
// 执行按键释放的操作
}
}
return ESP_OK;
}
void app_main()
{
// 初始化外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
// 注册事件回调
esp_periph_set_register_callback(set, periph_event_handler, NULL);
// 初始化并启动按键外设
periph_button_cfg_t btn_cfg = {
.gpio_mask = GPIO_SEL_36, // 使用GPIO36作为按键输入
};
esp_periph_handle_t button_handle = periph_button_init(&btn_cfg);
esp_periph_start(set, button_handle);
// 应用程序主循环
while (1) {
vTaskDelay(100 / portTICK_PERIOD_MS);
}
// 清理资源
esp_periph_set_destroy(set);
}
以下是一个复杂场景的示例,展示多个外设协同工作:
#include "esp_peripherals.h"
#include "periph_button.h"
#include "periph_sdcard.h"
#include "periph_wifi.h"
#include "periph_led.h"
// 外设事件回调函数
static esp_err_t periph_event_handler(audio_event_iface_msg_t *event, void *context)
{
switch(event->source_type) {
case PERIPH_ID_BUTTON:
if (event->cmd == BUTTON_PRESSED) {
// 按键按下,启动WiFi扫描
esp_periph_handle_t wifi = (esp_periph_handle_t)context;
esp_periph_send_cmd(wifi, PERIPH_WIFI_SCAN, NULL, 0);
}
break;
case PERIPH_ID_WIFI:
if (event->cmd == PERIPH_WIFI_CONNECTED) {
// WiFi连接成功,点亮LED
esp_periph_handle_t led = esp_periph_set_get_by_id(set, PERIPH_ID_LED);
esp_periph_send_cmd(led, PERIPH_LED_ON, NULL, 0);
} else if (event->cmd == PERIPH_WIFI_DISCONNECTED) {
// WiFi断开,熄灭LED
esp_periph_handle_t led = esp_periph_set_get_by_id(set, PERIPH_ID_LED);
esp_periph_send_cmd(led, PERIPH_LED_OFF, NULL, 0);
}
break;
case PERIPH_ID_SDCARD:
if (event->cmd == PERIPH_SDCARD_MOUNTED) {
// SD卡挂载成功,可以开始播放音乐
// ...
}
break;
}
return ESP_OK;
}
void app_main()
{
// 初始化外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
// 初始化各个外设
esp_periph_handle_t button = periph_button_init(&button_cfg);
esp_periph_handle_t wifi = periph_wifi_init(&wifi_cfg);
esp_periph_handle_t sdcard = periph_sdcard_init(&sdcard_cfg);
esp_periph_handle_t led = periph_led_init(&led_cfg);
// 注册事件回调,传入WiFi句柄作为上下文
esp_periph_set_register_callback(set, periph_event_handler, wifi);
// 启动所有外设
esp_periph_start(set, button);
esp_periph_start(set, wifi);
esp_periph_start(set, sdcard);
esp_periph_start(set, led);
// 应用程序主循环
while (1) {
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
事件丢失:队列溢出导致事件丢失
回调函数阻塞:回调函数执行时间过长导致系统响应变慢
事件风暴:短时间内产生大量事件导致系统过载
资源泄漏:未正确释放事件数据导致内存泄漏
ESP-ADF外设事件机制设计有以下优点:
存在的不足: