uCOS-II任务间通信之邮箱

uCOS-II任务间通信之邮箱
学过信号量之后再来看邮箱,发现它们是非常相似的,甚至有时候邮箱可以当做信号量来使用,邮箱相对信号量而言,只是多传递了一个指针变量。
和信号量很相似,ucos II提供了5个对邮箱进行操作的函数。它们是:
1. 建立一个邮箱 -- OSMboxCreate()
2. 等待一个邮箱中的消息 -- OSMboxPend()
3. 发送一个消息到邮箱中 -- OSMboxPost()
4. 无等待地从邮箱中得到一个消息 -- OSMboxAccept()
5. 查询一个邮箱的状态 -- OSMboxQuery()
使用邮箱之前,必须先建立该邮箱。该操作可以通过调用OSMboxCreate()函数来完成,并且要指定指针的初始值。一般情况下,这个初始值是NULL,但也可以初始化一个邮箱,使其在最开始就包含一条消息。如果使用邮箱的目的是用来通知任务某一个事件已经发生(发送一条消息),那么就要初始化该邮箱为NULL。如果用户用邮箱来共享某些资源,那么就要初始化该邮箱为一个非NULL的指针。在这种情况下,邮箱被当成一个二值信号量使用。

下面来看看创建一个邮箱函数的实现代码:
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. OS_EVENT *OSMboxCreate (void *msg)  
  2. {  
  3.     OS_EVENT *pevent;  
  4.    
  5.    
  6.     OS_ENTER_CRITICAL();  
  7.     pevent = OSEventFreeList;  
  8.     if (OSEventFreeList != (OS_EVENT *)0) {  
  9.         OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;  
  10.     }  
  11.     OS_EXIT_CRITICAL();  
  12.     if (pevent != (OS_EVENT *)0) {  
  13.         pevent->OSEventType = OS_EVENT_TYPE_MBOX;                      //(1)  
  14.         pevent->OSEventPtr  = msg;                                     //(2)  
  15.         OSEventWaitListInit(pevent);  
  16.     }  
  17.     return (pevent);                                                   //(3)  
  18. }  


仔细看看,其实和创建一个信号量的过程几乎是一样的,先申请一个空事件控制块,接着初始化这个事件控制块。最后返回一个指向这个事件控制块的指针。不同之处在于事件控制块的类型被设置成OS_EVENT_TYPE_MBOX(1),以及使用.OSEventPtr域来容纳消息指针。

接着来看看等待邮箱函数实现代码:
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)  
  2. {  
  3.     void  *msg;  
  4.    
  5.     OS_ENTER_CRITICAL();  
  6.     if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {               //(1)  
  7.         OS_EXIT_CRITICAL();  
  8.         *err = OS_ERR_EVENT_TYPE;  
  9.         return ((void *)0);  
  10.     }  
  11.     msg = pevent->OSEventPtr;  
  12.     if (msg != (void *)0) {                                        //(2)  
  13.         pevent->OSEventPtr = (void *)0;                            //(3)  
  14.         OS_EXIT_CRITICAL();  
  15.         *err = OS_NO_ERR;  
  16.     } else if (OSIntNesting > 0) {                                 //(4)  
  17.         OS_EXIT_CRITICAL();  
  18.         *err = OS_ERR_PEND_ISR;  
  19.     } else {  
  20.         OSTCBCur->OSTCBStat |= OS_STAT_MBOX;                       //(5)  
  21.         OSTCBCur->OSTCBDly   = timeout;  
  22.         OSEventTaskWait(pevent);  
  23.         OS_EXIT_CRITICAL();  
  24.         OSSched();  
  25.         OS_ENTER_CRITICAL();  
  26.         if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) {             //(6)  
  27.             OSTCBCur->OSTCBMsg      = (void *)0;  
  28.             OSTCBCur->OSTCBStat     = OS_STAT_RDY;  
  29.             OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;  
  30.             OS_EXIT_CRITICAL();  
  31.             *err                    = OS_NO_ERR;  
  32.         } else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) {           //(7)  
  33.             OSEventTO(pevent);                                     //(8)  
  34.             OS_EXIT_CRITICAL();  
  35.             msg                     = (void *)0;                   //(9)  
  36.             *err                    = OS_TIMEOUT;  
  37.         } else {  
  38.             msg                     = pevent->OSEventPtr;          //(10)  
  39.             pevent->OSEventPtr      = (void *)0;                   //(11)  
  40.             OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;               //(12)  
  41.             OS_EXIT_CRITICAL();  
  42.             *err                    = OS_NO_ERR;  
  43.         }  
  44.     }  
  45.     return (msg);  
  46. }  

