uCOSIII实时操作系统 十一 消息传递

news2024/10/3 8:23:07

目录

消息队列:

消息列队相关的API函数

创建消息队列:

等待消息列队:

向消息列队发送消息:

消息队列实验

任务内嵌消息队列:

任务内建消息队列的API函数

等待任务内建消息:

发送任务内建消息:

任务内建消息队列实验:


        一个任务或者中断有时候需要和另一个任务交流信息,这个就是消息传递的过程就叫做任务间通信,任务之间的信息传递有两种途径,一是使用全局变量,二是通过发布消息。

        使用全局变量的时候,每一个任务或者中断服务程序都必须保证气全局变量的独占访问,(通常解决方式:关中断、临界区、信号量)。消息也可以通过消息队列作为中介发布给任务。

消息队列:

下边是正点原子的解释:

中断服务程序中只能使用OSPost()函数!!!在UCOSIII中对于消息队列的读取可以使先进先出(FIFO)也可以是后进先出(LIFO)的方式。接收消息的任务旁边的小沙漏表示任务可以指定一个超时时间,如果任务在这段时间内没有接收到消息的话就会唤醒任务,并且返回一个错误码告诉UCOSIII超时,任务是因为接收消息超时而被唤醒的,不是因为接收到了消息。如果将这个超时时
间指定为0的话,那么任务就会一直等待下去,直到接收到消息。

消息列队中有一个列表,记录了所有正在等待获取得消息的任务,

消息列队相关的API函数

有关消息列队的API函数:

我们经常用的关于消息队列的函数主要有三个,创建消息队列函数OSQCreate(),向消息队列发送消息函数OSQPost()和等待消息队列函数OSQPend()。

创建消息队列:

OSQCreate函数用来创建一个消息队列,创建信号列队之前要先定义一个信号列队对象OS_Q + 列队对象名字;,消息队列使得任务或者中断服务程序可以向一个或者多个任务发送消息,函数原型如下:

p_q:指向一个消息队列,消息队列的存储空间必须由应用程序分配。

p_name:消息队列的名字。

max_qty: 指定消息队列的长度,必须大于 0。当然,如果 OS_MSGs 缓冲池中没有足够多的 OS_MSGs 可用,那么发送消息将会失败,并且返回相应的错误码,指明当前没有可用的 OS_MSGs。

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

等待消息列队:

当一个任务想要从消息队列中接收一个消息的话就需要使用函数 OSQPend()。当任务调用这个函数的时候,如果消息队列中有至少一个消息时,这些消息就会返回给函数调用者。函数原型如下:

p_q:指向一个消息队列。

timeout:等待消息的超时时间,如果在指定的时间没有接收到消息的话,任务就会被唤醒,接着运行。这个参数也可以设置为0,表示任务将一直等待下去,直到接收到消息。

opt:用来选择是否使用阻塞模式,有两个选项可以选择 OS_OPT_PEND_BLOCKING 如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息。 OS_OPT_PEND_NON_BLOCKING 如果消息队列没有任何消息的话任务就直接返回。

p_msg_size: 指向一个变量用来表示接收到的消息长度(字节数)。

p_ts:指向一个时间戳,表明什么时候接收到消息。如果这个指针被赋值为 NULL的话,说明用户没有要求时间戳。

p_err:用来保存调用此函数后返回的错误码。如果消息队列中没有任何消息,并且参数 opt 为OS_OPT_PEND_NON_BLOCKING 时,那么调用 OSQPend()函数的任务就会被挂起,直到接收到消息或者超时。如果有消息发送给消息队列,但是同时有多个任务在等待这个消息,那么 UCOSIII 将恢复等待中的最高优先级的任务。

向消息列队发送消息:

可以通过函数 OSQPost()向消息队列发送消息,如果消息队列是满的,则函数 OSQPost()就
会立刻返回,并且返回一个特定的错误代码,函数原型如下:

