摘? 要: 將嵌入式實時內核μC/OSⅡ移植到TI公司的DSP處理器TMS320C6201上的方法。重點說明了內核中與處理器相關部分的編程及其在系統中的作用。
關鍵詞: μC/OSⅡ內核? 嵌入式操作系統? DSP? 移植
?
嵌入式系統設計及其應用已對人類生活產生了巨大影響,并使人們未來的生活方式產生變化。進行嵌入式系統開發的一個基礎工作是實現嵌入式操作系統在相關處理器平臺上的移植。本文基于目前應用非常廣泛的DSP處理器體系結構,對μC/OS-Ⅱ嵌入式實時操作系統內核的移植做了分析和介紹,并給出了相應的移植源代碼。
1 μC/OSⅡ實時內核介紹
μC/OS-Ⅱ是一個簡單、高效的嵌入式實時操作系統內核,已被應用到各種嵌入式系統中。它支持x86、ARM、PowerPC、MIPS、DSP等眾多體系結構,并有上百個商業應用實例,其穩定性和可用性是經過實踐驗證的。同時,它的源代碼公開,可以從www.ucos-ii.com網站上獲得全部源碼以及其在各種體系結構平臺上的移植范例。
μC/OS-Ⅱ 2.0版以上的內核都具有可搶占的實時多任務調度功能。另外它還提供了許多系統服務,例如信號量、消息隊列、郵箱、內存管理、時間函數等,這些功能可以根據不同的需求進行裁減。可以說,μC/OS-Ⅱ是一個具備現代操作系統特點的RTOS。它結構清晰、注解詳盡,具有良好的可擴展性和可移植性,被廣泛地應用于各種架構的微處理器上。
2?TMS320C6201芯片介紹
TMS320C6201是TMS320系列產品中的新一代高性能的DSPs芯片。它是16位的定點數字信號處理器,在200MHz速率工作時可達1 600Mips;四通道DMA控制端口;最大3Mb片上存儲器;備有三種掉電模式;二個多通道緩沖串口;二枚32位定時器;超薄256/352腳BGA封裝;先進超長指令字結構;每周期執行八條32位指令,八個獨立通用功能單元;業內最先進的DSP C語言編譯器;一個新直觀性而又類似RISC的指令集,方便易用;匯編優化程序調度任務,方便匯編語言編程。
3?實時內核的移植
移植工作包括以下內容:修改OS_CPU.H中常量、數據類型和宏;用C語言改寫OS_CPU_C.C中六個簡單的函數;用匯編語言改寫OS_CPU_A.ASM中的四個函數。
3.1 OS_CPU.H文件的修改
在OS_CPU.H頭文件中定義了與處理器相關的常量OS_STK_GRCWTH、宏OS_ENTER_CRITICAL( )、宏OS_EXIT_CRITICAL( )、宏OS_TASK_SW( )以及可移植的數據類型等。
(1)可移植的數據類型
由于C語言中的short、int、long等數據類型的數位數隨著所使用處理器的不同而變化,所以移植性不強,μC/OSⅡ不支持。μC/OSⅡ定義了可移植的數據類型,包括8位、16位、32位的有符號數和無符號數等。此外還需要根據DSP芯片TMS320C6201的堆棧寬度定義任務堆棧數據類型OS_STK。具體定義如下:
typedef unsigned ? char BOOLEAN;布爾量
typedef unsigned?? char INT8U;8位無符號數
typedef signed???? char INT8S;8位有符號數
typedef unsigned?? short INT16U;16位無符號數
typedef signed???? short INT16S;16位有符號數
typedef unsigned ? int INT32U;32位無符號數
typedef signed???? int INT32S;32位有符號數
typedef float????? ??? FP32;32位單精度浮點數
typedef double????????? FP64;64位雙精度浮點數
typedef unsigned ? int OS_STK;TMS320C6201的堆棧入口寬度為32位
(2)宏定義
由于某些代碼在執行時不可分割,μC/OSⅡ實時內核在訪問這些代碼臨界區時必須禁止中斷,因此μC/OSⅡ實時內核在頭文件OS_CPU.H中還定義了宏OS_ENTER_CRITICAL( )和OS_EXIT_CRITICAL( ),分別用于開中斷和關中斷。對TMS320C6201而言,可用如下代碼實現:
extern cregister volatile unsigned int IER;
extern volatile unsigned int Always_Enabled_Interrupts;
extern volatile unsigned int Normally_Enabled_Interrupts;
static inline void OS_ENTER_CRITICAL(void)
{???? IER=Always_Enabled_Interrupts;
???? ?? asm(″NOP 4″);
} /*關全局中斷,進入臨界區*/
??? static inline void OS_EXIT_CRITICAL(void)
{?? IER=Normally_Enabled_Interrupts;
??? asm(″NOP 5″);
} /*開全局中斷,進入臨界區*/
#define OS_STK_GROWTH 1/*TMS320C6201的堆棧是由
?????????????????????? 低地址向高地址遞增*/
3.2 OS_CPU_C.C文件的修改
OS_CPU_C.C文件中需要用戶修改六個函數:OSTaskCreateHook( )、OSTaskDelHook( )、OSTaskSwHook( )、OSTaskStatHook( )、OSTaskCreateHook( )、OSTaskStkIinit( )。而實際需要修改的只有OSTaskStkIinit( )函數,其他函數是為方便用戶擴展功能而設,其定義可為空。
函數OSTaskStkIinit( )用于系統創建用戶任務時,建立并初始化任務堆棧。該函數與處理器的硬件體系密切相關,它將所有的寄存器壓棧,返回新的堆棧棧頂,并將它們保存在該任務的任務控制塊OS_TCB中,最終使初始化后的堆棧跟剛發生過一次中斷一樣。這樣,系統無需對調度程序作特殊處理即可直接對新任務進行調度。由于在TMS320C620中堆棧是按32位數據類型進行操作,所以堆棧數據類型OS_STK聲明為32位無符號整數。OSTaskStkIinit( )函數代碼如下:
void *OSTaskStkInit(void(*task)(void*pd),void*pdata,
void *ptos,INT16U opt)
{???? INITIAL_REGISTER_FRAME *Frame,*StackBottom
int*FirstFreeCellInStack
Frame=(INITIAL_REGISTER_FRAME*)((int)ptos &~7)
StackBottom=Frame
Frame--
Frame->A0.integer=0x0A0?? /*初始化寄存器*/
Frame->A1.integer=0x0A1
Frame->A2.integer=0x0A2
Frame->A3.integer=0x0A3
Frame->A4.integer=(int) pdata
Frame->A5.integer=0x0A5
Frame->A6.integer=0x0A6
Frame->A7.integer=0x0A7
Frame->A8.integer=0x0A8
Frame->A9.integer=0x0A9
Frame->A10.integer=0x0A10
Frame->A11.integer=0x0A11
Frame->A12.integer=0x0A12
Frame->A13.integer=0x0A13
Frame->A14.integer=0x0A14
Frame->A15.integer=0xA15
Frame->B0.integer=0x0B0
Frame->B1.integer=0x0B1
Frame->B2.integer=0x0B2
Frame->B3.integer=0x0B3
Frame->B4.integer=0x0B4
Frame->B5.integer=0x0B5
Frame->B6.integer=0x0B6
Frame->B7.integer=0x0B7
Frame->B8.integer=0x0B8
Frame->B9.integer=0x0B9
Frame->B10.integer=0x0B10
Frame->B11.integer=0x0B11
Frame->B12.integer=0x0B12
Frame->B13.integer=0x0B13
Frame->B14.integer=ReturnCurrentDP()
Frame->B15.integer=(int)StackBottom
Frame->AMR_Adressing_Mode_Register=0
Frame->CSR_Control_Status_Register=(0<<8)+3
Frame->IER_Interrupt_Enable_Register=Normally_
??????????????????????????????? Enabled_Interrupts
Frame->IRP_Interrupt_Return_Pointer=0xdeadbeef
Frame->Start_Address=task
rsFreeCellInStack=(int*) Frame
rstFreeCellInStack--
turn (FirstFreeCellInStack)
}
3.3 OS_CPU_A.ASM文件的修改
此文件包括的四個函數都涉及對寄存器的處理,與處理器有關。由于不同的處理器有不同的寄存器,所以操作系統在這個文件中給用戶留下四個函數接口,以便用戶根據所選處理器編寫相應的匯編程序以完成固定的功能。四個函數分別是:多任務啟動函數中調用的OSSTartHightRdy( )、任務切換函數OSCtxSw( )、中斷任務切換函數OSIntCtxSw( )、時鐘節拍服務函數OSTickISR( )。
(1)OSSTartHightRdy( )
該函數是由啟動函數OSStart( )調用的,功能是使系統能及時運行優先級最高的就緒任務。由于系統中數據指針OSTCBHighRdy一直指向就緒任務中優先級最高的任務控制塊OS_TCB,使得OSSTartHightRdy( )可以輕易地獲取最高優先級任務的棧頂指針,再將保存在此任務堆棧的寄存器值恢復到CPU寄存器中,使該任務得以運行,實現多任務的啟動。對于TMS320C6201,其代碼如下:
B??????? .S1???? _OSTaskSwHook?? /*調用用戶自
定義的_OSTaskSwHook*/
NOP??????????????????????????? 3
MVKL???? .S2???? OSStartHighRdy_1,B3
MVKH? ? .S2???? OSStartHighRdy_1,B3
OSStartHighRdy_1:
LDW???? .D2T2???? *+DP(_OSTCBHighRdy),B4
/*獲得最高優先級任務的TCB地址*/
LDB????? .D2T2???? *+DP(_OSPrioHighRdy),B5
NOP3
STW????? .D2T2???? B4,*+DP(_OSTCBCur)
??? STB????? .D2T2???? B5,*+DP(_OSPrioCur)
????|| mvk?? 1,b1
??? STB????? .D2T2???? B1,*+DP(_OSRunning)
????LDW????? .D2T2???? *B4,SP
????NOP????? 4
??? (2)OSCtxSw( )
??? 該函數由任務切換函數OS_TASK_SW( )進入。如果任務執行了某個函數,其結果若改變了當前任務的狀態(如OSTaskSuspend( )、OSTimeDly( ))或是改變了別的任務的狀態(如OSTaskResume( )、OSTimeDlyResume( )),則都要引起新的任務調度函數(OSSched( ))執行OS_TASK_SW( )。其代碼如下:
addk?????? .s2 (4 -_Framesize),SP
STW??????? .D2T2???? B3,*+SP(_StartAddress)
STW??????? .D2T2???? B0,*+SP(_B0)
|| mvc???? AMR,B0
STW??????? .D2T2???? B0,*+SP(_AMR)
|| mvc???? CSR,B0
STW??????? .D2T2???? B0,*+SP(_CSR)
|| mvc??? IER,B0
STW?????? .D2T2???? B0,*+SP(_IER)
STW?????? .D2T1???? A0,*+SP(_A0)
|| MV???? .L1X??? SP,A0
STW?????? .D2T1???? A1,*+SP(_A1)
|| addk?? .s1?????? (_IRP),A0
STW?????? .D2T1???? A0,*+SP(_B15)
STW?????? .D2T1???? A2,*+SP(_A2)
STW?????? .D2T1???? A3,*+SP(_A3)
STW?????? .D2T1???? A4,*+SP(_A4)
STW?????? .D2T1???? A5,*+SP(_A5)
STW?????? .D2T1???? A6,*+SP(_A6)
STW?????? .D2T1???? A7,*+SP(_A7)
STW?????? .D2T1???? A8,*+SP(_A8)
STW?????? .D2T1???? A9,*+SP(_A9)
STW?????? .D2T1???? A10,*+SP(_A10)
STW?????? .D2T1???? A11,*+SP(_A11)
STW?????? .D2T1???? A12,*+SP(_A12)
STW?????? .D2T1???? A13,*+SP(_A13)
STW?????? .D2T1???? A14,*+SP(_A14)
STW?????? .D2T1???? A15,*+SP(_A15)
LDW?????? .D2T2???? *+DP(_OSTCBCur),B0
??? STW?????? .D2T2???? B1,*+SP(_B1)
STW?????? .D2T2???? B2,*+SP(_B2)
STW?????? .D2T2???? B3,*+SP(_B3)
STW? ???? .D2T2???? B4,*+SP(_B4)
STW ????? .D2T2???? B5,*+SP(_B5)
STW ????? .D2T2???? B6,*+SP(_B6)
STW???????????????? SP,*B0
STW?????? .D2T2???? B7,*+SP(_B7)
STW?????? .D2T2???? B8,*+SP(_B8)
STW?????? .D2T2???? B9,*+SP(_B9)
STW?????? .D2T2???? B10,*+SP(_B10)
STW?????? .D2T2???? B11,*+SP(_B11)
STW?????? .D2T2???? B12,*+SP(_B12)
STW?????? .D2T2???? B13,*+SP(_B13)
STW?????? .D2T2???? B14,*+SP(_B14)
(3)OSIntCtxSw( )
μC/OS-Ⅱ中,中斷的產生可能會引起任務的切換,中斷服務程序的最后會調用OSIntExit( )檢查任務就緒狀況。如果需要進行任務切換,將調用OSIntCtxSw( )。所以SIntCtxSw( )又稱為中斷級的任務切換函數。需要注意是,任何中斷服務程序ISR前面都要像下文介紹的時鐘節拍函數STickISR( )流程的第②步那樣保存上下文環境。SIntCtxSw( )和OSCtxSw( )的后半部分幾乎相同,不同處是對當前任務的堆棧指針進行了調整。其代碼如下:
LDW??????? .D2T2???? *+DP(_OSTCBCur),B0
|| mv?????? SP,B1
|| addk???? .s2????? (8),B1
STW??????? ?.D2T2???? B1,*B0
NOP????? ? 4
(4)OSTickISR( )
μC/OS-Ⅱ中,時鐘節拍中斷是一個非常重要的中斷,因為整個操作系統的活動都受到它的激勵。OSTickISR( )的執行流程為:①硬件進入中斷處理;②保護上下文環境;③調用OSIntEnter( ),記錄中斷嵌套層數;④調用OSTimeTick( ),檢查處理各個任務的延時,并根據情況修改就緒任務表;⑤調用OSIntExit( ),檢查就緒任務表,看是否有比當前任務優先級更高的任務就緒。如果有,則進行調度;如果沒有,OSIntExit( )返回并恢復②所保存的上下文環境,并執行RETI回到被中斷的那個任務中繼續運行。
4? 結束語
μC/OS-Ⅱ作為一個優秀的實時操作系統已經被移植到各種體系結構的微處理器上,而DSP體系結構在嵌入式領域也獲得了廣泛的應用和支持。將μC/OS-Ⅱ移植到DSP平臺上,能夠更深入地了解實時操作系統的構造,加快在DSP平臺上的應用和開發,并為更高層次上的擴展和改進打下基礎。
參考文獻
1?? LABROSSE J J著,邵貝貝譯.μC/OS-Ⅱ—源碼公開的實時嵌入式操作系統.北京:中國電力出版社,2001