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