学习过信号量之后再来看邮箱,发现他们是非常相似的,甚至有时候邮箱可以当做信号量来使用,邮箱相对信号量而言,只是多传递一个指针变量
和信号量相似,ucos提供了5个对邮箱操作的函数它们是:
1.建立一个邮箱,OSMboxCreate()
2.等待一个邮箱的消息 OSMboxPend()
3.发送一个消息到邮箱,OSMboxPost()
4.无等待从邮箱中得到一个消息,OSMboxAccept();
5.查询一个邮箱的状态 OSMboxQuery();
使用邮箱之前,必须先建立该邮箱,该操作可以通过调用OSMboxCreate函数来完成,并且要指定指针的初始化值,一般情况下,这个初始值是NULL,
但也可以初始化一个邮箱,使其在最开始就包含一条消息,如果使用邮箱的目的是用来通知任务某一个事件已经发生(发送一条消息),那么就要初始化该邮箱为null,
如果用户用邮箱来共享资源,那么就要初始化该邮箱为一个非null指针,在这种情况下,该邮箱被当成一个二值信号量使用
OS_EVENT *OSMboxCreate(void *msg)
{
OS_EVENT *pevent;
OS_ENTER_CRITICAL();
pevent =OSEventFreeList;
if(OSEventFreeList!=(OS_EVENT*)0){
OSEventFreeList=(OS_EVENT*)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if(pevent!=(OS_EVENT*)0)
{
pevent->OSEventType=OS_EVENT_TYPE_MBOX; (1)
pevent->OSEventPtr=msg (2)
OSEventWaitListInit(pevent);
}
return (pevent) ; (3)
}
仔细看看,其实和创建一个信号量的过程几乎是一样的,先申请一个空事件控制块,接着初始化这个事件控制块,最后返回一个纸箱这个事件控制块的,不同之处在于事件
控制块的类型被设置成OS_EVENT_TYPE_MBOX 以及使用OSEventPtr来容纳消息指针。
接着来看等待邮箱函数实现代码:
void *OSMboxPend(OS_EVENT *pevent,INT16U timeout,INT8U *err)
{
void *msg;
OS_ENTER_CRITICAL();
if(pevent->OSEventType!=OS_EVENT_TYPE_MBOX) (1)
{
OS_EXIT_CRITICAL();
*err=OS_ERR_EVENT_TYPE;
return((void*)0);
}
msg=pevent->OSEventPtr;
if(msg!=(void*)0) (2)
{
pevent->OSEventPtr=(void*)0; (3)
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}else if(OSIntNesting>0) (4)
{
OS_EXIT_CRITICAL();
*err=OS_ERR_PEND_ISR;
}
else
{
OSTCBCur->OSTCBStat|=OS_STAT_MBOX; (5)
OSTCBCur->OSTCBDly=timeout;
OSEventTaskWait(pevent);
OS_EXIT_CRITICAL();
OSSched();
OS_ENTER_CRITICAL();
if((msg=OSTCBCur->OSTCBMsg)!=(void*)0) (6)
{
OSTCBCur->OSTCBMsg =(void*)0;
OSTCBCur->OSTCBStat=OS_STAT_RDY;
OSTCBCur->OSTCBEventPtr=(OS_EVENT*)0;
OS_EXIT_CRITICAL();
*err =OS_NO_ERR;
}
else if(OSTCBCur->OSTCBStat&OS_STAT_MBOX) (7)
{
OSEventTo(pevent); (8)
OS_EXIT_CRITICAL();
msg =(void*)0; (9)
*err =OS_TIMEOUT;
}
else
{
msg =pevent->OSEventPtr; (10);
pevent->OSEventPtr =(void*)0; (11)
OSTCBCur->OSTCBEventPtr =(OS_EVENT*)0; (12)
OS_EXIT_CRITICAL();
*err =OS_NO_ERR;
}
}
return(msg);
}
同样,它和OSSemPend()也相似,说白了就是先看有没有有用的消息,要使没有,就该把任务挂起来。
OSMboxPend首先检查该事件控制块是由OSMboxCreate()函数建立(1)。当OSEventPtr域是一个非
NULL的指针时,说明该邮箱有可用的消息(2);这种情况下,OSMboxPend()函数将该域的值复制到句柄变量
msg中,然后将OSEventPtr置为null,这正是我们期望的,也是执行OSMboxPend函数最快的路径。
如果此时邮箱中没有消息是可用的额(OSEventPtr域是null指针),OSMboxPend函数检查它的调用者是否是中断服务子程序。
像OSSemPend函数一样,不能在中断服务子程序中调用OSMboxPend(),因为中断服务子程序是不能等待的,但是如果邮箱有可用的消息,
即使从中断服务子程序中调用OSMboxPend()函数,也一样是成功的。如果没有可用的消息,OSMboxPend的调用任务就被挂起,直到邮箱中有了
消息或者等待超时时(5).当有其它任务向该邮箱发送了消息(或等待时间超时时),这时,该任务再一次成为最高任务优先级任务,OSSched()返回,这时,
OSMboxPend()函数要检查是否有消息被放到该任务的任务控制块中(6),如果有,那么该函数调用成功,对应的消息被返回到调用的函数
发送一个消息到邮箱中OSMboxPost()的代码如下:
INT8U OSMboxPost(OS_EVENT *pevent,void *msg)
{
OS_ENTER_CRITICAL();
if(pevent->OSEventType!=OS_EVENT_TYPE_MBOX) (1)
{
OS_EXIT_CRITICAL();
return(OS_ERR_EVENT_TYPE);
}
if(pevent->OSEventGrp) (2)
{
OSEventTaskRdy(pevent,msg,OS_STAT_MBOX); (3)
OS_EXIT_CRITICAL();
OSSched(); (4)
return(OS_NO_ERR);
}
else
{
if(pevent->OSEventPtr!=(void*)0) (5)
{
OS_EXIT_CRITICAL();
return(OS_MBOX_FULL);
}
else
{
pevent->OSEventPtr =msg ; (6)
OS_EXIT_CRITICAL();
return(OS_NO_ERR);
}
}
}
发送一个消息到邮箱和发送一个信号量也相似,就丝毫查看有没有任务在等待这个消息,如果有就把哪个任务从睡眠态拉回就就绪态。
代码的详细解释如下:
检查了事件控制块是否是一个邮箱后(1),OSMboxPost()函数还要检查是否有任务等待该邮箱汇总的消息(2)。如果事件控制块中的OSEventGrp域包含非零值,
就暗示有任务在等待该消息,这时,调用OSEventTaskRdy()将其中的最高级优先级任务从等待列表中删除,加入系统的就绪任务列表中,准备运行,然后,调用OSSched()(4)函数,检查该任务十分是系统中最高优先级的就绪任务,如果是,这些任务切换【仅当OSMboxPost()】函数是由任务调用时,该任务得以执行,如果该任务不是最高优先级的任务,OSSched()返回,OSMboxPost()的调用函数继续执行,如果没有任务任务等待该消息,指向消息的指针就被保存到邮箱中(6)(假设此时邮箱汇总的指针不是非NULL的【5】),这样,下一个调用OSMboxPend函数的任务就可以立刻得到该消息了。
#define TASK_STK_SIZE 512
char *s ;//MyTask发送的消息指针,
char *ss;//YouTask接收到的消息的指针
INT8U err;
INT8U y=0;
INT32U Times=0;
OS_EVENT *Str_Box; //定义事件控制块指针,定义消息邮箱的指针
OS_STK StartTask[TASK_STK_SIZE];
OS_STK MyTaskStk[TASK_STK_SIZE];
OS_STK YouTaskStk[TASK_STK_SIZE];
void StartTask(void *data);
void MyTask(void *data);
void YouTask(void *data);
void main(void)
{
OSInit();
Str_Box=OSMboxCreate((void*)0);//创建消息邮箱,返回值指向创建消息邮箱指针,初始值为null 表示创建的消息邮箱没有内容。
OSTaskCreate(StartTask,(void*)0,&StartTaskStk[TASK_STK_SIZE-1],0);
OSStart();
}
void StartTask(void *pdata)
{
#if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr;
#endif
INT16S key;
pdata=pdata;
OSStatInit();
OSTaskCreate(MyTask,(void*)0,&StartTaskStk[TASK_STK_SIZE-1],1);
OSTaskCreate(YouTask,(void*)0,&StartTaskStk[TASK_STK_SIZE-1],2);
for(;;)
{
if(PC_GetKey(&key)==TRUE)
{
if(key==0x1B)
{
PC_DOSReturn();
}
}
OSTimeDlyHMSM(0,0,3,0);
}
}
void MyTask(void *pdata)
{
#if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for(;;)
{
sprintf(s,"%d",times);//把Times赋值给s
OSMboxPost(Str_Box,s);//发送消息s 其中两个参数Str_Box是OS_EVENT *pevent表示消息邮箱指针,s是void*msg表示消息指针
该函数表示把消息s发送到消息邮箱Str_Box中
Times++;//MyTask的运行次数加1
OSTimeDlyHMSM(0,0,1,0);
}
}
void YouTask(void *pdata)
{
#if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for(;;)
{
ss=OSMboxPend(Str_Box,10,&err);//请求消息邮箱参数表示:Str_Box是消息邮箱指针,10表示等待时间 ss是邮箱中的消息指针
OSTimeDlyHMSM(0,0,1,0); //等待1s
}
}
//OSMboxPend时指定等待时间为10,所以当等待时间到了,即使邮箱中还是无消息,YouTask也会进入就绪态的,然后继续往下运行,但是
当OSMboxPend的等待时间设置为0,表示无限等待。