在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > uC/OS-II在51單片機上的移植1

            uC/OS-II在51單片機上的移植1

            作者: 時間:2016-11-22 來源:網絡 收藏
            引言:隨著各種應用電子系統(tǒng)的復雜化和系統(tǒng)實時性需求的提高,并伴隨應用軟件朝著系統(tǒng)化方向發(fā)展的加速,在16位/32位單片機中廣泛使用了嵌入式實時操作系統(tǒng)。然而實際使用中卻存在著大量8位單片機,從經濟性考慮,對某些應用場合,在8位MCU上使用操作系統(tǒng)是可行的。從學習操作系統(tǒng)角度,uC/OS-II for 51即簡單又全面,學習成本低廉,值得推廣。

            結語:μC/OS-II具有免費、簡單、可靠性高、實時性好等優(yōu)點,但也有缺乏便利開發(fā)環(huán)境等缺點,尤其不像商用嵌入式系統(tǒng)那樣得到廣泛使用和持續(xù)的研究更新。但開放性又使得開發(fā)人員可以自行裁減和添加所需的功能,在許多應用領域發(fā)揮著獨特的作用。當然,是否在單片機系統(tǒng)中嵌入μC/OS-II應視所開發(fā)的項目而定,對于一些簡單的、低成本的項目來說,就沒必要使用嵌入式操作系統(tǒng)了。

            本文引用地址:http://www.biyoush.com/article/201611/319609.htm

            uC/OS-II原理:
            uCOSII包括任務調度、時間管理、內存管理、資源管理(信號量、郵箱、消息隊列)四大部分,沒有文件系統(tǒng)、網絡接口、輸入輸出界面。它的移植只與4個文件相關:匯編文件(OS_CPU_A.ASM)、處理器相關C文件(OS_CPU.H、OS_CPU_C.C)和配置文件(OS_CFG.H)。有64個優(yōu)先級,系統(tǒng)占用8個,用戶可創(chuàng)建56個任務,不支持時間片輪轉。它的基本思路就是 “近似地每時每刻總是讓優(yōu)先級最高的就緒任務處于運行狀態(tài)” 。為了保證這一點,它在調用系統(tǒng)API函數、中斷結束、定時中斷結束時總是執(zhí)行調度算法。原作者通過事先計算好數據,簡化了運算量,通過精心設計就緒表結構,使得延時可預知。任務的切換是通過模擬一次中斷實現的。
            uCOSII工作核心原理是:近似地讓最高優(yōu)先級的就緒任務處于運行狀態(tài)。
            操作系統(tǒng)將在下面情況中進行任務調度:調用API函數(用戶主動調用),中斷(系統(tǒng)占用的時間片中斷OsTimeTick(),用戶使用的中斷)。
            調度算法書上講得很清楚,我主要講一下整體思路。
            (1)在調用API函數時,有可能引起阻塞,如果系統(tǒng)API函數察覺到運行條件不滿足,需要切換就調用OSSched()調度函數,這個過程是系統(tǒng)自動完成的,用戶沒有參與。OSSched()判斷是否切換,如果需要切換,則此函數調用OS_TASK_SW()。這個函數模擬一次中斷(在51里沒有軟中斷,我用子程序調用模擬,效果相同),好象程序被中斷打斷了,其實是OS故意制造的假象,目的是為了任務切換。既然是中斷,那么返回地址(即緊鄰OS_TASK_SW()的下一條匯編指令的PC地址)就被自動壓入堆棧,接著在中斷程序里保存CPU寄存器(PUSHALL)……。堆棧結構不是任意的,而是嚴格按照uCOSII規(guī)范處理。OS每次切換都會保存和恢復全部現場信息(POPALL),然后用RETI回到任務斷點繼續(xù)執(zhí)行。這個斷點就是OSSched()函數里的緊鄰OS_TASK_SW()的下一條匯編指令的PC地址。切換的整個過程就是,用戶任務程序調用系統(tǒng)API函數,API調用OSSched(),OSSched()調用軟中斷OS_TASK_SW()即OSCtxSw,返回地址(PC值)壓棧,進入OSCtxSw中斷處理子程序內部。反之,切換程序調用RETI返回緊鄰OS_TASK_SW()的下一條匯編指令的PC地址,進而返回OSSched()下一句,再返回API下一句,即用戶程序斷點。因此,如果任務從運行到就緒再到運行,它是從調度前的斷點處運行。
            (2)中斷會引發(fā)條件變化,在退出前必須進行任務調度。uCOSII要求中斷的堆棧結構符合規(guī)范,以便正確協調中斷退出和任務切換。前面已經說到任務切換實際是模擬一次中斷事件,而在真正的中斷里省去了模擬(本身就是中斷嘛)。只要規(guī)定中斷堆棧結構和uCOSII模擬的堆棧結構一樣,就能保證在中斷里進行正確的切換。任務切換發(fā)生在中斷退出前,此時還沒有返回中斷斷點。仔細觀察中斷程序和切換程序最后兩句,它們是一模一樣的,POPALL+RETI。即要么直接從中斷程序退出,返回斷點;要么先保存現場到TCB,等到恢復現場時再從切換函數返回原來的中斷斷點(由于中斷和切換函數遵循共同的堆棧結構,所以退出操作相同,效果也相同)。用戶編寫的中斷子程序必須按照uCOSII規(guī)范書寫。任務調度發(fā)生在中斷退出前,是非常及時的,不會等到下一時間片才處理。OSIntCtxSw()函數對堆棧指針做了簡單調整,以保證所有掛起任務的棧結構看起來是一樣的。
            (3)在uCOSII里,任務必須寫成兩種形式之一(《uCOSII中文版》p99頁)。在有些RTOS開發(fā)環(huán)境里沒有要求顯式調用OSTaskDel(),這是因為開發(fā)環(huán)境自動做了處理,實際原理都是一樣的。uCOSII的開發(fā)依賴于編譯器,目前沒有專用開發(fā)環(huán)境,所以出現這些不便之處是可以理解的。
            移植過程:
            (1)拷貝書后附贈光盤sourcecode目錄下的內容到C:YY下,刪除不必要的文件和EX1L.C,只剩下p187(《uCOSII》)上列出的文件。
            (2)改寫最簡單的OS_CPU.H
            數據類型的設定見C51.PDF第176頁。注意BOOLEAN要定義成unsigned char 類型,因為bit類型為C51特有,不能用在結構體里。
            EA=0關中斷;EA=1開中斷。這樣定義即減少了程序行數,又避免了退出臨界區(qū)后關中斷造成的死機。
            MCS-51堆棧從下往上增長(1=向下,0=向上),OS_STK_GROWTH定義為0
            #define OS_TASK_SW() OSCtxSw() 因為MCS-51沒有軟中斷指令,所以用程序調用代替。兩者的堆棧格式相同,RETI指令復位中斷系統(tǒng),RET則沒有。實踐表明,對于MCS-51,用子程序調用入棧,用中斷返回指令RETI出棧是沒有問題的,反之中斷入棧RET出棧則不行??傊瑢τ谌霔?,子程序調用與中斷調用效果是一樣的,可以混用。在沒有中斷發(fā)生的情況下復位中斷系統(tǒng)也不會影響系統(tǒng)正常運行。詳見《uC/OS-II》第八章193頁第12行
            (3)改寫OS_CPU_C.C
            我設計的堆棧結構如下圖所示:

            TCB結構體中OSTCBStkPtr總是指向用戶堆棧最低地址,該地址空間內存放用戶堆棧長度,其上空間存放系統(tǒng)堆棧映像,即:用戶堆??臻g大小=系統(tǒng)堆??臻g大小+1。
            SP總是先加1再存數據,因此,SP初始時指向系統(tǒng)堆棧起始地址(OSStack)減1處(OSStkStart)。很明顯系統(tǒng)堆棧存儲空間大小=SP-OSStkStart。
            任務切換時,先保存當前任務堆棧內容。方法是:用SP-OSStkStart得出保存字節(jié)數,將其寫入用戶堆棧最低地址內,以用戶堆棧最低地址為起址,以OSStkStart為系統(tǒng)堆棧起址,由系統(tǒng)棧向用戶棧拷貝數據,循環(huán)SP-OSStkStart次,每次拷貝前先將各自棧指針增1。
            其次,恢復最高優(yōu)先級任務系統(tǒng)堆棧。方法是:獲得最高優(yōu)先級任務用戶堆棧最低地址,從中取出“長度”,以最高優(yōu)先級任務用戶堆棧最低地址為起址,以OSStkStart為系統(tǒng)堆棧起址,由用戶棧向系統(tǒng)??截悢祿?,循環(huán)“長度”數值指示的次數,每次拷貝前先將各自棧指針增1。
            用戶堆棧初始化時從下向上依次保存:用戶堆棧長度(15),PCL,PCH,PSW,ACC,B,DPL,DPH,R0,R1,R2,R3,R4,R5,R6,R7。不保存SP,任務切換時根據用戶堆棧長度計算得出。
            OSTaskStkInit函數總是返回用戶棧最低地址。
            操作系統(tǒng)tick時鐘我使用了51單片機的T0定時器,它的初始化代碼用C寫在了本文件中。
            最后還有幾點必須注意的事項。本來原則上我們不用修改與處理器無關的代碼,但是由于KEIL編譯器的特殊性,這些代碼仍要多處改動。因為KEIL缺省情況下編譯的代碼不可重入,而多任務系統(tǒng)要求并發(fā)操作導致重入,所以要在每個C函數及其聲明后標注reentrant關鍵字。另外,“pdata”、“data”在uCOS中用做一些函數的形參,但它同時又是KEIL的關鍵字,會導致編譯錯誤,我通過把“pdata”改成“ppdata”,“data”改成“ddata”解決了此問題。OSTCBCur、OSTCBHighRdy、OSRunning、OSPrioCur、OSPrioHighRdy這幾個變量在匯編程序中用到了,為了使用Ri訪問而不用DPTR,應該用KEIL擴展關鍵字IDATA將它們定義在內部RAM中。
            (4)重寫OS_CPU_A.ASM
            A51宏匯編的大致結構如下:
            NAME 模塊名 ;與文件名無關
            定義重定位段 必須按照C51格式定義,匯編遵守C51規(guī)范。段名格式為:?PR?函數名?模塊名
            聲明引用全局變量和外部子程序 注意關鍵字為“EXTRN”沒有‘E’
            全局變量名直接引用
            無參數/無寄存器參數函數 FUNC
            帶寄存器參數函數 _FUNC
            重入函數 _?FUNC
            分配堆棧空間
            只關心大小,堆棧起點由keil決定,通過標號可以獲得keil分配的SP起點。切莫自己分配堆棧起點,只要用DS通知KEIL預留堆棧空間即可。
            ?STACK段名與STARTUP.A51中的段名相同,這意味著KEIL在LINK時將把兩個同名段拼在一起,我預留了40H個字節(jié),STARTUP.A51預留了1個字節(jié),LINK完成后堆棧段總長為41H。查看yy.m51知KEIL將堆棧起點定在21H,長度41H,處于內部RAM中。
            定義宏
            宏名 MACRO 實體 ENDM
            子程序
            OSStartHighRdy
            OSCtxSw
            OSIntCtxSw
            OSTickISR
            SerialISR
            END ;聲明匯編源文件結束

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

            (5)移植串口驅動程序
            在此之前我寫過基于中斷的串口驅動程序,包括打印字節(jié)/字/長字/字符串,讀串口,初始化串口/緩沖區(qū)。把它改成重入函數即可直接使用。
            系統(tǒng)提供的顯示函數是并發(fā)的,它不是直接顯示到串口,而是先輸出到顯存,用戶不必擔心IO慢速操作影響程序運行。串口輸入也采用了同樣的技術,他使得用戶在CPU忙于處理其他任務時照樣可以盲打輸入命令。
            (6)編寫測試程序Demo(YY.C)
            Demo程序創(chuàng)建了3個任務A、B、C優(yōu)先級分別為2、3、4,A每秒顯示一次,B每3秒顯示一次,C每6秒顯示一次。從顯示結果看,顯示3個A后顯示1個B,顯示6個A和2個B后顯示1個C,結果顯然正確。
            顯示結果如下:
            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程序經Keil701編譯后,代碼量為7-8K,可直接在KeilC51上仿真運行。
            編譯時要將OS_CPU_C.C、UCOS_II.C、OS_CPU_A.ASM、YY.C加入項目

            文件名 : 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)

            對外聲明4個不可重入函數
            PUBLIC OSStartHighRdy
            PUBLIC OSCtxSw
            PUBLIC OSIntCtxSw
            PUBLIC OSTickISR

            PUBLIC SerialISR

            分配堆??臻g。只關心大小,堆棧起點由keil決定,通過標號可以獲得keil分配的SP起點。
            ?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,任務切換時由相應程序調整
            ENDM

            POPALL MACRO
            POP ACC ;不必保存SP,任務切換時由相應程序調整
            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自動關中斷,此處不必用CLR EA指令,因為到此處還未開中斷,本程序退出后,開中斷。
            LCALL _?OSTaskSwHook

            OSCtxSw_in:

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

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

            *UserStkPtr ===> R5 用戶堆棧起始地址內容(即用戶堆棧長度放在此處) 詳見文檔說明 指針用法詳見C51.PDF第178頁
            MOVX A,@DPTR ;用戶堆棧中是unsigned char類型數據
            MOV R5,A ;R5=用戶堆棧長度

            恢復現場堆棧內容
            MOV R0,#OSStkStart

            restore_stack:

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

            恢復堆棧指針SP
            MOV SP,R0

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

            POPALL
            SETB EA ;開中斷
            RETI
            -------------------------------------------------------------------------
            RSEG ?PR?OSCtxSw?OS_CPU_A
            OSCtxSw:
            PUSHALL

            OSIntCtxSw_in:

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

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

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

            保存堆棧長度
            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

            調用用戶程序
            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 使用這兩個變量主要目的是為了使指針比較變?yōu)樽止?jié)比較,以便節(jié)省時間。
            MOV R0,#OSPrioCur
            MOV R1,#OSPrioHighRdy
            MOV A,@R1
            MOV @R0,A

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

            OSIntCtxSw:

            調整SP指針去掉在調用OSIntExit(),OSIntCtxSw()過程中壓入堆棧的多余內容
            SP=SP-4

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

            LJMP OSIntCtxSw_in
            -------------------------------------------------------------------------
            CSEG AT 000BH ;OSTickISR
            LJMP OSTickISR ;使用定時器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),無任務切換。
            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沒被用到,保留此語句防止告警產生
            stk = (OS_STK *)ptos; //用戶堆棧最低有效地址
            *stk++ = 15; //用戶堆棧長度
            *stk++ = (INT16U)task & 0xFF; //任務地址低8位
            *stk++ = (INT16U)task >> 8; //任務地址高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,任務切換時根據用戶堆棧長度計算得出。
            return ((void *)ptos);
            }

            #if OS_CPU_HOOKS_EN
            void OSTaskCreateHook (OS_TCB *ptcb) reentrant
            {
            ptcb = ptcb;
            }

            void OSTaskDelHook (OS_TCB *ptcb) reentrant
            {
            ptcb = ptcb;
            }

            void OSTimeTickHook (void) reentrant
            {
            }
            #endif

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



            關鍵詞: uCOS-II51單片機移

            評論


            相關推薦

            技術專區(qū)

            關閉