# 事件位(或标志)与事件组
事件位:用于指示事件是否发生;事件位通常称为事件标志。
事件组:是一组事件位;事件组中的各个事件位由位号引用(即每一 bit 代表某个事件)
# 事件组和事件位数据类型
事件组由 EventGroupHandle_t
类型的变量引用
如果 configUSE_16_BIT_TICKS
设置为 1
,则事件组中存储的位数(或标志)为 8;如果 configUSE_16_BIT_TICKS
设置为 0
,则为 24;对 configUSE_16_BIT_TICKS
的依赖性是由于在内部实现中用于线程本地存储的数据类型任务。
configUSE_16_BIT_TICKS
的具体的描述可以看 FreeRTOS 篇章之 FreeRTOSConfig.h 分析 。
事件组中的所有事件位都存储在 EventBits_t
类型的单个无符号变量中。事件位 0 存储在位位置 0,事件位 1 存储在位位置 1,依此类推。
下图显示了一个 24 位事件组,该组使用三个位来保存已描述的三个示例事件。在图像中,仅事件位 2 被设置。
# 使用事件组必须克服的问题
实施事件组时,RTOS 必须克服的两个主要挑战是:
1、避免在用户的应用程序中创建竞争条件:
在以下情况下,事件组的实现将会在应用程序中创建竞争条件:
- 目前尚不清楚谁负责清除单个位(或标志)。
- 尚不清楚何时清除一点。
- 尚不清楚在任务退出测试该位值的 API 函数时是否设置了位或清除了位(这可能是因为另一个任务或中断已更改了位的状态)。
FreeRTOS 事件组的实现通过智能的建立来确保设置、测试和清除位的原子性,从而消除了竞争条件的可能性。线程本地存储和谨慎使用 API 函数返回值使这成为可能。
2、避免不确定性:
事件组的概念隐含了不确定性行为,因为它不知道事件组上有多少个任务被阻止,因此,当设置了事件位时,也不知道需要测试多少条件或解除多少任务阻塞。
FreeRTOS 质量标准不允许不确定的行为在中断禁用或者中断服务中发生。为确保在设置事件位时不会违反这些严格的质量标准,需要以下两点:
- RTOS 调度器的锁定机制用于确保从 RTOS 任务设置事件位时,中断保持启用状态。
- 中央延迟中断机制用于在试图从中断服务例程设置事件位时,将设置位的动作延迟到任务。
# 事件组的 RTOS API 函数
事件组的 API 函数允许任务或者其他事项来设置事件组中的一个或多个事件位,清除事件组中的 一个或多个事件位,并暂挂(进入 “阻止” 状态,因此任务不会占用任何处理时间)以等待一个或多个事件位在事件组中被置位。
事件组还可用于同步任务,创建通常称为任务 “会合” 的任务。任务同步点是应用程序代码中的一个位置,在这个位置上,任务将处于阻塞状态 (不消耗任何 CPU 时间) 等待,直到参与同步的所有其他任务也到达它们的同步点。
需要 #include "event_groups.h"
功能 | API 接口 | 实际执行函数 | 其它 |
---|---|---|---|
事件组创建(动态) | xEventGroupCreate() | ||
事件组响应等待 | xEventGroupWaitBits() | ||
事件组位清除 | xEventGroupClearBits() | ||
事件组位清除(用于中断中) | xEventGroupClearBitsFromISR() | xTimerPendFunctionCallFromISR() | 实际执行函数由宏决定 |
事件组置位 | xEventGroupSetBits() | ||
事件组置位(用于中断中) | xEventGroupSetBitsFromISR() | xTimerPendFunctionCallFromISR() | 实际执行函数由宏决定 |
事件组位获取 | xEventGroupGetBits() | xEventGroupClearBits() | |
事件组位获取(用于中断中) | xEventGroupGetBitsFromISR() | ||
事件组释放删除 | vEventGroupDelete() | ||
事件组创建(静态) | xEventGroupCreateStatic() |
1、xEventGroupCreate () API 函数
EventGroupHandle_t xEventGroupCreate( void ); |
返回参数(此返回值应当保存下来,以作为操作此队列的句柄):
- NULL:表示事件组创建失败。原因是内存堆空间不足导致 FreeRTOS 无法为互斥量分配结构数据空间。
- 非 NULL:值表示事件组创建成功。返回值应当保存起来作为该事件组的句柄。
2、xEventGroupWaitBits () API 函数
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, | |
const EventBits_t uxBitsToWaitFor, | |
const BaseType_t xClearOnExit, | |
const BaseType_t xWaitForAllBits, | |
const TickType_t xTicksToWait ); |
传入参数:
- xEventGroup:事件组的目标句柄。这个句柄即是调用
xEventGroupCreate()
创建该事件组时的返回值。 - uxBitsToWaitFor:一个按位的值,表示在事件组中可设置的事件位(或标志)。
- xClearOnExit:如果设置为
pdTRUE
,那么如果满足了等待条件 (如果函数返回的原因不是超时),则在事件组中设置的uxBitsToWaitFor
中的任何位都将在xEventGroupWaitBits()
返回之前被清除;否则,如果设置为pdFALSE
,那么当调用xEventGroupWaitBits()
返回时,事件组中设置的位不会改变。 - xWaitForAllBits:如果设置为
pdTRUE
,那么当uxBitsToWaitFor
中的所有位被设置或者指定的块时间过期时,xEventGroupWaitBits()
将返回;否则,如果设置为pdFALSE
,那么当uxBitsToWaitFor
中设置的任何一个位或者指定的块时间过期时,xEventGroupWaitBits()
将返回。 - xTicksToWait:阻塞超时时间。
返回参数:
- 事件组中事件位的设置情况。如果
xEventGroupWaitBits()
因为超时而返回,则不会设置所有正在等待的事件位;在xClearOnExit
参数被设置为pdTRUE
的情况下,如果xEventGroupWaitBits()
是因为它正在等待的位被设置而返回,那么返回的值就是在任何位被自动清除之前的事件组值。
3、xEventGroupClearBits () API 函数
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, | |
const EventBits_t uxBitsToClear ); |
传入参数:
- xEventGroup:要清除其中位的事件组。
- uxBitsToClear :位值。表示在事件组中要清除的事件位(或标志)。
返回参数:
- 清除指定位之前的事件组的值
4、xEventGroupClearBitsFromISR () API 函数
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, | |
const EventBits_t uxBitsToClear ); |
传入参数:
- xEventGroup:要清除其中位的事件组。
- uxBitsToClear :位值。表示在事件组中要清除的事件位(或标志)。
返回参数(有两个可能的返回值):
- pdPASS:成功发送请求。
- pdFALSE:发送请求失败。计时器服务队列已满。
5、xEventGroupSetBits () API 函数
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, | |
const EventBits_t uxBitsToSet ); |
传入参数:
- xEventGroup:要在其中位设置的事件组。
- uxBitsToSet :一个按位的值,表示要设置的事件位(或标志)。
返回参数:
- 事件组在调用
xEventGroupSetBits()
时的值。
注意:用户通过参数 uxBitsToSet
设置的标志位并不一定会保留到此函数的返回值中,返回的值可能清除有以下两种情况:
a. 调用此函数的过程中,其它高优先级的任务就绪了,并且也修改了事件标志,此函数返回的事件标志位会发生变化。
b. 调用此函数的任务是一个低优先级任务,通过此函数设置了事件标志后,让一个等待此事件标志的高优先级任务就绪了,会立即切换到高优先级任务去执行,相应的事件标志位会被函数 xEventGroupWaitBits
清除掉,等从高优先级任务返回到低优先级任务后,函数 xEventGroupSetBits
的返回值已经被修改。
6、xEventGroupSetBitsFromISR () API 函数
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, | |
const EventBits_t uxBitsToSet, | |
BaseType_t *pxHigherPriorityTaskWoken ); |
传入参数:
- xEventGroup:要在其中位设置的事件组。
- uxBitsToSet :一个按位的值,表示要设置的事件位(或标志)。
- pxHigherPriorityTaskWoken:用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是
pdTRUE
,说明有高优先级任务要执行,否则没有;必须将xHigherPriorityTaskWoken
初始化为pdFALSE
。
返回参数(有两个可能的返回值):
- pdPASS:成功发送请求。
- pdFALSE:发送请求失败。计时器服务队列已满。
7、xEventGroupGetBits () API 函数
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup ); |
传入参数:
- xEventGroup:需要查询的事件组。
返回参数:
- 调用
xEventGroupGetBits()
时的事件组位。
8、xEventGroupGetBitsFromISR () API 函数
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); |
传入参数:
- xEventGroup:需要查询的事件组。
返回参数:
- 调用
xEventGroupGetBitsFromISR()
时的事件组位。
9、xEventGroupDelete () API 函数
void xEventGroupDelete( EventGroupHandle_t xEventGroup ); |
传入参数:
- xEventGroup:需要删除的事件组。
10、xEventGroupCreateStatic () API 函数
EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t *pxEventGroupBuffer ); |
该函数是用于在静态的时候,利用该函数创建一个事件组,具体可以去看他的注释,这里就不说了。
# 实例代码
相关的实例代码及文档,参看保存在官方文件路径 FreeRTOS/Demo/Common/Minimal
下的 EventGroupsDemo.c
文件