uCOSIII实时操作系统 十 事件标志组

news2024/11/18 14:45:59

目录

事件标志组:

事件标志组API函数:

创建事件标志组:

等待事件标志组:

向事件标志组发送标志:

事件标志组实验:


事件标志组:

有时候一个任务可能需要和多个事件同步这个时候就需要使用事件标志组。时间标志组与任务之间有两种同步机制:“或”同步和“与”同步,当一个事件发生,任务都被同步的同步机制是“或”同步需要所有的事件都发生任务才会被同步的机制是“与”同步。如下图所示:

下边是正点原子对他的解释:

为什么要使用事件标志组???直接搞个全局变量不就行了吗???真的可以吗

事件标志组是实现多任务同步的有效机制之一。在裸机编程的时候使用全局变量确实方便,但是在加上RTOS后就是另一种情况了。

  1. 使用事件标志组可以让RTOS内核有效的管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现。(麻烦至极)
  2. 使用全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心。
  3. 使用事件标志组可以有效的解决中断服务程序和任务之间的同步问题。

事件标志组API函数:

创建事件标志组:

在使用事件标志组之前,需要调用OSFlagCreate()创建一个事件标志组,而在创建事件标志组之前需要先定一个事件标志组(eg: OS_FLAG_GRP EventFlag;)。

OSFlagCreate()函数原型:

p_grp:指向事件标志组,

p_name:事件标志组的名字。

flags:定义事件标志组的初始值。

p_err:用来保存调用此函数后返回的错误码

   

等待事件标志组:

等待一个事件标志组需要调用函数OSFlagPend()函数,函数原型如下:

OSFlagPend()函数允许将事件标志组里事件标志的“与或”组合状态设置成任务的等待条件。

任务等待的条件可以使标志组里任意一个标志置位或者清零,也可以是所有事件标志都置位或者清零。如果任务等待的事件标志组不满足设置的条件,那么该任务被置位挂起,直到等待的事件标志组满足条件,指定的超时时间到,事件标志被删除或者被另一个任务终止了该任务的挂起状态。

参数:

p_grp : 指向时间标志组,

flags :   bit序列,任务需要等待事件标志组的哪个位就是把这个序列对应的位置1.根据设置这个序列可以是8bit,16bit,32bit比如任务需要等待事件标志组的bit0或者bit1(无论是等待置位还是清零)flag的值就是0x03。

 timeout:指定等待事件标志组的超时时间(时钟节拍数),如果在指定的超时时间内所等待的一个或多个事件没有发生,那么任务恢复运行。如果此值设置为 0,则任务就将一直等待下去,直到一个或多个事件发生。

 opt:决定任务等待的条件是所有标志置位、所有标志清零、任意一个标志置位还是任 意一个标志清零,具体定义如下:

OS_OPT_PEND_FLAG_CLR_ALL        等待事件标志组所有的位清零

OS_OPT_PEND_FLAG_CLR_ANY        等待事件标志组中任意一个标志清零

OS_OPT_PEND_FLAG_SET_ALL        等待事件标志组中所有的位置位

OS_OPT_PEND_FLAG_SET_ANY        等待事件标志组中任意一个标志置位 

上边调用四个选项的时候还可以搭配下边三个选项。

OS_OPT_PEND_FLAG_CONSUME       用来设置是否保留该事件的标志状态

OS_OPT_PEND_NON_BLOCKING        标志组不满足条件时,不挂起任务

OS_OPT_PEND_BLOCKING                  标志组不满足条件时挂起任务

这里应该注意选项OS_OPT_PEND_FLAG_CONSUME       的使用方法,如果我们希望任务等待事件标志组的任意一个标志置位,并在满  足条件后将对应的标志清零,那么就可以搭配OS_OPT_PEND_FLAG_CONSUME。       

p_ts:指向一个时间戳,记录了发送、终止和删除事件标志组的时刻,如果为这个指针赋值 NULL,则函数的调用者将不会收到时间戳。