如果有多个任务在等待消息队列的话,那么优先级最高的任务将获得这个消息。如果等待消息的任务优先级比发送消息的任务优先级高,则系统会执行任务调度,等待消息的任务立即恢复运行,而发送消息的任务被挂起。可以通过 opt 设置消息队列是FIFO 还是 LIFO。如果有多个任务在等待消息队列的消息,则 OSQPost()函数可以设置仅将消息发送给等待任务中优先级最高的任务(opt 设置为 OS_OPT_POST_FIF 或者 OS_OPT_POST_LIFO),也可以将消息发送给所有等待的任务(opt 设置为OS_OPT_POST_ALL) 。 如 果 opt 设 置OS_OPT_POST_NO_SCHED,则在发送完消息后,会进行任务调度。

p_q:指向一个消息队列。
p_void:指向实际发送的内容,p_void 是一个执行 void 类型的指针,其具体含义由用户程序的决定。
msg_size: 设定消息的大小,单位为字节数。
opt:用来选择消息发送操作的类型,基本的类型可以有下面四种。
OS_OPT_POST_ALL将消息发送给所有等待该消息队列的任务,需要和选项 OS_OPT_POST_FIFO 或者
OS_OPT_POST_LIFO 配合使用。
OS_OPT_POST_FIFO待发送消息保存在消息队列的末尾
OS_OPT_POST_LIFO待发送的消息保存在消息队列的开头
OS_OPT_POST_NO_SCHED禁止在本函数内执行任务调度。
我们可以使用上面四种基本类型来组合出其他几种类型,如下:
OS_OPT_POST_FIFO + OS_OPT_POST_ALL
OS_OPT_POST_LIFO + OS_OPT_POST_ALL
OS_OPT_POST_FIFO + OS_OPT_POST_NO_SCHED
OS_OPT_POST_LIFO + OS_OPT_POST_NO_SCHED OS_OPT_POST_FIFO + OS_OPT_POST_ALL +
OS_OPT_POST_NO_SCHED
OS_OPT_POST_LIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHED

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

消息队列实验

设计一个应用程序,该程序有 4 任务、两个消息队列和一个定时器。任务A 用于创建其他 3 个任务。任务B 为主任务,用于检测按键,并且将按键的值通过消息队列KEY_Msg 发送给任任务C,任务B 任务还用于检测消息队列 DATA_Msg的总大小和剩余空间大小,并且控制 LED0 的闪烁。任务C获取KEY_Msg内的消息,根据不同的消息做出相应的处理,定时器1的回调函数tmr1_callback通过消息队列DATA_Msg,将定时器1的运行次数作为信息发给任务D,任务D将DATA_Msg中的消息通过串口打印出来。

实验代码:

 #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);


//创建任务C
//定义任务优先级
#define TASK_D_PRIO 6
//定义任务控制块
OS_TCB TASK_D_TCB;
//定义任务堆栈大小
#define TASK_D_STK_SIZE 128
//定义任务堆栈
CPU_STK TASK_D_STK[TASK_D_STK_SIZE];
//定义任务函数
void TASK_D(void *arg);

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

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

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

//先定义两个消息队列和一个定时器的宏
消息队列///
#define KEYMSG_Q_NUM 1	//按键消息队列数量
#define DATAMSG_Q_NUM 4	//发送数据的消息队列的数量
OS_Q KEY_Msg;						//定义一个消息队列,用于按键的消息传递模拟消息邮箱
OS_Q DATA_Msg;					//定义一个消息列队用于发送数据
定时器///
u8 tmrlsta = 0;//标记定时器的工作状态
OS_TMR tmr1;//定义一个定时器
void tmr1_callback(void *p_arg); //定时器1回调函数

