技術(shù)頻道

娓娓工業(yè)
您現(xiàn)在的位置: 中國(guó)傳動(dòng)網(wǎng) > 技術(shù)頻道 > 應(yīng)用方案 > ucos-II移植到51單片機(jī)的解決辦法

ucos-II移植到51單片機(jī)的解決辦法

時(shí)間:2018-09-11 10:44:08來源:網(wǎng)絡(luò)轉(zhuǎn)載

導(dǎo)語(yǔ):?μC/OS-II由Micrium公司提供,是一個(gè)可移植、可固化的、可裁剪的、占先式多任務(wù)實(shí)時(shí)內(nèi)核,它適用于多種微處理器,微控制器和數(shù)字處理芯片(已經(jīng)移植到超過100種以上的微處理器應(yīng)用中)

本文主要是關(guān)于ucos-II的相關(guān)介紹,并著重對(duì)ucos-II移植到51單片機(jī)進(jìn)行了詳盡的闡述。

ucos-II

μC/OS-II由Micrium公司提供,是一個(gè)可移植、可固化的、可裁剪的、占先式多任務(wù)實(shí)時(shí)內(nèi)核,它適用于多種微處理器,微控制器和數(shù)字處理芯片(已經(jīng)移植到超過100種以上的微處理器應(yīng)用中)。同時(shí),該系統(tǒng)源代碼開放、整潔、一致,注釋詳盡,適合系統(tǒng)開發(fā)。μC/OS-II已經(jīng)通過聯(lián)邦航空局(FAA)商用航行器認(rèn)證,符合航空無線電技術(shù)委員會(huì)(RTCA)DO-178B標(biāo)準(zhǔn)。

μC/OS-II被廣泛應(yīng)用于微處理器、微控制器和數(shù)字信號(hào)處理器。

μC/OS-II的前身是μC/OS,最早出自于1992年美國(guó)嵌入式系統(tǒng)專家JeanJ.Labrosse在《嵌入式系統(tǒng)編程》雜志的5月和6月刊上刊登的文章連載,并把μC/OS的源碼發(fā)布在該雜志的BBS上。

μC/OS和μC/OS-II是專門為計(jì)算機(jī)的嵌入式應(yīng)用設(shè)計(jì)的,絕大部分代碼是用C語(yǔ)言編寫的。CPU硬件相關(guān)部分是用匯編語(yǔ)言編寫的、總量約200行的匯編語(yǔ)言部分被壓縮到最低限度,為的是便于移植到任何一種其它的CPU上。用戶只要有標(biāo)準(zhǔn)的ANSI的C交叉編譯器,有匯編器、連接器等軟件工具,就可以將μC/OS-II嵌入到開發(fā)的產(chǎn)品中。μC/OS-II具有執(zhí)行效率高、占用空間小、實(shí)時(shí)性能優(yōu)良和可擴(kuò)展性強(qiáng)等特點(diǎn),最小內(nèi)核可編譯至2KB。μC/OS-II已經(jīng)移植到了幾乎所有知名的CPU上。

嚴(yán)格地說uC/OS-II只是一個(gè)實(shí)時(shí)操作系統(tǒng)內(nèi)核,它僅僅包含了任務(wù)調(diào)度,任務(wù)管理,時(shí)間管理,內(nèi)存管理和任務(wù)間的通信和同步等基本功能。沒有提供輸入輸出管理,文件系統(tǒng),網(wǎng)絡(luò)等額外的服務(wù)。但由于uC/OS-II良好的可擴(kuò)展性和源碼開放,這些非必須的功能完全可以由用戶自己根據(jù)需要分別實(shí)現(xiàn)。

uC/OS-II目標(biāo)是實(shí)現(xiàn)一個(gè)基于優(yōu)先級(jí)調(diào)度的搶占式的實(shí)時(shí)內(nèi)核,并在這個(gè)內(nèi)核之上提供最基本的系統(tǒng)服務(wù),如信號(hào)量,郵箱,消息隊(duì)列,內(nèi)存管理,中斷管理等。

