stm32c8t6之freertos事件

发布时间:2024年01月06日

? ? ? ? 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();																//临界区
	
}

? ? ? ? ?这是开始任务函数,它的作用是用来创建其他任务的。

led任务

void ledTask(void *arg)
{
	while(1)
	{
		GPIO_SetBits(GPIOB,GPIO_Pin_8);
		vTaskDelay(500);
		GPIO_ResetBits(GPIOB,GPIO_Pin_8);
		vTaskDelay(500);
	}
}

? ? ? ? led任务比较简单,就是充当一个心跳包的功能,让它不停闪烁。我们可以通过判断led的状态来看我们的程序是否正常运行。

事件3任务

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();
}

?

?

?

?

文章来源:https://blog.csdn.net/w1256141689/article/details/135408744
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。