μC/OS-II系統(tǒng)在AVM處理器上的移植
μC/OS-II功能強大,支持56個用戶任務(wù),其內(nèi)核為占先式,支持信號量、郵箱、消息隊列等多種常用的進(jìn)程間通信機(jī)制,現(xiàn)已成功應(yīng)用到眾多商業(yè)嵌入式系統(tǒng)中,是一個成熟穩(wěn)定的實時內(nèi)核。與大多商用RTOS不同的是,μC/OS-II公開所有的源代碼,90%的代碼使用標(biāo)準(zhǔn)的ANSI C語言書寫,程序可讀性強、移植性好;同時它可免費獲得,即使商業(yè)應(yīng)用也只收取少量的許可費用。因此,對μC/OS-II實時操作系統(tǒng)的學(xué)習(xí)研究、開發(fā)、應(yīng)用具有重要意義。
Samsung S3C44B0X微處理器是三星公司專為手持設(shè)備和其它嵌入式應(yīng)用提供的高性價比的微控制器解決方案。它使用ARM公司的16位/32位RISC結(jié)構(gòu),內(nèi)核是arm7TDMI,工作在66MHz,片上集成了以下部件:8K Cache、外部存儲器控制器、LCD控制器、4個DMA通道、2個UART、1個多主I2C總線控制器、1個I2C總線控制器,以及5通道PWM定時器和1個內(nèi)部定時器、8通道12位ADC等,能夠與常用的外圍設(shè)備實現(xiàn)無縫連接,功能強大。目前,國內(nèi)應(yīng)用較為廣泛。
1 μC/OS-II實時操作系統(tǒng)結(jié)構(gòu)
圖1說明了μC/OS-II的軟硬件體系結(jié)構(gòu)。應(yīng)用程序處于整個系統(tǒng)的頂層,每個任務(wù)都可以認(rèn)為自已獨占了CPU,因而可以設(shè)計成為一個無限循環(huán)。μC/OS-II處理器無關(guān)的代碼提供了μC/OS-II的系統(tǒng)服務(wù),應(yīng)用程序可以使用這些API函數(shù)進(jìn)行內(nèi)存管理、任務(wù)間通信及創(chuàng)建、刪除任務(wù)等。
大部分的μC/OS-II代碼是使用ANSI C語言書寫的,因此μC/OS-II的可移植性好,然而仍需要使用C和匯編語言寫一些處理器相關(guān)代碼。μC/OS-II的移植需要滿足以下要求:
①處理器的C編譯器可以產(chǎn)生可重入代碼;
②可以使用C調(diào)用進(jìn)入和退出臨界區(qū)代碼;
③處理器必須支持硬件中斷,并且需要一個定時中斷源;
④ 處理器需要能夠容納一定數(shù)據(jù)的硬件堆棧;
⑤處理器需要有能夠在CPU寄存器與內(nèi)核和堆棧交換數(shù)據(jù)的指令。
S3C44B0X處理器完全滿足上述要求。
2 實時內(nèi)核μC/OS-II在S3C44B0X上的移植
我們使用arm SDT編譯器,移植μC/OS-II主要包括以下幾個步驟。
(1)設(shè)置OS_CPU.H中與處理器和編譯器相關(guān)的代碼
*************************************************
與編譯器相關(guān)的數(shù)據(jù)類型
*************************************************
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /*8位無符號整數(shù)*/
typedef signed char INT8S; /*8位有符號整數(shù)*/
typedef unsigned short INT16U; /*16位有符號整數(shù)*/
typedef signed short INT16S; /*16位無符號整數(shù)*/
typedef unsigned long INT32U; /*32位無符號整數(shù)*/
typedef signed long INT32S; /*32位有符號整數(shù)*/
typedef float FP32; /*單精度浮點數(shù)*/
typedef double FP64; /*雙精度浮點數(shù)*/
typedef unsigned int OS_STK;/*堆棧入口寬度為16位*/與arm處理器相關(guān)的代碼:
#define OS_ENTER_CRITICAL () armEnableInt() /*開啟中斷*/
#define OS_STK_GROWTH 1 /*堆棧由高地址向低地址增長*/
(2)用C語言編寫6個操作系統(tǒng)相關(guān)的函數(shù)(OS_CPU_C.C)
void OSTaskStkInit(void(task)(void *pd),void *pdata,void *ptos,INT16U opt)
{ unsigned int *stk;
opt =opt; /*因“opt”變量沒有用到,防止編譯器產(chǎn)生警告*/
stk =(unsigned int *)ptos; /*裝載堆棧指針*/
/*為新任務(wù)創(chuàng)建上下文*/
*--stk=(unsigned int)task; /*lr*/
*--stk=(unsigned int)task /*pc*/
*--stk=0; /*r12*/
*--stk=0; /*r11*/
*--stk=0; /*r10*/
*--stk=0; /*r9*/
*--stk=0; /*r8*/
*--stk=0; /*r7*/
*--stk=0; /*r6*/
*--stk=0; /*r5*/
*--stk=0; /*r4*/
*--stk=0; /*r3*/
*--stk=0; /*r2*/
*--stk=0; /*r1*/
*--stk=(unsigned int)pdata; /*r0*/
*--stk=(SVC32MODE0x0); /*cpsr IRQ,
*--stk=(SVC32MODE0x0); /*spsr IRQ,關(guān)閉FIQ*/
return((void*)stk);
}
后5個函數(shù)是鉤子函數(shù),可以不加代碼:
void OSTaskCreateHook(OS_TCB *ptcb)
void OSTaksDelHool (OS_TCB *ptcb)
void OSTaskSwHook(void)
void OSTaskStatHook(void)
(3)用匯編語言編寫4個與處理器相關(guān)的函數(shù)(OS_CPU.ASM)
OSStartHighRdy() ;運行優(yōu)先級最高的就緒任務(wù)
LDR r4,addr_OSTCBCur ;得到當(dāng)前任務(wù)的TCB地址
LDR r5,addr_OSTCBHighRdy ;得到高優(yōu)先級任務(wù)的TCB地址
LDR r5,addr_OSTCBHighRdy ;得到高優(yōu)先級任務(wù)的TCB地址
LDR r5,[r5] ;得到堆棧指針
LDR sp,[r5] ;切換到新的堆棧
STR r5,[r4] ;設(shè)置新的當(dāng)前任務(wù)的TCB地址
LDMFD sp!,{r4}
MSR CPSR_cxsf,r4
LDMFD sp!,{r0-r12,lr,pc} ;開始新的任務(wù)
END
OSCtxsw() 任務(wù)級的任務(wù)切換函數(shù)
STMFD sp!,{lr} ;保存PC指針
STMFD sp!,{lr} ;保存lr指針
STMFD sp!,{r0-r12} ;保存寄存器文件和返回地址
MRS r4,CPSR
STMFD sp!,{r4} ;保存當(dāng)前PSR
MRS r4,SPSR
STMFD sp!,{r4}
;OSPrioCur=OSPrioHighRdy
LDR r4,addr_OSPrioCur
LDR r5,addr_OSPrioHighRdy
LDRB r6,[r5]
STRB r6,[r4]
;得到當(dāng)前任務(wù)的TCB地址
LDR r4,addr_OSTCBCur
LDR r5,[r4]
STR sp,[r5] ;保存棧指針在占先任務(wù)的TCB上
;取得高優(yōu)先級任務(wù)的TCB地址
LDR r6,addr_OSTCBHighRdy
LDR r6,[r6]
LDR sp,[r6] ;得到新任務(wù)的堆棧指針
;OSTCBCur=OSTCBHighRdy
STR r6,[r4]
;得到當(dāng)前新任務(wù)的TCB地址
LDMFD sp!,{r4}
MSR SPSR_cxsf,r4
LDMFD sp!,{r4}
MSR SPSR_cxsf,r4[!--empirenews.page--]
LDMFD sp!,{r0-r12,lr pc}
OSIntCtxSw() ;中斷級的任務(wù)切換函數(shù)
LDMIA sp!,{al-vl,lr}
SUBS pc,lr,#4
SUB lr,lr,#4
MOV r12,lr
MRS lr,SPSR
AND lr,lr,#0XFFFFFE0
ORR lr,lr,#0XD3
MSR CPSR_CXSF,lr
OSTickISR() ;中斷服務(wù)函數(shù)
STMDB sp!,{r0-r11,lr}
MRS r0,CPSR
ORR r0,r0,#0x80; ;設(shè)置中斷禁止標(biāo)志
MSR CPSR_cxsf,r0 ;中斷結(jié)束
LDR r0,I_ISPC
LDR r1,=BIT_TIMER0
STR r1,{r0}
BL IrqStart
BL osTimeTick
BL IrqFinish
LDR r0,=need_to_swap_context
LDR R2,[R0]
CMP r2,#1
LDREQ pc,=_CON_SW
完成上述工作后,μC/OS-II就可以運行在arm處理器上了。
3 使用μC/OS-II系統(tǒng)應(yīng)注意的問題
①μC/OS-II和Linux等分時操作系統(tǒng)不同,不支持時間片輪轉(zhuǎn)法。它是一個基于優(yōu)先級的實時操作系統(tǒng)。每一個任務(wù)的優(yōu)先級必須不同(分析它的源碼會發(fā)現(xiàn),μC/OS-II把任務(wù)的優(yōu)先級當(dāng)作任務(wù)在標(biāo)識來使用,如果優(yōu)先級相同,任務(wù)將無法區(qū)分)。進(jìn)入就緒態(tài)的優(yōu)先級最高的任務(wù)首先得到CPU的使用權(quán),只有等它交出CPU的使用權(quán)后,其它任務(wù)才可以被執(zhí)行。所以,它只能就是多任務(wù),不能就是多進(jìn)程,至少不是我們所熟悉的那種多進(jìn)程。
②μC/OS-II對共享資源提供了保護(hù)的機(jī)制。μC/OS-II是一個支持多任務(wù)的操作系統(tǒng)。我們可以把一個完整的程序劃分成幾個任務(wù),不同的任務(wù)執(zhí)行不同的功能。對于共享資源(比如串口),μC/OS-II也提供了很好的解決辦法,一般情況下使用的是信號量方法。我們創(chuàng)建一個信號量并對它進(jìn)行初始化,當(dāng)一個任務(wù)需要使用一個共享資源時,它必須先申請得到這個信號量。在這個過程中即使有優(yōu)先權(quán)更高的任務(wù)進(jìn)入了就緒態(tài),因為無法得到信號量,也不能使用該資源。在μC/OS-II中稱為優(yōu)先級反轉(zhuǎn)。簡單地說,就是高優(yōu)先級任務(wù)必須等待低優(yōu)先級任務(wù)的完成。在上述情況下,在兩個任務(wù)之間發(fā)生優(yōu)先級后轉(zhuǎn)是無法避免的。所以不在使用μC/OS-II時,必須對所開發(fā)的系統(tǒng)了解清楚才能選擇對于某種共享資源是否使用信號量。
③μC/OS-II內(nèi)存管理不夠完善。在分析許多μC/OS-II的應(yīng)用實例中發(fā)現(xiàn),任務(wù)??臻g和內(nèi)存分區(qū)的創(chuàng)建采用了定義全局?jǐn)?shù)組的方法,這樣實現(xiàn)起來固然簡單,但不夠靈活有效。
編譯器會將全局?jǐn)?shù)組作為未初始化的全局變量,放到應(yīng)用程序映像的數(shù)據(jù)段。數(shù)組的大小是固定的,生成映像后不可能在使用中動態(tài)地改變。對于任務(wù)??臻g來說,數(shù)組定義大了會造成內(nèi)存浪費;定義小了任務(wù)棧溢出,會造成系統(tǒng)崩潰。對于內(nèi)存分區(qū),在不知道系統(tǒng)初始化后給用戶留下了多少自由內(nèi)存空間的情況下,很難定義內(nèi)存分區(qū)所使用數(shù)組的大小。此外,現(xiàn)在μC/OS-II只支持固定大小的內(nèi)存分區(qū),容易造成內(nèi)存浪費。μC/OS-II將來應(yīng)該被改進(jìn)以支持可變大小的內(nèi)存分區(qū)。因此,系統(tǒng)初始化后能清楚地掌握自由內(nèi)存空間的情況是很重要的。所以,應(yīng)避免使用全局?jǐn)?shù)組分配內(nèi)存空間,關(guān)鍵是要知道整個應(yīng)用程序在編譯、鏈接后代碼段和數(shù)據(jù)段的大小,在目標(biāo)板內(nèi)存中是如何定位,以及目標(biāo)板內(nèi)存的大小。
總之,隨著各種智能嵌入式系統(tǒng)的復(fù)雜化和系統(tǒng)實時性需求的提高,伴隨應(yīng)用軟件朝著系統(tǒng)化發(fā)展的加速,功能強大的實時操作系統(tǒng)μC/OS-II將會有更大的發(fā)展。