基于STM32F103移植华为LiteOS_消息队列之任务间通信

基于STM32F103移植华为LiteOS_消息队列之任务间通信


一、消息队列的一些专业描述

消息队列基本概念:
队列又称消息队列,是一种常用于任务间通信的数据结构,实现了接收来自任务或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在自己空间。任务能够从队列里面读取消息,当队列中的消息是空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。
用户在处理业务时,消息队列提供了异步处理机制,允许将一个消息放入队列,但并不立即处理它,同时队列还能起到缓冲消息作用。
Huawei LiteOS中使用队列数据结构实现任务异步通信工作,具有如下特性:
• 消息以先进先出方式排队,支持异步读写工作方式。
• 读队列和写队列都支持超时机制。
• 发送消息类型由通信双方约定,可以允许不同长度(不超过队列节点最大值)消息。
• 一个任务能够从任意一个消息队列接收和发送消息。
• 多个任务能够从同一个消息队列接收和发送消息。
• 当队列使用结束后,如果是动态申请的内存,需要通过释放内存函数回收。

消息队列运作原理
创建队列时,根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用,返回队列ID。

在队列控制块中维护一个消息头节点位置Head和一个消息尾节点位置Tail来表示当前队列中消息存储情况。Head表示队列中被占用消息的起始位置。Tail表示队列中被空闲消息的起始位置。刚创建时Head和Tail均指向队列起始位置。

写队列时,根据Tail找到被占用消息节点末尾的空闲节点作为数据写入对象。如果Tail已经指向队列尾则采用回卷方式。根据usWritableCnt判断队列是否可以写入,不能对已满(usWritableCnt为0)队列进行写队列操作。

读队列时,根据Head找到最先写入队列中的消息节点进行读取。如果Head已经指向队列尾则采用回卷方式。根据usReadableCnt判断队列是否有消息读取,对全部空闲(usReadableCnt为0)队列进行读队列操作会引起任务挂起。

删除队列时,根据传入的队列ID寻找到对应的队列,把队列状态置为未使用,释放原队列所占的空间,对应的队列控制头置为初始状态。
图1 队列读写数据操作示意图
图1 队列读写数据操作示意图
在这里插入图片描述

二、我的描述

消息队列就是一个双向链表,它就像工厂的流水线。正常的情况下,从尾部进行添加各种加工材料,然后从头部产出成品。但在不正常的情况下,老板之间将材料扔进头部的加工材料直接产出成品。这两种情况也就分别对应先出原则(First In First Out ,FIFO) 和后进先出原则(Last In First Out,LIFO)。
这里我是怎么知道还有LIFO机制的呢?通过野火的LiteOS指南可以知道,但是这个指南没有告诉这个机制怎么用,哪里引出来的这个机制?这个我是通过华为官方提供的开发指南文档知道的,如下图是我在该指南文档中截取的关键信息。
在这里插入图片描述
一般来说,消息队列在队列头部写数据是在特殊的紧急情况才会去使用的,正常情况都会遵守FIFO原则。
从上图还可以知道,LiteOS 提供了两种消息的传输方式,一种是复制方式,另一种是引用方式,通过上文的类比,读者可以选择自己需要的消息传输方式。读者可以根据消息的大小与重要性来选择消息的传递方式,假如消息是很重要的话,选择复制的方式会更加安全,假如消息的数据量很小的话,也是可以选择复制的方式。假如消息只是一些不重要的内容或者消息数据量很大, 可以选择引用方式。
相关开发指南资料链接:Huawei LiteOS开发全家桶

三、野火的LiteOS开发指南的消息队列实验错误地方

大家要是使用的是野火的《物联网操作系统 LiteOS开发实战指南—基于STM32》这个学习的,想必也会遇到读写消息队列的消息是出错的。
大家是否记得,写入消息队列的消息不能大于定义的消息队列的消息节点大小,而读取消息队列的消息不能小于定义的消息队列的消息节点大小。
如下图是野火教程文档中的实验程序。
在这里插入图片描述
在这里插入图片描述
在此,大家想必已经明白原因了。

四、我的基于STM32F103的LiteOS之消息队列读写实验

这里我说一下,我的实验是基于之前编写的按键状态机进行的实验。后续会写更多相关的面向对象编程的架构实验。

/* LiteOS头文件 */
#include "los_sys.h "
#include "los_task.ph"
#include "los_queue.h"

/* 外设驱动头文件 */
#include "led.h"
#include "usart1.h"
#include "key.h"

/* 定义任务 ID 变量,也叫任务句柄 */
UINT32 LED1_Task_Handle;					//RED LED
UINT32 LED2_Task_Handle;					//GREEN LED
UINT32 Key_Task_Handle;						//KEY
UINT32 Queue_Receive_Task_Handle;	//消息队列接收

