ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(核心API详解之外设集合管理)

外设集合管理API

esp_periph_set_init

功能描述:初始化外设集合,创建管理外设的数据结构和相关资源。

函数原型

/**
 * @brief      初始化外设集合
 * 
 * @param[in]  config    外设集合配置参数
 * 
 * @return     成功:返回外设集合句柄
 *             失败:返回NULL
 */
esp_periph_set_handle_t esp_periph_set_init(esp_periph_config_t *config)
{
    /* 初始化重要变量 */
    esp_err_t ret = ESP_OK;
    esp_periph_set_t *periph_sets = NULL;
    int _err_step = 1;  // 用于跟踪初始化进度的阶段计数器
    
    /* 使用链式执行分配关键资源,失败则出错 */
    bool _success = (
        (periph_sets                   = audio_calloc(1, sizeof(esp_periph_set_t))) && _err_step++ &&
        (periph_sets->state_event_bits = xEventGroupCreate())                        && _err_step++ &&
        (periph_sets->lock             = mutex_create())                             && _err_step++
    );

    AUDIO_MEM_CHECK(TAG, _success, {
        goto _periph_init_failed;
    });

    /* 初始化外设对象的链表结构,用于存储所有注册的外设 */
    STAILQ_INIT(&periph_sets->periph_list);

    /* 安装GPIO中断服务,优先级设为LEVEL2 */
    ret = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL2);
    if (ret == ESP_ERR_NOT_FOUND) {
        ESP_LOGE(TAG, "No free interrupt found with ESP_INTR_FLAG_LEVEL2");
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0))
        ESP_LOGE(TAG, "Select an available interrupt level based on the interrupt table below");
        esp_intr_dump(stdout);
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
    }

    /* 配置外设集合状态和运行参数 */
    periph_sets->run = false;  // 初始为非运行状态
    xEventGroupClearBits(periph_sets->state_event_bits, STARTED_BIT);  // 清除已启动标志
    xEventGroupSetBits(periph_sets->state_event_bits, STOPPED_BIT);     // 设置已停止标志
    
    /* 从用户配置中读取任务参数 */
    periph_sets->task_stack = config->task_stack;  // 任务栈大小
    periph_sets->task_prio = config->task_prio;    // 任务优先级
    periph_sets->task_core = config->task_core;    // 指定运行核心
    periph_sets->ext_stack = config->extern_stack; // 是否使用外部RAM

    /* 初始化事件处理接口 */
    audio_event_iface_cfg_t event_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
    event_cfg.queue_set_size = 0;  // 不使用队列集
    event_cfg.context = periph_sets;  // 上下文为外设集合
    event_cfg.on_cmd = process_peripheral_event;  // 设置外设事件处理回调
    periph_sets->event_handle.iface = audio_event_iface_init(&event_cfg);
    periph_sets->periph_dynamic_id = PERIPH_ID_CUSTOM_BASE;  // 设置自定义外设id的起始id

    /* 验证事件接口初始化结果并设置超时时间 */
    AUDIO_MEM_CHECK(TAG, periph_sets->event_handle.iface, goto _periph_init_failed);
    audio_event_iface_set_cmd_waiting_timeout(periph_sets->event_handle.iface, DEFAULT_ESP_PERIPH_WAIT_TICK);
    return periph_sets;

_periph_init_failed:
    /* 错误处理:释放已分配的所有资源 */
    if (periph_sets) {
        mutex_destroy(periph_sets->lock);
        vEventGroupDelete(periph_sets->state_event_bits);

        if (periph_sets->event_handle.iface) {
            audio_event_iface_destroy(periph_sets->event_handle.iface);
        }

        audio_free(periph_sets);
        periph_sets = NULL;
    }
    return NULL;
}

参数说明

  • config:外设集合配置参数,类型为esp_periph_config_t
    • task_stack:服务任务栈大小,建议值为4KB(4096),过小可能导致栈溢出
    • task_prio:服务任务优先级,建议值为5,范围为1-25,值越大优先级越高
    • task_core:服务任务运行核心,范围为0-1,表示运行在核心0或桃1上
    • extern_stack:是否使用外部RAM分配栈,默认为false

返回值

  • 成功:返回外设集合句柄,类型为esp_periph_set_handle_t
  • 失败:返回NULL,通常是由于内存分配失败

调用示例

// 使用默认配置初始化外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
if (set == NULL) {
    ESP_LOGE(TAG, "Failed to init periph set");
    return ESP_FAIL;
}

