在8051核單片機龐大的家族中,C8051F系列作為其中的后起之秀,是目前功能最全、速度最快的8051衍生單片機之一,正得到越來(lái)越廣泛的應用。它集成了嵌入式系統的許多先進(jìn)技術(shù),有豐富的模擬和數字資源.是一個(gè)完全意義上的SoC產(chǎn)品。 C805IF12X作為該系列中的高端部分,具有最快100MIPS的峰值速度,集成了最多的片上資源。其128 KB的片上Flash和8 KB的片上RAM足以滿(mǎn)足絕大多數應用的需求。使用C8051F12X,只需外加為數不多的驅動(dòng)和接口,就可構成較大型的完整系統。只是其中128 KB的Flash存儲器不可避免地要處理bank分區問(wèn)題。 幸運的是Keil C51開(kāi)發(fā)環(huán)境對C8051F系列有良好的支持,包括一般的跨bank分區的程序跳轉和調用。作為數據存儲器使用時(shí),Flash的分區讀寫(xiě)完全是編程者要考慮的事情,與開(kāi)發(fā)環(huán)境無(wú)關(guān)。本文只針對特殊的強制轉移和μC/OS—II在多bank分區中的移植問(wèn)題展開(kāi)討論。 1 C8051F12X在Keil C51中的多bank分區轉移機制 Keil C51的連接定位器支持分組連接,允許生成代碼長(cháng)度大于64 KB的8051目標程序_1_。一般的8051系統只提供16根地址線(xiàn),需要附加地址線(xiàn)來(lái)實(shí)現代碼分組切換,而編譯器產(chǎn)生bank切換代碼時(shí)受到配置文件L51_BANK.A51的支持,所以用戶(hù)必須根據自己的硬件結構來(lái)修改這個(gè)配置文件。 C8051F12X系列不用考慮硬件部分,也不存在地址線(xiàn)的擴展問(wèn)題,因為128 KB的4個(gè)bank區全部都在CPU內部,所以作為常規跨bank的跳轉和調用,不需要處理L51_BANK.A51配置文件。但在特殊情況下就必須考慮該問(wèn)題,否則程序將無(wú)法工作。下面以C8051F120為例先討論代碼的透明分組切換過(guò)程。 C805IF120在Keil C51的項目配置中被劃分為4個(gè)bank,每個(gè)32 KB。公共bank地址從0"0x7fff,其余bank從0x8000h"0xffff。在對應的配置文件L51_BANK.A51中,涉及到特殊功能寄存器PSBANK(SFR地址:0B1H)、SWITCHn宏、B_BANKn、?B_SWITCHn分組信息保存和切換代碼,以及?B_CURENTBANK變量。 PSBANK為C8051F120內的特殊功能寄存器,128KB Flash的分bank訪(fǎng)問(wèn)就是通過(guò)它來(lái)實(shí)現的。要想轉移到新的bank中去,必須賦予PSBANK正確的值,然后再轉向bank區內地址即可。 SWITCHn宏共有4個(gè),分別是SwITCH0、SWITCH1、SWITCH2和SWITCH3,對應切換到4個(gè)bank中。其中SWITCH0對應的語(yǔ)句為: MOV PSBANK.#00h ;把00h用1Ih、22h和33h替換;就是其他三個(gè)宏它將插入到?B_SWITCHn代碼中,用來(lái)切換新的bank和恢復到原來(lái)的bank。 所有4組?B_BANKn和?B_SWITCHn代碼也都是用宏實(shí)現的,對應4個(gè)bank處理。它們匯集在?BANK?SWITCH代碼段中,整個(gè)bank切換及恢復機制非常巧妙,可以實(shí)現任意bank之間函數的相互調用及嵌套。下面以bank3區中的main函數調用bank1區的Delay_noOS()延時(shí)函數為例說(shuō)明該機制。 void main(void){ MCUInit(); //初始化CPU Delay_n00s(10); //延時(shí)lO ms Lcmlnition(); bank3中被調用的函數Delay_noOS(10)對應的匯編語(yǔ)句為: LCALL C:5049 公共段(即Common段,對應bank0)中C:5049處的匯編語(yǔ)句如下: MOV dptr,#Delay_noOS AJMP B_BANK1 這里的B_BANK1就是宏?B_BANK&N中N為1的例程,F在進(jìn)入問(wèn)題的核心:全部的跨bank區程序切換及恢復過(guò)程依靠公共段中?BANK?SWITCH代碼段里的以下匯編代碼實(shí)現,對應的N為0、1、2和3。?BANK?SWITCH SEGMENT CODE PAGE ; ?B_BANK&N: PUSH ?B_CURRENTBANK (1) MOV A,#HIGH?BANK?SWITCH (2) PUSH ACC (3) PUSH DPL (4) PUSH DPH (5) ?B_SWITCH&N: MOV ?B_CURRENTBANK,#LOW? B_SWITCH&N (6) SWITCH&N (7) RET (8) : Delay_noOS(10)函數的返回地址,即函數LcmIni-tion()的入口地址(也在bank3中),其高低位字節表示為ADDH和ADDL。程序進(jìn)入main()后的?B_CURRENTBANK變量初值是?B_SWITCH3的低8位,其意義稍后敘述。AJMP B_BANK1后程序執行?B_BANK1和?B_SWITCH1的(1)~(8),執行到(5)時(shí)的堆棧結構如圖1所示。 繼續執行?B_SWITCH1到(7)時(shí),PSBANK變?yōu)橹赶騜ank1,?B_CURRENTBANK變?yōu)?B_SWITCH1的低8位。執行(8)后,從堆棧結構可以看出,堆棧彈出①作為新的PC值,程序進(jìn)入Delay_noOS(10)函數,延時(shí)功能完成后,函數最后一條RET指令開(kāi)始返回。這是Keil C51處理bank機制的關(guān)鍵,此時(shí)的返回地址為堆棧中的②,此地址即?B_SWITCH&H代碼的入口,這里對應main()函數所在的bank3分組,也就是?B_SWITCH3的人口。 因為所有?B_SWITCH&N的高8位地址,即?BANK?SWITCH代碼段的高8位都一樣,由語(yǔ)句(2)中的操作符HIGH?BANK?SWITCH確定;低8位保存在已經(jīng)壓棧的?B_CURRENTBANK變量中,此時(shí)堆棧中的?B_CURRENTBANK壓棧值是?B_SWITCH3的低8位,這樣②的地址就是?B_SWITCH3。 程序繼續執行?B_SWITCH3,在執行?B_SWITCH3的(6)語(yǔ)句之前,?B_CURRENTBANK還是前面執行?B_SWITCH1時(shí)的值,即?B_SWITCH1的低8位。執行語(yǔ)句(6)后,?B_CURRENTBANK恢復為?B_SWITCH3的低8位,為返回main函數做準備。然后PSBANK置為33h,即指向bank3,接著(zhù)執行RET語(yǔ)句,堆棧③成為RET的返回地址,程序回到了main()中Delay_noOS(10)的下一條語(yǔ)句繼續執行,?B_CURRENTBANK也已恢復。 這個(gè)調用過(guò)程中,用了6個(gè)堆棧字節,3條RET指令,關(guān)鍵內容就是?B_CURRENTBANK變量,它保存了可以恢復調用前bank環(huán)境代碼的地址低位。從被調用函數返回 到這個(gè)地址后,就能恢復調用前的bank環(huán)境,即賦予PSBANK正確的值。 不采用直接保存PSBANK值然后再恢復,而是用壓棧的方式保存了相關(guān)地址(語(yǔ)句(1)~(3)),是為了實(shí)現跨bank區的嵌套調用。例如,在Delay_noOS(10)函數中,如果再次跨bank去調用新函數,會(huì )再次重復上述過(guò)程,堆棧從②往上再長(cháng)6個(gè)字節。Delay_noOS(10)函數之前執行?B_SWITCHI產(chǎn)生的?B_CURRENTBANK值(?B_SWITCHI的低8位)也會(huì )進(jìn)棧,為調用完新函數后返回到bank1繼續執行Delay_noOS(10)提供保證。 2 無(wú)操作系統bank分區間的強制跳轉 通過(guò)上面的分析得知,如果要處理跨bank區的跳轉、調用和返回,關(guān)鍵是能正確處理好PSBANK中的內容。當程序沒(méi)有操作系統用于任務(wù)切換,而又需要強制退出某一函數進(jìn)入到另一函數的某一地址時(shí),比如說(shuō)在中斷發(fā)生后,結束原來(lái)的工作轉入到另一工作去,就需要處理好PSBANK。 如果不考慮bank,可以在轉入新地址之前執行一段代碼,保存該地址處的環(huán)境變量,包括堆棧指針sP和需要的入口地址。然后在中斷返回之前,恢復此環(huán)境變量,執行中斷返回指令進(jìn)入該新地址。這個(gè)思路和C51庫函數setjump和longjump比較相近,但比它們靈活,因為環(huán)境變量可以自己處理。 考慮bank后的情況稍微復雜些,環(huán)境變量中需增加bank的處理信息,那么只處理PSBANK行不行呢? 如果僅保存和恢復PSBANK,則很簡(jiǎn)單,在保存環(huán)境變量的程序中加入: JMPEnv[envl]=PSBANK; 在恢復環(huán)境變量的程序中加入: PSBANK=JMPEnv[envl]; 這里環(huán)境變量是二維數組JMPEnv,envl代表一個(gè)環(huán)境變量,即一個(gè)返回點(diǎn)。第二維是變量中的參數個(gè)數。因此可以保存多個(gè)環(huán)境變量以供使用。 初看起來(lái)這樣處理是沒(méi)有問(wèn)題的,可實(shí)際上不行。因為進(jìn)入返回點(diǎn)后,雖然PSBANK正確了,但是?B_CUR-RENTBANK可能已經(jīng)被修改,不能和返回點(diǎn)程序的bank區匹配,如果再次出現跨bank調用的話(huà)將不能正確返回。 處理方法是有點(diǎn)技巧的,因為C語(yǔ)言不支持匯編變量?B_CURRENTBANK的寫(xiě)法,所以在L51_bank.A51中要加上聲明: PUBLIC BLCURRENTBANK 和偽指令: B_CURRENTBANK EQU ?B_CURRENTBANK 這樣就可以在C程序中使用B_CURRENTBANK了,先聲明B_CURRENTBANK: extern Uchar data B_CURRENTBANK; 然后在保存環(huán)境變量程序中加入: JMPEnv[envl]=PSBANK; JMPEnv[envl]=B_CURRENTBANK; 恢復環(huán)境變量程序中加入: PSBANK=JMPEnv[envl]; B_CURRENTBANK=JMPEnv[envl]; 這樣恢復環(huán)境變量進(jìn)入到新程序后,也將恢復該程序對應的正確?B_cuRRENTBANK值,問(wèn)題得到解決。 3 no/0S-ll移植中的bank分區處理 μC/OS-II的51版本已經(jīng)很成熟,但是所有移植版本均未處理bank問(wèn)題,需要增加該內容,否則不能在包括C8051F12X系列及其他多bank程序中使用。 如前所述,Keil C51提供對跨bank調用的透明切換支持,但在使用操作系統時(shí),這種透明切換機制還需要提供對任務(wù)切換的支持。因為任務(wù)的切換,程序可能需要到別的代碼分組中去運行,而此時(shí)PSBANK和?B_CUR-RENTBANK還停留在原來(lái)代碼分組中的狀態(tài),將導致程序崩潰。顯然,無(wú)論由于什么情況導致的任務(wù)切換完成之前,都需要保存和恢復PSBANK和?B_CURRENT-BANK的值。解決的辦法是在每次任務(wù)切換前將PS-BANK和?B_CURRENTBANK壓入用戶(hù)任務(wù)棧。 按照μC/OS-II的要求,在任務(wù)創(chuàng )建時(shí),任務(wù)棧必須初始化成像運行中的任務(wù)剛剛發(fā)生過(guò)中斷一樣嘲。?B_CURRENTBANK的初始值取決于該任務(wù)所在分組對應的切換代碼段的低8位地址。所以,任務(wù)堆棧的初始化函數OSTaskStkInit需要加入一個(gè)參數INT8U bank,指明該任務(wù)位于哪個(gè)代碼分組中。又由于任務(wù)堆棧的初始化函數是被任務(wù)創(chuàng )建函數OSTaskCreate調用的,所以該函數一樣需要加入參數INT8U bank。 在壓棧,出棧宏中需要加入: PUSH PSBANK PUSH?B_CURRENTBANK : POP ?B_CURRENTBANK POP PSBANK 在任務(wù)堆棧的初始化函數OSTaskStkInit中需要加入: *stk++=17; //堆棧長(cháng)度增加2個(gè)到17 ; if(bank==0x22:){ //bank2 *stk++=bank; *stk++=CurrentBank2(); else if(bank==0x33){ //bank3 *stk++=bank; *stk++=CurrentBank3(); } else{ //bank1和common *stk++=0xll; //PSBANK *stk++=CurrentBank1(); ) 其中,bank0用任何的PSBANK值均沒(méi)有問(wèn)題,所以簡(jiǎn)化了PSBANK取值0x00的情況。 函數INT8U CurrentBank1(void),INT8U Current-Bank2(void)和INT8U CurrentBank3(void)是用匯編語(yǔ)言實(shí)現的,返回值通過(guò)R7傳遞,目的是獲得該任務(wù)所在分組對應切換代碼段(?SWITCHn)的低8位地址。不用C語(yǔ)言編寫(xiě)的原因同樣是?B_SWITCH&N不被C支持。 CurrentBank1(void)代碼如下,其他兩個(gè)類(lèi)同。 RSEG?PR?CurrentBank1?Os_CPU_A CurrentBank1: MOV DPTR,#?B_SWITCH1 MOV R7.DPL RET 結 語(yǔ) 本文介紹了Keil C51實(shí)現大于64 KB程序的bank分組代碼切換機制的原理,提出了沒(méi)有操作系統情況下非正常轉移時(shí)bank的處理方法以及μc/os—II操作系統在多bank分區程序移植中應采取的措施,在開(kāi)發(fā)實(shí)例中均得到了很好的應用。 |