当前位置: 首页 > article >正文

STM32F407ZGT6-UCOSIII笔记10:消息传递--消息队列

UCOS III 中消息可以通过消息队列进行与几个任务的交流、任务间通信

 本文学习与程序编写基于 正点原子的 STM32F1 UCOS开发手册

文章提供测试代码讲解、完整工程下载、测试效果图

我这个实验设计的不太好,但消息队列代码运行没啥问题...将就看看  

目录

消息队列:

创建消息队列:

等待消息队列:

向消息队列发送消息:

实验目标解释:

定义消息队列与软件定时器:

创建消息队列:

目前各个文件任务:

#include "main.h"

#include "ComTask.h"

 #include "MessageTask.h"

  #include "CalculateTask.h"

 测试效果截图: 

 测试工程下载:

提示:


消息队列:

消息一般包含:指向数据的指针,表明数据长度的变量和记录消息发布时刻的时间戳,指
针指向的可以是一块数据区或者甚至是一个函数,消息的内容必须一直保持可见性,因为发布数据采用的是引用传递是指针传递而不是值传递,也就说,发布的数据本身不产生数据拷贝。

创建消息队列:

等待消息队列:

向消息队列发送消息:

实验目标解释:

ComTask计数自己运行次数,

将自己运行次数通过消息队列 Message_Msg 发送给MessageTask,

同时检测消息队列DATA_Msg总大小,剩余大小,

消息队列 DATA_Msg 总大小,剩余大小 ,

DATA_Msg  剩余大小会因为 软件定时器 的发送操作而 改变

Message_Msg获取消息队列的消息,根据消息的情况点亮LED并打印

定义消息队列与软件定时器:

在 #include "Public.h" 文件中定义:

#include "Public.h"

OS_TMR OS_Timer1_Periodic;   //OS系统 软件定时器 1 周期模式 

OS_Q Message_Msg; //定义一共消息队列,用于comtask消息传递,模拟消息邮箱
OS_Q DATA_Msg;    //定义一个消息队列,用于发数据
#ifndef __Public_H
#define __Public_H

#include "headfile.h"

extern OS_TMR OS_Timer1_Periodic;   //OS系统 软件定时器 1 周期模式 


#define Message_Msg_NUM 1  // comtask消息队列数量
#define DATA_Msg_NUM 4     //  发数据消息队列数量
extern OS_Q Message_Msg; //定义一共消息队列,用于comtask消息传递,模拟消息邮箱
extern OS_Q DATA_Msg;    //定义一个消息队列,用于发数据

创建消息队列:

在 #include "main.h" 文件中的 void start_task(void *p_arg) 函数内进行创建:

代码如下:

//创建消息队列 Message_Msg 							
	OSQCreate( (OS_Q *) &Message_Msg,//消息队列 Message_Msg
						 (CPU_CHAR	* )"Message_Msg", //名称
						 (OS_MSG_QTY ) Message_Msg_NUM, //消息队列长度
						 (OS_ERR *)	 &err   
							);
//创建消息队列 DATA_Msg							
	OSQCreate( (OS_Q *) &DATA_Msg,//消息队列 DATA_Msg
						 (CPU_CHAR	* )"DATA_Msg", //名称
						 (OS_MSG_QTY ) DATA_Msg_NUM, //消息队列长度
						 (OS_ERR *)	 &err   
							);							

目前各个文件任务:

#include "main.h"

创建开始任务,初始化消息队列,初始化各个任务,初始化软件定时器

#include "main.h"

void start_task(void *p_arg);//开始任务函数


int main(void)
{
	OS_ERR err;
	CPU_SR_ALLOC();
  Init_ALL();
	OSInit(&err);		//初始化UCOSIII
	
	OS_CRITICAL_ENTER();//进入临界区
	//创建开始任务
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任务控制块
				 (CPU_CHAR	* )"start task", 		//任务名字
                 (OS_TASK_PTR )start_task, 			//任务函数
                 (void		* )0,					//传递给任务函数的参数
                 (OS_PRIO	  )START_TASK_PRIO,     //任务优先级
                 (CPU_STK   * )&START_TASK_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)START_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 	* )&err);				//存放该函数错误时的返回值
	OS_CRITICAL_EXIT();	//退出临界区	 
	OSStart(&err);  //开启UCOSIII

	while(1);
}

