当前位置: 首页 > backend >正文

FreeRTOS事件组-笔记

FreeRTOS事件组-笔记

    • **一、事件组的核心概念**
      • **1. 定义与特性**
    • **二、事件组的存储结构**
      • **1. 位数与配置**
    • **三、事件组的核心API函数**
      • **1. 创建与删除**
      • **2. 事件位操作**
      • **3. 读取事件组当前值**
      • **4. 等待事件条件成立**
    • **四、事件组的典型应用场景**
      • **1. 多事件组合触发**
      • **2. 事件广播**
      • **3. 任务同步**
    • **五、ISR中使用事件组的注意事项**
    • **六、事件组与队列/信号量的对比**
    • **七、代码示例**
      • **1. 按键事件触发多个任务**
      • **2. 等待多个事件同时发生**


一、事件组的核心概念

1. 定义与特性

  • 事件组是FreeRTOS中一种进程间通信技术,用于处理多个事件触发一个或多个任务运行的情况。与队列和信号量不同,事件组允许任务等待一个或多个事件的组合,并能解除所有等待同一事件的任务的阻塞状态,适用于事件的广播和多个任务的同步运行。其特性使得事件组适用于等待一组事件中某个事件发生后做出响应、一组事件都发生后的响应、事件广播和任务间的同步等场景。
    在这里插入图片描述

  • 事件组(Event Group):本质是一个位掩码(bitmask),用于表示多个事件的状态(每个位代表一个事件)。支持一对多广播,可同时唤醒多个任务。

  • 与传统同步机制的对比

    • 队列/信号量:仅支持一对一唤醒(一个任务被唤醒),且每次操作消耗资源(如队列数据被读走)。
    • 事件组:支持一对多唤醒(多个任务可同时响应同一事件),且事件位可保留或清除,无需消耗资源。

例如1:
先后按下左键和右键,或者只按下其中一个键,那事件组会解除所有等待同一事件的任务的阻塞状态。
例如2:
在任务1中使用LED1来闪烁报警,任务2中使用蜂鸣器报警。当报警事件发生时,两个任务同时解除阻塞状态,两个任务都开始运行。
在这里插入图片描述

在这里插入图片描述


二、事件组的存储结构

在这里插入图片描述

在这里插入图片描述

1. 位数与配置

  • 32位事件组
    • 低24位(bit0-bit23):用户可用事件位(表示具体事件,如按键、串口就绪)。
    • 高8位(bit24-bit31):保留给内核使用(如任务阻塞状态)。
  • 16位事件组(若 configUSE_16_BIT_TICKS == 1):
    • 低8位(bit0-bit7):用户可用事件位。
    • 高8位(bit8-bit15):保留给内核使用。

三、事件组的核心API函数

在这里插入图片描述

1. 创建与删除

  • 创建
    EventGroupHandle_t xEventGroupCreate(void);  // 动态分配内存
    EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t *pxEventGroupBuffer);  // 静态分配
    
  • 删除
    void vEventGroupDelete(EventGroupHandle_t xEventGroup);
    

2. 事件位操作

  • 设置事件位
    EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, EventBits_t uxBitsToSet);  // 任务中使用
    BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup, EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken);  // ISR中使用
    
  • 清除事件位
    EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, EventBits_t uxBitsToClear);  // 任务中使用
    BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup, EventBits_t uxBitsToClear);  // ISR中使用
    

3. 读取事件组当前值

#define xEventGroupGetBits(xEventGroup) xEventGroupClearBits(xEventGroup, 0)  // 不清除事件位,仅读取当前值

4. 等待事件条件成立

  • 通用等待函数
    EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,EventBits_t uxBitsToWaitFor,          // 需要等待的事件位掩码BaseType_t xClearOnExit,              // pdTRUE:退出时清除指定事件位;pdFALSE:不清除BaseType_t xWaitForAllBits,           // pdTRUE:需所有事件位都置位;pdFALSE:任一事件位置位即可TickType_t xTicksToWait               // 超时时间(portMAX_DELAY表示无限等待)
    );
    
  • 同步等待函数(用于同步点):
    EventBits_t xEventGroupSync(EventGroupHandle_t xEventGroup,EventBits_t uxBitsToSet,            // 需要置位的事件位EventBits_t uxBitsToWaitFor,        // 需要等待的事件位TickType_t xTicksToWait             // 超时时间
    );
    

四、事件组的典型应用场景

1. 多事件组合触发

  • 或关系:等待任意一个事件发生(如按键按下或串口数据到达)。
  • 与关系:等待所有事件发生(如左键和右键同时按下)。
    // 等待 BIT_0 或 BIT_1 中任意一个事件
    EventBits_t xBits = xEventGroupWaitBits(xEventGroup, BIT_0 | BIT_1, pdFALSE, pdFALSE, portMAX_DELAY);
    if (xBits & BIT_0) { /* BIT_0 触发 */ }
    if (xBits & BIT_1) { /* BIT_1 触发 */ }// 等待 BIT_0 和 BIT_1 同时发生
    xBits = xEventGroupWaitBits(xEventGroup, BIT_0 | BIT_1, pdTRUE, pdTRUE, portMAX_DELAY);
    

