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

FreeRTOS---基础知识6---事件组

事件组是 FreeRTOS 中用于 多任务同步 和 复合事件触发 的高效机制,允许任务等待多个事件中的任意组合(按位操作)。

以下是创建和使用事件组的步骤:

1. 包含必要的头文件

#include "FreeRTOS.h"
#include "event_groups.h"

2. 创建事件组

使用事件组之前,要先创建,得到一个句柄;使用事件组时,要使用句柄来表明使用哪个事件组。 有两种创建方法:动态分配内存、静态分配内存。函数原型如下:

EventGroupHandle_t xEventGroupCreate( void );
/* 创建一个事件组,返回它的句柄。
* 此函数内部会分配事件组结构体 
* 返回值: 返回句柄,非NULL表示成功
*/EventGroupHandle_t xEventGroupCreate( void );/* 创建一个事件组,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticEventGroup_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * 
pxEventGroupBuffer );

3. 设置事件位

可以设置事件组的某个位、某些位,使用的函数有2个: 在任务中使用 xEventGroupSetBits() 在ISR中使用 xEventGroupSetBitsFromISR() 有一个或多个任务在等待事件,如果这些事件符合这些任务的期望,那么任务还会被唤醒。 函数原型如下:

/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位? 
*              
如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1*               
可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0* 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了)*/EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet );/* 设置事件组中的位* xEventGroup: 哪个事件组* uxBitsToSet: 设置哪些位? *              如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1*               可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0* pxHigherPriorityTaskWoken: 有没有导致更高优先级的任务进入就绪态? pdTRUE-有, 
pdFALSE-没有* 返回值: pdPASS-成功, pdFALSE-失败*/BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t * pxHigherPriorityTaskWoken );

#define BIT_0 (1 << 0)  // 第0位
#define BIT_1 (1 << 1)  // 第1位// 设置BIT_0和BIT_1
xEventGroupSetBits(xMyEventGroup, BIT_0 | BIT_1);

注:1<<0表示将1左移0位,即设置第0位;1<<1表示将1左移1位,即设置第1位。

值得注意的是,ISR中的函数,比如队列函数xQueueSendToBackFromISR、信号量函数 xSemaphoreGiveFromISR,它们会唤醒某个任务,最多只会唤醒1个任务。 但是设置事件组时,有可能导致多个任务被唤醒,这会带来很大的不确定性。所以 xEventGroupSetBitsFromISR函数不是直接去设置事件组,而是给一个FreeRTOS后台任务(daemon task)发送队列数据,由这个任务来设置事件组。 如果后台任务的优先级比当前被中断的任务优先级高,xEventGroupSetBitsFromISR会设置 *pxHigherPriorityTaskWoken为pdTRUE。 如果daemon task成功地把队列数据发送给了后台任务,那么xEventGroupSetBitsFromISR的返回值 就是pdPASS。

4. 等待事件位

使用xEventGroupWaitBits来等待事件,可以等待某一位、某些位中的任意一个,也可以等待多位; 等到期望的事件后,还可以清除某些位。


EventBits_t xEventGroupWaitBits( const EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait );

你可以使用xEventGroupWaitBits()等待期望的事件,它发生之后再使用xEventGroupClearBits() 来清除。但是这两个函数之间,有可能被其他任务或中断抢占,它们可能会修改事件组。 可以使用设置xClearOnExit为pdTRUE,使得对事件组的测试、清零都在xEventGroupWaitBits() 函数内部完成,这是一个原子操作。

使用示例:

EventBits_t uxBits;// 等待BIT_0或BIT_1被设置,不清除事件位,等待任意一个位被设置,最多等待100ms
uxBits = xEventGroupWaitBits(xMyEventGroup,  // 事件组句柄BIT_0 | BIT_1,  // 等待的位pdFALSE,        // 不清除事件位pdFALSE,        // 等待任意一个位被设置pdMS_TO_TICKS(100) // 等待时间
);if((uxBits & BIT_0) != 0)
{// BIT_0被设置
}
else if((uxBits & BIT_1) != 0)
{// BIT_1被设置
}
else
{// 超时
}

注意:等待的事件中,它们要么是的关系,要么是的关系。也就是可以等待若干个事件中的任意一个,也可以等待若干个事件中的所有事件,但是不能在若干个事件中指定某些事件

5. 清除事件位


EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );

使用示例:

// 清除BIT_0
xEventGroupClearBits(xMyEventGroup, BIT_0);

