【FreeRTOS】详细讲解FreeRTOS中事件(event)并通过具体示例讲述其用法

news2024/11/19 10:34:18

文章目录

  • 事件
  • 函数解析
  • 示例

事件

  事件,实际上是一种任务间通信的机制,主要用于实现多任务间的同步其只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。即可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理;同样,也可以是多个任务同步多个事件。
  FreeRTOS中任务可以通过设置事件位来实现事件的触发和等待操作。但FreeRTOS的事件仅用于同步,不提供数据传输功能,其具有如下特点:

  • 事件只与任务相关联事件相互独立,一个32位的事件集合(EventBits_t类型的变量,实际可用与表示事件的只有24位),用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0表示该事件类型未发生、1表示该事件类型已经发生),一共24种事件类型;
  • 事件仅用于同步不提供数据传输功能;
  • 事件无排队性,即多次向任务设置同一事件(如果任务还未来得及读走),等效于只设置一次;
  • 允许多任务对同一事件进行读写操作;
  • 支持事件等待超时机制

应用

  事件在一定程度上可以代替信号量,用于任务与任务间、中断与任务间同步
  与信号量不同之处在于,事件是不可累计的,而信号量是可累计。并且,一个任务使用事件同步时,可以等待多个事件产生才进行同步;而信号量是单一的同步操作,只能与一个中断或任务同步,无法与多个中断或任务同步。

事件运行机制示意图

  实际使用事件相关函数前,需要将configSUPPORT DYNAMIC ALLOCATION置1。


函数解析

前奏

  • 定义事件控制权柄所用的EventGroupHandle_t其实是一个结构体指针,其声明为typedef struct EventGroupDef_t * EventGroupHandle_t
  • 事件控制块EventGroup_t,内部最多含有四个变量,其中两个变量uxEventBitsxTasksWaitingForBits是一直可使用,但变量uxEventGroupNumberucStaticallyAllocated收到FreeRTOS配置管理
typedef struct EventGroupDef_t
{
    EventBits_t uxEventBits;
    List_t xTasksWaitingForBits; 
    
    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxEventGroupNumber;
    #endif

    #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) \
     && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t ucStaticallyAllocated;
    #endif
} EventGroup_t;

   EventBits_t事件标志组存储在EventBits_t 类型的变量中。
   若FreeRTOS配置时,将configUSE_16_BIT_TICKS定义为1,那么EventBits_t就为16位,其中只有8位存储时间组;否则,若configUSE_16_BIT_TICKS定义为0,那么EventBits_t就为32位,其中24位存储事件组。每一位代表一个事件是否发生(事件发生为1,否则为0)。倘若兄弟认为8位或24位事件组不够用,还可以使用与或非等逻辑运算来组成更多事件。🤣🤣🤣
   xTasksWaitingForBits:该变量是一个列表,内部存储等待此事件的所有任务
   uxEventGroupNumber:记录事件组数量。
   ucStaticallyAllocated:记录时间组分配方式,如果事件组是静态分配的,则设置为 pdTRUE,以确保不尝试释放内存。

动态创建事件

原函数

EventGroupHandle_t xEventGroupCreate( void );

参数解析

  • 返回值:类型为EventGroupHandle_t,其为事件的控制权柄。

函数说明
  动态创建一个事件。

静态创建事件

原函数

EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );

参数解析

  • StaticEventGroup_t * pxEventGroupBuffer:为事件组分配的空间;
  • 返回值:类型为EventGroupHandle_t,其为事件的控制权柄

函数说明
   静态创建事件组,创建时需要传入存储空间。

创建事件

原函数

void vEventGroupDelete( EventGroupHandle_t xEventGroup );

参数解析

  • EventGroupHandle_t xEventGroup:传入事件组控制权柄;

函数说明
 &esnp;删除事件组,只要将事件组控制权柄传入即可。删除只能删除已经创建成功的事件组

事件触发

原函数


EventBits_t xEventGroupSetBits( 
								EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToSet
                              );

参数解析

  • EventGroupHandle_t xEventGroup:事件组的控制权柄;
  • const EventBits_t uxBitsToSet:需要触发事件的,也就是其在事件组的位置;
  • 返回值:返回函数操作的事件组的值;

