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個空事件控制塊。每當創建一個事件時,系統會從時間控制塊鏈表中取出一個空事件控制塊,並對他進行初始化以描述該事件,當應用程序刪除一個事件時,系統會將該事件的控制塊歸還給空事件控制塊鏈表。空事件控制塊鏈表如下所示