it's this section's map.
1、事件控制块(ECB)初始化OS_InitEventList
事件初始化代码在操作系统初始化函数OS_Init中被调用,其函数名为OS_InitEventList,定义如下
1 static void OS_InitEventList (void) 2 { 3 #if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0) 4 #if (OS_MAX_EVENTS > 1) 5 INT16U i; 6 OS_EVENT *pevent1; 7 OS_EVENT *pevent2; 8 9 10 OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* Clear the event table */ 11 pevent1 = &OSEventTbl[0]; 12 pevent2 = &OSEventTbl[1]; 13 for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { /* Init. list of free EVENT control blocks */ 14 pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; 15 pevent1->OSEventPtr = pevent2; 16 #if OS_EVENT_NAME_SIZE > 1 17 pevent1->OSEventName[0] = '?'; /* Unknown name */ 18 pevent1->OSEventName[1] = OS_ASCII_NUL; 19 #endif 20 pevent1++; 21 pevent2++; 22 } 23 pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; 24 pevent1->OSEventPtr = (OS_EVENT *)0; 25 #if OS_EVENT_NAME_SIZE > 1 26 pevent1->OSEventName[0] = '?'; 27 pevent1->OSEventName[1] = OS_ASCII_NUL; 28 #endif 29 OSEventFreeList = &OSEventTbl[0]; 30 #else 31 OSEventFreeList = &OSEventTbl[0]; /* Only have ONE event control block */ 32 OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED; 33 OSEventFreeList->OSEventPtr = (OS_EVENT *)0; 34 #if OS_EVENT_NAME_SIZE > 1 35 OSEventFreeList->OSEventName[0] = '?'; /* Unknown name */ 36 OSEventFreeList->OSEventName[1] = OS_ASCII_NUL; 37 #endif 38 #endif 39 #endif 40 }
line 10清空了之前的事件表
line 13~ line 22对除了最后一个之外的所有ECB进行了初始化,并构建了单向链表。
line 23~ line24使最后一个ECB指向了空地址0.
最后line 29将表头地址赋给全局变量OSEventFreeList.
2、事件等待表初始化OS_EventWaitListInit
当建立一个事件或消息时,如信号量的建立函数OSSemCreate等,需要对事件等待表进行初始化。事件等待表初始化函数实现对制定任务块中事件等待表和时间等待组清零,可悲操作系统的其它函数调用。
1 #if (OS_EVENT_EN) 2 void OS_EventWaitListInit (OS_EVENT *pevent) 3 { 4 #if OS_LOWEST_PRIO <= 63 5 INT8U *ptbl; 6 #else 7 INT16U *ptbl; 8 #endif 9 INT8U i; 10 11 12 pevent->OSEventGrp = 0; /* No task waiting on event */ 13 ptbl = &pevent->OSEventTbl[0]; 14 15 for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { 16 *ptbl++ = 0; 17 } 18 } 19 #endif
总的来说,这个函数的功能即把事件等待表再次初始化了一遍,做的和第一个函数中line 13~line 22中一样的工作,最后一个OSEventTbl被无视。
但是我不知道为什么要再Init一遍。
3、设置事件等待、取消事件等待、将等待的事件就绪
这三块我觉得与之前任务管理部分的操作有些类似,所以放一块儿讲。类比在看完源码后给出。
3.1 设置事件等待OS_EventTaskWait
当任务等待事件发生,并获得ECB后,需要在ECB中标记任务在等待事件的发生。才可以在事件发生时取消任务的阻塞。将任务在ECB中进行登记的函数是OS_EventTaskWait,参数是事件控制块的指针。
与OS_EventWaitListInit类似,OS_EventTaskWait在任务调用信号量、邮箱等时间等待函数时被对应函数调用。因为任务优先级可以通过当前TCB指针OSTCBCur得到,因此没有作为参数传递。
源码如下
1 #if (OS_EVENT_EN) 2 void OS_EventTaskWait (OS_EVENT *pevent) 3 { 4 INT8U y; 5 6 7 OSTCBCur->OSTCBEventPtr = pevent; /* Store ptr to ECB in TCB */ 8 9 pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */ 10 pevent->OSEventGrp |= OSTCBCur->OSTCBBitY; 11 12 y = OSTCBCur->OSTCBY; /* Task no longer ready */ 13 OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX; 14 if (OSRdyTbl[y] == 0) { 15 OSRdyGrp &= ~OSTCBCur->OSTCBBitY; /* Clear event grp bit if this was only task pending */ 16 } 17 } 18 #endif
line 7中:在TCB的OSTCBEventPtr域存储ECB指针,以后通过该任务的TCB可以直接找到这个事件的ECB。
之后做了两件事——标记和取消标记。听闻这里的操作和就绪表中设置就绪标志的完全一致的,那便先放着吧。
3.2 取消事件等待OS_EventTaskRemove
这个函数与上一个函数是相反的操作。当一个事件由于某种原因不再需要等待事件时,就需要在事件的等待表中取消该事件的等待标志。
1 #if (OS_EVENT_EN) 2 void OS_EventTaskRemove (OS_TCB *ptcb, 3 OS_EVENT *pevent) 4 { 5 INT8U y; 6 7 8 y = ptcb->OSTCBY; 9 pevent->OSEventTbl[y] &= ~ptcb->OSTCBBitX; /* Remove task from wait list */ 10 if (pevent->OSEventTbl[y] == 0) { 11 pevent->OSEventGrp &= ~ptcb->OSTCBBitY; 12 } 13 } 14 #endif
line 9:在事件等待表中删除事件等待标志
line 10:若该行已没有任务等待
line 11:删除事件等待组的事件等待标志
3.3 将等待事件的任务就绪
任务因为等待时间而在ECB中登记自己的等待,当事件发生时,如果该任务是事件等待表中优先级最高的任务,就绪就取消等待而回到就绪状态。
源码如下:
1 #if (OS_EVENT_EN) 2 INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg, INT8U msk, INT8U pend_stat) 3 { 4 OS_TCB *ptcb; 5 INT8U y; 6 INT8U x; 7 INT8U prio; 8 #if OS_LOWEST_PRIO > 63 9 INT16U *ptbl; 10 #endif 11 12 13 #if OS_LOWEST_PRIO <= 63 14 y = OSUnMapTbl[pevent->OSEventGrp]; /* Find HPT waiting for message */ 15 x = OSUnMapTbl[pevent->OSEventTbl[y]]; 16 prio = (INT8U)((y << 3) + x); /* Find priority of task getting the msg */ 17 #else 18 if ((pevent->OSEventGrp & 0xFF) != 0) { /* Find HPT waiting for message */ 19 y = OSUnMapTbl[ pevent->OSEventGrp & 0xFF]; 20 } else { 21 y = OSUnMapTbl[(pevent->OSEventGrp >> 8) & 0xFF] + 8; 22 } 23 ptbl = &pevent->OSEventTbl[y]; 24 if ((*ptbl & 0xFF) != 0) { 25 x = OSUnMapTbl[*ptbl & 0xFF]; 26 } else { 27 x = OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8; 28 } 29 prio = (INT8U)((y << 4) + x); /* Find priority of task getting the msg */ 30 #endif 31 32 ptcb = OSTCBPrioTbl[prio]; /* Point to this task's OS_TCB */ 33 ptcb->OSTCBDly = 0; /* Prevent OSTimeTick() from readying task */ 34 #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) 35 ptcb->OSTCBMsg = pmsg; /* Send message directly to waiting task */ 36 #else 37 pmsg = pmsg; /* Prevent compiler warning if not used */ 38 #endif 39 ptcb->OSTCBStat &= ~msk; /* Clear bit associated with event type */ 40 ptcb->OSTCBStatPend = pend_stat; /* Set pend status of post or abort */ 41 /* See if task is ready (could be susp'd) */ 42 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { 43 OSRdyGrp |= ptcb->OSTCBBitY; /* Put task in the ready to run list */ 44 OSRdyTbl[y] |= ptcb->OSTCBBitX; 45 } 46 47 OS_EventTaskRemove(ptcb, pevent); /* Remove this task from event wait list */ 48 #if (OS_EVENT_MULTI_EN > 0) 49 if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) { /* Remove this task from events' wait lists */ 50 OS_EventTaskRemoveMulti(ptcb, ptcb->OSTCBEventMultiPtr); 51 ptcb->OSTCBEventPtr = (OS_EVENT *)pevent;/* Return event as first multi-pend event ready*/ 52 } 53 #endif 54 55 return (prio); 56 } 57 #endif
几个参数的解析如下:
- pevent:对应ECB指针;
- pmsg:消息指针。当使用诸如消息队列或者消息邮箱的消息服务的时候使用该指针。当时是信号量或其他事件的时候不使用该指针;
- msk:清除状态位的掩码;使用该参数的目的是不需要操作系统判定是何种事件发生而增加运行成本。
- pend_stat:表示等待结束、任务就绪的原因(以宏来表示);
- 返回任务优先级(任务优先级seems to be unique)。
line 13~ line 30在事件等待表和事件等待组中找到最高优先级的等待任务的优先级(line 17~ line 30是任务数多于63的情况,书上未提);
line 32 根据优先级在优先级指针表中找到该任务的TCB指针;
line 33~ line 40对TCB的相关参数进行赋值;
line 42~ line 45判断任务是否被挂起,如果未被挂起就将任务就绪,完成从阻塞态到就绪态的转换;
line 47 调用OS_EventTaskRemove函数在ECB的事件等待表中删除该任务;
line 55返回任务优先级。
这三个函数的关系如图(略简陋)
Done