/* 定义消息队列的ID变量,消息队列句柄 */
UINT32 Test_Queue_Handle;

/* 定义消息队列长度 */
#define TEST_QUEUE_LEN			20	//队列中消息个数
#define TEST_QUEUE_SIZE			20	//消息节点大小

/* 定义全局变量 */
static CHAR send_data1[] = "djw";
static CHAR send_data2[] = "hyt";

/* 函数声明 */
static UINT32 AppTaskCreate();							//用于创建任务管理
static UINT32 Create_LED1_Task();						//创建任务1函数
static UINT32 Create_LED2_Task();						//创建任务2函数
static UINT32 Create_Key_Task();						//创建任务3函数
static UINT32 Create_Queue_Receive_Task();	//创建任务4函数

static void LED1_Task(void);					//任务1函数
static void LED2_Task(void);					//任务2函数
static void Key_Task(void);						//任务3函数
static void Queue_Receive_Task(void);	//任务4函数

int main(void)
{	
	UINT32 uwRet = LOS_OK; //定义一个任务创建的返回值,默认为创建成功
	
	/*中断优先级分组为 4*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	/* 板载相关初始化 */
	Usart1_Config();		//串口1
	LED_GPIO_Config();	//LED
	Key_Config_parameter();	//按键
	
	printf("KEYUP = %d\r\n",KEY_CLASS[KEYUP].Key_Init.GPIO_Pin_x);
	printf("KEY0 = %d\r\n",KEY_CLASS[KEY0].Key_Init.GPIO_Pin_x);
	printf("KEY1 = %d\r\n",KEY_CLASS[KEY1].Key_Init.GPIO_Pin_x);
	printf("KEY2 = %d\r\n",KEY_CLASS[KEY2].Key_Init.GPIO_Pin_x);

	printf("正点原子战舰开发板-LiteOS-SRAM 动态创建多任务!\r\n\r\n");
	
	/* LiteOS 内核初始化 */
	uwRet = LOS_KernelInit();
	if (uwRet != LOS_OK) {
		printf("LiteOS核心初始化失败!任务代码0X%X\r\n",uwRet);
		return LOS_NOK;
	}
	
	uwRet = AppTaskCreate();
	if (uwRet != LOS_OK) {
		printf("AppTaskCreate任务创建失败!任务代码0X%X\r\n",uwRet);
		return LOS_NOK;
	}
	
	/* 开启 LiteOS 任务调度 */
	LOS_Start();
	
	while(1){	//上面任务失败会进入此,LED关闭
		GPIO_SetBits(GPIOB, GPIO_Pin_5);
		GPIO_SetBits(GPIOE,GPIO_Pin_5);
	}
}

/**************************************************************************/
/******************************** 任务函数 ********************************/
/**************************************************************************/

/*******************************************************************
 * @ 函数名 : AppTaskCreate
 * @ 功能说明: 任务创建,为了方便管理,所有的任务创建函数都可以放在这个函数里面
 * @ 参数 : NULL
 * @ 返回值 : NULL
 *************************************************************/
static UINT32 AppTaskCreate(void)
{
	/* 定义一个返回类型变量,初始化为 LOS_OK */
	UINT32 uwRet = LOS_OK;

	/* 创建消息队列 */
	uwRet = LOS_QueueCreate("Test_Queue", 		/* 消息队列的名称 */
													TEST_QUEUE_LEN,			/* 消息队列的长度 */
													&Test_Queue_Handle,	/* 消息队列的 ID(句柄) */
													0,							/* 消息队列模式,官方暂时未使用 */
													TEST_QUEUE_SIZE);		/* 节点大小,单位为字节 */
	if (uwRet != LOS_OK) {
		printf("Test_Queue消息队列创建失败!失败代码 0X%X\r\n",uwRet);
		return uwRet;
	}	
	
	uwRet = Create_Queue_Receive_Task();
	if (uwRet != LOS_OK) {
		printf("Queue_Receive_Task 任务创建失败!失败代码0X%X\r\n",uwRet);
		return uwRet;
	}
	
	uwRet = Create_LED1_Task();
	if (uwRet != LOS_OK) {
		printf("LED1_Task任务创建失败!失败代码0X%X\r\n",uwRet);
		return uwRet;
	}
	
	uwRet = Create_LED2_Task();
	if (uwRet != LOS_OK) {
		printf("LED2_Task任务创建失败!失败代码0X%X\r\n",uwRet);
		return uwRet;
	}
	
	uwRet = Create_Key_Task();
	if (uwRet != LOS_OK) {
		printf("Key_Task 任务创建失败!失败代码0X%X\r\n",uwRet);
		return uwRet;
	}

	return LOS_OK;
}

