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 */
INT8U OSEventName[OS_EVENT_NAME_SIZE];
} OS_EVENT;
信号量和邮箱都是使用OS_EVENT这个结构体。在OSInit()初始化函数里面会创建OS_EVENT结构体的链表。
` OSInitHookBegin(); /* Call port specific initialization code */
OS_InitMisc(); /* Initialize miscellaneous variables */
OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */`
最后一行OS_InitEventList();就是建立OS_EXENT链表的。
感觉很像任务。任务有OS_TCB,事件有OS_EVENT;任务有OS_TCBInit(通常被OSTaskCreate调用)函数从OSTCBFreeList为首的链表里面提取空闲OS_TCB,事件有OSSemCreate或OSMboxCreate(OS_EVNET相比之下并没有类似于OS_TCBInit的函数)。
OS_EVENT的类别由由选用的函数决定,选OSSemCreate,创建出来的就是Sem类型,反之是mbox类型。
pevent = OSEventFreeList; /* Get next free event control block */
if (OSEventFreeList != (OS_EVENT *)0) { /* See if pool of free ECB pool was empty */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
取用OS_EVENT。就跟OSTaskCreate中的OS_TCBInit一样。
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_MBOX;
pevent->OSEventCnt = 0;
pevent->OSEventPtr = pmsg; /* Deposit message in event control block */
#if OS_EVENT_NAME_SIZE > 1
pevent->OSEventName[0] = '?';
pevent->OSEventName[1] = OS_ASCII_NUL;
#endif
OS_EventWaitListInit(pevent);
}
如果取用成功(指针变量不为空),进行初始化。其中OS_EventWaitListInit函数将每一个OS_EVENT具有的OSEventGrp和OSEventTbl清零(这两个很像OSRdyGrp和OSRdyTbl,就绪表),事件可以通过查找他们各自的OSEventGrp和OSEventTbl来确定应该出发哪一个事件(OSEventTbl表中所记录的优先级最高的任务)。由OS_EVENT自己查找的方法通常在OSSemPend和OSMboxPend中体现。
邮箱:
pmsg = pevent->OSEventPtr;
if (pmsg != (void *)0) { /* See if there is already a message */
pevent->OSEventPtr = (void *)0; /* Clear the mailbox */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return (pmsg); /* Return the message received (or NULL) */
}
由于定义方式不同,返回变量也不同。
我写的程序里面邮箱的初始化OSMboxCreate((void*)0);这样的话,第一遍经过OSMboxPend时就进入到上述判断语句。
而进入接下来的代码。
OSTCBCur->OSTCBStat |= OS_STAT_MBOX; /* Message not available, task will pend */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout; /* Load timeout in TCB */
OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next highest priority task ready to run */
OS_ENTER_CRITICAL();
先对当前的OS_TCB进行操作,然后调用OS_EventTaskWait把当前任务的优先级写入OSEventTbl,再从OSRdyTbl把此优先级从表中拿出。最后由OS_Sched切换成优先级最高的任务。
switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */
case OS_STAT_PEND_OK:
pmsg = OSTCBCur->OSTCBMsg;
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT:
pmsg = (void *)0;
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */
break;
case OS_STAT_PEND_TO:
default:
OS_EventTaskRemove(OSTCBCur, pevent);
pmsg = (void *)0;
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */
break;
}
主要是这段代码
pmsg = OSTCBCur->OSTCBMsg;
*perr = OS_ERR_NONE;
break;
它与结束时的
return (pmsg);
相对应。这里就体现出OSMboxPend和OSSemPend的不同。
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)
OSSemPend函数压根就没想过返回任何东西,而要返回OSMboxPend所传送的邮件。
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */
#if (OS_EVENT_MULTI_EN > 0)
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
OSTCBCur->OSTCBMsg = (void *)0; /* Clear received message */
OS_EXIT_CRITICAL();
return (pmsg);
问题是结尾为什么把当前的OS_TCB又设置一遍?
下面再谈谈OSMboxPost函数。
if (pevent->OSEventGrp != 0) { /* See if any task pending on mailbox */
/* Ready HPT waiting on event */
(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK);
OS_EXIT_CRITICAL();
OS_Sched(); /* Find highest priority task ready to run */
return (OS_ERR_NONE);
}
通常会经由这段代码返回。代码先检查事件中的OSEventTbl有没有等待任务。应该是有的。然后就是调用OS_EventTaskRdy这个函数了。
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 */
OS_EventTaskRdy函数根据OS_EVENT提供的信息,找出OSEventTbl中优先级最高的优先级。
ptcb = OSTCBPrioTbl[prio]; /* Point to this task's OS_TCB */
ptcb->OSTCBDly = 0;
再OSTCBPrioTbl中找到相应的OS_TCB,进行一系列操作。
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 */
把任务写入OSRdyTbl中。再用EventTaskRemove将对应优先级从OSEventTbl移除(此OS_EVENT专用的)。
#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
这段代码应该是关于互斥量。
至此,三个比较重要的函数(OSMboxCreate,OSMboxPend,OSMboxPost)介绍完了。信号量跟邮箱差不多。参考一下就应该差不多。