p_err:用来保存调用此函数后返回的错误码。

向事件标志组发送标志:

调用函数OSFlagPost()可以对事件标志组经行置位或者清零,函数原型如下:

一般情况下,需要经行置位或者清零的标志由一个掩码确定(参数:flag)。OSFlagPost()修改完事件标志以后,将检查并使那些等待条件已经满足的任务进入就绪状态,该函数可以对已经置位或者清零的标志进行重复置位和清零操作。

参数:

p_grp:指向事件标志组。

flags:决定对哪些位清零和置位,当 opt 参数为 OS_OPT_POST_FLAG_SET 的时,参flags 中置位的位就会在事件标志组中对应的位也将被置位。当 opt OS_OPT_POST_FLAG_CLR 的时候参数 flags 中置位的位在事件标志组中对应的位将被清零。

 opt:决定对标志位的操作,有两种选项。

OS_OPT_POST_FLAG_SET对标志位进行置位操作
OS_OPT_POST_FLAG_CLR对标志位进行清零操作

p_err: 保存调用此函数后返回的错误码。

事件标志组实验:

设计一个程序,只有按下KEY0和KEY1(不需要同时按下)时任务任务C才可以执行。

实验源码:

 #include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "includes.h"
#include "os_app_hooks.h"
#include "key.h"
//UCOSIII中以下优先级用户程序不能使用,ALIENTEK
//将这些优先级分配给了UCOSIII的5个系统内部任务
//优先级0:中断服务服务管理任务 OS_IntQTask()
//优先级1:时钟节拍任务 OS_TickTask()
//优先级2:定时任务 OS_TmrTask()
//优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
//优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()

//创建任务A
//定义任务优先级
#define TASK_A_PRIO 3
//定义任务控制块
OS_TCB TASK_A_TCB;
//定义任务堆栈大小
#define TASK_A_STK_SIZE 128
//定义任务堆栈
CPU_STK TASK_A_STK[TASK_A_STK_SIZE];
//定义任务函数
void TASK_A(void *arg);

//创建任务B
//定义任务优先级
#define TASK_B_PRIO 4
//定义任务控制块
OS_TCB TASK_B_TCB;
//定义任务堆栈大小
#define TASK_B_STK_SIZE 128
//定义任务堆栈
CPU_STK TASK_B_STK[TASK_B_STK_SIZE];
//定义任务函数
void TASK_B(void *arg);

//创建任务C
//定义任务优先级
#define TASK_C_PRIO 5
//定义任务控制块
OS_TCB TASK_C_TCB;
//定义任务堆栈大小
#define TASK_C_STK_SIZE 128
//定义任务堆栈
CPU_STK TASK_C_STK[TASK_C_STK_SIZE];
//定义任务函数
void TASK_C(void *arg);


//共享资源
u8 share_res[256];

//定义事件标志
#define KEY0_FLAG 0x01//0000 0001
#define KEY1_FLAG 0x02//0000 0010

//定义事件标志组
OS_FLAG_GRP EventFlag;


int main(void)
{
	OS_ERR err1;//错误码变量
	CPU_SR_ALLOC();//定义临界区需要的变量
	
	//硬件初始化
	delay_init();       //延时初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
	uart_init(115200);    //串口波特率设置
	LED_Init();
	KEY_Init();
	
	OSInit(&err1);//初始化UCOSIII
	OS_CRITICAL_ENTER();//进入临界区代码
	
	//创建开始任务A
	OSTaskCreate((OS_TCB 	* )&TASK_A_TCB,		//任务控制块
				 (CPU_CHAR	* )"main TASKA", 		//任务名字
                 (OS_TASK_PTR )TASK_A, 			//任务函数
                 (void		* )0,					//传递给任务函数的参数
                 (OS_PRIO	  )TASK_A_PRIO,     //任务优先级
                 (CPU_STK   * )&TASK_A_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)TASK_A_STK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)TASK_A_STK_SIZE,		//任务堆栈大小
                 (OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void   	* )0,					//用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                 (OS_ERR 	* )&err1);				//存放该函数错误时的返回值
	
	OS_CRITICAL_EXIT();//退出临界区代码
	OSStart(&err1);//开启UCOSIII

	while(1);
}

