第十三章 常用软件设计 13.1与软件结构相关的散转、查表程序设计 一、查表程序设计 单片机应用系统中,查表程序是一种常用程序,它广泛使用于LED显示器控制,打印机打印以及数据补偿、计算、转换等功能程序中。 查表,就是根据变量x在表格中查找y,使y=f(x)。 x有各种结构,如有时x可取小于n(n为定值)的自然数子集;有时,x取值范围较大,并且不会取到该范围中的所有值,即对某些x,f(x)无定义,例如x为不定长的字符串或x为某些ASCII字符等。 y也有各种结构,如有时y可取定字长的数,但不是所有该字长的数都有对应的x;有时y可取小于m(m为定值)的自然数子集。 对于表格本身,也有许多不同的结构。以存放顺序分,有有序表与无序表,以存放地点 分,有的表格存放在程序存储器中(用MOVC指令访问),有的表格存放在数据存储器中(用MOVX指令访问)。表格的存放内容也各有不同,有的只存放y值,有的只存放x值;有的表格中还包括含有几个子表;表格中的每一项的长度也各有不同,有的是定长,有的为不定长。其它还有多维表格等情况。下面介绍几种常见类型查表程序和这些表格的构成方法。 (一)x可取小于等于n的自然数的全体,y为定字长。 由于xi取值为自然数0,l,2,3,…,n有序等差排列,而yi又为定字长,故可以简化表格,在表中只存放yi的值。 例1 设有一个巡回检测报警装置,需对16路输入值进行比较,当每一路输入值超过该路的报警值时,实现报警。下面根据这一要求,编制一个查表程序。 x为路数,查表时xi按0,1,2,…,15(n=15)取数,yi为报警值,二字节数,依xi顺序列成表格放在TABl中。进入查表程序前,路数xi放在R2中,查表后的最大值放在R3、R4中。 查表程序如下: TB1: MOV A,R2 ;路数xi→R2 ADD A,R2 ;R2·2→A MOV R3,A ;保存路数xi值 ADD A,#TAB(rel) ;加上表首偏移量 MOVC A,@A+PC ;查第一字节 XCH A,R3 ;第一字节→R3 ADD A,#TAB(rel)+1 ;加上表首偏移量 MOVC A,@A+PC ;查第二字节 MOV R4,A ;第二字节入R4 RET TAB: DW 05F0H,0E89H,0A695H,1BAAH;报警值表 DW 0D9BH,7F93H,0378H,26D7H DW 2710H,9B3FH,1A66H,22B3H DW 1174H,16EFH,3aB4H9,0CA0H 上述查表程序使用MOVC A,@A+PC,因此,表格偏移不得超过255个字节。当表 格偏移大于255个字节时,使用MOVC A,@A十DPTR查表指令。 例2 在一个温度测量装置中,测量的电压与温度为非线性关系,需对它进行线性化处理。如测得值为10位二进制数(占两个字节),采用表格方法实现线性化处理时,可将所测出不同温度下的输入值(共有1024个数)数据构成一个表,表中存放温度值为yi,电压值为x(为了减少测量数据,也可用插值法求出其它数据)。设测量输入值放在R2、R3中,采用以下程序可以把它转换成线性温度值,然后再放回到R2、R3中。表TAB2可放在64k程序存储器中任何地方。 LTB2: MOV DPTR,#TAB2 MOV A,R3 CLR C RLC A MOV R3,A XCH A,R2 RLC A XCH R2,A ADD A,DPL MOV DPL,A MOV A,DPH ADDC A,R2 MOV DPH,A CLR A MOVC A,@A+DPTR MOV R2,A CLR A INC DPTR MOVC A,@A+DPTR MOV R3,A RET TAB2: DW … ;温度数值麦 二、散转程序设计 散转程序是一种并行分支程序。它是根据某种输入或运算结果,分别转向各个处理程序。在MCS-51单片机中,散转指令为JMP @A+DPTR。它按照程序运行时决定的地址执行间接转移指令。该指令把累加器的8位无符号内容与16位数据指针的内容相加后装入程序计数器,实现程序的转移。A的内容不同,散转的入口地址不同。 下面介绍几种不同的散转程序 (一)使用转移指令表的散转程序 在不少场合下,需要根据某一单元的内容是0,1,2,…,n时分别转向分支处理程序0,1,2,…,n。可直接使用散转指令JMP @A+DPTR。由于A可容纳的字节数只有256个,在DPTR为首地址的256个程序存储单元一般不可能容纳n个分支处理程序。因此,可只将这个区域中作为n个分支处理程序的转移指令表。采用AJMP转移指令需占用二个字节,而采用LJMP转移指令需占空间为三个字节。 例5 根据R2的内容,转向各个处理程序 (R2)=0,转向 PRG0 (R2)=1,转向 PRGl …… (R2)=n,转向 PRGn 程序清单 JMPl: MOV DPTR,#TBJ1 ;转移指令表(转移指令入口地址) MOV A,R2 ;给每个转移指令空出二个字节车间 ADD A,R2 JNC NADD INC DPH ; R2·2>256时表空间增加一面 NADD: JMP @A+DPTR TBJ1: AJMP PRG0 AJMP PRGl AJMP PRG2 …… AJMP PRGn PRG0: …… …… PRG1: …… …… PRG2: …… …… …… PRGn: …… …… 这个散转程序由于在分支转移指令表中使用AJMP指令,其寻址范围为2k字节,如果n个散转分支程序长度超过2k字节时,可改用64k寻址的LJMP指令。这时,上述程序中应给每个转移指令空出三个字节空间。 对于散转分支程序超过256个时,即n>255,一个字节存放不下,这时,可用两个字节,并利用对DPTR进行加法运算的方法,直接修改DPTR;然后再用JMP @A+DPTR实现散转。 三、子程序调用时的参数传递方法 在MCS-51应用程序中,完成子程序调用的指令为ACALL和LCALL,子程序返回指令为RET。 在功能上子程序与中断服务程序有相似之处。但中断请求是随机的,转向中断处理的时刻与主程序无直接关系,故一定要有现场保护与现场恢复,而对于子程序来说,它的调用安排是由主程序设定,故现场不一定要加以保护,可按实际情况灵活处理。 子程序调用时有个参数传递问题,即在子程序调用时,主程序应先把有关参数放到某些约定的位置,子程序在运行时,可以从约定位置得到有关参数,同样,子程序起运行结束前,也应把运算结果送到约定位置。在返回主程序后,主程序从约定的位置上得到所需要的结果。 子程序调用时,可采用以下几种常用的参数传递方法。 1.用工作寄存器或累加器传递参数 这种方法是将输入参数或结果参数放在工作寄存器或累加器中。使用这种方法程序简单,运算速度也高。是最常使用的参数传递方法,其缺点是传递参数不能太多。 2.用指针寄存器来传递参数 由于数据一般存放在存储器中,故可用指针来指示数据的位置,这样可大大节省传递数据的工作量,并可实现变长度运算。如果参数在内部RAM中,可用R0、R1作指针;参数在外部RAM或程序存储器中,可用DPTR作指针。可变长度运算时,可用一个寄存器来指出数据长度,也可在数据中设置结束标记。