函数说明
  触发事件组中某个事件(事件对应事件组的位置),其位置由uxBitsToSet的值决定。
  该函数内部有判断机制,即的判断该事件是否创建,只有事件组创建成功以及操作位有效,该函数才能正常运作

中断中唤醒事件

原函数

BaseType_t xEventGroupSetBitsFromISR( 
    					EventGroupHandle_t xEventGroup,
                		const EventBits_t uxBitsToSet,
                		BaseType_t * pxHigherPriorityTaskWoken
                	);

参数解析

  • EventGroupHandle_t xEventGroup:事件组的操作句柄;
  • const EventBits_t uxBitsToSet:需要触发事件的,也就是其在事件组的位置;
  • BaseType_t * pxHigherPriorityTaskWoken:在使用之前必须初始化成pdFALSE;
  • 返回值:类型为BaseType_t。若其值为pdFalse,则消息已经发送成功;否则,发送失败。

函数说明
  是xEventGroupSetBits函数的中断版,其功能是中断中唤醒某个事件。

等待事件唤醒

原函数

EventBits_t xEventGroupWaitBits( 
								 EventGroupHandle_t xEventGroup,
                                 const EventBits_t uxBitsToWaitFor,
                                 const BaseType_t xClearOnExit,
                                 const BaseType_t xWaitForAllBits,
                                 TickType_t xTicksToWait 
                                 );

参数解析

  • EventGroupHandle_t xEventGroup:事件的操作句柄;
  • const EventBits_t uxBitsToWaitFor:等待的事件,这里可以是多个事件,使用或运算即可连接;
  • const BaseType_t xClearOnExit:当其值为pdTRUE时,一旦监测到匹配的事件,系统就会将删除由该事件引起的标志位;否则,当其值为pdFALSE时,将删除标志位;
  • const BaseType_t xWaitForAllBits:当其值为pdTURE时,相应的uxBitsToWaitFor中多个事件应该使用&连接;否则当其值为pdFALSE时,uxBitsToWaitFor中多个事件应该使用|连接。这样函数返回值才是对应标志位的值,事件才能够触发。
  • TickType_t xTicksToWait:最大超时时间,单位为系统节拍时间,使用portMAX_DELAY即相当于一直阻塞;
  • 返回值:类型为EventBits_t,返回事件中事件标志位,但是返回值很可能并不是用户指定的事件位,需要再进行判断再处理。

函数说明
  xEventGroupWaitBits函数的作用是等待事件触发。若需要等待的事件一直未触发,函数就会阻塞,阻塞时间为最大超时时间xTicksToWait

清除事件标志位

原函数

EventBits_t xEventGroupClearBits( 
									EventGroupHandle_t xEventGroup, 
									const EventBits_t uxBitsToClear
							    );

参数解析

  • EventGroupHandle_t xEventGroup:事件组操作句柄;
  • const EventBits_t uxBitsToClear:时间组中需要清除的位置;
  • 返回值:类型EventBits_t,返回事件清除前的值;

函数说明
  清除事件组中对应位置的标志位。在获取事件时对应标志位未擦除,那么可以使用该函数做显示擦除

中断中清除事件标志位

原函数

BaseType_t xEventGroupClearBitsFromISR( 
										EventGroupHandle_t xEventGroup,
										const EventBits_t uxBitsToSet 
									  );

参数解析

  • EventGroupHandle_t xEventGroup:事件组操作句柄;
  • const EventBits_t uxBitsToClear:时间组中需要清除的位置;
  • 返回值:类型EventBits_t,返回事件清除前的值;

函数说明
  是函数xEventGroupClearBits的中断版,功能上与之一样。


示例

  声明8个事件,并且创建一个FreeRTOS事件,用于任务同步,再创建两个任务。完成:任务1,等待事件触发,7个事件中触发一个事件后反转对应LED的状态,当事件8触发时,统计其触发次数并且显示到LCD屏上; 任务2,每个1s发送一个事件触发请求,并且反转LED8的状态。

核心源码

