1、 uC-OSII 的原理
uC-OSII 包括任務調度、時間管理、內存管理、資源管理(信號量、郵箱、消息隊列)四大部分,沒有文件系統、網絡接口、輸入輸出界面。它的移植只與4 個文件相關: 匯編文件(OS_CPU_A.ASM)、處理器相關C 文件(OS_CPU.H、OS_CPU_C.C)和配置文件(OS_CFG.H)。有64 個優先級,系統占用8 個,用戶可創建56 個任務,不支持時間片輪轉。它的基本思路就是“近似地每時每刻總是讓優先級最高的就緒任務處于運行狀態” 。為了保證這一點,它在調用系統API 函數、中斷結束、定時中斷結束時總是執行調度算法。原作者通過事先計算好數據,簡化了運算量,通過精心設計就緒表結構,使得延時可預知。任務的切換是通過模擬一次中斷實現的。uC-OSII 工作核心原理是:近似地讓最高優先級的就緒任務處于運行狀態。操作系統將在下面情況中進行任務調度: 調用API 函數( 用戶主動調用), 中斷( 系統占用的時間片中斷OsTimeTick(),用戶使用的中斷)。其整體整體思路如下。
(1)、在調用API 函數時,有可能引起阻塞,如果系統API 函數察覺到運行條件不滿足,需要切換就調用OSSched()調度函數,這個過程是系統自動完成的,用戶沒有參與。OSSched()判斷是否切換,如果需要切換,則此函數調用OS_TASK_SW()。這個函數模擬一次中斷,好象程序被中斷打斷了,其實是OS 故意制造的假象,目的是為了任務切換。既然是中斷,那么返回地址(即緊鄰OS_TASK_SW()的下一條匯編指令的PC 地址)就被自動壓入堆棧,接著在中斷程序里保存CPU寄存器(PUSHALL)……。堆棧結構不是任意的,而是嚴格按照uC-OSII 規范處理的。OS 每次切換都會保存和恢復全部現場信息(POPALL),然后用RETI 回到任務斷點繼續執行。這個斷點就是OSSched()函數里的緊鄰OS_TASK_SW()的下一條匯編指令的PC 地址。切換的整個過程就是,用戶任務程序調用系統API 函數,API 調用OSSched(),OSSched()調用軟中斷OS_TASK_SW()即OSCtxSw,返回地址(PC 值)壓棧,進入OSCtxSw 中斷處理子程序內部。反之,切換程序調用RETI 返回緊鄰OS_TASK_SW()的下一條匯編指令的PC 地址,進而返回OSSched()下一句,再返回API 下一句,即用戶程序斷點。因此,如果任務從運行到就緒再到運行,它是從調度前的斷點處運行。
(2)、中斷會引發條件變化,在退出前必須進行任務調度。uC-OSII 要求中斷的堆棧結構符合規范,以便正確協調中斷退出和任務切換。前面已經說到任務切換實際是模擬一次中斷事件,而在真正的中斷里省去了模擬。只要規定中斷堆棧結構和uC-OSII 模擬的堆棧結構一樣,就能
保證在中斷里進行正確的切換。任務切換發生在中斷退出前,此時還沒有返回中斷斷點。仔細觀察中斷程序和切換程序最后兩句,它們是一模一樣的,POPALL+RETI。即要么直接從中斷程序退出,返回斷點;要么先保存現場到TCB,等到恢復現場時再從切換函數返回原來的中斷斷點(由于中斷和切換函數遵循共同的堆棧結構,所以退出操作相同,效果也相同)。用戶編寫的中斷子程序必須按照uC-OSII 規范書寫。任務調度發生在中斷退出前,是非常及時的,不會等到下一時間片才處理。OSIntCtxSw()函數對堆棧指針做了簡單調整,以保證所有掛起任務的棧結構看起來是一樣的。
(3)、在uCO-SII 里,任務必須寫成兩種形式之一(《uCOSII 中文版》p99 頁)。在有些RTOS開發環境里沒有要求顯式調用OSTaskDel(),這是因為開發環境自動做了處理,實際原理都是一樣的。uC-OSII 的開發依賴于編譯器,目前沒有專用開發環境,所以出現這些不便之處是可以理解的。
2、 uC-OSII 在 F28335上移植及應用
改寫文件OS_CPU.H
第一步:堆棧的增長方向
#define OS_STK_GROWTH 0 //F28335 的堆棧的增長方式是從有高地址向低地址,所以其值為0。
第二步:定義臨界段的宏
#define OS_ENTER_CRITICAL() asm(" SETC INTM ") //關中斷。
#define OS_EXIT_CRITICAL() asm(" CLRC INTM ") //開中斷。
第三步:定義任務切換宏。
任務切換是有匯編語言編寫的asm("TRAP #30")來實現的。即
#define OS_TASK_SW() asm("TRAP #30")
第四步:定義數據類型
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; * Unsigned 8 bit quantity */
typedef signed char INT8S; /* Signed 8 bit quantity */
typedef unsigned int INT16U; /* Unsigned 16 bit quantity*/
typedef signed int INT16S; /* Signed 16 bit quantity*/
typedef unsigned long INT32U; /* Unsigned 32 bit quantity */
typedef signed long INT32S; /* Signed 32 bit quantity */
typedef float FP32; /* Single precision floating point */
typedef double FP64; /* Double precision floating point*/
typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */
#define BYTE INT8S /* Define data types for backward compatibility */
#define UBYTE INT8U/* ... to uC/OS V1.xx. Not actually needed for */
#define WORD INT16S /* ... uC/OS-II. */
#define UWORD INT16U
#define LONG INT32S
#define ULONG INT32U
改寫文件OS_CPU.C的改寫
在文件 OS_CPU.C 中主要應該寫任務堆棧初始化函數
void *OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)
{ INT16U *stk;
INT16U temp;
opt = opt; /* 'opt' is not used, prevent warning */
stk=(INT16U*)ptos;
*stk++ = (INT16U)(pdata);
*stk++ = (INT16U)(pdata);
*stk++ = 0x00C1; /* ST0 = 0x1111 */
*stk++ = 0x0000; /* T = 0x0000 */
*stk++ = 0x3333; /* AL = 0x3333 */
*stk++ = 0x2222; /* AH = 0x2222 */
*stk++ = 0x5555; /* PL = 0x5555 */
*stk++ = 0x4444; /* PH = 0x4444 */
*stk++ = 0x7777; /* AR0 = 0x7777 */
*stk++ = 0x6666; /* AR1 = 0x6666 */
*stk++ = 0x8A4a; /* ST1 = 0x080B */
*stk++ = 0x0000; /* DP = 0x8888 */
*stk++ = 0xffff;//1001; /* IER = 0xBBBB */
*stk++ = 0xAAAA; /* DBGSTAT = 0xAAAA */
temp = ((INT32U)task)&0x0000ffff;
*stk++ = (INT16U)temp; /* 保存低16 位*/
temp = ((INT32U)task)>>16; /* Save task entry */
*stk++ = (INT16U)(temp); /* 保存高16 位*/
temp = ((INT32U)task)&0x0000ffff; /* RPCL = 0xCCCC*/
*stk++ = (INT16U)temp; /* 保存低16 位*/
temp = ((INT32U)task)>>16; /* RPCH = 0xCCCC */
*stk++ = (INT16U)(temp); /* 保存高16 位*/
stk++;
return ((void *)stk);
}