初始化时序图

下图展示了esp_periph_set_init函数的执行流程和关键步骤之间的交互关系:

应用程序 esp_periph_set_init 内存管理 GPIO ISR服务 事件管理 调用esp_periph_set_init(config) audio_calloc(1, sizeof(esp_periph_set_t)) 返回 periph_sets xEventGroupCreate() 返回 state_event_bits mutex_create() 返回 lock STAILQ_INIT(&periph_sets->>periph_list) 初始化外设链表 gpio_install_isr_service(ESP_INTR_FLAG_LEVEL2) 返回状态码 设置初始状态和事件标志 audio_event_iface_init(&event_cfg) 返回事件接口句柄 audio_event_iface_set_cmd_waiting_timeout(...) 返回 periph_sets 释放已分配资源 返回 NULL alt [初始化成功] [初始化失败] 应用程序 esp_periph_set_init 内存管理 GPIO ISR服务 事件管理

这个时序图直观展示了初始化过程中的关键步骤,包括资源分配、状态设置、事件系统初始化和错误处理策略。

esp_periph_set_destroy

功能描述:销毁外设集合,释放相关资源。

函数原型

/**
 * @brief      销毁外设集合
 * 
 * @param[in]  periph_set_handle    外设集合句柄
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL
 */
esp_err_t esp_periph_set_destroy(esp_periph_set_handle_t periph_set_handle)
{
    /* 参数检查 */
    if (periph_set_handle == NULL) {
        AUDIO_ERROR(TAG, "Peripherals have not been initialized");
        return ESP_FAIL;
    }
    
    /* 停止运行并等待外设完全停止 */
    periph_set_handle->run = false;  // 设置运行标志为关闭状态
    esp_periph_wait_for_stop(periph_set_handle, portMAX_DELAY);  // 无限期等待外设停止
    
    /* 遍历并安全清空所有外设 */
    esp_periph_handle_t item, tmp;
    STAILQ_FOREACH_SAFE(item, &periph_set_handle->periph_list, entries, tmp) {
        STAILQ_REMOVE(&periph_set_handle->periph_list, item, esp_periph, entries);  // 从链表移除
        audio_free(item->tag);  // 释放外设标签内存
        audio_free(item);       // 释放外设对象内存
    }
    
    /* 释放系统资源 */
    mutex_destroy(periph_set_handle->lock);            // 释放互斥锁
    vEventGroupDelete(periph_set_handle->state_event_bits);  // 释放事件组

    gpio_uninstall_isr_service();  // 卸载GPIO中断服务
    audio_event_iface_destroy(periph_set_handle->event_handle.iface);  // 释放事件接口
    
    /* 释放外设集合本身并置空句柄 */
    audio_free(periph_set_handle);
    periph_set_handle = NULL;  // 将句柄设置为空
    
    return ESP_OK;
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL

调用示例

// 销毁外设集合
esp_err_t ret = esp_periph_set_destroy(set);
if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to destroy periph set");
}

销毁时序图

下面的时序图展示了esp_periph_set_destroy函数的执行流程和资源释放顺序:

应用程序 esp_periph_set_destroy periph_set_handle 内存管理 GPIO ISR服务 事件管理 互斥锁管理 外设链表 调用esp_periph_set_destroy(Handle) 参数检查(Handle是否为NULL) 返回ESP_FAIL 设置Handle->>run = false 调用esp_periph_wait_for_stop(Handle) 等待外设停止 遍历链表STAILQ_FOREACH_SAFE 取出当前外设项 STAILQ_REMOVE移除外设 释放外设标签(audio_free(item->>tag)) 释放外设对象(audio_free(item)) loop [遍历链表中的每个外设] 逐个释放链表中的外设 释放互斥锁(mutex_destroy(Handle->>lock)) 删除事件组(vEventGroupDelete(Handle->>state_event_bits)) 卸载GPIO中断服务(gpio_uninstall_isr_service()) 释放事件接口(audio_event_iface_destroy(...)) 释放Handle内存(audio_free(Handle)) 返回ESP_OK alt [Handle为NULL] [Handle有效] 应用程序 esp_periph_set_destroy periph_set_handle 内存管理 GPIO ISR服务 事件管理 互斥锁管理 外设链表

这个时序图直观展示了销毁过程中的资源释放顺序,包括停止外设运行、清空外设链表、释放系统资源以及错误处理策略。

esp_periph_set_stop_all

