完整教程:ESP32事件组替代全局变量:高效控制任务循环

news/2025/12/3 13:43:44/文章来源:https://www.cnblogs.com/gccbuaa/p/19302000

基本概念

在ESP32的FreeRTOS环境中,用事件标志(Event Groups)替代全局变量(如my_sr.is_running)来控制任务循环,能实现线程安全的同步机制,避免竞态条件和忙等待。事件组是一个32位标志位集合,支持多个任务等待特定事件组合(OR/AND逻辑),任务阻塞等待事件发生,事件设置时自动唤醒,提高CPU效率并简化停止逻辑。[1][3]

为什么用事件标志替代全局变量

语法与参数详解

FreeRTOS事件组API基于EventGroupHandle_t句柄,核心函数如下(包含参数解释,所有返回EventBits_tBaseType_t,失败时pdFALSE):

  • 创建EventGroupHandle_t xEventGroupCreate(void)
    • 无参数。返回句柄(NULL失败,OOM常见);静态创建用vEventGroupCreateStatic节省动态内存。为什么:上电或init时调用,一次创建多任务共享。[3]
  • 设置事件EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)
    • xEventGroup:事件组句柄。
    • uxBitsToSet:要设置的位掩码(e.g., 0x01UL为位0);多位用|组合。
    • 从ISR用xEventGroupSetBitsFromISR。为什么:原子设置位,唤醒等待任务;返回设置前状态。[1]
  • 等待事件EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait)
    • xEventGroup:句柄。
    • uxBitsToWaitFor:感兴趣位(e.g., 0x01UL)。
    • xClearOnExit:true时,唤醒后自动清除匹配位(防重复);false手动清。
    • xWaitForAllBits:true等待所有位(AND);false任一位(OR,常见)。
    • xTicksToWait:超时(portMAX_DELAY无限,pdMS_TO_TICKS(100) 100ms);0非阻塞。
    • 为什么:阻塞直到事件发生,返回发生位;超时返回0。适合循环:while((bits = xEventGroupWaitBits(...)) == 0) { /* 继续工作 */ }。[3]
  • 清除事件EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear)
    • 同上参数。为什么:手动清位,防止任务重复响应(如停止事件后清)。[1]
  • 删除vEventGroupDelete(EventGroupHandle_t xEventGroup)
    • 无参数。为什么:stop/deinit时调用,释放内核资源;先唤醒所有等待任务。[3]
  • 其他uxEventGroupGetNumber(void)查空闲组数;ISR安全版加FromISR后缀。注意:位用UL后缀(无符号长),避免符号问题。[5]

在你的语音任务中的应用

基于之前代码(afe SR + ES8311),用事件组替换my_sr.is_running

完整修改代码示例

以下基于之前改进版,替换为事件组。变化以// 新增/修改:注释;每行解释为什么/坑避。假设头文件已含freertos/event_groups.h