uC/OS-II以源代碼的形式發(fā)布,是開源軟件,但并不意味著它是免費(fèi)軟件。你可以將其用于教學(xué)和私下研究(peacefulresearch);但是如果你將其用于商業(yè)用途,那么你必須通過Micrium獲得商用許可。

uCOSII移植的一點(diǎn)心得

uCOS-II是一種十分優(yōu)秀實(shí)時(shí)操作系統(tǒng),其在NASA的認(rèn)證通過直接說明了其優(yōu)秀及穩(wěn)健的性能,同時(shí)由于其完全open,所以受到廣大開源愛好者的喜愛。uCOS-II簡(jiǎn)單明了,同時(shí)絕大部分代碼都采用ANSIC編寫(除了與CPU相關(guān)代碼外),所以學(xué)習(xí)起來十分容易,是嵌入式學(xué)習(xí)乃至操作系統(tǒng)學(xué)習(xí)最好的入門OS之一。

我主要想講一下自己最近移植uCOS-II的心得,因?yàn)樽罱苍趯W(xué)習(xí)操作系統(tǒng),所以這段日子對(duì)于uCOS-II的學(xué)習(xí)的確也讓我對(duì)于操作系統(tǒng)有了一個(gè)實(shí)際深刻的認(rèn)識(shí)。

uCOS-II移植其實(shí)十分簡(jiǎn)單。對(duì)于一個(gè)處理器,需要做的工作只有:修改三個(gè)文件――os_cpu_c.c、os_cpu.h、os_cpu_a.asm(ASM文件根據(jù)編譯器不同而又有一些不同)。

用另一種方式說,需要做的工作就是修改五個(gè)函數(shù):

1、os_cpu_c.c:OSTaskStkInit;

2、os_cpu_a.asm:OSStartHighRdy、OSCtxSw、OSIntCtxSw、OSTickISR;

OSTaskStkInit函數(shù)是針對(duì)CPU壓棧的函數(shù),需要模仿出CPU初始化后的寄存器狀況。也使需要修改的唯一一個(gè)C語(yǔ)言函數(shù)。其他的都是匯編函數(shù)。

如果我們可以從uCOS-II官方網(wǎng)站上找到相同CPU或是相似的同一家族的CPU移植代碼,那么我們的移植工作將會(huì)簡(jiǎn)單得多。因?yàn)橹辽傥覀兛梢灾挥昧私膺@個(gè)處理器的內(nèi)部結(jié)構(gòu),而不用細(xì)致的了解其匯編指令等很多繁瑣而沒有意義的事情(有的處理器你可能一輩子不再用它,了解得太細(xì)致只是在浪費(fèi)時(shí)間)。

譬如,此次我要做的是將uCOS-II移植到瑞薩M16C/62A上,而官方網(wǎng)站上只有其62P的移植代碼,于是乎我就將二者的datasheet在CPU的寄存器、中斷部分仔細(xì)比對(duì),發(fā)現(xiàn)二者區(qū)別很小。最大的區(qū)別恐怕就在CPU內(nèi)部寄存器中的INTBL和PC寄存器二者順序相反吧,這只要在相關(guān)部分注意就可以了,所以很容易就搞定了CPU相關(guān)代碼部分。

總結(jié)一下移植中浪費(fèi)我時(shí)間的幾個(gè)小錯(cuò)誤吧,這完全是個(gè)人粗心導(dǎo)致的失誤:

1、在os_cpu.h文件中需要用宏定義將OS_TASK_SW指向OSCtxSw函數(shù),而我開始像以前一樣直接將OSCtxSw函數(shù)與0號(hào)軟終端鏈接起來,結(jié)果發(fā)現(xiàn)函數(shù)調(diào)用不成功。后來直接用宏定義將OS_TASK_SWdefine為OSCtxSw函數(shù),初步調(diào)試通過,即驗(yàn)證OSCtxSw函數(shù)正確,但是到后來調(diào)用任務(wù)的時(shí)候卻發(fā)現(xiàn)任務(wù)切換不正常,不得不重新將函數(shù)與中斷結(jié)合起來,當(dāng)然就不能還是0號(hào)中斷了,而是改為一個(gè)比較保險(xiǎn)的軟終端,這個(gè)問題糾結(jié)了很長(zhǎng)時(shí)間。