功能描述:停止外设集合中的所有外设。

函数原型

/**
 * @brief      停止集合中的所有外设
 * 
 * @details    通过设置每个外设的disabled标志为true来停止所有外设。
 *             当外设的disabled标志为true时,外设将在其处理循环中检测到此标志并停止运行。
 *             与esp_periph_set_destroy不同,此函数仅禁用外设,不会释放资源。
 * 
 * @param[in]  periph_set_handle    外设集合句柄
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL 参数无效(periph_set_handle为NULL)
 */
esp_err_t esp_periph_set_stop_all(esp_periph_set_handle_t periph_set_handle)
{
    /* 参数检查 */
    if (periph_set_handle == NULL) {
        AUDIO_ERROR(TAG, "Peripherals have not been initialized");
        return ESP_FAIL;
    }
    
    /* 遍历外设链表并设置禁用标志 */
    esp_periph_handle_t periph;
    STAILQ_FOREACH(periph, &periph_set_handle->periph_list, entries) {
        periph->disabled = true;  // 设置禁用标志,使外设在下一轮循环中停止
    }
    
    return ESP_OK;
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL

调用示例

// 停止所有外设
esp_err_t ret = esp_periph_set_stop_all(set);
if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to stop all peripherals");
}

停止外设时序图

应用程序 esp_periph_set_stop_all periph_set_handle 外设链表 调用esp_periph_set_stop_all(Handle) 参数检查(Handle是否为NULL) 返回ESP_FAIL 遍历链表STAILQ_FOREACH 获取当前外设项 设置periph->>disabled = true loop [遍历每个外设periph] 遍历链表并禁用所有外设 返回ESP_OK alt [Handle为NULL] [Handle有效] 应用程序 esp_periph_set_stop_all periph_set_handle 外设链表

这个时序图展示了esp_periph_set_stop_all函数的执行流程,包括参数检查、遍历外设链表、禁用每个外设以及返回结果。

esp_periph_set_get_by_id

功能描述:通过外设ID查找外设集合中的外设句柄。

函数原型

/**
 * @brief      通过外设ID查找外设句柄
 * 
 * @details    遍历外设集合中的链表,查找与指定ID匹配的外设。
 *             此函数会在查找过程中对外设链表加锁,以保证线程安全。
 *             如果找到匹配的外设,则返回其句柄;否则返回NULL。
 * 
 * @param[in]  periph_set_handle    外设集合句柄
 * @param[in]  periph_id            要查找的外设ID
 * 
 * @return     成功:返回匹配的外设句柄
 *             失败:返回NULL(参数无效或未找到指定ID的外设)
 */
