uCosIII之消息队列与内存管理使用心得
消息队列简介
顾名思义,消息队列是由消息按照一定的队列规则组成的,而消息包含了一个指向数据的指针、该数据的大小、时间戳变量,是任务间数据传递的重要手段。
消息队列中存放了等待该消息的任务。多个任务可以在消息队列中等待消息,当一个消息被发送到消息队列时,等待该消息的高优先级任务将接收这个消息。
官方提供的消息队列示意图:
由示意图我们可以看到,我们通过创建消息队列,然后在需要进行数据传输的任务间通过OSQPost和OSQPend往消息队列中发送消息或者从消息队列中获取消息(中断中只能发)来实现数据交互。消息的发送支持FIFO (先入先出),LIFO(后入先出)两种模式,我们可以根据数据的实时性要求来适当配置。
内存管理简介
uCos的内存管理方法与我们平时的malloc和free管理不太一样,uCos采用的是将连续的大块存储空间进行分区管理,每个分区中包含整数个大小相同的存储块,应用程序获得的是固定大小的存储块,这样一来分配和释放存储块花费的时间是固定可知的常数,比较适用于嵌入式实时操作系统。图解如下:
因此我们只需要创建好我们的内存分区,再设置好我们需要的内存块的大小和数量等信息就可以让uCos来管理我们的内存分区了,下面是创建好的内存分区的示例图:
消息队列与内存管理使用实例
我们通过uCos的消息队列和内存管理来实现一个数据发送和接收的任务,此实例通过一个任务将数据整合(获取内存块)到发送队列中并负责处理接收信息,另一个任务负责将整合的数据(即消息队列中的数据)发送出去并释放内存块。
示例代码如下:
#define TX_MSG_MEM_BLK_NUM 30 //内存分区的内存块数量
#define TX_MSG_MEM_BLK_SIZE 60 //内存分区的内存块大小
#define TX_MSG_QUEUE_SIZE 30 //发送消息队列长度
OS_Q CommInfoQueue;
OS_MEM CommInfoMem;
OS_SEM g_stCommDataSendSem;
//数据整合函数指针数组
void (*add_info_fun)(void);
void (*add_info_fun_list[3])(void) =
{
AddRealTimeWorkInfoPartA1ToSendQueue,
AddRealTimeWorkInfoPartB1ToSendQueue,
AddRealTimeWorkInfoPartB2ToSendQueue,
};
/*
***************************************************************************************************
* CommunicateTaskCreate()
*
* Description : The use of the the funciton is to create the task that manager the communication.
*
* Arguments : none.
*
* Returns : none
*
* Notes : none
***************************************************************************************************
*/
void CommTaskCreate(void)
{
OS_ERR err;
OSTaskCreate((OS_TCB *)&CommTaskTCB,
(CPU_CHAR *)"Comm Task Start",
(OS_TASK_PTR) CommTask,
(void *) 0,
(OS_PRIO) COMM_TASK_PRIO,
(CPU_STK *)&CommTaskStk[0],
(CPU_STK_SIZE) COMM_TASK_STK_SIZE / 10,
(CPU_STK_SIZE) COMM_TASK_STK_SIZE,
(OS_MSG_QTY) 5u,
(OS_TICK) 0u,
(void *) 0,
(OS_OPT)(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
APP_TRACE_INFO(("Created communicate Task, and err code is %d...\r\n", err));
OSSemCreate(&g_stCommDataSendSem,"Comm data send sem",0,&err);
OSQCreate((OS_Q *)&CommInfoQueue,
(CPU_CHAR *)"Comm Info Queue",
(OS_MSG_QTY)TX_MSG_QUEUE_SIZE,
(OS_ERR *)&err);
OSMemCreate((OS_MEM *)&CommInfoMem,
(CPU_CHAR *)"Comm Info Mem",
(void *)&CommInfoMemStorage[0][0],//内存分区起始地址
(OS_MEM_QTY )TX_MSG_MEM_BLK_NUM,//内存分区里的内存块数量
(OS_MEM_SIZE )TX_MSG_MEM_BLK_SIZE,//每个内存块的大小(字节)
(OS_ERR *)&err);
}
void CommTask(void *p_arg)
{
OS_ERR err;
while(DEF_TRUE) {
OSTimeDlyHMSM(0, 0, 1, 000,OS_OPT_TIME_HMSM_STRICT,&err);
if(g_u8WifiCmdRec == DEF_YES) { //响应上位机指令
ResponsePrgmCmd(g_u8SerRxMsgBuff);
g_u8WifiCmdRec = DEF_NO;
}
//添加实时信息到发送队列
for(uint8_t j = 0;j < 3 ;j++){
add_info_fun = add_info_fun_list[j];
add_info_fun();
}
//发送信号量,启动一次数据传输
OSSemPost(&g_stCommDataSendSem,OS_OPT_POST_1,&err);
}
}
/*
***************************************************************************************************
* CommDataSendTask()
*
* Description :Data send task.
*
* Argument(s) : none.
*
* Return(s) : none.
*
* Note(s) : none.
***************************************************************************************************
*/
static void CommDataSendTask(void *p_arg)
{
OS_ERR err;
OS_MSG_SIZE msg_size;
CPU_INT08U *pTxMsgData = NULL;
while(DEF_TRUE) {
pTxMsgData = OSQPend((OS_Q *)&CommInfoQueue,
(OS_TICK )OS_CFG_TICK_RATE_HZ / 5,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE *)&msg_size,
(CPU_TS *)NULL,
(OS_ERR *)&err);
if(err == OS_ERR_NONE){
if(pTxMsgData != NULL){
SendPrgmMsgFrame(msg_size, pTxMsgData);//发送数据
OSMemPut(&CommInfoMem,pTxMsgData,&err);
}
}
}
}
static void AddRealTimeWorkInfoPartA1ToSendQueue(void)
{
OS_ERR err;
CPU_INT08U *pTxBlkPtr;
pTxBlkPtr = OSMemGet(&CommInfoMem,&err);
if(err == OS_ERR_NONE){
//报头段
*(pTxBlkPtr + HEAD_BYTE_ONE) = 0xF1;
*(pTxBlkPtr + HEAD_BYTE_TWO) = 0xF2;
*(pTxBlkPtr + HEAD_BYTE_THREE) = 0xF3;
/*添加发送数据...*/
//报尾段
*(pTxBlkPtr + END_BYTE_ONE) = 0x5F;
*(pTxBlkPtr + END_BYTE_TWO) = 0x6F;
OSQPost(&CommInfoQueue,
pTxBlkPtr,
TX_MSG_MEM_BLK_SIZE,
OS_OPT_POST_FIFO,
&err);
if(err != OS_ERR_NONE){
APP_TRACE_INFO(("- ->RT work info part_a1 msg post err,err code is %d\n\r",err));
}
}else{
APP_TRACE_INFO(("- ->RT work info part_a1 mem blk get err,err code is %d\n\r",err));
}
}
数据添加部分的代码由于篇幅原因我就不一一列出来了,我们可以在往队列中添加数据的时候根据我们的需求设置我们的数据为FIFO或者LIFO。这里要注意的是我们的内存分区大小与数量一定要根据我们的数据量来进行合理分配,太大就浪费空间,太小会导致我们的内存块数量不够用。