#include "audio_sr.h"
#include "freertos/FreeRTOS.h"     // 为什么:FreeRTOS核心
#include "freertos/task.h"         // 为什么:任务API
#include "freertos/event_groups.h" // 新增:事件组头文件,必含
#include "esp_log.h"               // 为什么:日志
// 全局:移除is_running/mutex,新增事件组
sr_t my_sr = {
.is_wake = false,              // 保留:唤醒状态(可后续用事件替换)
.last_vad_state = VAD_SILENCE,
.ringBuf = NULL,
.vad_change_cb = NULL,
.wake_cb = NULL,
.event_group = NULL            // 新增:事件组句柄
};
esp_afe_sr_iface_t *afe_handle;
esp_afe_sr_data_t *afe_data;
// 新增:事件位定义,为什么:位0运行(启动设置),位1停止(stop设置);UL确保无符号
#define SR_EVENT_RUN    (1UL << 0)  // 位0:运行事件
#define SR_EVENT_STOP   (1UL << 1)  // 位1:停止事件
void audio_sr_init(void) {
// 原init不变:AFE配置详见前文
srmodel_list_t *models = esp_srmodel_init("model");
afe_config_t *afe_config = afe_config_init("M", models, AFE_TYPE_SR, AFE_MODE_LOW_COST);
afe_config->wakenet_init = true;
afe_config->afe_mode = DET_MODE_90;
afe_config->vad_init = true;
afe_config->vad_mode = VAD_MODE_2;
afe_config->aec_init = false;
afe_config->ns_init = false;
afe_config->se_init = false;
afe_handle = esp_afe_handle_from_config(afe_config);
afe_data = afe_handle->create_from_config(afe_config);
// 新增:检查NULL,为什么:早fail防崩溃
if (!afe_handle || !afe_data) {
ESP_LOGE("AUDIO_SR", "AFE init failed");
return;
}
}
void audio_sr_start(RingbufHandle_t ringBuf,
void (*wake_cb)(void),
void (*vad_change_cb)(vad_state_t)) {
// 新增:创建事件组,为什么:xEventGroupCreate动态分配内核块;失败OOM日志
my_sr.event_group = xEventGroupCreate();
if (!my_sr.event_group) {
ESP_LOGE("AUDIO_SR", "Event group create failed");
return;  // 为什么:早返回,避免任务无事件崩溃
}
// 设置状态,为什么:无锁,直接存指针(事件组保护循环)
my_sr.is_wake = false;
my_sr.last_vad_state = VAD_SILENCE;
my_sr.ringBuf = ringBuf;
my_sr.vad_change_cb = vad_change_cb;
my_sr.wake_cb = wake_cb;
// 设置运行事件,为什么:xEventGroupSetBits原子设置位0,唤醒初始等待(若有)
xEventGroupSetBits(my_sr.event_group, SR_EVENT_RUN);
// 创建任务,为什么:优先级5中等;句柄记录用于delete(可选,事件组无需等待句柄)
xTaskCreate(feed_task, "feed_task", 4 * 1024, NULL, 5, NULL);
xTaskCreate(fetch_task, "fetch_task", 4 * 1024, NULL, 5, NULL);
ESP_LOGI("AUDIO_SR", "SR started with event group");
}
// 新增/修改:停止函数,为什么:设置停止位唤醒任务;无mutex简化
void audio_sr_stop(void) {
// 设置停止事件,为什么:原子设置位1,立即唤醒所有等待任务
if (my_sr.event_group) {
xEventGroupSetBits(my_sr.event_group, SR_EVENT_STOP);
}
// 可选:等待任务退出,为什么:用vTaskDelay让内核调度退出;或用句柄+超时
vTaskDelay(pdMS_TO_TICKS(100));  // 为什么:给100ms退出时间,防强制delete;实际监控栈
ESP_LOGI("AUDIO_SR", "SR stop signal sent");
}
// 新增:deinit,为什么:先stop,后删事件组释放
void audio_sr_deinit(void) {
audio_sr_stop();
if (afe_data) {
afe_handle->destroy(afe_data);
afe_data = NULL;
}
if (afe_handle) {
// 假设API:释放AFE
esp_afe_destroy_from_handle(afe_handle);  // 查docs确认
afe_handle = NULL;
}
if (my_sr.event_group) {
vEventGroupDelete(my_sr.event_group);  // 为什么:释放内核资源;先唤醒剩余任务
my_sr.event_group = NULL;
}
// 重置其他字段,为什么:防误用旧状态
my_sr.is_wake = false;
my_sr.ringBuf = NULL;
my_sr.vad_change_cb = NULL;
my_sr.wake_cb = NULL;
}
void feed_task(void *args) {
// 原配置不变
int feed_chunksize = afe_handle->get_feed_chunksize(afe_data);
int feed_nch = afe_handle->get_feed_channel_num(afe_data);
int size = feed_chunksize * feed_nch * sizeof(int16_t);
MY_LOGI("feed_task: feed_chunksize=%d, feed_nch=%d, size=%d", feed_chunksize, feed_nch, size);
int16_t *feed_buff = (int16_t *)malloc(size);  // 为什么:动态分配;结合前malloc讲解
if (!feed_buff) {
ESP_LOGE("FEED_TASK", "malloc failed");
vTaskDelete(NULL);  // 为什么:自删,避免空指针循环
return;
}
// 修改:事件等待循环,为什么:等待RUN|STOP(OR),xClearOnExit=true自动清RUN(防重复),xWaitForAllBits=false(任一位),无限超时
// bits返回发生位:RUN继续,STOP退出
EventBits_t bits;
while ((bits = xEventGroupWaitBits(my_sr.event_group, SR_EVENT_RUN | SR_EVENT_STOP,
pdTRUE, pdFALSE, portMAX_DELAY)) != 0) {
if (bits & SR_EVENT_STOP) {  // 为什么:检查停止位,优雅退出
ESP_LOGI("FEED_TASK", "Stop event received");
break;  // 为什么:退出循环,安全点(喂后)
}
// if (bits & SR_EVENT_RUN) { /* 继续 */ }  // 隐式:RUN时执行
// 喂数据,为什么:bsp_sound_read阻塞读一帧;错误检查可选
bsp_sound_read(feed_buff, size);
afe_handle->feed(afe_data, feed_buff);  // 为什么:AFE内部处理,阻塞少
}
free(feed_buff);  // 为什么:退出前释放,防泄漏
vTaskDelete(NULL);  // 为什么:自删,释放栈;事件组自动处理唤醒
}
void fetch_task(void *args) {
// 修改:同feed,用事件循环;超时可选pdMS_TO_TICKS(10)防AFE卡住
EventBits_t bits;
while ((bits = xEventGroupWaitBits(my_sr.event_group, SR_EVENT_RUN | SR_EVENT_STOP,
pdTRUE, pdFALSE, portMAX_DELAY)) != 0) {
if (bits & SR_EVENT_STOP) {
ESP_LOGI("FETCH_TASK", "Stop event received");
break;
}
afe_fetch_result_t *result = afe_handle->fetch(afe_data);
if (!result) continue;  // 为什么:跳过无效帧
int16_t *processed_audio = result->data;
vad_state_t vad_state = result->vad_state;
wakenet_state_t wakeup_state = result->wakeup_state;
// 唤醒/VAD处理,为什么:is_wake无锁(单任务写),但生产加原子或事件
if (wakeup_state == WAKENET_DETECTED) {
my_sr.is_wake = true;  // 为什么:简单设置;多任务时用事件替换
if (my_sr.wake_cb) my_sr.wake_cb();
}
bool is_wake_local = my_sr.is_wake;  // 为什么:本地拷贝,减少竞争
if (is_wake_local && vad_state != my_sr.last_vad_state) {
my_sr.last_vad_state = vad_state;
if (my_sr.vad_change_cb) my_sr.vad_change_cb(vad_state);
}
if (is_wake_local && vad_state == VAD_SPEECH) {
if (result->vad_cache_size > 0) {
if (my_sr.ringBuf) {
xRingbufferSend(my_sr.ringBuf, result->vad_cache, result->vad_cache_size, 0);
}
}
if (my_sr.ringBuf) {
xRingbufferSend(my_sr.ringBuf, processed_audio, result->data_size, 0);
}
}
// 假设release:afe_handle->release(result);  // 查docs
}
vTaskDelete(NULL);
}

