基于STM32F103移植华为LiteOS_消息队列之任务间通信
一、消息队列的一些专业描述
消息队列基本概念: 队列又称消息队列,是一种常用于任务间通信的数据结构,实现了接收来自任务或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在自己空间。任务能够从队列里面读取消息,当队列中的消息是空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。 用户在处理业务时,消息队列提供了异步处理机制,允许将一个消息放入队列,但并不立即处理它,同时队列还能起到缓冲消息作用。 Huawei LiteOS中使用队列数据结构实现任务异步通信工作,具有如下特性: ? 消息以先进先出方式排队,支持异步读写工作方式。 ? 读队列和写队列都支持超时机制。 ? 发送消息类型由通信双方约定,可以允许不同长度(不超过队列节点最大值)消息。 ? 一个任务能够从任意一个消息队列接收和发送消息。 ? 多个任务能够从同一个消息队列接收和发送消息。 ? 当队列使用结束后,如果是动态申请的内存,需要通过释放内存函数回收。
消息队列运作原理 创建队列时,根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用,返回队列ID。
在队列控制块中维护一个消息头节点位置Head和一个消息尾节点位置Tail来表示当前队列中消息存储情况。Head表示队列中被占用消息的起始位置。Tail表示队列中被空闲消息的起始位置。刚创建时Head和Tail均指向队列起始位置。
写队列时,根据Tail找到被占用消息节点末尾的空闲节点作为数据写入对象。如果Tail已经指向队列尾则采用回卷方式。根据usWritableCnt判断队列是否可以写入,不能对已满(usWritableCnt为0)队列进行写队列操作。
读队列时,根据Head找到最先写入队列中的消息节点进行读取。如果Head已经指向队列尾则采用回卷方式。根据usReadableCnt判断队列是否有消息读取,对全部空闲(usReadableCnt为0)队列进行读队列操作会引起任务挂起。
删除队列时,根据传入的队列ID寻找到对应的队列,把队列状态置为未使用,释放原队列所占的空间,对应的队列控制头置为初始状态。 图1 队列读写数据操作示意图
二、我的描述
消息队列就是一个双向链表,它就像工厂的流水线。正常的情况下,从尾部进行添加各种加工材料,然后从头部产出成品。但在不正常的情况下,老板之间将材料扔进头部的加工材料直接产出成品。这两种情况也就分别对应先出原则(First In First Out ,FIFO) 和后进先出原则(Last In First Out,LIFO)。 这里我是怎么知道还有LIFO机制的呢?通过野火的LiteOS指南可以知道,但是这个指南没有告诉这个机制怎么用,哪里引出来的这个机制?这个我是通过华为官方提供的开发指南文档知道的,如下图是我在该指南文档中截取的关键信息。 一般来说,消息队列在队列头部写数据是在特殊的紧急情况才会去使用的,正常情况都会遵守FIFO原则。 从上图还可以知道,LiteOS 提供了两种消息的传输方式,一种是复制方式,另一种是引用方式,通过上文的类比,读者可以选择自己需要的消息传输方式。读者可以根据消息的大小与重要性来选择消息的传递方式,假如消息是很重要的话,选择复制的方式会更加安全,假如消息的数据量很小的话,也是可以选择复制的方式。假如消息只是一些不重要的内容或者消息数据量很大, 可以选择引用方式。 相关开发指南资料链接:Huawei LiteOS开发全家桶
三、野火的LiteOS开发指南的消息队列实验错误地方
大家要是使用的是野火的《物联网操作系统 LiteOS开发实战指南—基于STM32》这个学习的,想必也会遇到读写消息队列的消息是出错的。 大家是否记得,写入消息队列的消息不能大于定义的消息队列的消息节点大小,而读取消息队列的消息不能小于定义的消息队列的消息节点大小。 如下图是野火教程文档中的实验程序。 在此,大家想必已经明白原因了。
四、我的基于STM32F103的LiteOS之消息队列读写实验
这里我说一下,我的实验是基于之前编写的按键状态机进行的实验。后续会写更多相关的面向对象编程的架构实验。
#include "los_sys.h "
#include "los_task.ph"
#include "los_queue.h"
#include "led.h"
#include "usart1.h"
#include "key.h"
UINT32 LED1_Task_Handle;
UINT32 LED2_Task_Handle;
UINT32 Key_Task_Handle;
UINT32 Queue_Receive_Task_Handle;
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();
static UINT32 Create_LED2_Task();
static UINT32 Create_Key_Task();
static UINT32 Create_Queue_Receive_Task();
static void LED1_Task(void);
static void LED2_Task(void);
static void Key_Task(void);
static void Queue_Receive_Task(void);
int main(void)
{
UINT32 uwRet = LOS_OK;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Usart1_Config();
LED_GPIO_Config();
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");
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;
}
LOS_Start();
while(1){
GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
}
static UINT32 AppTaskCreate(void)
{
UINT32 uwRet = LOS_OK;
uwRet = LOS_QueueCreate("Test_Queue",
TEST_QUEUE_LEN,
&Test_Queue_Handle,
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;
}
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);
return uwRet;
}
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);
return uwRet;
}
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;
}
static UINT32 Create_Queue_Receive_Task() {
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;
}
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);
}
i++;
if(i>100) i=0;
LOS_TaskDelay(1000);
}
}
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);
}
i++;
if(i>100) i=0;
LOS_TaskDelay(500);
}
}
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");
}
}
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)) {
uwRet = LOS_QueueWrite(Test_Queue_Handle,
send_data1,
sizeof(send_data1),
0);
if (uwRet != LOS_OK) {
printf("消息不能写入到消息队列!错误代码 0X%X\r\n",uwRet);
}
}
if((trigger_state==KEY_CONFIRN_DOWN) && (i==KEY2)) {
uwRet = LOS_QueueWrite(Test_Queue_Handle,
send_data2,
sizeof(send_data1),
0);
if (uwRet != LOS_OK) {
printf("消息不能写入到消息队列!错误代码 0X%X\r\n",uwRet);
}
}
LOS_TaskDelay(20);
}
}
}
static void Queue_Receive_Task(void) {
UINT32 uwRet = LOS_OK;
UINT32 r_queue;
UINT32 buffsize = 20;
while(1) {
uwRet = LOS_QueueRead(Test_Queue_Handle,
&r_queue,
buffsize,
LOS_WAIT_FOREVER);
if(LOS_OK == uwRet) {
printf("接收到的消息队列消息是%s\r\n",(char *)r_queue);
} else {
printf("接收消息队列消息出错,错误代码%d\r\n",uwRet);
}
}
}
实验现象
|