FreeRTOS从入门到精通 第十五章(事件标志组)
参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili
一、事件标志组简介
1、概述
(1)事件标志位是一个“位”,用来表示事件是否发生。
(2)事件标志组是一组事件标志位的集合,可以简单的理解事件标志组,是一个整数。
(3)事件标志组的特点:
①每一个位与一个事件相关联,高8位除外,高8位用作存储事件标志组的控制信息。(下图所示的是32 位长度的事件标志组)
②每一位事件的含义,以及高电平和低电平分别代表什么,由用户自己决定。
③任意任务或中断都可以读写这些位。
④可以等待某一位成立,或者等待多位同时成立。
(4)一个事件组就包含了一个EventBits_t数据类型的变量,变量类型EventBits_t的定义如下所示,它实际上是一个16位或32位无符号的数据类型。
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
(5)事件标志组与队列、信号量的区别:
功能 | 唤醒对象 | 事件清除 |
队列、信号量 | 事件发生时,只会唤醒一个任务 | 是消耗型的资源,队列的数据被读走就没了;信号量被获取后就减少了 |
事件标志组 | 事件发生时,会唤醒所有符合条件的任务,可以理解为“广播”的作用 | 被唤醒的任务有两个选择,可以让事件保留不动,也可以清除事件 |
2、事件标志组相关API函数介绍
(1)事件标志组相关API函数概览:
函数 | 描述 |
xEventGroupCreate() | 使用动态方式创建事件标志组 |
xEventGroupCreateStatic() | 使用静态方式创建事件标志组 |
xEventGroupClearBits() | 清零事件标志位 |
xEventGroupClearBitsFromISR() | 在中断中清零事件标志位 |
xEventGroupSetBits() | 设置事件标志位 |
xEventGroupSetBitsFromISR() | 在中断中设置事件标志位 |
xEventGroupWaitBits() | 等待事件标志位 |
xEventGroupSync() | 设置事件标志位,并等待另一个事件标志位【A事件完成,同时还要等待B事件发生】 |
(2)xEventGroupCreate函数:
①函数定义:
EventGroupHandle_t xEventGroupCreate
(
void
)
②返回值:
返回值 | 描述 |
NULL | 事件标志组创建失败 |
其它值 | 事件标志组创建成功,返回其句柄 |
(3)xEventGroupClearBits函数:
①函数定义:
EventBits_t xEventGroupClearBits
(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear
)
②函数参数:
形参 | 描述 |
xEventGroup | 待操作的事件标志组句柄 |
uxBitsToSet | 待清零的事件标志位 |
③返回值:
返回值 | 描述 |
整数 | 清零事件标志位之前事件组中事件标志位的值 |
(4)xEventGroupSetBits函数:
①函数定义:
EventBits_t xEventGroupSetBits
(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear
)
②函数参数:
形参 | 描述 |
xEventGroup | 待操作的事件标志组句柄 |
uxBitsToSet | 待设置的事件标志位 |
③返回值:
返回值 | 描述 |
整数 | 函数返回时,事件组中的事件标志位值 |
(5)xEventGroupWaitBits函数:
①函数定义:
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 | 等待的阻塞时间 |
③返回值:
返回值 | 描述 |
等待的事件标志位值 | 等待事件标志位成功,返回等待到的事件标志位 |
其它值 | 等待事件标志位失败,返回事件组中的事件标志位 |
(6)xEventGroupSync函数:
①函数定义:
EventBits_t xEventGroupSync
(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait
)
②函数参数:
形参 | 描述 |
xEventGroup | 等待事件标志所在事件组 |
uxBitsToSet | 达到同步点后,要设置的事件标志 |
uxBitsToWaitFor | 等待的事件标志 |
xTicksToWait | 等待的阻塞时间 |
③返回值:
返回值 | 描述 |
等待的事件标志位值 | 等待事件标志位成功,返回等待到的事件标志位 |
其它值 | 等待事件标志位失败,返回事件组中的事件标志位 |
二、事件标志组实验
1、原理图与实验目标
(1)原理图(串口外设的接法与列表项的插入和删除实验相同,下图未示出):
(2)实验目标:
①设计3个任务——start_task、task1、task2:
[1]start_task:用于创建其它三个任务,并创建事件标志组。
[2]task1:读取按键按下键值,根据不同键值将事件标志组相应事件位置1,模拟事件发生(按下某个按键,对应的标志位置1)。
[3]task2:同时等待事件标志组中的多个事件位,当这些事件位都置1的话就执行相应的处理(串口打印信息),同时清除标志位。
②预期实验现象:
[1]程序下载到板子上后,暂时没有任何现象。
[2]按下相关按键,串口会输出相应的信息。
2、实验步骤
(1)将“队列集操作实验”的工程文件夹复制一份,在拷贝版中进行实验。
(2)更改FreeRTOS_experiment.c文件的内容,如下所示。
#include "FreeRTOS.h"
#include "task.h"
#include "LED.h"
#include "Key.h"
#include "Serial.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
//宏定义
#define START_TASK_STACK_SIZE 128 //start_task任务的堆栈大小
#define START_TASK_PRIO 1 //start_task任务的优先级
#define TASK1_STACK_SIZE 128 //task1任务的堆栈大小
#define TASK1_PRIO 2 //task1任务的优先级
#define TASK2_STACK_SIZE 128 //task2任务的堆栈大小
#define TASK2_PRIO 3 //task2任务的优先级
#define EVENTBIT_0 (1 << 0) //用该宏时表示希望事件标志组的bit0位置为1
#define EVENTBIT_1 (1 << 1) //用该宏时表示希望事件标志组的bit1位置为1
EventGroupHandle_t eventgroup_handle; //定义事件标志组句柄
//任务函数声明
void start_task(void);
void task1(void);
void task2(void);
//任务句柄
TaskHandle_t start_task_handler; //start_task任务的句柄
TaskHandle_t task1_handler; //task1任务的句柄
TaskHandle_t task2_handler; //task2任务的句柄
QueueSetHandle_t queueset_handle;
void FreeRTOS_Test(void)
{
//创建任务start_task
xTaskCreate((TaskFunction_t)start_task, //指向任务函数的指针
"start_task", //任务名字
START_TASK_STACK_SIZE, //任务堆栈大小,单位为字
NULL, //传递给任务函数的参数
START_TASK_PRIO, //任务优先级
(TaskHandle_t *) &start_task_handler //任务句柄,就是任务的任务控制块
);
//开启任务调度器
vTaskStartScheduler();
}
void start_task(void)
{
taskENTER_CRITICAL();
eventgroup_handle = xEventGroupCreate(); //创建事件标志组
if(eventgroup_handle != NULL)
{
Serial_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)
{
uint8_t key = 0;
while(1)
{
key = Key_GetNum();
if(key == 1)
{
//将事件标志组的bit0位置1
xEventGroupSetBits(eventgroup_handle, EVENTBIT_0);
}
else if(key == 2)
{
//将事件标志组的bit1位置1
xEventGroupSetBits(eventgroup_handle, EVENTBIT_1);
}
vTaskDelay(10);
}
}
void task2(void)
{
EventBits_t event_bit = 0;
while(1)
{
event_bit = xEventGroupWaitBits(eventgroup_handle, //事件标志组句柄
EVENTBIT_0 | EVENTBIT_1, //等待事件标志组的bit0和bit1位均置1
pdTRUE, //等待到事件标志位后,清除事件标志组的bit0和bit1位
pdTRUE, //等待事件标志组的bit0和bit1位都置1,就成立
portMAX_DELAY ); //死等
Serial_Printf("等待到的事件标志位值为:%#x\r\n",event_bit);
}
}
(3)程序完善好后点击“编译”,然后将程序下载到开发板上,打开串口助手分析信息。
3、程序执行流程
(1)main函数全流程:
①初始化串口模块。
②调用FreeRTOS_Test函数。
(2)测试函数全流程:
①创建任务start_task。
②开启任务调度器。
(3)多任务调度执行阶段较为简单,这里不再赘述。