//任务控制权柄
TaskHandle_t xHandleTsak[2];
// 事件控制权柄
EventGroupHandle_t myxEventGroupHandle_t = NULL;
// 声明事件
#define EVENT1 (0x01 << 0)
#define EVENT2 (0x01 << 1)
#define EVENT3 (0x01 << 2)
#define EVENT4 (0x01 << 3)
#define EVENT5 (0x01 << 4)
#define EVENT6 (0x01 << 5)
#define EVENT7 (0x01 << 6)
#define EVENT8 (0x01 << 7)

int main(void)
{	
	//存储创建任务的返回值
	BaseType_t xReturn[2] ;
	
	// 创建事件
	myxEventGroupHandle_t = xEventGroupCreate();
	if(myxEventGroupHandle_t == 0)
		//点亮LED7
		changeLedStateByLocation(LED8,ON);
	
	//动态创建任务
	xReturn[0] = xTaskCreate(
						(TaskFunction_t )eventTask1,(const char *)"task1",
						(uint16_t)128,(void*) NULL,1,&xHandleTsak[0]);
	xReturn[1] = xTaskCreate(
						(TaskFunction_t )eventTask2,(const char *)"task2",
						(uint16_t)128,(void*) NULL,1,&xHandleTsak[1]);
						
	if (pdPASS != xReturn[0])
		//点亮LED6
		changeLedStateByLocation(LED6,ON);				
	if (pdPASS != xReturn[1])
		//点亮LED7
		changeLedStateByLocation(LED7,ON);				
	if (pdPASS == xReturn[1] == xReturn[0])
		vTaskStartScheduler();
	
	return 0;
}

/********************************************
* 函数功能:事件测试函数1
* 函数参数:无
* 函数返回值:无
********************************************/
void eventTask1(void)
{
	// 设置变量接收事件
	EventBits_t r_event;
	// 保存LED的状态
	int ledState[] = {0,0,0,0,0,0,0};
	// 记录LED的位置
	uint16_t ledLocation[] = {LED1,LED2,LED3,LED4,LED5,LED6,LED7};
	// 记录EVENT8触发次数
	uint16_t eventTriggerCount = 0;
	// 保存显示到LCD中的数据
	char temp[30];
	// 用于循环
	int i = 0;
	
	while(1)
	{
		//阻塞等待8个事件中任意一个事件
		r_event = xEventGroupWaitBits(myxEventGroupHandle_t,EVENT1|EVENT2|EVENT3|EVENT4|EVENT5|EVENT6|EVENT7|EVENT8,
									  pdTRUE,pdFALSE,portMAX_DELAY);
		//判断事件类型
		if((r_event&EVENT1) !=0)
			++ledState[0];
		else if((r_event&EVENT2) !=0)
			++ledState[1];
		else if((r_event&EVENT3) !=0)
			++ledState[2];
		else if((r_event&EVENT4) !=0)
			++ledState[3];
		else if((r_event&EVENT5) !=0)
			++ledState[4];
		else if((r_event&EVENT6) !=0)
			++ledState[5];
		else if((r_event&EVENT7) !=0)
			++ledState[6];
		else
		{ 
			sprintf(temp," Event8 count:%d",++eventTriggerCount);
			LCD_DisplayStringLine(Line4,(uint8_t*)temp);
			changeAllLedByStateNumber(OFF);
		}
		
		// 设置每个LED灯状态
		for(i=0;i<7;++i)
			// 显示7个LED灯目前的状态
			changeLedStateByLocation(ledLocation[i],ledState[i]%2);
	}
}

/********************************************
* 函数功能:事件测试函数2
* 函数参数:无
* 函数返回值:无
********************************************/
void eventTask2(void)
{
	// 记录本次需要触发事件
	uint16_t event[] = {EVENT1,EVENT2,EVENT3,EVENT4,EVENT5,EVENT6,EVENT7,EVENT8};
	int	i = -1;
	// 保存系统时间
	static portTickType myPreviousWakeTime;
	// 保存阻塞时间
	const volatile TickType_t xDelay1000ms = pdMS_TO_TICKS( 1000UL );
	// 获取当前时间
	myPreviousWakeTime = xTaskGetTickCount();
	while(1)
	{
		// 每次执行都翻转LED8状态 以保证肉眼看到该任务在运行
		rollbackLedByLocation(LED8);
		// 发送事件同步
		xEventGroupSetBits(myxEventGroupHandle_t,event[++i%8]);
		// 非阻塞延时1s
		xTaskDelayUntil( &myPreviousWakeTime,xDelay1000ms );
	}
}

