實時多任務系統µC/OS-Ⅱ在DSP上的移植
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 ;不使能看門狗
splk #080feh,SCSR1 ;時鐘4倍頻
ldp #DP_EVA ;指向第232頁(0x7400~0x747F)
splk #080h,EVAIMRA ;通用定時器1 周期中斷使能
splk #0ffffh,EVAIFRA ;清中斷標志 ;
splk #0,GPTCONA ;無控制操作
splk #4E20h,T1PER ; 定時器計數使能 ,周期為20000
splk #0,T1CNT ; 計數初值清0
splk #080Ch,T1CON ;TMODE=01 連續增/減計數模式,計數時鐘不分頻
必須在最高優先級任務中開啟定時器,系統在優先級為0的任務里開啟T1。為提高系統的實時性,設置T1每500us(20000/40M)產生一次T1周期中斷,即TICK為500us,μC/OS-Ⅱ每500us做一次任務調度。
3. 移植代碼測試
CCS是一個強大的集成開發環境,帶有源碼級的調試工具,按照Jean J.Labrosse推薦的移植代碼測試方法和步驟很快完成了函數OSTaskStkInit()、函數OSStartHighRdy()、函數 OSCtxSw()、OSIntCtxSw()和OSTickISR()函數的驗證工作。證實了移植代碼是能正常工作的。為進一步測試其可靠性,又另外創建了15個任務,并用上了μC/OS-Ⅱ的信號量和郵箱同步機制,在每個任務里加上不同的發光二極管,經測試其實時性和穩定性都很好優異。
4. 任務調度過程中優先級翻轉問題及解決
優先級翻轉是即當一個高優先級任務通過信號量機制訪問共享資源時,該信號量已被一低優先級任務占有,而這個低優先級任務在訪問共享資源時可能又被其它一些中等優先級的任務搶先,因此造成高優先級任務被許多具有較低優先級的任務阻塞,實時性難以得到保證。解決優先級翻轉問題有優先級天花板和優先級繼承兩種辦法。優先級天花板是當任務申請某資源時,把該任務的優先級提升到可訪問這個資源的所有任務中的最高優先級,這個優先級稱為該資源的優先級天花板。這種方法簡單易行,不必進行復雜的判斷,不管任務是否阻塞了高優先級任務的運行,只要任務訪問共享資源都會提升任務的優先級。在μC/OS-Ⅱ中,可以通過 OSTaskChangePrio()改變任務的優先級,但是改變任務的優先級是很花時間的。如果不發生優先級翻轉而提升了任務的優先級,釋放資源后又改回原優先級,則無形中浪費了許多CPU時間,也影響了系統的實時性。優先級繼承是當任務A申請共享資源S時,如果S正在被任務C使用,通過比較任務C與自身的優先級,如發現任務C的優先級小于自身的優先級,則將任務C的優先級提升到自身的優先級,任務C釋放資源S后,再恢復任務C的原優先級。這種方法只在占有資源的低優先級任務阻塞了高優先級任務時才動態的改變任務的優先級,如果過程較復雜,則需要進行判斷。μC/OS-Ⅱ不支持優先級繼承,而且其以任務的優先級作為任務標識,每個優先級只能有一個任務,因此,不適宜在應用程序中使用優先級繼承。
在μC/OS-Ⅱ中,為解決優先級翻轉影響任務實時性的問題,可以借鑒優先級繼承的方法對優先級天花板方法進行改進。對μC/OS-Ⅱ的使用,共享資源任務的優先級不是全部提升,而是先判斷再決定是否提升。即當有任務A申請共享資源S時,首先判斷是否有別的的任務正在占用資源S,若無,則任務A繼續執行,若有,假設為任務B正在使用該資源,
則判斷任務B的優先級是否低于任務A,若高于任務A,則任務A掛起,等待任務B釋放該資源,如果任務B的優先級低于任務A,則提升任務B的優先級到該資源的優先級天花板,當任務B釋放資源后,再恢復到原優先級。在μC/OS-Ⅱ中,每個共享資源都可看作一個事件,每個事件都有相應的事件控制塊 ECB。在ECB中包含一個等待本事件的等待任務列表,該列表包括OSEventTbl[]和OSEventGrp兩個域,通過對等待任務列表的判斷可以很容易地確定是否有多個任務在等待該資源,同時也可判斷任務的優先級與當前任務優先級的高低,從而決定是否需要用 OSTaskChangePio()來改變任務的優先級。這樣,僅在優先級有可能發生翻轉的情況下才改變任務的優先級,而且利用事件的等待任務列表進行判斷,比用OSTaskChangePio()來改變任務的優先級速度快,并占用較少的CPU時間,有利于系統實時性的提高。
參考文獻:
[1]鄔可軍,朱銘鋯等.DSP實時多任務操作系統設計與實現[M]. 北京:電子工業出版社, 2005.
[2]鐘堅文,蔡旭.基于μC/OS-II的CAN總線驅動程序設計[J].微計算機信息,2005,7-2:35-37.
[3]Jean J.Labrosse著.邵貝貝等譯.嵌入式實時操作系統μC/OS-Ⅱ[M]第2版.北京,北京航空航天大學出版社,2003.5
[4]劉和平,王維俊等. TMS320LF240X DSP C語言開發應用[M]. 北京:北京航空航天大學出版社, 2003.
[5]TI.Code Composer User's Guide.SPRU509C.pdf
linux操作系統文章專題:linux操作系統詳解(linux不再難懂)
評論