esp_periph_handle_t esp_periph_set_get_by_id(esp_periph_set_handle_t periph_set_handle, int periph_id)
{
    /* 参数检查 */
    esp_periph_handle_t periph;
    if (periph_set_handle == NULL) {
        AUDIO_ERROR(TAG, "Peripherals have not been initialized");
        return NULL;
    }
    
    /* 加锁并遍历链表查找外设 */
    mutex_lock(periph_set_handle->lock);
    STAILQ_FOREACH(periph, &periph_set_handle->periph_list, entries) {
        if (periph->periph_id == periph_id) {
            mutex_unlock(periph_set_handle->lock);
            return periph;  // 找到匹配的外设,返回其句柄
        }
    }
    
    /* 未找到匹配的外设 */
    ESP_LOGD(TAG, "Periph id %d not found", periph_id);
    mutex_unlock(periph_set_handle->lock);
    return NULL;
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得
  • periph_id:要查找的外设ID,可以是预定义的esp_periph_id_t枚举值或自定义的外设ID

返回值

  • 成功:返回匹配的外设句柄
  • 失败:返回NULL(参数无效或未找到指定ID的外设)

调用示例

// 获取按钮外设句柄
esp_periph_handle_t button = esp_periph_set_get_by_id(set, PERIPH_ID_BUTTON);
if (button != NULL) {
    // 对按钮外设进行操作
    esp_periph_stop(button);
} else {
    ESP_LOGE(TAG, "Button peripheral not found");
}

查找外设时序图

应用程序 esp_periph_set_get_by_id periph_set_handle 外设链表 互斥锁 调用esp_periph_set_get_by_id(Handle, periph_id) 参数检查(Handle是否为NULL) 返回NULL 调用mutex_lock(Handle->>lock) 获取锁成功 遍历链表STAILQ_FOREACH 获取当前外设项 比较外设ID(periph->>periph_id == periph_id) 调用mutex_unlock(Handle->>lock) 释放锁成功 返回匹配的外设句柄 alt [ID匹配] loop [遍历外设链表] 遍历完成未找到匹配外设 记录日志"Periph id %d not found" 调用mutex_unlock(Handle->>lock) 释放锁成功 返回NULL alt [Handle为NULL] [Handle有效] 应用程序 esp_periph_set_get_by_id periph_set_handle 外设链表 互斥锁

这个时序图展示了esp_periph_set_get_by_id函数的执行流程,包括参数检查、加锁操作、遍历外设链表查找匹配ID的外设、释放锁以及返回结果。

esp_periph_set_get_event_iface

功能描述:获取外设集合的事件接口句柄。

函数原型

/**
 * @brief      获取外设集合的事件接口句柄
 * 
 * @details    返回外设集合内部使用的事件接口句柄,应用程序可以使用此接口直接访问事件系统。
 *             通过此接口,应用程序可以监听和处理所有外设产生的事件。
 * 
 * @param[in]  periph_set_handle    外设集合句柄
 * 
 * @return     返回事件接口句柄(audio_event_iface_handle_t)
 */
audio_event_iface_handle_t esp_periph_set_get_event_iface(esp_periph_set_handle_t periph_set_handle)
{
    return periph_set_handle->event_handle.iface;
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得

返回值

  • 返回事件接口句柄(audio_event_iface_handle_t)

调用示例

// 获取外设集合的事件接口
audio_event_iface_handle_t evt = esp_periph_set_get_event_iface(set);
if (evt) {
    // 使用事件接口处理外设事件
    audio_event_iface_msg_t msg;
    esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY);
    if (ret == ESP_OK) {
        // 处理事件...
    }
}

获取事件接口时序图

应用程序 esp_periph_set_get_event_iface periph_set_handle 事件系统 调用esp_periph_set_get_event_iface(Handle) 访问Handle->>event_handle.iface 返回事件接口句柄 返回事件接口句柄 应用程序后续可以使用此接口 使用接口监听外设事件(audio_event_iface_listen) 返回外设事件 应用程序 esp_periph_set_get_event_iface periph_set_handle 事件系统

这个时序图展示了esp_periph_set_get_event_iface函数的执行流程,以及应用程序如何使用获取到的事件接口来监听和处理外设事件。

esp_periph_set_register_callback

功能描述:为外设集合注册事件回调函数,当有外设事件发生时会调用该回调。

函数原型

/**
 * @brief      注册外设集合的事件回调函数
 * 
 * @details    为外设集合设置一个全局事件处理回调函数。当外设产生事件时,
 *             除了通过事件接口发送事件外,还会调用此回调函数进行额外处理。
 *             这使应用程序可以集中处理所有外设事件。
 * 
 * @param[in]  periph_set_handle    外设集合句柄
 * @param[in]  cb                   事件回调函数
 * @param[in]  user_context         用户上下文指针,会在回调函数中传递
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL(参数无效)
 */
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)
{
    if (periph_set_handle == NULL) {
        return ESP_FAIL;
    } else {
        periph_set_handle->event_handle.cb = cb;
        periph_set_handle->event_handle.user_ctx = user_context;
        return ESP_OK;
    }
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得
  • cb:事件回调函数,函数原型为esp_err_t (*esp_periph_event_handle_t)(audio_event_iface_msg_t *event, void *context)
  • user_context:用户上下文指针,会在回调函数中传递给应用程序

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL(参数无效)

调用示例

// 定义事件回调函数
static esp_err_t periph_event_handler(audio_event_iface_msg_t *event, void *context)
{
    // 处理不同类型的外设事件
    switch (event->source_type) {
        case PERIPH_ID_BUTTON:
            // 处理按钮事件...
            break;
        case PERIPH_ID_TOUCH:
            // 处理触摸事件...
            break;
        default:
            break;
    }
    return ESP_OK;
}

// 注册回调函数
esp_err_t ret = esp_periph_set_register_callback(set, periph_event_handler, NULL);
if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to register peripheral event callback");
}

注册回调时序图

应用程序 esp_periph_set_register_callback periph_set_handle 事件系统 外设 调用esp_periph_set_register_callback(Handle, cb, user_ctx) 参数检查(Handle是否为NULL) 返回ESP_FAIL 设置Handle->>event_handle.cb = cb 设置Handle->>event_handle.user_ctx = user_ctx 返回ESP_OK alt [Handle为NULL] [Handle有效] 注册回调后的事件处理流程 外设产生事件 事件传递到外设集合 调用注册的回调函数(cb)处理事件 应用程序 esp_periph_set_register_callback periph_set_handle 事件系统 外设

