FreeRTOS——事件组
一、事件组概念
FreeRTOS事件组(EventGroups)是FreeRTOS中的一种机制,用于在多任务环境下实现任务之间的事件通知和同步,允许任务等待多个事件中的一个或多个事件发生,或者等待一组特定的事件全部发生。
我们可以把它看作是一个32位的整数,整数的每一位代表一个事件,每一个事件的含义由开发者自定义,例如位0是点灯,位1是按下按键,位2是串口打印等等。当某个事件触发时,在事件组中对应的位会被置1,如果没触发则对应的位为0。
提示:允许一个或多个任务去进行读写,也允许ISR进行读写。
事件组用一个整数来表示,其中的高8位留给内核使用,只能用其他的位来表示事件:
如果configUSE_16_BIT_TICKS是1,那么这个整数就是16位的,低8位用来表示事件。
如果configUSE_16_BIT_TICKS是0,那么这个整数就是32位的,低24位用来表示事件。
使用事件组时我们需要思考两个问题:
1.唤醒谁?
队列、信号量:当事件发生时,只会唤醒一个任务。
事件组:当事件发生时,会唤醒所有符合条件的任务。
2.是否清除事件?
队列、信号量:它们是消耗型的资源,队列的数据被读走就没了;信号量被获取后就减少了。
事件组:被唤醒的任务有两个选择,可以让事件保留不动,也可以清除事件。
二、常用的API使用
1.创建事件组
动态创建
代码如下(示例):
EventGroupHandle_t xEventGroupCreate( void ); //返回一个事件组的句柄,用于后续对事件组的操作
静态创建
代码如下(示例):
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );
参数:一个StaticEventGroup_t结构体,并传入它的指针’
返回值:若成功返回句柄,失败返回pdNULL
2.删除函数
代码如下(示例):
void vEventGroupDelete( EventGroupHandle_t xEventGroup ); //参数是需要删除的事件组的句柄,NULL是删除自己
3.设置事件
在任务中使用:
代码如下(示例):
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
参数:
1.xEventGroup:事件组句柄
2.uxBitsToSet :写入十六进制数,换算成二进制数后来确定哪些位为1,从而设置哪些位触发
在中断ISR中使用:
代码如下(示例):
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t* pxHigherPriorityTaskWoken );
参数:
1.xEventGroup:事件组句柄
2.uxBitsToSet :写入十六进制数,换算成二进制数后来确定哪些位为1,从而设置哪些位触发
3.pxHigherPriorityTaskWoken :有没有导致更高优先级的任务进入就绪态? pdTRUE-有, pdFALSE-没有
4.等待事件
可以等待某一位、某些位中的任意一个,也可以等待多位;等到期望的事件后,还可以清除某些位。
代码如下(示例):
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );
参数:
1.xEventGroup:事件组句柄
2.uxBitsToWaitFor:等待哪些位?
3.xClearOnExit:触发后是否清除事件位? ( pdTRUE:清除uxBitsToWaitFor指定的位 pdFALSE:不清除)
4.xWaitForAllBits:确定触发方式(pdTRUE:全部设置的位都为1才触发 pdFALSE:某一个设置的位为1就触发)
5.xTicksToWait :超时时间(阻塞时间)
返回值:
返回事件值,如果满足触发则返回触发时的事件值,如果是超时退出的返回退出时的事件值。
5.同步点
这种机制非常适合于多个任务需要在某些操作开始之前相互同步的场景。
代码如下(示例):
EventBits_t xEventGroupSync( ventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait);
这个函数允许多个任务同时等待一个事件组中的一个或多个特定标志位被设置。当所有等待的任务都调用了xEventGroupSync,且指定的标志位被设置后,这些任务将被同时释放
参数:
1.xEventGroup: 事件组句柄
2.uxBitsToSet:设置当前的事件组值
3.uxBitsToWaitFor:满足触发条件的事件组值
4.xTicksToWait:超时时间
返回值:
返回事件值,如果满足触发则返回触发时的事件值,如果是超时退出的返回退出时的事件值。
当调用xEventGroupSync( )时,任务会设置 uxBitsToSet 指定的位,然后等待直到 uxBitsToWaitFor 指定的所有位都被设置(由这个任务或其它任务)。如果在指定的 xTicksToWait 时间内所有这些位都被设置,xEventGroupSync返回当前事件组的状态;如果超时,则返回超时时间到的事件组值。
三、实例
实例背景:
任务A是“客人”,任务B是“服务员”,任务C是“厨师”,主要逻辑是厨房的厨师需要等待客人点完菜并且服务员把单子送到厨房才能开始炒菜,而服务员需要等待客人点完菜才能把单子送到厨房。
代码如下(示例):
#define BIT_ORDER_PLACED (1 << 0) // 客人点完菜(0x01)
#define BIT_ORDER_TAKEN_TO_KITCHEN (1 << 1) // 服务员把单子送到厨房(0x02)
int main(void) {
// 事件组句柄
EventGroupHandle_t xRestaurantEventGroup;
// 创建事件组
xEventGroup = xEventGroupCreate();
// 创建任务
xTaskCreate(vTaskCustomer, "TaskCustomer", 1000, NULL, 1, NULL);
xTaskCreate(vTaskWaiter, "TaskWaiter", 1000, NULL, 2, NULL);
xTaskCreate(vTaskChef, "TaskChef", 1000, NULL, 3, NULL);
// 启动调度器
vTaskStartScheduler();
for (;;);
return 0;
}
void vTaskChef(void *pvParameters) {
// 厨师等待订单被送到厨房
xEventGroupWaitBits(xEventGroup ,BIT_ORDER_PLACED | BIT_ORDER_TAKEN_TO_KITCHEN , pdTRUE, pdTRUE, portMAX_DELAY);
// 开始烹饪
printf("Chef is cooking the food...\n");
vTaskDelay(pdMS_TO_TICKS(2000)); // 假设烹饪需要一些时间
}
void vTaskWaiter(void *pvParameters) {
// 服务员等待客人点完菜
xEventGroupWaitBits(xEventGroup , BIT_ORDER_PLACED , pdFALSE, pdTRUE, portMAX_DELAY); //这里等待触发后不可以清除!!
// 服务员把订单送到厨房
printf("Waiter is taking the order to the kitchen...\n");
vTaskDelay(pdMS_TO_TICKS(500)); // 假设送单子到厨房需要一些时间
// 设置订单已送到厨房的事件标志位
xEventGroupSetBits(xEventGroup , BIT_ORDER_TAKEN_TO_KITCHEN);
}
void vTaskCustomer(void *pvParameters) {
// 客人点菜
printf("Customer is ordering food...\n");
vTaskDelay(pdMS_TO_TICKS(1000)); // 假设点菜花费一些时间
// 点菜完成,设置事件标志位
xEventGroupSetBits(xEventGroup , BIT_ORDER_PLACED);
// 客人等待其他任务完成
vTaskDelay(portMAX_DELAY);
}
总结
本文记录了FreeRTOS中事件组的相关内容。