彻底弄清FreeRTOS中的事件组(Event Groups)

2023-05-16

​之前已经学过两个任务之间可以利用信号量队列来通信,任务可以利用这两个机制等待某一个事件发生,但是假如需要等待多个事件发生呢?这就需要用到事件组了。

事件组可以让任务进入阻塞态等待一个或多个事件的组合发生。当事件发生时,事件组将正在等待同一事件或事件组合的所有任务解除阻塞。

事件组的特性使它们对于同步多个任务向多个任务广播事件允许任务在阻塞态下等待一组事件中的任何一个发生,以及允许任务在阻塞态下等待多个操作完成非常有用。通常可以用单个事件组替换多个二进制信号量,所以可以减少RAM的使用。

事件组功能是可选的。要包含事件组功能,构建FreeRTOS时要包含源文件event_groups.c。

目录

1.事件组的特性

2.如何使用事件组

2.1创建事件组

2.2设置事件位

2.3等待事件位

3.使用事件组同步任务


1.事件组的特性

事件标志(event flags)事件位(event bits)

事件标志是一个布尔值(1或0),用于指示事件是否发生。事件组其实就是一组事件标志。

一个事件标志只能是1或0,将一个事件标志的状态存储在一个位中,一个事件组中所有事件标志的状态存储在一个变量中。

事件组中每个事件标志的状态由EventBits_t类型变量中的单个位表示。因此,事件标志也被称为事件“位”。如果EventBits_t变量中的一个位被设置为1,那么该位表示的事件已经发生。如果EventBits_t变量中的一个位被设置为0,那么该位表示的事件还没有发生。

事件组中的事件位数取决于FreeRTOSConfig.h1中的configUSE_16_BIT_TICKS

如果configUSE_16_BIT_TICKS为1,则每个事件组包含8个可用的事件位。

如果configUSE_16_BIT_TICKS为0,则每个事件组包含24个可用的事件位。

使用事件组的实际例子:

FreeRTOS+TCP TCP/IP栈的实现提供了如何使用事件组来同时设计和最小化资源使用的实际示例。

TCP套接字必须响应许多不同的事件。包括接受事件(accept)、绑定事件(bind)、读取事件(read)和关闭事件(close)。套接字可以响应事件取决于套接字的状态。例如,如果套接字已经创建,但还没有绑定到地址,那么它可以接收绑定事件,但不会接收读取事件(如果没有地址,它就不能读取数据)。

FreeRTOS+TCP套接字的状态保存在一个名为FreeRTOS_Socket_t的结构中。该结构包含一个事件组,该事件组为套接字必须处理的每个事件定义了一个事件位。FreeRTOS+TCP API调用该块来等待事件或事件组,在事件组上阻塞。事件组还包含一个“abort”位,允许TCP连接被终止,无论套接字当时正在等待哪个事件。

2.如何使用事件组

2.1创建事件组

事件组是EventGroupHandle_t类型的变量。xEventGroupCreate() API函数用于创建事件组,并返回一个EventGroupHandle_t。

EventGroupHandle_t xEventGroupCreate( void );

2.2设置事件位

xEventGroupSetBits() API函数可以在事件组中设置一个或多个位,用于通知任务所设置的位表示的事件已经发生。

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet );
参数作用
xEventGroup要设置位的事件组的句柄。
uxBitsToSet

指定事件组中要设置为1的事件位或事件位的位掩码。事件组的值通过将事件组的现有值与uxBitsToSet中传递的值按位或来更新。

例如,将uxBitsToSet设置为0x04(二进制0100)将导致事件组中的事件位3被设置,而事件组中的所有其他事件位保持不变。

返回值返回调用xEventGroupSetBits()时事件组的值。注意,返回值不一定包含uxBitsToSet指定的位,因为这些位可能已经被不同的任务再次清除。

xEventGroupSetBitsFromISR()是xEventGroupSetBits()的中断安全版本。

给出信号量是一种确定性操作,因为预先知道给出信号量最多会导致一个任务离开阻塞态。但是当在事件组中设置位时,预先不知道有多少任务将离开阻塞状态,因此在事件组中设置位不是一个确定性操作。