2、在看書的時(shí)候不仔細(xì)直接導(dǎo)致我犯了一個(gè)大錯(cuò)誤。起初以為task只要是能夠達(dá)到功能的死循環(huán)即可。所以每個(gè)task函數(shù)都是while(1)或者for(;;;),但是我沒有注意到一點(diǎn)就是每個(gè)task里面都應(yīng)該有OSTimeDly()函數(shù),否則將導(dǎo)致任務(wù)之間不能跳轉(zhuǎn)。所以最初的實(shí)驗(yàn)現(xiàn)象是永遠(yuǎn)只有一個(gè)任務(wù)在運(yùn)行,但是任務(wù)不能切換……

3、未注意到版本之間的區(qū)別。我們知道在新版本的uCOS-II中,添加了一個(gè)文件os_tmr.c,主要是在timer上面做了很大的調(diào)整,但是我沒有注意到這一點(diǎn),仍舊按照老版本的方法調(diào)試,導(dǎo)致函數(shù)調(diào)用讓我完全不知所措。最后注意到os的源代碼的不同,仔細(xì)閱讀源代碼之后知道了其用法,其實(shí)如果不需要timer太強(qiáng)大的功能,只要在os_cfg.h文件中將OS_TMR_EN設(shè)置為0即可。這在習(xí)慣老版本調(diào)試方法的同學(xué)而言是很好的方法。

ucos-II移植到51單片機(jī)的解決辦法

先來了解和51移植相關(guān)的三個(gè)概念:

第一,移植UCOS必須要了解編譯器,我們一般使用的51編譯器都是KEIL。值得一提的是KEIL對(duì)可重入函數(shù)的處理。由于51單片機(jī)的堆棧指針是8位的,所以硬件堆棧只能設(shè)置在內(nèi)部RAM的DATA區(qū)和IDATA區(qū)(DATA、IDATA、PDATA、XDATA、CODE這些概念相關(guān)資料很多,我不想在此處滋述),所以51的堆棧是很緊張的。于是,KEIL將函數(shù)內(nèi)的動(dòng)態(tài)變量和函數(shù)傳遞的參數(shù)(當(dāng)然有一部分參數(shù)是用寄存器直接傳送的),放在分配的固定數(shù)據(jù)段中,函數(shù)執(zhí)行時(shí)在固定的數(shù)據(jù)段中去取得相關(guān)的數(shù)據(jù),而不是像傳統(tǒng)的CPU都用堆棧來處理,這就導(dǎo)致了函數(shù)不可重入,因?yàn)楫?dāng)一個(gè)函數(shù)沒執(zhí)行完成時(shí)再次執(zhí)行會(huì)把數(shù)據(jù)段里的內(nèi)容覆蓋掉。為了使函數(shù)可重入KEIL引入了仿真堆棧的概念(重入函數(shù)需在函數(shù)定義后面加上reentrant關(guān)鍵字),用仿真堆棧來傳遞參數(shù)及分配動(dòng)態(tài)變量,就好像傳統(tǒng)堆棧的入棧、出棧操作一般,如此函數(shù)第二次進(jìn)入執(zhí)行時(shí),就不會(huì)覆蓋掉上一次的變量和參數(shù),仿真堆棧實(shí)現(xiàn)原理詳見http://hi.baidu.com/lyb1900/blog/item/99b6313defc2b40abaa167fe.html。但是,KEIL的這一機(jī)制會(huì)給我們移植造成了麻煩,任務(wù)切換時(shí)不僅要保存好硬件堆棧內(nèi)容,還要保存好仿真堆棧的內(nèi)容。(建議先理解仿真堆棧的概念)

