嵌入式μC/OS-II系統(tǒng)基礎(chǔ)之邵貝貝節(jié)選
μC/OS-II 通過 uCOS_II.H 中定義的 OS_EVENT 數(shù)據(jù)結(jié)構(gòu)來維護(hù)一個(gè)事件控制塊的所有信息
[程序清單 L6.1],也就是本章開篇講到的事件控制塊 ECB。該結(jié)構(gòu)中除了包含了事件本身的定
義,如用于信號量的計(jì)數(shù)器,用于指向郵箱的指針,以及指向消息隊(duì)列的指針數(shù)組等,還定義
了等待該事件的所有任務(wù)的列表。
typedef struct {
void *OSEventPtr; /* 指向消息或者消息隊(duì)列的指針 */
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任務(wù)列表 */
INT16U OSEventCnt; /* 計(jì)數(shù)器(當(dāng)事件是信號量時(shí)) */
INT8U OSEventType; /* 時(shí)間類型 */
INT8U OSEventGrp; /* 等待任務(wù)所在的組 */
} OS_EVENT;
.OSEventPtr 指針,只有在所定義的事件是郵箱或者消息隊(duì)列時(shí)才使用。 當(dāng)所定義的事件是
郵箱時(shí),它指向一個(gè)消息,而當(dāng)所定義的事件是消息隊(duì)列時(shí),它指向一個(gè)數(shù)據(jù)結(jié)構(gòu),詳見 6.06
節(jié)消息郵箱和 6.07 節(jié)消息隊(duì)列。
.OSEventTbl[] 和 .OSEventGrp 很像前面講到的 OSRdyTbl[]和 OSRdyGrp,只不過前兩者
包含的是等待某事件的任務(wù),而后兩者包含的是系統(tǒng)中處于就緒狀態(tài)的任務(wù)。(見 3.04 節(jié) 就
緒表)
.OSEventCnt 當(dāng)事件是一個(gè)信號量時(shí),.OSEventCnt 是用于信號量的計(jì)數(shù)器,(見 6.05 節(jié)
信號量)。
e .OSEventType 定義了事件的具體類型。它可以是信號量(OS_EVENT_SEM)、郵箱
(OS_EVENT_TYPE_MBOX)或消息隊(duì)列(OS_EVENT_TYPE_Q)中的一種。用戶要根據(jù)該域的具體值
來調(diào)用相應(yīng)的系統(tǒng)函數(shù),以保證對其進(jìn)行的操作的正確性。
下面的代碼將一個(gè)任務(wù)放到事件的等待任務(wù)列表中。
程序清單 L6.2 ——將一個(gè)任務(wù)插入到事件的等待任務(wù)列表中
pevent->OSEventGrp |= OSMapTbl[prio >> 3];
pevent->OSEventTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
程序清單 L6.3 從等待任務(wù)列表中刪除一個(gè)任務(wù)
if ((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) {
pevent->OSEventGrp &= ~OSMapTbl[prio >> 3];
}
程序清單 L6.4 在等待任務(wù)列表中查找最高優(yōu)先級的任務(wù)
y = OSUnMapTbl[pevent->OSEventGrp];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (y << 3) + x;
void OSEventWaitListInit (OS_EVENT *pevent)
{
INT8U i;
pevent->OSEventGrp = 0x00;
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
pevent->OSEventTbl[i] = 0x00;
}
}
程序清單 L6.6 使一個(gè)任務(wù)進(jìn)入就緒狀態(tài)
void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
{
OS_TCB *ptcb;
INT8U x;
INT8U y;
INT8U bitx;
INT8U bity;
INT8U prio;
y = OSUnMapTbl[pevent->OSEventGrp]; (1)
bity = OSMapTbl[y]; (2)
x = OSUnMapTbl[pevent->OSEventTbl[y]]; (3)
bitx = OSMapTbl[x]; (4)
prio = (INT8U)((y << 3) + x); (5)
if ((pevent->OSEventTbl[y] &= ~bitx) == 0) { (6)
pevent->OSEventGrp &= ~bity;
}
ptcb = OSTCBPrioTbl[prio]; (7)
ptcb->OSTCBDly = 0; (8)
ptcb->OSTCBEventPtr = (OS_EVENT *)0; (9)
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN
ptcb->OSTCBMsg = msg; (10)
#else
msg = msg;
#endif
ptcb->OSTCBStat &= ~msk; (11)
if (ptcb->OSTCBStat == OS_STAT_RDY) { (12)
OSRdyGrp |= bity; (13)
OSRdyTbl[y] |= bitx;
}
}
程序清單 L6.7 使一個(gè)任務(wù)進(jìn)入等待狀態(tài)
void OSEventTaskWait (OS_EVENT *pevent)
{
OSTCBCur->OSTCBEventPtr = pevent; (1)
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { (2)
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; (3)
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
程序清單 L6.8 因?yàn)榈却瑫r(shí)將任務(wù)置為就緒狀態(tài)
void OSEventTO (OS_EVENT *pevent)
{
if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0)
{ (1)
pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; (2)
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (3)
}
信號量
μC/OS-II 中的信號量由兩部分組成:一個(gè)是信號量的計(jì)數(shù)值,它是一個(gè) 16 位的無符號整
數(shù) (0 到 65,535 之間) ; 另一個(gè)是由等待該信號量的任務(wù)組成的等待任務(wù)表。 用戶要在 OS_CFG.H
中將 OS_SEM_EN 開關(guān)量常數(shù)置成 1,這樣μC/OS-II 才能支持信號量。信號量
μC/OS-II 中的信號量由兩部分組成:一個(gè)是信號量的計(jì)數(shù)值,它是一個(gè) 16 位的無符號整
數(shù) (0 到 65,535 之間) ; 另一個(gè)是由等待該信號量的任務(wù)組成的等待任務(wù)表。 用戶要在 OS_CFG.H
中將 OS_SEM_EN 開關(guān)量常數(shù)置成 1,這樣μC/OS-II 才能支持信號量。
程序清單 L6.9 建立一個(gè)信號量
OS_EVENT *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent;
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; (1)
if (OSEventFreeList != (OS_EVENT *)0) { (2)
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) { (3)
pevent->OSEventType = OS_EVENT_TYPE_SEM; (4)
pevent->OSEventCnt = cnt; (5)
OSEventWaitListInit(pevent); (6)
}
return (pevent); (7)
}
程序清單 L6.10 等待一個(gè)信號量
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)
OS_EXIT_CRITICAL();
*err = OS_ERR_EVENT_TYPE;
}
if (pevent->OSEventCnt > 0) { (2)
pevent->OSEventCnt--; (3)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
6-15
} else if (OSIntNesting > 0) { (4)
OS_EXIT_CRITICAL();
*err = OS_ERR_PEND_ISR;
} else {
OSTCBCur->OSTCBStat |= OS_STAT_SEM; (5)
OSTCBCur->OSTCBDly = timeout; (6)
OSEventTaskWait(pevent); (7)
OS_EXIT_CRITICAL();
OSSched(); (8)
OS_ENTER_CRITICAL();
if (OSTCBCur->OSTCBStat & OS_STAT_SEM) { (9)
OSEventTO(pevent); (10)
OS_EXIT_CRITICAL();
*err = OS_TIMEOUT;
} else {
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (11)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
}
}
程序清單 L6.11 發(fā)出一個(gè)信號量
INT8U OSSemPost (OS_EVENT *pevent)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
if (pevent->OSEventGrp) { (2)
OSEventTaskRdy(pevent, (void *)0, OS_STAT_SEM); (3)
OS_EXIT_CRITICAL();
OSSched(); (4)
return (OS_NO_ERR);
} else {
if (pevent->OSEventCnt < 65535) {
pevent->OSEventCnt++; (5)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
} else {
OS_EXIT_CRITICAL();
return (OS_SEM_OVF);
}
}
}
程序清單 L6.12 無等待地請求一個(gè)信號量
INT16U OSSemAccept (OS_EVENT *pevent)
{
INT16U cnt;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)
OS_EXIT_CRITICAL();
return (0);
}
cnt = pevent->OSEventCnt; (2)
if (cnt > 0) { (3)
pevent->OSEventCnt--; (4)
}
OS_EXIT_CRITICAL();
return (cnt); (5)
}
程序清單 L6.13 查詢一個(gè)信號量的狀態(tài)
INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata)
{
INT8U i;
INT8U *psrc;
INT8U *pdest;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
pdata->OSEventGrp = pevent->OSEventGrp; (2)
psrc = &pevent->OSEventTbl[0];
pdest = &pdata->OSEventTbl[0];
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*pdest++ = *psrc++;
}
pdata->OSCnt = pevent->OSEventCnt; (3)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
程序清單 L6.14 建立一個(gè)郵箱
OS_EVENT *OSMboxCreate (void *msg)
6-20
{
OS_EVENT *pevent;
OS_ENTER_CRITICAL();
pevent = OSEventFreeList;
if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_MBOX; (1)
pevent->OSEventPtr = msg; (2)
OSEventWaitListInit(pevent);
}
return (pevent); (3)
}
程序清單 L6.15 等待一個(gè)郵箱中的消息
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
void *msg;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)
OS_EXIT_CRITICAL();
*err = OS_ERR_EVENT_TYPE;
return ((void *)0);
}
msg = pevent->OSEventPtr;
if (msg != (void *)0) { (2)
pevent->OSEventPtr = (void *)0; (3)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else if (OSIntNesting > 0) { (4)
OS_EXIT_CRITICAL();
*err = OS_ERR_PEND_ISR;
} else {
OSTCBCur->OSTCBStat |= OS_STAT_MBOX; (5)
OSTCBCur->OSTCBDly = timeout;
OSEventTaskWait(pevent);
OS_EXIT_CRITICAL();
OSSched();
OS_ENTER_CRITICAL();
if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) { (6)
OSTCBCur->OSTCBMsg = (void *)0;
OSTCBCur->OSTCBStat = OS_STAT_RDY;
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) { (7)
OSEventTO(pevent); (8)
OS_EXIT_CRITICAL();
msg = (void *)0; (9)
*err = OS_TIMEOUT;
} else {
6-22
msg = pevent->OSEventPtr; (10)
pevent->OSEventPtr = (void *)0;
(11)
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (12)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
}
return (msg);
}
程序清單 L6.16 向郵箱中發(fā)送一條消息
INT8U OSMboxPost (OS_EVENT *pevent, void *msg)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
if (pevent->OSEventGrp) { (2)
OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); (3)
OS_EXIT_CRITICAL();
OSSched(); (4)
return (OS_NO_ERR);
} else {
if (pevent->OSEventPtr != (void *)0) { (5)
OS_EXIT_CRITICAL();
return (OS_MBOX_FULL);
} else {
pevent->OSEventPtr = msg; (6)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
}
}
程序清單 L6.16 向郵箱中發(fā)送一條消息
INT8U OSMboxPost (OS_EVENT *pevent, void *msg)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
if (pevent->OSEventGrp) { (2)
OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); (3)
OS_EXIT_CRITICAL();
OSSched(); (4)
return (OS_NO_ERR);
} else {
if (pevent->OSEventPtr != (void *)0) { (5)
OS_EXIT_CRITICAL();
return (OS_MBOX_FULL);
} else {
pevent->OSEventPtr = msg; (6)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
}
}
程序清單 L6.17 無等待地從郵箱中得到消息
void *OSMboxAccept (OS_EVENT *pevent)
{
void *msg;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)
OS_EXIT_CRITICAL();
return ((void *)0);
}
msg = pevent->OSEventPtr; (2)
if (msg != (void *)0) { (3)
pevent->OSEventPtr = (void *)0; (4)
}
OS_EXIT_CRITICAL();
return (msg); (5)
}
程序清單 L6.18 查詢郵箱的狀態(tài)
INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata)
{
INT8U i;
6-25
INT8U *psrc;
INT8U *pdest;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
pdata->OSEventGrp = pevent->OSEventGrp; (2)
psrc = &pevent->OSEventTbl[0];
pdest = &pdata->OSEventTbl[0];
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*pdest++ = *psrc++;
}
pdata->OSMsg = pevent->OSEventPtr; (3)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
程序清單 L6.19 使用郵箱作為二值信號量
OS_EVENT *MboxSem;
void Task1 (void *pdata)
{
INT8U err;
for (;;) {
OSMboxPend(MboxSem, 0, &err); /* 獲得對資源的訪問權(quán) */
6-26
.
. /* 任務(wù)獲得信號量,對資源進(jìn)行訪問 */
.
OSMboxPost(MboxSem, (void*)1); /* 釋放對資源的訪問權(quán) */
}
}
程序清單 L6.21 建立一個(gè)消息隊(duì)列
OS_EVENT *OSQCreate (void **start, INT16U size)
{
OS_EVENT *pevent;
OS_Q *pq;
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; (1)
if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; (2)
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) {
OS_ENTER_CRITICAL();
pq = OSQFreeList; (3)
if (OSQFreeList != (OS_Q *)0) {
OSQFreeList = OSQFreeList->OSQPtr;
}
OS_EXIT_CRITICAL();
if (pq != (OS_Q *)0) {
pq->OSQStart = start; (4)
pq->OSQEnd = &start[size];
pq->OSQIn = start;
pq->OSQOut = start;
pq->OSQSize = size;
pq->OSQEntries = 0;
pevent->OSEventType = OS_EVENT_TYPE_Q; (5)
pevent->OSEventPtr = pq; (6)
OSEventWaitListInit(pevent); (7)
} else {
OS_ENTER_CRITICAL();
pevent->OSEventPtr = (void *)OSEventFreeList; (8)
6-33
OSEventFreeList = pevent;
OS_EXIT_CRITICAL();
pevent = (OS_EVENT *)0;
}
}
return (pevent); (9)
}
9.03 OS_CPU.H 文件
OS_CPU.H 文件中包含與處理器相關(guān)的常量,宏和結(jié)構(gòu)體的定義。程序清單L9.2是為80x86編
寫的OS_CPU.H文件的內(nèi)容。
9.04 OS_CPU_A.ASM
μC/OS-II 的移植需要用戶改寫OS_CPU_A.ASM中的四個(gè)函數(shù):
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
9.04.01 OSStartHighRdy()
該函數(shù)由SStart()函數(shù)調(diào)用,功能是運(yùn)行優(yōu)先級最高的就緒任務(wù),在調(diào)用OSStart()之前,
用戶必須先調(diào)用OSInit(),并且已經(jīng)至少創(chuàng)建了一個(gè)任務(wù)(請參考OSTaskCreate()和
OSTaskCreateExt()函數(shù))。OSStartHighRdy()默認(rèn)指針OSTCBHighRdy指向優(yōu)先級最高就緒任務(wù)
的任務(wù)控制塊(OS_TCB)(在這之前OSTCBHighRdy已由OSStart()設(shè)置好了)。圖F9.3給出了由
函 數(shù) OSTaskCreate() 或 OSTaskCreateExt() 創(chuàng) 建 的 任 務(wù) 的 堆 棧 結(jié) 構(gòu) 。 很 明 顯 ,
OSTCBHighRdy->OSTCBStkPtr指向的是任務(wù)堆棧的頂端。
程序清單L 9.3 OSStartHighRdy().
_OSStartHighRdy PROC FAR
MOV AX, SEG _OSTCBHighRdy ; 載入 DS
MOV DS, AX ;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr
(1)
MOV SS, ES:[BX+2] ;
MOV SP, ES:[BX+0] ;
;
POP DS ; 恢復(fù)任務(wù)環(huán)境 (2)
POP ES ; (3)
POPA ; (4)
;
IRET ; 運(yùn)行任務(wù) (5)
_OSStartHighRdy ENDP
9.05 OS_CPU_C.C
μC/OS-II 的移植需要用戶改寫OS_CPU_C.C中的六個(gè)函數(shù):
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
OSTaskResume ()的返回值為下述之一:
? OS_NO_ERR:函數(shù)調(diào)用成功。
? OS_TASK_RESUME_PRIO:要喚醒的任務(wù)不存在。
? OS_TASK_NOT_SUSPENDED:要喚醒的任務(wù)不在掛起狀態(tài)。
? OS_PRIO_INVALID:參數(shù)指定的優(yōu)先級大于或等于 OS_LOWEST_PRIO。
OS_MAX_EVENTS 定義系統(tǒng)中最大的事件控制塊的數(shù)量。系統(tǒng)中的每一個(gè)消息郵箱,消息隊(duì)
列,信號量都需要一個(gè)事件控制塊。例如,系統(tǒng)中有 10 個(gè)消息郵箱,5 個(gè)消息隊(duì)列,3 個(gè)信號
量,則 OS_MAX_EVENTS 最小應(yīng)該為 18。只要程序中用到了消息郵箱,消息隊(duì)列或是信號量,
則 OS_MAX_EVENTS 最小應(yīng)該設(shè)置為 2。