淺談μCOSII在Cortex-M3核的ARM處理器上的移植
隨著(zhù)科學(xué)技術(shù)的發(fā)展,嵌入式技術(shù)已被廣泛應用到汽車(chē)電子、無(wú)線(xiàn)通信、數碼產(chǎn)品等各個(gè)領(lǐng)域。嵌入式操作系統及嵌入式處理器技術(shù)發(fā)展迅猛,嵌入式操作系統典型代表有μCOS—II、μClinux、Winclow CE、VxWorks等;嵌入式處理器包括ARM、MIPS、PowerPC等。
本文引用地址:http://dyxdggzs.com/article/149960.htm1 軟硬件開(kāi)發(fā)環(huán)境及處理器介紹
1.1 軟件硬開(kāi)發(fā)環(huán)境
本移植過(guò)程使用的軟件環(huán)境是RealView MDK開(kāi)發(fā)套件,此產(chǎn)品是ARM公司最新推出的針對各種嵌入式處理器的軟件開(kāi)發(fā)工具,該開(kāi)發(fā)套件功能強大,包括了μVisiON3集成開(kāi)發(fā)環(huán)境和RealView編譯器。使用的硬件平臺是深圳英蓓特公司推出的全功能評估板STMl03V100,其上所采用的處理器是ST意法半導體公司生產(chǎn)的32位哈佛結構ARM處理器STM32F103VBT6,該處理器內置ARM公司最新的Cortex—M3核,并且具有非常豐富的片上資源。
1.2 關(guān)于基于Cortex-M3的ARM處理器的介紹
基于Cortex—M3核的ARM處理器支持兩種模式,分別稱(chēng)為線(xiàn)程模式和處理模式。程序可以在系統復位時(shí)或中斷返回時(shí)兩種情況下進(jìn)入線(xiàn)程模式,而處理模式只能通過(guò)中斷或異常的方式來(lái)進(jìn)入。處于線(xiàn)程模式中代碼可以分別運行在特權方式下和非特權方式下。處于處理模式中的代碼總是運行在特權方式下。運行在特權方式下的代碼對系統資源具有完全訪(fǎng)問(wèn)權,而運行在非特權方式下的代碼對系統資源的訪(fǎng)問(wèn)權受到一定限制。處理器可以運行在Thumb狀態(tài)或Debug狀態(tài)。在指令流正常執行期間,處理器處于Thumb狀態(tài)。當進(jìn)行程序調試時(shí),指令流可以暫停執行,這時(shí)處理器處于Debug狀態(tài)。處理器有兩個(gè)獨立的堆棧指針,分別稱(chēng)為MSP和PSP。系統復位時(shí)總是處于線(xiàn)程模式的特權方式下,并且默認使用的堆棧指針是MSP。本移植過(guò)程中假設任務(wù)總是運行在線(xiàn)程模式的特權方式下且總是使用堆棧指針PSP。
2 移植過(guò)程詳解
2.1 μCOS-II內核介紹
μCOS—II是一個(gè)實(shí)時(shí)可剝奪型操作系統內核,該操作系統支持最多64個(gè)任務(wù),但每個(gè)任務(wù)的優(yōu)先級必須互不相同,優(yōu)先級號小的任務(wù)比優(yōu)先級號大的任務(wù)具有更高的優(yōu)先級,并且該操作系統總是調度優(yōu)先級最高的就緒態(tài)任務(wù)運行。此內核的代碼是美國人Jean J.Labrosse用C語(yǔ)言編寫(xiě)的,具有很好的可移植性,其2.52版本通過(guò)了美國航天航空管理局的安全認證,可靠性非常高。文中所述的移植過(guò)程使用的就是該版本的源代碼。
2.2 開(kāi)始移植
μCOS—II v2.52的源代碼按照移植要求分為需要修改部分和不需要修改部分。其中需要修改源代碼的文件包括頭文件OS_CPU.H、C語(yǔ)言文件OS_CPU.C以及匯編格式文件OS_CPU_A.ASM。
2.2.1 修改頭文件OS_CPU.H
頭文件OS_CPU.H中需要修改的內容有與編譯器相關(guān)的數據類(lèi)型重定義部分和與處理器相關(guān)的少量代碼。由于本移植過(guò)程中使用的是RealView編譯器,因此通過(guò)查閱此編譯器的相關(guān)說(shuō)明文檔可以得到其所支持的基本數據類(lèi)型,據此修改OS_CPU.H中與編譯器相關(guān)的數據類(lèi)型重定義部分。修改后代碼如下所示:
其中定義的數據類(lèi)型OS_STK指出了處理器堆棧中的數據是32位的,OS_CPU_SR指出了處理器狀態(tài)寄存器字長(cháng)也為32位。
頭文件中與處理器相關(guān)部分代碼包括臨界區訪(fǎng)問(wèn)處理、處理器堆棧增長(cháng)方向及任務(wù)切換宏定義。臨界區代碼訪(fǎng)問(wèn)涉及到全局中斷開(kāi)關(guān)指令,由文獻可以得知關(guān)中斷和開(kāi)中斷可以分別由指令CPSID i和CPSIE i實(shí)現,文中臨界段訪(fǎng)問(wèn)處理如下:
其中INT_DIS()和INT_EN()分別對應關(guān)中斷和開(kāi)中斷處理過(guò)程。
根據文獻可知文中所使用的處理器支持的堆棧為滿(mǎn)遞減方式,即堆棧的增長(cháng)方向是從內存高地址向低地址方向遞減并且堆棧指針總是指向棧頂的數據。在頭文件OS_CPU.H中相應代碼只須修改一條,如下所示
#define OS_STK_GROWTH1
此定義中的1代表堆棧方向是向下遞減的。
頭文件OS_CPU.H中最后一個(gè)要修改的地方是任務(wù)切換宏定義,μCOS—II內核就是通過(guò)這個(gè)宏調用來(lái)觸發(fā)任務(wù)級的任務(wù)切換。任務(wù)切換一般是通過(guò)陷阱或軟件中斷來(lái)實(shí)現的,在基于Cortex—M3核的處理器中支持一條稱(chēng)為超級用戶(hù)調用的指令SVC,此指令是ARM軟件中斷指令SWI的升級版。此處的宏定義代碼修改為如下形式
#define OS_TASK_SW()OS_SVC()
其中OS_SVC()之中包含了SVC指令,它可以由嵌入匯編的方式在C語(yǔ)言代碼中進(jìn)行定義,如下所示
_asm void OS_SVC(void){SVCOx00}
以上代碼以嵌入匯編的方式定義了一個(gè)輸入參數和返回值都為空的C語(yǔ)言函數,嵌入匯編的格式在RealView編譯器的說(shuō)明文檔中有詳細的說(shuō)明。
2.2.2 修改C語(yǔ)言文件OS_CPU.C
根據文獻可知文件OS_CPU.C中有10個(gè)C語(yǔ)言函數需要編寫(xiě),這些函數中唯一必要的函數是OSTaskStkInit,其他9個(gè)函數必須聲明,但不一定要包含任何代碼。為了簡(jiǎn)潔起見(jiàn),本移植過(guò)程只編寫(xiě)了OSTaskStkInit,此函數的作用是把任務(wù)堆棧初始化成好像剛發(fā)生過(guò)中斷一樣。要初始化堆棧首先必須了解微處理器在中斷發(fā)生前后的堆棧結構,根據文獻易知微處理器在中斷發(fā)生前后的堆棧結構,并且可知寄存器xPSR、PC、LR、R12、R3、R2、R1、RO是中斷時(shí)由硬件自動(dòng)保存的。初始化時(shí)需要注意的地方是xPSR、PC和LR的初值,對于其他寄存器的初值沒(méi)有特別的要求。xPSR比特位是Thumb狀態(tài)位,初始化時(shí)須置1,否則執行代碼時(shí)會(huì )引起一個(gè)稱(chēng)為Invstate的異常,這是因為內置Cortex—M3核的微處理器只支持Thumb和Thumb2指令集。堆棧中PC和LR須初始化為任務(wù)的入口地址值,這樣才能在任務(wù)切換時(shí)跳轉到正確的地方開(kāi)始執行。此函數可以用以下代碼來(lái)實(shí)現:
2.2.3 修改匯編語(yǔ)言文件OS_CPU_A.ASM
匯編文件OS_CPU_A.ASM中需要編寫(xiě)的函數分別為OSStartHighRdy、OSCtxSw、OSIntCtxSw和OSTickISR。第一個(gè)函數的作用是啟動(dòng)多任務(wù)調度,此函數只在操作系統開(kāi)始調度任務(wù)前執行一次,以后不再調用。按照文獻中所述須將堆棧中的寄存器依次彈出,然后執行一條中斷返回指令來(lái)開(kāi)始第一個(gè)用戶(hù)任務(wù)的調度。但基于Cortex—M3核的ARM處理器在執行中斷返回指令時(shí)必須處于處理模式下,否則將會(huì )引起內存訪(fǎng)問(wèn)異常。當系統上電啟動(dòng)時(shí)或程序重置后,處理器會(huì )進(jìn)入線(xiàn)程模式,而要在函數OSStartHighRdy中執行中斷返回指令就首先需要進(jìn)行模式轉換,進(jìn)入處理模式,而進(jìn)行同步可控制模式轉換的途徑是超級用戶(hù)調用,即通過(guò)SVC指令產(chǎn)生軟件中斷可轉換到處理模式。實(shí)際上考慮到此函數只在啟動(dòng)多任務(wù)調度開(kāi)始前被調用一次,并且第一次調度任務(wù)運行時(shí)任務(wù)堆棧中除了xPSR、PC和LR的初值以外,其他寄存器的初值無(wú)關(guān)緊要。因此可以簡(jiǎn)化該函數的編寫(xiě),只須從第一個(gè)任務(wù)的堆棧中取出該任務(wù)的首地址,然后修改堆棧指針使其指向任務(wù)堆棧中內存地址最高處,即相當于拋棄任務(wù)堆棧中所有數據,最后根據取出的地址直接跳轉到任務(wù)入口地址處開(kāi)始執行。這樣可以免去軟件中斷和模式切換,從而簡(jiǎn)化了對此函數的編寫(xiě)。需要說(shuō)明的是在拋棄任務(wù)堆棧中所用數據的同時(shí)也將xPSR的初值拋棄了,但這并不影響第一個(gè)任務(wù)投人運行,因為在跳轉到第一個(gè)任務(wù)運行之前,指令流是在Thumb狀態(tài)下正常執行的,xPSR已經(jīng)有了確定的值。此函數代碼如下所示:
BX r0;直接跳轉到第一個(gè)任務(wù)的入口地址
第二個(gè)匯編語(yǔ)言函數OSCtxSw是任務(wù)級的任務(wù)切換函數。若在任務(wù)執行過(guò)程中有一個(gè)比當前任務(wù)優(yōu)先級更高的任務(wù)進(jìn)入就緒態(tài),μCOS—II內核就會(huì )啟動(dòng)OSCtxSw進(jìn)行任務(wù)切換。該函數會(huì )保存當前任務(wù)狀態(tài),然后恢復那個(gè)優(yōu)先級更高的任務(wù)狀態(tài),使之投入運行。前述的宏定義#defineOS_TASK_SW()OS_SVC()中的OS_SVC()包含了SVC軟件中斷指令,此中斷的中斷向量應該設為函數OSCtxSw的入口地址,即OSCtxSw是SVC指令產(chǎn)生中斷的中斷服務(wù)程序,其源代碼如下:
由于微處理器在進(jìn)入中斷時(shí)按堆棧增長(cháng)方向自動(dòng)順序保存了如下8個(gè)寄存器:xPSR、PC、LR、R12、R3、R2、R1、R0,因此在程序中只須保存另外8個(gè)寄存器,保存順序可以隨意,但注意彈棧時(shí)要按照先進(jìn)后出的方式進(jìn)行。按照本文開(kāi)頭的假定,任務(wù)總是運行在線(xiàn)程模式的特權方式下且總是使用堆棧指針PSP。而中斷產(chǎn)生后,中斷服務(wù)程序將處于處理模式下,并且默認使用的堆棧指針是MSP。因此在保存堆棧指針的時(shí)候需要保存的是當前任務(wù)的PSP。中斷返回前新任務(wù)的堆棧指針需要恢復到PSP中。中斷返回使用如下指令
MOVrO,#Oxfffffffd
BXr0
其中立即數#0xfffffffd包含了返回信息,用這兩條指令可以使中斷返回時(shí)使用任務(wù)堆棧指針PSP,返回后任務(wù)處于線(xiàn)程模式且使用任務(wù)堆棧指針PSP。
第三個(gè)匯編語(yǔ)言函數OSIntCtxSw與OSCtxSw類(lèi)似。若任務(wù)執行過(guò)程中產(chǎn)生了中斷,且中斷服務(wù)程序使得一個(gè)比當前被中斷的任務(wù)具有更高優(yōu)先級的任務(wù)就緒時(shí),μCOS—II內核就會(huì )在中斷返回之前調用函數OSIntCtxSw。在此函數中不需要像任務(wù)級任務(wù)切換函數那樣保存當前任務(wù)狀態(tài),因為當前任務(wù)已經(jīng)被中斷,在進(jìn)入中斷服務(wù)程序的時(shí)候任務(wù)狀態(tài)已被保存。其源代碼與函數OSctxSw中保存當前任務(wù)堆棧PSP指令以后部分相同,此處不再列出。
第4個(gè)匯編語(yǔ)言函數OSTickISR是系統時(shí)鐘節拍的中斷服務(wù)函數。處理器STM32F103VBT6中有一個(gè)專(zhuān)用系統時(shí)鐘節拍定時(shí)器SysTick,本移植過(guò)程使用此定時(shí)器產(chǎn)生每100 ms一次的時(shí)鐘節拍中斷。此函數源代碼如下:
3 程序開(kāi)發(fā)模式討論
傳統應用程序開(kāi)發(fā)模式稱(chēng)為超循環(huán)模式,即通常主程序是由C語(yǔ)言中的for語(yǔ)句或while語(yǔ)句構成的一個(gè)無(wú)限循環(huán),程序在此循環(huán)中檢測事件的發(fā)生,從而轉向不同的任務(wù)。這種程序開(kāi)發(fā)模式有兩個(gè)主要的不足之處。首先從程序維護和可靠性的角度來(lái)看,所有任務(wù)都需要程序開(kāi)發(fā)人員來(lái)進(jìn)行全局性的維護,當系統變得龐大和復雜時(shí),任務(wù)的維護會(huì )變得非常麻煩,同時(shí)程序的可靠性也受到影響。其次,從任務(wù)級響應時(shí)間來(lái)看,這個(gè)時(shí)間是不確定的,因為程序在循環(huán)體中檢測事件發(fā)生的位置是固定的,但事件的發(fā)生是隨機的,因此從事件發(fā)生到程序檢測到事件發(fā)生這段時(shí)間也是不確定的。
在基于嵌入式操作系統的應用程序開(kāi)發(fā)過(guò)程中,應用程序開(kāi)發(fā)人員只需關(guān)心各個(gè)任務(wù)本身,而任務(wù)調度由操作系統代勞。以下的例子說(shuō)明了基于μCOS—II嵌入式操作系統的應用程序開(kāi)發(fā)模式
其中函數SysInit的作用是根據具體應用對處理器芯片進(jìn)行必要的初始化,例如對系統的時(shí)鐘分配以及通用輸入輸出端口配置。函數OSInit是μCOS—II操作系統的內核初始化程序。第一個(gè)OSTaskCreate函數創(chuàng )建了任務(wù)Taskl,此任務(wù)的入口地址是Taskl,優(yōu)先級是0。第二個(gè)OSTaskCreate函數創(chuàng )建了任務(wù)Task2,此任務(wù)的入口地址是Task2,優(yōu)先級是1。函數OSTaskCrate還會(huì )將其創(chuàng )建的任務(wù)置于就緒態(tài)。文獻敘述了函數OSTa-skCreate的各個(gè)參數的含義。函數OSStart用于啟動(dòng)多任務(wù)調度。OSTimeDly是μCOS—II內核提供的系統調用函數,用于延時(shí)或定時(shí),這里的參數5表示延時(shí)5個(gè)時(shí)鐘節拍。應用程序開(kāi)發(fā)人員需要做的就是通過(guò)調用μCOS—II內核提供的任務(wù)創(chuàng )建函數OSTaskCreate將編寫(xiě)好的任務(wù)程序交給操作系統管理。
該例中在調用OSStart后,操作系統發(fā)現任務(wù)Taskl的優(yōu)先級最高,于是操作系統就調度任務(wù)Taskl使其投入運行,而任務(wù)Task2暫時(shí)不能獲得處理器的使用權。任務(wù)Taskl首先點(diǎn)亮一個(gè)LED,然后延時(shí)一段時(shí)間,當運行到OSTimeDly處時(shí),該任務(wù)被掛起而處于等待狀態(tài),此時(shí)任務(wù)Task2成為優(yōu)先級最高的就緒態(tài)任務(wù),于是操作系統調度Task2運行。當5個(gè)時(shí)鐘節拍的延時(shí)時(shí)間結束時(shí),系統時(shí)間節拍中斷服務(wù)子程序會(huì )重新將任務(wù)Taskl置于就緒狀態(tài),此時(shí)任務(wù)Taskl再一次成為優(yōu)先級最高的就緒態(tài)任務(wù),于是操作系統保存任務(wù)Task2的狀態(tài),并恢復任務(wù)Taskl的狀態(tài)使其又一次獲得處理器的使用權。此后程序執行過(guò)程將重復上述步驟??梢钥吹?,在這個(gè)例子中的現象是某個(gè)LED燈不停的閃爍。
μCOS—II操作系統內核是實(shí)時(shí)可剝奪型的,這意味著(zhù)在任務(wù)執行過(guò)程中或中斷服務(wù)子程序中,一旦有一個(gè)新的更高優(yōu)先級的任務(wù)就緒,內核將立刻調度此新任務(wù)運行,這說(shuō)明響應任務(wù)的時(shí)間是即刻的、確定的。
綜上所述,基于嵌入式操作系統的應用程序開(kāi)發(fā)過(guò)程相對于以往傳統應用程序開(kāi)發(fā)大為簡(jiǎn)化而且任務(wù)級響應時(shí)間也得到最優(yōu)化。
4 結束語(yǔ)
通過(guò)將移植過(guò)程中修改的μCOS—II內核代碼與上述例子中的應用程序代碼在μVision3集成開(kāi)發(fā)環(huán)境中編輯整合后進(jìn)行編譯、鏈接并且下載到目標硬件平臺進(jìn)行長(cháng)時(shí)間觀(guān)察,發(fā)現LED不停的閃爍,說(shuō)明本移植過(guò)程是成功的。
c語(yǔ)言相關(guān)文章:c語(yǔ)言教程
評論