第二,其他類型的CPU可以在任務(wù)切換時(shí)先將SP指針保存到被中斷任務(wù)的OSTCBCur-》OSTCBStkPtr中,再將高優(yōu)先級(jí)任務(wù)的OSTCBCur-》OSTCBStkPtr恢復(fù)到SP中就可以了,各個(gè)任務(wù)使用各自的堆棧空間,互不干擾,切換也很方便。而51的堆棧指針是8位的,SP只能指向內(nèi)部RAM空間,但是內(nèi)部RAM很小,根本不可能將所有任務(wù)堆棧都設(shè)置在內(nèi)部RAM中(DATA和IDATA區(qū))。所以,51只能設(shè)置一個(gè)固定的硬件堆棧,每個(gè)任務(wù)可以在外部RAM中設(shè)置各自的任務(wù)堆棧,任務(wù)切換時(shí),將本任務(wù)所使用到的硬件堆棧的長(zhǎng)度和內(nèi)容保存到任務(wù)堆棧中,然后將高優(yōu)先級(jí)任務(wù)的用戶堆棧里的內(nèi)容恢復(fù)到硬件堆棧中。所以51切換任務(wù)會(huì)比較慢。

第三,在KEIL的工程配置Target選項(xiàng)中會(huì)有一個(gè)MemoryModel選項(xiàng)。用鼠標(biāo)點(diǎn)擊MemoryModel的下拉箭頭,會(huì)有3個(gè)選項(xiàng)。

Small:變量存儲(chǔ)在內(nèi)部ram里。

Compact:變量存儲(chǔ)在外部ram里,使用頁(yè)8位間接尋址

Large:變量存儲(chǔ)在外部Ram里,使用16位間接尋址。

這三個(gè)變量決定了定義的變量在不加存儲(chǔ)類型關(guān)鍵字時(shí),變量存放的位置。這一點(diǎn)很多網(wǎng)站、資料都說的很明白。但是其實(shí)還有一點(diǎn)很多資料都是沒說的。它還默認(rèn)決定了上述仿真堆棧的位置。這一點(diǎn)在51的啟動(dòng)代碼STARTUP.asm中能體現(xiàn)出來。其中有一段如下:

;StackSpaceforreentrantfunctionsintheSMALLmodel.

IBPSTACKEQU1;setto1ifsmallreentrantisused.

IBPSTACKTOPEQU0FFH+1;settopofstacktohighestlocation+1.

;

;StackSpaceforreentrantfunctionsintheLARGEmodel.

XBPSTACKEQU0;setto1iflargereentrantisused.

XBPSTACKTOPEQU7FFFH+1;settopofstacktohighestlocation+1.

;

;StackSpaceforreentrantfunctionsintheCOMPACTmodel.

PBPSTACKEQU0;setto1ifcompactreentrantisused.

PBPSTACKTOPEQU7FFFH+1;settopofstacktohighestlocation+1.

IFIBPSTACK《》0

EXTRNDATA(?C_IBP)

MOV?C_IBP,#LOWIBPSTACKTOP

ENDIF

IFXBPSTACK《》0

EXTRNDATA(?C_XBP)

MOV?C_XBP,#HIGHXBPSTACKTOP

MOV?C_XBP+1,#LOWXBPSTACKTOP

ENDIF

IFPBPSTACK《》0

EXTRNDATA(?C_PBP)

MOV?C_PBP,#LOWPBPSTACKTOP

ENDIF

注釋講的很清楚,根據(jù)所選模式,編譯器會(huì)將IBPSTACK、PBPSTACK或者XBPSTACK設(shè)置為1,就決定了仿真堆棧在IDATA區(qū)、PDAIA區(qū)還是XDATA區(qū)。對(duì)應(yīng)的,KEIL會(huì)自動(dòng)分配一個(gè)仿真堆棧指針,分別是?C_IBP、?C_PBP和(?C_XBP、?C_XBP+1),由于尋址XDATA區(qū)需要16位地址,所以需要兩個(gè)字節(jié)。這三個(gè)指針是KEIL根據(jù)選擇的MemoryModel選項(xiàng)自動(dòng)分配的。

