劉洪利,趙 萍
(上海電力學(xué)院計(jì)算機(jī)與信息工程學(xué)院,上海 200090)
近年來(lái),隨著科技的飛速發(fā)展,嵌入式應(yīng)用已經(jīng)滲透到生產(chǎn)和生活的各個(gè)領(lǐng)域.對(duì)于國(guó)內(nèi)中小型系統(tǒng)的設(shè)計(jì),免費(fèi)軟件和開放代碼是最佳選擇[1].μC/OS-Ⅱ是目前源碼開放的嵌入式系統(tǒng)之一,它提供了操作系統(tǒng)最基本的功能,其核心代碼短小精悍,易于移植,受到人們的青睞.μC/OS-Ⅱ已通過(guò)聯(lián)邦航空局商用航行器認(rèn)證,成功移植到40多種CPU上,但其數(shù)據(jù)和程序存儲(chǔ)器的開銷很大,至少要達(dá)到8 k字節(jié).μC/OS-Ⅱ是源碼完全公開的嵌入式實(shí)時(shí)操作系統(tǒng),最多可分配64個(gè)任務(wù),其中系統(tǒng)任務(wù)占用8個(gè),其余56個(gè)任務(wù)用戶可以自由分配.
μC/OS-Ⅱ的代碼90%都是用ANSI C寫的,可移植性好,安全性高,代碼的容量至少為8 kB.由于AT89S51單片機(jī)的ROM只有4 kB,所以要擴(kuò)展的外部程序存儲(chǔ)器容量要大于8 kB;每個(gè)任務(wù)都有自己的硬件棧和仿真棧,硬件棧用于保存任務(wù)運(yùn)行時(shí)系統(tǒng)棧內(nèi)的數(shù)據(jù).用戶棧中保存的仿真棧與硬件棧相向生長(zhǎng),中間為空閑間隔.硬件棧的保存恢復(fù)是通過(guò)拷貝實(shí)現(xiàn)的.而對(duì)于仿真堆棧的保存,μC/OS-Ⅱ只提供堆??臻g和只操作堆棧指針,不進(jìn)行內(nèi)存拷貝,因此其效率相對(duì)較高.
盡管μC/OS-Ⅱ?qū)⒉煌蝿?wù)使用不同空間看成是優(yōu)點(diǎn),但為了在51單片機(jī)上有效實(shí)現(xiàn)任務(wù)重入,建議用戶使用統(tǒng)一的固定大小的堆??臻g.用戶堆棧空間的大小是可以精確計(jì)算出來(lái)的,用戶堆棧空間=硬件堆??臻g+仿真堆棧空間.硬件棧占用內(nèi)部RAM,內(nèi)部RAM執(zhí)行效率高、速度快.如果堆棧空間過(guò)大,會(huì)影響KEIL編譯的程序性能,如果堆棧空間小,在中斷嵌套和程序調(diào)用時(shí)會(huì)造成系統(tǒng)崩潰,因此綜合考慮,可將硬件堆??臻g大小確定為64 B,用戶可以根據(jù)實(shí)際情況自行設(shè)定.仿真堆棧大小取決于形參和局部變量的類型及數(shù)量,并可以精確算出.因?yàn)樗杏脩魲J褂孟嗤臻g,所以取占用空間最大的任務(wù)函數(shù)的空間大小為仿真堆??臻g大小,這樣用戶堆??臻g大小就唯一確定了.將用戶堆??臻g大小用宏定義在OS_CFG.H文件中,宏名為MaxStkSize.
由于AT89S51片內(nèi)只有128個(gè)數(shù)據(jù)存儲(chǔ)器單元和4 k個(gè)ROM單元,因此需要根據(jù)系統(tǒng)中任務(wù)個(gè)數(shù)的多少來(lái)外擴(kuò)程序存儲(chǔ)器和數(shù)據(jù)存儲(chǔ)器[2].為了便于調(diào)試,系統(tǒng)還添加了液晶顯示模塊和按鍵輸入模塊.其中,數(shù)據(jù)RAM采用靜態(tài)數(shù)據(jù)存儲(chǔ)器6164(8 k×8位);程序存儲(chǔ)器采用EPROM27128(16 k×8位);液晶顯示器采用金鵬公司的C系列OCMJ4X8C顯示模塊,可以顯示字母、數(shù)字、漢字及圖形等,也可用于顯示鍵盤輸入值.鍵盤采用矩陣式鍵盤,可以節(jié)省單片機(jī)接口.其硬件結(jié)構(gòu)如圖1所示.
圖1 系統(tǒng)原理示意
處理器和編譯器需要滿足下列要求[3]:
(1)所用的C編譯器可產(chǎn)生可重入型代碼;
(2)用C語(yǔ)言就可以打開和關(guān)閉中斷;
(3)處理器能產(chǎn)生中斷,通常在10~100 Hz;
(4)處理器帶有能容納一定數(shù)量數(shù)據(jù)的硬件堆棧.
(5)處理器有將堆棧指針和其他的CPU寄存器從內(nèi)存中讀出和存到堆?;騼?nèi)存中的指令.
AT89S51單片機(jī)和KEIL C51編譯器完全可以滿足上述要求,因此可以將μC/OS-Ⅱ移植到AT89S51單片機(jī)上.
μC/OS-Ⅱ操作系統(tǒng)的軟件結(jié)構(gòu)如圖2所示[4],其中“核心代碼(處理器無(wú)關(guān))”部分從網(wǎng)上直接下載無(wú)需修改;“設(shè)置代碼(應(yīng)用相關(guān))”部分在移植時(shí)根據(jù)項(xiàng)目要求僅作少許修改即可;OS_CPU.H,OS_CPU_A.ASM,OS_CPU_C.C 是與處理器緊密相關(guān)的代碼,移植的主要工作就是根據(jù)處理器編寫這3個(gè)函數(shù).
圖2 μC/OS-Ⅱ的軟件結(jié)構(gòu)
OS_CPU.H主要定義與KEIL C51編譯器相關(guān)的數(shù)據(jù)類型、宏和常量.在μC/OS-Ⅱ執(zhí)行臨界段代碼前要先關(guān)中斷,執(zhí)行完后又要開中斷.在KEIL C51編譯器中,可以用C語(yǔ)言直接開/關(guān)中斷.用 EA=0可以關(guān)中斷,則有#define OS_ENTER_CRITICAL EA=0;用EA=1可以開中斷,則有#define OS_EXIT_CRITICAL EA=1.開/關(guān)中斷的方法有3種,在這里采用最簡(jiǎn)單的方法,即進(jìn)入臨界段代碼前先關(guān)閉中斷,執(zhí)行完臨界段代碼后再打開中斷,則有#define OS_CRITICAL_METHOD 1.
AT89S51單片機(jī)的堆棧增長(zhǎng)方向都是從低地址向高地址增長(zhǎng)的,所以有#define OS_STK_GROWTH 0.在μC/OS-Ⅱ中任務(wù)間進(jìn)行切換時(shí)要求采用軟中斷實(shí)現(xiàn),但AT89S51單片機(jī)沒(méi)有軟中斷指令,且函數(shù)調(diào)用與中斷的堆棧結(jié)構(gòu)相同,因此可以用函數(shù)調(diào)用來(lái)實(shí)現(xiàn)入棧,即#define OS_TASK_SW()OSCtxSw(),用中斷返回指令RETI實(shí)現(xiàn)出棧.
在OS_CPU_C.C中主要是編寫函數(shù)OSTaskStkInit(),該函數(shù)的主要任務(wù)是初始化新建任務(wù)的私有堆棧.μC/OS-Ⅱ處于就緒態(tài)的任務(wù)堆棧看起來(lái)像剛剛發(fā)生過(guò)中斷一樣,所有CPU寄存器中的數(shù)據(jù)都保存在任務(wù)的私有堆棧中.AT89S51單片機(jī)在函數(shù)調(diào)用時(shí)只將程序計(jì)數(shù)器PC的值(16位數(shù)據(jù))自動(dòng)壓入系統(tǒng)棧中,PSW,ACC,B,DPL,DPH,R0,R1,R2,R3,R4,R5,R6,R7,SP則需要手動(dòng)依次保存到系統(tǒng)棧.在任務(wù)切換時(shí),為了實(shí)現(xiàn)任務(wù)的私有堆棧和系統(tǒng)棧之間的復(fù)制,任務(wù)私有堆棧的結(jié)構(gòu)和系統(tǒng)棧結(jié)構(gòu)應(yīng)該基本相同,主要區(qū)別是在任務(wù)私有堆棧中還要保存仿真棧的地址,每個(gè)任務(wù)都有自己的仿真棧,仿真棧由編譯器自動(dòng)分配.任務(wù)私有堆棧不是真正的堆棧,是外部RAM,所以CPU寄存器的值都要手動(dòng)保存,而系統(tǒng)棧只需手動(dòng)保存除PC寄存器之外的其他寄存器.任務(wù)私有堆棧和系統(tǒng)棧的結(jié)構(gòu)如圖3所示.
圖3 任務(wù)私有堆棧和系統(tǒng)棧的結(jié)構(gòu)
OS_CPU_A.ASM包含了與處理器AT89S51緊密相關(guān)的匯編代碼,由 OSStartHighRdy(),OSCtxSw(),OSIntCtxSw(),OSTickISR()4 個(gè)函數(shù)組成.
(1)OSStartHighRdy() 用來(lái)查找就緒表中優(yōu)先級(jí)最高的任務(wù)并加以運(yùn)行.首先根據(jù)最高優(yōu)先級(jí)任務(wù)的任務(wù)控制塊指針,找到該任務(wù)的私有堆棧指針,然后找到私有堆棧的長(zhǎng)度,并將私有堆棧中的數(shù)據(jù)復(fù)制到系統(tǒng)棧,再通過(guò)POPALL將除PC之外的寄存器值從系統(tǒng)棧彈到CPU寄存器后,用RETI指令將系統(tǒng)棧中PC的值恢復(fù)到PC寄存器[5].
(2)OSCtxSw() 主要任務(wù)是保存當(dāng)前任務(wù)的上下文,然后將就緒表中優(yōu)先級(jí)最高任務(wù)的私有堆棧中的數(shù)據(jù)恢復(fù)到CPU的各個(gè)寄存器中.先將CPU寄存器(除PC外)中的數(shù)據(jù)用PUSHALL指令保存到系統(tǒng)棧中,并從系統(tǒng)棧復(fù)制到任務(wù)的私有堆棧中,然后將新的棧頂指針保存到當(dāng)前任務(wù)的任務(wù)控制塊中,由此當(dāng)前任務(wù)的上下文就保存完畢.將就緒表中優(yōu)先級(jí)最高的任務(wù)設(shè)置為當(dāng)前任務(wù),堆棧指針指向該任務(wù)的私有堆棧,將私有堆棧中的數(shù)據(jù)復(fù)制到系統(tǒng)棧,然后用POPALL指令將系統(tǒng)棧中的數(shù)據(jù)彈至CPU寄存器中,最后用RETI指令將PC的值彈出,CPU轉(zhuǎn)去執(zhí)行就緒表中優(yōu)先級(jí)最高的任務(wù).
(3)OSIntCtxSw() 由于當(dāng)前執(zhí)行的是中斷服務(wù)程序,不需要返回,只需將就緒表中優(yōu)先級(jí)最高的任務(wù)設(shè)置為當(dāng)前任務(wù),然后恢復(fù)其寄存器即可.它與OSCtxSw()的區(qū)別在于不用保存當(dāng)前任務(wù)的上下文.
(4)OSTickISR() 主要任務(wù)是安裝時(shí)鐘,并設(shè)置時(shí)鐘節(jié)拍.在AT89S51中,采用定時(shí)器零.在執(zhí)行OSTickISR()前,先關(guān)中斷,保存CPU寄存器的值到系統(tǒng)棧,設(shè)置時(shí)鐘頻率,啟動(dòng)定時(shí)器零,調(diào)用時(shí)鐘服務(wù)函數(shù),調(diào)度一次,恢復(fù)CPU寄存器的值,然后中斷返回.
原則上,與處理器無(wú)關(guān)的代碼不用修改.由于KEIL編譯器在缺省情況下編譯的代碼不可重入,所以要在每個(gè)C函數(shù)及其聲明后標(biāo)注reentrant關(guān)鍵字.另外,“pdata”和“data”在 uCOS中用作一些函數(shù)的形參,但它同時(shí)又是KEIL的關(guān)鍵字,容易導(dǎo)致編譯錯(cuò)誤.文獻(xiàn)[6]將“pdata”改成“ppdata”,“data”改成“ddata’,以避免此類錯(cuò)誤的發(fā)生.
在系統(tǒng)運(yùn)行前,必須設(shè)置時(shí)鐘節(jié)拍發(fā)生器定時(shí)器零的初值,定時(shí)器零工作模式設(shè)置為1,16位定時(shí)器,其初值的計(jì)算公式為:
式中:f——時(shí)鐘節(jié)拍的頻率,此處設(shè)置為50;
F0SC——晶振頻率,設(shè)置為12 MHz;
x——定時(shí)器初值,x=B1E0H.
設(shè)置好時(shí)鐘節(jié)拍發(fā)生器定時(shí)器零后,再配置μC/OS-Ⅱ,這樣代碼可以在仿真環(huán)境中通過(guò)斷點(diǎn)和跟蹤等手段進(jìn)行調(diào)試.為了更直觀地看到程序運(yùn)行結(jié)果,在此設(shè)置兩個(gè)任務(wù):Mytask和Yourtask.Mytask在屏上顯示為Mytask,優(yōu)先級(jí)為5;Yourtask在屏上顯示為Yourtask發(fā)送的消息,優(yōu)先級(jí)為6.
本文主要討論了 μC/OS-Ⅱ操作系統(tǒng)在AT89S51單片機(jī)上的應(yīng)用方法及其應(yīng)注意的問(wèn)題.實(shí)踐證明,該方法切實(shí)可行,簡(jiǎn)單易掌握,具有較強(qiáng)的實(shí)用性.但在使用過(guò)程還應(yīng)注意如下幾個(gè)問(wèn)題.一是μC/OS-Ⅱ是一個(gè)基于優(yōu)先級(jí)的實(shí)時(shí)操作系統(tǒng),優(yōu)先級(jí)是任務(wù)唯一的標(biāo)識(shí),每個(gè)任務(wù)的優(yōu)先級(jí)必須不同.為避免優(yōu)先級(jí)反轉(zhuǎn),需要使用互斥型信號(hào)量來(lái)管理共享資源.二是μC/OS-Ⅱ是多任務(wù)操作系統(tǒng),需要為每個(gè)任務(wù)分配私有堆棧(私有堆棧中包括中斷堆棧,且中斷可以嵌套達(dá)255層),但由于AT89S51單片機(jī)的內(nèi)部RAM只有128 B,且AT89S51的硬件堆棧不能放在片外,所以任務(wù)的私有堆棧只能放在外部RAM中.三是由于μC/OS-Ⅱ操作系統(tǒng)本身有大量的代碼,引入OS需要占用CPU10% ~20%的負(fù)荷能力.此外頻率決定了CPU的耗費(fèi),頻率越高耗費(fèi)越大,至一定程度時(shí)需更換更強(qiáng)的CPU.
[1]騰凌巧,劉常春,戴琨.嵌入式操作系統(tǒng)的移植與測(cè)試[J].平頂山工學(xué)院學(xué)報(bào),2003,12(4):33-35.
[2]LABROSSE Jean.μC/OS-Ⅱ源碼公開的實(shí)時(shí)嵌入式操作系統(tǒng)[M].邵貝貝,譯.北京:中國(guó)電力出版社,2001:56-67.
[3]田志鑫,張雷,趙明揚(yáng).在51單片機(jī)上移植μC/OS-Ⅱ關(guān)鍵問(wèn)題的解決[J].微計(jì)算機(jī)信息,2007,23(12):45-48.
[4]任哲,潘樹林,房紅征.嵌入式操作系統(tǒng)基礎(chǔ)UCOS-Ⅱ和Linux[M].北京:北京航空航天大學(xué)出版社,2001:65-68.
[5]姚念龍,尹航,姜久春.μC/OS-Ⅱ在MC9S12A64上的移植和應(yīng)用[J].微計(jì)算機(jī)信息,2006,22(8):12-15.
[6]孟慶峰.實(shí)時(shí)內(nèi)核μC/OS-Ⅱ在S3C44B0X上移植的研究與實(shí)現(xiàn)[J].安徽電子信息職業(yè)技術(shù)學(xué)院學(xué)報(bào),2008,7(7):34-36.