FreeRTOS设计和实现标准不允许在中断服务例程中或中断被禁用时执行非确定性操作。因此,xEventGroupSetBitsFromISR()不直接在中断服务例程中设置事件位,而是将操作推迟到RTOS守护任务。

不了解守护任务的可以看这篇:FreeRTOS全解析-7.中断安全API和推迟中断处理

BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t *pxHigherPriorityTaskWoken );
参数作用
xEventGroup要设置位的事件组的句柄。
uxBitsToSet指定事件组中要设置为1的事件位或事件位的位掩码。
pxHigherPriorityTaskWoken

xEventGroupSetBitsFromISR()不直接在中断服务例程中设置事件位,而是通过在timer命令队列上发送命令将操作推迟到RTOS守护进程任务。如果守护任务处于阻塞态,等待timer命令队列上的数据可用,则写入timer命令队列将使守护任务离开阻塞态。如果守护任务的优先级高于当前正在执行的任务(被中断的任务)的优先级,那么,在内部,xEventGroupSetBitsFromISR()将把* pxhigherprioritytaskkoken设置为pdTRUE。

如果xEventGroupSetBitsFromISR()值为pdTRUE,则应该在中断退出之前执行上下文切换。这将确保中断直接返回到守护进程任务,因为守护进程任务将是最高优先级的就绪态任务。

还是不会用的可以看这篇FreeRTOS全解析-7.中断安全API和推迟中断处理

返回值

1.pdPASS,数据成功发送到timer命令队列。

2. pdFALSE,如果'set bits'命令不能写入定时器命令队列,因为队列已满,则返回pdFALSE。

2.3等待事件位

xEventGroupWaitBits() API函数允许任务读取事件组的值,如果事件组中的一个或多个事件位尚未设置,则可以选择在阻塞态下等待事件组中的一个或多个事件位设置完成。

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

uxBitsToWaitFor和xWaitForAllBits参数值的组合决定了调用xEventGroupWaitBits() 的任务,是否要进入阻塞态等待。

uxBitsToWaitFor指定要测试事件组中的事件位;

xWaitForAllBits指定按位或测试或按位与测试。pdTRUE表示uxBitsToWaitFor中指定的位都被设置,才会解除阻塞,pdFALSE表示有一个设置了就行。

例子:

事件组的值uxBitsToWaitForxWaitForAllBits结果
00000101pdFALSE进入阻塞态,因为位0和2都没有设置
01000101pdTRUE进入阻塞态,位0没有设置,位2设置了,但pdTRUE表示两个都得被设置
01000110pdFALSE不会阻塞,位1没有设置,位2设置了,pdFALSE表示有一个设置了就行
01000110pdTRUE进入阻塞态,位1没有设置,位2设置了,pdTRUE表示都得设置

xClearOnExit被设置为pdTRUE,那么在调用任务退出xEventGroupWaitBits() API函数之前,uxBitsToWaitFor指定的事件位将被清除回0。如果xClearOnExit被设置为pdFALSE,那么事件组中事件位的状态不会被xEventGroupWaitBits() API函数修改。

事件位还可以使用xEventGroupClearBits() API函数手动清除,手动清除要注意访问冲突问题,如果xClearOnExit设置为pdTRUE,则事件位的测试和清除将显示在将任务调用为原子操作(不可被其他任务或中断中断)。

3.使用事件组同步任务

一个任务等待多个事件很简单,比如A任务的操作需要B、C、D任务中的某个操作,那A任务调用xEventGroupWaitBits()去等待,当B、C、D中操作完成了,B、C、D分别调用一下xEventGroupSetBits(),都调用后A任务就会继续运行。

但是有时需要两个或多个任务相互同步。例如:

任务A接收一个事件,然后将该事件所需的一些处理委托给其他三个任务:任务B、任务C和任务D(也就是说,没有A就没有BCD)。如果任务A在任务B、C和D都完成处理之前不能接收另一个事件(也就是说没有BCD就没有A),那么这四个任务将需要彼此同步。每个任务的同步点在该任务完成处理后,并且在其他每个任务完成处理之前不能继续进行。任务A只有在所有四个任务都达到同步点后才能接收另一个事件。

