文章目录
- 目的
- 基础说明
- 相关函数
- 使用演示
- 总结
目的
事件组是RTOS中相对常用的用于任务间交互的功能,这篇文章将对相关内容做个介绍。
本文代码测试环境见前面的文章:《FreeRTOS入门(01):基础说明与使用演示》
基础说明
事件组(EventGroup)和单片机硬件的事件或者中断有点像,每个事件组对象就是一个变量(寄存器)。如果 configUSE_16_BIT_TICKS
值为 0
这就是一个32位的变量,为 1
就是一个16位的变量。
事件组对象的高8位供FreeRTOS内部使用,剩余位供用户使用。每一个位(bit)表示一个事件发送,通常该位值为 0
,表示未发生事件;值为 1
表示发生了事件。
事件组事件在设置的时候可以设置某一位,也可以同时设置某些位。等待事件的任务可以等待某一位有效,也可以等到某些位同时有效。事件被设置后会唤醒所有符合条件的任务(可以用作广播功能)。被唤醒的事件可以选择是否清除事件标志位。
相关函数
// 创建并返回事件组句柄
EventGroupHandle_t xEventGroupCreate( void )
// 删除事件组
// 在被删除的事件组上阻塞的任务将被取消阻塞,并且报告事件组值为0
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
// 获取事件组中某个或某些位,如果不可用则阻塞
// uxBitsToWaitFor表示要等待的标志位(可以设置多个位)
// xClearOnExit为pdTRUE时会在事件发生后清除所等待的标志位,为pdFALSE则不清除
// xWaitForAllBits为pdTRUE时需要等所有等待的标志位都设置后才会返回,为pdFALSE时任意一位设置均会返回
EventBits_t xEventGroupWaitBits( const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
// 设置标志位
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
// 清除标志位
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
// 事件组的设置与清除在中断中使用的版本,在中断中设置与清除事件组不会立即进行,而是会延时到后面的FreeRTOS守护进程中执行,所以可能需要设置下面才可使用
// INCLUDE_xEventGroupSetBitFromISR, configUSE_TIMERS, INCLUDE_xTimerPendFunctionCall, configUSE_TRACE_FACILITY
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
// 获取当前事件组状态
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup )
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
// 同步设置(写和等待合并的功能)
// uxBitsToSet为要写的位,uxBitsToWaitFor为要等待的位
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait );
使用演示
下面是个基础使用的演示:
#include "debug.h"
#include "FreeRTOS.h" // 引入头文件
#include "task.h" // 引入头文件
#include "event_groups.h" // 引入头文件
EventGroupHandle_t xEventGroup; // 事件组句柄
void task1(void *pvParameters) {
while(1) {
xEventGroupSetBits(xEventGroup, 0b0011); // bit1 bit0 写1
vTaskDelete(NULL);
}
}
void task2(void *pvParameters) {
while(1) {
vTaskDelay(500);
xEventGroupSetBits(xEventGroup, 0b0100); // bit2 写1
vTaskDelete(NULL);
}
}
void task3(void *pvParameters) {
while(1) {
// 当 bit0 为1时触发,响应后不清除标志位
EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, 0b0001, pdFALSE, pdFALSE, portMAX_DELAY);
printf("t3-%d\r\n", uxBits); // 打印事件触发时的标志位
vTaskDelay(300);
}
}
void task4(void *pvParameters) {
while(1) {
// 当 bit3 bit2 均为0时触发,响应后清除标志位
EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, 0b0110, pdTRUE, pdTRUE, portMAX_DELAY);
printf("t4-%d\r\n", uxBits); // 打印事件触发时的标志位
printf("t4-%d\r\n", xEventGroupGetBits(xEventGroup)); // 打印当前标志位
xEventGroupClearBits(xEventGroup, 0x0001); // 清除标志位
printf("t4-%d\r\n", xEventGroupGetBits(xEventGroup)); // 打印当前标志位
}
}
int main(void) {
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
xEventGroup = xEventGroupCreate(); //
xTaskCreate(task1, "task1", 256, NULL, 5, NULL);
xTaskCreate(task2, "task2", 256, NULL, 5, NULL);
xTaskCreate(task3, "task3", 256, NULL, 5, NULL);
xTaskCreate(task4, "task4", 256, NULL, 5, NULL);
vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环
while(1) {} // 程序不会运行到这里
}
// 时间 任务 xEventGroup
// tick 0000 0b0000
// tick 0000 task1 0b0011
// tick 0001 task3 0b0011 // 打印输出
// tick 0301 task3 0b0011 // 打印输出
// tick 0500 task2 0b0111
// tick 0501 task4 0b0111 // 打印输出
// tick 0501 task4 0b0001 // 打印输出
// tick 0501 task4 0b0000 // 打印输出
下面是个同步方式演示:
#include "debug.h"
#include "FreeRTOS.h" // 引入头文件
#include "task.h" // 引入头文件
#include "event_groups.h" // 引入头文件
EventGroupHandle_t xEventGroup; // 事件组句柄
void task1(void *pvParameters) {
while(1) {
printf("t1s1-%d\r\n", xEventGroupGetBits(xEventGroup)); // 打印事件触发时的标志位
EventBits_t uxBits = xEventGroupSync(xEventGroup, 0b0001, 0b0011, portMAX_DELAY); // 设置0b0001,等待0b0011
printf("t1s2-%d\r\n", uxBits); // 打印事件触发时的标志位
vTaskDelete(NULL);
}
}
void task2(void *pvParameters) {
while(1) {
vTaskDelay(500);
printf("t2s1-%d\r\n", xEventGroupGetBits(xEventGroup)); // 打印事件触发时的标志位
EventBits_t uxBits = xEventGroupSync(xEventGroup, 0b0010, 0b0011, portMAX_DELAY); // 设置0b0010,等待0b0011
printf("t2s2-%d\r\n", uxBits); // 打印事件触发时的标志位
vTaskDelete(NULL);
}
}
int main(void) {
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
xEventGroup = xEventGroupCreate(); //
xTaskCreate(task1, "task1", 256, NULL, 5, NULL);
xTaskCreate(task2, "task2", 256, NULL, 5, NULL);
vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环
while(1) {} // 程序不会运行到这里
}
总结
就像本文基础说明中介绍的那样,事件组和单片机硬件的事件或者中断有点像,理解了这点的话事件组使用上其实并不复杂的。