1 RAM数据的读取方法
EM78系列的RAM既可以认为是普通单片机中的RAM,也可以认为是寄存器(通用和专用寄存器)。以EM78P447为例,RAM共148×8位,其结构如图1所示。
其中地址为20H一3EH,共有124(31×4)个,分布在4个RAM“体”(Bank)上。4个体分别标记为“BankO(体O)”、“Bankl(体1)”、“Bank2(体2)”、“Bank3(体3)”。当程序中用到的变量不多或者数据量不是很大时,这种RAM结构对程序不会有什么影响,即程序变量能够分配在一个Bank中;但是当变量较多时,操作起来将会有些麻烦,同时有可能因为程序中Bank选择不当导致程序出错。笔者觉得当数据量较大时,只要是对数据操作,就要先进行Bank的选择。特别是对子程序而言,一进入子程序就要进行Bank的选择,从而控制所操作数据的范围,时刻要清楚自己操作的数据在哪个Bank内。通过改变“体选码”,即RSR寄存器的最高2位,来选择所要访问的数据Bank。下面给出一个程序来说明。
BS RSR,7 :到Bank2
MOV A,0X2F
AND A,@OB00111111
MOV OX2E,A
CALL LCD _LEAR ;调用显示屏清零程序
LCD CLEAR:
BC RSR,7 ;到Bank0
BC RSR,6
MOVA,@OB00000000
MOV LCd address,A
……
如上面程序所示,在调用LCD_ CLEAR之前访问了Bank2数据区,接着调用LCD-CLEAR子程序。此时在子程序内,若没有注意数据的Bank问题,即没有设置选择BankO,则原本要对BankO的数据操作则会发生在Bank2的相应数据地址上,从而程序发生错误。所以进入子程序后,马上选择要使用数据的Bank,从而防止发生误操作EM78系列单片机的开发语言为汇编语言,所以当编写汇编程序时,一定要养成一个良好的编程习惯,这非常有利于程序的开发和维护。笔者建议程序中对变量按如下操作。
①定义变量表。在程序开始的地方定义好变量表,为每一个变量取名,分配地址空间。
②相关变量最好定义在同一Bank内,这样当对这些相关变量操作时就可以免去选择Bank的麻烦。
③以变量名进行操作。不要对变量的地址直接操作,最好以变量名进行操作,这样当变量需要改变名字或者需要更改分配地址时,直接更改变量表即可,而不用更改具体的程序。这点对于较大的程序非常有利,不但可以增加程序的可读性,更重要的是提高程序的编写便利性和维护性。
以上几点对于编写较高质量的代码都非常重要,应在具体实践中仔细体会。
2 程序跨页跳转和跨页调用技巧
首先需要介绍一下EM 78系列单片机的程序存储器ROM结构。EM78系列的程序存储器ROM容量为4K×13位,采用Page(“页”)分配原则,就是将4K的程序空间分为4页,每个页容量为1K。其结构如图2所示,其中Page0(000H~3FFH)、Pagel(400H~7FFH)、Page2(800H~BFFH)、Page3(COOH~FFFH)。指令系统中的两条长距离跳转指令JMP和CALL,所携带的地址码仅仅有lO位,210=lK地址空间,即只能在1K的空间内跳转。当使用JMP指令时,装入目标地址到PC程序指针的低10位;使用CALL指令时,装入目标地址至PC程序指针的低10位,且PC+l压栈,调用同1K页面内的任何程序。PC程序指针(寄存器R2)和堆栈的位数是12位,即寻址空间分别为4K,一个程序页面为lK。页面选择通过设定状态寄存器R3的Bit6(PS1)、Bit5(PSO)来完成。
在EM447中,当需要跳转或调用不同页面的子程序时,则需在调用前修改R3的PSl、PS0,这样当执行IMP指令或CALL指令时会将状态寄存器R3的PSl、PS0载入PC的A11、A10,所以PC程序指针可以在4K范围的地址空间内自由跳转。
当编写的程序代码量较大(超过1K)时,程序跨页跳转和跨页调用是避免不了的。在使用JMP指令时,一定要知道将要跳到哪个Page;使用CALL指令时,一定要知道要调用的子程序位于哪个Page中。这样在使用JMP指令和CALL指令之前必须要设置PSl和PS0位来选择将要跳转或调用程序的存储空间。例程如下:
BS STATUS,PSl ;到Page3
BS STATUS,PS0
CALL INIT CLEAR ;调用初始数据清零程序
CALL SYS INIT ;调用系统初始化程序
BC STATUS,PSI ;到Pagel
JMP SYS_BEGIN ;跳转到系统启动程序
例程中首先设置PS相应位来选择Page3,然后调用INIT_CLEAR和SYS_INIT,之后选择Pagel,跳到Pagel中的SYS BEGIN主程序中。对于较大的程序,这种调用和跳转是经常出现的,所以要求编写程序前必须熟悉程序地址空间的分配。
3 ROM模块化编程原则
因为义隆提供的调试环境只支持汇编语言,而用汇编语言编写的程序,结构条理性劣于用C语言编写的程序,所以编写程序时应尽可能的将子程序功能模块化,即以完成某个功能来编写子程序,这样结构清晰、调用方便。
笔者在编写程序时,针对EM78系列单片机ROM的Page结构,将子程序按区分功能原则分别存放到不同的Page内,如图3所示为笔者项目中程序的存储空间分配。
图3中,PageO存放的主要是比较常用的子程序,如系统初始化程序、键盘扫描、液晶显示等;Pagel存放的是一些数据处理程序,如不同Bank的数据拷贝,十六进制转为二进制压缩BCD码等程序;Page2存放的加密算法程序,这是笔者项目开发中最重要的部分,所以单独的放在一个Page内;Page3主要是对IC存储器24LC02的一些读写操作子程序。这样将功能相近的子程序放在一个Page内,从而根据单片机的结构特点结合项目开发的实际来划分程序存储空间。在这样调试时,很容易发现问题出现在哪个区域,使得程序结构清晰明了,调用方便,易于调试和维护。
4 采用分区指令冗余技术提高软件抗干扰性
为了确保程序稳定可靠运行,有时必须采用软硬件抗干扰设计。在某些场台,大量的干扰源虽然不会造成单片机硬件系统的破坏,却常常会破坏数字信号的时序,更改单片机寄存器内容,导致程序的“跑飞”或进入死循环。因此在提高硬件可靠性的基础上,还需要在程序设计中采取软件抗干扰,从而提高软件的可靠性,减少软件错误的发生或者在发生错误的情况下仍能使系
统恢复正常运行。
针对EM78系列单片机,笔者采用了分区指令冗余技术来防止程序的跑飞。即对程序中没有使用的程序空间用“NOP_NOP_JMP”指令将其填满。当程序跑飞到单指令的“NOF”上时,不会发生将操作数当作指令来执行的错误,同时后面增加“JMP”指令使程序跳转到程序跑飞处理程序上。图4给出图例。
图4中,针对EM78系列单片机ROM的结构特点,分别在4个页里存放一个单独的跑飞处理程序Error(),程序的功能可以是“踢狗”(外置“狗”)。这样做的原因如下:如果只编写一个跑飞处理程序,假设放在PageO里,如果程序在Page2跑飞后,它不会跳到PageO的跑飞处理程序,反而会跳到Page2中,这样程序仍然处于一种跑飞的状态。所以在每个Page里添加Error()程序,这样无论程序在哪个Page里跑飞,都会跳到Error()处理程序中。笔者实际中采用了这种方法取得了很好的效果。
5 其它常见问题分析
在使用EM78系列芯片进行开发时,除了上面要注意的几点,还会遇见一些其它小问题。下面就提出这些问题及解决方法,希望读者编程时有所注意。
(1)切记堆栈的深度
汇编语言编程中经常调用子程序,子程序的使用可以大大减少程序的书写量,提高程序的使用效率。但在对EM78系列芯片进行开发时,调用子程序应切记堆栈的深度。EM78系列单片机采用的是5级深度的硬件堆栈,它既不占用程序空间也不占用数据空间,是独立的暂存空间,不需要进栈和出栈的堆栈操作专用指令。堆栈的使用是环行的,若已有5个返回地址被存入堆栈后,若再次调用子程序,则第6个返回地址将被存入堆栈的第一层中(该层原有的内容将被覆盖),所以设计应用程序时,子程序的嵌套调用层数不能超过5层,若嵌套调用超过5层,则会发生程序跑飞。
(2)长时间的软件延时方法
EM 78系列芯片内部带有定时器,但对于较长时间的延时(如几十s)则不太适用。笔者在开发中要使用20s的延时,编写了一段软件延时程序,采用3层嵌套程序,源代码如下,程序可以直接移植运行。
;…主循环………
LOOP0: MOV A,@18H
MOV DATAl,A
LOOF1: MOV A,@07fH
MOV DATA2,A
LOOP2: MOV A,@07IfH
MOV DATA3,A
LOOp3: JMP CHECK ;跳到应用程序
CHE(1K BACK:DJZ DATA3 ;应用程序退出
JMP LOOP3
DJZ DATA2
JMP LOOP2
DJZ DATAl
IMP LOOP1
;20s后执行OTHER程序
IMP OTHER
(3)查表程序中防止程序跑飞的技巧
在单片机开发应用中,经常要用到查表程序来实现代码转换,例如8段LED的译码显示。EM78系列芯片的指令系统中,有查表所需的专用指令TBL和RETL。查表过程是,先通过把被显数字(即索引值)作为在数据表中的偏移量存入A,通过子程序传递参数,子程序将参数和当前PC相加,则PC跳到偏移位置,然后经由RETL将所列数据装入A,然后退出子程序。假设所列数据表中只有1O项,则传递到子程序的参数最大为9。但如果程序跑飞,则有可能传入大于9的参数,这样此数加上PC指针则跳出数据表的范围,程序出错。笔者在自己的程序中加上如下措施,从而有效的解决了这个问题。即进入子程序后,首先判断传人参数是否大于9,即偏移量是否正确,不正确则跳到错误处理程序ERROR(),正确则继续执行。这样可确保显示的数据都是正确的。
;……数据译码表……
DATA_TPANS_JBL:
AND A,@OXOF;判断显示的偏移数据是否超过09
MOV Disp_limit,a
BC STATUS,C
SUB A,@OxO9
IBS STATUS,C
JMP ERROR ;若显示偏移数据出错跳到出错
;处理程序
……
……
TBL
RETL 0BI 10101 11 ;
“O”段码AB′ABDEF,“0XD7”
RETL 0800000110 ;“1”段码:“Bc” ,“0X06”
……
……
RETL 0810110111 ;“9”段码’′ABCDFG′,"0XB7"以上是笔者在产品开发中的一些心得,文中所列举的程序均经ELAN WICE。E编译通过,读者可直接引用。