3、事件管理及等待任务表
ucosii中的任务间的同步和通信主要通过事件OS_EVENT来管理的,结构体OS_EVENT定义如下:
typedef struct os_event {
INT8U OSEventType;
void *OSEventPtr;
INT16U OSEventCnt;
#if OS_LOWEST_PRIO <= 63
INT8U OSEventGrp;
INT8U OSEventTbl[OS_EVENT_TBL_SIZE];
#else
INT16U OSEventGrp;
INT16U OSEventTbl[OS_EVENT_TBL_SIZE];
#endif
} OS_EVENT;
成员变量OSEventType主要由信号量、互斥信号量、消息邮箱和消息队列几种类型。类似于任务优先级就绪表,OSEventGrp和OSEventTbl是用来管理等待该事件的任务等待表。若事件有效(为非0),则任务可直接申请该事件,若事件无效(为0),则任务需要在事件等待表中置位(以任务优先级号来标注其在事件等待表中的位置)。
(1)、信号量
一个刚创建且计数器值为10的信号量示意图如下:
上图中看出,刚初始化的信号量,其任务等待表中均为0。下面介绍在信号量创建、请求、发送、删除和查询信号量中用到的几个内核函数。
a、初始化任务事件等待表
void OS_EventWaitListInit (OS_EVENT *pevent)
{
#if OS_LOWEST_PRIO <= 63
INT8U *ptbl;
#else
INT16U *ptbl;
#endif
INT8U i;
/* No task waiting on event */
pevent->OSEventGrp = 0;
ptbl = &pevent->OSEventTbl[0];
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*ptbl++ = 0;
}
}
b、让任务由就绪状态转换为等待状态,等待时间由成员变量OSTCBCur->OSTCBDly = timeout; 决定,并由系统滴答函数OSTimeTick对等待时间递减操作,系统在请求信号量等待期间也进行任务调度。等待超时后,任务由等待状态变为就绪状态。
void OS_EventTaskWait (OS_EVENT *pevent)
{
INT8U y;
OSTCBCur->OSTCBEventPtr = pevent;
/* Put task in waiting list */
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
/* Task no longer ready */
y = OSTCBCur->OSTCBY;
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
}
c、删除任务的等待状态
void OS_EventTaskRemove (OS_TCB *ptcb,
OS_EVENT *pevent)
{
INT8U y;
/* Remove task from wait list */
y = ptcb->OSTCBY;
pevent->OSEventTbl[y] &= ~ptcb->OSTCBBitX;
if (pevent->OSEventTbl[y] == 0) {
pevent->OSEventGrp &= ~ptcb->OSTCBBitY;
}
}
d、SYSTEM TICK处理,遍历整个任务链表,进行时间处理
void OSTimeTick (void)
{
OS_TCB *ptcb;
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) {
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) {
if (--ptcb->OSTCBDly == 0) {
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= ~(INT8U)OS_STAT_PEND_ANY;
ptcb->OSTCBStatPend = OS_STAT_PEND_TO;
} else {
ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
}
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext;
OS_EXIT_CRITICAL();
}
}
}
e、任务由等待状态设置为就绪状态
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg, INT8U msk, INT8U pend_stat)
{
OS_TCB *ptcb;
INT8U y;
INT8U x;
INT8U prio;
y = OSUnMapTbl[pevent->OSEventGrp]; /* Find HPT waiting for message */
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (INT8U)((y << 3) + x);
ptcb = OSTCBPrioTbl[prio];
ptcb->OSTCBDly = 0;
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb->OSTCBMsg = pmsg;
#else
pmsg = pmsg;
#endif
ptcb->OSTCBStat &= ~msk;
ptcb->OSTCBStatPend = pend_stat;
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[y] |= ptcb->OSTCBBitX;
}
OS_EventTaskRemove(ptcb, pevent);
return (prio);
}
信号量semaphore的相关操作函数见os_sem.c。
(2)消息邮箱和消息队列
任务间的通信主要是通过消息邮箱和消息队列来实现的,消息队列用到一个新的结构体OS_Q,其定义如下:
typedef struct os_q {
struct os_q *OSQPtr;
void **OSQStart;
void **OSQEnd;
void **OSQIn;
void **OSQOut;
INT16U OSQSize;
INT16U OSQEntries;
} OS_Q;
使用消息队列需先定义指针数组void *MessageStorage[size] 。邮箱和队列具体函数实现同信号量类似。
(3)信号量集
信号量集的主要功能是实现多个信号量的组合,其各个信号量均为二值信号量。定义的信号量集和等待任务链表结构体如下:
OS_FLAG_GRP OSFlagTbl[OS_MAX_FLAGS];
OS_FLAG_GRP *OSFlagFreeList;
typedef struct os_flag_grp {
INT8U OSFlagType;
void *OSFlagWaitList;
OS_FLAGS OSFlagFlags;
} OS_FLAG_GRP;
typedef struct os_flag_node {
void *OSFlagNodeNext;
void *OSFlagNodePrev;
void *OSFlagNodeTCB;
void *OSFlagNodeFlagGrp;
OS_FLAGS OSFlagNodeFlags;
INT8U OSFlagNodeWaitType;
} OS_FLAG_NODE;