嵌入式實時操作系統(tǒng)μC/OS-Ⅱ在DSP上的移植
0. 引言 μC/OS-Ⅱ內(nèi)核是一個強占式優(yōu)先級調(diào)度的系統(tǒng),能管理63個任務(wù),支持旗語、信號量、互斥信號量、隊列和消息郵箱,是一個是典型的嵌入式實時操作系統(tǒng)。它最早由Jean J. Labrosse創(chuàng)作,源碼完全公開,已有眾多應(yīng)用范例,可靠性能得到保證,內(nèi)核小,移植性好。TI的C2000系列DSP處理器 TMS320LF2407A片內(nèi)集成16路10位AD,4個通用定時器,8個16位PWM通道,4個CAP捕獲單元,41路I/O通道,以及SPI、 RS232、CAN等通信接口,豐富的片內(nèi)資源,使得控制器不需任何擴展就能滿足所有的功能要求,且冗余很少。 DSP處理器在數(shù)學(xué)運算方面的優(yōu)勢也為智能化過程所需的數(shù)據(jù)處理提供了支持。TMS320LF2407A指令采用4級流水線操作,最高能以40M的系統(tǒng)時鐘工作,再加上合適的RTOS的調(diào)度,完全能保證系統(tǒng)的實時性。其開發(fā)系統(tǒng)TMS320C2XX Code Composer Studio滿足μC/OS-Ⅱ的移植條件,因此,它是嵌入式計算機控制系統(tǒng)主控制芯片的一個較好選擇。筆者在設(shè)計基于CAN的工程機械嵌入式智能顯示儀時,選用2407A做主控制芯片,軟件方面,將實時內(nèi)核μC/OS-Ⅱ移植到該DSP 控制器TMS320LF2407A上,而應(yīng)用程序是在μC/OS-Ⅱ內(nèi)核基礎(chǔ)上的一系列任務(wù)。 1. μC/OS-Ⅱ的移植 由于μC/OS-Ⅱ在設(shè)計時就已經(jīng)充分考慮了可移植性,所以μC/OS-Ⅱ的移植相對來說比較容易。移植工作包括以下幾個內(nèi)容:(1)用#define設(shè)置一個常量的值(OS_CPU.H)(2)聲明10個數(shù)據(jù)類型(OS_CPU.H)(3)用#define聲明三個宏 (OS_CPU.H)(4)用C語言編寫六個簡單的函數(shù)(OS_CPU_C.C)(5)編寫四個匯編語言函數(shù)(OS_CPU_A.ASM);即μC/OS-Ⅱ的移植要修改3個文件OS_CPU.H、OS_CPU_C.C和OS_CPU_A.ASM。其中匯編語言文件OS_CPU_A.ASM是可選擇的,因為某些C編譯器允許用戶在C語言中插入?yún)R編語言,所以用戶可以將所需的匯編語言代碼直接放到OS_CPU_C.C中。CCS的C編譯器允許在 C語言中嵌入?yún)R編語言,但是由于這種方式破壞了C語言的完整性,因此只提倡在程序開始系統(tǒng)初始化部分少量采用。而在C語言中嵌入實現(xiàn)某一完整功能的多句匯編語言時,就不提倡采用這種方法。所以,移植中還是對OS_CPU_A.ASM做了修改。 2. 編寫移植代碼 移植μC/OS-Ⅱ的主要工作是聲明與硬件相關(guān)的數(shù)據(jù)類型,定義與中斷有關(guān)的宏定義,定義堆棧增長方向宏定義,編寫堆棧初始化函數(shù),HOOK接口函數(shù),任務(wù)級上下文切換函數(shù),中斷級上下文切換函數(shù)以及系統(tǒng)時鐘定時服務(wù)函數(shù)等。 2.1 移植OS_CPU.H文件 (1)一個常量值。OSInit需要知道當(dāng)OS_TaskIdle() 和OS_TaskStat( ) 函數(shù)建立任務(wù)時,堆棧的頂端地址在哪里;其次調(diào)用OSTaskStkChk( )時,μC/OS-Ⅱ需要知道堆棧的底端地址在哪里。所以需要指明堆棧的增長方向。絕大多數(shù)微處理器和微控制器的堆棧是從上往下遞減的,但是也有某些處理器使用的是相反的方式。TMS320LF2407A的堆棧方向是從下往上增長的,所以: #define OS_STK_GROWTH 0;// 堆棧方向是從下往上增長 (2) 聲明數(shù)據(jù)類型。μC/OS-Ⅱ考慮到通用性,在內(nèi)核中使用了自定義數(shù)據(jù)類型,與編譯器無關(guān),這就要求移植時必須定義微處理器的數(shù)據(jù)類型與μC/OS-Ⅱ的數(shù)據(jù)類型相一致,保證移植后的μC/OS-Ⅱ在微處理器平臺上運行,在移植中應(yīng)將其聲明為CCS編譯器可識別的類型。這可以由OS_CPU.h頭文件實現(xiàn),程序如下所示。 typedef unsigned char BOOLEAN;/*定義ucos里的boolean為unsigned char*/ typedef unsigned char INT8U; /*定義ucos里的INT8U為unsigned char*/ typedef signed char INT8S; /*定義ucos里的INT8S為signed char*/ typedef unsigned int INT16U; /*定義ucos里的INT16U為unsigned int*/ typedef signed int INT16S; /*定義ucos里的INT16S為signed int*/ typedef unsigned long INT32U; /*定義ucos里的INT32U為unsigned long*/ typedef signed long INT32S; /*定義ucos里的INT32S為signed long */ typedef float FP; /*定義ucos里的FP為float*/ #define OS_STK INT16U /*堆棧入口寬度為16位*/ 由于系統(tǒng)沒有用到OS_CPU_SR類型數(shù)據(jù),所以沒有定義此數(shù)據(jù)類型。 (3)3個宏定義。μC/OS-Ⅱ在內(nèi)核中通過禁止中斷來保護臨界區(qū),因此,需要在C語言中插入禁止和允許中斷的匯編代碼,DSP里用SETC INTM來屏蔽中斷,用CLRC INTM來使能中斷。所以移植代碼定義了下面兩條宏定義: #define OS_ENTER_CRITICAL() asm(" SETC INTM") #define OS_EXIT_CRITICAL() asm(" CLRC INTM") μC/OS-Ⅱ定義了三種保護臨界區(qū)的方式,此移植版本采用的是最簡單的第一種方法。此種方法就要求中斷關(guān)閉的情況下不能調(diào)用μC/OS-Ⅱ的功能函數(shù)。這對于應(yīng)用來說是可以接受的,所以就選擇了此種模式。TMS320LF2407A支持多種中斷方式,包括可屏蔽硬中斷INT1~INT6,不可屏蔽硬中斷RESET和NMI_VECT,不可屏蔽軟中斷INT8~INT16和INT20~INT31以及中斷陷阱TRAP。因此使用INT31軟中斷來調(diào)用OSCtxSw()來從任務(wù)堆棧中恢復(fù)處理器所用的寄存器。用INT2的定時器1周期中斷來調(diào)用OSTickISR()。定義模仿INT31中斷的宏,來跳轉(zhuǎn)到INT31 #define OS_TASK_SW() asm(" INTR 31") 在中斷向量表里的定義 .include f2407regs.h .global _c_int0, _OSTickISR, RESET, _OSCtxSw,_GRIS5,_adint,_nothing .asect "vectors",0 …… INT2 B _OSTickISR ; B _c_int2 …… INT31 B _OSCtxSw ; task switching service vector. 2.2 移植OS_CPU_C.H文件 μC/OS-Ⅱ的移植范例要求編寫10個簡單的C函數(shù),但是真正必要的函數(shù)是OSTaskStkInit(),其他9個函數(shù)必須申明,但并不一定要包含任何代碼。OSTaskStkInit()主要是對任務(wù)堆棧的初始化。TMS320LF2407A的堆棧與一般微處理器的堆棧不同,一般微處理器的堆棧由編程定義一塊內(nèi)存作為堆棧比較靈活,而TMS320LF2407A的堆棧,是在CPU內(nèi)有8級深度的硬件堆棧,因此任務(wù)堆棧的初始化與一般微處理器的堆棧初始化不同。芯片本身的堆棧(以下簡稱US)只有8級,無法作為系統(tǒng)的堆棧使用,所以CCS編譯器將CPU內(nèi)部的兩個寄存器AR0和AR1保留,AR1作為堆棧指針,AR0用作堆棧中臨時變量指針FP。編譯器將函數(shù)或中斷壓進US的返回地址,彈出放在SP(AR1)指向的堆棧中,并保存CPU 的工作環(huán)境,不同的是函數(shù)只保存程序要用到的寄存器,中斷要調(diào)用I$$SAVE子程序,保存CPU所有的寄存器,返回時調(diào)用I$$REST子程序,恢復(fù) I$$SAVE和I$$REST兩個函數(shù)是μC/OS-Ⅱ操作系統(tǒng)移植到TMS320LF2407A上的基礎(chǔ),一定要很清楚后才能夠成功移植 OSTaskStkInit()函數(shù)。
2.3 移植OS_CPU_C.H文件
需要在該文件中編寫4個匯編語言函數(shù):(1)OSStartHighRdy():這是系統(tǒng)完成初始化后啟動多任務(wù)運行時要調(diào)用的函數(shù),主要功能是:將OSRunning標(biāo)志置為TRUE,然后獲取已建立的優(yōu)先級最高任務(wù)的堆棧指針,并從其堆棧中恢復(fù)處理器寄存器,最后執(zhí)行返回指令返回上述任務(wù)中運行該任務(wù)。(2)OSCtxSw():在本移植中,任務(wù)級任務(wù)切換用軟中斷intr31實現(xiàn),OSCtxSw()即為該中斷的中斷服務(wù)程序。它先要將當(dāng)前處理器寄存器壓入當(dāng)前任務(wù)的堆棧中,將當(dāng)前堆棧指針保存到當(dāng)前任務(wù)的任務(wù)控制塊中;然后用與OSStartHighRdy()相類似的方法運行當(dāng)前處于就緒態(tài)中優(yōu)先級最高的任務(wù)。(3)OSIntCtxSw():該函數(shù)被OSIntExit()函數(shù)調(diào)用,用于在ISR中進行任務(wù)切換。它與OSCtxSw() 的區(qū)別在于無需對當(dāng)前任務(wù)的工作現(xiàn)場進行保存,因為這一工作在進入ISR之時已經(jīng)做了。(4)OSTickISR():用定時器產(chǎn)生一個周期為恒定值的時鐘源提供給μC/OS-Ⅱ,這是μC/OS-Ⅱ時間延遲和超時功能的時間基準(zhǔn)。OSTickISR()是該定時器周期中斷的中斷服務(wù)程序。它主要有兩個功能:一個是調(diào)用OSTimeTick()函數(shù),計算自系統(tǒng)上電以來所經(jīng)歷的時鐘節(jié)拍數(shù),并將每個處于延時等待狀態(tài)任務(wù)的OSTCBDIy項減1;另一個是調(diào)用OSIntExit()函數(shù)查看是否有更高優(yōu)先級的任務(wù)因時鐘節(jié)拍到來而延遲時間到并進入就緒態(tài),如果有,則進行中斷級的任務(wù)切換。另外,在該函數(shù)的入口處要將OSIntNesting加1;在出口處將OSIntNesting減1。其中堆棧的構(gòu)造,采用了系統(tǒng)庫函數(shù)I$$SAVE和I$$RSET函數(shù)來保護/恢復(fù)現(xiàn)場、保護/恢復(fù)任務(wù)棧。時鐘節(jié)拍TICK中斷由實時時鐘完成,但是2407A中沒有此定時器,移植是采用T1的周期中斷來實現(xiàn)的,時鐘頻率為10M,4倍頻后CPU時鐘為40M。系統(tǒng)初始化代碼如下。
ldp #00e0h ;指向第224頁(0x7000~0x707F)
splk #00e8h,WDCR ;不使能看門狗
s
評論