//开始任务函数
void start_task(void *p_arg)
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;

	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	//统计任务                
#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,&err);  
#endif		
	//初始化 OS_Timer1_Periodic 软件定时器
	OSTmrCreate((OS_TMR *) &OS_Timer1_Periodic ,   //OS系统 软件定时器 1 周期模式 
							(CPU_CHAR *) "OS_Timer1_Periodic", //定时器名称
							(OS_TICK ) 100,  // 启动延时 为100*10 ms
							(OS_TICK ) 20,   // 周期为 20*10 ms
							(OS_OPT  ) OS_OPT_TMR_PERIODIC, //周期定时模式
							(OS_TMR_CALLBACK_PTR ) OS_Timer1_Periodic_callback,//回调函数
							(void * )  0,//参数为0
							(OS_ERR *) &err		
							);
	
	OS_CRITICAL_ENTER();	//进入临界区
	
//创建消息队列 Message_Msg 							
	OSQCreate( (OS_Q *) &Message_Msg,//消息队列 Message_Msg
						 (CPU_CHAR	* )"Message_Msg", //名称
						 (OS_MSG_QTY ) Message_Msg_NUM, //消息队列长度
						 (OS_ERR *)	 &err   
							);
//创建消息队列 DATA_Msg							
	OSQCreate( (OS_Q *) &DATA_Msg,//消息队列 DATA_Msg
						 (CPU_CHAR	* )"DATA_Msg", //名称
						 (OS_MSG_QTY ) DATA_Msg_NUM, //消息队列长度
						 (OS_ERR *)	 &err   
							);							
							
	//创建ComTask任务
	OSTaskCreate((OS_TCB 	* )&COMTASKTaskTCB,		
				 (CPU_CHAR	* )"com task", 		
                 (OS_TASK_PTR )comTask, 			
                 (void		* )0,					
                 (OS_PRIO	  )COMTASK_TASK_PRIO,     
                 (CPU_STK   * )&COMTASK_TASK_STK[0],	
                 (CPU_STK_SIZE)COMTASK_STK_SIZE/10,	
                 (CPU_STK_SIZE)COMTASK_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,					//之前为0 //2个时间片 2*5ms
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);				
				 
	//创建MessageTask任务
	OSTaskCreate((OS_TCB 	* )&MessageTaskTaskTCB,		
				 (CPU_CHAR	* )"Message task", 		
                 (OS_TASK_PTR )MessageTask, 			
                 (void		* )0,					
                 (OS_PRIO	  )MessageTask_TASK_PRIO,     	
                 (CPU_STK   * )&MessageTask_TASK_STK[0],	
                 (CPU_STK_SIZE)MessageTask_STK_SIZE/10,	
                 (CPU_STK_SIZE)MessageTask_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,					//之前为0 //2个时间片 2*5ms
                 (void   	* )0,				
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 
                 (OS_ERR 	* )&err);
				 
	//创建CalculateTask任务
	OSTaskCreate((OS_TCB 	* )&CalculateTaskTaskTCB,		
				 (CPU_CHAR	* )"Calculate task", 		
                 (OS_TASK_PTR )CalculateTask,	
                 (void		* )0,					
                 (OS_PRIO	  )CalculateTask_TASK_PRIO,     	
                 (CPU_STK   * )&CalculateTask_TASK_STK[0],	
                 (CPU_STK_SIZE)CalculateTask_STK_SIZE/10,	
                 (CPU_STK_SIZE)CalculateTask_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,					
                 (void   	* )0,				
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 
                 (OS_ERR 	* )&err);				 
								 
		//创建 Shared_Str共享全局资源 的 信号量							
		OSSemCreate((OS_SEM *) &SEM_Shared_Str, //指向信号量
								 (CPU_CHAR *) "SEM_Shared_Str", //信号量名称
								  (	OS_SEM_CTR ) 1,     //信号量值为1
								  (OS_ERR *)  &err
								 );		
								 
		//创建 Shared_Str共享全局资源 的 互斥信号量							
		OSMutexCreate((OS_MUTEX *) &MUTEX_Shared_Str, //指向互斥信号量
								 (CPU_CHAR *) "MUTEX_Shared_Str", //互斥信号量名称
								  (OS_ERR *)  &err
								 );
								 
								 
	OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err);		//挂起开始任务			 
	OS_CRITICAL_EXIT();	//进入临界区
}

