电子园51单片机学习网'\4{+Y-O5MnA
1 C8051F12X在Keil C51中的多bank分区转移机制
C:t!^h$m"II44606 Keil C51的连接定位器支持分组连接,允许生成代码长度大于64 KB的8051目标程序_1_。一般的8051系统只提供16根地址线,需要附加地址线来实现代码分组切换,而编译器产生bank切换代码时受到配置文件L51_BANK.A51的支持,所以用户必须根据自己的硬件结构来修改这个配置文件。
/IS y*eK|l44606电子园51单片机学习网:@:|yrvk#daf'NS
C8051F12X系列不用考虑硬件部分,也不存在地址线的扩展问题,因为128 KB的4个bank区全部都在CPU内部,所以作为常规跨bank的跳转和调用,不需要处理1.5l_BANK.A51配置文件。但在特殊情况下就必须考虑该问题,否则程序将无法工作。下面以C8051F120为例先讨论代码的透明分组切换过程。
0EDW!Gm44606
J(Hmh'Q44606 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变量。电子园51单片机学习网 RFN&MF,Ay5p%e q
电子园51单片机学习网vx4HMN7|B
PSBANK为C8051F120内的特殊功能寄存器,128KB Flash的分bank访问就是通过它来实现的。要想转移到新的bank中去,必须赋予PSBANK正确的值,然后再转向bank区内地址即可。
&xi,guOr/N,E44606电子园51单片机学习网&dp3}P~/S,oz.h+E
SWITCHn宏共有4个,分别是SwITCH0、SWlTCH1、SWITCH2和SWITCH3,对应切换到4个bank中。其中SWITCH0对应的语句为:电子园51单片机学习网*[1B C#MPX0JF.t!}o.q
MOV PSBANK.#00h ;把00h用1Ih、22h和33h替换,
;就是其他三个宏
J.w_\'g @p i q44606
6q-e#`/k'c0Z!e44606 它将插入到?B_SWITCHn代码中,用来切换新的bank和恢复到原来的bank。电子园51单片机学习网+L+wf)d vl,g y
ZJ8qa4XE"}v44606 所有4组?B_BANKn和?B_SWlTCHn代码也都是用宏实现的,对应4个bank处理。它们汇集在?BANK?SWITCH代码段中,整个bank切换及恢复机制非常巧妙,可以实现任意bank之间函数的相互调用及嵌套。下面以bank3区中的main函数调用bankl区的Delay_noOS()延时函数为例说明该机制。
~;Y w}0L44606void main(void){电子园51单片机学习网)pI V"yvj?L
MCUInit(); //初始化CPU
!D^a8A*_#P1_44606Delay_n00s(10); //延时lO ms
Zl4i"c~ g44606Lcmlnition();电子园51单片机学习网3`,Cg9_4^Q!X]3a
:
N6pHP8nd+]?44606:电子园51单片机学习网P'U;S ^7Yi-i
bank3中被调用的函数Delay_noOS(10)对应的汇编语句为:
T%ScI0H;d&iaH _44606LCALL C:5049电子园51单片机学习网|f;zBV6L7xu
公共段(即Common段,对应bank0)中C:5049处的
*]K5E \#@B2H44606
!s8zo Ar;S8z44606汇编语句如下:
xn"caz44606MOV dptr,#Delay_noOS
,~A;P1R#Q44606AJMP B_BANKl电子园51单片机学习网%BWK HH|x
+^2n]JG*|44606 这里的B_BANKl就是宏?B_BANK&N中N为1的例程。现在进入问题的核心:全部的跨bank区程序切换及恢复过程依靠公共段中?BANK?SWITCH代码段里的以下汇编代码实现,对应的N为0、1、2和3。?BANK?SWlTCH SEGMENT CODE PAGE
u/Dzg'x)B6S8Xmp44606;电子园51单片机学习网"gCOA}{4F;U
?B_BANK&N:电子园51单片机学习网{d]N|
PUSH ?B_CURRENTBANK (1)
Q5J ik p44606MOV A,#HIGH?BANK?SWITCH (2)
fG8_e&^.jd44606PUSH ACC (3)电子园51单片机学习网+eEk`#?_$I
PUSH DPL (4)
UP`;I Z i a44606PUSH DPH (5)电子园51单片机学习网(R{SO[8@:Q}q
?B_SWITCH&N:
6^ C}H(oY K8N*Y;u44606MOV ?B_CURRENTBANK,#LOW? B_SWITCH&N电子园51单片机学习网(?#Vsq;wb
(6)
!S$rY5i:h44606SWlTCH&N (7)电子园51单片机学习网NE c.c [xG
RET (8)
T!~7x1l/L9H"s`-a)X44606:电子园51单片机学习网.}&v ufPb/a*z9A
Delay_noOS(10)函数的返回地址,即函数LcmIni-tion()的入口地址(也在bank3中),其高低位字节表示为ADDH和ADDL。程序进入main()后的?B_CURRENTBANK变量初值是?B_SWITCH3的低8位,其意义稍后叙述。AJMP B_BANKl后程序执行?B_BANKl和?B_SWITCHl的(1)~(8),执行到(5)时的堆栈结构如图1所示。电子园51单片机学习网7o2wQh$ui6g
电子园51单片机学习网c1r6_DI6P+x0P\
2P`7aEUo.Jn {44606 继续执行?B_SWITCHl到(7)时,PSBANK变为指向bankl,?B_CURRENTBANK变为?B_SWITCHl的低8位。执行(8)后,从堆栈结构可以看出,堆栈弹出①作为新的PC值,程序进入Delay_noOS(10)函数,延时功能完成后,函数最后一条RET指令开始返回。这是Keil C51处理bank机制的关键,此时的返回地址为堆栈中的②,此地址即?B_SWITCH&H代码的入口,这里对应main()函数所在的bank3分组,也就是?B_SWITCH3的人口。电子园51单片机学习网J:@ G*ta"u*eZ+U
~I+Vxy#ko[Rf44606 因为所有?B_SWITCH&N的高8位地址,即?BANK?SWITCH代码段的高8位都一样,由语句(2)中的操作符HIGH?BANK?SWITCH确定;低8位保存在已经压栈的?B_CURRENTBANK变量中,此时堆栈中的?B_CURRENTBANK压栈值是?B_SWITCH3的低8位,这样②的地址就是?B_SWITCH3。电子园51单片机学习网;e ?#I L-v9S@ q
电子园51单片机学习网v4|%@ M1m%xf
程序继续执行?B_SWITCH3,在执行?B_SWITCH3的(6)语句之前,?B_CURRENTBANK还是前面执行?B_SWITCHl时的值,即?B_SWITCHl的低8位。执行语句(6)后,?B_CURRENTBANK恢复为?B_SWITCH3的低8位,为返回main函数做准备。然后PSBANK置为33h,即指向bank3,接着执行RET语句,堆栈③成为RET的返回地址,程序回到了main()中Delay_noOS(10)的下一条语句继续执行,?B_CURRENTBANK也已恢复。
%Z)d V vK"}/S_+V44606电子园51单片机学习网X'|B` o+k!\k4^ j
这个调用过程中,用了6个堆栈字节,3条RET指令,关键内容就是?B_CURRENTBANK变量,它保存了可以恢复调用前bank环境代码的地址低位。从被调用函数返回 到这个地址后,就能恢复调用前的bank环境,即赋予PSBANK正确的值。
e^-r8Eg44606
A6T2iB2m44606 不采用直接保存PSBANK值然后再恢复,而是用压栈的方式保存了相关地址(语句(1)~(3)),是为了实现跨bank区的嵌套调用。例如,在Delay_noOS(10)函数中,如果再次跨bank去调用新函数,会再次重复上述过程,堆栈从②往上再长6个字节。Delay_noOS(10)函数之前执行?B_SWITCHI产生的?B_CURRENTBANK值(?B_SWITCHI的低8位)也会进栈,为调用完新函数后返回到bankl继续执行Delay_noOS(10)提供保证。
+B |^t;J,~3v;C4gw446062 无操作系统bank分区间的强制跳转
B,X`I)? iW44606 通过上面的分析得知,如果要处理跨bank区的跳转、调用和返回,关键是能正确处理好PSBANK中的内容。当程序没有操作系统用于任务切换,而又需要强制退出某一函数进入到另一函数的某一地址时,比如说在中断发生后,结束原来的工作转入到另一工作去,就需要处理好PSBANK。
$UyMqO44606 电子园51单片机学习网L5ff }CYS
如果不考虑bank,可以在转入新地址之前执行一段代码,保存该地址处的环境变量[2],包括堆栈指针sP和需要的入口地址。然后在中断返回之前,恢复此环境变量,执行中断返回指令进入该新地址。这个思路和C51库函数setjump和longjump比较相近,但比它们灵活,因为环境变量可以自己处理。
2{|C)P7Nfe$v44606
v)t$d9GhFVb44606 考虑bank后的情况稍微复杂些,环境变量中需增加bank的处理信息,那么只处理PSBANK行不行呢?电子园51单片机学习网2|+]$z(KlM D1W
电子园51单片机学习网v.hUx?R$R D
如果仅保存和恢复PSBANK,则很简单,在保存环境变量的程序中加入:
mFfm(uR;U44606JMPEnv[envl][3]=PSBANK;
3U6}` cFV2v44606电子园51单片机学习网 z F"Ci8l#{?Rm
在恢复环境变量的程序中加入:
%n\e8w1ji%N;L44606PSBANK=JMPEnv[envl][3];电子园51单片机学习网Pb8[dP*H
电子园51单片机学习网x9y+Xm/U ihd&l
这里环境变量是二维数组JMPEnv,envl代表一个环境变量,即一个返回点。第二维是变量中的参数个数。因此可以保存多个环境变量以供使用。电子园51单片机学习网 G!V]} C?5Gx0w
rwe l[0[,u44606 初看起来这样处理是没有问题的,可实际上不行。因为进入返回点后,虽然PSBANK正确了,但是?B_CUR-RENTBANK可能已经被修改,不能和返回点程序的bank区匹配,如果再次出现跨bank调用的话将不能正确返回。电子园51单片机学习网.Ip.p }\~]9] ~/~
3{)@"z)i*~-l[l44606 处理方法是有点技巧的,因为C语言不支持汇编变量?B_CURRENTBANK的写法,所以在L51_bank.A51中要加上声明:
5`J{2{Z.l44606PUBLIC BLCURRENTBANK电子园51单片机学习网Z'z2[Cf$gO c
和伪指令:电子园51单片机学习网)xj OOnM5~
B_CURRENTBANK EQU ?B_CURRENTBANK电子园51单片机学习网C9R qEi8r@
这样就可以在C程序中使用B_CURRENTBANK
? ].O6C,? z$M44606了,先声明B_CURRENTBANK:
^/yy `/n1Xwm/x%S44606extern Uchar data B_CURRENTBANK;电子园51单片机学习网!fy hoA
然后在保存环境变量程序中加入:电子园51单片机学习网~ q+FT#a'f gr.R
JMPEnv[envl][3]=PSBANK;电子园51单片机学习网$`7X;K%x'i%LM(^5T
JMPEnv[envl][4]=B_CURRENTBANK;
h8j!iq1jD44606恢复环境变量程序中加入:电子园51单片机学习网(IGu/Pe _E6o
PSBANK=JMPEnv[envl][3];电子园51单片机学习网3^A!][&q d)F
B_CURRENTBANK=JMPEnv[envl][4];电子园51单片机学习网X{9LQ%XD}EAe2DL
这样恢复环境变量进入到新程序后,也将恢复该程序对应的正确?B_cuRRENTBANK值,问题得到解决。
电子园51单片机学习网4{,R+G7c9?#W0c7b
3 no/0S-ll移植中的bank分区处理
6QN5l:R$B&]!D/^(_@44606 μC/OS-II的51版本已经很成熟,但是所有移植版本均未处理bank问题,需要增加该内容,否则不能在包括C8051F12X系列及其他多bank程序中使用。
$y {*e5k/`y44606
v u@^C F44606 如前所述,Keil C51提供对跨bank调用的透明切换支持,但在使用操作系统时,这种透明切换机制还需要提供对任务切换的支持。因为任务的切换,程序可能需要到别的代码分组中去运行,而此时PSBANK和?B_CUR-RENTBANK还停留在原来代码分组中的状态,将导致程序崩溃。显然,无论由于什么情况导致的任务切换完成之前,都需要保存和恢复PSBANK和?B_CURRENT-BANK的值。解决的办法是在每次任务切换前将PS-BANK和?B_CURRENTBANK压入用户任务栈。
0QTUL-G%J!@44606电子园51单片机学习网h$v1H Q&^j},r
按照μC/OS-II的要求,在任务创建时,任务栈必须初始化成像运行中的任务刚刚发生过中断一样嘲。?B_CURRENTBANK的初始值取决于该任务所在分组对应的切换代码段的低8位地址。所以,任务堆栈的初始化函数OSTaskStkInit需要加入一个参数INT8U bank,指明该任务位于哪个代码分组中。又由于任务堆栈的初始化函数是被任务创建函数OSTaskCreate调用的,所以该函数一样需要加入参数INT8U bank。
1|0n7zL(pd!g44606在压栈,出栈宏中需要加入:
Y5jzm[Da4?%?44606PUSH PSBANK电子园51单片机学习网:SC+]&kH`i;c*T
PUSH?B_CURRENTBANK电子园51单片机学习网d;Z*M/K4` ]"e
:电子园51单片机学习网 h;w6k/_ fPm ?R
POP ?B_CURRENTBANK电子园51单片机学习网 ]D {c'l%OM
POP PSBANK电子园51单片机学习网5EkW8P?(K[ oG0_
在任务堆栈的初始化函数OSTaskStkInit中需要加入:电子园51单片机学习网1Y9{_$oy
*stk++=17; //堆栈长度增加2个到17
Pk.^yo&G3[6w44606;电子园51单片机学习网d)l U7f a MG]_w
if(bank==0x22:){ //bank2
j C|2}\44606*stk++=bank;
X,If;Vk\ pJ mC&S44606*stk++=CurrentBank2();电子园51单片机学习网+a9}6G)@!`KN
else if(bank==0x33){ //bank3电子园51单片机学习网7JC#sV9n
*stk++=bank;电子园51单片机学习网 D%tO:LYq `
*stk++=CurrentBank3();
N ytuLD/D44606}
B*nc$DL9I7F+T44606else{ //bankl和common
P.D.b}2s)dzM44606*stk++=0xll; //PSBANK
7Wafm'h9X9fF44606*stk++=CurrentBankl();
T M7?3JG44606)
7vk\ qF d44606电子园51单片机学习网?zJe }e4Q)k:SOLs;K
其中,bank0用任何的PSBANK值均没有问题,所以简化了PSBANK取值0x00的情况。
Pg"l h@44606电子园51单片机学习网J`^`4BU ^
函数INT8U CurrentBankl(void),INT8U Current-Bank2(void)和INT8U CurrentBank3(void)是用汇编语言实现的,返回值通过R7传递,目的是获得该任务所在分组对应切换代码段(?SWITCHn)的低8位地址。不用C语言编写的原因同样是?B_SWITCH&N不被C支持。电子园51单片机学习网0P;u1sH1b.MSJ E sdf
CurrentBankl(void)代码如下,其他两个类同。
1HsB I8d9@F&n44606RSEG?PR?CurrentBankl?Os_CPU_A
.}%`iX,X44606CurrentBankl:
fw)v!m4x!_44606MOV DPTR,#?B_SWITCHl电子园51单片机学习网 b.?1HL9vK.]C
MOV R7.DPL
PI#U@E]#j7Q~44606RET电子园51单片机学习网V `,s {,K)K;nT
&PD8vT+o'O44606结 语
a7d-A-Q!M2o-@44606 本文介绍了Keil C51实现大于64 KB程序的bank分组代码切换机制的原理,提出了没有操作系统情况下非正常转移时bank的处理方法以及μc/os—II操作系统在多bank分区程序移植中应采取的措施,在开发实例中均得到了很好的应用。
文章评论(0条评论)
登录后参与讨论