/******************************************************************
 * @ 函数名 : Create_LED1_Task
 * @ 功能说明: 创建 LED1_Task 任务
 * @ 参数 :	NULL
 * @ 返回值 : NULL
 ******************************************************************/
static UINT32 Create_LED1_Task()
{
	//定义一个创建任务的返回类型,初始化为创建成功的返回值
	UINT32 uwRet = LOS_OK;
	
	//定义一个用于创建任务的参数结构体
	TSK_INIT_PARAM_S task_init_param;

	task_init_param.usTaskPrio = 6; /* 任务优先级,数值越小,优先级越高 */
	task_init_param.pcName = "LED1_Task";/* 任务名 */
	task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)LED1_Task;		//任务入口函数
	task_init_param.uwStackSize = 1024; /* 栈大小 */

	uwRet = LOS_TaskCreate(&LED1_Task_Handle, &task_init_param);	//LED1_Task_Handle任务ID
	return uwRet;
}

/******************************************************************
 * @ 函数名 : Create_LED1_Task
 * @ 功能说明: 创建 LED1_Task 任务
 * @ 参数 :	NULL
 * @ 返回值 : NULL
 ******************************************************************/
static UINT32 Create_LED2_Task()
{
	//定义一个创建任务的返回类型,初始化为创建成功的返回值
	UINT32 uwRet = LOS_OK;
	
	//定义一个用于创建任务的参数结构体
	TSK_INIT_PARAM_S task_init_param;

	task_init_param.usTaskPrio = 7; /* 任务优先级,数值越小,优先级越高 */
	task_init_param.pcName = "LED2_Task";/* 任务名 */
	task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)LED2_Task;		//任务入口函数
	task_init_param.uwStackSize = 1024; /* 栈大小 */

	uwRet = LOS_TaskCreate(&LED2_Task_Handle, &task_init_param);	//LED1_Task_Handle任务ID
	return uwRet;
}

/*******************************************************************
 * @ 函数名 : Create_Key_Task
 * @ 功能说明: 创建 Key_Task 任务
 * @ 参数 :	NULL
 * @ 返回值 : NULL
 ******************************************************************/
static UINT32 Create_Key_Task() {
	// 定义一个创建任务的返回类型,初始化为创建成功的返回值
	UINT32 uwRet = LOS_OK;
	TSK_INIT_PARAM_S task_init_param;

	task_init_param.usTaskPrio = 4; /* 任务优先级,数值越小,优先级越高 */
	task_init_param.pcName = "Key_Task"; /* 任务名*/
	task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Key_Task; //任务入口函数
	task_init_param.uwStackSize = 1024; /* 栈大小 */

	uwRet = LOS_TaskCreate(&Key_Task_Handle,&task_init_param);/*创建任务 */

 return uwRet;	
}

/*******************************************************************
 * @ 函数名 : Create_Queue_Receive_Task
 * @ 功能说明: 创建 Key_Task 任务
 * @ 参数 :	NULL
 * @ 返回值 : NULL
 ******************************************************************/
static UINT32 Create_Queue_Receive_Task() {
	//定义一个返回类型变量,初始化为 LOS_OK
	UINT32 uwRet = LOS_OK;	
	//定义一个用于创建任务的参数结构体
	TSK_INIT_PARAM_S task_init_param;
	
	task_init_param.usTaskPrio = 5; /* 任务优先级,数值越小,优先级越高 */
	task_init_param.pcName = "Queue_Receive_Task"; /* 任务名*/
	task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Queue_Receive_Task; //任务入口函数
	task_init_param.uwStackSize = 1024; /* 栈大小 */
	
	uwRet = LOS_TaskCreate(&Queue_Receive_Task_Handle,&task_init_param);/*创建任务 */

 return uwRet;	
}


/**************************************************************************/
/******************************** 任务函数 ********************************/
/**************************************************************************/

/******************************************************************
 * @ 函数名 : LED1_Task
 * @ 功能说明: LED1_Task 任务实现
 * @ 参数 : NULL
 * @ 返回值 : NULL
 *****************************************************************/
static void LED1_Task(void) {
	int i = 0;
	/* 任务都是一个无限循环,不能返回 */
	while(1) {
		if(i%2==0) {
			GPIO_ResetBits(GPIOB, GPIO_Pin_5);
		} else {
			GPIO_SetBits(GPIOB, GPIO_Pin_5);
		}
//		printf("任务1---1000ms\r\n");
		i++;
		if(i>100) i=0;
		LOS_TaskDelay(1000);
	}
}

/******************************************************************
 * @ 函数名 : LED2_Task
 * @ 功能说明: LED2_Task 任务实现
 * @ 参数 : NULL
 * @ 返回值 : NULL
 *****************************************************************/
