freertos中的事件主要是用来把若干个任务关联起来的一种机制,就好比我创建了3个任务。当我3个任务都发生了之后,那么事件就起效果了,就可以执行事件函数里的代码逻辑。若这三个任务当中有一个任务没有发生,那么就进入不了事件函数,大概就是这么一个逻辑。.
#include "event_groups.h"
EventGroupHandle_t Event_Handle; //事件句柄
Event_Handle = xEventGroupCreate(); //事件创建函数
void vEventGroupDelete(Event_Handle); //事件删除函数
首先定义事件的头文件和句柄,然后调用xEventGroupCreate()函数创建事件。删除事件时调用vEventGroupDelete()函数 ,把事件句柄传入即可。
#define KEY1_EVENT (0x01 << 0) //设置事件掩码的位0
#define KEY2_EVENT (0x01 << 1) //设置事件掩码的位1
#define KEY3_EVENT (0x01 << 2) //设置事件掩码的位2
xEventGroupSetBits(Event_Handle,KEY1_EVENT); //事件1标志位置位
xEventGroupSetBits(Event_Handle,KEY2_EVENT); //事件2标志位置位
xEventGroupSetBits(Event_Handle,KEY3_EVENT); //事件3标志位置位
xEventGroupClearBits(KEY1_EVENT); //清除事件1标志位
xEventGroupClearBits(KEY2_EVENT); //清除事件2标志位
xEventGroupClearBits(KEY3_EVENT); //清除事件3标志位
要进行事件标志位置位和清除之前要先定义事件标志位。在freertos中,事件的标志位常用的有24个位,也就是说我们至少可以定义24个事件标志位 。
我这边宏定义了3个事件标志位,调用xEventGroupSetBits()函数给标志位置位。第一个参数是事件句柄,第二个参数是定义的标志位。其次,调用xEventGroupClearBits()函数可以清除事件标志位,传入事件标志位即可。但是,这两个函数不能在中断里使用,中断里有自己的置位和清位函数。
xEventGroupSetBitsFromISR(Event_Handle,KEY1_EVENT,pdTRUE);
xEventGroupClearBitsFromISR(Event_Handle,KEY1_EVENT);
这是中断里的置位和清除函数,置位函数有3个参数,第一个是事件的句柄,第二个是事件的标志位,第3个可以给pdTRUE或者pdFALSE。清除函数的参数有两个,第一个参数给事件句柄,第二个参数给事件的标志位。
EventBits_t r_event; // 定义一个事件接收变量
r_event = xEventGroupWaitBits(Event_Handle, // 事件对象句柄
KEY1_EVENT|KEY2_EVENT|KEY3_EVENT, // 接收线程感兴趣的事件
pdTRUE, // 退出时清除事件位
pdTRUE, // 满足感兴趣的所有事件
portMAX_DELAY); // 指定超时事件,一直等
等待事件函数是事件的核心,它的作用是等待事件标志位的发生,当事件标志位全部置位或者部分置位时(这个根据自己的情况来设置),可以通过判断来执行自己编写的事件逻辑。
vEventGroupWaitBits()函数有5个参数,第一个是事件句柄,第二个是判断的事件标志位,我这里直接判断3个事件标志位。第三个是是否进行标志位清除,我这里选择pdTRUE,代表当等待事件函数满足时,把所有的事件标志位都清除了。当然也可以选pdFALSE,这个不会清除事件标志位,如果自己想手动清除标志位,可以调用xEventGroupClearBits()函数清除。
第四个参数默认选pdTRUE就好了,第五个参数是阻塞时间,我给的是portMAX_DELAY,这个是freertos的一个宏定义,代表一个最大时间,就是一直阻塞在这里等待事件标志位的置位。
本次实验将在stm32c8t6单片机上设置两个按键key1和key2,按下key1时置位事件1标志位,按下key2时置位事件2标志位。除此之外,还将设置一个函数不断置位事件3。
void startTask(void *arg)
{
BaseType_t xReturn = pdFALSE; //创建接收值
taskENTER_CRITICAL(); //临界区
Event_Handle = xEventGroupCreate();
xReturn = xTaskCreate(ledTask,"ledTask",64,NULL,1,&ledTask_handler); //创建led任务
xReturn = xTaskCreate(eventTask,"eventTask",64,NULL,3,&eventTask_handler); //创建事件任务
xReturn = xTaskCreate(event3Task,"event3Task",64,NULL,4,&event3Task_handler); //创建事件3任务
xReturn = xTaskCreate(keyTask,"keyTask",64,NULL,3,&keyTask_handler); //创建按键任务
if(xReturn == pdTRUE)
{
printf("Task create ok\n");
}
else
{
printf("Task create error\n");
}
vTaskDelete(startTask_handler); //删除开始任务
taskEXIT_CRITICAL(); //临界区
}
这是开始任务函数,它的作用是用来创建其他任务的。
void ledTask(void *arg)
{
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
}
}
led任务比较简单,就是充当一个心跳包的功能,让它不停闪烁。我们可以通过判断led的状态来看我们的程序是否正常运行。
void event3Task(void *arg)
{
while(1)
{
xEventGroupSetBits(Event_Handle,KEY3_EVENT);
printf("event3 ok\n");
vTaskDelay(2000);
}
}
事件任务3就是一直置位事件3标志位就好了。每两秒置位一次。
void keyTask(void *arg)
{
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == RESET)
{
xEventGroupSetBits(Event_Handle,KEY1_EVENT);
printf("event1 ok\n");
}
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) == RESET)
{
xEventGroupSetBits(Event_Handle,KEY2_EVENT);
printf("event2 ok\n");
}
vTaskDelay(200);
}
}
按键任务的逻辑也很简单,当我两个按键按下后,分别置位两个同的事件标志位。
void eventTask(void *arg)
{
EventBits_t r_event; // 定义一个事件接收变量
while(1)
{
r_event = xEventGroupWaitBits(Event_Handle, // 事件对象句柄
KEY1_EVENT|KEY2_EVENT|KEY3_EVENT, // 接收线程感兴趣的事件
pdTRUE, // 退出时清除事件位
pdTRUE, // 满足感兴趣的所有事件
portMAX_DELAY); // 指定超时事件,一直等
vTaskDelay(100);
if((r_event & (KEY1_EVENT|KEY2_EVENT|KEY3_EVENT)) == (KEY1_EVENT|KEY2_EVENT|KEY3_EVENT))
{
printf("OK\n");
}
}
}
首先定义一个事件接收变量r_event,然后调用事件等待函数xEventGroupWaitBits()。然后用if语句做一个判断,判断成功则打印OK。
首先按下复位,串口打印Task create ok,说明所有任务都创建成功了。然后事件3置位了两次,串口便打印了两次event3 ok。接着按下key1,串口打印了event1 ok,说明事件1置位成功。接着再按下key2,串口打印event2 ok,说明事件2置位成功。再加上事件3是每隔两秒置位一次,所以串口就直接打印了OK,说明事件都被置位了。随后先按下key2,再按下key1,串口也打印了OK。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "queue.h"
#include "led.h"
#include "stdlib.h"
#include "semphr.h"
#include "event_groups.h"
TaskHandle_t startTask_handler; //总任务的句柄
TaskHandle_t ledTask_handler; //led的句柄
TaskHandle_t eventTask_handler; //事件任务的句柄
TaskHandle_t keyTask_handler; //按键任务的句柄
TaskHandle_t event3Task_handler; //事件3任务的句柄
EventGroupHandle_t Event_Handle; //事件句柄
#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位0
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位1
#define KEY3_EVENT (0x01 << 2)//设置事件掩码的位2
void ledTask(void *arg)
{
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
vTaskDelay(500);
}
}
void eventTask(void *arg)
{
EventBits_t r_event; // 定义一个事件接收变量
while(1)
{
r_event = xEventGroupWaitBits(Event_Handle, // 事件对象句柄
KEY1_EVENT|KEY2_EVENT|KEY3_EVENT, // 接收线程感兴趣的事件
pdTRUE, // 退出时清除事件位
pdTRUE, // 满足感兴趣的所有事件
portMAX_DELAY); // 指定超时事件,一直等
vTaskDelay(100);
if((r_event & (KEY1_EVENT|KEY2_EVENT|KEY3_EVENT)) == (KEY1_EVENT|KEY2_EVENT|KEY3_EVENT))
{
printf("OK\n");
}
}
}
void event3Task(void *arg)
{
while(1)
{
xEventGroupSetBits(Event_Handle,KEY3_EVENT);
printf("event3 ok\n");
vTaskDelay(2000);
}
}
void keyTask(void *arg)
{
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == RESET)
{
xEventGroupSetBits(Event_Handle,KEY1_EVENT);
printf("event1 ok\n");
}
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) == RESET)
{
xEventGroupSetBits(Event_Handle,KEY2_EVENT);
printf("event2 ok\n");
}
vTaskDelay(200);
}
}
void startTask(void *arg)
{
BaseType_t xReturn = pdFALSE; //创建接收值
taskENTER_CRITICAL(); //临界区
Event_Handle = xEventGroupCreate();
xReturn = xTaskCreate(ledTask,"ledTask",64,NULL,1,&ledTask_handler); //创建led任务
xReturn = xTaskCreate(eventTask,"eventTask",64,NULL,3,&eventTask_handler); //创建事件任务
xReturn = xTaskCreate(event3Task,"event3Task",64,NULL,4,&event3Task_handler); //创建事件3任务
xReturn = xTaskCreate(keyTask,"keyTask",64,NULL,3,&keyTask_handler); //创建按键任务
if(xReturn == pdTRUE)
{
printf("Task create ok\n");
}
else
{
printf("Task create error\n");
}
vTaskDelete(startTask_handler); //删除开始任务
taskEXIT_CRITICAL(); //临界区
}
int main(void)
{
LED_Init();
usrt1_init(9600);
xTaskCreate(startTask,"startTask",512,NULL,1,&startTask_handler); //创建开始任务
vTaskStartScheduler();
}