注意事项

  1. 事件组使用32位变量存储事件位(在32位架构上),所以最多可以有32个不同的事件位

  2. 事件组是线程安全的,可以在中断和任务中使用

  3. 在中断服务程序(ISR)中设置事件位时,应使用xEventGroupSetBitsFromISR()函数

  4. 使用前确保在FreeRTOSConfig.h中启用了事件组功能(configUSE_EVENT_GROUPS设置为1)

事件组是FreeRTOS中非常强大的同步机制,特别适合需要等待多个事件中任意组合的场景。

6. 事件组与队列的联合使用

事件组仅用于同步和事件通知,不能用于传递数据。数据传递需要使用队列(Queue)、流缓冲区(Stream Buffer)或消息缓冲区(Message Buffer)等机制。下面我将详细说明如何结合使用事件组和队列来实现数据传递。

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"// 定义事件位
#define DATA_READY_BIT (1 << 0)// 创建事件组和队列
EventGroupHandle_t xDataEventGroup;
QueueHandle_t xDataQueue;// 数据生产者任务
void vDataProducerTask(void *pvParameters)
{int iDataToSend = 0;while(1){// 生成数据iDataToSend = generateData(); // 假设的函数// 将数据发送到队列if(xQueueSend(xDataQueue, &iDataToSend, portMAX_DELAY) == pdPASS){// 数据成功入队后,设置事件位通知消费者xEventGroupSetBits(xDataEventGroup, DATA_READY_BIT);}vTaskDelay(pdMS_TO_TICKS(100)); // 适当延迟}
}// 数据消费者任务
void vDataConsumerTask(void *pvParameters)
{int iReceivedData;EventBits_t uxBits;while(1){// 等待数据就绪事件uxBits = xEventGroupWaitBits(xDataEventGroup,DATA_READY_BIT,pdTRUE,    // 退出时清除该位pdFALSE,   // 不需要等待所有位portMAX_DELAY);if((uxBits & DATA_READY_BIT) != 0){// 从队列中获取数据if(xQueueReceive(xDataQueue, &iReceivedData, 0) == pdPASS){// 处理接收到的数据processData(iReceivedData); // 假设的函数}}}
}// 初始化函数
void vInitDataTransfer(void)
{// 创建事件组xDataEventGroup = xEventGroupCreate();// 创建队列,能存储10个int数据xDataQueue = xQueueCreate(10, sizeof(int));// 创建任务xTaskCreate(vDataProducerTask, "Producer", configMINIMAL_STACK_SIZE, NULL, 2, NULL);xTaskCreate(vDataConsumerTask, "Consumer", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
}

http://www.dtcms.com/a/328498.html

相关文章:

  • Effective C++ 条款37:绝不重新定义继承而来的缺省参数值
  • Linux系统编程Day13 -- 程序地址空间
  • Vue3 整合高德地图完成搜索、定位、选址功能,已封装为组件开箱即用(最新)
  • 前端对接豆包AI(vue3+TS版本)
  • 力扣-739.每日温度
  • Leetcode-138. 复制带随机指针的链表
  • AI智能体的“四大支柱”:CAP框架核心层、执行层、约束层、操作层详解​
  • 手机蓝牙无感开锁在智能柜锁与智能箱包中的整体解决方案
  • Iptables 详细使用指南
  • 10-docker基于dockerfile自动制作镜像
  • 计算机网络摘星题库800题笔记 第5章 传输层
  • Ansible 详细笔记
  • _init__.py的作用
  • 电路板的GND与外壳地EARTH通过电容电阻相连
  • 操作系统1.6:虚拟机
  • 图形设计器-Qt Designer (一)包含 LinuxCNC 小部件
  • 基于LLVM的memcpy静态分析工具:设计思路与原理解析(C/C++代码实现)
  • 浏览器面试题及详细答案 88道(12-22)
  • word——选项自动对齐(针对试卷中选项对齐)
  • 2025牛客暑期多校训练营3(FDJAEHB)
  • SuperMap GIS基础产品FAQ集锦(20250811)
  • 多级库存预警:浪智WMS智慧化系统的实时监控体系
  • 启保停-----------单相照明灯的接法
  • LaTex论文审稿修改
  • Day 10-2: Mini-GPT完整手写实战 - 从组件组装到文本生成的端到端实现
  • Jmeter性能测试过程中遇到connection reset的解决方案
  • 深入解析 React 中的 useRef Hook
  • 【c++】反向赋值:颠覆传统的数据交互范式
  • day49 力扣42. 接雨水 力扣84.柱状图中最大的矩形
  • 《疯狂Java讲义(第3版)》学习笔记ch1