#include "ComTask.h"

#include "ComTask.h"

/*
	ComTask 
	打印系统节拍频率 删除CalculateTask 

	ComTask 计数打印自己运行次数
	发送 Message_Msg 消息
	
	打印 消息队列 DATA_Msg 总大小
	计算 打印 DATA_Msg 剩余大小
	
	
	延时50ms
*/

void comTask(void * p_arg)
{
	OS_ERR err;
	int i=0,OSTime_tickRate;
	char msgq_remain_size;//消息队列剩余大小	
	
	//char ComTask_str[]="ComTask_write";
	p_arg = p_arg;
	OSTime_tickRate=OSCfg_TickRate_Hz;            //获取系统节拍频率,(该宏定义在 OS_CFG_APP.H)
	UsartPrintf(USART1, "OSCfg_TickRate_Hz = %d Hz \r\n",OSTime_tickRate);  //打印系统节拍频率
	OSTaskDel((OS_TCB*)&CalculateTaskTaskTCB,&err);      //删除CalculateTask 
	UsartPrintf(USART1, "ComTask delete CalculateTask !\r\n");	//打印删除 CalculateTask 提示
	
	while (DEF_TRUE)
	{
		i++;
		
		UsartPrintf(USART1, "ComTask Print %d\r\n",i);	//打印 ComTask 运行次数
		//发送Message_Msg 消息
			OSQPost((OS_Q *) &Message_Msg, 
					(void *) i,
					(OS_MSG_SIZE ) 1,
					(OS_OPT ) OS_OPT_POST_FIFO,
					(OS_ERR *) &err 
					);
					
		msgq_remain_size =DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries; //计算消息队列 DATA_Msg 剩余大小
	  UsartPrintf(USART1, "DATA_Msg Total Size %d \r\n",DATA_Msg.MsgQ.NbrEntriesSize);	//打印 消息队列 DATA_Msg 总大小
	  UsartPrintf(USART1, "DATA_Msg Remain Size %d \r\n",msgq_remain_size);	//打印 消息队列 DATA_Msg 剩余大小		
					
		OSTimeDlyHMSM(0,0,0,50,OS_OPT_TIME_HMSM_STRICT,&err); //延时50ms
	}
}




 #include "MessageTask.h"

#include "MessageTask.h"

/*
	MessageTask
	计数打印一次运行次数
	请求 Message_Msg 消息 
	根据 消息情况 点灯与打印灯颜色
	延时1000 ms 
*/
void MessageTask (void * p_arg)
{
	OS_ERR err;
	int i=0;
	char *KEY;
	OS_MSG_SIZE size;
	
	//char MessageTask_str[]="MessageTask_write";
	
	p_arg = p_arg;
	while (DEF_TRUE)
	{
		i++;	
		UsartPrintf(USART1, "MessageTask Print %d\r\n",i);	//打印 MessageTask 运行次数
		//请求 Message_Msg 
		KEY=OSQPend((OS_Q *) &Message_Msg, 
								(OS_TICK ) 0,
								(OS_OPT ) OS_OPT_PEND_BLOCKING,	
								(OS_MSG_SIZE *)&size,
								(CPU_TS *) 0,
								(OS_ERR *) &err 
								);
		if(KEY[0] % 2==0)
		{
			LED_Blue_L;LED_GREEN_H;
			OSTmrStart(&OS_Timer1_Periodic ,&err); //启动软件 定时器 OS_Timer1_Periodic
			UsartPrintf(USART1, "MessageTask Print BLUE\r\n",i);	//打印 灯
			
		}
		else if(KEY[0] % 2 !=0)
		{
			LED_Blue_H;LED_GREEN_L;
			UsartPrintf(USART1, "MessageTask Print GREEN\r\n",i);	//打印 灯			
		}
		
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);                                //延时1000ms				
	}
	
}

  #include "CalculateTask.h"

