UC/OS-II基础知识之事件控制块及事件处理函数
1.等待任务列表
作为功能完善的事件,应该对那些处于等待任务具有两方面的管理功能,一是要对等待事件的所有任务进行记录并排序,而是允许等待任务有一个等待时限,即当等待任务认为等不及时可以退出对事件的请求。对于等待事件任务的记录,系统使用了与任务就绪表类似的位图,即定义了一个INT8U类型的数组OSEventTbl【】作为等待事件任务的记录表,即等待任务表。
等待任务表仍然以任务的优先级别为顺序为每个任务分配一个二进制位,并用该为为1来表示这一位对应的任务为事件的等待任务,否则不是等待任务。同样为了加快对该表的访问,也定义了一个INT8U类型的变量OSEventGrp来表示等待任务表中的任务组,示意图如下所示:
至于等待任务的时限则记录在等待任务的任务控制块TCB的成员OSTCBDly中。每当有任务的等待时限到达时,便将该任务从等待任务表中删除,并使他进入就绪状态。
2.事件控制块的结构
为了将描述事件的数据结构统一起来,UC/OS-II使用事件控制块ECB的数据结构来描述如信号量,信号队列和邮箱这些事件。事件控制块的数据结构如下所示:
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) */
#if OS_LOWEST_PRIO <= 63
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 */
#else
INT16U OSEventGrp; /* Group corresponding to tasks waiting for event to occur */
INT16U OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */
#endif
#if OS_EVENT_NAME_SIZE > 1
INT8U OSEventName[OS_EVENT_NAME_SIZE];
#endif
} OS_EVENT;
#endif
应用程序中的任务通过指针pevent来访问事件控制块,事件控制块结构示意图如下图所示
成员OSEventType为事件的类型如下表所示
成员OSEventPtr主要用来存放消息邮箱或者消息队列的指针。
成员OSEventCnt为信号量的计数器。
成员OSEventTbl[OS_EVENT_TBL_SIZE]表示等待任务表。
3.对事件控制块的操作
UC/OS-II有四个对事件控制块进行基本操作的函数(定义在OS_CORE.C)
3.1事件控制块的初始化
调用函数OS_EventWaitListInit()可以对事件控制块进行初始化。函数原型如下
#if OS_EVENT_EN
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;
}
}
这个函数的作用就是把OSEventGrp及任务等待表中每一位都请0,即令事件的任务等待表中不含任何等待任务
事件控制块被初始化后的情况如下所示
3.2使一个任务进入等待状态的函数
把一个任务至于等待状态要调用函数OS_EventTaskWait()函数原型如下
void OS_EventTaskWait (OS_EVENT *pevent)
{
INT8U y;
OSTCBCur->OSTCBEventPtr = pevent; /* Store pointer to event control block in TCB */
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 */
}
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
3.3使一个任务进入就绪状态的函数
把一个任务至于等待状态要调用函数OS_EventTaskRdy()函数原型如下
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
{
OS_TCB *ptcb;
INT8U x;
INT8U y;
INT8U prio;
#if OS_LOWEST_PRIO <= 63
INT8U bitx;
INT8U bity;
#else
INT16U bitx;
INT16U bity;
INT16U *ptbl;
#endif
#if OS_LOWEST_PRIO <= 63
y = OSUnMapTbl[pevent->OSEventGrp]; /* Find HPT waiting for message */
bity = (INT8U)(1 << y);
x = OSUnMapTbl[pevent->OSEventTbl[y]];
bitx = (INT8U)(1 << x);
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;
}
bity = (INT16U)(1 << y);
ptbl = &pevent->OSEventTbl[y];
if ((*ptbl & 0xFF) != 0) {
x = OSUnMapTbl[*ptbl & 0xFF];
} else {
x = OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8;
}
bitx = (INT16U)(1 << x);
prio = (INT8U)((y << 4) + x); /* Find priority of task getting the msg */
#endif
pevent->OSEventTbl[y] &= ~bitx; /* Remove this task from the waiting list */
if (pevent->OSEventTbl[y] == 0) {
pevent->OSEventGrp &= ~bity; /* Clr group bit if this was only task pending */
}
ptcb = OSTCBPrioTbl[prio]; /* Point to this task's OS_TCB */
ptcb->OSTCBDly = 0; /* Prevent OSTimeTick() from readying task */
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Unlink ECB from this task */
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb->OSTCBMsg = msg; /* Send message directly to waiting task */
#else
msg = msg; /* Prevent compiler warning if not used */
#endif
ptcb->OSTCBPendTO = OS_FALSE; /* Cancel 'any' timeout because of post */
ptcb->OSTCBStat &= ~msk; /* Clear bit associated with event type */
if (ptcb->OSTCBStat == OS_STAT_RDY) { /* See if task is ready (could be susp'd) */
OSRdyGrp |= bity; /* Put task in the ready to run list */
OSRdyTbl[y] |= bitx;
}
return (prio);
}
#endif
该函数的作用就是把调用这个函数的任务在任务等待列表中的位置清0,再把任务就绪表中对应的位置1,然后引发一次中断。
3.4使一个等待超时的任务进入就绪状态的函数
如果一个正在等待事件的任务已经超过了等待事件,却任然没有获得事件等原因而未具备运行的条件,却又要使他进入就绪状态,这时需要调用OS_EventTO()函数
void OS_EventTO (OS_EVENT *pevent)
{
INT8U y;
y = OSTCBCur->OSTCBY;
pevent->OSEventTbl[y] &= ~OSTCBCur->OSTCBBitX; /* Remove task from wait list */
if (pevent->OSEventTbl[y] == 0x00) {
pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBPendTO = OS_FALSE; /* Clear the Pend Timeout flag */
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set status to ready */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* No longer waiting for event */
}
3.5空事件控制块链表
UC/OS-II在初始化时,系统会在初始化函数OSInit()中按应用程序使用事件OS_MAX_EVENT(在OS_CFG.H定义)创建OS_MAX_EVENT个空事件控制块。每当创建一个事件时,系统会从时间控制块链表中取出一个空事件控制块,并对他进行初始化以描述该事件,当应用程序删除一个事件时,系统会将该事件的控制块归还给空事件控制块链表。空事件控制块链表如下所示