C/ OS 是一種多任務實時操作系統。內核源代碼公開、短小精干、可裁剪、執行時間可確定, 可移植性較強, 非常適用于一些中小型嵌入式系統開發。uC/OS 可以移植到8~ 64 位的不同類型、不同規模的嵌入式系統, 并能在大部分的8 位、16 位、32 位, 甚至64 位的微處理器和DSP?上運行[ 1] 。
MCF52235是飛思卡爾公司Co ldf ire 系列32 位單片機解決方案的嵌入式微控制器, 采用的是V2 版本的RISC 內核。MCF52235 內部有32 KB SRAM 和256 KB FLASH, 并且集成了標準的Coldfire外圍設備, 包括三個適合中長距離通信的SCI, 一個I2 C 和一個用于系統內部和外圍設備通信的Q SPI。在60?Hz的核心頻率下, MCF52235 的處理能力為56 MIPS, 具備較高的性能價格比[ 24] 。MCF52235 對于移植C/ OS 來說有足夠的
RAM 和FLASH, 且有較快的處理速度和較低的成本,所以對于嵌入式應用系統的開發來說, 嵌入C/ OS到MCF52235 微控制器是一個不錯的選擇。uC/ OS 的體系結構要實現C/ OS 向MCF52235 的移植, 需要做兩方面的工作: 一是重新定義內核的大小和功能; 二是為內核編寫與硬件相關的代碼。C/ OS 的文件結構如圖1 所示。可以看到, C/ OS 與CPU 類型無關的C 代碼文件COS . C 包括很多文件, 它們是C/ OS 的內核和很多功能函數, 其中前三個文件是實時內核、任務管理和時鐘節拍, 這三個文件是一定要用的。后面6 個功能函數用于任務間的通信, 應用程序中可能只用到其中的幾個, 不用的可以不包含進去, 以免編譯時生成沒用的代碼。這部分代碼與CPU 類型無關, 在移植時, 這些文件不要改動。配置文件OS_CFG. H 需要根據應用要求來進行,主要作用是確定C/ OS 提供的系統功能函數, 應用程序用哪些和不用哪些, 這個文件移植時需要修改。與CPU 類型有關的代碼文件主要有三個: OS _CPU. H, OS_CPU_A. ASM 和OS_CPU_C. C。文件定義用于特定CPU 的數據類型來定義相關的宏。OS _CPU_A . ASM 是用匯編語言寫的與硬件有關的代碼,OS_CPU_C. C 是用C 語言寫的與硬件有關的代碼。由于移植使用C 交叉編譯工具, 在C 代碼中可以插入匯編語句, 在移植中可將這兩個文件合并成一個文件[ 5] 。
產生時鐘節拍的定時中斷來自微控制器內部, 但并非來自V2 內核內部, 可以用實時時鐘產生定時中斷,也可以用片內的外設模塊定時器單元來產生定時中斷,這部分代碼顯然與硬件相關, 移植時要自己寫[ 6] 。
2 移植過程
所謂移植, 就是使一個實時內核能在某個微處理器或微控制器上運行。為了方便移植, 大部分的C/ OS代碼是用C 語言寫的, 但仍需要用C 和匯編語言寫一些與處理器相關的代碼, 這是因為C/ OSII 在讀寫處理器寄存器時只能通過匯編語言來實現 。移植過程主要包括移植前的準備、BSP ( 板級支持包) 的編寫和與處理器相關代碼的修改和編寫。C/OS 核心代碼、與CPU 相關的接口程序、BSP 和用戶應用程序之間的關系如圖2 所示。
移植前的準備
進入C/ OS 官方網站下載C/ OS 源代碼。打開Codew arrior 6. 4 建立MCF52235 的工程文件, 然后把C/ OS 的源代碼文件加入到工程里面[ 8] 。其中有幾個地方需要改動:
( 1) 下載的源代碼中os_cfg _r. h 改為o s_cfg. h;os_dbg_r. c改為os_dbg. c。
( 2) 由于會引起重復定義錯誤, 需要把源代碼中重復包含的文件注釋掉。
( 3) 需要在INT ERNAL_FLASH 模式下編譯, 而不能在RAM 模式下, 否則會產生溢出錯誤。
?編寫BSP
板級支持包( BSP) 是介于底層硬件和操作系統之間的軟件層次, 負責進行系統啟動后最初的硬件和軟件
初始化, 并對底層硬件進行封裝, 使得操作系統不再面對具體的硬件[ 9] 。在此建立兩個BSP 文件: BSP. ASM 和BSP. C。其中, BSP. ASM 中包含了匯編語言寫的中斷接口程序。BSP. C 中包含了硬件和軟件的初始化程序和產生時鐘節拍的中斷服務程序。
與處理器相關代碼的修改和編寫
有三個與處理器相關的文件, 即OS_CPU . H, OS_CPU _ A. ASM 和OS _ CPU _ C. C 需要修改。由于MCF52235 有eMAC 模塊, 所以還需要編寫OS_CPU _I. ASM 文件, 用來在任務切換和中斷時以及中斷返回
時保存和恢復相關寄存器。
?1 、OS_CPU. H 的移植
OS_CPU. H 包含了一些與處理器和編譯器相關的宏定義和數據類型定義。由于使用Codew arrior 編譯
器, shor t 類型是16 位的, int 類型是32 位的。MCF52235 的堆棧是32 位寬的, 因此OS_STK 定義為
32 位, 所有任務的堆棧必須聲明使用OS_ST K 這種數據類型。數據類型定義如下:
ty pedef unsigned char BOOLEAN;
ty pedef unsigned char INT 8U;
ty pedef signed char INT8S;
ty pedef unsigned sho rt INT16U;
ty pedef signed shor t INT16S;
ty pedef unsigned int INT32U;
ty pedef signed int INT32S;
typedef float?FP32;
typedef double FP64;
typedef unsigned int OS_STK;
typedef unsigned shor t OS_CPU_SR;
( 1) 臨界區域處理。像所有的實時性內核一樣, 在進入代碼臨界區時要關中斷, 完成時要開中斷。C/
OS 定義了兩個宏來關閉和使能中斷: OS_ENT ER_CRITICAL( ) 和OS_EXIT _CRIT ICAL( ) 。C/ OS定義了三種方法來關閉和使能中斷, 大多數情況下選擇第三種方法。
# define OS_CRITICAL_METH OD # 3
# define OS_ENTER_CRITICAL( ) { cpu_sr = OS _CPU_
SR_Save( ) ; } / / 關中斷
# def ineOS _ EXIT _ CRITICAL( ) { OS _ CPU _ SR_ Resto re
( cpu_sr) ; } / / 開中斷
( 2) 任務層上下文切換。當C/ OS 調用OS _TASK_SW( ) 時發生任務層的上下文切換。因為上下
文切換是根據處理器的不同而不同的, 所以需要執行一個匯編的函數。在這種情況下, 用TRA P 指令來產
生一個異常, 用T RAP 指令的優點是能使它像發生了一次中斷一樣。這里用# 14 T RAP, 因為大多數情況
下, # 15 TRAP 被調試和監控程序保留了。# 14TRAP 定位于VBR+ 0x00B8, 然后跳轉到相應的地址。
在這個向量處放置OSCtx Sw( ) 的地址。這個函數聲明在OS_CPU_A. ASM 里。VBR 代表向量基址寄存器,
包含異常向量表的基址, 程序開始時被初始化為0x00000000, 但是在運行時可以改變。
# define OS_TASK_SW( ) asm( T RAP # 14; )
( 3) 堆棧的增長方向。MCF52235 的堆棧增長方向是從高地址向低地址, 因此OS _ST K_GROWTH 置
為1。
# define OS_STK_GROWTH 1
?2 、OS_CPU _C. C 的移植
OS_CPU_C. C 里面包含10 個比較簡單的C 語言函數, 一般來說C/ OS 只需要OST askStkInit ( ) 。其他函數是用來讓用戶在自己的程序里擴展操作系統功能的。如果需要使用這些函數, 需要在OS_CFG. H 里設置OS_CPU _HOOKS_EN 為1。堆棧的初始化: OSTaskStkInit ( ) 雖然是用C 語言編
寫的, 但它是一個與CPU 硬件相關的函數。這個函數功能是初始化任務的堆棧, 由建立任務函數OSTask
Create( ) 或擴展地建立任務函數OSTaskCreateExit ( ) 調用。任務堆棧初始化的實質就是模擬一次中斷, 使堆棧看起來就像剛發生過中斷一樣。任務堆棧中保存了任務代碼的起始地址和一些CPU 寄存器的值, 一旦條件滿足, 就可以執行該任務。初始化后的任務堆棧結構如圖3所示。
3 、OS_CPU_A. ASM 的移植
這個文件包含5 個相當簡單的匯編函數, 因為一般不能用C 語言來保存和恢復寄存器。
( 1) OS_CPU_SR_Save( )
這個函數是通過保存中斷屏蔽寄存器, 然后關閉中斷來實現OS_CRITICAL_MET HOD # 3 的。當函數返回時, D0 包含了狀態寄存器的內容, 里面包含當前的中斷關閉狀態。這個返回值被調用函數保存到變量
cpu_sr 中。
( 2) OS_CPU_SR_Restore( )
這個函數用來實現恢復中斷屏蔽到調用OS _ENTER_CRITICAL( ) 之前的狀態。也就是說調用OS_
ENTER_CRITICAL( ) 之前中斷是關閉的, 那么在OS_EXIT_CRITICAL( ) 之后, 中斷是關閉的。
( 3) OSStartHighRdy( )
這個函數被OSStar t ( ) 調用來運行優先級最高的任務。OSStar t ( ) 設置OSTCBHighRdy 指向優先級最高任務的OS _T CB。一旦從OSTaskSwHoo k( ) 返回,就把OSRunning 設為OS_T RU E, 它表明現在RT OS
將要運行。從最高優先級任務的OS_T CB 中恢復堆棧指針, 然后從任務堆棧里取出CPU 寄存器。最后執行
一個RET 指令, 這個指令可以從堆棧中彈出SR 和PC,現在的任務代碼就開始執行。
( 4) OSCtx Sw( )
當一個任務不再運行時就會發生一個任務級的任務切換, 比如任務調用一個延遲10 個時鐘節拍的函數。
這時, C/ OS 需要找出下一個最重要的任務準備去運行。OSCtx Sw ( ) 的功能是保存需要掛起的任務的CPU 寄存器和堆棧, 恢復需要運行任務的CPU 寄存器和堆棧。任務級上下文切換如圖4 所示。
( 5) OSINTCtx Sw( )
當中斷服務函數完成時, 調用OSIntEx it ( ) 函數去決定是否有一個更重要的任務比被中斷的任務更需要執行。這種情況下, OSIntEx it( ) 決定運行哪個任務, 然后調用OSIntCtx Sw ( ) 。這種情況下, 中斷服務程序已經保存了被中斷任務的CPU 寄存器, 而需要做的只是去恢復新任務的CPU 寄存器。
?4 、OS_CPU _I. ASM 的編寫
如果用到增強的乘法累加單元( eMAC) 模塊, 在上下文切換和中斷時就應該保存和恢復eMAC 寄存器。保存和恢復eMAC 寄存器通過兩個宏來實現[ 10] 。代碼如下:
。 macro OS_EM AC_SAVE
MOVE. L MACSR, D7
CLR. L D0
MOVE. L D0, M ACSR
MOVE. L ACC0, D0
MOVE. L ACC1, D1
MOVE. L ACC2, D2
MOVE. L ACC3, D3
MOVE. L ACCEXT01, D4
MOVE. L ACCEXT23, D5
MOVE. L MASK, D6
LEA 32( A7) , A7
MOVEM. L D0D7, ( A7)
。 endm
。 macro OS_EM AC_REST ORE
MOVEM. L ( A7) , D0D7
MOVE. L # 0, MACSR
MOVE. L D0, ACC0
MOVE. L D1, ACC1
MOVE. L D2, ACC2
MOVE. L D3, ACC3
MOVE. L D4, ACCEXT01
MOVE. L D5, ACCEXT23
MOVE. L D6, MASK
MOVE. L D7, MACSR
LEA 32( A7) , A7
。 endm
?時鐘節拍的產生
最后還需要編寫利用片內定時器產生時鐘節拍的中斷服務程序。C/ OS要求微控制器提供一個簡單的時鐘, 用于任務的延時等功能。在此利用可編程中斷定時器來產生時鐘節拍中斷。在定時器中斷服務程序中調用OSTimeTick( ) 就產生了系統所需要的時鐘節拍。C/OS中產生中斷后的中斷處理程序如下所示:
_BSP_TickISR:
MOVE. W # 0x2700, SR
LEA 60( A7) , A7
MOVEM. L D0D7/ A0 A6, ( A7)
OS_EMAC_SAVE
MOVEQ. L # 0, D0
MOVE. B ( _OSI ntNesting) , D0
ADDQ. L # 1, D0
MOVE. B D0, ( _OSI ntNesting)
CMPI. L # 1, D0
BNE _BSP_TICkISR_1
MOVE. L ( _OSTCBCur) , A1
MOVE. L A7, ( A1)
_BSP_TickISR_1:
JSR _BSP_T ickISR_H andler
JSR _OSInt Ex it
OS_EMAC_RESTORE
MOVEM. L ( A7) , D0D7/ A0A6
LEA 60( A7) , A7
RTE
?任務的創建和移植代碼的測試
源程序移植完, 用戶就可以試著制作自己的項目。編寫任務代碼, 與以前在前后臺系統中基本相同, 不同
的是要把每個任務獨立編寫成一個文件, 最后由主程序統一調度。為了測試是否移植成功, 用STaskCreateExt ( ) 創建了兩個任務。一個任務使板上LED 每一秒閃動一次, 另一個任務是用片內A/ D 采樣板上的加速度傳感器信號, 并在數碼管上顯示出當前加速度數值。最后調用OSStar t( ) 啟動多任務調度。
?1 、定義每個任務的堆棧大小
OS_STK
TaskStartStk[ T ASKSTART ST K_SIZE] ;
OS_STK ADT askStk[ T ASKSTK_SIZE] ;
然后在main( ) 函數里系統初始化:
OSInit( ) ;
2、 創建任務
OST askCreateEx t( TaskStart, ( void * ) 0, ( OS _ ST K * )
& T askStar tSt k[ T ASKSTARTST K_SIZE1] , T ASK _START _
PRIO, TASK_START_PRIO, ( OS_ST K* ) & T askStart St k[ 0] ,
TASK_ST ART_ST K_SIZE, ( vo id * ) 0, OS_TASK_OPT _ST K_
CH K| OS_TASK_OPT_STK_CLR) ;
OSTaskCreateExt ( ADT ask, ( vo id * ) 0, ( OS _ STK * )
& ADTaskStk[ TASKST K_SIZE1] , ADTASK_PRIO, ADT ASK
_ PRIO, ( OS _ ST K * ) & ADTaskStk [ 0] , TASK _ ST K _SIZE,
( vo id * ) 0, OS_TASK_OPT_STK_CHK) ;
3 、系統啟動運行
OSStart( ) ;
?4 、測試結果
測試代碼經過編譯下載到實驗板上運行后, 通過實驗板上顯示的信息, 表示兩個任務在交替運行, 說明移植工作是成功的, 如圖5 所示。
評論
查看更多