uTenux的消息缓冲区是一个通过传递大小可变的消息来实现同步和通信的对象。
消息缓冲区由三部分组成:1、待发消息队列 2、等接收消息的任务队列 3、用来保存缓冲消息的空间。
和信号相比,消息队列能够传递更多的信息。与管道相比,消息队列提供了有格式的数据,这可以减少开发人员的工作量。但消息队列仍然有大小限制。
在uTenux中当缓冲区空时,接收消息的任务进入等待状态。当缓冲区满时,发送消息的任务进入等待状态。
每个消息只能接收一次,消息被接收接收后消息从消息缓冲区中删除。
使用tk_snd_mbf发送消息到消息缓冲区时,由于发送的消息字提供了VP型的消息头指针和消息长度。接收数据时,只有OS知道数据的长度。至于数据内部结构谁都不知道,因此必须消息的收发双方必须事先约定好消息类型,接收方根据消息数据的类型准备接收到消息的存放空间。关于VP指针(void*)前文已详诉,这里就不唠叨了。关于VP类型,只需要记住一点即可:
Void类型的指针是无类型的指针,可以指向任何结构。因为它就是一个指针!
在引用这个指针时候,需要预先知道这个指针指向地址的结构。不然编译器无法知道所指数据的类型而报错
与邮箱不同,消息缓冲传递的不是地址而是实实在在的将消息复制到缓冲区。
基于这个特性,我觉得消息缓冲区更适合处理大量数据的传输。比如usart接收数据的处理。Usart每次接收到数据,直接丢到缓冲去即可。uasrt后来接收到的数据不会覆盖上次的数据,OS只管根据自己的需要处理下缓冲去消息即可。
比如对于STM32F4,USART的数据寄存器地址是(0x40001004),如果是按照邮箱的方式来处理数据。只需要将这个地址0x40001004传递给邮箱,接收消息的任务结束时候,只需要从这个地址取出来数据即可。至于中间这个数据是否发生了变化,接收端不知道。
而使用消息队列的时候却是首先将地址0x40001004中的数据,复制到消息队列中。任务从消息队列中取消息的时候,也不会关心原数据的地址。(感觉我这样分析像数据队列了)
消息缓冲区的SVC
tk_cre_mbf (mbf = message buffer)创建消息缓冲区 。
消息队列的各种属性在设置时设置。其中mbfatr消息缓冲区的属性的设置如下;
TA_TFIFO 等待发送的任务按FIFO的数序排队
TA_TPRI 等待发送的任务按优先级顺序排队
TA_USERBUF 表示任务使用用户指定的区域作为缓冲区
TA_DSNAME 设定DS对象名
一个悲剧的小插曲:
按照习惯性的写法,在接收消息的时候我这样写的:
if(E_OK == tk_rcv_mbf(MbfID_1,(VP)msgrcv,-1)) { tm_putstring((UB*)"Task A receive a message.the message is:\n"); tm_putstring((UB*)msgrcv); } else { tm_putstring((UB*)"Task A Failed receive the message\n"); PutErcd(ercd); }
然后发现TaskA死活收不到消息,这就怪了。一直检查发送部分,没出错。最后看手册才注意到:返回值是接收的消息大小(字节数)或错误编码。这样写即使受到消息了也不会正常显示。。。
不仔细看手册函数说明害死人啊!
【实验描述】
首先创建两个任务TaskA和TaskB,优先级分别为18和20.然后创建两个消息队列。
启动任务TaskA,在TaskA中启动TaskB。
TaskA向MbfID_1发送消息,然后去等待MbfID_2中的消息。此时MbfID_1中没消息,TaskA进入休眠状态。任务TaskB开始执行。
TaskB首先接收MbfID_1中的消息,通过串口显示出来后再向MbfID_2中发送消息。发送完成后,TaskA释放等待条件满足,立即抢断TaskB。开始执行剩下的代码和循环。
如果没有上面的小插曲,这个实验是相当低简单。可惜疏忽了一下,费了一晚上时间才搞定。
【实验代码及输出】
#include "MessbufSample.h" #define MBF1SIZE 200 #define MBF2SIZE 200 #define MBF1MAXMSIZE 80 #define MBF2MAXMSIZE 80 void MbfSampleTaskA(W stacd,VP exinf); void MbfSampleTaskB(W stacd,VP exinf); static ID TaskID_A; static ID TaskID_B; static ID MbfID_1; static ID MbfID_2; ER MbfSample( void) { T_CTSK ctsk; T_CMBF cmbf; //创建任务TaskA ctsk.bufptr = NULL; ctsk.exinf = (VP)NULL; ctsk.itskpri = 18; ctsk.stksz = 512; ctsk.task = MbfSampleTaskA; ctsk.tskatr = TA_HLNG | TA_RNG0; TaskID_A = tk_cre_tsk(&ctsk); //创建任务TaskB ctsk.itskpri = 20; ctsk.task = MbfSampleTaskB; TaskID_B = tk_cre_tsk(&ctsk); //创建消息缓冲区MbfID_1 cmbf.bufptr = (VP)NULL; //不使用用户缓冲区 cmbf.bufsz = MBF1SIZE; //消息缓冲区200字节 cmbf.exinf = (VP)NULL; cmbf.maxmsz = MBF1MAXMSIZE; //最大消息大小 cmbf.mbfatr = TA_TFIFO; MbfID_1 = tk_cre_mbf(&cmbf); //创建消息缓冲区MbfID_2 cmbf.bufsz = MBF2SIZE; cmbf.maxmsz = MBF2MAXMSIZE; MbfID_2 = tk_cre_mbf(&cmbf); tk_sta_tsk(TaskID_A,5); return TRUE; } void MbfSampleTaskA(W stacd,VP exinf) { UW len; B c; T_RMBF rmbf; W msgsz; B msgsend1[] = "Do you think hanshuyujifen is very good?\n"; B msgsend2[] = "Do you think hanshuyujifen work hard?\n"; B msgrcv[100] = "\0"; tk_sta_tsk(TaskID_B,0); while(1) { tm_putstring((UB*)"I am in task a\n"); tm_putstring((UB*)"Task A send a message:\n"); tm_putstring((UB*)msgsend1); if(E_OK == tk_snd_mbf(MbfID_1,(VP)msgsend1,strlen(msgsend1),-1)) { tm_putstring((UB*)"Task A send message sucessfully\n"); } else { tm_putstring((UB*)"Task A Failed send a message:\n"); return; } if(E_OK == tk_snd_mbf(MbfID_1,(VP)msgsend2,strlen(msgsend2),-1)) { tm_putstring((UB*)"Task A send message sucessfully\n"); } else { tm_putstring((UB*)"Task A Failed send a message:\n"); return; } if(E_OK < tk_rcv_mbf(MbfID_2,(VP)msgrcv,-1)) { tm_putstring((UB*)"Task A receive a message.the message is:\n"); tm_putstring((UB*)msgrcv); } else { tm_putstring((UB*)"Task A Failed receive the message\n"); } } } void MbfSampleTaskB(W stacd,VP exinf) { UW len; B msgsend[] = "Yes,i think so.\n"; B msgrcv[100] = "\0"; B msgrcv2[100] = "\0"; while(1) { tk_rcv_mbf(MbfID_1,(VP)msgrcv,-1); tm_putstring((UB*)"Task B receive a message.\nThe Message is:\n"); tm_putstring((UB*)msgrcv); if(E_OK == tk_rcv_mbf(MbfID_1,(VP)msgrcv,-1)); tm_putstring((UB*)"Task B receive a message.\nThe Message is:\n"); tm_putstring((UB*)msgrcv2); Delay(0x1000000); tm_putstring((UB*)"Task B will send a message,the message is:\n"); tm_putstring((UB*)msgsend); if(E_OK == tk_snd_mbf(MbfID_2,(VP)msgsend,strlen(msgsend),-1)) { tm_putstring((UB*)"TaskB send a message sucessfully\n"); } else { tm_putstring((UB*)"Task B Failed receive the message\n"); } } }
输出如下:
----------------------------------------------------
micro Tenux Version 1.6.00(build 0180)
Supported MCU is ST STM32F407VG
Copyright(c) 2008-2013 by Dalian uLoong Co.,Ltd.
----------------------------------------------------
I am in task a
Task A send a message:
Do you think hanshuyujifen is very good?
Task A send message sucessfully
Task B receive a message.
The Message is:
Do you think hanshuyujifen is very good?
Task B will send a message,the message is:
Yes,i think so.
Task A receive a message.the message is:
Yes,i think so.
。。。。。。
【附加实验】
在上述消息队列中,一次放置多个消息。
如果消息缓冲空间足够,发送多个消息跟发送一个消息,对发送发来说没什么区别。当消息缓冲快满的时候,容纳不下新消息,发送任务就会进入等待状态。直到缓冲区有足够空间。
但是接收第二条消息时候一定要记得把用于存放消息的内存块先清空了。否则的话,会将上一条信息没有被覆盖掉的东西输出来。