十七、FreeRTOS之FreeRTOS事件标志组
本节需要掌握以下内容:
1,事件标志组简介(了解)
2,事件标志组相关API函数介绍(熟悉)
3,事件标志组实验(掌握)
4,课堂总结(掌握)
一、时间标志组简介(了解)
时间标志位:用一个位,来表示事件是否发生,通常我们会定义一个标志位uint8_t flag = 0,bit0 = 1,表示按键按下了,bit0表示事件未发生。
事件标志组是一组事件标志位的集合,(一个位是一个事件标志位,把么8个位就组成了一个事件标志组,)那么可以简单的理解事件标志组,就是一个整数。
事件标志组的特点:
- 它的每一位表示一个事件(高8位不算)
- 每一位事件的含义。由用户自己决定,如:bit0表示按键是否按下,bit1表示是否接受到消息.......这些位的值为1:表示事件发生了;值为0:表示事件未发生
- 任意任务或者中断都可以读写这些位
- 可以等待某一位成立,或者等待多位同时成立
1.1 事件标志组简介
一个事件组就包含了一个EventBits_t数据类型的变量,变量类型EventBits_t的定义如下所示:
EventBits_t实际上是一个16位或32位的无符号的数据类型
虽然使用了32位无符号的数据类型变量来存储事件标志,但其中的高8位用作存储事件标志组的控制信息,低24位用作存储事件标志,所以说一个事件组最多可以存储24个事件标志!
1.2 事件标志组与队列、信号量的区别?
功能 | 唤醒对象 | 事件清除 |
队列、信号量 | 事件发生时,只会唤醒一个任务 | 是消耗型的资源,队列的数据被读走就 没了;信号量被获取后就减少了 |
事件标志组 | 事件发生时,会唤醒所有符合条件的任务,可以理解为“广播”的作用 | 被唤醒的任务有两个选择,可以让事件 保留不动,也可以清除事件 |
二、事件标志组相关API函数介绍(熟悉)
函数 | 描述 |
xEventGroupCreate() | 使用动态方式创建事件标志组 |
xEventGroupCreateStatic() | 使用静态方式创建事件标志组 |
xEventGroupClearBits() | 清零事件标志位 |
xEventGroupClearBitsFromISR() | 在中断中清零事件标志位 |
xEventGroupSetBits() | 设置事件标志位 |
xEventGroupSetBitsFromISR() | 在中断中设置事件标志位 |
xEventGroupWaitBits() | 等待事件标志位 |
xEventGroupSync() | 设置事件标志位,并等待事件标志位 |
此处只列举了常用的一部分,更多事件标志组相关的API函数介绍请查阅《FreeRTOS开发指南》-- 第十六章“FreeRTOS事件标志组”
2.1 动态方式创建事件标志组API函数
EventGroupHandle_t xEventGroupCreate ( void ) ;
返回值 | 描述 |
NULL | 事件标志组创建失败 |
其他值 | 事件标志组创建成功,返回其句柄 |
2.2 清除事件标志位API函数
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
形参 | 描述 |
xEventGroup | 待操作的事件标志组句柄 |
uxBitsToSet | 待清零的事件标志位 |
返回值 | 描述 |
整数 | 清零事件标志位之前事件组中事件标志位的值 |
2.3 设置事件标志位API函数
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )
形参 | 描述 |
xEventGroup | 待操作的事件标志组句柄 |
uxBitsToSet | 待设置的事件标志位 |
返回值 | 描述 |
整数 | 函数返回时,事件组中的事件标志位值 |
2.4 等待事件标志位API函数
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
形参 | 描述 |
xEvenrGroup | 等待的事件标志组句柄 |
uxBitsToWaitFor | 等待的事件标志位,可以用逻辑或等待多个事件标志位 |
xClearOnExit | 成功等待到事件标志位后,清除事件组中对应的事件标志位, pdTRUE :清除uxBitsToWaitFor指定位; pdFALSE:不清除 |
xWaitForAllBits | 等待 uxBitsToWaitFor 中的所有事件标志位(逻辑与) pdTRUE:等待的位,全部为1 pdFALSE:等待的位,某个为1 |
xTicksToWait | 等待的阻塞时间 |
拿一个8位的变量来举例,假设设置bit0和bit1都成立,这时候第二个参数就可以或起来,写成0x02|0x01,再多几个也可以这样或起来。
返回值 | 描述 |
等待的事件标志位值 | 等待事件标志位成功,返回等待到的事件标志位 |
其他值 | 等待事件标志位失败,返回事件组中的事件标志位 |
特点:可以等待某一位、也可以等待多位,等到期望的事件后,还可以清除某些位
2.5 同步函数
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait )
形参 | 描述 |
xEventGroup | 等待事件标志所在事件组 |
uxBitsToSet | 达到同步点后,要设置的事件标志 |
uxBitsToWaitFor | 等待的事件标志 |
xTicksToWait | 等待的阻塞时间 |
返回值 | 描述 |
等待的事件标志位值 | 等待事件标志位成功,返回等待到的事件标志位 |
其他值 | 等待事件标志位失败,返回事件组中的事件标志位 |
举个例子:
Task1:做饭 Task2:做菜
Task1做好自己的事之后,需要等待菜也做好,大家在一起吃饭。
特点:同步!
三、事件标志组实验(掌握)
3.1、实验目的:
学习 FreeRTOS 的事件标志组相关API的使用。
3.2、实验设计:
将设计三个任务:start_task、task1、task2
三个任务的功能如下:
- start_task:用来创建task1和task2任务,并创建事件标志组
- task1:读取按键按下键值,根据不同的键值将事件标志组相应事件位置1,模拟事件发生
- task2:同时等待事件标志组中的多个事件位,当这些事件位都置1的话就执行相应的处理
4.3 实验代码
demo.c
/******************************************************************************************************/
/*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) /* 0x01 */
#define EVENTBIT_1 (1 << 1) /* 0x10 */
/**
* @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(); /* 退出临界区 */
}
/* 任务一,事件标志组相应事件位置1 */
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);
}
}
把程序下进去之后,复位
首先打印的是:事件标志组创建成功!
为什么不打印等待事件的标志位值呢?因为此时BIT0 与 BIT1都不是1,Task2处于阻塞状态,
按下KEY0也不会打印,KEY0与KEY1都按了才会打印:等待事件的标志位值为0x03(0x01|0x02)
当事件等待函数xEventGroupWaitBits()执行完毕之后,BIT0 与 BIT1都会被清除,所以打印一次之后,下次进来之后,就又进入阻塞状态了
如果把第三个参数改为pdFALSE,那就是不清零,就一直打印一直打印。
如果把第四个参数改成pdFLASE,那么BIT0 与 BIT1只有有一个为1,就会打印对应的键值。
四、总结