《電子技術應用》
您所在的位置:首頁 > 通信與網(wǎng)絡 > 設計應用 > C8051 F12X中多bank的分區(qū)跳轉問題處理
C8051 F12X中多bank的分區(qū)跳轉問題處理
OFweek電子工程網(wǎng)
摘要: 本文介紹了KeilC51實現(xiàn)大于64KB程序的bank分組代碼切換機制的原理,提出了沒有操作系統(tǒng)情況下非正常轉移時bank的處理方法以及μc/os—II操作系統(tǒng)在多bank分區(qū)程序移植中應采取的措施,在開發(fā)實例中均得到了很好的應用,。
Abstract:
Key words :

  在8051核單片機龐大的家族中,,C8051F系列作為其中的后起之秀,是目前功能最全,、速度最快的8051衍生單片機之一,,正得到越來越廣泛的應用。它集成了嵌入式系統(tǒng)的許多先進技術,,有豐富的模擬和數(shù)字資源.是一個完全意義上的SoC產(chǎn)品,。

  C805IFl2X作為該系列中的高端部分,具有最快100MIPS的峰值速度,,集成了最多的片上資源,。其128 KB的片上Flash和8 KB的片上RAM足以滿足絕大多數(shù)應用的需求。使用C8051F12X,,只需外加為數(shù)不多的驅動和接口,,就可構成較大型的完整系統(tǒng)。只是其中128 KB的Flash存儲器不可避免地要處理bank分區(qū)問題,。

  幸運的是Keil C51開發(fā)環(huán)境對C8051F系列有良好的支持,,包括一般的跨bank分區(qū)的程序跳轉和調用。作為數(shù)據(jù)存儲器使用時,,F(xiàn)lash的分區(qū)讀寫完全是編程者要考慮的事情,,與開發(fā)環(huán)境無關。本文只針對特殊的強制轉移和μC/OS—II在多bank分區(qū)中的移植問題展開討論,。

  1 C8051F12X在Keil C51中的多bank分區(qū)轉移機制

  Keil C51的連接定位器支持分組連接,,允許生成代碼長度大于64 KB的8051目標程序_1_。一般的8051系統(tǒng)只提供16根地址線,,需要附加地址線來實現(xiàn)代碼分組切換,,而編譯器產(chǎn)生bank切換代碼時受到配置文件L51_BANK.A51的支持,所以用戶必須根據(jù)自己的硬件結構來修改這個配置文件,。

  C8051F12X系列不用考慮硬件部分,,也不存在地址線的擴展問題,因為128 KB的4個bank區(qū)全部都在CPU內部,所以作為常規(guī)跨bank的跳轉和調用,,不需要處理1.5l_BANK.A51配置文件,。但在特殊情況下就必須考慮該問題,否則程序將無法工作,。下面以C8051F120為例先討論代碼的透明分組切換過程,。

  C805IFl20在Keil C51的項目配置中被劃分為4個bank,每個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訪問就是通過它來實現(xiàn)的,。要想轉移到新的bank中去,,必須賦予PSBANK正確的值,然后再轉向bank區(qū)內地址即可,。

  SWITCHn宏共有4個,,分別是SwITCH0、SWlTCH1,、SWITCH2和SWITCH3,,對應切換到4個bank中。其中SWITCH0對應的語句為:

  MOV PSBANK.#00h ;把00h用1Ih,、22h和33h替換,,

  ;就是其他三個宏

  它將插入到?B_SWITCHn代碼中,,用來切換新的bank和恢復到原來的bank,。

  所有4組?B_BANKn和,?B_SWlTCHn代碼也都是用宏實現(xiàn)的,,對應4個bank處理。它們匯集在,?BANK?SWITCH代碼段中,整個bank切換及恢復機制非常巧妙,,可以實現(xiàn)任意bank之間函數(shù)的相互調用及嵌套,。下面以bank3區(qū)中的main函數(shù)調用bankl區(qū)的Delay_noOS()延時函數(shù)為例說明該機制。

  void main(void){

  MCUInit(); //初始化CPU

  Delay_n00s(10); //延時lO ms

  Lcmlnition();

 ?。?/p>

 ?。?/p>

  bank3中被調用的函數(shù)Delay_noOS(10)對應的匯編語句為:

  LCALL C:5049

  公共段(即Common段,對應bank0)中C:5049處的

  匯編語句如下:

  MOV dptr,,#Delay_noOS

  AJMP B_BANKl

  這里的B_BANKl就是宏,?B_BANK&N中N為1的例程。現(xiàn)在進入問題的核心:全部的跨bank區(qū)程序切換及恢復過程依靠公共段中,?BANK,?SWITCH代碼段里的以下匯編代碼實現(xiàn),對應的N為0,、1,、2和3。,?BANK,?SWlTCH 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

 ?。?)

  SWlTCH&N (7)

  RET (8)

  :

  Delay_noOS(10)函數(shù)的返回地址,,即函數(shù)LcmIni-tion()的入口地址(也在bank3中),,其高低位字節(jié)表示為ADDH和ADDL。程序進入main()后的,?B_CURRENTBANK變量初值是,?B_SWITCH3的低8位,其意義稍后敘述,。AJMP B_BANKl后程序執(zhí)行,?B_BANKl和?B_SWITCHl的(1)~(8),執(zhí)行到(5)時的堆棧結構如圖1所示,。

  

  繼續(xù)執(zhí)行,?B_SWITCHl到(7)時,PSBANK變?yōu)橹赶騜ankl,,,?B_CURRENTBANK變?yōu)椋緽_SWITCHl的低8位,。執(zhí)行(8)后,,從堆棧結構可以看出,堆棧彈出①作為新的PC值,,程序進入Delay_noOS(10)函數(shù),,延時功能完成后,函數(shù)最后一條RET指令開始返回,。這是Keil C51處理bank機制的關鍵,,此時的返回地址為堆棧中的②,此地址即,?B_SWITCH&H代碼的入口,,這里對應main()函數(shù)所在的bank3分組,也就是,?B_SWITCH3的人口,。

  因為所有?B_SWITCH&N的高8位地址,,即,?BANK?SWITCH代碼段的高8位都一樣,,由語句(2)中的操作符HIGH,?BANK?SWITCH確定;低8位保存在已經(jīng)壓棧的,?B_CURRENTBANK變量中,,此時堆棧中的?B_CURRENTBANK壓棧值是,?B_SWITCH3的低8位,,這樣②的地址就是?B_SWITCH3,。

  程序繼續(xù)執(zhí)行,?B_SWITCH3,在執(zhí)行,?B_SWITCH3的(6)語句之前,,,?B_CURRENTBANK還是前面執(zhí)行?B_SWITCHl時的值,,即,?B_SWITCHl的低8位。執(zhí)行語句(6)后,,?B_CURRENTBANK恢復為,?B_SWITCH3的低8位,,為返回main函數(shù)做準備。然后PSBANK置為33h,,即指向bank3,,接著執(zhí)行RET語句,堆棧③成為RET的返回地址,,程序回到了main()中Delay_noOS(10)的下一條語句繼續(xù)執(zhí)行,,?B_CURRENTBANK也已恢復,。

  這個調用過程中,,用了6個堆棧字節(jié),3條RET指令,,關鍵內容就是,?B_CURRENTBANK變量,它保存了可以恢復調用前bank環(huán)境代碼的地址低位,。從被調用函數(shù)返回 到這個地址后,,就能恢復調用前的bank環(huán)境,即賦予PSBANK正確的值,。

  不采用直接保存PSBANK值然后再恢復,,而是用壓棧的方式保存了相關地址(語句(1)~(3)),是為了實現(xiàn)跨bank區(qū)的嵌套調用,。例如,,在Delay_noOS(10)函數(shù)中,如果再次跨bank去調用新函數(shù),,會再次重復上述過程,,堆棧從②往上再長6個字節(jié)。Delay_noOS(10)函數(shù)之前執(zhí)行,?B_SWITCHI產(chǎn)生的,?B_CURRENTBANK值(?B_SWITCHI的低8位)也會進棧,,為調用完新函數(shù)后返回到bankl繼續(xù)執(zhí)行Delay_noOS(10)提供保證,。

  2 無操作系統(tǒng)bank分區(qū)間的強制跳轉

  通過上面的分析得知,,如果要處理跨bank區(qū)的跳轉、調用和返回,,關鍵是能正確處理好PSBANK中的內容,。當程序沒有操作系統(tǒng)用于任務切換,而又需要強制退出某一函數(shù)進入到另一函數(shù)的某一地址時,,比如說在中斷發(fā)生后,,結束原來的工作轉入到另一工作去,就需要處理好PSBANK,。

  如果不考慮bank,,可以在轉入新地址之前執(zhí)行一段代碼,保存該地址處的環(huán)境變量[2],,包括堆棧指針sP和需要的入口地址,。然后在中斷返回之前,恢復此環(huán)境變量,,執(zhí)行中斷返回指令進入該新地址,。這個思路和C51庫函數(shù)setjump和longjump比較相近,但比它們靈活,,因為環(huán)境變量可以自己處理,。

  考慮bank后的情況稍微復雜些,環(huán)境變量中需增加bank的處理信息,,那么只處理PSBANK行不行呢,?

  如果僅保存和恢復PSBANK,則很簡單,,在保存環(huán)境變量的程序中加入:

  JMPEnv[envl][3]=PSBANK;

  在恢復環(huán)境變量的程序中加入:

  PSBANK=JMPEnv[envl][3];

  這里環(huán)境變量是二維數(shù)組JMPEnv,,envl代表一個環(huán)境變量,即一個返回點,。第二維是變量中的參數(shù)個數(shù),。因此可以保存多個環(huán)境變量以供使用。

  初看起來這樣處理是沒有問題的,,可實際上不行,。因為進入返回點后,雖然PSBANK正確了,,但是,?B_CUR-RENTBANK可能已經(jīng)被修改,不能和返回點程序的bank區(qū)匹配,,如果再次出現(xiàn)跨bank調用的話將不能正確返回,。

  處理方法是有點技巧的,因為C語言不支持匯編變量,?B_CURRENTBANK的寫法,,所以在L51_bank.A51中要加上聲明:

  PUBLIC BLCURRENTBANK

  和偽指令:

  B_CURRENTBANK EQU ,?B_CURRENTBANK

  這樣就可以在C程序中使用B_CURRENTBANK

  了,先聲明B_CURRENTBANK:

  extern Uchar data B_CURRENTBANK;

  然后在保存環(huán)境變量程序中加入:

  JMPEnv[envl][3]=PSBANK;

  JMPEnv[envl][4]=B_CURRENTBANK;

  恢復環(huán)境變量程序中加入:

  PSBANK=JMPEnv[envl][3];

  B_CURRENTBANK=JMPEnv[envl][4];

  這樣恢復環(huán)境變量進入到新程序后,,也將恢復該程序對應的正確,?B_cuRRENTBANK值,問題得到解決,。

 

  3 no/0S-ll移植中的bank分區(qū)處理

 

  μC/OS-II的51版本已經(jīng)很成熟,,但是所有移植版本均未處理bank問題,需要增加該內容,,否則不能在包括C8051F12X系列及其他多bank程序中使用,。

  如前所述,Keil C51提供對跨bank調用的透明切換支持,,但在使用操作系統(tǒng)時,這種透明切換機制還需要提供對任務切換的支持,。因為任務的切換,,程序可能需要到別的代碼分組中去運行,而此時PSBANK和,?B_CUR-RENTBANK還停留在原來代碼分組中的狀態(tài),,將導致程序崩潰。顯然,,無論由于什么情況導致的任務切換完成之前,,都需要保存和恢復PSBANK和?B_CURRENT-BANK的值,。解決的辦法是在每次任務切換前將PS-BANK和,?B_CURRENTBANK壓入用戶任務棧。

  按照μC/OS-II的要求,,在任務創(chuàng)建時,,任務棧必須初始化成像運行中的任務剛剛發(fā)生過中斷一樣嘲。,?B_CURRENTBANK的初始值取決于該任務所在分組對應的切換代碼段的低8位地址,。所以,任務堆棧的初始化函數(shù)OSTaskStkInit需要加入一個參數(shù)INT8U bank,,指明該任務位于哪個代碼分組中,。又由于任務堆棧的初始化函數(shù)是被任務創(chuàng)建函數(shù)OSTaskCreate調用的,所以該函數(shù)一樣需要加入?yún)?shù)INT8U bank,。

  在壓棧,,出棧宏中需要加入:

  PUSH PSBANK

  PUSH?B_CURRENTBANK

 ?。?/p>

  POP ,?B_CURRENTBANK

  POP PSBANK

  在任務堆棧的初始化函數(shù)OSTaskStkInit中需要加入:

  *stk++=17; //堆棧長度增加2個到17

  ;

  if(bank==0x22:){ //bank2

  *stk++=bank;

  *stk++=CurrentBank2();

  else if(bank==0x33){ //bank3

  *stk++=bank;

  *stk++=CurrentBank3();

  }

  else{ //bankl和common

  *stk++=0xll; //PSBANK

  *stk++=CurrentBankl();

 ?。?/p>

  其中,bank0用任何的PSBANK值均沒有問題,,所以簡化了PSBANK取值0x00的情況,。

  函數(shù)INT8U CurrentBankl(void),INT8U Current-Bank2(void)和INT8U CurrentBank3(void)是用匯編語言實現(xiàn)的,,返回值通過R7傳遞,,目的是獲得該任務所在分組對應切換代碼段(?SWITCHn)的低8位地址,。不用C語言編寫的原因同樣是,?B_SWITCH&N不被C支持。

  CurrentBankl(void)代碼如下,,其他兩個類同,。

  RSEG?PR,?CurrentBankl,?Os_CPU_A

  CurrentBankl:

  MOV DPTR,#,?B_SWITCHl

  MOV R7.DPL

  RET

  結 語

  本文介紹了Keil C51實現(xiàn)大于64 KB程序的bank分組代碼切換機制的原理,,提出了沒有操作系統(tǒng)情況下非正常轉移時bank的處理方法以及μc/os—II操作系統(tǒng)在多bank分區(qū)程序移植中應采取的措施,在開發(fā)實例中均得到了很好的應用,。

此內容為AET網(wǎng)站原創(chuàng),,未經(jīng)授權禁止轉載。