FreeRTOS——事件标志组
一、简介
1)介绍
事件标志位:使用单个二进制位(0 或 1),来表示某个特定事件是否发生。
事件标志组:是一组事件标志位的集合, 可以简单的理解事件标志组,就是一个整数。
事件标志组的特点:
- 它的每一个位表示一个事件(高8位不算)
- 每一位事件的含义,由用户自己决定,如:bit0表示按键是否按下,bit1表示是否接受到消息
- 这些位的值为1:表示事件发生了;值为0:表示事件未发生
-
- 任意任务或中断都可以读写这些位
- 可以等待某一位成立,或者等待多位同时成立
typedef TickType_t EventBits_t;#if ( configUSE_16_BIT_TICKS == 1 )typedef uint16_t TickType_t;#define portMAX_DELAY ( TickType_t ) 0xffff
#elsetypedef uint32_t TickType_t;#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
可以看出 EventBits_t 本质上是 TickType_t,而 TickType_t 数据类型又是由 configUSE_16_BIT_TICKS 这个宏决定的,如果该宏置 1,则为 uint16_t 类型,否则为 uint32_t 类型。(configUSE_16_BIT_TICKS 默认为 0)
3)事件标志组结构说明
以无符号整形(uint32_t)为例,该类型有32位,但是并不是32位都用作于标志位,只有低24位用做存储事件标志位,高8位用于存储事件标志组的控制信息,所以说一个事件组最多可以存储 24 个事件标志!
示意图如下:
4)事件标志组与队列、信号量的对比
功能 | 唤醒对象 | 时间清楚 |
队列、信号量 | 事件发生时,只会唤醒一个任务 | 是消耗型的资源,队列的数据被读走就没了;信号量被获取后就减少了 |
事件标志组 | 事件发生时,会唤醒所有符合条件的任务,可以理解为“广播”的作用 | 被唤醒的任务有两个选择,可以让事件保留不动,也可以清除事件 |
二、API函数
函数 | 描述 |
xEventGroupCreate() | 使用动态方式创建事件标志组 |
xEventGroupCreateStatic() | 使用静态方式创建事件标志组 |
xEventGroupClearBits() | 清零事件标志位 |
xEventGroupClearBitsFromISR() | 在中断中清零事件标志位 |
xEventGroupSetBits() | 设置事件标志位 |
xEventGroupSetBitsFromISR() | 在中断中设置事件标志位 |
xEventGroupWaitBits() | 等待事件标志位 |
xEventGroupSync() | 设置事件标志位,并等待事件标志位 |
1. 动态创建事件标志组函数
/* 动态创建事件标志组函数原型 */
EventGroupHandle_t xEventGroupCreate( void )
该函数没有入口参数,使用该函数需要将宏 configSUPPORT_DYNAMIC_ALLOCATION 置 1。
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 事件标志组创建成功,返回其句柄 |
2. 清除事件标志位函数
/* 函数原型 */
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToClear )
参数说明:
- xEventGroup:待操作的事件标志组句柄
- uxBitsToClear :待清除的事件标志位
返回值 | 描述 |
整数 | 清除事件标志位前,事件标志位中的值 |
3. 设置事件标志位函数
/* 函数原型 */
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet )
参数说明:
- xEventGroup:待操作的事件标志组句柄
- uxBitsToSet :待设置的事件标志位(置 1)
返回值 | 描述 |
整数 | 事件组中的事件标志位值 |
4. 等待事件标志位函数
/* 函数原型 */
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait )
用于等待某些事件发生后,再继续执行。例如:按下按键0(将bit0 置 1),再发送消息(获取到了 bit0 置 1)。
参数说明:
- xEventGroup:等待的事件标志组句柄
- uxBitsToWaitFor:等待的事件标志位,可以用逻辑或(&)等待多个事件
- xClearOnExit:成功等待到事件标志位后,是否将对应的事件标志位清零
-
pdTRUE :清除uxBitsToWaitFor指定位
-
pdFALSE:不清除
-
-
xWaitForAllBits:等待 uxBitsToWaitFor 中的某个时间,或者全部事件同时满足
-
pdTRUE:等待的位,全部置 1
-
pdFALSE:等待的位,一个置 1
-
-
xTicksToWait :等待的阻塞时间
返回值 | 描述 |
等待到的事件标志位 | 等待事件标志位成功,返回等待到的事件标志位 |
其他值 | 等待事件标志位失败,返回事件组中的事件标志位 |
5. 通过事件标志位进行同步函数
/* 函数原型 */
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait )
用于任务同步,例如:
task1:做饭
task2:做菜
在 task1 做好饭之后需要等待 task2 也做好菜,然后一起吃饭。
参数说明:
- xEventGroup:等待事件标志所在的事件标志组句柄
- uxBitsToSet:达到同步点后,要设置的事件标志位
- uxBitsToWaitFor:等待的事件标志位
- xTicksToWait :等待的阻塞时间
返回值 | 描述 |
等待到的事件标志位 | 等待事件标志位成功,返回等待到的事件标志位 |
其他值 | 等待事件标志位失败,返回事件组中的事件标志位 |
三、代码示例
实验描述:task1通过按键0和按键1分别设置事件标志组的bit0位和bit1位,task2 去等待bit0位和bit1位,两个事件同时发生时task2继续执行,并打印标志位的值,task3和task2一样的作用,用于判断是否为广播式通知
main.c 文件
#include "malloc.h"
#include "freertos_demo.h"
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "usmart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */key_init(); /* 初始化按键 */my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */freertos_demo(); /* 创建起始任务 */
}
freertos_demo.c 文件,主要功能实现
#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
/*FreeRTOS******************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
#include "queue.h"
/***************************************************************************************//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK_STACK_SIZE 256
#define TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_STACK_SIZE 256
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_STACK_SIZE 256
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);/* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK3_STACK_SIZE 256
#define TASK3_PRIORITY 4
TaskHandle_t task3_handle;
void task3(void *pvParameters);/***************************************************************************************/
#define EVENTBIT0 1 << 0 /* bit0位置1 */
#define EVENTBIT1 1 << 1 /* bit1位置1 */
EventGroupHandle_t event_bit; /* 事件标志组句柄 */void freertos_demo(void)
{/* 创建起始任务 */xTaskCreate((TaskFunction_t )start_task, /*函数指针,函数入口*/(char * )"start_task", /*任务名字*/(configSTACK_DEPTH_TYPE)TASK_STACK_SIZE, /*任务堆栈的大小*/(void * )NULL, /*任务参数*/(UBaseType_t )TASK_PRIORITY, /*任务优先级*/(TaskHandle_t * )&start_task_handle /*任务句柄*/);vTaskStartScheduler();
}/* 起始任务:创建其他任务和事件标志组 */
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); /* 进入临界区 */event_bit = xEventGroupCreate(); /* 创建事件标志组 */if(event_bit == NULL) printf("事件标志组创建失败\r\n");else printf("事件标志组创建成功\r\n");/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(char * )"task1",(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK1_PRIORITY,(TaskHandle_t * )&task1_handle);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(char * )"task2",(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK2_PRIORITY,(TaskHandle_t * )&task2_handle);/* 创建任务3 */xTaskCreate((TaskFunction_t )task3,(char * )"task3",(configSTACK_DEPTH_TYPE)TASK3_STACK_SIZE,(void * )NULL,(UBaseType_t )TASK3_PRIORITY,(TaskHandle_t * )&task3_handle);taskEXIT_CRITICAL(); /* 退出临界区 */vTaskDelete(NULL); /* 删除当前任务 */
}/* 任务1:通过按键去设置相对于的事件标志位 */
void task1(void *pvParameters)
{uint8_t key = 0;while(1){key = key_scan(0);if(key == KEY0_PRES){xEventGroupSetBits( event_bit,EVENTBIT0); /* 设置bit0位置1 */}else if(key == KEY1_PRES){xEventGroupSetBits( event_bit,EVENTBIT1); /* 设置bit1位置1 */}vTaskDelay(10);}
}EventBits_t print_bit = 0;
void task2(void *pvParameters)
{while(1){print_bit = xEventGroupWaitBits(event_bit, /* 等待的事件标志组 */EVENTBIT0 | EVENTBIT1, /* 等待事件标志组的bit0和bit1位 */ pdTRUE, /* 成功等待后清理标志位 */pdFALSE, /* 等待所有的标志位 */portMAX_DELAY); /* 死等 */printf("task2: %#x\r\n",print_bit);}
}void task3(void *pvParameters)
{while(1){print_bit = xEventGroupWaitBits(event_bit, /* 等待的事件标志组 */EVENTBIT0 | EVENTBIT1, /* 等待事件标志组的bit0和bit1位 */ pdTRUE, /* 成功等待后清理标志位 */pdFALSE, /* 等待所有的标志位 */portMAX_DELAY); /* 死等 */printf("task3: %#x\r\n",print_bit);}
}