1、 uC-OSII 的原理
uC-OSII 包括任務(wù)調(diào)度、時(shí)間管理、內(nèi)存管理、資源管理(信號量、郵箱、消息隊(duì)列)四大部分,沒有文件系統(tǒng)、網(wǎng)絡(luò)接口、輸入輸出界面。它的移植只與4 個(gè)文件相關(guān): 匯編文件(OS_CPU_A.ASM)、處理器相關(guān)C 文件(OS_CPU.H、OS_CPU_C.C)和配置文件(OS_CFG.H)。有64 個(gè)優(yōu)先級,系統(tǒng)占用8 個(gè),用戶可創(chuàng)建56 個(gè)任務(wù),不支持時(shí)間片輪轉(zhuǎn)。它的基本思路就是“近似地每時(shí)每刻總是讓優(yōu)先級最高的就緒任務(wù)處于運(yùn)行狀態(tài)” 。為了保證這一點(diǎn),它在調(diào)用系統(tǒng)API 函數(shù)、中斷結(jié)束、定時(shí)中斷結(jié)束時(shí)總是執(zhí)行調(diào)度算法。原作者通過事先計(jì)算好數(shù)據(jù),簡化了運(yùn)算量,通過精心設(shè)計(jì)就緒表結(jié)構(gòu),使得延時(shí)可預(yù)知。任務(wù)的切換是通過模擬一次中斷實(shí)現(xiàn)的。uC-OSII 工作核心原理是:近似地讓最高優(yōu)先級的就緒任務(wù)處于運(yùn)行狀態(tài)。操作系統(tǒng)將在下面情況中進(jìn)行任務(wù)調(diào)度: 調(diào)用API 函數(shù)( 用戶主動(dòng)調(diào)用), 中斷( 系統(tǒng)占用的時(shí)間片中斷OsTimeTick(),用戶使用的中斷)。其整體整體思路如下。
(1)、在調(diào)用API 函數(shù)時(shí),有可能引起阻塞,如果系統(tǒng)API 函數(shù)察覺到運(yùn)行條件不滿足,需要切換就調(diào)用OSSched()調(diào)度函數(shù),這個(gè)過程是系統(tǒng)自動(dòng)完成的,用戶沒有參與。OSSched()判斷是否切換,如果需要切換,則此函數(shù)調(diào)用OS_TASK_SW()。這個(gè)函數(shù)模擬一次中斷,好象程序被中斷打斷了,其實(shí)是OS 故意制造的假象,目的是為了任務(wù)切換。既然是中斷,那么返回地址(即緊鄰OS_TASK_SW()的下一條匯編指令的PC 地址)就被自動(dòng)壓入堆棧,接著在中斷程序里保存CPU寄存器(PUSHALL)……。堆棧結(jié)構(gòu)不是任意的,而是嚴(yán)格按照uC-OSII 規(guī)范處理的。OS 每次切換都會保存和恢復(fù)全部現(xiàn)場信息(POPALL),然后用RETI 回到任務(wù)斷點(diǎn)繼續(xù)執(zhí)行。這個(gè)斷點(diǎn)就是OSSched()函數(shù)里的緊鄰OS_TASK_SW()的下一條匯編指令的PC 地址。切換的整個(gè)過程就是,用戶任務(wù)程序調(diào)用系統(tǒng)API 函數(shù),API 調(diào)用OSSched(),OSSched()調(diào)用軟中斷OS_TASK_SW()即OSCtxSw,返回地址(PC 值)壓棧,進(jìn)入OSCtxSw 中斷處理子程序內(nèi)部。反之,切換程序調(diào)用RETI 返回緊鄰OS_TASK_SW()的下一條匯編指令的PC 地址,進(jìn)而返回OSSched()下一句,再返回API 下一句,即用戶程序斷點(diǎn)。因此,如果任務(wù)從運(yùn)行到就緒再到運(yùn)行,它是從調(diào)度前的斷點(diǎn)處運(yùn)行。
(2)、中斷會引發(fā)條件變化,在退出前必須進(jìn)行任務(wù)調(diào)度。uC-OSII 要求中斷的堆棧結(jié)構(gòu)符合規(guī)范,以便正確協(xié)調(diào)中斷退出和任務(wù)切換。前面已經(jīng)說到任務(wù)切換實(shí)際是模擬一次中斷事件,而在真正的中斷里省去了模擬。只要規(guī)定中斷堆棧結(jié)構(gòu)和uC-OSII 模擬的堆棧結(jié)構(gòu)一樣,就能
保證在中斷里進(jìn)行正確的切換。任務(wù)切換發(fā)生在中斷退出前,此時(shí)還沒有返回中斷斷點(diǎn)。仔細(xì)觀察中斷程序和切換程序最后兩句,它們是一模一樣的,POPALL+RETI。即要么直接從中斷程序退出,返回?cái)帱c(diǎn);要么先保存現(xiàn)場到TCB,等到恢復(fù)現(xiàn)場時(shí)再從切換函數(shù)返回原來的中斷斷點(diǎn)(由于中斷和切換函數(shù)遵循共同的堆棧結(jié)構(gòu),所以退出操作相同,效果也相同)。用戶編寫的中斷子程序必須按照uC-OSII 規(guī)范書寫。任務(wù)調(diào)度發(fā)生在中斷退出前,是非常及時(shí)的,不會等到下一時(shí)間片才處理。OSIntCtxSw()函數(shù)對堆棧指針做了簡單調(diào)整,以保證所有掛起任務(wù)的棧結(jié)構(gòu)看起來是一樣的。
(3)、在uCO-SII 里,任務(wù)必須寫成兩種形式之一(《uCOSII 中文版》p99 頁)。在有些RTOS開發(fā)環(huán)境里沒有要求顯式調(diào)用OSTaskDel(),這是因?yàn)殚_發(fā)環(huán)境自動(dòng)做了處理,實(shí)際原理都是一樣的。uC-OSII 的開發(fā)依賴于編譯器,目前沒有專用開發(fā)環(huán)境,所以出現(xiàn)這些不便之處是可以理解的。
2、 uC-OSII 在 F28335上移植及應(yīng)用
改寫文件OS_CPU.H
第一步:堆棧的增長方向
#define OS_STK_GROWTH 0 //F28335 的堆棧的增長方式是從有高地址向低地址,所以其值為0。
第二步:定義臨界段的宏
#define OS_ENTER_CRITICAL() asm(" SETC INTM ") //關(guān)中斷。
#define OS_EXIT_CRITICAL() asm(" CLRC INTM ") //開中斷。
第三步:定義任務(wù)切換宏。
任務(wù)切換是有匯編語言編寫的asm("TRAP #30")來實(shí)現(xiàn)的。即
#define OS_TASK_SW() asm("TRAP #30")
第四步:定義數(shù)據(jù)類型
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 中主要應(yīng)該寫任務(wù)堆棧初始化函數(shù)
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);
}