结果


事件测试结果图


  小编也有其他的一些相关文章,欢迎各位看官点击观看😉😉😉

  • 【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法
  • 【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法
  • 【FreeRTOS】详细讲解FreeRTOS中消息队列并通过示例讲述其用法

    最后 ,欢迎大家留言或私信交流,共同进步!😁😁😁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么data属性是一个函数而不是一个对象?

一、实例和组件定义data的区别 vue实例的时候定义data属性既可以是一个对象&#xff0c;也可以是一个函数 const app new Vue({el:"#app",// 对象格式data:{foo:"foo"},// 函数格式data(){return {foo:"foo"}} }) 组件中定义data属性&#x…

MACD二次金叉选股公式,零轴上下、一次三次金叉举一反三

本文介绍了MACD二次金叉选股公式编写思路&#xff0c;以MACD零轴之下二次金叉为例&#xff0c;编写选股公式&#xff0c;进行信号过滤&#xff0c;并把选股公式改编为技术指标。此外举一反三&#xff0c;介绍了三次金叉以及MACD零轴上二次金叉。 一、MACD二次金叉选股公式编写 …

ECharts线性渐变色示例演示

第003个点击查看专栏目录Echarts的渐变色采用了echarts.graphic.LinearGradient的方法&#xff0c;可以根据代码中的内容来看如何使用。线性渐变&#xff0c;多用于折线柱形图&#xff0c;前四个参数分别是 x0, y0, x2, y2, 范围从 0 - 1&#xff0c;相当于在图形包围盒中的百分…

计算机图形学 第10章 真实感图形

目录 学习目标 前情提要 # RGB颜色模型 HSV颜色模型 CMYK颜色模型/印刷颜色模型 简单光照模型&#xff08;考&#xff09;⭐⭐⭐ 简单光照模型假定&#xff1a; 材质属性 环境光模型 漫反射光模型 镜面反射光模型 Phong反射模型: 光强衰减 增加颜色 光滑着色 直线段的…

CentOS 7.9安装zabbix6.0LTS版本

环境说明 这里使用为 CentOS 7.9版本进行测试验证&#xff0c;zabbix Server 采用源码包部署&#xff0c;数据库采用 MySQL8.0版本&#xff0c;zabbix-web使用 &#xff0c;nginxphp来实现。 具体信息如下&#xff1a; 软件名版本zabbix-server6.0.12LTSzabbix-agent6.0.12LT…

MySQL数据库的常见考点

文章目录1、ACID事务原理事务持久性事务原子性MVCC基本概念MVCC基本原理undo logundo log版本链readviewMVCC实现原理RC读已提交RR可重复读MVCC实现原理总结2、并发事务引发的问题3、事务隔离级别4、索引4.1、索引概述4.2、索引优缺点4.3、索引结构二叉树B-Tree树BTree树BTree优…

测试组合生成器-allpairspy

1、前言 在我们写功能用例时&#xff0c;常常会遇到多个参数有很多的选项&#xff0c;而如果想把这些参数值都要覆盖执行的话&#xff0c;工作量可想而知。那有没有什么办法既可以减少用例数量&#xff0c;也可以保证用例质量又降低测试时间成本&#xff0c;本篇将介绍一款工具…

关于机房精密空调监控系统,你想了解的都在这里!

机房精密空调是针对现代电子设备机房规划的专用空调&#xff0c;它的作业精度和可靠性都要比一般空调高得多。在计算机机房中的设备是由很多的微电子、精细 机械设备等组成&#xff0c;而这些设备运用了很多的易受温度、湿度影响的电子元器件、机械构件及资料&#xff0c;所以精…

Unreal学习笔记2-绘制简单三角形

文章目录1. 概述2. 详论2.1. 代码实现2.2. 解析&#xff1a;Component2.3. 解析&#xff1a;材质2.4. 解析&#xff1a;包围盒2.5. 解析&#xff1a;Section3. 其他4. 参考1. 概述 之所以写这个绘制简单三角形的实例其实是想知道如何在Unreal中通过代码绘制自定义Mesh&#xf…

网页开发:MySQL和Python案例

