一、简介
事件标志组与信号量一样属于任务间同步的机制,但是信号量一般用于任务间的单事件同
步,对于任务间的多事件同步,仅使用信号量就显得力不从心了。FreeRTOS 提供的事件标志组
可以很好的处理多事件情况下的任务同步。
1. 事件标志
事件标志是一个用于指示事件是否发生的布尔值,一个事件标志只有 0 或 1 两种状态,
FreeRTOS 将多个事件标志储存在一个变量类型为 EventBits_t 变量中,这个变量就是事件组。
2. 事件组
事件组是一组事件标志的集合,一个事件组就包含了一个 EventBites_t 数据类型的变量,
变量类型 EventBits_t 的定义如下所示:
typedef TickType_t EventBits_t;
#if ( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#else
typedef uint32_t TickType_t;
#endif
#define configUSE_16_BIT_TICKS 0
从上面可以看出,EventBits_t 实际上是一个 16 位或 32 位无符号的数据类型。当
configUSE_16_BIT_TICKS 配置为 0 时,EventBits_t 是一个 32 位无符号的数据类型;当
configUSE_16_BIT_TICKS 配置为 1 时,EventBits_t 是一个 16 为无符号的数据类型。在本套教程的所有配套例程中,都将配置项 configUSE_16_BIT_TICKS 配置为 0,因此本文就以
EventBits_t 为 32 位无符号数据类型为例进行讲解,对于另外一种情况,也是大同小异的。
虽然说使用了 32 位无符号的数据类型变量来存储事件标志,但这并不意味着,一个
EventBits_t 数据类型的变量能够存储 32 个事件标志,FreeRTOS 将这个 EventBits_t 数据类型的变量拆分成两部分,其中低 24 位[23:0](configUSE_16_BIT_TICKS 配置位 1 时,是低 8 位[7:0])用于存储事件标志,而高 8 位[31:24](configUSE_16_BIT_TICKS 配置位 1 时,依然是高 8 位[15:8])用作存储事件标志组的一些控制信息,也就是说一个事件组最多可以存储 24 个事件标志。EventBits_t 数据类型变量的位使用情况如下图所示:
从上图中可以看到,变量中低 24 位中的每一位都是一个事件标志,当某一位被置一时,就
表示这一位对应的事件发生了。
特点
3.事件标志组与队列、信号量的区别
二、相关 API 函数
1. 创建事件标志组
FreeRTOS 提供了两种创建事件标志组的方式,分别为动态方式创建事件标志组和静态方式创建事件标志组,两者的区别在于静态方式创建事件标志组时,需要用户提供创建事件标志组所需的内存空间,而使用动态方式创建事件标志组时,FreeRTOS 会自动从 FreeRTOS 管理的
堆中分配创建事件标志组所需的内存空间。
动态方式创建事件标志组 API 函数的函数原型如下所示:
EventGroupHandle_t xEventGroupCreate(void);
静态方式创建事件标志组 API 函数的函数原型如下所示:
EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t * pxEventGroupBuffer);
2. 删除事件标志组
FreeRTOS 提供了用于删除事件标志组的 API 函数,函数原型如下所示:
void vEventGroupDelete(EventGroupHandle_t xEventGroup);
3. 等待事件标志位
等待事件标志位使用的是函数 xEventGroupWaitBits(),其函数原型如下所示:
EventBits_t xEventGroupWaitBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait)
4. 设置事件标志位
FreeRTOS 提供了两个用于设置事件标志位的 API 函数,这个两个函数分别用于在任务和
在中断中设置事件标志位。
在任务中设置事件标志位 API 函数的函数原型如下所示:
EventBits_t xEventGroupSetBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet)
在中断中设置事件标志位 API 函数的函数原型如下所示:
BaseType_t xEventGroupSetBitsFromISR(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t * pxHigherPriorityTaskWoken)
5. 清零事件标志位
FreeRTOS 提供了两个用于清零事件标志位的 API 函数,这个两个函数分别用于在任务和
在中断中清零事件标志位。
在任务中清零事件标志位 API 函数的函数原型如下所示:
EventBits_t xEventGroupClearBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear)
6. 获取事件组中事件标志位的值
FreeRTOS 提供了两个用于获取事件组中事件标志位值的 API 函数,这个两个函数分别用
于在任务和在中断中获取事件组中事件标志位的值。
在任务中获取事件组中事件标志位值 API 函数的函数原型如下所示:
EventBits_t xEventGroupGetBits(xEventGroup);
7. 函数 xEventGroupSync()
此函数一般用于多任务同步,其中每个任务都必须等待其他任务达到同步点,然后才能继续执行。函数 xEventGroupSync()的函数原型如下所示:
EventBits_t xEventGroupSync(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait)
三、相关实验
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );
/******************************************************************************************************/
EventGroupHandle_t eventgroup_handle;
#define EVENTBIT_0 (1 << 0)
#define EVENTBIT_1 (1 << 1)
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
eventgroup_handle = xEventGroupCreate();
if(eventgroup_handle != NULL)
{
printf("事件标志组创建成功!!\r\n");
}
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
xEventGroupSetBits( eventgroup_handle, EVENTBIT_0); /* 将事件标志组的bit0位置1 */
}else if(key == KEY1_PRES)
{
xEventGroupSetBits( eventgroup_handle, EVENTBIT_1); /* 将事件标志组的bit1位置1 */
}
vTaskDelay(10);
}
}
/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
EventBits_t event_bit = 0;
while(1)
{
event_bit = xEventGroupWaitBits( eventgroup_handle, /* 事件标志组句柄 */
EVENTBIT_0 | EVENTBIT_1, /* 等待事件标志组的bit0和bit1位 */
pdTRUE, /* 成功等待到事件标志位后,清除事件标志组中的bit0和bit1位 */
pdTRUE, /* 等待事件标志组的bit0和bit1位都置1,就成立 */
portMAX_DELAY ); /* 死等 */
printf("等待到的事件标志位值为:%#x\r\n",event_bit);
}
}