MCS-51的基本运算指令是针对8位二进制数的,带有很大的局限性,但通过软件设计,可以完成各种各样的运算。本节主要讨论常用的程序设计方法,以进一步熟悉MCS-51指令系统,掌握汇编语言程序设计方法和技巧。
在许多情况下,本来通过计算才能解决的问题也可以改用查表方法解决,而且要简便得多。因此,在实际单片机应用中,常常需要编制查表程序以缩短程序长度和提高程序执行效率。
所谓查表是根据存放在ROM中数据表格的项数来查找和它对应的表中的值。例如:查y=x2(设x为0~9)的平方表时,我们可以预先计算出x为0~9时的y值作为数据表格存放在起始地址为DTAB的ROM存储器中,并使x的值和数据表格的项数(所查数据的实际地址对DTAB的偏移量)一一对应。这样,我们就可以根据DTAB+x来找到和x对应的y值。
采用MCS-51汇编语言进行查表尤为方便,它有两条专门的查表指令:
MOVC A, @A+DPTR
MOVC A, @A +PC
第一条查表指令采用DPTR存放数据表格的起始地址,其查表过程比较简单。查表前需要把数据表格起始地址存入DPTR,然后把所查表的项数送入累加器A,最后使用MOVC A,@A+DPTR指令完成查表。
采用MOVC A,@a+PC指令查表,所需操作有所不同,其步骤分为3步:
(1)使用传送指令把所查数据表格的项数送入累加器A。
(2)使用ADD A,#data指令对累加器A进行修正。data值由下式确定。
PC + data=数据表始址DTAB
其中,PC是查表指令MOVC A,@A+PC的下一条指令码的起始地址。因此,data值实际上等于查表指令和数据表格之间的字节数。
(3)采用查表指令MOVC A,@A+PC完成查表。
查表程序主要用于代码转换、代码显示、实时值查表计算和按命令号实现转移等。
【例4-21】 已知R0低4位有一个十六进制数(0~9或A~F中的一个),请编出能把它转换成相应ASCII码并送入R0的程序。
解:本题给出3种求解方案,两种是计算求解,一种是查表求解,请比较它们的优劣。
(1)计算求解1。由ASCII码字符表可知0~9的ASCII码为30H~39H,A~F的确ASCII码为41H~46H。因此,计算求解的思路是,若R0≤9,则R0内容只需加30H;若R0>9,则R0需加37H。
相应程序如下:
ORG 0400H
MOV A,R0 ;取转换值到A
ANL A,#0FH ;屏蔽高4位
CJNE A,#10,NEXT1 ;A和10比较
NEXT1: JNC NEXT2 ;若A>9,则转NEXT2
ADD A,#30H ;若A<10,则A←A+30H
SJMP DONE ;转DONE
NEXT2: ADD A,#37H ;A←A+37H
DONE: MOV R0,A ;存结果
SJMP $
END
(2)计算求解2。本方案先把R0中内容加上90H,并作十进制调整,然后再用ADDC指令使R0中内容加上40H,也作十进制调整,所得结果即为相应ASCII码。
本方案实际上和第一种方案类似,只是使用了十进制调整指令。当R0<10时,经过上述处理相当于R0中内容加了30H;当R0=0AH时,加90H并进行十进制调整后,低4位的半进位位加到高4位后变为A0H,对高4位进一步调整便得到00H,最后使用ADDC指令(C=1)加上40H实际上相当于加上41H。
相应程序如下:
ORG 0400H
MOV A,R0 ;取转换值到A
ANL A,#0FH ;屏蔽高4位
ADD A,#90H ;A中内容加90H
DA A ;十进制调整
ADDC A,#40H ;A中内容加40H
DA A ;十进制调整
MOV R0,A ;存转换结果
SJMP $ ;结束
END
(3)查表求解。查表求解时,两条查表指令均可以使用。现以MOVC A,@A+PC指令为例,给出相应程序如下:
ORG 0400H
MOV A,R0 ;取转换值到A
ANL A,#0FH ;屏蔽高4位
ADD A,#03H ;地址调整
MOVC A, @A+PC ;查表
MOV R0,A ;存结果
ASCTAB:DB '0','1','2','3','4'
DB '5','6','7','8','9'
DB 'A','B','C','D','E','F'
END
【例4-22】 设有一起始地址为DTATAB的数据表格,表中存放有1024个元素,每个元素为两个字节。请编出能根据R5、R4中元素序号查找对应元素并放入R5、R4(R5中为高8位和R4中为低8位)的程序。
解:由于数据表格内的每个元素为二字节,故R5、R4中元素序号应扩大两倍后再和DPTR中数据表格起始地址DTATAB相加,以获得数据元素的绝对地址。
根据这一思想,相应参考程序如下:
ORG 0500H
START: MOV DPTR,#DTATAB ;数据表格起始地址送DPTR
MOV A,R4 ;元素序号低字节送A
CLR C ;CY清零
RLC A ;2×元素序号低字节
XCH A,R5 ;存入R5,元素序号高字节送A
RLC A ;2×元素序号高字节
XCH A,R5,
ADD A,DPL ;2×元素序号低字节+DPL
MOV DPL,A ;存入DPL
MOV A,DPH
ADDC A,R5 ;2×元素序号高字节+DPH
MOV DPH,A ;存入DPH
CLR A ;清零A
MOVC A,@A+DPTR ;查表得元素高字节
MOV R5,A ;存入R5
MOV A,#01H ;查表得元素低字节
MOVC A,@A+DPTR
MOV R4,A ;存入R4
RET ;返回主程序
DTATAB: DW……;元素表格,高字节在前
DW……
……
END
本程序是以子程序形式编写的,可为主程序调用。程序使用了DPTR作为基地址寄存器,故它可以在64KB范围内查表。
运算程序可以分为浮点数运算程序和定点数运算程序两大类。浮点数就是小数点不固定的数,其运算通常比较麻烦,常由阶码运算和数值运算两部分组成;定点数就是小数点固定的数,通常包括整数、小数和混合小数等,其运算比较简单,但在数位相同时定点数的表示范围比浮点数的小。本教材只介绍定点数运算程序设计问题,若无特别说明,所有程序均指定点数运算程序。
MCS-51单片机提供了单字节运算指令,但在实际应用中经常需要编写一些多字节运算程序,这些运算程序通常编成子程序形式,以供主程序在需要时调用。
加减运算程序可以分为无符号多字节数加减运算程序和带符号多字节数加减运算程序两种。现分述如下。
(1)无符号多字节加减运算程序。
无符号多字节加法运算程序的编制已在前面作过介绍,现以多字节减法程序为例加以介绍。
【例4-23】 已知:以BLOCK1和BLOCK2为起始地址的存储区中分别有5个字节无符号被减数和减数(低位在前,高位在后),请编一减法子程序令它们相减并把差放入以BLOCK1为起始地址的存储单元中。
解:本程序算法很简单,只要用减法指令从低字节开始相减即可。
相应程序如下:
ORG 0A00H
SBYTESUB: MOV R0,#BLOCK1 ;被减数始址送R0
MOV R1,#BLOCK2 ;减数始址送R1
MOV R2,#05H ;字长送R2
CLR C ;CY清零
LOOP: MOV A,@R0 ;被减数送A
SUBB A,@R1 ;相减,形成C
MOV @R0,A ;存差
INC R0 ;修改被减数地址指针
INC R1 ;修改减数地址指针
DJNZ R2,LOOP ;若未完,则LOOP
图4-18 双字节无符号乘法 程序流程图 |
END
(2)带符号单字节加减运算程序。
带符号单字节加减运算程序和无符号加减运算程序类似,只是符号位处理上有所差别。
【例4-24】 双字节无符号数乘法。
解:编程说明。MCS-51指令系统中只有单字节乘法指令,因此双字节相乘需分解为4次单字节相乘。若被乘数(ab)和乘数(cd)分别表示为(az+zb)和(cz+zd)。其中,a、b、c、d都是8位数,z表示8位“0”,其乘积表示为:
(az+zb)(cz+zd)=zz+zz+zz+zz
式中,、、、为相应的两个8位数的乘积,占16位,可用4次乘法指令并求和,以便得到两个双字节数的乘积。
当被乘数R5(高)、R4(低)、乘数R3(高)、R2(低)时,其算法如下。
流程图如图4-18所示,参考程序如下:
功能: 双字节无符号数乘法。
入口: R5(高),R4(低),被乘数;
R3(高),R2(低),乘数。
出口: (R1)=积的低位字节地址指针。
MULBIN:MOV A,R1 ;将积的指针暂存在R6
MOV R6,A
MOV R7,#04H ;积有4个单元
CLEAR: MOV @R1,#00H ;积单元清零
INC R1
DJNZ R7,CLEAR ;4个单元清零
MOV A,R6
MOV R1,A ;恢复积的指针R1
MUL1: MOV A,R2 ;(R2)×(R4)
MOV B,R4
MUL AB
ACALL ADDM ;调用加部分积子程序
MOV A,R2 ;(R2)×(R5)
MOV B,R5
MUL AB
ACALL ADDM ;调用加部分积子程序
MOV A,R3 ;(R3)×(R4)
MOV B,R4
MUL AB
DEC R1 ;指针调回
ACALL ADDM ;调用加部分积子程序
MOV A,R3 ;(R3)×(R5)
MOV B,R5
MUL AB
ACALL ADDM ;调用加部分积子程序
MOV A,R6 ;恢复地址指针
MOV R1,A
RET
子程序如下:
ADDM: ADD A,@R1 ;加部分积,A为部分积低位
MOV @R1,A ;保存积的低位
MOV A,B ;部分积高位送A
INC R1 ;指针加1
ADDC A,@R1 ;加高位部分积
MOV @R1,A ;保存积
INC R1 ;指针加1
MOV A,@R1 ;更高位加上一次的进位
ADDC A,#0 ;加进位
MOV @R1,A ;送回结果
DEC R1 ;指针减1
RET ;返回
在单片机应用系统的信号中,常含有各种噪声和干扰,影响了信号的真实性,因此应采取适当的方法消除噪声和干扰。数字滤波就是一种有效的方法。常用的数字滤波方法有算术平均值法、滑动平均值法等。下面以算术平均值法为例讲述数字滤波程序问题。
【例4-25】 片外RAM中从ADIN处开始存放16个字节的数据信号,编程实现用算术平均值法进行滤波,结果存放在累加器A中。
解:算术平均值法就是用求16个字节数据信号的算术平均值的方法进行滤波。
参考程序如下:
功能: 求16字节算术平均值。
入口: ADIN指针,指向16字节外部数据。
出口: (R5)=16字节外部数据的算术平均值。
AVl6D: MOV R7,#16 ;设置计数器
MOV DPTR,#ADIN ;指向数据区
MOV R5,#0 ;(R6,R5)用于存放累加结果
MOV R6,#0
LOOP: MOVX A,@DPTR ;取外部数据
ADD A,R5 ;加部分和低位
MOV R5,A ;送回
MOV A,R6 ;取高位
ADDC A,#0 ;加低位的进位
MOV R6,A ;送回
INC DPTR ;调整外部数据指针
DJNZ R7,LOOP ;共加16个数
MOV R7,#4 ;右移4次,相当于除以16
LOOP1: CLR C ;清进位
MOV A,R6 ;先移高4位
RRC A ;带进位右移1位
MOV R6,A ;送回
MOV A,R5 ;后移低4位
RRC A ;带进位右移1位
MOV R5,A ;送回
DJNZ R7,LOOP1 ;共移4次
RET
在算术平均值滤波程序中,数据个数m的取值一般为2m,这样便于计算,顺序累加和右移m次即可。为确保精度,本程序采用双字节数加法,采取右移4次的方法达到求平均数的目的。
MCS-51系列单片机中,有两个16位的定时器/计数器,它们是可编程的。在对它们进行初始化编程时,一般包括以下几个步骤。
(1)确定工作方式,对TMOD进行赋值。
(2)置定时器/计数器的初值,写入TH0、TL0或TH1、TL1中。
(3)根据需要开放中断。
(4)启动定时器/计数器。
下面举例说明定时器/计数器的使用。
【例4-26】 设晶振为6MHz,应用定时器T0产生一个宽度为1ms,周期为4ms的矩形波,由P1.0口输出。波形如图4-19所示。
图4-19 P1.0输出波形图
选用定时器0工作方式1,定时时间1ms,其初值计算为:
10-3=12(216-X)/(6×106)
X=0FE0CH
程序清单如下:
START: MOV R5, #01H
MOV TMOD, #01H ;设置定时器0工作方式1
MOV TL0, #0CH ;定时器0置数
MOV TH0, #0FEH ;定时器0置数
SETB TR0 ;启动定时器0
LOOP: JNB TF0, $ ;等待定时器0定时到
CLR TF0 ;时间到,清除定时器0溢出标志
MOV TL0, #0CH
MOV TH0, #0F0H
DJNZ R5, LOOP
MOV R5, #0H
CPL P1. 0
JB P1. 0, LOOP
MOV R5, #03H
AJMP LOOP