目录 一、MySQL的概念和引入 二、MySQL指令 1、数据库管理&#xff08;文件夹&#xff09; 2、数据表的管理&#xff08;文件&#xff09; 3、数据行操作 三、常用的数据类型 四、员工管理案例 1、使用MySQL内置工具&#xff08;命令&#xff09; 2、Python代码实现 ①…

Linux学习之常用基本命令【2】

文章目录一 文件内容查看1.1 catcatcat 由第一行开始显示1.2 tactactac 由最后一行开始显示1.3 nlnlnl 显示行号1.4 moremoremore 翻页1.5 lesslessless 翻页1.6 headheadhead 显示前几行1.7 tailtailtail 显示后几行1.8 拓展&#xff1a;LinuxLinuxLinux 链接概念二 VimVimVim…

Ubuntu18.04.6 配置固定ip、ssh登录、root账号

上文讲解了如何下载安装ubuntu&#xff0c;https://blog.csdn.net/weixin_47491957/article/details/128839639 ubuntu在安装完成后&#xff0c;是不能进行ssh登录、且没有root账号&#xff0c;本文带来如何配置ssh登录及root账号 在做这些设置之前&#xff0c;我们要确定ip固…

【手写 Promise 源码】第十三篇 - Promise.allsettled 和 Promise.any 的实现

一&#xff0c;前言 上一篇&#xff0c;主要实现了 Promise 静态 API&#xff08;类方法&#xff09;&#xff1a;Promise.race&#xff0c;主要涉及以下几个点&#xff1a; 测试原生 Promise.race 的使用&#xff1b;Promise.race 的功能与特性分析&#xff1b;Promise.race…

10、声明和创建字符串

目录 一、声明字符串 二、创建字符串 &#xff08;1&#xff09;String(char a[]) &#xff08;2&#xff09;String(char a[], int offset, int length) &#xff08;3&#xff09;String(char[] value) 一、声明字符串 在Java语言中字符串必须包含在一对双引号&#xf…

记录每日LeetCode 2325.解密消息 Java实现

题目描述&#xff1a; 给你字符串 key 和 message &#xff0c;分别表示一个加密密钥和一段加密消息。解密 message 的步骤如下&#xff1a; 使用 key 中 26 个英文小写字母第一次出现的顺序作为替换表中的字母 顺序 。 将替换表与普通英文字母表对齐&#xff0c;形成对照表。…

【Flink】详解StreamGraph

概述 没有看上一期的小伙伴请先看上一期【Flink】浅谈Flink架构和调度&#xff0c;上一期的一个核心内容就是 Flink 中的执行图可以分成四层&#xff1a;StreamGraph → JobGraph → ExecutionGraph → 物理执行图。 今天我们好好谈论一下StreamGraph&#xff0c;StreamGraph…

WPF MaterialDesignIn 收银系统(2)主界面

前言 接上一篇&#xff0c;既然登陆页面搞定了&#xff0c;接下来就是登陆成功后跳转的主界面了。 界面 思考 到了这一步&#xff0c;我们就要开始思考&#xff0c;怎么来丰富我们的应用&#xff0c;最少需要哪些东西、功能、模块、数据等等&#xff0c;才能支撑起一个收银系统…

java分支语句

流程控制1.1流程控制语句概述1.2流程控制语句分类顺序结构顺序结构是程序中最简单最基本的结构。分支结构(if,switch)循环结构(for,while,do...while)if语句2.1if语句格式1//格式 if(关系表达式){ 语句体&#xff1b; }执行流程&#xff1a;首先计算关系表达式的值如果关系表达…

每日一练10——井字棋密码强度等级

文章目录井字棋思路&#xff1a;代码&#xff1a;密码强度等级思路&#xff1a;代码&#xff1a;井字棋 题目链接&#xff1a; 思路&#xff1a; 井字棋&#xff0c;是一种在3*3格子上进行的连珠游戏&#xff0c;三个相同就代表获胜。 井字棋有四种情况表示当前玩家获胜&…

MySQL 面试题(一):索引失效的几种情况

❤️ 博客首页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;MySQL 教程&#xff1a;从入门到精通 文章目录一、like 以通配符 % 开头的索引会失效二、is not null 无法使用索引&a…