事件组用来创建同步点:

必须参与同步的每个任务在事件组中被分配一个唯一的事件位。

每个任务在到达同步点时设置自己的事件位。

在设置了自己的事件位之后,每个任务都阻塞在事件组上,等待其他同步任务的事件位也设置好。

但是,xEventGroupSetBits()和xEventGroupWaitBits() API函数不能在此场景中使用。如果使用了它们,那么位的设置(表示任务已达到其同步点)和位的测试(确定其他同步任务是否已达到其同步点)将作为两个独立的操作执行。要明白为什么这是一个问题,考虑一个场景,任务A,任务B和任务C尝试使用事件组进行同步:

1.任务A和任务B已经到达同步点,所以它们的事件位在事件组中设置,它们处于阻塞态,等待任务C的事件位也设置完成。

2. 任务C到达同步点,使用xEventGroupSetBits()设置其在事件组中的位。只要任务C的位设置好,任务A和任务B就会离开阻塞状态,并清除所有三个事件位。

3.然后任务C调用xEventGroupWaitBits()来等待所有三个事件位都设置好,但是到那时,所有三个事件位都已经被清除,任务A和任务B已经离开了各自的同步点,因此同步失败。

要成功地使用事件组创建同步点,事件位的设置和随后的事件位测试必须作为单个不可中断操作执行,就要用到xEventGroupSync() API函数。

EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait );

参数意思和xEventGroupWaitBits() 是一样的。

如果xEventGroupSync() 是因为满足了解除阻塞条件而返回,返回前会自动清除相应的位。

返回值是事件组的值,满足条件返回时,虽然事件组相应的位会被清0,但是返回值是没清除0的值。因为等待超时返回的话,就是事件组的值。

例如:等待的位是1和2,当事件组为0110,即位1,和2都被设置为1了,也就是说符合解除阻塞条件了,函数就会返回这时,返回值就是0110.

等待的位是1和2,当事件组为0100,或者0000,或者其他不是0110的情况,并且已经超时了,函数就会返回这时,返回值就是为0100,或者0000,或者其他不是0110的情况.

一个完整例子:

static void vSyncingTask( void *pvParameters ){  const TickType_t xMaxDelay = pdMS_TO_TICKS( 4000UL );  const TickType_t xMinDelay = pdMS_TO_TICKS( 200UL );  TickType_t xDelayTime;  EventBits_t uxThisTasksSyncBit;  const EventBits_t uxAllSyncBits = ( mainFIRST_TASK_BIT |mainSECOND_TASK_BIT |mainTHIRD_TASK_BIT );  uxThisTasksSyncBit = ( EventBits_t ) pvParameters;  for( ;; )  {    xDelayTime = ( rand() % xMaxDelay ) + xMinDelay;    vTaskDelay( xDelayTime );    vPrintTwoStrings( pcTaskGetTaskName( NULL ), "reached sync point" );    xEventGroupSync( xEventGroup,uxThisTasksSyncBit,uxAllSyncBits,portMAX_DELAY );    vPrintTwoStrings( pcTaskGetTaskName( NULL ), "exited sync point" );  }}EventGroupHandle_t xEventGroup;int main( void ){  xEventGroup = xEventGroupCreate();  xTaskCreate( vSyncingTask, "Task 1", 1000, mainFIRST_TASK_BIT, 1, NULL );  xTaskCreate( vSyncingTask, "Task 2", 1000, mainSECOND_TASK_BIT, 1, NULL );  xTaskCreate( vSyncingTask, "Task 3", 1000, mainTHIRD_TASK_BIT, 1, NULL );  vTaskStartScheduler();  for( ;; );  return 0;}

任务在到达同步点时打印"reached sync point",然后等待同步,同步后离开同步点,打印"exited sync point"

系列文章合集:

FreeRTOS文章合集

嵌入式Linux基础文章合集

ARM学习系列合集

C语言学习系列合集

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