这个时序图展示了esp_periph_set_register_callback函数的执行流程,包括参数检查、设置回调函数和上下文,以及回调函数在外设事件发生时的调用过程。

esp_periph_set_get_queue

功能描述:获取外设集合使用的事件队列句柄,可用于直接访问和处理事件队列。

函数原型

/**
 * @brief      获取外设集合的事件队列句柄
 * 
 * @details    返回外设集合内部事件接口使用的队列句柄。应用程序可以使用此队列句柄
 *             直接访问事件队列,例如在需要与其他队列一起使用 xQueueSelectFromSet 
 *             进行多队列监听的场景。
 * 
 * @param[in]  periph_set_handle    外设集合句柄
 * 
 * @return     返回队列句柄(QueueHandle_t)
 */
QueueHandle_t esp_periph_set_get_queue(esp_periph_set_handle_t periph_set_handle)
{
    return audio_event_iface_get_queue_handle(periph_set_handle->event_handle.iface);
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得

返回值

  • 返回外设集合使用的事件队列句柄(QueueHandle_t)

调用示例

// 获取外设集合的队列句柄
QueueHandle_t periph_queue = esp_periph_set_get_queue(set);
if (periph_queue) {
    // 使用队列句柄进行操作
    // 例如:与其他队列一起使用
    QueueSetHandle_t queue_set = xQueueCreateSet(10);
    xQueueAddToSet(periph_queue, queue_set);
    
    // 从队列集合中等待事件
    QueueSetMemberHandle_t active_member = xQueueSelectFromSet(queue_set, portMAX_DELAY);
    if (active_member == periph_queue) {
        // 处理外设事件...
    }
}

获取队列时序图

应用程序 esp_periph_set_get_queue periph_set_handle 事件接口 队列系统 调用esp_periph_set_get_queue(Handle) 调用audio_event_iface_get_queue_handle(Handle->>event_handle.iface) 访问事件接口内部的队列句柄 返回队列句柄 返回队列句柄 返回队列句柄 应用程序后续可以使用此队列句柄 使用队列句柄进行操作(如添加到队列集合) 直接从队列读取事件 应用程序 esp_periph_set_get_queue periph_set_handle 事件接口 队列系统

这个时序图展示了esp_periph_set_get_queue函数的执行流程,以及应用程序如何使用获取到的队列句柄进行进一步操作。

esp_periph_set_list_init

功能描述:初始化外设集合中的所有外设,适用于无任务模式的外设集合。

函数原型

/**
 * @brief      初始化外设集合中的所有外设
 * 
 * @details    遍历外设集合中的所有外设,并调用每个外设的初始化函数。
 *             此函数专为无任务模式的外设集合设计,即在创建外设集合时
 *             未指定任务栈大小(task_stack = 0)的情况。
 *             在无任务模式下,应用程序需要手动调用此函数进行初始化。
 * 
 * @param[in]  periph_set    外设集合句柄
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL
 */
esp_err_t esp_periph_set_list_init(esp_periph_set_handle_t periph_set)
{
    esp_periph_handle_t periph;
    STAILQ_FOREACH(periph, &periph_set->periph_list, entries) {
        if (periph->init) {
            periph->init(periph);
        }
    }
    return ESP_OK;
}

参数说明

  • periph_set:外设集合句柄,通过esp_periph_set_init获得

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL

调用示例

// 创建无任务模式的外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
periph_cfg.task_stack = 0;  // 设置为无任务模式
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);

// 添加外设
esp_periph_handle_t button = periph_button_init(&button_cfg);
esp_periph_start(set, button);

// 手动初始化所有外设
esp_err_t ret = esp_periph_set_list_init(set);
if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to initialize peripherals");
}

// 在主循环中手动运行外设
while (1) {
    audio_event_iface_msg_t msg;
    // 从某处获取消息...
    esp_periph_set_list_run(set, msg);
    vTaskDelay(10 / portTICK_PERIOD_MS);
}

初始化列表时序图

