【LiteOS】LiteOS消息队列使用总结

LiteOS针对队列读写提供带拷贝和不带拷贝的两种方式,这两种方式在使用上有所区别,如不注意就会入坑,故此总结一下。
消息队列,是一种常用于任务间通信的数据结构,实现了接收来自任务或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在自己空间。队列提供一部处理机制,可以起到缓存消息的作用。

队列创建

LiteOS创建队列的函数如下:

UINT32 LOS_QueueCreate(CHAR *pcQueueName,
                             UINT16 usLen,
                             UINT32 *puwQueueID,
                             UINT32 uwFlags,
                             UINT16 usMaxMsgSize);
//usLen    队列长度(消息数量),即队列可以存多少条消息
//uwFlags  队列类型 FIFO or PRIO,暂时保留未用
//usMaxMsgSize  消息最大字节数(消息长度)

注意: LOS_QueueCreate 会根据传入的队列长度、消息长度来动态申请内存空间,用于缓存用户写入队列的数据。usMaxMsgSize 队列消息的最大长度,LiteOS实际在申请空间时,会将该值再加上4,这多出来的4字节空间用于保存消息的实际长度。

UINT16          usMsgSize = usMaxMsgSize + sizeof(UINT32);

不带拷贝读写方式

不带拷贝的读写方式,本质上是将消息的地址存入消息队列中,即4字节的指针。
但是:不带拷贝的读写方式,不会保存消息的长度,即用户是无法传递不定长的消息的。这块应该是LiteOS的一个bug,原则上也是可以保存的,需要修改下源码

不带拷贝的读写函数接口如下:

UINT32 LOS_QueueWrite(UINT32 uwQueueID, 
                        VOID *pBufferAddr, 
                        UINT32 uwBufferSize, UINT32 uwTimeOut)
//pBufferAddr 要写入的数据起始地址
//uwBufferSize 写入长度,在不带拷贝模式下,该值在代码中被固定修改为4字节(指针长度)
//uwTimeOut 超时时间. 0表示不阻塞,最大值为 LOS_WAIT_FOREVER 

UINT32 LOS_QueueRead(UINT32  uwQueueID, 
                        VOID *pBufferAddr, 
                        UINT32 uwBufferSize, UINT32 uwTimeOut)
//pBufferAddr 存储获取到的数据地址(读取的数据要保存的目的位置)
//uwBufferSize 缓冲区长度,大于等于创建队列时的最大消息长度,尽量传入最大值

注意: 不带拷贝的读取函数,需要传入缓存长度uwBufferSize,该值必须初始化为一个大于等于队列最大消息长度的值

带拷贝的读写方式

带拷贝的读写方式,就将指定地址的消息数据全部存入消息队列中,同时队列中也会保存消息的实际长度。
带拷贝的读写函数接口如下:

//带拷贝的写入
UINT32 LOS_QueueWriteCopy(UINT32 uwQueueID,
                            VOID *pBufferAddr,
                            UINT32 uwBufferSize,
                            UINT32 uwTimeOut);
//带拷贝的读取
UINT32 LOS_QueueReadCopy(UINT32 uwQueueID,
                                VOID *pBufferAddr,
                                UINT32 *puwBufferSize,
                                UINT32 uwTimeOut);
// pBufferAddr 缓存地址
// puwBufferSize 通过该指针返回读取到的消息长度。
// 使用前,该变量需要初始为大于等于队列最大消息长度的值(队列创建时指定)
// 该值可以理解为我们将要存储消息的缓存长度,它必须是能够放得下最大长度的消息
//读取结束后,该变量保存之际读取到的消息长度

注意: 带拷贝的读取函数,需要传入指针变量puwBufferSize,函数会通过该指针返回实际读取到的消息长度。但是该指针指向的值,在使用前必须初始化为一个大于等于队列最大消息长度的值
按照如下方式使用队列读取函数:

UINT32 rxLen = 10; //rxLen的初始必须大于队列消息最大长度
uwRet = LOS_QueueReadCopy(QueID,buffer,&rxLen,0);
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于这个实验涉及到多个任务和多个功能模块,代码会比较长,这里只提供一个简单的示例代码,供您参考和理解。 ``` #include "los_sys.h" #include "los_sem.h" #include "los_swtmr.h" #include "los_queue.h" #define LIGHT_NUM 4 #define LIGHT_PERIOD 2000 // 光强周期变化时间,单位毫秒 #define LIGHT_THRESHOLD 50 // 光强阈值,低于该值则开灯,高于该值则关灯 #define LIGHT_DELAY 2000 // 路灯开关延时,单位毫秒 static UINT32 g_lightIntensity = 0; // 光强值 static UINT32 g_lightStatus[LIGHT_NUM] = {0}; // 路灯状态,0表示关灯,1表示开灯 static UINT32 g_lightDelay[LIGHT_NUM] = {0}; // 路灯开关延时,单位毫秒 static UINT32 g_lightSemId = 0; // 光强信号量 static UINT32 g_lightSwtmrId = 0; // 光强定时器 static UINT32 g_lightQueueId = 0; // 路灯消息队列 static VOID LightTask(UINT32 lightId) { while (1) { UINT32 intensity; LOS_SemPend(g_lightSemId, LOS_WAIT_FOREVER); // 获取光强信号量 intensity = g_lightIntensity; // 获取光强值 LOS_SemPost(g_lightSemId); // 释放光强信号量 if (intensity < LIGHT_THRESHOLD) { // 光强低于阈值,开灯 if (g_lightStatus[lightId] == 0) { // 路灯当前为关灯状态 g_lightStatus[lightId] = 1; // 开灯 LOS_QueueWrite(g_lightQueueId, &lightId, sizeof(UINT32), LOS_WAIT_FOREVER); // 发送消息 LOS_TaskDelay(g_lightDelay[lightId]); // 延时 } } else { // 光强高于阈值,关灯 if (g_lightStatus[lightId] == 1) { // 路灯当前为开灯状态 g_lightStatus[lightId] = 0; // 关灯 LOS_QueueWrite(g_lightQueueId, &lightId, sizeof(UINT32), LOS_WAIT_FOREVER); // 发送消息 LOS_TaskDelay(g_lightDelay[lightId]); // 延时 } } } } static VOID LightTimer(VOID) { UINT32 i; UINT32 intensity = 0; // 模拟光强值周期性变化 intensity = (UINT32)(100 * sin((double)LOS_TickCountGet() / LIGHT_PERIOD) + 100); LOS_SemPend(g_lightSemId, LOS_WAIT_FOREVER); // 获取光强信号量 g_lightIntensity = intensity; // 更新光强值 LOS_SemPost(g_lightSemId); // 释放光强信号量 } static VOID LightManagerTask(VOID) { UINT32 lightId; while (1) { LOS_QueueRead(g_lightQueueId, &lightId, sizeof(UINT32), LOS_WAIT_FOREVER); // 接收消息 if (g_lightStatus[lightId] == 0) { // 路灯关灯 printf("Light %d is turned off.\n", lightId); } else { // 路灯开灯 printf("Light %d is turned on.\n", lightId); } } } UINT32 AppMain(VOID) { UINT32 i; UINT32 uwRet = LOS_OK; // 创建光强信号量 uwRet = LOS_SemCreate(1, &g_lightSemId); if (uwRet != LOS_OK) { return LOS_NOK; } // 创建光强定时器 uwRet = LOS_SwtmrCreate(LIGHT_PERIOD / LOS_MS_PER_TICK, LOS_SWTMR_MODE_PERIOD, LightTimer, &g_lightSwtmrId, 0); if (uwRet != LOS_OK) { return LOS_NOK; } // 创建路灯消息队列 uwRet = LOS_QueueCreate("LightQueue", LIGHT_NUM, sizeof(UINT32), &g_lightQueueId, 0); if (uwRet != LOS_OK) { return LOS_NOK; } // 创建路灯控制任务 for (i = 0; i < LIGHT_NUM; i++) { uwRet = LOS_TaskCreate(LightTask, &i, NULL, 0, 0, NULL); if (uwRet != LOS_OK) { return LOS_NOK; } } // 创建路灯管理任务 uwRet = LOS_TaskCreate(LightManagerTask, NULL, NULL, 0, 0, NULL); if (uwRet != LOS_OK) { return LOS_NOK; } // 启动光强定时器 uwRet = LOS_SwtmrStart(g_lightSwtmrId); if (uwRet != LOS_OK) { return LOS_NOK; } // 启动LiteOS内核 LOS_Start(); return LOS_OK; } ``` 这个示例代码中,使用了 `LOS_SemCreate` 创建光强信号量,`LOS_SwtmrCreate` 创建光强定时器,`LOS_QueueCreate` 创建路灯消息队列,`LOS_TaskCreate` 创建路灯控制任务和路灯管理任务等函数。任务之间的通信和同步使用了光强信号量和路灯消息队列。定时器模拟了光强值的周期性变化,路灯控制任务根据光强值的变化来控制路灯的开关,并通过消息队列向路灯管理任务发送消息。路灯管理任务接收到消息后,打印相应的提示信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值