(1)實(shí)時(shí)系統(tǒng)和前/后臺(tái)系統(tǒng);
前/后臺(tái)系統(tǒng):一個(gè)大循環(huán),循環(huán)查詢各種標(biāo)志位。如果標(biāo)志位置位,就執(zhí)行相應(yīng)的服務(wù)程序。標(biāo)志位就是標(biāo)志事件的發(fā)生,事件響應(yīng)延時(shí)處于不可預(yù)測(cè)狀態(tài)。最壞的情況是循環(huán)中所有其他的事件服務(wù)程序執(zhí)行完,才響應(yīng)當(dāng)前事件。中斷服務(wù)雖然能即時(shí)/優(yōu)先響應(yīng),但是它們和主循環(huán)的通訊,也是通過(guò)置主循環(huán)中相應(yīng)的標(biāo)志位來(lái)完成的。
實(shí)時(shí)系統(tǒng)(uCOS):整個(gè)程序分成一個(gè)個(gè)看起來(lái)好象是并行的任務(wù),每個(gè)任務(wù)都在等待事件的發(fā)生。除了最低優(yōu)先級(jí)任務(wù)(在uCOS中是IDLE任務(wù))是死循環(huán)以外,其他的任務(wù)都不能死循環(huán),只能在驅(qū)動(dòng)事件驅(qū)動(dòng)下工作。任何驅(qū)動(dòng)事件的產(chǎn)生,都使優(yōu)先級(jí)最高的就緒任務(wù)運(yùn)行。任務(wù)和任務(wù)/任務(wù)和中斷的通訊,是通過(guò)相應(yīng)事件驅(qū)動(dòng)來(lái)完成的。
驅(qū)動(dòng)事件:
不論是什么系統(tǒng),CPU不可能一直在工作。CPU的工作是在各種驅(qū)動(dòng)事件的驅(qū)動(dòng)下工作的。CPU在完成一次驅(qū)動(dòng)事件事件服務(wù)程序以后,進(jìn)入IDLE模式等待新的驅(qū)動(dòng)事件的發(fā)生。包括實(shí)時(shí)系統(tǒng)和前/后臺(tái)系統(tǒng)都是在驅(qū)動(dòng)事件的驅(qū)動(dòng)下運(yùn)行的。
按照uCOS中的觀點(diǎn),驅(qū)動(dòng)事件分為三類:
1、事件 (Event)。包括信號(hào)量(Semaphores)、事件標(biāo)志組(Flag)、郵箱(Message Box)、郵箱隊(duì)列(Message Queue)。
2、時(shí)間(Time Tick)。包括時(shí)間延時(shí)和事件超時(shí)。
3、中斷(Interrupt)??梢园l(fā)出各種event。
由于第1種事件,通常都是在第2、3種狀態(tài)下發(fā)出的,所以其實(shí)事件的驅(qū)動(dòng)只有兩種:時(shí)間(定時(shí))和中斷(各種異步中斷)。
時(shí)間實(shí)際上也是中斷的一種,可以說(shuō)程序的驅(qū)動(dòng)事件只有一種,就是:中斷。
前/后臺(tái)系統(tǒng)中還有一種驅(qū)動(dòng)事件的產(chǎn)生,在主循環(huán)中不斷的查詢。有別與一般的定時(shí)查詢,這種查詢是為了將事件的響應(yīng)時(shí)間降到最低,也可以將其歸納于定時(shí)(時(shí)間)事件。
(2)uCOS C51移植的準(zhǔn)備工作;
2004年8月份,我在書城買了一本《uCOS-Ⅱ 第2版》,準(zhǔn)備學(xué)習(xí)RTOS。因?yàn)橐郧皼]有玩過(guò)RTOS,在工作之余斷斷續(xù)續(xù)的看了3、4章。一直到12月初的時(shí)候,公司要重新設(shè)計(jì)一個(gè)項(xiàng)目,恰好要把uCOS移植到c51上。我的RTOS學(xué)習(xí)才正式開始。
因?yàn)閷?duì)OS向往以久,我并不想在網(wǎng)上Down一個(gè)現(xiàn)成的移植OS程序,做一個(gè)OS的應(yīng)用者。揭開OS的神秘面紗,了解OS的內(nèi)部運(yùn)行機(jī)制,這才是我想要做的。本文的主要目的是討論uCOS的移植,希望對(duì)即將進(jìn)行uCOS c51移植的兄弟有些幫助。對(duì)于OS的內(nèi)部運(yùn)行機(jī)制,由于東西比較多,在這里不想太展開。如果以后有時(shí)間,也想寫一篇文章來(lái)討論討論。
最開始,我的計(jì)劃就是看書,看《uCOS-Ⅱ 第2版》。看完這本幾百頁(yè)的大本本,花了我2個(gè)半星期。因?yàn)槭枪ぷ餍枰?,我才可以這樣心安理得的在那里看呀看書^_^,辛苦呀?。在這期間,為了自己的思想不受別人的影響,我堅(jiān)決沒有從網(wǎng)上下任何uCOS的資料,我手頭的資料就是uCOS-Ⅱ的書和附帶光盤,這些就是最權(quán)威的資料了。在看書的時(shí)候,我都堅(jiān)持做筆記,把每天的重點(diǎn),明白的東西和心中的疑問(wèn)都隨時(shí)記錄下來(lái)。對(duì)付這種大本本,前后的知識(shí)又相互關(guān)聯(lián),光靠我們的大腦是搞不定啊。
弄懂了uCOS的內(nèi)核,下一本書應(yīng)該是《單片機(jī)高級(jí)語(yǔ)言C51Windows環(huán)境編程與應(yīng)用》。對(duì)于Keil C我還是很熟的,還是花了2、3天來(lái)復(fù)習(xí)。這里的重點(diǎn)是C51對(duì)匯編的轉(zhuǎn)換結(jié)構(gòu),例于數(shù)據(jù)/系統(tǒng)堆棧的使用,C&Asm混合編程。我想對(duì)于任何CPU的uCOS移植,C語(yǔ)言的實(shí)現(xiàn)機(jī)制,你都是要了解的。這里也是要花大把時(shí)間的。
《uCOS-Ⅱ 第2版》和《單片機(jī)高級(jí)語(yǔ)言C51Windows環(huán)境編程與應(yīng)用》這兩本書網(wǎng)上都可以下電子檔的,我這里也有(大家需要可以來(lái)信索?。?。
uCOS和C51的書都看完了。我就下載了一堆uCOS的C51移植資料。其中的源程序有很多個(gè)版本的,不過(guò)詳細(xì)的移植文檔只有一個(gè)版本:巨龍一位大蝦的“uCOS C51移植心得”,相信很多人都看過(guò)。這些資料的作者都是我移植過(guò)程中的老師,有了這些資料,我才能把心中的朦朧想法變成源程序。但是我也發(fā)現(xiàn)這些資料中大多都有一些錯(cuò)誤和遺漏,當(dāng)然這是難免的。這也正是驅(qū)使我寫這篇文章的原因,希望在前輩的基礎(chǔ)上有所進(jìn)步。歡迎大家來(lái)批評(píng)!
真正的源代碼移植,我花了大概一個(gè)星期時(shí)間。
(3)uCOS C51的移植概況;
1、工具:
uCOS 2.52版;
Keil C V6.23a。
2、uCOS V2.52的文件結(jié)構(gòu)與移植所需要的修改:
A、與處理器無(wú)關(guān)的文件:
OS_CORE.C
OS_FLAG.C
OS_MBOX.C
OS_MEM.C
OS_MUTEX.C
OS_Q.C
OS_SEM.C
OS_TASK.C
OS_TIME.C
uCOS_II.C
uCOS_II.H
這些文件在c51的移植過(guò)程中,只需要給函數(shù)加上重入屬性即可。
B、與應(yīng)用相關(guān)的文件:
INCLUDES.H: 包含C51的標(biāo)準(zhǔn)庫(kù)頭文件;對(duì)”pdata”等c51關(guān)鍵字的重定義
OS_CFG.H: “OS_TICKS_PER_SEC”、“ OS_FLAGS”注意可能需要修改。
C、與處理器相關(guān)的文件:
OS_CPU.H: 數(shù)據(jù)類型、關(guān)中斷方法、任務(wù)堆棧方向、任務(wù)切換的宏定義都需要修改。
OS_CPU_A.ASM: OSTickISR()、OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()這幾個(gè)函數(shù)的編寫,是整個(gè)移植的關(guān)鍵。
OS_CPU_C.C:OSTaskStkInit()函數(shù)的編寫。
(4)uCOS C51具體的移植過(guò)程;
1、C51的堆棧結(jié)構(gòu);
這是整個(gè)移植過(guò)程中的重中之重,所以特別詳細(xì)介紹。
A、 系統(tǒng)堆棧;
c51中,系統(tǒng)堆棧的棧底地址是“?STACK”,棧頂指針就是“SP”拉,棧的生長(zhǎng)方向是向上的,棧空間分配在51的內(nèi)部RAM(idata)中。“?STACK”分配在所有內(nèi)部RAM數(shù)據(jù)段的最后面,所以系統(tǒng)堆棧的范圍是從?STACK到內(nèi)部RAM的最高位(0x80或者0xFF)。
B、 數(shù)據(jù)堆棧;
c51中,由于我們使用OS,采用的LARGE編譯模式,所以數(shù)據(jù)堆棧的指針是“?C_XBP”, 棧的生長(zhǎng)方向是向下的,??臻g分配在51的外部RAM(xdata)中。
C、 C51中斷中堆棧的保護(hù);
研究中斷中堆棧的保護(hù)的意義在于,因?yàn)閡COS中的任務(wù)切換,本身就是模擬一次中斷的發(fā)生:保護(hù)Task1的CPU寄存器,SP切換到Task2的堆棧,彈出Task2的CPU寄存器。用C51寫中斷函數(shù)的時(shí)候,編譯器會(huì)自動(dòng)保護(hù)CPU的寄存器,所以中斷返回時(shí)任務(wù)調(diào)度OSIntCtxSw(),就不用重新保護(hù)寄存器。
C51中斷中調(diào)用函數(shù)可以分為四種情況(中斷函數(shù)本身不設(shè)為reentrant):
一、 沒有函數(shù)調(diào)用;
二、 調(diào)用非reentrant函數(shù),函數(shù)中沒有嵌套調(diào)用其他函數(shù);
三、 調(diào)用非reentrant函數(shù),函數(shù)中嵌套調(diào)用其他函數(shù);
四、 調(diào)用reentrant函數(shù)。
t0_isr:
PUSH ACC
PUSH B
PUSH DPH
PUSH DPL
PUSH PSW
MOV PSW,#00H
PUSH AR0
PUSH AR1
PUSH AR2
PUSH AR3
PUSH AR4
PUSH AR5
PUSH AR6
PUSH AR7
用戶代碼
POP AR7
POP AR6
POP AR5
POP AR4
POP AR3
POP AR2
POP AR1
POP AR0
POP PSW
POP DPL
POP DPH
POP B
POP ACC
RETI
因?yàn)閡COS中所有的函數(shù)都必須是重入函數(shù),因此我們只需要研究第四種情況下的堆棧保護(hù),對(duì)于其他情況有興趣可以在c51中看看。(注意:可能因?yàn)閏51編譯器的版本不同,上述壓棧的順序可能不同。)
2、uCOS C51任務(wù)切換時(shí)的堆棧操作;
每個(gè)任務(wù)都有一個(gè)獨(dú)立的數(shù)據(jù)堆棧,系統(tǒng)堆棧是公用空間。
保護(hù)Task1的CPU寄存器: 首先將CPU寄存器按上例壓進(jìn)Task1系統(tǒng)堆棧,再將整個(gè)Task1系統(tǒng)堆棧壓進(jìn)Task1數(shù)據(jù)堆棧;
SP切換:?C_XBP = Task2 的數(shù)據(jù)堆棧棧頂?shù)刂贰?br>彈出Task2的CPU寄存器:從Task2的數(shù)據(jù)堆棧重新恢復(fù)整個(gè)系統(tǒng)堆棧,然后再?gòu)腡ask2系統(tǒng)堆棧中恢復(fù)CPU寄存器值。
實(shí)現(xiàn)的方法有很多種,只要遵循uCOS任務(wù)切換的原理就可以了。
3、INCLUDES.H的移植;
4、OS_CPU.H的移植;
5、OS_CPU_A.ASM的移植;
6、OS_CPU_C.C的移植;
7、其他的移植;