应用程序 esp_periph_set_list_init periph_set_handle 外设链表 外设 调用esp_periph_set_list_init(Handle) 遍历链表STAILQ_FOREACH 获取当前外设项periph 检查periph->>init是否存在 调用periph->>init(periph) 返回初始化结果 alt [init函数存在] loop [遍历每个外设] 返回ESP_OK 应用程序 esp_periph_set_list_init periph_set_handle 外设链表 外设

这个时序图展示了esp_periph_set_list_init函数的执行流程,包括遍历外设链表并调用每个外设的初始化函数。此函数专为无任务模式的外设集合设计,允许应用程序手动控制外设的初始化过程。

esp_periph_set_list_run

功能描述:运行外设集合中的所有外设,处理指定的事件消息,适用于无任务模式的外设集合。

函数原型

/**
 * @brief      运行外设集合中的所有外设
 * 
 * @details    遍历外设集合中的所有外设,并调用每个外设的运行函数处理指定的消息。
 *             此函数专为无任务模式的外设集合设计,需要在应用程序的主循环中
 *             定期调用,以确保外设能正常处理事件。
 * 
 * @param[in]  periph_set    外设集合句柄
 * @param[in]  msg           要处理的事件消息
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL
 */
esp_err_t esp_periph_set_list_run(esp_periph_set_handle_t periph_set, audio_event_iface_msg_t msg)
{
    esp_periph_handle_t periph;
    STAILQ_FOREACH(periph, &periph_set->periph_list, entries) {
        if (periph->run) {
            periph->run(periph, &msg);
        }
    }
    return ESP_OK;
}

参数说明

  • periph_set:外设集合句柄,通过esp_periph_set_init获得
  • msg:要处理的事件消息,包含命令、数据和源信息

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL

调用示例

// 创建无任务模式的外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
periph_cfg.task_stack = 0;  // 设置为无任务模式
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);

// 添加外设并初始化
esp_periph_handle_t button = periph_button_init(&button_cfg);
esp_periph_start(set, button);
esp_periph_set_list_init(set);

// 获取事件接口和队列
audio_event_iface_handle_t evt = esp_periph_set_get_event_iface(set);
QueueHandle_t queue = esp_periph_set_get_queue(set);

// 在主循环中手动运行外设
while (1) {
    audio_event_iface_msg_t msg;
    // 从队列中获取消息
    if (xQueueReceive(queue, &msg, portMAX_DELAY) == pdTRUE) {
        // 调用所有外设的运行函数处理消息
        esp_periph_set_list_run(set, msg);
        
        // 处理消息后的其他操作...
    }
}

运行列表时序图

应用程序 队列 esp_periph_set_list_run periph_set_handle 外设链表 外设 从队列中获取消息(xQueueReceive) 返回消息msg 调用esp_periph_set_list_run(Handle, msg) 遍历链表STAILQ_FOREACH 获取当前外设项periph 检查periph->>run是否存在 调用periph->>run(periph, &msg) 返回处理结果 alt [run函数存在] loop [遍历每个外设] 返回ESP_OK 继续主循环处理 应用程序 队列 esp_periph_set_list_run periph_set_handle 外设链表 外设

这个时序图展示了esp_periph_set_list_run函数的执行流程,包括如何从队列中获取消息并遍历外设链表调用每个外设的运行函数处理消息。此函数与esp_periph_set_list_init配合使用,实现无任务模式下的外设运行控制。

esp_periph_set_list_destroy

功能描述:销毁外设集合中的所有外设,适用于无任务模式的外设集合。

函数原型

/**
 * @brief      销毁外设集合中的所有外设
 * 
 * @details    遍历外设集合中的所有外设,并调用每个外设的销毁函数。
 *             此函数专为无任务模式的外设集合设计,在应用程序需要
 *             手动释放外设资源时使用。注意这不会释放外设集合本身的资源,
 *             只是调用每个外设的销毁函数。
 * 
 * @param[in]  periph_set    外设集合句柄
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL
 */
esp_err_t esp_periph_set_list_destroy(esp_periph_set_handle_t periph_set)
{
    esp_periph_handle_t periph;
    STAILQ_FOREACH(periph, &periph_set->periph_list, entries) {
        if (periph->destroy) {
            periph->destroy(periph);
        }
    }
    return ESP_OK;
}

参数说明

  • periph_set:外设集合句柄,通过esp_periph_set_init获得

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL

调用示例

// 创建无任务模式的外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
periph_cfg.task_stack = 0;  // 设置为无任务模式
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);

// 添加外设并初始化
esp_periph_handle_t button = periph_button_init(&button_cfg);
esp_periph_start(set, button);
esp_periph_set_list_init(set);

