移植μC/OS-Ⅱ
圖 8.2 在ISR執行過程中的堆棧內容.

接著,CPU會調用正確的ISR。μC/OS-Ⅱ要求用戶的ISR在開始時要保存剩下的處理器寄存器[F8.2(2)]。一旦寄存器保存好了,μC/OS-Ⅱ就要求用戶或者調用OSIntEnter(),或者將變量OSIntNesting加1。在這個時候,被中斷任務的堆棧中只包含了被中斷任務的寄存器內容。現在,ISR可以執行中斷服務了。并且如果ISR發消息給任務(通過調用OSMboxPost()或OSQPost()), 恢復任務(通過調用OSTaskResume()), 或者調用OSTimeTick()或OSTimeDlyResume()的話,有可能使更高優先級的任務處于就緒狀態。
假設有一個更高優先級的任務處于就緒狀態。μC/OS-Ⅱ要求用戶的ISR在完成中斷服務的時候調用OSIntExit()。OSIntExit()會告訴μC/OS-Ⅱ到了返回任務級代碼的時間了。
調用OSIntExit()會導致調用者的返回地址被保存到被中斷的任務的堆棧中[F8.2(3)]。
OSIntExit()剛開始時會禁止中斷,因為它需要執行臨界段的代碼。根據OS_ENTER_CRITICAL()的不同執行過程(參看8.03.02),處理器的狀態寄存器會被保存到被中斷的任務的堆棧中[F8.2(4)]。OSIntExit()注意到由于有更高優先級的任務處于就緒狀態,被中斷的任務已經不再是要繼續執行的任務了。在這種情況下,指針OSTCBHighRdy會被指向新任務的OS_TCB,并且OSIntExit()會調用OSIntCtxSw()來執行任務切換。調用OSIntCtxSw()也同樣使返回地址被保存到被中斷的任務的堆棧中[F8.2(5)]。
在用戶切換任務的時候,用戶只想將某些項([F8.2(1)]和[F8.2(2)])保留在堆棧中,并忽略其它項(F8.2(3),(4)和(5)) 。這是通過調整堆棧指針(加一個數在堆棧指針上)來完成的[F8.2(6)]。加在堆棧指針上的數必須是明確的,而這個數主要依賴于移植的目標處理器(地址空間可能是16,32或64位),所用的編譯器,編譯器選項,內存模式等等。另外,處理器狀態字可能是8,16,32甚至64位寬,并且OSIntExit()可能會分配局部變量。有些處理器允許用戶直接增加常量到堆棧指針中,而有些則不允許。在后一種情況下,可以通過簡單的執行一定數量的pop(出棧)指令來實現相同的功能。一旦堆棧指針完成調整,新的堆棧指針會被保存到被切換出去的任務的OS_TCB中[F8.2(7)]。
OSIntCtxSw()的原型如程序清單L8.3所示。這些代碼必須寫在匯編語言中,因為用戶不能直接從C語言中訪問CPU寄存器。如果用戶的編譯器支持插入匯編語言代碼的話,用戶就可以將OSIntCtxSw()代碼放到OS_CPU_C.C文件中,而不放到OS_CPU_A.ASM文件中。正如用戶所看到的那樣,除了第一行以外,OSIntCtxSw()的代碼與OSCtxSw()是一樣的。這樣在移植實例中,用戶可以通過“跳轉”到OSCtxSw()中來減少 OSIntCtxSw()代碼量。
程序清單 L8.3 OSIntCtxSw()的原型
voidOSIntCtxSw(void)
{
調整堆棧指針來去掉在調用:
OSIntExit(),
OSIntCtxSw()過程中壓入堆棧的多余內容;
將當前任務堆棧指針保存到當前任務的OS_TCB中:
OSTCBCur->OSTCBStkPtr= 堆棧指針;
調用用戶定義的OSTaskSwHook();
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy;
得到需要恢復的任務的堆棧指針:
堆棧指針 =OSTCBHighRdy->OSTCBStkPtr;
將所有處理器寄存器從新任務的堆棧中恢復出來;
執行中斷返回指令;
}
OSIntCtxSw()是μC/OS-Ⅱ(和μC/OS)中唯一的與編譯器相關的函數;在我收到的e-mail中,關于該函數的e-mail明顯多于關于μC/OS其它方面的。如果在多次任務切換后用戶的系統崩潰了,用戶應該懷疑堆棧指針在OSIntCtxSw()中是否被正確地調整了。
8.04.04OSTickISR()
μC/OS-Ⅱ要求用戶提供一個時鐘資源來實現時間的延時和期滿功能。時鐘節拍應該每秒鐘發生10-100次。 為了完成該任務, 可以使用硬件時鐘, 也可以從交流電中獲得50/60Hz的時鐘頻率。
用戶必須在開始多任務調度后(即調用OSStart()后)允許時鐘節拍中斷。換句話說,就是用戶應該在OSStart()運行后,μC/OS-Ⅱ啟動運行的第一個任務中初始化節拍中斷。通常所犯的錯誤是在調用OSInit()和OSStart()之間允許時鐘節拍中斷(如程序清單L8.4所示)。
程序清單 L8.4 在不正確的位置啟動時鐘節拍中斷
voidmain(void)
{
.
.
OSInit();/* 初始化 ? μC/OS-II*/
.
.
/* 應用程序初始化代碼 ...*/
/*... 調用OSTaskCreate()建立至少一個任務 */
.
.
允許時鐘節拍中斷;/* 千萬不要在這里允許!!! */
.
.
OSStart();/* 開始多任務調度 */
}
有可能在μC/OS-Ⅱ開始執行第一個任務前時鐘節拍中斷就發生了。在這種情況下,μC/OS-Ⅱ的運行狀態不確定,用戶的應用程序也可能會崩潰。
時鐘節拍ISR的原型如程序清單L8.5所示。這些代碼必須寫在匯編語言中,因為用戶不能直接從C語言中訪問CPU寄存器。如果用戶的處理器可以通過單條指令來增加OSIntNesting,那么用戶就沒必要調用 OSIntEnter()了。增加OSIntNesting要比通過函數調用和返回快得多。OSIntEnter()只增加OSIntNesting,并且作為臨界段代碼中受到保護。
程序清單 L8.5 時鐘節拍ISR的原型
voidOSTickISR(void)
{
保存處理器寄存器;
調用OSIntEnter()或者直接將 OSIntNesting加1;
調用OSTimeTick();
調用OSIntExit();
恢復處理器寄存器;
執行中斷返回指令;
}
8.05OS_CPU_C.C
μC/OS-Ⅱ的移植實例要求用戶編寫六個簡單的C函數:
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
唯一必要的函數是OSTaskStkInit(),其它五個函數必須得聲明但沒必要包含代碼。
評論