μC/OS-II的任务之间的通讯与同步
另外,消息队列一旦建立就不能再删除了。试想,如果有任务正在等待某个消息队列中的消息,而此时又删除该消息队列,将是很危险的。
程序清单L6.21建立一个消息队列
OS_EVENT*OSQCreate(void**start,INT16Usize)
{
OS_EVENT*pevent;
OS_Q*pq;
OS_ENTER_CRITICAL();
pevent=OSEventFreeList;(1)
if(OSEventFreeList!=(OS_EVENT*)0){
OSEventFreeList=(OS_EVENT*)OSEventFreeList->OSEventPtr;(2)
}
OS_EXIT_CRITICAL();
if(pevent!=(OS_EVENT*)0){
OS_ENTER_CRITICAL();
pq=OSQFreeList;(3)
if(OSQFreeList!=(OS_Q*)0){
OSQFreeList=OSQFreeList->OSQPtr;
}
OS_EXIT_CRITICAL();
if(pq!=(OS_Q*)0){
pq->OSQStart=start;(4)
pq->OSQEnd=start[size];
pq->OSQIn=start;
pq->OSQOut=start;
pq->OSQSize=size;
pq->OSQEntries=0;
pevent->OSEventType=OS_EVENT_TYPE_Q;(5)
pevent->OSEventPtr=pq;(6)
OSEventWaitListInit(pevent);(7)
}else{
OS_ENTER_CRITICAL();
pevent->OSEventPtr=(void*)OSEventFreeList;(8)
OSEventFreeList=pevent;
OS_EXIT_CRITICAL();
pevent=(OS_EVENT*)0;
}
}
return(pevent);(9)
}
6.8.2 等待一个消息队列中的消息,OSQPend()
程序清单L6.22是OSQPend()函数的源代码。OSQPend()函数首先检查事件控制块是否是由OSQCreate()函数建立的[L6.22(1)],接着,该函数检查消息队列中是否有消息可用(即.OSQEntries是否大于0) [L6.22(2)]。 如果有, OSQPend()函数将指向消息的指针复制到msg变量中, 并让.OSQOut指针指向队列中的下一个单元[L6.22(3)],然后将队列中的有效消息数减1[L6.22(4)]。因为消息队列是一个循环的缓冲区,OSQPend()函数需要检查.OSQOut是否超过了
队列中的最后一个单元[L6.22(5)]。当发生这种越界时,就要将.OSQOut重新调整到指向队列的起始单元[L6.22(6)]。这是我们调用OSQPend()函数时所期望的,也是执行OSQPend()函数最快的路径。
程序清单L6.22在一个消息队列中等待一条消息
void*OSQPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err)
{
void*msg;
OS_Q*pq;
OS_ENTER_CRITICAL();
if(pevent->OSEventType!=OS_EVENT_TYPE_Q){(1)
OS_EXIT_CRITICAL();
*err=OS_ERR_EVENT_TYPE;
return((void*)0);
}
pq=pevent->OSEventPtr;
if(pq->OSQEntries!=0){(2)
msg=*pq->OSQOut++;(3)
pq->OSQEntries--;(4)
if(pq->OSQOut==pq->OSQEnd){(5)
pq->OSQOut=pq->OSQStart;(6)
}
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}elseif(OSIntNesting>0){(7)
OS_EXIT_CRITICAL();
*err=OS_ERR_PEND_ISR;
}else{
OSTCBCur->OSTCBStat|=OS_STAT_Q;(8)
OSTCBCur->OSTCBDly=timeout;
OSEventTaskWait(pevent);
OS_EXIT_CRITICAL();
OSSched();(9)
OS_ENTER_CRITICAL();
if((msg=OSTCBCur->OSTCBMsg)!=(void*)0){(10)
OSTCBCur->OSTCBMsg=(void*)0;
OSTCBCur->OSTCBStat=OS_STAT_RDY;
OSTCBCur->OSTCBEventPtr=(OS_EVENT*)0;(11)
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}elseif(OSTCBCur->OSTCBStatOS_STAT_Q){(12)
OSEventTO(pevent);(13)
OS_EXIT_CRITICAL();
msg=(void*)0;(14)
*err=OS_TIMEOUT;
}else{
msg=*pq->OSQOut++;(15)
pq->OSQEntries--;
if(pq->OSQOut==pq->OSQEnd){
pq->OSQOut=pq->OSQStart;
}
OSTCBCur->OSTCBEventPtr=(OS_EVENT*)0;(16)
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}
}
return(msg);(17)
}
如果这时消息队列中没有消息(.OSEventEntries是0),OSQPend()函数检查它的调用者是否是中断服务子程序[L6.22(7)]。象OSSemPend()和OSMboxPend()函数一样,不能在中断服务子程序中调用OSQPend(), 因为中断服务子程序是不能等待的。 但是, 如果消息队列中有消息,即使从中断服务子程序中调用OSQPend()函数,也一样是成功的。
如果消息队列中没有消息,调用OSQPend()函数的任务被挂起[L6.22(8)]。当有其它的任
务向该消息队列发送了消息或者等待时间超时,并且该任务成为最高优先级任务时,OSSched()
返回[L6.22(9)]。这时,OSQPend()要检查是否有消息被放到该任务的任务控制块中[L6.22(10)]。如果有,那么该次函数调用成功,把任务的任务控制块中指向消息队列的指针删除[L6.22(17)],并将对应的消息被返回到调用函数[L6.22(17)]。
在OSQPend()函数中,通过检查任务的任务控制块中的.OSTCBStat域,可以知道是否等到时间超时。如果其对应的OS_STAT_Q位被置1,说明任务等待已经超时[L6.22(12)]。这时,通过调用函数OSEventTo()可以将任务从消息队列的等待任务列表中删除[L6.22(13)]。这时,因为消息队列中没有消息,所以返回的指针是NULL[L6.22(14)]。
如果任务控制块标志位中的OS_STAT_Q位没有被置1,说明有任务发出了一条消息。
OSQPend()函数从队列中取出该消息[L6.22(15)]。然后,将任务的任务控制中指向事件控制块的指针删除[L6.22(16)]。
6.8.3 向消息队列发送一个消息(FIFO),OSQPost()
程序清单L6.23是OSQPost()函数的源代码。在确认事件控制块是消息队列后
[L6.23(1)],OSQPost()函数检查是否有任务在等待该消息队列中的消息[L6.23(2)]。当事件控
评论