// 使用外设进行操作...

// 应用程序结束时销毁外设
esp_periph_set_list_destroy(set);

// 完全释放外设集合资源
esp_periph_set_destroy(set);

销毁列表时序图

应用程序 esp_periph_set_list_destroy periph_set_handle 外设链表 外设 调用esp_periph_set_list_destroy(Handle) 遍历链表STAILQ_FOREACH 获取当前外设项periph 检查periph->>destroy是否存在 调用periph->>destroy(periph) 返回销毁结果 alt [destroy函数存在] loop [遍历每个外设] 返回ESP_OK 注意:此函数仅销毁外设,不释放外设集合结构 调用esp_periph_set_destroy释放外设集合 应用程序 esp_periph_set_list_destroy periph_set_handle 外设链表 外设

这个时序图展示了esp_periph_set_list_destroy函数的执行流程,包括遍历外设链表并调用每个外设的销毁函数。此函数与esp_periph_set_list_initesp_periph_set_list_run配合使用,完成无任务模式下外设的完整生命周期管理。

注意:与esp_periph_set_destroy不同,此函数仅调用外设的销毁函数,不会释放外设集合本身的资源或从链表中移除外设。完全释放外设集合的资源需要调用esp_periph_set_destroy函数。

esp_periph_remove_from_set

功能描述:从外设集合中移除指定的外设,并取消其事件注册。

函数原型

/**
 * @brief      从外设集合中移除指定的外设
 * 
 * @details    将指定的外设从外设集合的链表中移除,并取消其事件注册。
 *             注意这不会销毁外设或释放其资源,只是从集合中移除其引用。
 *             如果需要销毁外设,应在调用此函数前手动调用外设的销毁函数。
 * 
 * @param[in]  periph_set_handle  外设集合句柄
 * @param[in]  periph             要移除的外设句柄
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL
 */