本文此任务开头就被删除,不做解释

#include "Public.h"

软件定时器任务:打印运行提示信息,发送消息 DATA_Msg  以改变DATA_Msg 的剩余大小

(因为这个消息没有消费者 及时请求 它 !)


// OS_Timer1_Periodic 定时器回调函数 
// 打印提示信息
void OS_Timer1_Periodic_callback(void *p_tmr,void *p_arg)
{
	OS_ERR err;
	char *pbuf;
	static int Timer1_count=0;
	Timer1_count++;
	//打印中断次数
	UsartPrintf(USART1, "OS_Timer1_Periodic Print %d\r\n",Timer1_count);

	//发送DATA_Msg 消息
	OSQPost((OS_Q *) &DATA_Msg, 
					(void *) pbuf,
					(OS_MSG_SIZE ) 10,
					(OS_OPT ) OS_OPT_POST_FIFO,
					(OS_ERR *) &err 
					);
					
}

 测试效果截图: 

我这个实验设计的不太好,但消息队列代码运行没啥问题...将就看看

刚上电,定时器 OS_Timer1_Periodic 没有启动,所以DATA_Msg的 总大小与剩余大小都是4

随后执行到启动定时器时:

因为定时器发送的 DATA_Msg 消息并没有消费者及时来请求

所以它的队列空间就会一直减少:

MessageTask根据情况亮灯:

我们还发现,即使Comtask发送消息频次很快,但MessageTask还是1000ms(MessageTask的延时周期)响应一次

 测试工程下载:

 工程包含一份程序设计框架说明,不明白可以看看:

https://download.csdn.net/download/qq_64257614/90154608

提示:

如果 软件定时器 因为变量语句太多被卡主了,就在这个文件更改它的栈大小,改大些:


http://www.kler.cn/a/448168.html

相关文章:

  • 在安卓Android应用中实现二维码图像的保存与条形码文本合并
  • springboot460实习生管理系统设计和实现(论文+源码)_kaic
  • 【游戏设计原理】20 - 囚徒困境
  • 海外外卖APP开发新方向:基于同城外卖系统源码的多元化解决方案
  • 企业数字化转型和人工智能(AI)之间的关系
  • 【HarmonyOS之旅】DevEco Studio的安装与环境配置
  • MMAudio - 自动给视频配音效
  • 利用Python爬虫获取商品历史价格信息:技术与实践
  • H264编解码标准码流分析:I帧、P帧、B帧语法
  • 【深入解析C#第四版】读书笔记1:C# 特性之【类型系统】
  • 探索JavaScript数组API:提升你的编程效率
  • 探究大模型为何因数据增多而效果更佳及其优势
  • D102【python 接口自动化学习】- pytest进阶之fixture用法
  • 十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
  • Linux 网络维护相关命令简介
  • 从入门到实战:基于 UniApp 的全场景开发实践
  • SMMU软件指南SMMU编程之命令队列
  • 数字逻辑(五)——用二进制来表示音频和视频
  • 微服务详细教程之nacos和sentinel实战
  • 机器学习(三)-多项式线性回归
  • 深入了解Java在人工智能领域的最新应用
  • 关系型数据库的完整性和一致性
  • C++初阶—类与对象(下篇)
  • 基于Socket实现客户端和服务端的Tcp通信(C#)
  • 18个AI变现案例分享,每一个都可以作为副业赚睡后收入!
  • Vue.js前端框架教程9:Vue插槽slot用法