#if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 0) (1)
typedef struct {
INT8U OSEventType; /* Type of event control block (see OS_EVENT_TYPE_???) */(2)
INT8U OSEventGrp; /* Group corresponding to tasks waiting for event to occur */(3)
INT16U OSEventCnt; /* Semaphore Count (not used if other EVENT type) */(4)
void *OSEventPtr; /* Pointer to message or queue structure */(5)
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */(6)
} OS_EVENT;
#endif
注释:
(1)条件编译,决定是否编译此数据结构的定义,减少代码量;
(2)定义事件具体类型,2.52版本的uC/OS II共包含五种类型,声明如下:
#define OS_EVENT_TYPE_UNUSED 0 /* 未使用 */
#define OS_EVENT_TYPE_MBOX 1 /* 邮箱 */
#define OS_EVENT_TYPE_Q 2 /* 队列 */
#define OS_EVENT_TYPE_SEM 3 /* 信号量 */
#define OS_EVENT_TYPE_MUTEX 4 /* 互斥量 */
#define OS_EVENT_TYPE_FLAG 5 /* 标志量 */
(3)(6)OSEventGrp与OSEventTbl[]的作用类似于OSRdyGrp与OSRdyTbl[],存放等待某事件的任务(就绪状态的任务);
(4)当OSEventType为OS_EVENT_TYPE_SEM,OSEventCnt是用于信号量的计数器;当当OSEventType为OS_EVENT_TYPE_MUTEX,OSEventCnt用于互斥信号量与优先级继承优先级(PIP)的计数器;
(5)指向消息或队列结构的指针,也就是说只有OSEventType为OS_EVENT_TYPE_MBOX或OS_EVENT_TYPE_Q时才有用。
图1. ECB控制块结构
从图1. 中可以看出ECB中也包含一个类似就绪表的结构,将一个任务放入事件的等待任务列表的操作与就绪表的相关操作完全相同。
在uC/OS II中,事件控制块的总数由用户所需要的信号量、邮箱和消息队列的总数决定。该值由OS_CFG.H 中的#define OS_MAX_EVENTS定义。在调用OSInit()时,执行了OS_InitEventList()函数,所有事件控制块被链接成一个单向链表——空闲事件控制块链表。每当建立一个信号量、邮箱或者消息队列时,就从该链表中取出一个空闲事件控制块,并对它进行初始化。调用删除信号量、互斥量、邮箱以及队列的函数,可将事件控制块放回到空余事件控制块列表中,图2所示。
图2. 空余事件控制块链表
对ECB的操作一般包括:
* 初始化一个事件控制块 (void OS_EventWaitListInit (OS_EVENT *pevent))
* 使一个任务进入就绪态 (INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk))
* 使一个任务进入等待某事件的状态 (void OS_EventTaskWait (OS_EVENT *pevent))
* 因为等待超时而使一个任务进入就绪态 (void OS_EventTO (OS_EVENT *pevent))
对OS_EventTaskRdy()函数和OS_EventTO函数的调用都需要关中断。
1. OS_EventWaitListInit (OS_EVENT *pevent)
此函数被与ECB建立相关的函数调用,如OSSemCreate()、OSMutexCreate()、OSQCreate()和OSMboxCreate()。函数功能就是对ECB中的等待任务列表进行初始化,函数创建时,等待任务列表初始化为空。这个函数是对内的,即此函数可以被uC/OS II调用,用户应用程序不可以直接调用此函数。代码如下:
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
void OS_EventWaitListInit (OS_EVENT *pevent)
{
INT8U *ptbl;
pevent->OSEventGrp = 0×00; /* No task waiting on event */
ptbl = &pevent->OSEventTbl[0];
#if OS_EVENT_TBL_SIZE > 0
*ptbl++ = 0×00;
#endif
#if OS_EVENT_TBL_SIZE > 1
*ptbl++ = 0×00;
#endif
#if OS_EVENT_TBL_SIZE > 2
*ptbl++ = 0×00;
#endif
#if OS_EVENT_TBL_SIZE > 3
*ptbl++ = 0×00;
#endif
#if OS_EVENT_TBL_SIZE > 4
*ptbl++ = 0×00;
#endif
#if OS_EVENT_TBL_SIZE > 5
*ptbl++ = 0×00;
#endif
#if OS_EVENT_TBL_SIZE > 6
*ptbl++ = 0×00;
#endif
#if OS_EVENT_TBL_SIZE > 7
*ptbl = 0×00;
#endif
}
#endif
上面的代码功能比较简单,唯一需要注意的是,Labrosse先生使用条件编译代替了for循环,这样做的目的是减少编译时间(具体的效率,暂不清楚)。
2. INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
当某事件发生时,要将等待该事件任务列表中优先级最高的的任务置于就绪态,信号量、互斥型信号量、消息邮箱、消息队列所对应的POST函数都会调用OS_EventTaskRdy()。这个函数是对内的,即此函数可以被uC/OS II调用,用户应用程序不可以直接调用此函数。源码如下:
#if OS_EVENT_EN > 0
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
{
OS_TCB *ptcb;
INT8U x;
INT8U y;
INT8U bitx;
INT8U bity;
INT8U prio;
y = OSUnMapTbl[pevent->OSEventGrp]; /* 寻找等待队列中优先级最高的任务 */
bity = OSMapTbl[y];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
bitx = OSMapTbl[x];
prio = (INT8U)((y << 3) + x); /* 换算成优先级值 */
if ((pevent->OSEventTbl[y] &= ~bitx) == 0×00) { /* 从等待列表中移除该优先级 */
pevent->OSEventGrp &= ~bity;
}
ptcb = OSTCBPrioTbl[prio]; /* 取得该优先级对应的TCB */
ptcb->OSTCBDly = 0; /* 停止OSTimeTick()对OSTCBDly的递减操作 */
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* 任务已就绪,不再等待,移除指针 */
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb->OSTCBMsg = msg; /* 如被消息类函数调用,传递参数 */
#else
msg = msg; /* Prevent compiler warning if not used */
#endif
ptcb->OSTCBStat &= ~msk; /* 清楚标志位 */
if (ptcb->OSTCBStat == OS_STAT_RDY) { /* 如果任务处于就绪态,插入就绪表 */
OSRdyGrp |= bity; /* Put task in the ready to run list */
OSRdyTbl[y] |= bitx;
}
return (prio);
}
#endif
需要注意的是if (ptcb->OSTCBStat == OS_STAT_RDY),这说明最高优先级任务得到该事件后不一定就能进入就绪态,也许该任务由于其他原因“suspend”了。
3. void OS_EventTaskWait (OS_EVENT *pevent)
当某任务等待某事件的发生时,信号量、互斥型信号量、消息邮箱、消息队列所对应的PEND函数就会调用OS_EventTaskWait(),是当前任务脱离就绪态,并放到相应的ECB的任务等待表中。这个函数是对内的,即此函数可以被uC/OS II调用,用户应用程序不可以直接调用此函数。源码如下:
#if OS_EVENT_EN > 0
void OS_EventTaskWait (OS_EVENT *pevent)
{
OSTCBCur->OSTCBEventPtr = pevent; /* 在该任务的TCB中保存ECB的指针 */
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0×00) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY; /* 从就绪表中移除需要等待的任务优先级 */
}
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;/* 把就绪表中移除的任务优先级加入等待表*/
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
#endif
4. void OS_EventTO (OS_EVENT *pevent)
这个函数牵涉到任务等待超时问题,uC/OS II中可以为任务等待设置一个等待时间,如果在规定的时间内任务等待的事件得不到相应(没有发生),那么OSTimeTick()函数会因为等待超时而将任务置为就绪态。信号量、互斥型信号量、消息邮箱、消息队列所对应的PEND函数就会调用OS_EventTO()函数,完成上述工作。这个函数是对内的,即此函数可以被uC/OS II调用,用户应用程序不可以直接调用此函数。源码如下:
#if OS_EVENT_EN > 0
void OS_EventTO (OS_EVENT *pevent)
{
if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0×00) {
pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY; /* 等待列表中删除等待超时的任务优先级 */
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* 设置为就绪态 */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* TCB中删除对应的ECB指针 */
}
#endif