//开始任务函数
void TASK_A(void *arg)
{
	OS_ERR err2_3;//错误码变量
	CPU_SR_ALLOC();//定义临界区需要的变量
	arg = arg;
	
	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err2_3);  	//统计任务                
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了测量中断关闭时间
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //当使用时间片轮转的时候
	 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3);  
#endif	
	
//#if OS_CFG_APP_HOOKS_EN				//使用钩子函数
//	App_OS_SetAllHooks();			
//#endif
	
#if OS_CFG_SCHED_ROUND_ROBIN_EN
	//使用时间片轮转调度功能,时间片长度为一个时钟节拍即1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3);
#endif
	

	OS_CRITICAL_ENTER();//进入临界区代码
	
	//创建事件标志组
	OSFlagCreate((OS_FLAG_GRP *)&EventFlag,
							 (CPU_CHAR *)"EventFlag",
							 (OS_FLAGS )0,
							 (OS_ERR *)&err2_3
							);
	
	
	
	//创建开始任务B
	OSTaskCreate((OS_TCB 	* )&TASK_B_TCB,		//任务控制块
				 (CPU_CHAR	* )"main TASKB", 		//任务名字
                 (OS_TASK_PTR )TASK_B, 			//任务函数
                 (void		* )0,					//传递给任务函数的参数
                 (OS_PRIO	  )TASK_B_PRIO,     //任务优先级
                 (CPU_STK   * )&TASK_B_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)TASK_B_STK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)TASK_B_STK_SIZE,		//任务堆栈大小
                 (OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void   	* )0,					//用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                 (OS_ERR 	* )&err2_3);				//存放该函数错误时的返回值
			 	
//创建开始任务C
	OSTaskCreate((OS_TCB 	* )&TASK_C_TCB,		//任务控制块
				 (CPU_CHAR	* )"main TASKC", 		//任务名字
                 (OS_TASK_PTR )TASK_C, 			//任务函数
                 (void		* )0,					//传递给任务函数的参数
                 (OS_PRIO	  )TASK_C_PRIO,     //任务优先级
                 (CPU_STK   * )&TASK_C_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)TASK_C_STK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)TASK_C_STK_SIZE,		//任务堆栈大小
                 (OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void   	* )0,					//用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                 (OS_ERR 	* )&err2_3);				//存放该函数错误时的返回值
			
 								 
	OS_CRITICAL_EXIT();//退出临界区代码
	
	//任务一执行完函数之后删掉自身
	OSTaskDel((OS_TCB *)0,&err2_3);			 
}


//任务B任务函数  高优先级
void TASK_B(void *arg)
{
//	u8 key;
	OS_ERR err_B;
	u8 key;
	OS_FLAGS num_flag;
	
	while(1)
	{
		
		key = KEY_Scan(0); //扫描按键
		if(key==KEY0_PRES)
		{
			
			printf("KEY0:向事件标志组发送标志!\r\n");
			num_flag=OSFlagPost(&EventFlag,KEY0_FLAG,OS_OPT_POST_FLAG_SET,&err_B);//向事件标志组发送标志
			printf("事件标记组的值为%d\r\n",num_flag);
			LED0 = ~LED0;
		}
		else if(key==KEY1_PRES)
		{
			
			printf("KEY1:向事件标志组发送标志!\r\n");
			num_flag=OSFlagPost(&EventFlag,KEY1_FLAG,OS_OPT_POST_FLAG_SET,&err_B);//向事件标志组发送标志
			printf("任务B:事件标记组的值为%d\r\n",num_flag);
			LED1 = ~LED1;
		}
	
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B); //延时10ms
	}
	
}