void check_msg_queue(u8 *p)
{
	CPU_SR_ALLOC();
	u8 msgq_remain_size; //消息队列剩余大小
	OS_CRITICAL_ENTER(); //进入临界段
	msgq_remain_size = DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries;
	sprintf((char*)p,"Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); //显示DATA_Msg消息队列总的大小
	printf("Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize);
	sprintf((char*)p,"Remain Size:%d",msgq_remain_size); //显示DATA_Msg剩余大小
	printf("Remain Size:%d",DATA_Msg.MsgQ.NbrEntriesSize);
	OS_CRITICAL_EXIT(); //退出临界段
}


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();//进入临界区代码
	
		//创建消息队列KEY_Msg
	OSQCreate ((OS_Q* )&KEY_Msg, //消息队列
						 (CPU_CHAR* )"KEY Msg", //消息队列名称
						 (OS_MSG_QTY )KEYMSG_Q_NUM, //消息队列长度,这里设置为1
						 (OS_ERR* )&err2_3); //错误码
	
	//创建消息队列DATA_Msg
	OSQCreate ((OS_Q* )&DATA_Msg,
						(CPU_CHAR* )"DATA Msg",
						(OS_MSG_QTY )DATAMSG_Q_NUM,
						(OS_ERR* )&err2_3);
						
	//创建定时器1
	OSTmrCreate((OS_TMR *)&tmr1, //定时器1
							(CPU_CHAR *)"tmr1", //定时器名字
							(OS_TICK )0, //0ms
							(OS_TICK )50, //50*10=500ms
							(OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式
							(OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数
							(void *)0, //参数为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);				//存放该函数错误时的返回值
			
//创建开始任务D	创建MSGDIS任务
	OSTaskCreate((OS_TCB 	* )&TASK_D_TCB,		//任务控制块
				 (CPU_CHAR	* )"main TASKD", 		//任务名字
                 (OS_TASK_PTR )TASK_D, 			//任务函数
                 (void		* )0,					//传递给任务函数的参数
                 (OS_PRIO	  )TASK_D_PRIO,     //任务优先级
                 (CPU_STK   * )&TASK_D_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)TASK_D_STK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)TASK_D_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)
{
	OS_ERR err_B;
	u8 key;
	OS_FLAGS num_flag;
	
	while(1)
	{
		
		key = KEY_Scan(0); //扫描按键
		if(key)
		{
			OSQPost((OS_Q* )&KEY_Msg,
			(void* )&key,
			(OS_MSG_SIZE)1,
			(OS_OPT )OS_OPT_POST_FIFO,
			(OS_ERR* )&err_B);//消息发送
			printf("任务B发送信息\r\n");
		}
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B); //延时10ms
	}
	
}

//任务C任务函数 按键处理任务的任务函数
void TASK_C(void *arg)
{
	u8 num;
	u8 *key;
	OS_MSG_SIZE size;
	OS_ERR err_C;
	while(1)
	{
		//请求消息KEY_Msg
		key=OSQPend((OS_Q* )&KEY_Msg,
						(OS_TICK )0,
						(OS_OPT )OS_OPT_PEND_BLOCKING,
						(OS_MSG_SIZE* )&size,
						(CPU_TS* )0,
						(OS_ERR* )&err_C);
		printf("任务C请求消息\r\n");
		printf("任务C接收到的信号值:%d\r\n",*key);
			switch(*key)
      {
      case WKUP_PRES: //KEY_UP控制LED1
				LED1 = ~LED1;
      break;
      case KEY0_PRES: //KEY0刷新LCD背景
				num++;
      break;
      case KEY1_PRES: //KEY1控制定时器1
				tmrlsta = !tmrlsta;
      if(tmrlsta)
      {
				OSTmrStart(&tmr1,&err_C);
				printf("定时器1开启\r\n");
      }
      else
      {
				OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err_C); //停止定时器1
				printf("定时器1关闭\r\n");
      }
      break;
      }
			OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}

//任务D任务函数 显示消息队列中的消息
void TASK_D(void *arg)
{
	u8 *p;
	OS_MSG_SIZE size;
	OS_ERR err;
	while(1)
	{
	//请求消息
		p=OSQPend((OS_Q* )&DATA_Msg,
							(OS_TICK )0,
							(OS_OPT )OS_OPT_PEND_BLOCKING,
							(OS_MSG_SIZE* )&size,
							(CPU_TS* )0,
							(OS_ERR* )&err);
		printf("任务D请求消息\r\n");
		printf("任务D接收到的信号值:%d\r\n",*p);
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err); //延时1s
	}
}

//定时器1回调函数
void tmr1_callback(void *p_arg)
{
	static u8 num = 0;
	OS_ERR err;
	num++;
	OSQPost((OS_Q* )&DATA_Msg,
			(void* )&num,
			(OS_MSG_SIZE)1,
			(OS_OPT )OS_OPT_POST_FIFO,
			(OS_ERR* )&err);//消息发送
			printf("定时器发送信号:%d\r\n",num);
			
	
}








上面有四个函数:TASK_A/B/C/D,这四个函数分别为定时器 1的回调函数、主任务的任务函数、按键处理任务的任务函数和显示任务的任务函数。tmrl callback0()函数是定时器1的回调函数,在TASK_A任务中我们是创建了一个定时器tmrl的,tmrl是一个周期定时器,定时周期为500ms。在 tmrl 的回调函数 tmrl callback0()中通过函数 OSQPost0向消息队列 DATA Msg 发送消息,这里向消息队列发送数据采用的是 FIFO方式,当发送失败的话就释放相应的内存并关闭定时器。TASK_B函数为主任务的任务函数,在这个函数中我们不断的扫描按键的键值,然后将键值发到消息队列 KEY Msg 中,这里向消息队列发送数据采用的是 FIFO 方式。
TASK_C为按键处理任务,在 main task 务中我们将按键值发送到了消息队列KEY Msg 中,在本函数中我们调用OSQPend0函数从消息队列 KEY Msg 中取消息,也就是按键值,然后根据不同的键值做出相应的处理。KEY UP 控制 LEDKEY0用来控制刷新 ;KEY1 控制 tmr1 的开关。
TASK_D()通过调用OSOPend0函数获得消息队列 DATA Msg 中的数据,并将获得到的
消息显示在 串口上。

实验现象:

任务内嵌消息队列:

和信号量一样,UCOSIII中每个任务也都有其内建消息队列,这样的话用户就不需要使用外部的消息队列就可以直接向任务发布消息,这个特性不仅简化了代码,而且比使用外部消息队列更加有效。

根据函数原型可知使用前应该现将OS_CFG_TASK_Q_EN置1,

任务内建消息队列的API函数

等待任务内建消息:

函数 OSTaskQPend()用来请求消息,该函数让任务直接接收从其他任务或者 ISR 中发送来的消息,不需要经过中间的消息队列,函数原型如下:

timeout:等待消息的超时时间,如果在指定的时间没有接收到消息的话,任务就会被唤醒,
接着运行。这个参数也可以设置为 0,表示任务将一直等待下去,直到接收到消息。

opt:用来选择是否使用阻塞模式,有两个选项可以选择。
OS_OPT_PEND_BLOCKING如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息。
OS_OPT_PEND_NON_BLOCKING 如果消息队列没有任何消息的话任务就直接返回。

p_msg_size: 指向存放消息大小的变量。

p_ts:指向一个时间戳,表明什么时候接收到消息。如果这个指针被赋值为 NULL的话,说明用户没有要求时间戳。

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

发送任务内建消息:

函数 OSTaskQPost()可以通过一个任务的内建消息队列向这个任务发送一条消息,同外置的
消息队列一样,一条消息就是一个指针,函数原型如下:

p_tcb:指向接收消息的任务的 TCB,可以通过指定一个 NULL 指针或该函数调用者的TCB 地址来向该函数的调用者自己发送一条消息。

p_void:发送给一个任务的消息。

msg_size:指定发送的消息的大小(字节数)。

opt:指定发送操作的类型,LIFO 和 FIFO 只能二选一。

OS_OPT_POST_FIFO待发送消息保存在消息队列的末尾(先进先出)
OS_OPT_POST_LIFO待发送的消息保存在消息队列的开头(后进先出)

上面两个选项可以搭配下面这个选项一起使用。OS_OPT_POST_NO_SCHED指定该选项时,在发送后不会进行任务调度,因此,该函数的调用者还可以继续运行。

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

任务内建消息队列实验:

设计一个应用程序,该程序有 3 任务和一个定时器。任务 TASK_A用于创建其他 2 个任务。TASK_B任务为主任务,用于检测按键,当检测到按键 KWY_UP 被按下的时候就开启或关闭定时器 1, TASK_B任务还用于检测 任务C任务的内建消息队列的总大小和剩余空间大小,并且控制 LED0 的闪烁。
定时器 1 的回调函数 tmr1_callback 通过任务 任务C内建的消息队列将定时器 1 的运行次数作为信息发送给任务C,任务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;

//先定义两个消息队列和一个定时器的宏
//消息队列///
//#define KEYMSG_Q_NUM 1	//按键消息队列数量
//#define DATAMSG_Q_NUM 4	//发送数据的消息队列的数量
//OS_Q KEY_Msg;						//定义一个消息队列,用于按键的消息传递模拟消息邮箱
//OS_Q DATA_Msg;					//定义一个消息列队用于发送数据

//指定内建消息队列大小
#define TASK_Q_NUM 4


定时器///
u8 tmrlsta = 0;//标记定时器的工作状态
OS_TMR tmr1;//定义一个定时器
void tmr1_callback(void *p_arg); //定时器1回调函数

//void check_msg_queue(u8 *p)
//{
//	CPU_SR_ALLOC();
//	u8 msgq_remain_size; //消息队列剩余大小
//	OS_CRITICAL_ENTER(); //进入临界段
//	msgq_remain_size = DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries;
//	sprintf((char*)p,"Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); //显示DATA_Msg消息队列总的大小
//	printf("Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize);
//	sprintf((char*)p,"Remain Size:%d",msgq_remain_size); //显示DATA_Msg剩余大小
//	printf("Remain Size:%d",DATA_Msg.MsgQ.NbrEntriesSize);
//	OS_CRITICAL_EXIT(); //退出临界段
//}


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();//进入临界区代码
	
						
	//创建定时器1
	OSTmrCreate((OS_TMR *)&tmr1, //定时器1
							(CPU_CHAR *)"tmr1", //定时器名字
							(OS_TICK )0, //0ms
							(OS_TICK )50, //50*10=500ms
							(OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式
							(OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数
							(void *)0, //参数为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)
{
	OS_ERR err_B;
	u8 key=0,num=0;
	
	while(1)
	{
		
		key = KEY_Scan(0); //扫描按键
		if(key==WKUP_PRES)
		{
				tmrlsta = !tmrlsta;
      if(tmrlsta)
      {
				OSTmrStart(&tmr1,&err_B);
				printf("定时器1开启\r\n");
      }
      else
      {
				OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err_B); //停止定时器1
				printf("定时器1关闭\r\n");
      }
		}
		num++;
		if(num%10==0)
		{
				LED1 = ~LED1;
		}
	
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B); //延时10ms
	}
	
}

//任务C任务函数 任务内建信号量任务
void TASK_C(void *arg)
{
	u8 *num;
	OS_MSG_SIZE size;
	OS_ERR err_C;
	while(1)
	{
		//请求消息KEY_Msg
		num=OSTaskQPend((OS_TICK )0,
			          		(OS_OPT )OS_OPT_PEND_BLOCKING,
			          		(OS_MSG_SIZE* )&size,
			          		(CPU_TS* )0,
			          		(OS_ERR* )&err_C);
		printf("任务C请求消息\r\n");
		printf("任务C接收到的信号值:%d\r\n",*num);

		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}



//定时器1回调函数
void tmr1_callback(void *p_arg)
{
	static u8 num = 0;
	OS_ERR err;
	num++;

	OSTaskQPost((OS_TCB* )&TASK_C_TCB,
			(void* )&num,
			(OS_MSG_SIZE)1,
			(OS_OPT )OS_OPT_POST_FIFO,
			(OS_ERR* )&err);//消息发送
			printf("定时器发送信号:%d\r\n",num);
			
	
}

实验现象:刚开始的时候只会看到led的闪烁,当按下KEY_UP之后,定时器打开每个一段时间发送信息,然后任务C每隔一秒接收一次并打印。

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

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

相关文章

Python-pptx教程之一从零开始生成PPT文件

简介 python-pptx是一个用于创建、读取和更新PowerPoint(.pptx)文件的python库。 典型的用途是根据动态内容(如数据库查询、分析数据等),将这些内容自动化生成PowerPoint演示文稿,将数据可视化&#xff0c…

ES6初步了解生成器

生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同 语法: function * fun(){ } function * gen(){console.log("hello generator");}let iterator gen()console.log(iterator)打印: 我们发现没有打印”hello…

计算机组成原理(一目了然的顶级总纲)(持续更新!)

文章目录 886冯诺依曼计算机计算机的五大部件(又称五大字系统)细化的计算机组成框图存储器 886 计算机系统由“硬件”和“软件”两大部分组成。 计算机的软件通常又可以分为两大类:系统软件和应用软件。 冯诺依曼计算机 数学家冯诺依曼&am…

MySQL数据库增删改查

删除表 drop table 表名; drop table if exists 表名;修改表 修改表名 alter table 表名 rename to 新表名;添加列 alter table 表名 add 列名 数据类型;删除列 alter table 表名 drop 列名;修改数据类型 alter …

WorkPlus专注私有化部署,为企业安全打造超级沟通协作APP

在如今全球化竞争和高速发展的商业环境中,企业内部的沟通和协作至关重要。面对众多的通讯和协作平台,WorkPlus独辟蹊径,专注私有化部署,致力于为企业打造安全专属、自主可控的超级沟通协作APP。正是这一专注与创新,让W…

发表《Nature》!哈佛大学团队成功研发自纠错量子计算机

(图片来源:网络) 量子计算机能达到当今最快的超级计算机也无法达到的速度和效率。然而,该技术尚未大规模推广和商业化,很大程度上是因为它无法自我纠错。与经典计算机不同,量子计算机无法通过一遍又一遍地…

双赢!企业咨询行业和低代码工具的破局之路

对于传统咨询企业来说,主要专注于流程和功能方面的咨询,在信息化时代中,以流程和业务驱动的模式为基础进行战略咨询、管理咨询和业务咨询,作为传统企业的外脑,在大数据时代,咨询行业在数智化时代如何应对自…

发表《数学》期刊!西班牙研究人员成功应用量子计算模型来预测多种疾病

Jos Luis Salmern 将量子计算应用于医疗保健领域(图片来源:网络) 谷歌量子人工智能(AI)研究小组的Sergio Boixo表示,量子计算还处于起步阶段,虽然很难预测其未来,但该技术已取得了一…

lvs+keepalived: 高可用集群

lvskeepalived: 高可用集群 keepalived为lvs应运而生的高可用服务。lvs的调度器无法做高可用,于是keepalived软件。实现的是调度器的高可用。 但是:keepalived不是专门为集群服务的,也可以做其他服务器的高可用。 lvs的高可用集群&#xf…

第九章 无线网络和移动网络 | 计算机网络(谢希仁 第八版)

文章目录 第九章 无线网络和移动网络9.1 无线局域网WLAN9.1.1 无线局域网的组成9.1.2 802.11局域网的物理层9.1.3 802.11局域网的MAC层协议9.1.4 802.11局域网的MAC帧 9.2 无线个人区域网WPAN9.3 无线城域网WMAN9.4 蜂窝移动通信网9.4.1 蜂窝无线通信技术简介9.4.2 移动IP9.4.3…

1. 概述

1.概述 1.1 信息时代的计算机网络 1.1.1 计算机网络的各类应用 1.1.2 计算机网络带来的负面问题 1.2 因特网概述 1.2.1 网络、互联网与因特网的区别与关系 若干个节点(Node)和链路(Link)互连形成了网络(Network&…

Flask后端开发(一)-基础知识和前期准备

目录 1.背景介绍1.1. 项目背景1.2. 项目难点1.3. 项目环境 2. flask后端开发实现的功能3. flask部署和前后端对接3.1. flask运行配置和服务器部署3.2. flask前后端传参 4. 后端测试工具4.1. 工具介绍4.2. 工具使用 后记 1.背景介绍 1.1. 项目背景 就是前几个月临时接手了一个…

Linux基础命令4——Linux快捷键与帮助命令

目录 Linux快捷键 Linux常用帮助命令 help命令——只适用于于内置命令 --help命令——只适用于外置命令 内外部都可使用的帮助命令 man命令 info命令 Linux快捷键 ctrlshift加号 放大屏幕 ctrl减号 缩小屏幕 ctrl l …

28、Flink 的SQL之DROP 、ALTER 、INSERT 、ANALYZE 语句

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

中国技术的对外输出:Telegram也开始搞小程序应用了

Telegram 宣布为其开发者提供了一项“能够在其中运行迷你应用”的新功能( 迷你应用即 Mini App,下文中以“小程序”代替)。 在 Telegram 的博客中,开发人员介绍可以使用 JavaScript 构建自己的迷你应用 在一篇博客文章中&#xf…

蓝桥杯 Java 青蛙过河

import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改/**二分法从大(n)到小找足够小的步长前缀和记录每个位置的前面有的总石头数(一个石头表示可以容纳一个青蛙,一位置有多少个石头hi就是多少)&…

2023/10/24 MySQL学习

列表查询 in的用法,括号里面跟的应该是具体值,可填多个 select 表中变量名 from 表名 where 变量名 like ___... 一个下划线代表一个字符,多个就是多个字符 %号是任意多字符 %号的用法 %任意字符串 代表查找以这个字符串结尾的变量名 %任意字符串% 查找以这个字符串在变…

PPT放映时显示安全警告:已阻止OLE操作。如果选择启用OLE操作,您的计算机可能不再安全。如果不信任此演示文稿的来源,请不要启用此内容

PPT放映时显示安全警告:已阻止OLE操作。如果选择启用OLE操作,您的计算机可能不再安全。如果不信任此演示文稿的来源,请不要启用此内容。 ⚙️1.软件环境⚙️🔍2.问题描述🔍🐡3.解决方法🐡&#…

MS5192T/MS5193T——低噪声、低功耗、16/24 位∑-ΔADC

MS5192T/MS5193T 是一款适合高精度测量应用的低功耗、低 噪声、三通道差分输入的 16bit/24bit 模数转换器。其内部集成了 输入缓冲器、低噪声仪表放大器,当增益设置为 64 ,更新速率为 4.17Hz 时,均方根噪声为 25nV 。集成了精密低噪声、低漂移…

ITSource 分享 第3期【在线个人网盘】

项目介绍 本期给大家介绍一个在线个人网盘 系统. 可以上传,下载,分享文件。 一 业务介绍 本系统分为以下几个模块: 1.登录注册 除了账号密码登录,如果配置了qq邮箱配置的话,还支持qq一键授权登录。 2.首页大盘 首页是个人网盘…