注意:不要試圖在選擇好模式后將仿真堆棧設(shè)置在另一模式的空間中。比如,我用的小模式編譯,仿真堆棧在IDATA區(qū),用的仿真堆棧指針是?C_IBP,但是我現(xiàn)在在啟動(dòng)代碼中將IBPSTACK定義為0,將XBPSTACK設(shè)置為1,看起來我們先把仿真堆棧設(shè)置在XDATA區(qū)了,但實(shí)際上其它代碼段中使用的仿真堆棧指針任然是?C_IBP。有趣的是,KEIL還為我們的啟動(dòng)代碼做了一個(gè)很友好的列表框選擇界面。但實(shí)際上選擇好編譯模式后,仿真堆棧使用空間是不能更改的,不知道KEIL為什么這么做?但是我們有時(shí)候要根據(jù)單片機(jī)的型號(hào)選擇仿真堆棧的起始地址。

講了那么多,應(yīng)該來看看關(guān)于堆棧的組織了,首先是不知道哪位前輩移植的,用的小模式編譯的堆棧結(jié)構(gòu):

每個(gè)任務(wù)分都需要配一個(gè)任務(wù)堆棧,OSTCBCur-》OSTCBStkPtr指向任務(wù)堆棧的棧底,任務(wù)堆棧的首字節(jié)是仿真堆棧指針?C_IBP(由于是小模式編譯,所以使用的仿真堆棧設(shè)置在IDAIA區(qū))。用戶堆棧中緊接著存放的是該任務(wù)的仿真堆棧中的內(nèi)容。再接著是系統(tǒng)堆棧(就是SP指針?biāo)傅亩褩#┑拈L(zhǎng)度,最后是系統(tǒng)堆棧的內(nèi)容。

任務(wù)在切換時(shí),首先將當(dāng)前的?C_IBP的值保存到本任務(wù)堆棧的首地址中,然后將仿真堆棧的全部?jī)?nèi)容復(fù)制到任務(wù)堆棧中(仿真堆棧棧底固定在IDATA區(qū)的最高字節(jié)0xff,可以根據(jù)(0xff-?C_IBP+1)的值來確定所使用的仿真堆棧的長(zhǎng)度),接著保存系統(tǒng)堆棧的長(zhǎng)度(系統(tǒng)堆棧設(shè)置在DATA或IDATA區(qū)中,系統(tǒng)堆棧的棧底的地址我們可以在啟動(dòng)代碼中設(shè)置,長(zhǎng)度可以用(SP-Stack+1)來計(jì)算得到)最后將所用的系統(tǒng)堆棧中的內(nèi)容復(fù)制到任務(wù)堆棧中。

然后得到高優(yōu)先級(jí)的任務(wù)堆棧,首先恢復(fù)高優(yōu)先級(jí)任務(wù)的?C_IBP,然后計(jì)算出高優(yōu)先級(jí)任務(wù)所用仿真堆棧的長(zhǎng)度,將保存的仿真堆棧的內(nèi)容一一恢復(fù)到仿真堆棧中,然后得到系統(tǒng)棧的長(zhǎng)度,再將保存的系統(tǒng)堆棧的內(nèi)容恢復(fù)到系統(tǒng)堆棧中,最后恢復(fù)SP指針并執(zhí)行RETI返回指令,便實(shí)現(xiàn)了任務(wù)切換。

任務(wù)被打斷時(shí)將仿真堆棧和系統(tǒng)堆棧的內(nèi)容全都備份到任務(wù)堆棧中,在恢復(fù)運(yùn)行時(shí)將相應(yīng)的內(nèi)容還原到系統(tǒng)堆棧和仿真堆棧中。

這種方法的缺點(diǎn)是,任務(wù)切換將會(huì)變的很慢,因?yàn)橐謩e拷貝和恢復(fù)仿真堆棧和系統(tǒng)堆棧的全部?jī)?nèi)容。完全可以將仿真堆棧設(shè)置在XDATA區(qū)中,任務(wù)切換時(shí),只需保存和恢復(fù)?C_XBP指針就行了,而不必每次都拷貝和恢復(fù)仿真堆棧的全部?jī)?nèi)容。由于SP指針只有8位,系統(tǒng)堆棧只能設(shè)置在內(nèi)部RAM中。

