STM32-FreeRTOS操作系统-事件

引言

本文深入探讨STM32与FreeRTOS结合时的事件机制,从事件的创建、触发到处理,剖析其在复杂任务调度中的关键作用,助力开发者更好地掌握这一核心技术,提升系统运行效率与稳定性。

事件的定义

在 FreeRTOS 中,事件组中的每个事件位通常是一个二进制位,可以表示一个特定的事件。事件位通常通过宏定义来表示,以便于代码的可读性和维护性。

通俗的讲,事件的核心就是设置事件标志位,然后读取该标志位状态,进而执行要执行的逻辑。类似按键检测一样,不断检测按键有没有被按下,如果被按下则执行按下逻辑。

事件

步骤跟前面的差不多,这里就不再一一介绍,感兴趣的可以看看我前面的文章。

首先是定义事件的句柄,并给其赋初值为NULL。

EventGroupHandle_t Event_Handle =NULL; //事件句柄赋初值

接着在临界区创建事件组:

/* 创建 Event_Handle */

Event_Handle = xEventGroupCreate();

xEventGroupCreate()

该函数原型为:

EventGroupHandle_t xEventGroupCreate(void);

可以看出,该函数是一个无参数有返回值的函数。该函数的主要作用分配内存并初始化一个新的事件组,如果创建成功则返回一个非空事件组的句柄;如果创建失败则返回NULL,这通常是由于内存空间分配不足导致。

xEventGroupSetBits()

xEventGroupSetBits()是 FreeRTOS 中的一个函数,用于向事件组中设置位。

EventBits_t xEventGroupSetBits(

EventGroupHandle_t xEventGroup,

const EventBits_t xEventGroup

);

可以看出,该函数是含参且有返回值的函数,参数xEventGroup:填入事件组的句柄;参数xEventGroup:要设置的位掩码。返回值:返回事件组当前设置的位掩码。

xEventGroupWaitBits()

xEventGroupWaitBits 是 FreeRTOS 中用于等待事件组中指定位的函数。它允许任务阻塞等待事件组中的一个或多个位被设置。该函数的原型为:

EventBits_t xEventGroupWaitBits(

EventGroupHandle_t xEventGroup,

const EventBits_t uxBitsToWaitFor,

const BaseType_t xClearOnExit,

const BaseType_t xWaitForAllBits,

TickType_t xTicksToWait

);

第一个参数xEventGroup:填入事件组的句柄;

第二个参数uxBitsToWaitFor:要等待的掩码;

第三个参数xClearOnExit:当任务退出等待时,是否删除事件组中已设置的位

pdTRUE :如果任务等待的位被设置,则在任务退出等待时清除这些位。

pdFALSE :不自动清除事件组中的位。

第四个参数xWaitForAllBits:指定任务是等待所有指定的位都被设置,还是等待任意一个指定的位被设置。

pdTRUE :任务会等待所有指定的位都被设置。

pdFALSE :任务会等待任意一个指定的位被设置。

第五个参数xTicksToWait:任务等待事件组中位被设置的最大时间。如果在指定时间内没有位被设置,则任务会超时退出等待。

返回值:返回每一位掩码,包括被设置和未被设置的掩码

示例代码

cs 复制代码
#include "myfreertos.h"
#include "FreeRTOS.h"
#include "event_groups.h"
#include "semphr.h"
#include "queue.h"
#include "Usart.h"
#include "Task.h"
#include "led.h"
#include "key.h"


TaskHandle_t MyTaskHandler;//任务句柄

TaskHandle_t LED_TASK_Handler;//LED任务句柄

TaskHandle_t KEY_TASK_Handler;//LED任务句柄

void MyTask(void *pvParameters);    //声明启动函数

void LED_TASK(void *pvParameters);   //声明LED任务函数

void KEY_TASK(void *pvParameters);   //声明KEY任务函数

EventGroupHandle_t Event_Handle =NULL; //事件句柄赋初值

#define KEY_EVENT1  (0x01 << 0)//设置事件掩码的位0
#define KEY_EVENT2  (0x01 << 1)//设置事件掩码的位1

void Start_Task(void)
{
	xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//动态方法创建任务
	vTaskStartScheduler();//启动任务调动		
}

void MyTask(void *pvParameters)           //开始创建任务函数
{	
	taskENTER_CRITICAL();           //进入临界区
	
	/* 创建 Event_Handle */
	Event_Handle = xEventGroupCreate();	
	
	xTaskCreate(LED_TASK,"LED_TASK",50,NULL,3,&LED_TASK_Handler);//动态方法创建LED任务
	
	xTaskCreate(KEY_TASK,"KEY_TASK",50,NULL,4,&KEY_TASK_Handler);//动态方法创建LED任务

    vTaskDelete(MyTaskHandler);    //删除开始任务
	
	taskEXIT_CRITICAL();           //退出临界区
}

