FreeRTOS-使用 event groups
使用 event group
需要在工程中添加源文件event_groups.c
,并引用头文件#include "event_groups.h"
。
1 使用event group 在任务间同步事件
使用 event group
需要先创建:
1 | EventGroupHandle_t xEventGroupCreate( void ); |
创建成功时,该API 会返回一个用来标识该 event group
的句柄。
当某个事件发生时,需要设置event group
中表示该事件的对应位:
1 | EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); |
- xEventGroup:xEventGroupCreate 的返回值,用来标识某个
event group
的句柄 - uxBitsToSet:设置
event group
中的某些位,开发者定义每个位表示的对应事件。 32位处理器中,event group
只有低24位可用,高8位是一些控制标记,event group
内部使用的。
当任务需要等待某个事件发生时,就是等待 event group
中表示该事件的位被置位:
1 | EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup, |
- xEventGroup:xEventGroupCreate 的返回值,用来标识某个
event group
的句柄 - uxBitsToWaitFor:等待
event group
中的某些位被置位,开发者定义每个位表示的对应事件。 - xClearOnExit:pdTRUE或pdFALSE。该值用来设置,当任务所等待的条件满足后(期望的事件发生了),该函数返回时,是否自动清除
event group
中 uxBitsToWaitFor 所表示的那些位。设置为pdTRUE,表示自动清除,当任务进入阻塞态,等待一些事件发生(等待event group
中的某些位被置位),当事件发生后,任务恢复就绪态,同时自动清零event group
中事件所对应的位(类似于一些硬件中断发生后,需要主动清除硬件中断标记,避免重复触发的场景) - xWaitAllBits:设置为pdTRUE,表示要所有事件均发生(uxBitsToWaitFor中设置的那些位所表示的事件)。设置为pdFALSE,表示任意一个事件发生,即满足条件。
- 返回值:返回
event group
的值,当我们是等待多个事件中的任意一个发生时,就可以根据该值来判断是哪个事件发生了(参考后文在中断和任务间同步事件的代码)。注意,返值不受参数xClearOnExit 的影响,如果xClearOnExit=pdTRUE,返回的值是自动清除操作执行前的值。
为了演示,使用event group
在任务间同步事件,我们创建 3 个任务,task_a
、task_b
和task_c
。task_b
和task_c
每次执行工作前都需要先等待“事件”(task_a
做完一些工作),“事件”未发生时,task_b
和task_c
就会阻塞。只有当task_a
做完一些工作,设置“事件”,通知task_b
和task_c
可以运行了。task_b
和task_c
才能继续运行。
其逻辑如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14TASK_A:
while 1:
do something // 执行工作
set event // 工作完成了,设置事件,通知 b,c 开始工作
TASK_B:
while 1:
wait event // 等待任务A工作完成 并设置事件
do something
TASK_C:
while 1:
wait event // 等待任务A工作完成 并设置事件
do something
任务代码如下: task_b和task_c 可以共用一个任务函数,这里为了直观,每个任务单独写了一个。
1 | // 使用第 0 位来表示事件:task_a工作执行完 |
ps:代码中,task_b
和task_c
调用xEventGroupWaitBits 等待事件时,均设置了自动清除 xClearOnExit=pdTRUE。这不会有影响(即task_b
的清除不会导致task_c
判断不到事件发生了),因为 event group
是遍历所有等待事件的任务,依次检查每个任务是否由于发生了某个事件,使得等待条件满足了,满足了就会恢复就绪态。所有任务遍历完后才会清除相应位。
main 函数实现如下:
1 | // 使用event group 在任务间同步事件 |
运行结果如下:task_b、task_c每次等待task_a运行后才能运行。 并且每次task_a运行完,设置事件后,task_b、task_c都能收到事件,恢复就绪态并运行。这也体现了 event group
的广播特性(区别于信号量每次只会让优先级最高的那个任务获恢复就绪)。
1 | start FreeRTOS |
2 使用event group 在中断和任务间同步事件
任务等待某个硬件相关的中断事件,是非常常见的应用场景。如任务等待按键事件,等待串口接收数据事件等。这里我们使用 event group
来替代二值信号量,在中断服务函数和任务间同步事件。
我们创建一个任务,该任务等待按键事件和串口接收数据事件,任意一个事件发生都可以唤醒任务。因此,我们需要在按键中断处理函数/串口中断处理函数中调用设置事件API,即xEventGroupSetBits
的中断版本(带FromISR后缀):
1 | BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, |
- xEventGroup:创建api: xEventGroupCreate 返回的,用来标识某个
event group
的句柄 - uxBitsToSet:设置
event group
中的某些位,开发者定义每个位表示的对应事件。 32位处理器中,event group
只有低24位可用,高8位是一些控制标记,模块内部使用的。 - pxHigherPriorityTaskWoken:该值的作用见后文代码中的注释。
程序逻辑如下图所示:
注意:在文章FreeRTOS-event groups 实现原理的最后,我们提到过,当我们在中断服务函数中调用xEventGroupSetBitsFromISR
来设置发生了某个事件时,实际的工作(设置event group相应位,遍历所有等待事件的任务查看是否有任务的等待条件满足了)会通过延后执行机制推迟到退出中断以后再执行。而内核提供的延后执行机制依赖软件定时器服务任务以及一些配置宏。所以,为了使用 API:xEventGroupSetBitsFromISR
,需要在工程配置文件FreeRTOSConfig.h
中添加如下配置:
1 |
具体的任务和中断服务代码如下:
1 |
|
main函数代码:
1 | // 使用event group 在中断服务函数和任务间同步事件 |
输出如下所示:
1 | start FreeRTOS |
ps:需要注意文章代码中的日志输出函数,产品代码中如果需要使用的话,需要考虑线程安全性(多任务安全性),因为中断/任务切换可能发生在另一个任务正在输出日志但还未输出完的时候,这就可能造成日志错乱
FreeRTOS交流QQ群-663806972