摘要:μC/OS-Ⅱ由于其源代碼開放、穩定、可靠的特點,在嵌入式開發中得到了廣泛應用。該文針對其移植開發,探討了這一過程中所采用的步驟、一般方法和技巧。
關鍵詞:μC/OS-Ⅱ;嵌入式系統;移植
中圖分類號:TP316文獻標識碼:A文章編號:1009-3044(2008)29-0493-02
Methods on Transplantation of μC/OS-II
XU Jing-feng
(Base Course Department,Nanjing Institute of Politics,Nanjing 210003,China)
Abstract: μC/OS-II has been widely used in the embedded system development for its feature of stability, reliability and open source. This paper focuses on the developing steps and general methods in the transplantation of the real time operating system.
Key words: μC/OS-II; embedded system; transplantation
μC/OS-Ⅱ是一個搶占式的實時多任務操作系統,具有高穩定性和高可靠性,可固化、可剪裁。此外,μC/OS-Ⅱ的一大鮮明特點就是源代碼公開,便于移植和維護。在實際的項目開發過程中,可以針對項目所采用的芯片或開發工具,根據自己的需要進行移植。
本文將以在TMS320C6711 DSP上的移植過程為例,具體分析μC/OS-Ⅱ在嵌入式開發平臺上進行移植的一般方法和技巧。
1 深入了解所采用的系統核心
在選定了系統平臺和開發工具之后,進行μC/OS-Ⅱ的移植時,一定要深入了解系統的核心細節。首先要了解的是中斷處理機制,如何開啟、屏蔽中斷,可否保存前一次中斷狀態等。其次,芯片是否有軟中斷或是陷阱指令,又是如何觸發的。另外,還需關注系統對于存儲器的使用機制,諸如內存的地址空間,堆棧的增長方向,有無批量壓棧的指令等。在本例中,使用的是TMS320C6711 DSP。這是TI公司6000系列中的一款浮點型號,由于其時鐘頻率非常高,且采用了超常指令字(VLIW)結構、類RISC指令集、多級流水等技術,所以運算性能相當強大,在通信設備、圖像處理、醫療儀器等方面都有著廣泛的應用。
具體到C6711,其中斷有3種類型,即復位、不可屏蔽中斷(NMI)和可屏蔽中斷(INT4-INT15)。可屏蔽中斷由CSR寄存器控制全局使能,此外也可用IER寄存器分別置位使能。由于在C6711中沒有軟中斷機制,所以μC/OS-II的任務切換需要編寫一個專門的函數實現。此外,由于C6711沒有專門的中斷返回指令、批量壓棧指令,相應的任務切換代碼均需編程完成。因為采用了類RISC核心,所以C6711的內核結構中,只有A0-A15和B0-B15這兩組32bit的通用寄存器。
2 分析所采用的開發工具的特點
無論使用的系統核心是什么,C語言開發工具對于μC/OS-Ⅱ是必不可少的,在本項目中,我們采用TI的C語言開發工具CCS for C6000。為此,在動手前一定要針對具體的開發工具搞清具體的技術細節,以便方便移植工作。
例如開啟C編譯器的“匯編代碼列表(list)”功能,編譯器會為每個C語言源文件生成其對應的匯編代碼文件。通過比較C代碼與生成的ASM代碼,我們可以了解該開發工具對于寄存器、堆棧、中斷向量等使用的特點,以便做到心中有數。
3 編寫移植代碼
在深入了解了系統核心與開發工具的基礎上,真正編寫移植代碼的工作就相對比較簡單了。μC/OS-Ⅱ自身的代碼絕大部分都是用ANSI C編寫的,而且代碼的層次結構十分干凈利落,與平臺相關的移植代碼都存在于OS_CPU_A.ASM、OS_CPU_C.C以及OS_CPU.H這三個文件當中。在移植的時候,結合前面兩個步驟中已掌握的信息,就可以實施了。
但是,由于系統核心、開發工具的千差萬別,在實際項目中,一般都會有一些處理方法上的不同,需要特別注意。以C6711的移植為例:
1) 中斷的開啟和屏蔽的兩個宏定義為:
#define OS_ENTER_CRITICAL() Disable_int()
#define OS_EXIT_CRITICAL() Enable_int()
Disable_int和Enable_int是用匯編語言編寫的兩個函數。在這里使用了控制狀態寄存器(CSR)的一個特性——CSR中除了控制全局中斷的GIE位之外,還有一個PGIE位,可用于保存之前的GIE狀態。因此在Disable_int中要先將GIE的值寫入PGIE,然后再將GIE寫0,屏蔽中斷。而在Enable_int中則從PGIE讀出值,寫入GIE,從而回復到之前的中斷設置。這樣,就可以避免使用這兩個宏而意外改變了系統的中斷狀態。此外,也沒有使用堆棧或局部變量,比原作者推薦的方法要好。
2) 任務的切換:
前文說過,C6711中沒有軟中斷機制,所以任務的切換需要用匯編語言自行編寫一個函數_OSCtxSw來實現:
#define OS_TASK_SW() OSCtxSw()
在C6711中需要入棧保護的寄存器包括A0-A15、B0-B15、CSR、IER、IRP和AMR,這些再加上當前的程序地址構成一個存儲幀,需要入棧保存。
_OSCtxSw函數中,需要像發生了一次中斷那樣,將上述存儲幀入棧,然后獲取被激活任務的TCB指針,將其存儲幀的內容彈出,從而完成任務切換。需要特別注意的是,在這里OS_TASK_SW是作為函數調用的,所以如前文所述,調用時的當前程序地址是保存在B3寄存器中的,這也就是任務重新激活時的返回地址。
3) 中斷的編寫:
在CCS中如果用“interrupt”關鍵字聲明函數,在編譯時,會自動將該函數中使用到的寄存器進行入棧、出棧保護。但是,這會導致各種中斷發生時,出入棧的內容各不相同。這對于μC/OS-II是會引起嚴重錯誤的。因為μC/OS-II要求中斷發生時的入棧操作使用和發生任務切換時完全一樣的存儲幀結構。因此,在移植時、基于μC/OS-II進行開發時,都不應當使用“interrupt”關鍵字,而應用如下結構編寫中斷函數:
void OSTickISR (void)
{
DSP_C6x_Save(); //服務函數,入棧
OSIntEnter();
if (OSIntNesting==1)
{
OSTCBCur->OSTCBStkPtr=(OS_STK*) DSP_C6x_GetCurrentSP(); //服務函數
} //獲取當前SP的值,若允許中斷嵌套則在此處開中斷
OSTimeTick();
OSIntExit();
DSP_C6x_Resume(); //服務函數,出棧
}
DSP_C6x_Save和DSP_C6x_Resume是兩個服務函數,分別完成中斷的出、入棧操作。它們與OS_TASK_SW函數的區別在于:中斷發生時的當前程序地址是自動保存在IRP寄存器的,應將其作為任務返回地址,而不再是B3。此外,DSP_C6x_Resume是一個永遠不會返回的函數,在將所有內容出棧后,它就直接跳轉回到中斷發生前的程序地址處,繼續執行。
4 進行移植的測試
在編寫完了所有的移植代碼之后,就可以編寫幾個簡單的任務程序進行測試了,大體上可以分三個步驟來進行,相關資料比較詳盡,這里就不多作贅述了。
5 針對項目的開發平臺,封裝服務函數
最后這個步驟,往往是容易被忽視的,但對于保持項目代碼的簡潔、易維護有很重要的意義。μC/OS-Ⅱ的原作者強烈建議將源代碼分路徑進行存儲。例如本文例子中的所有源代碼就應按如下路徑結構存儲:
\\uCOS-II
├─SOURCE //平臺無關代碼
│OS_CORE.C
│......
├─TI_C6711 //系統核心
├─CCS //開發工具
│OS_CPU.H
│OS_CPU_A.ASM
│OS_CPU_C.C
│
├─DSP_C6x_Service //服務函數
│DSP_C6x_ Service.H
│DSP_C6x_ Service.ASM
│
└─ TEST //具體的開發項目代碼
OS_CFG.H
INCLUDES.H
TEST.C......
如上,DSP_C6x_Service中的服務函數,類似于原作者提供的80x86版本中的PC.C和PC.H文件。在本文的例子中,服務函數則包括了上文提及的中斷相關函數,以及系統初始化函數DSP_C6x_SystemInit()和時鐘初始化函數DSP_C6x_TimerInit()等。而具體的開發項目代碼,則可以分別在“/TI_C6711”路徑下新建自己的目錄,就如同移植測試的“TEST”項目,而無需再關注μC/OS-Ⅱ的源代碼和服務函數。如此,就可以避免不必要的編譯錯誤,也便于開發項目的維護。
參考文獻:
[1] 拉伯羅斯.嵌入式實時操作系統μC/OS-Ⅱ[M].2版,邵貝貝,譯.北京:北京航空航天大學出版社,2005.
[2] Voss M,Ben Ismail T.Towards a theory for hardware-software codesign[C].Proc of the Int Workshop on Hardware-Software Codesign,1994.