void LED_TASK(void *pvParameters)
{
			EventBits_t R_EVENT;  /* 定义一个事件接收变量 */
			while(1)
			{
					R_EVENT = xEventGroupWaitBits(Event_Handle,KEY_EVENT1|KEY_EVENT2,pdTRUE,pdFALSE,portMAX_DELAY);
					if( ((R_EVENT & KEY_EVENT1) == KEY_EVENT1) || ((R_EVENT & KEY_EVENT2) == KEY_EVENT2))
					{
							LED = !LED;
					}
					vTaskDelay(200);
			}
	
}

void KEY_TASK(void *pvParameters)
{
	while(1)
	{
			if( GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) != 0 )
			{
						xEventGroupSetBits(Event_Handle,KEY_EVENT1);
			}
			
//			if( GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6) != 0 )
//			{
//						xEventGroupSetBits(Event_Handle,KEY_EVENT2);
//			}
		  vTaskDelay(200);
	}
	
}

示例代码讲解

首先时事件掩码:

#define KEY_EVENT1 (0x01 << 0)//设置事件掩码的位0

#define KEY_EVENT2 (0x01 << 1)//设置事件掩码的位1

该掩码的大小由configEVENT_GROUP_MAX_BITS宏定义决定,通常是32位。对于上面这两个掩码来说,对于第一个KEY_EVENT1(0x01 << 0)来说,就是把1向左移0位,结果就是将32位掩码中的第0位设置为1。对于第二个KEY_EVENT2 (0x01 << 1)来说,就是把1向左移1位,结果就是将32位掩码中的第1位设置为1。

其次是:

xEventGroupSetBits(Event_Handle,KEY_EVENT1);

我的思路是如果按键PA8按下,则设置该掩码KEY_EVENT1。当然,下面的则设置该掩码KEY_EVENT2。

然后是:

R_EVENT = xEventGroupWaitBits(Event_Handle,KEY_EVENT1|KEY_EVENT2,pdTRUE,pdFALSE,portMAX_DELAY);//等待key1或key2

if( ((R_EVENT & KEY_EVENT1) == KEY_EVENT1) || ((R_EVENT & KEY_EVENT2) == KEY_EVENT2))

{

LED = !LED;

}

这个KEY_EVENT1|KEY_EVENT2的作用是等待KEY_EVENT1或KEY_EVENT2中的任意一个位被设置。

这个pdTRUE的作用是当任务退出等待时,清除事件组中已设置的位。就是说每次执行完这个函数后,事件组已设置都会被清除;如果把pdTRUE改成pdFALSE ,那么该已设置的位将会保持设置状态。如果想删除它,就要调用EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear);该函数是一个含参有返回值的函数,参数xEventGroup:填入事件组的句柄;参数uxBitsToClear:要删除的掩码。返回值返回的是当前事件组中设置的位掩码。如果把全部掩码都删除,那就返回0。

这个pdFALSE的作用是等待设置的位掩码的任意一位被设置,该事件就被触发,然后该函数就会返回对应的位掩码。如果将pdFALSE改为pdTRUE ,则需要对应的掩码被全部设置,该事件才会被触发

最后是这个:

(R_EVENT & KEY_EVENT1) == KEY_EVENT1)

R_EVENT 是等待事件函数返回的位掩码,然后与KEY_EVENT1掩码相与,作用是判断对应的事件是否被触发。

总结

本文仅是个人观点,不代表最终解释,如有不足,欢迎指出。

相关推荐
GUET_一路向前2 小时前
STM32_I2C Timing参数计算方法(I2C speed:120k/240k/400k)
stm32·单片机·嵌入式硬件·iic
TDengine (老段)3 小时前
TDengine IDMP 地图展示数据功能快速上手
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
加成BUFF3 小时前
树莓派5控制WS2812 RGB灯带:原理、接线与实战指南
python·嵌入式硬件·远程连接·ws2812·树莓派5
从零点3 小时前
STM32学习笔记CAN
笔记·stm32·学习
yumgpkpm3 小时前
Cloudera CDP7、CDH5、CDH6 在华为鲲鹏 ARM 麒麟KylinOS做到无缝切换平缓迁移过程
大数据·arm开发·华为·flink·spark·kafka·cloudera
清风6666663 小时前
基于单片机的架空线路接地故障检测与报警系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
雪兽软件3 小时前
什么是物联网?它是如何运作的?
物联网
恶魔泡泡糖3 小时前
keil4创建工程项目
c语言·单片机
ACP广源盛139246256733 小时前
GSV1011@ACP#1011产品规格详解及产品应用分享
嵌入式硬件·计算机外设·音视频