应用与测试建议

  • 调用audio_sr_init(); audio_sr_start(...); /* 运行 */ audio_sr_stop(); audio_sr_deinit();
  • 调试:加ESP_LOGI("Bits: %x", xEventGroupGetBits(my_sr.event_group));查状态。为什么:确认事件流。[1]
  • 收获:全局变量→事件组,提升代码鲁棒(无竞态);小白:事件如共享“开关”,任务等“铃声”。扩展:加位2为“暂停”,xEventGroupSetBits(..., SR_EVENT_PAUSE)。[3]
  • 坑避:事件组耗~100字节内核RAM;多事件用xEventGroupSync同步组。适用于你的SR项目,无需改AFE逻辑。[5]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/985523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PbootCMS网站获取指定栏目下面所有单页内容办法(PbootCMS 获取栏目下所有单页内容的方法与代码示例)

在使用 PbootCMS 建站时,有时需要一次性获取某个栏目下的所有单页(专题模型)内容。例如,展示某个栏目中所有单页的内容摘要或详细信息。本文将详细介绍如何通过 {pboot:nav} 和 {pboot:content} 标签实现这一需求,…

2025去英国留学哪个中介好

2025去英国留学哪个中介好一、2025年去英国留学如何选择中介机构作为从业12年英国留学申请规划导师,我经常被学生和家长询问:2025年计划去英国留学,应该如何选择可靠的中介机构?这个问题的核心在于评估中介的专业性…

2025年五大生物绳填料供应商排行榜,生物绳填料定制品牌商新