//任务C任务函数  中优先级
void TASK_C(void *arg)
{
	OS_ERR err_C;
	while(1)
	{
		//等待事件标志组
		OSFlagPend (&EventFlag,KEY0_FLAG+KEY1_FLAG,0,OS_OPT_PEND_FLAG_SET_ALL,0,&err_C);
		printf("任务C等到事件\r\n");
		printf("任务C:事件标记组的值为%d\r\n",EventFlag.Flags);
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}


实验现象:

文章参考:正点原子《STM32F1 UCOS开发手册_V2.0》

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

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

相关文章

word修改公式默认字体并打出漂亮公式

文章目录 word公式简介传统方法1——mathtype传统方法2——word自带公式编辑器最简洁方法——更改word自带公式字体快捷方式效果展示 word公式简介 word自带的公式字体Cambria Math不可否认很丑,要打出latex格式的漂亮字体很困难。使用Markdown工具很多只能导出为不…

基于WebRTC的程序因虚拟内存不足导致闪退问题的排查以及解决办法的研究

目录 1、WebRTC简介 2、问题现象描述 3、将Windbg附加到目标进程上分析 3.1、Windbg没有附加到主程序进程上,没有感知到异常或中断 3.2、Windbg感知到了中断,中断在DebugBreak函数调用上 3.3、32位进程用户态虚拟地址和内核态虚拟地址的划分 …

伊始:「深入浅出」的学习

深入浅出是一种有效的学习原则。这种学习原则基于最新的认知科学、神经生物学及教育心理学研究结果,通过使用元认知,采用不同的方法展示信息并加速学习过程。 图形结合统一思想 与单纯的文字相比较,图形结合(当然,你也…

Mybatis 相关模块以及设计模式分析

一、缓存模块 MyBatis作为一个强大的持久层框架,缓存是其必不可少的功能之一,Mybatis中的缓存分为一级缓存和二级缓存。但本质上是一样的,都是使用Cache接口实现的。缓存位于 org.apache.ibatis.cache包下。 通过结构我们能够发现Cache其实使…

[Leetcode] 0035. 搜索插入位置

35. 搜索插入位置 题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target …

『C++ - 模板』之模板进阶

文章目录 模板进阶非类型模板参数类型模板参数与非类型模板参数的不同 模板的特化全特化偏特化全特化与偏特化的区别 模板的分离编译解决办法 总结 模板进阶 非类型模板参数 类型模板参数与非类型模板参数的不同 类型模板参数非类型模板参数 ​ 类型模板参数一般用来设置模…

Self-Supervised Learning(2021补)

文章目录 引子X mask inputNext Sentence PredictionDownstream TasksGLUEBERT的四个用法情感分析POS标注自然语言推断Natural Language Inferencee (NLI)问答(抽取式) BERT的衍生模型Multi-lingual BERTGPT的野望(略) 发现有这一…

sql语句数据库查询:如果当前元素已经使用过,下拉框不显示该元素该如何查询?

写宿舍管理系统,做到宿管和楼栋关系时,新增一个宿管,一个宿管管理一栋楼,如果当前楼栋已选择,那么就不能再选,如图所示: 最开始使用的是: SELECT DISTINCT b.building_num,b.TYPE,b…

Linux系统编程05

在代码中启动多个进程 使用system库函数启动多个进程 传统的进程调用就是我们在命令框里输入运行某个进程,而我们可以依靠代码,实现让一个进程取启动另一个进程 在进程运行过程我们使用命令ps -elf看到正在运行的有三个进程 system的调用过程 首先./…

Zoho Creator推出全新的Canvas布局设计器功能

自2021年Zoho CRM的UI设计工具——Canvas画布功能发布以来,受到了广泛好评,它的出现为CRM的页面布局形式提供了更多选择和可能,让CRM用户彻底告别了“单调、死板、机械”的交互页面。 8月1日,Zoho Creator也推出了全新的Canvas画…

聊一聊如何在Vue中使用事件总线( Event Bus)进行组件间通信

事件总线模式允许不同的组件之间进行通信。它要求一个中央枢纽,组件可以通过它发送和接收事件,从而使组件之间的数据交换和交互更加顺畅。本文探讨了它的使用方法,以便开发人员能够充分利用它在Vue开发中的潜力。 跨通信是应用程序中组件之间…

jmeter如何测试websocket接口?

jmeter做接口测试,很多人都是做http协议的接口,就有很多人问websocket的接口怎么测试啊? 首先,我们要明白,websocket接口是什么接口。 然后,我们怎么用jmeter测试? jmeter要测试websocket接口…

B/S医院手术麻醉临床系统:围术期的认识

手术是治疗很多疾病最有效而且绕不开的措施。而从医生和患者确定了要进行手术治疗的时候,医院相关人员就会开始围着患者团团转,在术前、术中和术后,医生会告诉患者很多事情,患者也会了解很多就诊相关知识。 一、围术期的认识 围术…

qml之动态元素类型

文章目录 动画例子 应用动画例子 缓动曲线例子 动画分组例子 嵌套动画代码 状态和转换代码 动画 QMlL使用插值的方式控制属性的更改。动画是在指定的时间内一些列属性的持续变化。 常用的动画类型元素动画:PropertyAnimation:属性值改变播放动画NumberAnimation:qr…

【快速解决】在vs2022中配置SFML图形库

目录 SFML 图形库的安装步骤如下: 1.下载 SFML 在 SFML 的官网(下载对应操作系统版本的 SFML)。​编辑 2.解压文件 将下载的压缩包解压至任意位置,得到类似如下的目录结构: 3.配置 VS 打开 Visual Studio&#xff…

Linux ————使用常用的Linux命令

(一)Linux命令的特点 在Linux系统的早期版本中,由于不支持图形用户界面,命令行成为了主要的操作手段。对于那些在文本模式和终端模式下需要查看系统状态和监控系统操作的用户,熟悉常用的Linux命令是至关重要的。以下是…

自然语言处理---Transformer机制详解之Self attention机制详解

1 Self-attention的特点 self-attention是一种通过自身和自身进行关联的attention机制, 从而得到更好的representation来表达自身. self-attention是attention机制的一种特殊情况,在self-attention中, QKV, 序列中的每个单词(token)都和该序列中的其他所有单词(to…

RDB.js:适用于 Node.js 和 Typescript 的终极对象关系映射器

RDB.js 是适用于 Node.js 和 Typescript 的终极对象关系映射器,可与 Postgres、MS SQL、MySQL、Sybase SAP 和 SQLite 等流行数据库无缝集成。无论您是使用 TypeScript 还是 JavaScript(包括 CommonJS 和 ECMAScript)构建应用程序&#xff0c…

高效MMdetection(3.1.0)环境安装和训练自己数据集教程(实现于Linux(ubuntu),可在windows尝试)

很久没用mmdetection了,作为目标检测常见的几个深度学习框架,mmdetection用的人还是很多的,其中比较吸引人的一点就是mmdetection集成了非常多的算法,对于想做实验对比和算法学习的人来说,基于这个框架可以事半功倍。因…

“暂停加息,股市低迷:242只股票创新低,比特币突破2.8万美元后看涨趋势不可挡!“

11 月1日 FOMC 会议 美联储主席杰罗姆鲍威尔周五在纽约发表讲话,毫不意外地,他采取了更加鸽派的立场,因为在不确定的世界中,美国政府的过度杠杆化和可能即将到来的经济衰退已成为共识。 根据鲍威尔对未来加息的最低限度讨论&…