1. 程式人生 > >uC/OS-II原始碼分析(總體思路 三)

uC/OS-II原始碼分析(總體思路 三)

在Task中,一般執行一段時間之後呼叫OSTimeDly推遲一段時間再繼續執行,OSTimeDly將本程序從Ready TCBList中刪除,然後將Delay的時間設定給OSTCBDly,最後呼叫OS_Sched進行程序排程。

void OSTimeDly (INT16U ticks)

{

 INT8U y;

 if (ticks > 0) {

 OS_ENTER_CRITICAL();

 y = OSTCBCur->OSTCBY;

 OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;

 if (OSRdyTbl[y] == 0) {

 OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

 }

 OSTCBCur->OSTCBDly = ticks;

 OS_EXIT_CRITICAL();

 OS_Sched();

 }

}

如果ticks為零,說明不需延遲,則什麼事情都不做。否則,呼叫OS_ENTER_CRITICAL進入臨界段,將本程序從Ready TCBList中刪除的程式碼如下:

 y = OSTCBCur->OSTCBY;

 OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;

 if (OSRdyTbl[y] == 0) {

 OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

 }

y為當前程序所在Group,OSRdyTbl[y]為該Group所在位元組,&=~則將該位元組中本程序所佔用的Bit清零。如果OSRdyTbl[y]為0,則說明這個Group中沒有程序處於Ready狀態,則將OSRdyGrp中該Group所佔用的Bit清零。

然後將ticks儲存在OSTCBDly中,每次OSTimeTick執行時會將這個值減一直至為零。

呼叫OS_EXIT_CRITICAL離開臨界段,緊接著呼叫OS_Sched進入排程例程。

OS_Sched

OS_Sched是程序排程所使用的函式,在這裡面找到最高優先順序的程序,然後切換到該程序執行。

void OS_Sched (void)

{

 INT8U y;

 OS_ENTER_CRITICAL();

 if (OSIntNesting == 0) {

 if (OSLockNesting == 0) {

 y = OSUnMapTbl[OSRdyGrp];

 OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

 if (OSPrioHighRdy != OSPrioCur) {

 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

 OSCtxSwCtr++;

 OS_TASK_SW();

 }

 }

 }

 OS_EXIT_CRITICAL();

}

OS_Sched不允許在中斷巢狀中呼叫,因此先判斷是否是中斷巢狀,並且是否限制程序排程,這兩個條件都滿足之後,找到最高優先順序的程序,如果這個程序不是當前程序,則將新的程序TCB指標儲存到OSTCBHighRdy中,為排程計數器OSCtxSwCtr加一,然後呼叫巨集OS_TASK_SW()進行切換。

OS_TASK_SW()巨集也是一個自定義的巨集,uC/OS-II推薦使用軟中斷方式實現。

OSCtxSw是一箇中斷響應函式,一般我們在初始化時將這個軟終端和OSCtxSw掛接好。在OSCtxSw中所需要做的事情就是將當前暫存器的值儲存到當前堆疊中,然後切換堆疊到新程序的堆疊,將暫存器的值出棧,然後呼叫中斷返回指令IRET就返回到新程序中斷前的地方繼續執行了。

定時中斷

uC/OS-II的定時中斷必須在OSStart之後初始化,而不能在OSStart之前,因為害怕第一個TimeTick發生時第一個程序還沒有開始執行,而這時uC/OS是處於不可預期狀態,會導致宕機。

因此對於定時中斷,我一般是放在最高階程序的初始化中進行,然後將定時中斷和OSTickISR掛接。

OSTickISR也是一個使用者自定義函式,所要完成的功能一個是儲存當前的暫存器到當前堆疊將OSIntNesting加一,然後呼叫uC/OS提供的OSTimeTick函式,然後呼叫OSIntExit()將OSIntNesting減一,最後將各暫存器值出棧,使用中斷返回指令IRET返回。

OSTimeTick在每個時鐘中斷中被呼叫一次,在該函式中會更新各個程序TCB所對應的OSTCBDly,如果該OSTCBDly減為0,則對應的TCB就被放入Ready TCBList中。

 OS_ENTER_CRITICAL();

 OSTime++;

 OS_EXIT_CRITICAL();

 ptcb = OSTCBList;

 while (ptcb->OSTCBPrio != OS_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 &= ~OS_STAT_PEND_ANY;

 ptcb->OSTCBPendTO = TRUE;

 } else {

 ptcb->OSTCBPendTO = FALSE;

 }

 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {

 OSRdyGrp |= ptcb->OSTCBBitY;

 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

 }

 }

 }

 ptcb = ptcb->OSTCBNext;

 OS_EXIT_CRITICAL();

 }

首先在臨界段將OSTime加一,然後遍歷整個非Free的TCBList,如果OSTCBDly不為0,則,將OSTCBDly減一,如果這時OSTCBDly為0,而且TCB對應的程序需要等待任何訊號量或Event等,則說明超時時間到了,將當前TCB的State中OS_STAT_PEND_ANY位去掉,然後將OSTCBPendTo設定為TRUE,表示這是PEND的超時,否則設定OSTCBPendTO為FALSE。

如果OSTCBDly減為零,且該程序沒有Suspend,則將該程序放入Ready TCBList中,使用方法同TaskCreate中的方法。

然後我們來說說OSIntExit這個函式。該函式程式碼如下:

void OSIntExit (void)

{

 INT8U y;

 if (OSRunning == TRUE) {

 OS_ENTER_CRITICAL();

 if (OSIntNesting > 0) {

 OSIntNesting--;

 }

 if (OSIntNesting == 0) {

 if (OSLockNesting == 0) {

 y = OSUnMapTbl[OSRdyGrp];

 OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

 if (OSPrioHighRdy != OSPrioCur) {

 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

 OSCtxSwCtr++;

 OSIntCtxSw();

 }

 }

 }

OS_EXIT_CRITICAL();

 }

}

首先判斷OSRunning是否為1,也就是OS是否在執行,當然沒有執行就什麼都不做。

然後將OSIntNesting減一,這個是需要在臨界段進行的。如果OSIntNesting減為零,並且沒有限制程序切換,則找到當前最高優先順序的程序(方法同OS_Sched()),然後呼叫OSIntCtxSw進行程序切換。

OSIntCtxSw()是使用者自定義函式,該函式的主要功能與OSCtxSw類似,只是需要對當前的堆疊進行稍微的調整,將OSIntExit和OSIntCtxSw呼叫所需要的堆疊去掉,然後做的和OSCtxSw一樣。

在實際的Porting中發現要去掉OSIntExit和OSIntCtxSw呼叫所佔用的堆疊還是比較麻煩的,因此我就現在OSTickISR剛開始的時候儲存好現場之後就將堆疊指標賦給當前程序TCB的OSStkPtr,這樣,在OSIntCtxSw中就不需要重新對當前堆疊的值進行儲存,只需進行切換就可以了