<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          uCOS51移植心得[社區(qū)]

          作者: 時(shí)間:2004-12-10 來(lái)源:網(wǎng)絡(luò) 收藏
          前一段時(shí)間,我參與了一個(gè)SNMP網(wǎng)管板的項(xiàng)目,我負(fù)責(zé)硬件設(shè)計(jì)和單板軟件開(kāi)發(fā)。該板的硬件由MCS51+RTL8019AS組成,有64K FLASH 和64K SRAM。軟件部分有操作系統(tǒng)和TCPIP協(xié)議棧。硬件比較簡(jiǎn)單,用了一個(gè)月就搞定了,協(xié)議棧我參考了老古開(kāi)發(fā)板的部分程序又上網(wǎng)找了SNMP源代碼也很快完成了,但是測(cè)試時(shí)發(fā)現(xiàn)當(dāng)使用較低時(shí)鐘頻率的CPU時(shí)(為了降低成本),由于ASN.1編解碼部分過(guò)于龐大,而我的程序又是一個(gè)大循環(huán),AGENT的響應(yīng)速度受到嚴(yán)重影響,用戶(hù)界面也反應(yīng)遲鈍。更壞的消息是公司為了適應(yīng)市場(chǎng)需求,還要在上面跑PPP和HTTP。那樣的話(huà),我就得用40MHz的AT89C51RD2或者人為的把程序斷成幾部分然后用狀態(tài)機(jī)的方法在運(yùn)行時(shí)再把它們連接起來(lái)。不過(guò),我不想增加成本,也不想把程序搞亂,迫不得已,只好使用操作系統(tǒng)。

          說(shuō)實(shí)在的,一開(kāi)始我也不是很有把握,一來(lái)我不清楚51的FLASH是否裝得下這么多代碼,二來(lái)我只做過(guò)OS應(yīng)用開(kāi)發(fā),對(duì)于它的想都不敢想。不過(guò),我在BBS上搜索了一陣兒后還是有了一些頭緒。我找到了幾個(gè)OS的源代碼(我喜歡用現(xiàn)成的),按照代碼大小、實(shí)時(shí)性、使用人數(shù)、眾人口碑等標(biāo)準(zhǔn),最后選定了uCOS2。我感覺(jué)它的實(shí)時(shí)性有保障,延時(shí)可預(yù)測(cè),代碼據(jù)說(shuō)可小到2K,網(wǎng)上討論這個(gè)話(huà)題的人也比較多,而且它的網(wǎng)站上有針對(duì)KEIL C51的實(shí)例。

          經(jīng)過(guò)一番查找,我得到了5個(gè)版本。其中3個(gè)是用KEIL編譯的。本來(lái)我想直接把OS代碼嵌到應(yīng)用程序中,但后來(lái)發(fā)現(xiàn)沒(méi)有一個(gè)可以直接使用。有的無(wú)法用KEIL直接編譯,有的需要修改DLL在軟件仿真下使用。而我需要的是能在串口輸入輸出,不需要修改任何無(wú)關(guān)軟件,能在軟件仿真和硬件上運(yùn)行的實(shí)時(shí)多任務(wù)操作系統(tǒng)。沒(méi)有辦法,我只好硬著頭皮去改編。

          我分析了自己的劣勢(shì):1。KEIL剛開(kāi)始使用,不太熟悉;2。混合編程以前從沒(méi)有作過(guò);3。時(shí)間緊迫,要在1個(gè)月內(nèi)搞定。而我的優(yōu)勢(shì)就是有5個(gè)實(shí)例可供參考,可以上網(wǎng)查資料。一開(kāi)始,我用“堆棧”、“混合編程”、“匯編”、“ucos”等關(guān)鍵字在C51BBS和老古論壇上檢索相關(guān)信息并逐條閱讀,讀過(guò)之后,頭腦中的思路逐漸清晰了。我了解到在KEIL的HLP目錄下有A51.PDF和C51.PDF非常全面的介紹了匯編和C51,是KEIL的權(quán)威用戶(hù)手冊(cè);SP初始化、內(nèi)存清0等操作在STARTUP.A51文件中實(shí)現(xiàn),用戶(hù)可以改寫(xiě)它;KEIL的變量,子程序等的分配信息可以在.M51文件里查到;KEIL自己的論壇里有很多疑難問(wèn)題的解答……通過(guò)閱讀并經(jīng)過(guò)思考,解決了堆棧起點(diǎn)、堆??臻g大小的設(shè)定等關(guān)鍵問(wèn)題。論壇里的問(wèn)題有些是我沒(méi)有想到的,這使我發(fā)現(xiàn)了自己的疏漏。

          在網(wǎng)上獲得大量信息后,我開(kāi)始閱讀《uCOSII》中文版,一共讀了3遍。第一遍是瀏覽,了解到uCOSII包括任務(wù)調(diào)度、時(shí)間管理、內(nèi)存管理、資源管理(信號(hào)量、郵箱、消息隊(duì)列)四大部分,沒(méi)有文件系統(tǒng)、網(wǎng)絡(luò)接口、輸入輸出界面。它的移植只與4個(gè)文件相關(guān):匯編文件(OS_CPU_A.ASM)、處理器相關(guān)C文件(OS_CPU.H、OS_CPU_C.C)和配置文件(OS_CFG.H)。有64個(gè)優(yōu)先級(jí),系統(tǒng)占用8個(gè),用戶(hù)可創(chuàng)建56個(gè)任務(wù),不支持時(shí)間片輪轉(zhuǎn)。第二遍主要是把整個(gè)工作過(guò)程在頭腦里過(guò)了一下,不懂地方有針對(duì)性的查書(shū),重點(diǎn)是思考工作原理和流程。我發(fā)現(xiàn)其實(shí)它的思路挺簡(jiǎn)單的。就是 “近似地每時(shí)每刻總是讓優(yōu)先級(jí)最高的任務(wù)處于運(yùn)行狀態(tài)” 。為了保證這一點(diǎn),它在調(diào)用系統(tǒng)API函數(shù)、中斷結(jié)束、定時(shí)中斷結(jié)束時(shí)總是執(zhí)行調(diào)度算法。原作者通過(guò)事先計(jì)算好數(shù)據(jù),簡(jiǎn)化了運(yùn)算量,通過(guò)精心設(shè)計(jì)就緒表結(jié)構(gòu),使得延時(shí)可預(yù)知。任務(wù)的切換是通過(guò)模擬一次中斷實(shí)現(xiàn)的。第三遍重點(diǎn)看了移植部分的內(nèi)容。對(duì)照實(shí)例,研究了代碼的具體實(shí)現(xiàn)方法。

          前期準(zhǔn)備用了20幾天,真正編寫(xiě)代碼只用了1.5天,調(diào)試用了2天。具體過(guò)程如下:

          (1)拷貝書(shū)后附贈(zèng)光盤(pán)sourcecode目錄下的內(nèi)容到C:YY下,刪除不必要的文件和EX1L.C,只剩下p187(《uCOSII》)上列出的文件。

          (2)改寫(xiě)最簡(jiǎn)單的OS_CPU.H
          數(shù)據(jù)類(lèi)型的設(shè)定見(jiàn)C51.PDF第176頁(yè)。注意BOOLEAN要定義成unsigned char 類(lèi)型,因?yàn)閎it類(lèi)型為C51特有,不能用在結(jié)構(gòu)體里。

          EA=0關(guān)中斷;EA=1開(kāi)中斷。這樣定義即減少了程序行數(shù),又避免了退出臨界區(qū)后關(guān)中斷造成的死機(jī)。

          MCU-51堆棧從下往上增長(zhǎng)(1=向下,0=向上),OS_STK_GROWTH定義為0
          #define OS_TASK_SW() OSCtxSw() 因?yàn)镸CU-51沒(méi)有軟中斷指令,所以用程序調(diào)用代替。兩者的堆棧格式相同,RETI指令復(fù)位中斷系統(tǒng),RET則沒(méi)有。實(shí)踐表明,對(duì)于MCU-51,用子程序調(diào)用入棧,用中斷返回指令RETI出棧是沒(méi)有問(wèn)題的,反之中斷入棧RET出棧則不行??傊瑢?duì)于入棧,子程序調(diào)用與中斷調(diào)用效果是一樣的,可以混用。在沒(méi)有中斷發(fā)生的情況下復(fù)位中斷系統(tǒng)也不會(huì)影響系統(tǒng)正常運(yùn)行。詳見(jiàn)《uC/OS-II》第八章193頁(yè)第12行

          (3)改寫(xiě)OS_CPU_C.C

          我設(shè)計(jì)的堆棧結(jié)構(gòu)如下圖所示:
          *********************************************************************************
          *
          * ----------
          * |OSTCBCur|
          * ----------
          * |
          * | ----------------------- ----------
          * ---->|OSTCBCur->OSTCBStkPtr| SP---->| |
          * ----------------------- ----------
          * | | |
          * | ---------- - ----------
          * | | | | | . |
          * | ---------- | | . |
          * | | | | | . |
          * | ---------- | ----------
          * | | . |長(zhǎng)度 | | +1
          * | | . | | ----------
          * | | . | | OSStack---->| | 0
          * | ---------- | ----------
          * | | | | OSStkStart---->| 不關(guān)心 | -1 低地址
          * | ---------- - ----------
          * -------->| 長(zhǎng)度 | 低地址 系統(tǒng)堆棧
          * ----------
          * 用戶(hù)堆棧 長(zhǎng)度=SP-OSStkStart
          *********************************************************************************

          TCB結(jié)構(gòu)體中OSTCBStkPtr總是指向用戶(hù)堆棧最低地址,該地址空間內(nèi)存放用戶(hù)堆棧長(zhǎng)度,其上空間存放系統(tǒng)堆棧映像,即:用戶(hù)堆??臻g大小=系統(tǒng)堆??臻g大小+1。

          SP總是先加1再存數(shù)據(jù),因此,SP初始時(shí)指向系統(tǒng)堆棧起始地址(OSStack)減1處(OSStkStart)。很明顯系統(tǒng)堆棧存儲(chǔ)空間大小=SP-OSStkStart。

          任務(wù)切換時(shí),先保存當(dāng)前任務(wù)堆棧內(nèi)容。方法是:用SP-OSStkStart得出保存字節(jié)數(shù),將其寫(xiě)入用戶(hù)堆棧最低地址內(nèi),以用戶(hù)堆棧最低地址為起址,以O(shè)SStkStart為系統(tǒng)堆棧起址,由系統(tǒng)棧向用戶(hù)棧拷貝數(shù)據(jù),循環(huán)SP-OSStkStart次,每次拷貝前先將各自棧指針增1。

          其次,恢復(fù)最高優(yōu)先級(jí)任務(wù)系統(tǒng)堆棧。方法是:獲得最高優(yōu)先級(jí)任務(wù)用戶(hù)堆棧最低地址,從中取出“長(zhǎng)度”,以最高優(yōu)先級(jí)任務(wù)用戶(hù)堆棧最低地址為起址,以O(shè)SStkStart為系統(tǒng)堆棧起址,由用戶(hù)棧向系統(tǒng)??截悢?shù)據(jù),循環(huán)“長(zhǎng)度”數(shù)值指示的次數(shù),每次拷貝前先將各自棧指針增1。

          用戶(hù)堆棧初始化時(shí)從下向上依次保存:用戶(hù)堆棧長(zhǎng)度(15),PCL,PCH,PSW,ACC,B,DPL,DPH,R0,R1,R2,R3,R4,R5,R6,R7。不保存SP,任務(wù)切換時(shí)根據(jù)用戶(hù)堆棧長(zhǎng)度計(jì)算得出。

          OSTaskStkInit函數(shù)總是返回用戶(hù)棧最低地址。

          操作系統(tǒng)tick時(shí)鐘我使用了51單片機(jī)的T0定時(shí)器,它的初始化代碼用C寫(xiě)在了本文件中。

          最后還有幾點(diǎn)必須注意的事項(xiàng)。本來(lái)原則上我們不用修改與處理器無(wú)關(guān)的代碼,但是由于KEIL編譯器的特殊性,這些代碼仍要多處改動(dòng)。因?yàn)镵EIL缺省情況下編譯的代碼不可重入,而多任務(wù)系統(tǒng)要求并發(fā)操作導(dǎo)致重入,所以要在每個(gè)C函數(shù)及其聲明后標(biāo)注reentrant關(guān)鍵字。另外,“pdata”、“data”在uCOS中用做一些函數(shù)的形參,但它同時(shí)又是KEIL的關(guān)鍵字,會(huì)導(dǎo)致編譯錯(cuò)誤,我通過(guò)把“pdata”改成“ppdata”,“data”改成“ddata”解決了此問(wèn)題。OSTCBCur、OSTCBHighRdy、OSRunning、OSPrioCur、OSPrioHighRdy這幾個(gè)變量在匯編程序中用到了,為了使用Ri訪(fǎng)問(wèn)而不用DPTR,應(yīng)該用KEIL擴(kuò)展關(guān)鍵字IDATA將它們定義在內(nèi)部RAM中。

          (4)重寫(xiě)OS_CPU_A.ASM
          A51宏匯編的大致結(jié)構(gòu)如下:
          NAME 模塊名 ;與文件名無(wú)關(guān)
          ;定義重定位段 必須按照C51格式定義,匯編遵守C51規(guī)范。段名格式為:?PR?函數(shù)名?模塊名
          ;聲明引用全局變量和外部子程序 注意關(guān)鍵字為“EXTRN”沒(méi)有‘E’
          全局變量名直接引用
          無(wú)參數(shù)/無(wú)寄存器參數(shù)函數(shù) FUNC
          帶寄存器參數(shù)函數(shù) _FUNC
          重入函數(shù) _?FUNC
          ;分配堆棧空間
          只關(guān)心大小,堆棧起點(diǎn)由keil決定,通過(guò)標(biāo)號(hào)可以獲得keil分配的SP起點(diǎn)。切莫自己分配堆棧起點(diǎn),只要用DS通知KEIL預(yù)留堆棧空間即可。
          ?STACK段名與STARTUP.A51中的段名相同,這意味著KEIL在LINK時(shí)將把兩個(gè)同名段拼在一起,我預(yù)留了40H個(gè)字節(jié),STARTUP.A51預(yù)留了1個(gè)字節(jié),LINK完成后堆棧段總長(zhǎng)為41H。查看yy.m51知KEIL將堆棧起點(diǎn)定在21H,長(zhǎng)度41H,處于內(nèi)部RAM中。
          ;定義宏
          宏名 MACRO 實(shí)體 ENDM
          ;子程序
          OSStartHighRdy
          OSCtxSw
          OSIntCtxSw
          OSTickISR
          SerialISR
          END ;聲明匯編源文件結(jié)束

          一般指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù) 詳見(jiàn)C51.PDF第178頁(yè)
          低位地址存高8位值,高位地址存低8位值。例如0x1234,基址+0:0x12 基址+1:0x34

          (5)移植串口驅(qū)動(dòng)程序

          在此之前我寫(xiě)過(guò)基于中斷的串口驅(qū)動(dòng)程序,包括打印字節(jié)/字/長(zhǎng)字/字符串,讀串口,初始化串口/緩沖區(qū)。把它改成重入函數(shù)即可直接使用。

          系統(tǒng)提供的顯示函數(shù)是并發(fā)的,它不是直接顯示到串口,而是先輸出到顯存,用戶(hù)不必?fù)?dān)心IO慢速操作影響程序運(yùn)行。串口輸入也采用了同樣的技術(shù),他使得用戶(hù)在CPU忙于處理其他任務(wù)時(shí)照樣可以盲打輸入命令。

          (6)編寫(xiě)測(cè)試程序Demo(YY.C)

          Demo程序創(chuàng)建了3個(gè)任務(wù)A、B、C優(yōu)先級(jí)分別為2、3、4,A每秒顯示一次,B每3秒顯示一次,C每6秒顯示一次。從顯示結(jié)果看,顯示3個(gè)A后顯示1個(gè)B,顯示6個(gè)A和2個(gè)B后顯示1個(gè)C,結(jié)果顯然正確。
          顯示結(jié)果如下:
          AAAAAA111111 is active
          AAAAAA111111 is active
          AAAAAA111111 is active
          BBBBBB333333 is active
          AAAAAA111111 is active
          AAAAAA111111 is active
          AAAAAA111111 is active
          BBBBBB333333 is active
          CCCCCC666666 is active
          AAAAAA111111 is active
          AAAAAA111111 is active
          AAAAAA111111 is active
          BBBBBB333333 is active
          AAAAAA111111 is active
          AAAAAA111111 is active
          AAAAAA111111 is active
          BBBBBB333333 is active
          CCCCCC666666 is active
          Demo程序經(jīng)Keil701編譯后,代碼量為7-8K,可直接在KeilC51上仿真運(yùn)行。
          編譯時(shí)要將OS_CPU_C.C、UCOS_II.C、OS_CPU_A.ASM、YY.C加入項(xiàng)目

          以上是我這次移植的一些,寫(xiě)出來(lái)只是讓準(zhǔn)備在51上運(yùn)行操作系統(tǒng)的同行們少走彎路并增強(qiáng)使用信心。我強(qiáng)烈推薦大家在自己的51系統(tǒng)中使用uCOS這個(gè)簡(jiǎn)單實(shí)用的自己的操作系統(tǒng)。它的大小應(yīng)該不是問(wèn)題,性能上的提高卻是顯著的。但愿此文能對(duì)朋友們有所幫助,錯(cuò)誤在所難免,希望各位大蝦指正,諸位高手們見(jiàn)笑了!

          注:全部源碼可來(lái)信索要(asdjf@163.com),以下僅為關(guān)鍵代碼部分。

          文件名 : OS_CPU_A.ASM

          $NOMOD51
          EA BIT 0A8H.7
          SP DATA 081H
          B DATA 0F0H
          ACC DATA 0E0H
          DPH DATA 083H
          DPL DATA 082H
          PSW DATA 0D0H
          TR0 BIT 088H.4
          TH0 DATA 08CH
          TL0 DATA 08AH

          NAME OS_CPU_A ;模塊名

          ;定義重定位段
          ?PR?OSStartHighRdy?OS_CPU_A SEGMENT CODE
          ?PR?OSCtxSw?OS_CPU_A SEGMENT CODE
          ?PR?OSIntCtxSw?OS_CPU_A SEGMENT CODE
          ?PR?OSTickISR?OS_CPU_A SEGMENT CODE

          ?PR?_?serial?OS_CPU_A SEGMENT CODE

          ;聲明引用全局變量和外部子程序
          EXTRN IDATA (OSTCBCur)
          EXTRN IDATA (OSTCBHighRdy)
          EXTRN IDATA (OSRunning)
          EXTRN IDATA (OSPrioCur)
          EXTRN IDATA (OSPrioHighRdy)

          EXTRN CODE (_?OSTaskSwHook)
          EXTRN CODE (_?serial)
          EXTRN CODE (_?OSIntEnter)
          EXTRN CODE (_?OSIntExit)
          EXTRN CODE (_?OSTimeTick)

          ;對(duì)外聲明4個(gè)不可重入函數(shù)
          PUBLIC OSStartHighRdy
          PUBLIC OSCtxSw
          PUBLIC OSIntCtxSw
          PUBLIC OSTickISR

          ;PUBLIC SerialISR

          ;分配堆棧空間。只關(guān)心大小,堆棧起點(diǎn)由keil決定,通過(guò)標(biāo)號(hào)可以獲得keil分配的SP起點(diǎn)。
          ?STACK SEGMENT IDATA
          RSEG ?STACK
          OSStack:
          DS 40H
          OSStkStart IDATA OSStack-1

          ;定義壓棧出棧宏
          PUSHALL MACRO
          PUSH PSW
          PUSH ACC
          PUSH B
          PUSH DPL
          PUSH DPH
          MOV A,R0 ;R0-R7入棧
          PUSH ACC
          MOV A,R1
          PUSH ACC
          MOV A,R2
          PUSH ACC
          MOV A,R3
          PUSH ACC
          MOV A,R4
          PUSH ACC
          MOV A,R5
          PUSH ACC
          MOV A,R6
          PUSH ACC
          MOV A,R7
          PUSH ACC
          ;PUSH SP ;不必保存SP,任務(wù)切換時(shí)由相應(yīng)程序調(diào)整
          ENDM

          POPALL MACRO
          ;POP ACC ;不必保存SP,任務(wù)切換時(shí)由相應(yīng)程序調(diào)整
          POP ACC ;R0-R7出棧
          MOV R7,A
          POP ACC
          MOV R6,A
          POP ACC
          MOV R5,A
          POP ACC
          MOV R4,A
          POP ACC
          MOV R3,A
          POP ACC
          MOV R2,A
          POP ACC
          MOV R1,A
          POP ACC
          MOV R0,A
          POP DPH
          POP DPL
          POP B
          POP ACC
          POP PSW
          ENDM

          ;子程序
          ;-------------------------------------------------------------------------
          RSEG ?PR?OSStartHighRdy?OS_CPU_A
          OSStartHighRdy:
          USING 0 ;上電后51自動(dòng)關(guān)中斷,此處不必用CLR EA指令,因?yàn)榈酱颂庍€未開(kāi)中斷,本程序退出后,開(kāi)中斷。
          LCALL _?OSTaskSwHook

          OSCtxSw_in:

          ;OSTCBCur ===> DPTR 獲得當(dāng)前TCB指針,詳見(jiàn)C51.PDF第178頁(yè)
          MOV R0,#LOW (OSTCBCur) ;獲得OSTCBCur指針低地址,指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù)
          INC R0
          MOV DPH,@R0 ;全局變量OSTCBCur在IDATA中
          INC R0
          MOV DPL,@R0

          ;OSTCBCur->OSTCBStkPtr ===> DPTR 獲得用戶(hù)堆棧指針
          INC DPTR ;指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù)
          MOVX A,@DPTR ;.OSTCBStkPtr是void指針
          MOV R0,A
          INC DPTR
          MOVX A,@DPTR
          MOV R1,A
          MOV DPH,R0
          MOV DPL,R1

          ;*UserStkPtr ===> R5 用戶(hù)堆棧起始地址內(nèi)容(即用戶(hù)堆棧長(zhǎng)度放在此處) 詳見(jiàn)文檔說(shuō)明 指針用法詳見(jiàn)C51.PDF第169頁(yè)
          MOVX A,@DPTR ;用戶(hù)堆棧中是unsigned char類(lèi)型數(shù)據(jù)
          MOV R5,A ;R5=用戶(hù)堆棧長(zhǎng)度

          ;恢復(fù)現(xiàn)場(chǎng)堆棧內(nèi)容
          MOV R0,#OSStkStart

          restore_stack:

          INC DPTR
          INC R0
          MOVX A,@DPTR
          MOV @R0,A
          DJNZ R5,restore_stack

          ;恢復(fù)堆棧指針SP
          MOV SP,R0

          ;OSRunning=TRUE
          MOV R0,#LOW (OSRunning)
          MOV @R0,#01

          POPALL
          SETB EA ;開(kāi)中斷
          RETI
          ;-------------------------------------------------------------------------
          RSEG ?PR?OSCtxSw?OS_CPU_A
          OSCtxSw:
          PUSHALL

          OSIntCtxSw_in:

          ;獲得堆棧長(zhǎng)度和起址
          MOV A,SP
          CLR C
          SUBB A,#OSStkStart
          MOV R5,A ;獲得堆棧長(zhǎng)度

          ;OSTCBCur ===> DPTR 獲得當(dāng)前TCB指針,詳見(jiàn)C51.PDF第178頁(yè)
          MOV R0,#LOW (OSTCBCur) ;獲得OSTCBCur指針低地址,指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù)
          INC R0
          MOV DPH,@R0 ;全局變量OSTCBCur在IDATA中
          INC R0
          MOV DPL,@R0

          ;OSTCBCur->OSTCBStkPtr ===> DPTR 獲得用戶(hù)堆棧指針
          INC DPTR ;指針占3字節(jié)。+0類(lèi)型+1高8位數(shù)據(jù)+2低8位數(shù)據(jù)
          MOVX A,@DPTR ;.OSTCBStkPtr是void指針
          MOV R0,A
          INC DPTR
          MOVX A,@DPTR
          MOV R1,A
          MOV DPH,R0
          MOV DPL,R1

          ;保存堆棧長(zhǎng)度
          MOV A,R5
          MOVX @DPTR,A

          MOV R0,#OSStkStart ;獲得堆棧起址
          save_stack:

          INC DPTR
          INC R0
          MOV A,@R0
          MOVX @DPTR,A
          DJNZ R5,save_stack

          ;調(diào)用用戶(hù)程序
          LCALL _?OSTaskSwHook

          ;OSTCBCur = OSTCBHighRdy
          MOV R0,#OSTCBCur
          MOV R1,#OSTCBHighRdy
          MOV A,@R1
          MOV @R0,A
          INC R0
          INC R1
          MOV A,@R1
          MOV @R0,A
          INC R0
          INC R1
          MOV A,@R1
          MOV @R0,A

          ;OSPrioCur = OSPrioHighRdy 使用這兩個(gè)變量主要目的是為了使指針比較變?yōu)樽止?jié)比較,以便節(jié)省時(shí)間。
          MOV R0,#OSPrioCur
          MOV R1,#OSPrioHighRdy
          MOV A,@R1
          MOV @R0,A

          LJMP OSCtxSw_in
          ;-------------------------------------------------------------------------
          RSEG ?PR?OSIntCtxSw?OS_CPU_A

          OSIntCtxSw:

          ;調(diào)整SP指針去掉在調(diào)用OSIntExit(),OSIntCtxSw()過(guò)程中壓入堆棧的多余內(nèi)容
          ;SP=SP-4

          MOV A,SP
          CLR C
          SUBB A,#4
          MOV SP,A

          LJMP OSIntCtxSw_in
          ;-------------------------------------------------------------------------
          CSEG AT 000BH ;OSTickISR
          LJMP OSTickISR ;使用定時(shí)器0
          RSEG ?PR?OSTickISR?OS_CPU_A

          OSTickISR:

          USING 0
          PUSHALL

          CLR TR0
          MOV TH0,#70H ;定義Tick=50次/秒(即0.02秒/次)
          MOV TL0,#00H ;OS_CPU_C.C 和 OS_TICKS_PER_SEC
          SETB TR0

          LCALL _?OSIntEnter
          LCALL _?OSTimeTick
          LCALL _?OSIntExit
          POPALL
          RETI
          ;-------------------------------------------------------------------------
          CSEG AT 0023H ;串口中斷
          LJMP SerialISR ;工作于系統(tǒng)態(tài),無(wú)任務(wù)切換。
          RSEG ?PR?_?serial?OS_CPU_A

          SerialISR:

          USING 0
          PUSHALL
          CLR EA
          LCALL _?serial
          SETB EA
          POPALL
          RETI
          ;-------------------------------------------------------------------------
          END
          ;-------------------------------------------------------------------------

          文件名 : OS_CPU_C.C

          void *OSTaskStkInit (void (*task)(void *pd), void *ppdata, void *ptos, INT16U opt) reentrant
          {
          OS_STK *stk;

          ppdata = ppdata;
          opt = opt; //opt沒(méi)被用到,保留此語(yǔ)句防止告警產(chǎn)生
          stk = (OS_STK *)ptos; //用戶(hù)堆棧最低有效地址
          *stk++ = 15; //用戶(hù)堆棧長(zhǎng)度
          *stk++ = (INT16U)task 0xFF; //任務(wù)地址低8位
          *stk++ = (INT16U)task >> 8; //任務(wù)地址高8位
          *stk++ = 0x00; //PSW
          *stk++ = 0x0A; //ACC
          *stk++ = 0x0B; //B
          *stk++ = 0x00; //DPL
          *stk++ = 0x00; //DPH
          *stk++ = 0x00; //R0
          *stk++ = 0x01; //R1
          *stk++ = 0x02; //R2
          *stk++ = 0x03; //R3
          *stk++ = 0x04; //R4
          *stk++ = 0x05; //R5
          *stk++ = 0x06; //R6
          *stk++ = 0x07; //R7
          //不用保存SP,任務(wù)切換時(shí)根據(jù)用戶(hù)堆棧長(zhǎng)度計(jì)算得出。
          return ((void *)ptos);
          }

          #if OS_CPU_HOOKS_EN
          void OSTaskCreateHook (OS_TCB *ptcb) reentrant
          {
          ptcb = ptcb; /* Prevent compiler warning */
          }

          void OSTaskDelHook (OS_TCB *ptcb) reentrant
          {
          ptcb = ptcb; /* Prevent compiler warning */
          }

          void OSTimeTickHook (void) reentrant
          {
          }
          #endif

          //初始化定時(shí)器0
          void InitTimer0(void) reentrant
          {
          TMOD=TMOD0xF0;
          TMOD=TMOD|0x01; //模式1(16位定時(shí)器),僅受TR0控制
          TH0=0x70; //定義Tick=50次/秒(即0.02秒/次)
          TL0=0x00; //OS_CPU_A.ASM 和 OS_TICKS_PER_SEC
          ET0=1; //允許T0中斷
          TR0=1;
          }

          文件名 : YY.C

          #include includes.h>

          #define MAX_STK_SIZE 64

          void TaskStartyya(void *yydata) reentrant;
          void TaskStartyyb(void *yydata) reentrant;
          void TaskStartyyc(void *yydata) reentrant;

          OS_STK TaskStartStkyya[MAX_STK_SIZE+1];//注意:我在ASM文件中設(shè)置?STACK空間為40H即64,不要超出范圍。
          OS_STK TaskStartStkyyb[MAX_STK_SIZE+1];//用戶(hù)棧多一個(gè)字節(jié)存長(zhǎng)度
          OS_STK TaskStartStkyyc[MAX_STK_SIZE+1];

          void main(void)
          {
          OSInit();

          InitTimer0();
          InitSerial();
          InitSerialBuffer();

          OSTaskCreate(TaskStartyya, (void *)0, TaskStartStkyya[0],2);
          OSTaskCreate(TaskStartyyb, (void *)0, TaskStartStkyyb[0],3);
          OSTaskCreate(TaskStartyyc, (void *)0, TaskStartStkyyc[0],4);

          OSStart();
          }


          void TaskStartyya(void *yydata) reentrant
          {
          yydata=yydata;
          clrscr();
          PrintStr("*******************************");
          PrintStr(" * Hello! The world. *");
          PrintStr(" *******************************");

          for(;;){
          PrintStr(" AAAAAA111111 is active.");
          OSTimeDly(OS_TICKS_PER_SEC);
          }
          }

          void TaskStartyyb(void *yydata) reentrant
          {
          yydata=yydata;

          for(;;){
          PrintStr(" BBBBBB333333 is active.");
          OSTimeDly(3*OS_TICKS_PER_SEC);
          }
          }

          void TaskStartyyc(void *yydata) reentrant
          {
          yydata=yydata;

          for(;;){
          PrintStr(" CCCCCC666666 is active.");
          OSTimeDly(6*OS_TICKS_PER_SEC);
          }
          }


          * - 本貼最后修改時(shí)間:2003-5-29 11:25:08 修改者:gdtyy
          * - 修改原因:+

          作者信箱 asdjf@163.com


          原文 http://www.21icbbs.com/club/bbs/list.asp?boardid=8page=1t=338692tp=%u79FB%u690D%u5FC3%u5F97



          關(guān)鍵詞: 社區(qū) 心得 移植 uCOS51

          評(píng)論


          相關(guān)推薦

          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();