再來看看楊屹大俠大模式編譯下的堆棧結(jié)構(gòu):

同樣,每個(gè)任務(wù)分配一個(gè)任務(wù)堆棧,OSTCBCur-》OSTCBStkPtr指向任務(wù)堆棧的棧底,任務(wù)堆棧的首字節(jié)是系統(tǒng)堆棧的長(zhǎng)度,接著是系統(tǒng)堆棧的全部?jī)?nèi)容。再接著是仿真堆棧指針?C_XBP的高低字節(jié)(因?yàn)槭谴竽J骄幾g,所以仿真堆棧在XDATA區(qū)),任務(wù)堆棧再高的字節(jié)是作為仿真堆棧用的,用戶堆棧的棧頂就是仿真堆棧的棧底。

任務(wù)切換時(shí),首先計(jì)算任務(wù)使用的系統(tǒng)堆棧的長(zhǎng)度,將長(zhǎng)度保存在任務(wù)堆棧棧底,然后將使用的系統(tǒng)堆棧的內(nèi)容全部復(fù)制到任務(wù)堆棧中,最后保存當(dāng)前的?C_XBP仿真堆棧指針的高低字節(jié)。

接著恢復(fù)高優(yōu)先級(jí)任務(wù)的信息,先得到堆棧長(zhǎng)度,將備份的堆棧內(nèi)容恢復(fù)到系統(tǒng)堆棧中,并恢復(fù)SP指針(根據(jù)長(zhǎng)度和系統(tǒng)堆棧的棧底可以計(jì)算出SP指針的值)。最后恢復(fù)?C_XBP的高低字節(jié)。便實(shí)現(xiàn)了任務(wù)的切換。

任務(wù)切換時(shí)將系統(tǒng)堆棧的內(nèi)容和仿真堆棧指針保存起來,再將高優(yōu)先級(jí)任務(wù)的仿真堆棧指針和系統(tǒng)堆棧的內(nèi)容恢復(fù)。

和上述的小模式下的切換過程相比,仿真堆棧的內(nèi)容在任務(wù)切換時(shí)不需要保存和恢復(fù)了,任務(wù)切換速度會(huì)提高不少。但是讀過楊屹大俠代碼的朋友肯定知道,每個(gè)任務(wù)堆棧的大小都要設(shè)置成相同。這對(duì)于有些堆棧使用很少的任務(wù)來說是很浪費(fèi)的,而且51的RAM本來就那么緊張?仿真堆棧被設(shè)置在任務(wù)堆棧的最高地址處,細(xì)心的朋友會(huì)發(fā)現(xiàn),堆棧檢測(cè)函數(shù)肯定是無法運(yùn)行了。

正是意識(shí)到這些缺陷,我對(duì)楊屹大俠移植的代碼進(jìn)行了一些改動(dòng),堆棧結(jié)構(gòu)也有較大改變,使用的也是大模式編譯:

先來了解和51移植相關(guān)的三個(gè)概念:

第一,移植UCOS必須要了解編譯器,我們一般使用的51編譯器都是KEIL。值得一提的是KEIL對(duì)可重入函數(shù)的處理。由于51單片機(jī)的堆棧指針是8位的,所以硬件堆棧只能設(shè)置在內(nèi)部RAM的DATA區(qū)和IDATA區(qū)(DATA、IDATA、PDATA、XDATA、CODE這些概念相關(guān)資料很多,我不想在此處滋述),所以51的堆棧是很緊張的。于是,KEIL將函數(shù)內(nèi)的動(dòng)態(tài)變量和函數(shù)傳遞的參數(shù)(當(dāng)然有一部分參數(shù)是用寄存器直接傳送的),放在分配的固定數(shù)據(jù)段中,函數(shù)執(zhí)行時(shí)在固定的數(shù)據(jù)段中去取得相關(guān)的數(shù)據(jù),而不是像傳統(tǒng)的CPU都用堆棧來處理,這就導(dǎo)致了函數(shù)不可重入,因?yàn)楫?dāng)一個(gè)函數(shù)沒執(zhí)行完成時(shí)再次執(zhí)行會(huì)把數(shù)據(jù)段里的內(nèi)容覆蓋掉。為了使函數(shù)可重入KEIL引入了仿真堆棧的概念(重入函數(shù)需在函數(shù)定義后面加上reentrant關(guān)鍵字),用仿真堆棧來傳遞參數(shù)及分配動(dòng)態(tài)變量,就好像傳統(tǒng)堆棧的入棧、出棧操作一般,如此函數(shù)第二次進(jìn)入執(zhí)行時(shí),就不會(huì)覆蓋掉上一次的變量和參數(shù),仿真堆棧實(shí)現(xiàn)原理詳見http://hi.baidu.com/lyb1900/blog/item/99b6313defc2b40abaa167fe.html。但是,KEIL的這一機(jī)制會(huì)給我們移植造成了麻煩,任務(wù)切換時(shí)不僅要保存好硬件堆棧內(nèi)容,還要保存好仿真堆棧的內(nèi)容。(建議先理解仿真堆棧的概念)

