任务与任务,任务与中断服务程序之间可以通信,通信介质称为事件控制块。通信方式有以下几种,如下图:
(1)中断服务程序或者任务可以给ECB发送信号;只有任务能够等到中断服务程序或者ECB发送的信号;等待ECB设置有超时机制,如图A。
(2)当多个任务等到任务或者中断服务程序给ECB发送信号时,只有等待的最高优先级任务才会加入就序列表准备运行。ECB可以是信号量,消息邮箱,消息队列。
(3)当ECB使用信号量时,任务既能等待ECB响应,也能给ECB发送信号。
ucosII通过结构体OS_EVENT来管理ECB
OSEventType:事件类型(信号量、锁、邮箱、消息队列);
OSEventPtr:ECB为邮箱或消息队列时使用;
OSEventTbl和OSEventGrp:等待事件表,类似于任务就绪表OSRdyTbal与OSRdyGrp;
OSEventCnt:用来给锁或者信号量计数。
typedef struct os_event {
INT8U OSEventType; /* Type of event control block (see OS_EVENT_TYPE_xxxx) */
void *OSEventPtr; /* Pointer to message or queue structure */
INT16U OSEventCnt; /* Semaphore Count (not used if other EVENT type) */
INT8U OSEventGrp; /* Group corresponding to tasks waiting for event to occur */
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */
}OS_EVENT
等待事件的任务会加入等待队列,由变量OSEventTbl和OSEventGrp实现,实现原理与任务就绪表的实现原理相同,均采用优先级位图法,这里不加赘述。下面介绍关于ECB的相关操作。
将任务加载至事件等待列表过程与将任务加载至就绪列表相同,具体代码如下:
pevent->OSEventGrp |= OSMapTbl[prio >> 3];
pevent->OSEventTbl[prio >>3] |= OSMapTbl[prio & 0x07];
将任务从等待列表移除过程与将任务从就绪列表移除相同,具体代码如下:
if((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07])== 0){
pevent->OSEventGrp &= ~OSMapTbl[prio >> 3];
}
y = OSUnMapTbl[pevent->OSEventGrp];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (y << 3) + x;
各个事件的ECB数量取决于定义在OS_CFG.H中的OS_MAX_EVENTS。在函数OS_InitEventList()
中进行初始化。当任一事件(信号量、锁、邮箱、消息队列)被创造时,就会从空闲列表移除并初始化。
OS_InitEventList()
函数如下:
static void OS_InitEventList(void)
{
#if(OS_MAX_EVENTS > 1)
INT16U i;
OS_EVENT *pevent1;
OS_EVENT *pevent2;
OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* Clear the event table */
pevent1 = &OSEventTbl[0];
pevent2 = &OSEventTbl[1];
for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { /* Init. list of free EVENT control blocks */
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = pevent2;
pevent1++;
pevent2++;
}
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = (OS_EVENT *)0;
#endif
OSEventFreeList = &OSEventTbl[0];
#else
OSEventFreeList = &OSEventTbl[0]; /* Only have ONE event control block */
OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList->OSEventPtr = (OS_EVENT *)0;
}
函数OS_EventWaitListInit()用来初始化事件等待列表,在事件被创建时调用(OSSemCreate()、OSMutexCreate()、OSMboxCreate()和OSQcreate())。
void OS_EventWaitListInit (OS_EVENT *pevent)
{
#if OS_LOWEST_PRIO <= 63
INT8U *ptbl;
#else
INT16U *ptbl;
#endif
INT8U i;
pevent->OSEventGrp = 0; /* No task waiting on event */
ptbl = &pevent->OSEventTbl[0];
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*ptbl++ = 0;
}
}
当事件接收到了信号时,在等待列表内的最高优先级的任务需要加入任务就绪列表,并将其移除事件等待列表。该函数被POST函数调用。
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg, INT8U msk, INT8U pend_stat)
{
OS_TCB *ptcb;
INT8U y;
INT8U x;
INT8U prio;
#if OS_LOWEST_PRIO > 63
INT16U *ptbl;
#endif
#if OS_LOWEST_PRIO <= 63
y = OSUnMapTbl[pevent->OSEventGrp]; /* Find HPT waiting for message */
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (INT8U)((y << 3) + x); /* Find priority of task getting the msg */
#else
if ((pevent->OSEventGrp & 0xFF) != 0) { /* Find HPT waiting for message */
y = OSUnMapTbl[ pevent->OSEventGrp & 0xFF];
} else {
y = OSUnMapTbl[(pevent->OSEventGrp >> 8) & 0xFF] + 8;
}
ptbl = &pevent->OSEventTbl[y];
if ((*ptbl & 0xFF) != 0) {
x = OSUnMapTbl[*ptbl & 0xFF];
} else {
x = OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8;
}
prio = (INT8U)((y << 4) + x); /* Find priority of task getting the msg */
#endif
ptcb = OSTCBPrioTbl[prio]; /* Point to this task's OS_TCB */
ptcb->OSTCBDly = 0; /* Prevent OSTimeTick() from readying task */
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb->OSTCBMsg = pmsg; /* Send message directly to waiting task */
#else
pmsg = pmsg; /* Prevent compiler warning if not used */
#endif
ptcb->OSTCBStat &= ~msk; /* Clear bit associated with event type */
ptcb->OSTCBStatPend = pend_stat; /* Set pend status of post or abort */
/* See if task is ready (could be susp'd) */
/* 加入就绪列表 */
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {
OSRdyGrp |= ptcb->OSTCBBitY; /* **Put task in the ready to run list** */
OSRdyTbl[y] |= ptcb->OSTCBBitX;
}
OS_EventTaskRemove(ptcb, pevent); /* Remove this task from event wait list */
#if (OS_EVENT_MULTI_EN > 0)
if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) { /* Remove this task from events' wait lists */
OS_EventTaskRemoveMulti(ptcb, ptcb->OSTCBEventMultiPtr);
ptcb->OSTCBEventPtr = (OS_EVENT *)pevent;/* Return event as first multi-pend event ready*/
}
#endif
return (prio);
}
当ECB正在被占用时,任务需要等待,便要将其从就绪列表移除,加入等待列表。
void OS_EventTaskWait (OS_EVENT *pevent)
{
INT8U y;
OSTCBCur->OSTCBEventPtr = pevent; /* Store ptr to ECB in TCB */
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
y = OSTCBCur->OSTCBY; /* Task no longer ready */
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY; /* Clear event grp bit if this was only task pending */
}
}
void OS_EventTaskRemove (OS_TCB *ptcb,
OS_EVENT *pevent)
{
INT8U y;
y = ptcb->OSTCBY;
pevent->OSEventTbl[y] &= ~ptcb->OSTCBBitX; /* Remove task from wait list */
if (pevent->OSEventTbl[y] == 0) {
pevent->OSEventGrp &= ~ptcb->OSTCBBitY;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY;
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
}