同样,它和OSSemPend()也很相似,说白了就是先看有没有有用的消息,要是没有,就把该任务挂起来。
 OSMboxPend()首先检查该事件控制块是由OSMboxCreate()函数建立的(1)。当.OSEventPtr域是一个非NULL的指针时,说明该邮箱中有可用的消息(2)。这种情况下,OSMboxPend()函数将该域的值复制到局部变量msg中,然后将.OSEventPtr置为NULL(3)。这正是我们所期望的,也是执行OSMboxPend()函数最快的路径。
如果此时邮箱中没有消息是可用的(OSEventPtr域是NULL指针),OSMboxPend()函数检查它的调用者是否是中断服务子程序(4)。象OSSemPend()函数一样,不能在中断服务子程序中调用OSMboxPend(),因为中断服务子程序是不能等待的。这里的代码同样是为了以防万一。但是,如果邮箱中有可用的消息,即使从中断服务子程序中调用OSMboxPend()函数,也一样是成功的。
如果邮箱中没有可用的消息,OSMboxPend()的调用任务就被挂起,直到邮箱中有了消息或者等待超时(5)。当有其它的任务向该邮箱发送了消息后(或者等待时间超时),这时,该任务再一次成为最高优先级任务,OSSched()返回。这时,OSMboxPend()函数要检查是否有消息被放到该任务的任务控制块中(6)。如果有,那么该次函数调用成功,对应的消息被返回到调用函数。
发送一个消息到邮箱中OSMboxPost()的代码如下:
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. INT8U OSMboxPost (OS_EVENT *pevent, void *msg)  
  2. {  
  3.     OS_ENTER_CRITICAL();  
  4.     if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {            //(1)  
  5.         OS_EXIT_CRITICAL();  
  6.         return (OS_ERR_EVENT_TYPE);  
  7.     }  
  8.     if (pevent->OSEventGrp) {                                   //(2)  
  9.         OSEventTaskRdy(pevent, msg, OS_STAT_MBOX);              //(3)  
  10.         OS_EXIT_CRITICAL();  
  11.         OSSched();                                              //(4)  
  12.         return (OS_NO_ERR);  
  13.     } else {  
  14.         if (pevent->OSEventPtr != (void *)0) {                  //(5)  
  15.             OS_EXIT_CRITICAL();  
  16.             return (OS_MBOX_FULL);  
  17.         } else {  
  18.             pevent->OSEventPtr = msg;                           //(6)  
  19.             OS_EXIT_CRITICAL();  
  20.             return (OS_NO_ERR);  
  21.         }  
  22.     }  
  23. }  

发送一个消息到邮箱和发送一个信号量也很相似,就是查看有没有任务在等待这个消息,如果有就把那个任务从睡眠态拉回就绪态。
代码的详细解释如下:

检查了事件控制块是否是一个邮箱后(1),OSMboxPost()函数还要检查是否有任务在等待该邮箱中的消息(2)。如果事件控制块中的OSEventGrp域包含非零值,就暗示着有任务在等待该消息。这时,调用OSEventTaskRdy()将其中的最高优先级任务从等待列表中删除(3),加入系统的就绪任务列表中,准备运行。然后,调用OSSched()函数(4),检查该任务是否是系统中最高优先级的就绪任务。如果是,执行任务切换[仅当OSMboxPost()函数是由任务调用时],该任务得以执行。如果该任务不是最高优先级的任务,OSSched()返回,OSMboxPost()的调用函数继续执行。如果没有任何任务等待该消息,指向消息的指针就被保存到邮箱中(6)(假设此时邮箱中的指针不是非NULL的(5))。这样,下一个调用OSMboxPend()函数的任务就可以立刻得到该消息了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值