实用指南:FreeRTOS 事件组详解
1. 事件组基本概念
1.1 与队列、信号量的区别
事件组与队列、信号量有两个主要区别:
唤醒机制不同:
队列、信号量:事件发生时,只会唤醒一个任务
事件组:事件发生时,会唤醒所有符合条件的任务,具有"广播"作用
事件处理方式不同:
队列、信号量:消耗型资源(队列数据被读走就消失;信号量被获取后减少)
事件组:被唤醒的任务可以选择是否清除事件
2. 事件组操作流程
2.1 基本使用流程
创建事件组
任务C、D等待事件
任务A、B产生事件
2.2 事件等待选项
等待类型:可以等待某一位、某些位中的任意一个(或关系),或等待多位同时成立(与关系)
清除选项:可选择在获取事件后清除或保留事件
3. 事件组API详解
3.1 创建事件组
动态创建:
c
EventGroupHandle_t xEventGroupCreate(void);
内部自动分配事件组结构体内存
返回句柄,非NULL表示成功
静态创建:
c
EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t *pxEventGroupBuffer);
需要预先分配StaticEventGroup_t结构体
避免动态内存分配,适合内存受限场景
3.2 删除事件组
c
void vEventGroupDelete(EventGroupHandle_t xEventGroup);
删除动态创建的事件组,回收内存
参数:要删除的事件组句柄
3.3 设置事件
在任务中设置:
c
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet);
设置事件组中的特定位
可以同时设置多个位(如0x15表示设置bit4、bit2、bit0)
在ISR中设置:
c
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t *pxHigherPriorityTaskWoken);
通过向FreeRTOS后台任务发送队列数据来间接设置事件组
避免在ISR中直接执行可能唤醒多个任务的操作
3.4 等待事件
c
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait);
参数详解:
参数 | 说明 |
---|---|
xEventGroup | 要等待的事件组句柄 |
uxBitsToWaitFor | 要等待的位(位掩码) |
xWaitForAllBits | 测试方式:pdTRUE-所有位都为1(与关系),pdFALSE-任意位为1(或关系) |
xClearOnExit | 退出时是否清除事件:pdTRUE-清除,pdFALSE-保留 |
xTicksToWait | 阻塞超时时间:0-立即返回,portMAX_DELAY-无限等待,具体tick数 |
返回值:
事件发生时:返回"非阻塞条件成立"时的事件值
超时退出:返回超时时刻的事件值
4. 事件等待示例分析
4.1 "与"关系等待(所有位必须为1)
c
// 示例1:等待bit0和bit2同时为1
EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, 0x05, // bit0和bit2pdTRUE, // 退出时清除pdTRUE, // 等待所有位portMAX_DELAY);// 事件组值变化过程:
// 初始值: 0100 (只有bit2为1) → 任务阻塞
// 设置bit0: 0101 → 满足条件,任务唤醒
4.2 "或"关系等待(任意位为1即可)
c
// 示例2:等待bit1或bit2任意一个为1
EventBits_t uxBits = xEventGroupWaitBits(xEventGroup,0x06, // bit1和bit2pdTRUE, // 退出时清除pdFALSE, // 等待任意位100); // 等待100个tick// 事件组值变化过程:
// 初始值: 0100 (bit2为1) → 立即满足条件,任务不阻塞
5. 实际代码示例
5.1 基本事件组使用
c
// 定义事件位
#define TASK_READY_BIT (1 << 0)
#define DATA_READY_BIT (1 << 1)
#define ERROR_BIT (1 << 2)EventGroupHandle_t xEventGroup;// 任务A:设置事件
void vTaskA(void *pvParameters)
{while(1){// 执行某些操作...xEventGroupSetBits(xEventGroup, TASK_READY_BIT);vTaskDelay(pdMS_TO_TICKS(1000));}
}// 任务B:等待事件
void vTaskB(void *pvParameters)
{const EventBits_t xBitsToWaitFor = TASK_READY_BIT | DATA_READY_BIT;while(1){// 等待任意一个事件发生(或关系)EventBits_t xEvent = xEventGroupWaitBits(xEventGroup, // 事件组句柄xBitsToWaitFor, // 等待的位pdTRUE, // 退出时清除事件pdFALSE, // 或关系:任意位为1即可portMAX_DELAY); // 无限等待if((xEvent & TASK_READY_BIT) != 0){// 处理TASK_READY事件}if((xEvent & DATA_READY_BIT) != 0){// 处理DATA_READY事件}}
}
5.2 复杂事件条件处理
c
// 等待多个条件同时满足(与关系)
void vComplexTask(void *pvParameters)
{// 等待系统就绪且数据有效const EventBits_t xAllRequiredBits = TASK_READY_BIT | DATA_READY_BIT;while(1){EventBits_t xEvent = xEventGroupWaitBits(xEventGroup,xAllRequiredBits,pdTRUE, // 清除事件pdTRUE, // 与关系:所有位必须为1pdMS_TO_TICKS(5000)); // 最多等待5秒if((xEvent & xAllRequiredBits) == xAllRequiredBits){// 所有条件满足,执行操作}else{// 超时,处理超时逻辑}}
}
6. 重要注意事项
6.1 原子操作优势
使用xClearOnExit
参数为pdTRUE时,事件组的测试和清零在xEventGroupWaitBits()
函数内部原子完成,避免了任务切换导致的事件状态不一致问题。
6.2 ISR中的特殊处理
在ISR中使用xEventGroupSetBitsFromISR()
时需要注意:
实际设置操作由后台任务执行,存在延迟
可能唤醒多个任务,带来不确定性
需要检查
pxHigherPriorityTaskWoken
参数,必要时进行任务切换
6.3 事件位管理
合理规划事件位分配,避免冲突
使用宏定义提高代码可读性
注意事件位的清除时机,避免误清除
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/921127.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!