彻底弄清FreeRTOS中的事件组(Event Groups) 的相关文章

  • const与define的区别

    1 define是预编译指令 xff0c const是普通变量的定义 xff0c define定义的宏是在预处理阶段展开的 xff0c 而const定义的只读变量是在编译运行阶段使用的 2 const定义的是变量 xff0c 而define
  • 如何摆脱CRUD等打杂状态,从事更高价值工作

    每个月都会有十来个来询者向我抱怨工作低端 xff0c 程序员说自己每天CRUD xff0c 重复 枯燥 没技术含量 xff0c 销售助理说自己天天搜集客户信息 打印资料 帮老大带饭 xff0c 繁琐 无聊 不重要 xff0c 他们都说自己整
  • Windows下Qt 5.2 for Android开发入门

    Qt 5 2 发布了 xff0c 支持 Android 平台 xff0c 太好了 之前公司项目 xff0c 为了移植一个依赖 Qt 的程序到安卓平台上 xff0c 我自己交叉编译了 Qt Embedded 4 5 2 xff0c 费了老大劲
  • Qt Quick 之 QML 与 C++ 混合编程详解

    Qt Quick 技术的引入 xff0c 使得你能够快速构建 UI xff0c 具有动画 各种绚丽效果的 UI 都不在话下 但它不是万能的 xff0c 也有很多局限性 xff0c 原来 Qt 的一些技术 xff0c 比如低阶的网络编程如 Q
  • 漫谈程序员系列:一张图道尽程序员的出路

    推背图 相传由唐太宗时期的司天监李淳风和袁天罡合著 xff08 此两人其实是超级武学高手 xff0c 参见小椴的 开唐 xff09 xff0c 推算大唐以后中国两千多年的国运盛衰 xff0c 在中国七大预言书中居首 xff0c 是当之无愧的
  • 漫谈程序员系列:咦,你也在混日子啊

    戳你一下 xff0c 疼吗 xff1f 混日子的定义 来自百度百科的定义 xff1a 生活等方面过得不怎么好 xff0c 无目标 xff0c 混混沌沌 混日子 xff1a 即没有理想 xff0c 没有抱负 xff0c 糊里糊涂地生活 也指工
  • QtAndroid详解(1):QAndroidJniObject

    Qt 5 3之后 xff0c 新增了 QtAndroid 名字空间 xff0c 内有下列四个方法 xff1a QAndroidJniObject AndroidActivity int androidSdkVersion void star
  • freeSWITCH安装、配置与局域网测试

    这次来说说 freeSWITCH 的安装和配置 1 安装 freeSWITCH 下载页面 xff1a https freeswitch org confluence display FREESWITCH Installation 我们 Wi
  • 就 3 点,提升工作效率

    要想提高工作效率 xff0c 不论你看什么书 xff0c 看什么文章 xff0c 用什么工具 xff0c 只有下面这三点最重要 xff1a 动力剖析自己 xff0c 找到改善的切入点付诸行动并且坚持 目标驱动 有目标才能高效 我们爬山 xf
  • Python3 下 ROS 的使用 cv_bridge

    Python 3 下 ROSmsg 转 cv2 项目中用到的 Tensorflow2 4 的环境 xff0c 该环境只支持python3 版本 xff0c 项目中遇到不少需要和 ROS 交互的地方 xff0c 所以不断探索 python3
  • 深度图和RGB图对齐

    深度图 canny RGB canny Alignment xff1a code span class token function import span cv2 span class token function import span
  • 认识romfs文件系统

    1 1 什么是romfs romfs 是一个只读文件系统 xff0c 主要用在 mainly for initial RAM disks of installation disks 使用romfs 文件系统可以构造出一个最小的内核 xff0
  • Livox Lidar 特征提取方式总结

    传统的 旋转式雷达 xff0c 激光固定在雷达的旋转部件上 xff0c 依靠转子的转动而获得360的旋转视野 xff0c 由于旋转部件的存在 xff0c 为了获得更加精准的数据 xff0c 需要跟多的人工校准和更复杂的设计 xff0c 也因
  • C++ 菜鸟之路 (四) boost::thread 多线程全解析

    boost thread 的一般用法 boost thread的几个函数 锁 lock 函数多线程函数的限制 官方解释 xff1a http www cplusplus com reference thread thread joinabl
  • ROS 之 advertise 详解

    在学习过程中接触到如下的一段话 span class hljs comment ROS handles span ros span class hljs tag NodeHandle span node tf span class hljs
  • Linux 下 openMP 效率并未提升的解决方案

    OpenMP 正确观察计算时间OpenMP 经验总结 xff08 1 xff09 openmp 线程使用范围 xff08 2 xff09 openmp 多层嵌套的问题 OpenMP 正确观察计算时间 在使用 openmp的过程中 xff0c
  • C++ Yaml文件解析安装及使用 yaml-cpp

    C 43 43 Yaml文件解析安装及使用 安装 yaml cpp克隆官方库编译 yaml cpp 示例代码robot cpprobot yaml编译 robot cpp运行结果 难点分析与总结什么是 a 与 so 文件静态链接库 a 与动
  • 点云数据格式解析 sensor_msgs::PointCloud2

    在使用多线激光的时候需要总是会碰到点云数据 xff0c 这里简单的接受一下点云数据 xff0c 并堆数据结构进行分析 xff0c 方便自己后期对点云特征数据进行处理 文章目录 Rviz中的点云数据点云数据结构分析点云数据 python 解析
  • Arduino 读取GPS 数据发送解析并发布ROS topic(二)

    Arduino 读取GPS 数据发送解析并发布ROS topic 一 https blog csdn net Fourier Legend article details 84107494 概述 本部分将主要讲将串口接受到的数据 xff0c
  • LOAM进行点云地图创建

    3D激光点云数据处理入门 xff08 一 xff09 使用LOAM进行点云地图创建 LOAM 原理简述topic关系算法分析算法伪代码 LOAM 建图实践创建你的 ROS Workspace下载LOAM Package下载数据包运行 LOA