为帮助环保工程企业精准锁定适配水处理项目需求的生物绳填料合作伙伴,规避选型中材质不达标、挂膜效率低、安装周期长等坑点,我们从产品核心性能(挂膜速率、比表面积、抗污堵性)、定制化能力(场景适配设计、规格灵…

2025宁波英国留学中介有哪些

2025宁波英国留学中介有哪些一、2025年宁波英国留学中介有哪些作为一位从业超过十年的国际教育规划师,我时常遇到宁波地区的学生和家长咨询同一个问题:在2025年,选择哪家留学中介办理英国留学申请更为稳妥?这个问题…

2025宁波英国留学中介哪个好

2025宁波英国留学中介哪个好一、2025年宁波英国留学中介选择指南 作为从事12年国际教育规划师的我,经常收到宁波地区学生和家长关于英国留学中介的咨询。2025年,随着英国签证政策优化(如毕业生工作签证延长至3年)…

2025年上海十大沙盘制作公司推荐:上海顺恒模型制作实力强

本榜单依托长三角市场深度调研与房企、展馆等核心客户真实反馈,围绕震撼效果呈现、体验交互设计、简约美学落地三大核心需求筛选标杆企业,为项目方提供精准选型参考,助力匹配适配的沙盘服务伙伴。 TOP1 推荐:上海顺…

2025年重庆AI搜索排名品牌企业推荐:看看哪家服务性价比高

本榜单聚焦西南地区AI营销赛道,结合企业技术实力、服务口碑与行业适配性,筛选出5家标杆机构,为企业布局AI搜索流量提供精准参考,助力抢占AI决策时代先机。 TOP1 推荐:重庆瑾宸云信息科技有限公司 推荐指数:★★★…

2025南京英国留学中介排名

2025南京英国留学中介排名一、南京学生如何选择英国留学中介随着2025年留学申请季的临近,南京地区的学生和家长对英国留学中介的选择日益关注。作为从事15年国际教育规划师的我,经常被问及“南京有哪些靠谱的英国留学…

完整教程:Mamba YOLO: 基于状态空间模型的目标检测简单基线

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

AI元人文核心:价值原语的双重通译——在文明契约与算法架构之间

AI元人文核心:价值原语的双重通译——在文明契约与算法架构之间 引言:智能时代的价值转译危机与意义行为原生 我们正深陷一场静默的文明危机:人类用数千年凝结的社会规则,在智能时代遭遇了系统性的“转译失灵”。当…

linux三剑客-awk实战组合用法

echo -n 1,2,3|4,5,6|7,8,9 |awk BEGIN{RS="|";FS=","} {print $1,$2,$3}功能: 将字符串 1,2,3|4,5,6|7,8,9 按照指定的分隔符分割,并打印每一部分的三个字段。 参数说明: echo -n 1,2,3|4,5,6|7…

口碑不错的吐司连续切片机生产厂家推荐

在面包食品工业化生产浪潮中,一台性能稳定、切片精准的吐司连续切片机是保障产能与产品一致性的核心设备。面对市场上参差不齐的供应商,如何找到口碑不错的吐司连续切片机生产厂家?如何确定吐司连续切片机怎么合作?…

2025年口碑不错的自费出书公司排行榜,权威便宜自费出书公司

为帮作者高效锁定适配自身需求的自费出书合作伙伴,避免选型走弯路,我们从服务全流程覆盖度(如选题策划到发行推广)、合规性保障(ISBN书号与CIP备案正规性)、成本透明度(无隐形消费)及真实客户口碑(侧重不同题…

开放式厨房绝配!2025年油烟吸力表现卓越的十大集成灶品牌权威推荐

开放式厨房绝配!2025年油烟吸力表现卓越的十大集成灶品牌权威推荐 随着现代家居设计理念的不断演进,开放式厨房已成为越来越多家庭的核心生活空间。它打破了传统的空间隔阂,营造出开阔、通透、利于家庭成员互动的美…

linux三剑客-grep实战组合用法

find /home -size +1G |xargs du -h查找大于1G的文件,前面是查找,管道符后面是显示大小grep -i error app.log功能: 在 app.log 中搜索包含 "error" 的行(忽略大小写)。 参数: -i: 忽略大小写。 tail -f …

2025年12月河北唐山楔形/涡轮/弯管/超声波/V锥/喷嘴/平衡/孔板流量计供应厂家可靠排行

本文基于2025年12月唐山地区楔形流量计市场趋势,提供一份公正的厂家推荐指南。文章涵盖行业背景、5家实力厂家的详细推荐(包括企业规模、技术实力等维度)、楔形流量计基本介绍、挑选靠谱厂家的实用建议,以及采购指…

题解:Kuangyeyes Random Number

这是一道 Hunan University Judge Online 的题目。题目链接:Kuangyeyes Random Number Problem Description Kuangyeye has a random number in range [0,1023],now he wants to you guess it. Input No input Output…

LightRAG:图增强检索框架,索引速度提升10倍

开篇 你的 RAG 系统能回答"人工智能的发展趋势"这类宏观问题吗?传统向量检索在面对复杂查询时往往力不从心。香港大学团队开源的 LightRAG 提供了新思路:用知识图谱双层索引重构检索架构,在保持轻量化的同…

2025年口碑好的烤漆龙骨行业内口碑厂家排行榜

2025年口碑好的烤漆龙骨行业内口碑厂家排行榜行业背景与市场趋势烤漆龙骨作为现代建筑装饰中的重要支撑材料,近年来随着全球建筑业的持续发展和室内装修标准的不断提高,市场需求稳步增长。据行业数据显示,2024年全球…

C语言基础数据类型

C 语言中有几种基础数据类型,它们是构建更复杂数据结构和程序逻辑的基石。以下详细解释这些基础数据类型,并给出相应的示例。 1. 整型(int)解释:用于表示整数。在不同的系统中,int 类型所占的字节数可能不同,但…