第二,其他類型的CPU可以在任務(wù)切換時(shí)先將SP指針保存到被中斷任務(wù)的OSTCBCur-》OSTCBStkPtr中,再將高優(yōu)先級(jí)任務(wù)的OSTCBCur-》OSTCBStkPtr恢復(fù)到SP中就可以了,各個(gè)任務(wù)使用各自的堆棧空間,互不干擾,切換也很方便。而51的堆棧指針是8位的,SP只能指向內(nèi)部RAM空間,但是內(nèi)部RAM很小,根本不可能將所有任務(wù)堆棧都設(shè)置在內(nèi)部RAM中(DATA和IDATA區(qū))。所以,51只能設(shè)置一個(gè)固定的硬件堆棧,每個(gè)任務(wù)可以在外部RAM中設(shè)置各自的任務(wù)堆棧,任務(wù)切換時(shí),將本任務(wù)所使用到的硬件堆棧的長(zhǎng)度和內(nèi)容保存到任務(wù)堆棧中,然后將高優(yōu)先級(jí)任務(wù)的用戶堆棧里的內(nèi)容恢復(fù)到硬件堆棧中。所以51切換任務(wù)會(huì)比較慢。

第三,在KEIL的工程配置Target選項(xiàng)中會(huì)有一個(gè)MemoryModel選項(xiàng)。用鼠標(biāo)點(diǎn)擊MemoryModel的下拉箭頭,會(huì)有3個(gè)選項(xiàng)。

Small:變量存儲(chǔ)在內(nèi)部ram里。

Compact:變量存儲(chǔ)在外部ram里,使用頁(yè)8位間接尋址

Large:變量存儲(chǔ)在外部Ram里,使用16位間接尋址。

這三個(gè)變量決定了定義的變量在不加存儲(chǔ)類型關(guān)鍵字時(shí),變量存放的位置。這一點(diǎn)很多網(wǎng)站、資料都說的很明白。但是其實(shí)還有一點(diǎn)很多資料都是沒說的。它還默認(rèn)決定了上述仿真堆棧的位置。這一點(diǎn)在51的啟動(dòng)代碼STARTUP.asm中能體現(xiàn)出來。其中有一段如下:

;StackSpaceforreentrantfunctionsintheSMALLmodel.

IBPSTACKEQU1;setto1ifsmallreentrantisused.

IBPSTACKTOPEQU0FFH+1;settopofstacktohighestlocation+1.

;

;StackSpaceforreentrantfunctionsintheLARGEmodel.

XBPSTACKEQU0;setto1iflargereentrantisused.

XBPSTACKTOPEQU7FFFH+1;settopofstacktohighestlocation+1.

;

;StackSpaceforreentrantfunctionsintheCOMPACTmodel.

PBPSTACKEQU0;setto1ifcompactreentrantisused.

PBPSTACKTOPEQU7FFFH+1;settopofstacktohighestlocation+1.

IFIBPSTACK《》0

EXTRNDATA(?C_IBP)

MOV?C_IBP,#LOWIBPSTACKTOP

ENDIF

IFXBPSTACK《》0

EXTRNDATA(?C_XBP)