随机推荐

  • ROS - teb_local_planner 参数总结

    参考官方教程 http wiki ros org teb local planner Tutorials 全英文看着有点累 在此总结一下调试的过程和小小的经验 安装 teb local planner sudo apt get instal
  • 路径规划算法(2) - A*寻路算法 python实现及解析

    代码 span class token comment coding 61 utf 8 span span class token keyword import span math span class token comment 启发距离
  • 使用FSMC驱动LCD以及数据线偏移的问题

    FSMC的理解 使用FSMC功能将8080接口的LCD当外部RAM来使用 xff08 数据传给LCD时没经过内部SRAM xff0c 所以一帧图片很大也可以直接传 xff09 xff0c 根据STM的地址分配图可以看出外部RAM的地址由0x
  • C++调用HTTP实现方式

    转自 xff1a http blog 163 com lyz sea blog static 11558670720118245052189 Http访问有两种方式 xff0c GET和POST xff0c 就编程来说GET方式相对简单点
  • 跟我学c++中级篇——STL智能指针再述

    一 智能指针 xff08 smart pointer xff09 在前面的文章分析中对智能指针分析的还是比较多的 xff0c 这里把一具体的遗漏以及一些新的感悟再总结一下 xff0c 以之为鉴 什么是智能指针 xff1f 在C C 43 4
  • 从CAN到CANOpen——准入门大全(二)

    第二节 关于CAN ID xff1a 一个标准帧的ID共11位 xff0c 一个扩展帧的ID共29位 扩展帧的ID分11位和18位的两段 xff0c 如下图所示 RTR显性 xff1a 数据帧 xff1b 隐性 xff1a 远程帧 SRR隐
  • vscode使用clang-format格式化C++代码

    1 安装c c 43 43 插件 2 在首选项 设置中搜索format xff0c 设置Editor Default Formatter为ms vscode cpptools 3 在扩展C C 43 43 中设置 Clang format
  • EC20 GPS RMC格式数据转化

    文章目录 目录 前言 一 RMC是什么 xff1f 二 EC20 输出的RMC解析 1 EC20返回的RMC报文 2 RMC报文解析 3 NMEA数据ddmm mmmm转换成dd ddddd 4 RMC UTC时间转化成北京时间 总结 前言
  • C++ 第三方常用网络库

    From xff1a https www cnblogs com aitantianderuangutou p 11416902 html 1 ACE 庞大 复杂 xff0c 适合大型项目 开源 免费 xff0c 不依赖第三方库 xff0c
  • Postman 使用方法详解

    From xff1a https zhuanlan zhihu com p 534078123 Postman V9 16 绿色版汉化 xff1a https www cr173 com soft 1497202 html Postman是
  • 串口波形分析

    本文使用逻辑分析仪 xff0c 抓取串口波形 xff0c 进而分析串口数据 串口配置为115200波特率 xff0c 8个数据位 xff0c 1个停止位 xff0c 无校验方式 字符1的波形如下图 xff1a 从图中可以看到8个数据位 xf
  • 电影资源 BT PT下载的电影命名 规则 资源 详解

    初识 一般来说 xff0c 正规压制组压制的电影 xff0c 都采用 0day 命名方式 xff0c 即 xff1a 英文名称 版本说明 年份 片源 分辨率 视频编码 音频格式 压制小组 例如文件名 xff1a Jumanji The Ne
  • 对于51单片机的RAM内存分配(包含栈的分配)

    对于51单片机的RAM内存分配 xff08 包含栈的分配 xff09 我使用的是SH79F3283 xff0c 内部RAM有256字节 xff0c 由常规寄存器 静态存储区和堆栈组成的 xff0c 创建一个新的程序默认占用9个字节RAM x
  • 忘记windows密码解决办法(用户密码或SYSKEY)

    64 TOC 还有朋友问第一层的BIOS密码怎么解 xff1a 拔电池就能解 xff0c 断电 xff0c 打开电脑主板 xff0c 找到一个纽扣电池 xff0c 拔掉 xff0c 长按电源键 xff0c 再插电 xff0c 第一层就没了
  • 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码

    超详细讲解哈夫曼树 Huffman Tree 以及哈夫曼编码的构造原理 方法 xff0c 并用代码实现 1哈夫曼树基本概念 路径 从树中一个结点到另一个结点之间的分支构成这两个结点间的路径 结点的路径长度 两结点间路径上的分支数 树的路径长
  • Makefile命令参数、调用其他Makefile、嵌套

    更多单片机 嵌入式内容及参考资料 xff1a 大叔的嵌入式小站 xff1a Makefile学习 3 make命令参数 调用其他makefile 嵌套 一 Make命令运行参数 参数 参数的作用 C dir读入指定目录下的makefile
  • ARM学习-ARM指令集详解

    目录 1 ARM 存储器访问指令 1 1 LDR 和STR 加载 存储字和无符号字节指令 1 2 LDM和STM 批量加载 存储指令 1 3 SWP 寄存器和存储器交换指令 2 ARM 数据处理指令 2 1数据传送指令 2 1 1 MOV
  • IIC(I2C)协议详解

    1 简介 IIC 即I C xff0c 全称 Inter Integrated Circuit xff0c 字面上的意思是集成电路之间 xff0c 它其实是I C Bus简称 xff0c 所以中文应该叫 集成电路总线 xff0c 它是一种串
  • STM32F4+FreeRTOS+LVGL实现嵌入式快速开发(缝合怪)

    极速进行项目开发 xff0c 只需要懂一款芯片架构 43 一个操作系统 43 一个GUI 各种部件程序全靠抄 xff0c 成为究极缝合怪 本文用stm32f407 43 FreeRTOS 43 lvgl演示一些demo 原文链接 xff1a
  • 彻底弄清FreeRTOS中的事件组(Event Groups)

    之前已经学过两个任务之间可以利用信号量 队列来通信 xff0c 任务可以利用这两个机制等待某一个事件发生 xff0c 但是假如需要等待多个事件发生呢 xff1f 这就需要用到事件组了 事件组可以让任务进入阻塞态 xff0c 等待一个或多个事