esp_err_t esp_periph_remove_from_set(esp_periph_set_handle_t periph_set_handle, esp_periph_handle_t periph)
{
    STAILQ_REMOVE(&periph_set_handle->periph_list, periph, esp_periph, entries);
    esp_periph_register_on_events(periph, NULL);
    return ESP_OK;
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得
  • periph:要从集合中移除的外设句柄

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL

调用示例

// 创建外设集合
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_start(set, button);

// 使用外设...

// 如果需要先停止外设
esp_periph_stop(button);

// 从集合中移除外设
esp_periph_remove_from_set(set, button);

// 如果需要,手动销毁外设
// 注意:在某些情况下,外设可能需要先调用其销毁函数
// button->destroy(button);

移除外设时序图

应用程序 esp_periph_remove_from_set 外设链表 esp_periph_register_on_events 外设 调用esp_periph_remove_from_set(set, periph) STAILQ_REMOVE(从链表中移除外设) esp_periph_register_on_events(periph, NULL) periph->>on_evt = NULL 返回ESP_OK 返回ESP_OK 注意:此函数仅从集合中移除外设,不销毁外设 应用程序 esp_periph_remove_from_set 外设链表 esp_periph_register_on_events 外设

这个时序图展示了esp_periph_remove_from_set函数的执行流程,包括从外设链表中移除外设并取消其事件注册的过程。

注意:此函数只从外设集合中移除外设的引用,不会销毁外设或释放其资源。如果需要完全清理外设资源,应先停止外设,然后手动调用其销毁函数,最后再从集合中移除。

esp_periph_alloc_periph_id

功能描述:为自定义外设分配唯一的外设 ID。

函数原型

/**
 * @brief      为自定义外设分配唯一的外设 ID
 * 
 * @details    从外设集合的动态 ID 计数器中分配一个新的唯一 ID。
 *             当创建自定义外设时,如果不想使用预定义的 ID,可以使用此函数
 *             获取唯一的 ID。所有自定义外设的 ID 都从 PERIPH_ID_CUSTOM_BASE 开始递增。
 * 
 * @param[in]  periph_set_handle  外设集合句柄
 * @param[out] periph_id          用于存储分配的外设 ID 的指针
 * 
 * @return     成功:ESP_OK
 *             失败:ESP_FAIL(当参数无效时)
 */
esp_err_t esp_periph_alloc_periph_id(esp_periph_set_handle_t periph_set_handle, int *periph_id)
{
    if ((periph_set_handle == NULL) || (periph_id == NULL)) {
        AUDIO_ERROR(TAG, "Invalid parameters: periph_set_handle or periph_id is NULL");
        return ESP_FAIL;
    }
    *periph_id = periph_set_handle->periph_dynamic_id++;
    return ESP_OK;
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得
  • periph_id:输出参数,用于存储分配的外设 ID

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL(当参数无效时)

调用示例

// 创建外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);

// 为自定义外设分配 ID
int my_periph_id;
esp_err_t ret = esp_periph_alloc_periph_id(set, &my_periph_id);
if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to allocate peripheral ID");
    return;
}

// 使用分配的 ID 创建自定义外设
esp_periph_handle_t my_periph = esp_periph_create(my_periph_id, "my_custom_periph");
if (my_periph == NULL) {
    ESP_LOGE(TAG, "Failed to create custom peripheral");
    return;
}

// 设置外设函数
esp_periph_set_function(my_periph, my_init_cb, my_run_cb, my_destroy_cb);

// 启动外设
esp_periph_start(set, my_periph);

分配 ID 时序图

应用程序 esp_periph_alloc_periph_id periph_set_handle esp_periph_create 调用esp_periph_alloc_periph_id(set, &id) 检查参数有效性 访问并递增 periph_dynamic_id 返回分配的 ID 使用 ID 创建外设(esp_periph_create) 返回外设句柄 应用程序 esp_periph_alloc_periph_id periph_set_handle esp_periph_create

这个时序图展示了esp_periph_alloc_periph_id函数的执行流程,包括如何从外设集合中分配唯一 ID 并在后续创建自定义外设的过程。

注意:使用此函数分配的 ID 都是从PERIPH_ID_CUSTOM_BASE开始递增的值,确保不会与预定义的外设 ID 冲突。这对于开发自定义外设非常有用,特别是当需要在运行时动态创建多个自定义外设时。

esp_periph_set_change_waiting_time

功能描述:更改外设集合的命令等待超时时间。

函数原型

/**
 * @brief      更改外设集合的命令等待超时时间
 * 
 * @details    设置外设集合事件接口的命令等待超时时间。
 *             在外设任务循环中,当没有命令需要处理时,任务会等待一段时间。
 *             这个函数允许调整这个等待时间,以平衡响应速度和系统资源消耗。
 * 
 * @param[in]  periph_set_handle  外设集合句柄
 * @param[in]  time_ms            等待时间,单位为毫秒
 * 
 * @return     成功:ESP_OK
 */
esp_err_t esp_periph_set_change_waiting_time(esp_periph_set_handle_t periph_set_handle, int time_ms)
{
    audio_event_iface_set_cmd_waiting_timeout(esp_periph_set_get_event_iface(periph_set_handle), time_ms / portTICK_RATE_MS);
    return ESP_OK;
}

参数说明

  • periph_set_handle:外设集合句柄,通过esp_periph_set_init获得
  • time_ms:新的等待超时时间,单位为毫秒

返回值

  • 成功:返回ESP_OK

调用示例

// 创建外设集合
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_start(set, button);

// 调整命令等待超时时间为 100 毫秒
esp_periph_set_change_waiting_time(set, 100);

// 对于需要更快响应的场景,可以设置更短的等待时间
esp_periph_set_change_waiting_time(set, 10);  // 10 毫秒

// 对于需要节省系统资源的场景,可以设置更长的等待时间
esp_periph_set_change_waiting_time(set, 500);  // 500 毫秒

更改等待时间时序图

应用程序 esp_periph_set_change_waiting_time esp_periph_set_get_event_iface 事件接口 外设任务 调用esp_periph_set_change_waiting_time(set, time_ms) 调用esp_periph_set_get_event_iface(set) 返回事件接口句柄 调用audio_event_iface_set_cmd_waiting_timeout(iface, ticks) 设置超时时间并返回 返回ESP_OK 外设任务循环中使用新的超时时间 等待命令(audio_event_iface_waiting_cmd_msg) 使用新的超时时间等待 应用程序 esp_periph_set_change_waiting_time esp_periph_set_get_event_iface 事件接口 外设任务

这个时序图展示了esp_periph_set_change_waiting_time函数的执行流程,包括如何获取事件接口并设置新的命令等待超时时间。

注意:调整等待时间是一种平衡响应速度和系统资源消耗的方法。较短的等待时间可以提高外设响应速度,但会增加 CPU 使用率;较长的等待时间可以降低 CPU 使用率,但可能会影响外设响应的实时性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

omnibots

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值