MOV?C_XBP,#HIGHXBPSTACKTOP

MOV?C_XBP+1,#LOWXBPSTACKTOP

ENDIF

IFPBPSTACK《》0

EXTRNDATA(?C_PBP)

MOV?C_PBP,#LOWPBPSTACKTOP

ENDIF

注釋講的很清楚,根據(jù)所選模式,編譯器會(huì)將IBPSTACK、PBPSTACK或者XBPSTACK設(shè)置為1,就決定了仿真堆棧在IDATA區(qū)、PDAIA區(qū)還是XDATA區(qū)。對(duì)應(yīng)的,KEIL會(huì)自動(dòng)分配一個(gè)仿真堆棧指針,分別是?C_IBP、?C_PBP和(?C_XBP、?C_XBP+1),由于尋址XDATA區(qū)需要16位地址,所以需要兩個(gè)字節(jié)。這三個(gè)指針是KEIL根據(jù)選擇的MemoryModel選項(xiàng)自動(dòng)分配的。

注意:不要試圖在選擇好模式后將仿真堆棧設(shè)置在另一模式的空間中。比如,我用的小模式編譯,仿真堆棧在IDATA區(qū),用的仿真堆棧指針是?C_IBP,但是我現(xiàn)在在啟動(dòng)代碼中將IBPSTACK定義為0,將XBPSTACK設(shè)置為1,看起來我們先把仿真堆棧設(shè)置在XDATA區(qū)了,但實(shí)際上其它代碼段中使用的仿真堆棧指針任然是?C_IBP。有趣的是,KEIL還為我們的啟動(dòng)代碼做了一個(gè)很友好的列表框選擇界面。但實(shí)際上選擇好編譯模式后,仿真堆棧使用空間是不能更改的,不知道KEIL為什么這么做?但是我們有時(shí)候要根據(jù)單片機(jī)的型號(hào)選擇仿真堆棧的起始地址。

標(biāo)簽:

點(diǎn)贊

分享到:

上一篇:變頻的調(diào)速系統(tǒng)的組成及在紡...

下一篇:干貨!西門子SIMATIC PLC計(jì)時(shí)...

中國(guó)傳動(dòng)網(wǎng)版權(quán)與免責(zé)聲明:凡本網(wǎng)注明[來源:中國(guó)傳動(dòng)網(wǎng)]的所有文字、圖片、音視和視頻文件,版權(quán)均為中國(guó)傳動(dòng)網(wǎng)(m.u63ivq3.com)獨(dú)家所有。如需轉(zhuǎn)載請(qǐng)與0755-82949061聯(lián)系。任何媒體、網(wǎng)站或個(gè)人轉(zhuǎn)載使用時(shí)須注明來源“中國(guó)傳動(dòng)網(wǎng)”,違反者本網(wǎng)將追究其法律責(zé)任。

本網(wǎng)轉(zhuǎn)載并注明其他來源的稿件,均來自互聯(lián)網(wǎng)或業(yè)內(nèi)投稿人士,版權(quán)屬于原版權(quán)人。轉(zhuǎn)載請(qǐng)保留稿件來源及作者,禁止擅自篡改,違者自負(fù)版權(quán)法律責(zé)任。

網(wǎng)站簡(jiǎn)介|會(huì)員服務(wù)|聯(lián)系方式|幫助信息|版權(quán)信息|網(wǎng)站地圖|友情鏈接|法律支持|意見反饋|sitemap

傳動(dòng)網(wǎng)-工業(yè)自動(dòng)化與智能制造的全媒體“互聯(lián)網(wǎng)+”創(chuàng)新服務(wù)平臺(tái)

網(wǎng)站客服服務(wù)咨詢采購(gòu)咨詢媒體合作

Chuandong.com Copyright ?2005 - 2024 ,All Rights Reserved 深圳市奧美大唐廣告有限公司 版權(quán)所有
粵ICP備 14004826號(hào) | 營(yíng)業(yè)執(zhí)照證書 | 不良信息舉報(bào)中心 | 粵公網(wǎng)安備 44030402000946號(hào)