uC/OS-II在A(yíng)RM系統上的移植與實(shí)現
摘要:使用ARM公司提供的ADS 開(kāi)發(fā)工具,將uC/ OS - II 移植到ARM 處理器上,并將移植結果應用在跑馬燈和數碼管的實(shí)現上,運行正常,表明移植成功.
關(guān)鍵詞:uC/ OS - II ;ARM;移植
0 引言
在開(kāi)發(fā)嵌入式系統時(shí),一般選擇基于A(yíng)RM 和uC/ OS - II 的嵌入式開(kāi)發(fā)平臺,因為ARM 微處理器具有處理速度快、超低功耗、價(jià)格低廉、應用前景廣泛等優(yōu)點(diǎn)[1 ] . 將uC/ OS - II 移植到ARM 系統之后,可以充分結合兩者的優(yōu)勢. 如果一個(gè)程序在一個(gè)環(huán)境里能工作,我們經(jīng)常希望能將它移植到另一個(gè)編譯系統、處理器或者操作系統上,這就是移植技術(shù).移植技術(shù)可以使一種特定的技術(shù)在更加廣泛的范圍使用,使軟件使用更加靈活,不局限于某一條件.uC/OS - II 是由Jean J . Labrosse 先生編寫(xiě)的完整的可移植、固化、裁剪的占先式實(shí)時(shí)多任務(wù)內核.uC/ OS - II 的源代碼完全開(kāi)放,這是其他商業(yè)實(shí)時(shí)內核無(wú)法比擬的[2 ] . 它是針對嵌入式應用設計的,在設計之初就充分考慮了可移植性,它的大部分源代碼都是用高可移植性的ANSIC 編寫(xiě)的[3 ] . uC/ OS - II可以移植到從8 位到64 位的不同類(lèi)型、不同規模的嵌入式系統,并能在大部分的8 位、16 位、32 位、甚至64 位的微處理器和DSP 上運行. 由于uC/ OS - II是一個(gè)實(shí)時(shí)操作系統,所以如果將它嵌入到ARM處理器上,就能夠進(jìn)一步簡(jiǎn)化ARM系統的開(kāi)發(fā).
圖1 uC/ OS - II 文件體系結構
1 uC/ OS - II 的移植
uC/OS - II 的文件系統結構包括核心代碼部分、設置代碼部分、與處理器相關(guān)的移植代碼部分[4 ] . 結構如圖1 所示.其中最上邊的軟件應用層是uC/ OS - II 上的代碼. 核心代碼部分包括7 個(gè)源代碼文件和1 個(gè)頭文件. 功能分別是內核管理、事件管理、消息隊列管理、存儲管理、消息管理、信號量處理、任務(wù)調度和定時(shí)管理. 設置代碼部分包括2 個(gè)頭文件,用來(lái)配置事件控制塊的數目以及是否包含消息管理相關(guān)代碼. 而與處理器相關(guān)的移植代碼部分則是進(jìn)行移植過(guò)程中需要更改的部分,包括1 個(gè)頭文件OS CPU. H ,1 個(gè)匯編文件OS CPU A. S 和1 個(gè)C 代碼文件.實(shí)際上將uC/ OS - II 移植到ARM 處理器上,需要完成的工作主要是以下三個(gè)與體系結構相關(guān)的文件:OS CPU. H ,OS CPU. C 以及OS CPU A. S[5 ] .
1. 1 OS CPU. H 的移植
文件OS CPU. H 中包括了用# define 語(yǔ)句定義的與處理器相關(guān)的常數、宏以及類(lèi)型. 移植時(shí)主要修改的內容有:與編譯器相關(guān)的數據類(lèi)型的設定;用#define 語(yǔ)句定義2 個(gè)宏開(kāi)關(guān)中斷;根據堆棧的方向定義OS STK GROWTH等.
在將uC/ OS - II 移植到ARM 處理器上時(shí),首先進(jìn)行基本配置和數據類(lèi)型定義. 重新定義數據類(lèi)型是為了增加代碼的可移植性,因為不同的編譯器所提供的同一數據類(lèi)型的數據長(cháng)度并不相同,例如int型,在有的編譯器中是16 位,而在另外一些編譯器中則是32 位. 所以,為了便于移植,需要重新定義數據類(lèi)型,如INT32U 代表無(wú)符號32 位整型. typedefunsigned int INT8U ,就是定義一個(gè)8 位的無(wú)符號整型數據類(lèi)型. 其次就是對ARM 處理器相關(guān)宏進(jìn)行定義,如ARM處理器中的退出臨界區和進(jìn)入臨界區的宏定義,退出臨界區宏定義[5 ] : # define OS EXITCRITICAL () ARMDisable Int ( ) / / 關(guān)中斷,進(jìn)入臨界區宏定義# define OS ENTER CRITICAL ( ) AR2MEnableInt () / / 開(kāi)中斷. 最后就是堆棧增長(cháng)方向的設定. 當進(jìn)行函數調用時(shí),入口參數和返回地址一般都會(huì )保存在當前任務(wù)的堆棧中,編譯器的編譯選項和由此生成的堆棧指令就會(huì )決定堆棧的增長(cháng)方向[6 ] ,定義為# define OS STK GROWTH 1.
圖2 堆棧增長(cháng)方向
1. 2 OS CPU. C 的移植
OS CPU. C 的移植包括任務(wù)堆棧初始化和相應函數的實(shí)現. 在這里,共有6 個(gè)函數:OSTaskStkInit( ) , OSSTaskCreateHook ( ) , OSTaskDelHook ( ) , OS2TaskSwHook( ) ,OSTaskStatHook ( ) , OSTimeTickHook () . 其中后面的5 個(gè)HOOK函數又稱(chēng)為鉤子函數,主要是用來(lái)對uC/ OS - II 進(jìn)行功能擴展. 這些函數為用戶(hù)定義函數,由操作系統調用相應的HOOK函數去執行,在一般情況下,他們都沒(méi)有代碼,所以實(shí)現為空函數即可. 而函數OSTaskStkInit ( ) 對堆棧進(jìn)行初始化,在A(yíng)RM 系統中,任務(wù)堆??臻g由高到低依次為PC ,LR ,R12 ,R11 , ,R1 ,R0 ,CPSR ,SPSR. 在進(jìn)行堆棧初始化以后,OSTaskStkInit ( ) 返回新的堆棧棧頂指針.
1. 3 OS CPU A. S 的移植
OS CPU A. S 文件的移植需要對處理器的寄存器進(jìn)行操作,所以必須用匯編語(yǔ)言來(lái)編寫(xiě). 這個(gè)文件的實(shí)現集中體現了所要移植到處理器的體系結構和uC/ OS - II 的移植原理[6 ] . 它包括4 個(gè)子函數:OSStartHighRdy() , OSCtxSw() , OSIntCtxSw() ,OSTick2ISR() . 其中難點(diǎn)在于OSIntCtxSw() 和OSTickISR() 函數的實(shí)現,因為這兩個(gè)函數的實(shí)現與移植者的移植思路以及相關(guān)硬件定時(shí)器、中斷寄存器的設置有關(guān).在實(shí)際的移植工作中,這兩處也是比較容易出錯的地方.
OSIntCtxSw( ) 函數由OSIntExit ( ) 函數調用,而OSIntExit () 函數又由OSTickISR() 調用. OSIntCtxSw()函數最重要的作用就是它完成在中斷ISR 中直接進(jìn)行任務(wù)切換,從而提高了實(shí)時(shí)響應的速度. 它發(fā)生的時(shí)機是在ISR 執行到OSIntExit ( ) 時(shí),如果發(fā)現有高優(yōu)先級的任務(wù)因為等待time tick 的到來(lái)獲得了執行 7 2 第4 期李學(xué)橋等:uC/ OS - II 在A(yíng)RM系統上的移植與實(shí)現的條件,就可以馬上被調度執行,而不用返回被中斷的那個(gè)任務(wù)之后再進(jìn)行任務(wù)切換. 實(shí)現OSIntCtxSw() 的方法大致也有兩種情況[7 ] :一是通過(guò)調整SP 堆棧指針的方法,根據所用的編譯器對于函數嵌套的處理,通過(guò)精確計算出所需要調整的SP 位置來(lái)使得進(jìn)入中斷時(shí)所作的保護現場(chǎng)的工作可以被重用. 二是設置需要切換標志位的方法,在OSIntCtxSw( ) 里面不發(fā)生切換,而是設置一個(gè)需要切換的標志,等函數嵌套從進(jìn)入OSIntExit ( ) = > OS ENTER CRITI2CAL() = > OSIntCtxSw( ) = > OS EXIT CRITICAL() = > OSIntExit ( ) 退出后,再根據標志位來(lái)判斷是否需要進(jìn)行中斷級的任務(wù)切換.
其次是對OSTickISR() 修改.OSTickISR() 首先在被中斷任務(wù)堆棧中保存CPU 寄存器的值,然后調用OSIntEnter () . 隨后調用OSTimeTick() ,檢查所有處于延時(shí)等待狀態(tài)的任務(wù),判斷是否有延時(shí)結束就緒的任務(wù). 最后調用OSIntExit ( ) . 如果在中斷中(或其他嵌套的中斷) 有更高優(yōu)先級的任務(wù)就緒,并且當前中斷為中斷嵌套的最后一層,OSIntExit ( ) 將進(jìn)行任務(wù)調度. 如果進(jìn)行了任務(wù)調度,OSIntExit () 將不再返回調用者,而是用新任務(wù)的堆棧中的寄存器數值恢復CPU 現場(chǎng),然后實(shí)現任務(wù)切換. 如果當前中斷不是中斷嵌套的最后一層,或中斷中沒(méi)有改變任務(wù)的就緒狀態(tài), OSIntExit ( ) 將返回調用者OSTickISR ( ) ,OSTickISR() 返回被中斷的任務(wù). 最后就是退出臨界區和進(jìn)入臨界區函數. 進(jìn)入臨界區時(shí),必須關(guān)閉中斷,用ARMDisableInt () 函數實(shí)現. 在退出臨界區的時(shí)候恢復原來(lái)的中斷狀態(tài),通過(guò)ARMEnableInt ( ) 函數來(lái)實(shí)現[7 ] . 至于進(jìn)行任務(wù)級上下文切換,則是由匯編子程序OSCtxSw 實(shí)現.
2 在A(yíng)RM系統上的實(shí)現
以跑馬燈和數碼管為例,說(shuō)明uC/ OS - II 的移植過(guò)程:跑馬燈是4 個(gè)小燈輪流變明變暗,很方便看出效果. 跑馬燈在日常中使用很多,比如狀態(tài)欄跑馬燈、文字跑馬燈、圖片跑馬燈、單片機跑馬燈等[8 ] . 本文采用的是單片機跑馬燈. 實(shí)現單片機跑馬燈的程序中,只有地址口為低電平(接地) 時(shí),發(fā)光管才會(huì )亮. 所以只要循環(huán)控制地址口的各個(gè)引腳的電平高低變化就可使LED 循環(huán)點(diǎn)亮:首先是全不亮,接著(zhù)第1 個(gè)燈亮,第2 個(gè)燈亮,第3 個(gè)燈亮,第4 個(gè)燈亮,第5 個(gè)燈亮,最后所有的燈一起亮.
筆者使用6 個(gè)共陽(yáng)極LED 數碼管來(lái)實(shí)現在7 段數碼管上循環(huán)顯示0~9 ,A~F 字符. 每個(gè)顯示位的段選線(xiàn)與一個(gè)8 位并行口線(xiàn)對應相連,只要在顯示位上的段選線(xiàn)上保持段碼電平不變,則該位就能保持相應的顯示字符. 這里的8 位并行口可以直接采用并行I/ O 口,也可以采用串入/ 并出的移位寄存器或是其他具有三態(tài)功能的鎖存器等. 當采用動(dòng)態(tài)顯示接口時(shí),在多位LED 顯示時(shí),為了簡(jiǎn)化電路,降低成本,將所有位的段選線(xiàn)并聯(lián)在一起,由一個(gè)8 位I/ O口控制. 而共陰(或共陽(yáng)) 極公共端分別由相應的I/ O 線(xiàn)控制,實(shí)現各位的分時(shí)選通. 由于各個(gè)數碼管是共用同一個(gè)段碼輸出口分時(shí)輪流通電的,從而大大簡(jiǎn)化了硬件線(xiàn)路,降低了成本.
對于數碼管的實(shí)現分為3 個(gè)步驟:
1) 制作LED 字符與碼段對應表
2) 掃描控制
3 ( (U8 3 ) 0x02000006) = 0x3E; / 3 使能第一個(gè)數碼管
3 /
3) 段碼輸出
( (U8 3 ) 0x02000004) = seg7table[0 ] ;
根據上面的LED 字符與碼段對應表,控制相應的數字進(jìn)行輸出. 數碼管掃描控制地址為0x02000006 ,8 位訪(fǎng)問(wèn),比如Bit0 控制數碼管0 ,并且低電平有效,Bit5 控制數碼管5 ,低電平有效,數碼管顯示試驗系統中采用的是動(dòng)態(tài)顯示接口,其中數碼管掃描控制地址為0x02000006 ,位0 ―5 分別對應一個(gè)數碼管,將其中每位清0 來(lái)選擇相應的數碼管;地址0x02000004 為數碼管的數據寄存器,控制數碼管的段碼輸出.
3 多任務(wù)應用程序
uC/OS - II 的移植及跑馬燈和數碼管的實(shí)現如下[9 ] :首先是C 語(yǔ)言入口函數Main (所有C 程序的入口) . 它里面包括調用函數ARMTargetInit () 初始化ARM處理器,調用OSInit ( ) 進(jìn)行uC/ OS - II 操作系統初始化,然后調用OSTaskCreate ( ) 函數創(chuàng )建任務(wù)TaskLED 和TaskSEG,最后調用ARMTargetStart () 函數啟動(dòng)時(shí)鐘節拍中斷,并且調用OSStart ( ) 啟動(dòng)系統任務(wù)調度,由于在程序當中使用for ( ; ;) ,這是一個(gè)永無(wú)止境的回路,所以裝置可以一直進(jìn)行下去,直到關(guān)閉裝置.
void Main(void)
{ARMTargetInit () ;
uHALr printf (″uC/ OS - II # n″) ;
OSInit () ;
Sem1 = OSSemCreate(0) ;
Sem2 = OSSemCreate(1) ;
OSTaskCreate(TaskLED , (void 3 ) IdLED , (OS STK 3 )
StackLED[ STACKSIZE - 1 ] , 5) ;
OSTaskCreate(TaskSEG, (void 3 ) IdSEG, (OS STK 3 )
StackSEG[ STACKSIZE - 1 ] , 6) ;
ARMTargetStart () ;OSStart () ;
return ;}
4 結語(yǔ)
使用創(chuàng )建好的模板Temp 新建一個(gè)工程Temp ,并將模板中的Core 和Assemble 文件夾中的文件加入到工程Temp 中. 1) 新建一個(gè)文件Temp. c ,并將其添加到Temp 工程的App 文件夾中. 2) 打開(kāi)Temp. c文件,添加兩個(gè)任務(wù),它們的任務(wù)處理函數分別為T(mén)askLED() 和TaskSEG() . 3) 在TaskLED( ) 函數中每隔50 個(gè)時(shí)鐘節拍使所有跑馬燈閃爍一次(即按順序亮,然后全亮,最后全滅,順序循環(huán)) . 4) 在TaskSEG() 函數中每隔50 個(gè)時(shí)鐘節拍切換一次數碼管顯示(循環(huán)從0~F 顯示) . 5) 編譯工程Temp ,如果出錯,則進(jìn)行修改后再編譯. 6) 將Temp 下載并運行,看結果. 正確的結果是將每隔1/ 2 s 切換一次數碼管顯示,每隔1/ 2 s使所有跑馬燈閃爍一次. 經(jīng)持續了2 h試驗,沒(méi)有出現錯誤,跑馬燈和數碼管正常運轉,結果證明移植成功.
參考文獻:
[1 ] 雷必成, 吳高標, 吳永良. 嵌入式實(shí)時(shí)操作系統uC/ OS
- II 的移植探討[J ] . 自動(dòng)化技術(shù)與應用,2003 , (5) :1 ―3.
[2 ] 邵貝貝. 嵌入式實(shí)時(shí)操作系統uC/ OS - II[M] . 第2 版.
北京:北京航空航天大學(xué)出版社,2003. 2 ―30.
[3 ] 葉豐橋,黃海. uC/ OS - II 在51XA 上的移植應用[J ] .
工業(yè)控制計算機,2002 , (10) :1 ―2.
[4 ] 田澤. 嵌入式開(kāi)發(fā)與應用實(shí)驗教程[M] . 北京: 北京航
空航天大學(xué)出版社,2004. 264 ―270.
[5 ] 陳賾. ARM嵌入式技術(shù)實(shí)踐教程[M] . 北京:北京航空
航天大學(xué)出版社,2005. 189 ―203.
[6 ] 王田苗. 嵌入式系統設計與實(shí)例開(kāi)發(fā)[M] . 北京: 清華
大學(xué)出版社,2003. 62 ―89.
[7 ] 朱華軍. uC/ OS - II 操作系統在A(yíng)RM處理器上的技巧
[J ] . 計算機工程,2004 , (S1) :2 ―3.
[8 ] 蘇中義,楊宇. 嵌入式系統[J ] . 嵌入系統,2004 , (3) :11
[9 ] 曾鳴. uC/ OS - II 實(shí)時(shí)操作系統在嵌入式平臺上進(jìn)行
移植的一般方法和技巧[ J ] . 今日電子, 2004 , (11) :2 ―3.
評論