嵌入式操作系統μC/OS-II在P89V51RD2中的移植
自嵌入式系統開(kāi)發(fā)以來(lái),很長(cháng)時(shí)間都采用前后臺系統軟件設計模式:主程序為一個(gè)無(wú)限循環(huán),單任務(wù)順序執行。通過(guò)設置一個(gè)或多個(gè)中斷來(lái)處理異步事件。
這種系統對于簡(jiǎn)單的應用是可以的,但對于實(shí)時(shí)性要求比較高的、處理任務(wù)較多的應用,就會(huì )暴露出實(shí)時(shí)性差、系統可靠性低、穩定性差等缺點(diǎn)。
μC/OS-II 是一種基于優(yōu)先級的搶占式多 任務(wù)實(shí)時(shí)操作系統, 包含了實(shí)時(shí)內核、任務(wù)管理、時(shí)間管理、任務(wù)間通信同步(信號量,郵箱,消息 隊列)和內存管理等功能。它可以使各個(gè)任務(wù)獨立工作,互不干涉,很容易實(shí)現準時(shí)而且無(wú)誤執行,使實(shí)時(shí)應用程序的設計和擴展變得容易,使應用程序的設計過(guò)程大為減化。而且它內核源代碼公開(kāi),可移植性強,為編程人員提供了很好的一個(gè)軟件平臺。通過(guò)μC/OS-II在P89V51RD2 上的移植,可以掌握移植和測試μC/OS-II 的實(shí)質(zhì)內容,很容易將其移植到其它的CPU平臺上。
μC/OS-II 介紹
μC /OS-II是一個(gè)完整的、可移植、可固化、可裁剪的占先式實(shí)時(shí)多任務(wù)內核。μC/OS-II絕大部分的代碼是用ANSI的C語(yǔ)言編寫(xiě)的,包含一小部 分匯編代碼,
μC/OS-II 內核結構
多任務(wù)系統中,內核負責管理各個(gè)任務(wù) ,或者說(shuō)為每個(gè)任務(wù)分配CPU 時(shí)間 ,并且負責任務(wù)之間的通訊。內核提供的基本服務(wù)是任務(wù)切換。 μC/OS-II可以管理多達64個(gè)任務(wù)。由于它的作者占用和保留了8個(gè)任務(wù),所以留給用戶(hù)應用程序最多 可有56個(gè)任務(wù)。賦予各個(gè)任務(wù)的優(yōu)先級必須是不相同的。這意味著(zhù)μC/OS-II不支持時(shí)間片輪轉調度法 (round-robin scheduli ng)。μC/OS-II為每個(gè)任務(wù)設置獨立的 堆??臻g,可以快速實(shí)現任務(wù)切換 。μC/OS-II近似地每時(shí)每刻總是讓優(yōu)先級最高的就緒任務(wù)處于運行狀態(tài),為了保證這一點(diǎn),它在調用系統API 函數、中斷結束、定時(shí)中斷結束時(shí)總是執行調度算法,μC/OS-II通過(guò)事先計算好數據簡(jiǎn)化了運算量,通過(guò)精心設計就緒表結構使得延時(shí)可預知。
P89V51RD2 微處理器介紹
P89V51RD2是Philips公司生產(chǎn)的一款80C51微控制器,包含64KB Flash和1024字節的數據RAM。P89V51RD2的典型特性是它的X2方式選項。利用該特性,設計者可使應用程序以傳統的80C51時(shí)鐘頻率(每個(gè)機器周期包含12個(gè)時(shí)鐘)或X2 方式(每個(gè)機器周期包含6個(gè)時(shí)鐘)的時(shí)鐘頻率運行,選擇X2方式可在相同時(shí)鐘頻率下獲得2倍的吞吐量。從該特性獲益的另一種方法是將時(shí)鐘頻率減半來(lái)保持特性不變,這 樣可以極大地降低EMI。Flash程序存儲器支持并行和串行在系統編程(ISP),ISP允許在軟件控制下對成品中的器件進(jìn)行重復編程。應用固件的 產(chǎn)生/更新能力實(shí)現了ISP的大范圍應用。 5V的工作電壓,操作頻率為0~40MHz。P89V51RD2的資源和ISP的功能使得它很適合用來(lái)做μC/OS-II的移植調試。并不需要購買(mǎi)仿真器和編程器等額外投資。
μC/OS-II 的移植
移植就是使μC/OS-II能在P89V51RD2上運行。為了方便移植,大部分的μC/OS-II的代碼是用C語(yǔ)言編寫(xiě)的;但是仍需要用C語(yǔ)言和匯編語(yǔ)言編寫(xiě)一些處理器硬件相關(guān)的代碼,這是因為μC/OS-II在讀/寫(xiě)處理器寄存器時(shí),只能通過(guò)匯編語(yǔ)言來(lái)實(shí)現。由于μC/OS-II在設計時(shí)就已經(jīng)充分考慮了可移植性,所以μC/OS-II的移植相對來(lái)說(shuō)是比較容易的。
硬件平臺構成
由于P89V51RD2是一款80C51微控制器,片內包含了64KB的FLASH程序存儲器,并且支持串行在線(xiàn)編程(ISP)。使它在ROM空間上很適合做μC/OS-II的移植。但是它片內RAM空間很有限,只有1KB,不能滿(mǎn)足μC/OS-II對RAM的要求。但是由于P89V51RD2可以擴展RAM空間,使這一問(wèn)題得以解決。我們?yōu)樗鼣U展了一片32KB的RAM來(lái)構成移植μC/OS-II的硬件平臺。這樣P89V51RD2就滿(mǎn)足了移植μC/OS-II的所有要求。
編譯器的選擇
由于μC/OS-II絕大部分代碼是用標準的C語(yǔ)言編寫(xiě)的,所以C語(yǔ)言開(kāi)發(fā)工具對于μC/OS-II是必不可少的。由于μC/OS-II是一個(gè)可剝奪行的占先式內核,所以要求C編譯器可以產(chǎn)生可重入型代碼。筆者選擇Keil C51集成開(kāi)發(fā)環(huán)境作為開(kāi)發(fā)工具。該開(kāi)發(fā)工具有C編譯器,匯編器和鏈接定位器等工具構成。鏈接器用來(lái)將不同模塊(編譯過(guò)或匯編過(guò)的文件)鏈接成目標文件,定位器則允許將代碼和數據放置在目標處理器的指定內存中。Keil C51 還可以生成HEX格式的編程文件用于編程EPROM或是FLASH,同時(shí)可以實(shí)現完整軟件仿真支持。Keil C51支持所有8051變種的微控制器。通過(guò)設置編譯控制選項,它完全可以滿(mǎn)足編譯μC/OS-II源代碼的要求。
可重入函數問(wèn)題
可重入函數可以被一個(gè)以上的任務(wù)調用,而不必擔心數據被破壞??芍厝牒瘮等魏螘r(shí)候都可以被中斷,一段時(shí)間后又可以繼續運行,而相應的數據不會(huì )丟失。由于μC/OS-II是搶占式的實(shí)時(shí)多任務(wù)內核,同一個(gè)函數可能會(huì )被不同的任務(wù)調用,也可能會(huì )被中斷,因此,移植μC/OS-II要求C語(yǔ)
μC/OS-II源文件移植
在了解了P89V51RD2微處理器和Keil C51 編譯器的技術(shù)細節的基礎上,就可以開(kāi)始μC/OS-II源文件移植的工作了。真正編寫(xiě)移植代碼的工作就相對比較簡(jiǎn)單了。圖1表示了基于μC/OS-II的應用的系統結構結構。由圖1可以看出由于μC/OS-II自生的絕大部分代碼是使用ANSI C編寫(xiě)的,而且代碼的層次結構十分干凈,與平臺相關(guān)的移植代碼僅僅存在于OS_CPU_A.ASM、OS_CPU_C.C以及OS_CPU.H這三個(gè)文件當中。下面分別解釋各個(gè)文件在P89V51RD2上的移植。
圖1 μC/OS-II軟件體系結構和各模塊之間的關(guān)系
μC/OS-II中與處理器CPU 類(lèi)型無(wú)關(guān)的代碼:uCOS_II.H和uCOS_II.C,其中uCOS_II.C 文件包含以下文件:OS_CORE.C OS_TASK.C OS_TIME.C OS_SEM.C OS_MBOX.C OS_MUTEX.C 和OS_FLAG.C 也就是說(shuō)原則上這些文件可以直接添加不用修改。但是由于Keil C51編譯器的特殊性,這些代碼仍要多處改動(dòng),因為Keil C51缺省情況下編譯的代碼不可重入而多任務(wù)系統要求并發(fā)操作導致重入,所以要在每個(gè)C 函數及其聲明后標注reentrant 關(guān)鍵字,另外“pdata” 和“data” 在uCOS中用做一些函數的形參,但它同時(shí)又是Keil C51 的關(guān)鍵字,會(huì )導致編譯錯誤。我通過(guò)把“pdata”改成“ppdata”,“data”改成“ddata”解決了此問(wèn)題。OSTCBCur、OSTCBHighRdy、OSRunning、OSPrioCur、OSPrioHighRdy 這幾個(gè)變量在匯編程序中用到了,為了使用寄存器R0或R1訪(fǎng)問(wèn)而不用DPTR,應該用Keil C51擴展關(guān)鍵字IDATA將它們定義在內部RAM中。
OS_CPU.H的移植
OS_CPU.H包括了用#define語(yǔ)句定義的、與處理器相關(guān)的常數、宏及類(lèi)型。因為不同的處理器有不同的字長(cháng),所以μC/OS-II的移植包括的一系列數據類(lèi)型定義,以確保其可移植性。μC/OS-II代碼不使用語(yǔ)言中的short,int,及l(fā)ong等數據類(lèi)型,因為它們是與編譯器相關(guān)的,是不可移植的。采用定義的整形數據結構等既是可移植的,又很直觀(guān)。參考Cx51編譯手冊,可以完成OS_CPU.H里所有數據類(lèi)型的定義。
與所有的實(shí)時(shí)內核一樣,μC/OS-II需要先關(guān)中斷,再處置臨界段代碼,并且在處置完畢后重新開(kāi)中斷。這樣可以保護臨界段代碼免受多任務(wù)或中斷服務(wù)子程序的破壞。為了隱藏不同編譯器提供的不同的關(guān)中斷和開(kāi)中斷的實(shí)現方法,增強可移植性,μC/OS-II在OS_CPU.H中定義了2個(gè)宏,來(lái)開(kāi)中斷和關(guān)中斷:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。根據P89V51RD2的結構和Keil C51提供的方法,我們通過(guò)置位或清零中斷允許位來(lái)實(shí)現。
代碼如下:
OS_ENTER_CRITICAL() EA="0"
OS_EXIT_CRITICAL() EA="1"
MCS-51 堆棧從下往上增長(cháng)(1=向下0=向上) ,OS_STK_GROWTH 定義為0。
OS_TASK_SW() OSCtxSw() ,因為P89V51RD2沒(méi)有軟中斷指令所以用程序調用代替。在用匯編語(yǔ)言編寫(xiě)的OSCtxSw()中,模擬系統產(chǎn)生中斷時(shí)的堆棧操作。以保證系統任務(wù)的正確切換。
OS_CPU_C.C的移植
μC/OS-II的移植要求用戶(hù)在OS_CPU_C.C中編寫(xiě)10個(gè)簡(jiǎn)單的C函數。但唯一必要的μC/OS-II的移植要求用戶(hù)在OS_CPU_C.C中編寫(xiě)10個(gè)簡(jiǎn)單的C函數。但唯一
OSTaskStkInit()是在系統創(chuàng )建任務(wù)時(shí)用來(lái)初始化任務(wù)堆棧的,使堆??雌饋?lái)就象中斷剛發(fā)生一樣,所有寄存器都保存在堆棧中。由于P89V51RD2硬件堆棧很小,最多只能有在內部RAM空間的256字節。因此很難將所有任務(wù)的堆棧都用硬件堆棧來(lái)實(shí)現。為了解決這個(gè)問(wèn)題,我們?yōu)槊總€(gè)任務(wù)在外部RAM空間都分配一段連續的存儲區,用來(lái)模擬每個(gè)任務(wù)的堆棧。
在μC/OS-II進(jìn)行任務(wù)切換時(shí),首先將P89V51RD2硬件堆棧中的內容復制到要失去CPU擁有權的任務(wù)的外部模擬堆棧區,然后將要得到CPU擁有權的任務(wù)的外部模擬堆棧中的有效數據復制到P89V51RD2的硬件堆棧中。這樣就實(shí)現了任務(wù)保護和切換。任務(wù)模擬堆棧和硬件的堆棧結構如圖2所示。TCB 結構體中OSTCBStkPtr 總是指向用戶(hù)堆棧最低地址,該地址空間內存放用戶(hù)堆棧長(cháng)度,其上空間存放系統堆棧映像,即:任務(wù)模擬堆??臻g大小=系統硬件堆??臻g大小+1。SP 總是先加1再存數據,因此SP初始時(shí)指向系統堆棧起始地址(OSStack)減1 處(OSStkStart)。很明顯系統硬件堆棧存儲空間大小=SP-OSStkStart。編寫(xiě)OSTaskStkInit()主要完成用戶(hù)堆棧初始化,從下向上依次保存用戶(hù)堆棧長(cháng)度(5),PCL, PCH,PSW, AC C,B, DPL, DPH,R0,R1, R2,R3,R4,R5,R6,R7。不保存SP,任務(wù)切換時(shí)根據用戶(hù)堆棧長(cháng)度計算得出。緊接著(zhù)的兩字節保存可重入函數仿真堆棧的指針X_CP的高8位和低8位,初始化為任務(wù)模擬棧的最高地址的高8位和低8位。OSTaskStkInit()總是返回任務(wù)模擬棧的最低地址。
圖2 P89V51RD2移植μC/OS-II的堆棧結構
OS_CPU_A.ASM的移植
OS_CPU_A.ASM的移植要求用戶(hù)編寫(xiě)4個(gè)簡(jiǎn)單的匯編語(yǔ)言函數:
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
μ C/OS-II的啟動(dòng)函數OSStart()調用OSStartHighRdy()來(lái)使就緒態(tài)任務(wù)中 優(yōu)先級最高的任務(wù)開(kāi)始運行,我們通過(guò)將任務(wù)模擬棧的有效長(cháng)度內的數據復制到系統硬件堆棧,然后使用緊接著(zhù)的兩字節來(lái)改寫(xiě)X_CP的值。使可重入函數仿真堆棧指針指向該任務(wù)模擬棧的最高地址,這樣做是因為Keil C51使用的可重入函數仿真堆棧的增長(cháng)方向是向下的,和系統硬件堆棧的增長(cháng)方向相反。這樣就完成了OSStartHighRdy()的移植。
OSCtxSw()和OSIntCtxSw()兩個(gè)匯編函數的功能主要完成任務(wù)的切換。不同的是OSCtxSw()在任務(wù)級調用,而OSIntCtxSw()是在中斷推出時(shí)調用。
對于在P89V51RD2上的移植而言,這兩個(gè)函數的實(shí)現基本相同。只是OSIntCtxSw()在中斷調用中 由于OSIntExit()和自身對硬件堆棧的影響,需要將要保存的SP指針向下調整4個(gè)字節,以消除影響。μC/OS-II在需要任務(wù)切換時(shí),根據CPU是否處在中斷狀態(tài)選擇調用其中一個(gè)函數。如圖2堆棧結構 所示,任務(wù)切換時(shí)先保存當前任務(wù)堆棧內容,方法是:用SP-OSStkStart 得出保存字節數。
將其寫(xiě)入任務(wù)模擬堆棧最低地址內。以任務(wù)模擬堆棧最低地址為起址,以OSStkStart為系統硬件堆棧起址,由系統堆棧向用戶(hù)堆??截悢祿?。循環(huán)SP-OSStkStart次,每次拷貝前先將各自棧指針增1。其次恢復最高優(yōu)先級任務(wù)系統堆棧方法是:獲得最高優(yōu)先級任務(wù)用戶(hù)堆棧最低地址,從中取出長(cháng)度。以最高優(yōu)先級任務(wù)用戶(hù)模擬堆棧最低地址為起址,以OSStkStart 為系統堆棧起址,由任務(wù)模擬堆棧向系統堆??截悢祿?。循環(huán)“有效長(cháng)度”數值指示的次數。每次拷貝前先將各自棧指針增1。
μC/OS-II要求用戶(hù)提供一個(gè)周期性的時(shí)鐘源,來(lái)實(shí)現時(shí)間的延遲和超時(shí)功能。在P89V51RD2中我們通過(guò)定時(shí)器T0來(lái)提供時(shí)鐘源。頻率設為50Hz。T0的初始化函數在OS_CPU_C.C實(shí)現。時(shí)鐘節拍中斷服務(wù)子程序的編寫(xiě)也很簡(jiǎn)單,示意性代碼如下:
void OSTickISR(void)
{
保存處理器寄存器;
調用OSIntEnter();
定時(shí)器計數器重裝;
調用OSTimeTick();
調用OSIntExit();
恢復處理器寄存器;
執行中斷返回指令;
}
μC/OS-II 移植代碼的測試
完成μC/OS-II移植后,就要對移植的代碼進(jìn)行測試。測試移植的μC/OS-II是否能夠完成任務(wù)調度、時(shí)間管理、任務(wù)管
LedFlash等待信號量有效時(shí),對P1.1口進(jìn)行一次取反操作。P1.1連接LED驚醒觀(guān)察。定時(shí)器中斷服務(wù)子 程序定時(shí)發(fā)出信號量。這樣任務(wù)LedFlash實(shí)現LED的閃爍功能。
μC/OS-II測試程序的文件結構,硬件測試結果和Keil C51的軟件仿真結果如圖3所示。結果表明μC/OS-II在P89V51RD2上 的移植是成功的。
圖3 μC/OS-II的測試程序的文件結構,軟件仿真和硬件運行測試結果
結語(yǔ)
通過(guò)μC/OS-II在P89V51RD2上的移植,掌握了μC/OS-II內核的工作原理和移植方法,測試程序表明移植代碼可以穩定可靠的運行,實(shí)現了多任務(wù)的管理和調度。μC/OS-II實(shí)時(shí)操作系統的移入,不但可以提高系統的實(shí)時(shí)性、可靠性和穩定性,還提高了應用軟件的可移植性,降低了開(kāi)發(fā)人員的工作量。
linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)
評論