2. 事件广播

  • 多任务响应同一事件:当某个事件位被置位时,所有等待该事件的任务都会被唤醒。
    // 任务A:等待 BIT_0
    xEventGroupWaitBits(xEventGroup, BIT_0, pdFALSE, pdFALSE, portMAX_DELAY);// 任务B:等待 BIT_0
    xEventGroupWaitBits(xEventGroup, BIT_0, pdFALSE, pdFALSE, portMAX_DELAY);// 中断中置位 BIT_0
    xEventGroupSetBitsFromISR(xEventGroup, BIT_0, &xHigherPriorityTaskWoken);
    

3. 任务同步

  • 同步点:多个任务在某个同步点等待其他任务完成后再继续执行。
    // 任务1:设置 BIT_0 并等待 BIT_1
    xEventGroupSync(xEventGroup, BIT_0, BIT_1, portMAX_DELAY);// 任务2:设置 BIT_1 并等待 BIT_0
    xEventGroupSync(xEventGroup, BIT_1, BIT_0, portMAX_DELAY);
    

五、ISR中使用事件组的注意事项

  1. 非确定性操作:FreeRTOS禁止在ISR中直接执行非确定性操作(如唤醒多个任务)。因此,xEventGroupSetBitsFromISR 实际通过定时器守护任务异步执行事件位设置。
  2. 上下文切换:若唤醒的高优先级任务需要立即运行,需在ISR中调用 portYIELD_FROM_ISR(xHigherPriorityTaskWoken)

六、事件组与队列/信号量的对比

特性事件组队列/信号量
唤醒方式多任务唤醒(一对多广播)单任务唤醒(一对一)
事件组合支持或/与关系(多个事件组合触发)仅支持单一事件触发
资源消耗低(仅需一个位掩码)高(需存储数据或计数)
适用场景多任务同步、事件广播、多条件等待数据传递、资源管理、简单同步

七、代码示例

1. 按键事件触发多个任务

// 创建事件组
EventGroupHandle_t xButtonEventGroup = xEventGroupCreate();// 任务A:LED闪烁报警
void vLEDTask(void *pvParameters) {while (1) {xEventGroupWaitBits(xButtonEventGroup, BIT_0, pdTRUE, pdFALSE, portMAX_DELAY);// LED闪烁}
}// 任务B:蜂鸣器报警
void vBuzzerTask(void *pvParameters) {while (1) {xEventGroupWaitBits(xButtonEventGroup, BIT_0, pdTRUE, pdFALSE, portMAX_DELAY);// 蜂鸣器响}
}// 中断服务程序:按键按下触发事件
void GPIO_IRQHandler(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;xEventGroupSetBitsFromISR(xButtonEventGroup, BIT_0, &xHigherPriorityTaskWoken);portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

2. 等待多个事件同时发生

// 任务A:等待 BIT_0 和 BIT_1 同时发生
void vTaskA(void *pvParameters) {while (1) {EventBits_t xBits = xEventGroupWaitBits(xEventGroup, BIT_0 | BIT_1, pdTRUE, pdTRUE, portMAX_DELAY);if ((xBits & BIT_0) && (xBits & BIT_1)) {// 执行操作}}
}

http://www.xdnf.cn/news/12650.html

相关文章:

  • 33、原子操作
  • C++常用的自动化测试库
  • PostgreSQL数据类型使用
  • 【生活】程序员防猝si指南
  • java_网络服务相关_gateway_nacos_feign区别联系
  • JAVA-springboot log日志
  • 打卡46天
  • DDPM优化目标公式推导
  • JAVA国际版二手交易系统手机回收好物回收发布闲置商品系统源码支持APP+H5
  • 每日算法刷题Day25 6.7:leetcode二分答案3道题,用时1h40min(遇到两道动态规划和贪心时间较长)
  • 【Go语言基础【15】】数组:固定长度的连续存储结构
  • FreeRTOS任务之深入篇
  • ps蒙版介绍
  • Mac软件卸载指南,简单易懂!
  • MySQL复杂SQL(多表联查/子查询)详细讲解
  • 程序代码篇---Python串口
  • Vibe Coding AI编程
  • 安全访问家中 Linux 服务器的远程方案 —— 专为单用户场景设计
  • nginx日志的一点理解
  • Nginx 事件驱动理解
  • LeetCode 118 杨辉三角 (Java)
  • 移动应用开发专业核心课程以及就业方向
  • QT使用AES加解密,openssl及QCA问题记录
  • OpenLayers 从后端服务加载 GeoJSON 数据
  • Windows 下搭建 Zephyr 开发环境
  • Linux 文件系统与 I/O 编程核心原理及实践笔记
  • 【科研绘图系列】R语言绘制论文组图(multiple plots)
  • BERT, GPT, Transformer之间的关系
  • 接口不是json的内容能用Jsonpath获取吗,如果不能,我们选用什么方法处理呢?
  • 巴西医疗巨头尤迈Kafka数据泄露事件的全过程分析与AI安防策略分析