【FreeRTOS】事件组
FreeRTOS 事件组
- 事件组和事件位数据类型
- 一、事件组的创建
- 二、清除事件组
- 三、设置事件组
- 四、等待事件组
- 五、查看事件组标志位
- 六、删除事件组
使用信号量来同步任务时,只能与单个任务进行同步,如果某个任务需要等待多个事件时,只用信号量就很难做到;FreeRTOS 的事件组 (Event Groups) 提供了等待多个事件的解决方法,适合处理“等待多个事件中任意一个或多个发生”的场景。
事件组和事件位数据类型
事件组由 EventGroupHandle_t 类型的变量引用。
如果 configUSE_16_BIT_TICKS 设为 1,则事件组中用户可用的存储的位(或标志)数为 8; 如果 configUSE_16_BIT_TICKS 设为 0,则为 24,其中**高 8 位(位 24-31)被系统使用,用户不可以使用。 **
事件组中的所有事件位都 存储在 EventBits_t
类型的单个无符号整数变量中。事件位 0 存储在BIT 0 中, 事件位 1 存储在BIT 1 中,依此类推。
下图表示一个 24 位事件组, 使用 3 个位来保存 3 个事件,仅设置了 事件位 2。

一、事件组的创建
在使用事件组之前,必须创建,否则系统会出错;
创建函数 | 描述 |
---|---|
xEventGroupCreate( void ) | 动态分配方式创建 |
xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) | 使用静态方式创建,需要用户提供一个事件组变量。 |
动态创建事件组:
EventGroupHandle_t xEventGroupCreate( void );
静态创建事件组:
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer );
静态创建示例:
StaticEventGroup_t xEventGroupBuffer;
xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
二、清除事件组
清除事件组,即把事件组中的某些标志位或者全部标志位,清零。
在任务中把事件组中指定的标志位清零
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
参数:
- xEventGroup:要操作的事件组句柄
- uxBitsToClear:要清零的标志位,例如 要清除BIT2 的话,就设置为0x04;可以清除多个标志位,0x07表示同时清除Bit0,Bit1和Bit2 三个标志位。
返回值:
- 清除指定位 之前的事件组的值。
在中断中把事件组中指定的标志位清零
由于中断中不能有阻塞,因此在中断中清除操作会被推迟到 RTOS 守护进程任务,也称为定时器服务任务。 守护进程任务的优先级由 configTIMER_TASK_PRIORITY 设置。
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToClear );
参数:
-
xEventGroup
要在其中清除位的事件组。
-
uxBitsToClear
指定要在事件组中清除的一个或多个位的按位值。例如,将
uxBitsToClear
设置为 0x08,可清除第 3 位。 将uxBitsToClear
设置为 0x09,可清除第 3 位和第 0 位。
返回:
-
pdPASS
如果操作已成功延迟到 RTOS 守护进程任务,则返回 pdPASS。 -
否则,返回 pdFALSE。只有满足以下条件,才返回 pdFALSE:定时器命令队列 已满。
三、设置事件组
在事件组中设置位将自动解除 所有等待位的任务的阻塞状态。
在任务中设置事件标志位
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet );
参数:
-
xEventGroup
要设置位的事件组。
-
uxBitsToSet
指定要在事件组中设置的一个或多个位的按位值。例如,将
uxBitsToSet
设置为 0x08,则只设置第 3 位(BIT3)。将uxBitsToSet
设置为 0x09,即可设置第 3 位和第 0 位。
返回:
- 调用 xEventGroupSetBits() 返回时事件组的值。
在调用xEventGroupSetBits
设置事件组的标志位时,
有两种原因导致 返回值 中 uxBitsToSet
参数指定的位被清除:
-
如果
xEventGroupSetBits
设置某位导致等待该位的高优先级进入就绪状态,系统会立即切换到高优先级任务中去执行, 那么相应的事件标志位会被函数xEventGroupWaitBits
清除掉,等从高优先级任务返回低优先级任务时,xEventGroupSetBits
返回值的就已经被修改了。 -
任何优先级高于 调用
xEventGroupSetBits()
的任务的未阻塞(或其他就绪状态)任务都将执行, 并可能在调用xEventGroupSetBits()
返回之前更改事件组值。
在中断中设置事件组
在事件组中设置位不是确定性操作,因为 可能有未知数量的任务正在等待设置一个或多个位。 FreeRTOS 不允许在中断或临界区 中执行不确定的操作。因此,
xEventGroupSetBitFromISR()
会向 RTOS 守护进程任务发送一条消息, 从而在守护进程任务的上下文中执行设置操作,其中使用的是调度器锁 而非临界区。
从中断服务程序中设置位 会将设置操作推迟到 RTOS 守护进程任务(也叫定时器服务任务) 。RTOS 守护进程任务 与其他RTOS任务一样, 都是根据优先级进行调度的。因此,如果置位操作必须立即完成 (在应用程序创建的任务执行之前), 那么RTOS守护进程任务的优先级必须要高于 其他使用事件组的任务。RTOS 守护进程任务的优先级由 configTIMER_TASK_PRIORITY 定义设置(该定义位于 FreeRTOSConfig.h 中)。
要使用该函数,需要设置FreeRTOSConfig.h 和 FreeRTOS.h
#define configUSE_TIMERS 1
#define INCLUDE_xTimerPendFunctionCall 1
中断中设置事件组API
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t *pxHigherPriorityTaskWoken );
参数:
-
xEventGroup
要设置位的事件组。
-
uxBitsToSet
指定要设置的一个或多个位的按位值。例如,将 uxBitsToSe 设置为 0x08,则只设置第 3 位。将 uxBitsToSet 设置为 0x09,即可设置第 3 位和第 0 位。
-
pxHigherPriorityTaskWoken
如上所述,调用该函数将导致向 RTOS 守护进程任务发送一条消息。 如果守护进程任务的优先级高于当前运行任务 (被中断的任务)的优先级,那么
*pxHigherPriorityTaskWoken 将被设置为 pdTRUE (被 xEventGroupSetBitsFromISR() 设置),表示应在中断退出前请求上下文切换 。因此,*pxHigherPriorityTaskWoken 必须初始化为 pdFALSE。
返回:
- 如果消息已发送到 RTOS 守护进程任务,则返回 pdPASS,否则返回 pdFAIL。 如果定时器服务队列已满,则返回pdFAIL。
示例:
#define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 )/* An event group which it is assumed has already been created by a call toxEventGroupCreate(). */
EventGroupHandle_t xEventGroup;void anInterruptHandler( void )
{BaseType_t xHigherPriorityTaskWoken, xResult;/* xHigherPriorityTaskWoken must be initialised to pdFALSE. */xHigherPriorityTaskWoken = pdFALSE;/* Set bit 0 and bit 4 in xEventGroup. */xResult = xEventGroupSetBitsFromISR(xEventGroup, /* The event group being updated. */BIT_0 | BIT_4, /* The bits being set. */&xHigherPriorityTaskWoken );/* Was the message posted successfully? */if( xResult != pdFAIL ){/* If xHigherPriorityTaskWoken is now set to pdTRUE then a contextswitch should be requested. The macro used is port specific and willbe either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - refer tothe documentation page for the port being used. */portYIELD_FROM_ISR( xHigherPriorityTaskWoken );}
}
四、等待事件组
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup, /* 事件组句柄*/const EventBits_t uxBitsToWaitFor, /* 等待被设置的事件标志位,不能为0 */const BaseType_t xClearOnExit, /* 是否清零被置位的事件标志位,返回之后自动清除标志位,等待下一次事件再次触发 */const BaseType_t xWaitForAllBits, /* 是否等待所有标志位都被置位 */TickType_t xTicksToWait ); /* 设置超时时间 */
参数:
- xClearOnExit
是否清除被置位的事件标志位,如果这个参数设置为 pdTRUE
, 且 函数 xEventGroupWaitBits
在超时时间 xTicksToWait
之前正常返回,那么相应被设置的事件标志位会被清零。如果这个参数设置位pdFALSE
, 则返回后,事件组中设置的标志位不能被清零。
- xTicksToWait
等待时间,时钟节拍周期,如果设置为 portMAX_DELAY
,表示永久等待。
返回值:
事件位等待完成设置或阻塞时间过期时 的事件组值,通过返回值用户可以检测是哪个事件标志位被置 1 了 。
⭐️ 用户可以通过返回值来检查哪些标志位被置1了,xClearOnExit 这一个参数设置为 pdTRUE,不会影响返回值。如果 xClearOnExit 设置位pdFALSE,如果需要,你需要自己手动去清除事件组标志位。
五、查看事件组标志位
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
返回 RTOS 事件组中事件位(事件标志)的当前值。 不能从中断使用此函数。
参数:
-
xEventGroup
正在查询的事件组。
返回:
- 调用事件组中事件位的值。
中断版本函数为:
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
六、删除事件组
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
删除先前的事件组, 该事件组必须事先已被创建。
当事件组被删除后,阻塞在该事件组上的任务将被取消阻塞,并且向等待事件的任务返回事件组的值为 0。