static void LED2_Task(void) {
	int i = 0;
	/* 任务都是一个无限循环,不能返回 */
	while(1) {
		if(i%2==0) {
			GPIO_SetBits(GPIOE,GPIO_Pin_5);
		} else {
			GPIO_ResetBits(GPIOE,GPIO_Pin_5);
		}
//		printf("任务2---500ms\r\n");
		i++;
		if(i>100) i=0;
		LOS_TaskDelay(500);
	}
}

/******************************************************************
 * @ 函数名 : Key_Task
 * @ 功能说明: Key_Task 任务实现
 * @ 参数 : NULL
 * @ 返回值 : NULL
 *****************************************************************/
static void Key_Task(void) {
	UINT32 uwRet = LOS_OK;
	UINT8 i,trigger_state;		
	UINT32 j=0;	//是用来检测长按按下的扫描周期数的
	while(1) {
		Read_Key_State();	//启动状态机检测按键状态
		for(i=0;i<KEY_CLASS_NUM;i++) {
			trigger_state = KEY_CLASS[i].Key_State_Machine.Key_Event;	//获取按键触发状态
			
			if((trigger_state==KEY_CONFIRN_DOWN) && (i==KEYUP)) {
				printf("挂起LED_LED2_Task 任务!\r\n");
				uwRet = LOS_TaskSuspend(LED2_Task_Handle);
				if (uwRet == LOS_OK) {
					printf("挂起LED_LED2_Task 任务成功!\r\n");
				}
			}
			
			if((trigger_state==KEY_CONFIRN_DOWN) && (i==KEY0)) {
				printf("恢复 LED_LED2_Task 任务!\r\n");
				uwRet = LOS_TaskResume(LED2_Task_Handle);
				if (uwRet == LOS_OK) {
						printf("恢复 LED_LED2_Task 任务成功!\r\n");
				}
			}
			
			/* 如果为KEY1和长按事件和长按的周期不为0次,那么我就自减按下的周期数 */
			if((trigger_state==KEY_LONG_DOWN) && (i==KEY1) && (KEY_CLASS[i].Key_State_Machine.Key_Count>=0)) {
				KEY_CLASS[i].Key_State_Machine.Key_Count--;
				++j;
				printf("count = %d\r\n",j);
			} else if((trigger_state==KEY_UP) && (i==KEY1)) {
				j = 0;
			}
			
			/***************** 发送消息到消息队列 *****************/
			if((trigger_state==KEY_CONFIRN_DOWN) && (i==KEY1)) {
				/* 将消息写入消息队列中,等待时间为0 */
				uwRet = LOS_QueueWrite(Test_Queue_Handle,	/* 消息队列的ID(句柄) */
															send_data1,				/* 写入消息队列的消息 */	
															sizeof(send_data1),	/* 写入消息队列的消息长度 */
															0);
				if (uwRet != LOS_OK) {
					printf("消息不能写入到消息队列!错误代码 0X%X\r\n",uwRet);
				}
			}
			
			if((trigger_state==KEY_CONFIRN_DOWN) && (i==KEY2)) {
				/* 将消息写入消息队列中,等待时间为0 */
				uwRet = LOS_QueueWrite(Test_Queue_Handle,	/* 消息队列的ID(句柄) */
															send_data2,				/* 写入消息队列的消息 */	
															sizeof(send_data1),	/* 写入消息队列的消息长度 */
															0);
			if (uwRet != LOS_OK) {
				printf("消息不能写入到消息队列!错误代码 0X%X\r\n",uwRet);
			}
		}
			
		LOS_TaskDelay(20);	
		}
	}
}

/******************************************************************
 * @ 函数名 : Queue_Receive_Task
 * @ 功能说明: 创建 Queue_Rec25ve_Task 任务
 * @ 参数 :	NULL
 * @ 返回值 : NULL
 ******************************************************************/
static void Queue_Receive_Task(void) {
	/* 定义一个返回类型变量,初始化为 LOS_OK */
	UINT32 uwRet = LOS_OK;
	UINT32 r_queue;	//接收消息队列数据缓存区
	UINT32 buffsize = 20;	//接收消息队列节点大小
	while(1) {
		/* 消息队列读取,等待时间为一直等待模式 */
		uwRet = LOS_QueueRead(Test_Queue_Handle,		/* 消息队列的ID(句柄) */
													&r_queue,							/* 接收消息队列信息的保存缓存位置 */
													buffsize,							/* 接收消息队列节点大小 */
													LOS_WAIT_FOREVER);		/* 等待时间,一直等待 */
		if(LOS_OK == uwRet) {
			printf("接收到的消息队列消息是%s\r\n",(char *)r_queue);
		} else {
			printf("接收消息队列消息出错,错误代码%d\r\n",uwRet);
		}
	}
}



实验现象

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邓家文007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值