在介绍二进制信号量时曾经讲过,二进制信号量可以代替我们裸机开发中的标志位来使用。在裸机开发中我们使用标志位来表示某个事件是否发生,并且其他程序利用标记位的状态来判断程序是否可以继续进行。但是这种大量使用标记位的情况会导致代码的逻辑异常的复杂。
虽然使用二进制信号量可以很好的实现标志位的实现以及相应的任务安排,但是二进制信号量并不适用于大量的标志位。因为一个二进制信号量只能表示一个事件,假如我们的程序有大量的事件那有没有办法不用二进制信号量可以很好的管理这些事件呢?
这就是本期介绍的内容:事件位和事件组
在FreeRTOS中我们把一个用作判断事件是否发生的情况作为事件位,用0或1表示。它可以用来表示一个事件是否发生。比如函数Test是否被调用,用户是否按下按键等等事件。
这些由多个事件位构成的集合则被称为事件组。事件组中不同的位编号代表不同的事件位。例如位编号0(0x00)用来代表函数Test是否被调用,事件组位编号1(0x01)代表用户是否按下按钮……
FreeRTOS 通过相对应的宏定义来确定事件组的长度(包含多少事件位)例如config_16_BIT_TICKS为0的话则代表一个事件组可以包含24位事件位。
使用事件组
和之前的其他内容一样,使用事件组的准备也要包含相关头文件、定义相对应的宏定义、调用创建函数。
事件组的创建函数非常简单,只需要定义一个事件组的句柄来接收事件组创建函数的返回值即可。事件组的长度前面说过利用宏定义来确定时间组长度。
等待事件组的函数和信号量相似,设置我们需要等待事件发生的位并且设置超时时间。但是注意的是我们选择需要等待的位不能设置为0
第三个参数用来设置等到事件位触发后是否清空数据位。
其中的第四个参数xWaitForAllBits则是用来 确定位之间的与或关系 ,例如如果我的位设置是0x03即第0和第1位,如果xWaitForAllBits是pdTRUE则需要第0和第1位同时为1,如果是pdFalse则第0和第1只要有一个事件触发就执行。
该函数用来设置事件位,需要注意的是该函数的返回值是事件组各位的值,但是如果有更高优先级的任务在等待则会立刻清空事件位,所以会导致事件位返回时被清空。
测试实验
首先我们创建一个事件组,宏定义中定义这个事件组总共有24个事件位。
EventHandler = xEventGroupCreate();
接着我们创建一个按键检测任务,当按钮一按下时,将事件组第0位置1,当按钮二按下时,将事件组第1位置1;
void Low_Task(void * pvParameters)//参数为 void * pvParameters
{
while(1)
{
if(KEY_Scan(0)==1)
{
xEventGroupSetBits(EventHandler,1< < 0);//1< < 0是0x01
}
if(KEY_Scan(0)==2)
{
xEventGroupSetBits(EventHandler,1< < 1);//1< < 1是0x02
}
vTaskDelay(10);
}
}
最后我们设置一个轮询事件组的语句,并且设置第四个参数为pdFlase,当有一个按键按下时,LED翻转。
void Mid_Task(void * pvParameters)//参数为 void * pvParameters
{
BaseType_t err;
while(1)
{
err = xEventGroupWaitBits(
(const EventGroupHandle_t) EventHandler,
(const EventBits_t) 0x03,
(const BaseType_t) pdTRUE,
(const BaseType_t) pdFALSE,
(TickType_t) portMAX_DELAY );
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
vTaskDelay(10);
}
}
接着我们把pdFlase改成pdTRUE,当两个按钮(事件)都触发过的时候,才能执行任务。
void Mid_Task(void * pvParameters)//参数为 void * pvParameters
{
BaseType_t err;
while(1)
{
err = xEventGroupWaitBits(
(const EventGroupHandle_t) EventHandler,
(const EventBits_t) 0x03,
(const BaseType_t) pdTRUE,
(const BaseType_t) pdTRUE,
(TickType_t) portMAX_DELAY );
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
vTaskDelay(10);
}
}
其中xEventGroupWaitBits返回值会根据不同的情况而改变。
如果因为事件组全部触发而完成的函数的话,err返回函数清空前的事件组的值。
如果因为超时而导致阻塞结束的话,err返回的则是当前事件组的值。