《单片机原理及接口技术》课件第4章_第1页
《单片机原理及接口技术》课件第4章_第2页
《单片机原理及接口技术》课件第4章_第3页
《单片机原理及接口技术》课件第4章_第4页
《单片机原理及接口技术》课件第4章_第5页
已阅读5页,还剩141页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第4章MCS-51单片机汇编程序设计4.1汇编语言程序设计概述

4.2程序设计的基本结构及常用子程序

4.3编程及开发环境

本章小结

练习与思考题

4.1汇编语言程序设计概述

4.1.1汇编语言以及汇编语言程序设计特点

1.汇编语言的特点

以助记符标识指令的程序设计语言,就是计算机的汇编语言,一条指令就是汇编语言的一条语句。其特点如下:

(1)助记符指令和机器指令一一对应,所以用汇编语言编写的程序效率高,占用存储空间小,运行速度快,因此汇编语言能编写出最优秀的程序。

(2)使用汇编语言编程比使用高级语言困难。因为汇编语言是面向计算机的语言,汇编语言的程序设计人员必须对计算机硬件有相当深入的了解。

(3)汇编语言能直接访问存储器及接口电路,也能处理中断,因此汇编语言程序能直接管理和控制硬件设备。

(4)汇编语言缺乏通用型,程序不易移植,各种计算机都有自己的汇编语言,不同计算机的汇编语言之间不能

通用。

2.汇编语言程序设计的特点

所谓程序设计就是编写计算机程序。汇编语言程序设计就是使用汇编指令来编写计算机程序。在进行单片机的汇编语言程序设计时请注意以下要点:

(1)在程序中要对数据的存放、寄存器和工作单元的使用等做出具体安排。

(2)设计人员必须对所使用的单片机的硬件结构有较为详细的了解,特别是对各类寄存器、端口、定时器/计数器、中断等内容更应了如指掌,以便在程序设计中熟练使用。

4.1.2MCS-51汇编语言伪指令

计算机一般都配备汇编语言,每一条语句就是一条指令,命令CPU执行一定的操作,完成规定的功能。但是用汇编语言编写的源程序,计算机不能直接执行,因为计算机只认识机器指令(二进制编码)。因此必须把汇编语言源程序通过汇编程序翻译成机器语言程序(称为目标程序),计算机才能执行,这个翻译过程称为汇编。汇编程序对用汇编语言写的源程序进行汇编时,还要提供一些汇编用的控制指令,例如要指定程序或数据存放的起始地址;要给一些连续存放的数据确定单元等。但是,这些指令在汇编时并不产生目标代码,不影响程序的执行,所以称为伪指令。常用的伪指令有下列几种:

1.ORG(Origin,起点)

ORG伪指令总是出现在每段源程序或数据块的开始,它指明此语句后面的程序或数据块的起始地址。其一般格式为

ORGnn(绝对地址或标号)

在汇编时由nn确定此语句后面第一条指令(或第一个数据)的地址。该段源程序(或数据块)就连续存放在以后的地址内,直到遇到另一个ORGnn语句为止。例4-1

ORG 8000H

MOV R0,#50H

MOV A,R4

ADD A,@R0

MOV R3,A

ORG伪指令说明其后面源程序的目标代码在存储器中存放的起始地址是8000H,即:

2.DB(DefineByte,定义字节)

一般格式为

[标号:]DB字节常数或字符或表达式

其中,标号区段可有可无;字节常数或字符是指一个字节数据,或用逗号分开的字节串,或用引号括起来的ASCII码字符串(一个ASCII字符相当于一个字节)。此伪指令的功能是把字节常数或字节串存入内存连续单元中。例4-2

ORG9000H

DATA1:DB73H,01H,90H

DATA2:DB02H

伪指令ORG9000H指定了标号DATA1的地址为9000H,伪指令DB指定了数据73H,01H、90H顺序地存放在从9000H开始的单元中,DATA2也是一个标号,它的地址与前一条伪指令DB连续,为9003H,因此数据02H存放在9003H单元中,即:

3.DW(DwfineWord,定义一个字)

一般格式为

[标号码]DW字或字符串

DW伪指令的功能与DB相似,其区别在于DB用以定义一个字节,而DW用以定义一个字(规定为两个字节,即16位二进制数),故DW主要用来定义地址。存放时一个字需两个单元。例如:

ORG 0100H

DW 2389H,75H

0100H~0103H单元中分别存放23H、89H、00H和75H。

4.EQU(Equate,等值)

一般格式为

标号EQU操作数

EQU伪指令的功能是将操作数赋予标号,使两边的两个量等值。例如:

AREAEQU1000H

给标号AREA赋值为1000H,

STKEQUAREA

相当于STK = AREA。若AREA已赋值为1000H,则STK也为1000H。

使用EQU伪指令给一个标号赋值后,这个标号在整个源程序中的值是固定的。也就是说,在一个源程序中,任何一个标号只能赋值一次。

5.END(汇编结束)

一般格式为

[标号:]END[地址或标号]

其中标号以及操作数字段的地址或标号不是必要的。

END伪指令是一个结束标志,用来指示汇编语言源程序段在此结束。因此,在一个源程序中只允许出现一个END语句,并且它必须放在整个程序(包括伪指令)的最后面,是源程序模块的最后一个语句。如果END语句出现在中间,则汇编程序将不汇编END后面的语句。例4-3

ORG8400H

PRG0EQU8450H

PRG1EQU80H

PRG2EQUB0H

MOVA,R2

MOVDPTR,#TBJ3

MOVCA,@A+DPTR

JMP@A+DPTR

TBJ3:DWPRG0

DBPRG1

DBPRG2

END上述程序中伪指令规定:程序存放在8400H开始的单元中,字节数据放在标号地址TBJ3开始的单元中,与程序区紧连着。标号PRG0赋值为8450H,PRG1赋值为80H,PRG2赋值为B0H。4.1.3汇编语言的程序设计

1.编制程序的步骤

1)任务分析

首先要对单片机应用系统所要完成的任务进行分析,明确系统的设计要求、功能要求和技术指标,然后还要对系统的硬件资源和工作环境进行分析,确定硬件系统资源。

2)算法优化

算法是解决问题的具体方法。一个应用系统经过分析、研究后,利用严密的数学方法或数学模型来描述,从而将一个实际的问题转化成计算机进行处理的问题。同一个问题的算法可以多样,结果也可能不尽相同,所以应对各种算法进行分析比较,并进行合理的优化。

3)程序总体构思

经过任务分析、算法优化后,就可以进行程序的总体构思,确定程序的结构和数据形式,并考虑资源的分配和参数的计算等。根据总体构思编制程序流程图。

程序流程图可以分为总体流程图和局部流程图。总体流程图侧重反映程序的逻辑结构和各程序模块之间的相互关系。局部流程图反映程序模块的具体实施细节。

在此基础上,一般还应编制一个资源分配表,包括数据结构和形式、参数计算、通信协议、各自程序的入口和出口说明等。

4)编制源程序

根据所用计算机的指令系统,按照已编制的程序框图用汇编语言编制出源程序。

5)上机调试

将编制出的程序在计算机上调试,直至实现预定的

功能。

2.编制程序的方法和技巧

1)采用模块化设计

计算机应用系统一般由多个模块组成,其中包括一个主程序和多个子程序模块。每一个模块都完成一个明确的任务,实现某个具体的功能,如发送、延时、显示等。采用模块化程序设计方法,就是将任务细化,分别设计具有能完成具体单一任务的独立程序并分别调试,最后将这些模块程序装配成整体程序并进行联调。模块化程序设计把一个功能复杂的程序划分成若干个单一的程序模块,有利于程序的设计和调试,有利于程序的优化和分工,有利于修改,提高了程序的可读性,通用性较强,使程序的结构一目了然。所以进行程序设计学习时,一开始就要建立程序模块化的设计思想。

2)采用循环结构和子程序

循环结构和子程序可减少程序容量,节省程序存储空间,提高程序代码利用效率。对于多重循环,要注意各重循环的初值和循环结束的条件,避免出现程序无休止循环的“死循环”现象。对于通用的子程序,除了用于存放子程序入口参数的寄存器外,子程序中用到的其他寄存器的内容应压入堆栈保护现场,并要特别注意堆栈操作的压入和弹出的顺序。对于中断处理子程序,除了要保护处理程序中用到的寄存器外,还要保护标志寄存器。这是由于在中断处理过程中难免对标志位产生影响,而中断处理结束返回主程序时可能会遇到以中断前的状态标志为依据的条件转移指令,如果标志位被破坏,则整个程序就乱了。

4.2程序设计的基本结构及常用子程序

一般把程序结构分为3种形式,即顺序结构、分支结构和循环结构。下面介绍这3种结构程序设计的基本方法。

4.2.1顺序程序结构

顺序程序结构是指计算机按指令在存储器中存放的先后次序来执行程序,除非用特殊指令让它跳转。例4-4

编写1 + 2的程序。

首先用ADDA,Rn指令,该指令是将寄存器Rn中的数与累加器A中的数相加,结果存于A中,这就要求先将1和2分别送到A中和寄存器Rn中,而Rn有4组,每组有8个单元R0~R7。首先要知道Rn在哪组,默认值(不设定值)是第0组,在同一个程序中,同组中的Rn不能重复使用,不然会数据出错,唯独A可反复使用,不出问题。明确了这些后,可写出程序如下: ORG0000H ;首地址

MOVR2,#02 ;(R2)=02H

MOVA,#01 ;(A)=01H

ADDA,R2 ;(A)=(A)+(R2)=03H

END ;程序结束标志,必不可少的

程序到此编写完成,然后在仿真软件中调试、验证,若不对,反复修改程序,直到完全正确为止。

该程序若用ADDA,direct指令编程时,可写出如下程序: ORG 0000H

MOV 30H,#02 ;(30H)=02H

MOV A,#01 ;(A)=01H

ADD A,30H ;(A)=(A)+(30H)=03H

END

该程序若用ADDA,#data指令编程时,可写出如下程序:

ORG 0000H

MOV A,#01 ;(A)=01H

ADD A,#02 ;(A)=(A)+02H=03H

END从以上例子可见,同一个程序有多种编写方法,思路不同编出来的程序不同,但结果都一样。我们认为最后一个程序较好。

以上加法程序是最简单的形式,加法有多种:无进位加法、进位加法、有符号加法、无符号加法,还有浮点数的加法、单字节加法、双字节加法、多字节加法,等等。一般编写程序时,编成通用的程序。在调用通用程序之前,先判断是哪一种类型,再调相应的子程序。如以上1+2的程序也可以这样编写:先将加数和被加数分别送入40H、41H单元,加完后和送入42H单元。它的完整程序是: ORG0000H

MOV40H,#01H

MOV41H,#02H

AD1:MOVR0,#40H ;设R0为数据指针

MOVA,@R0 ;取N1

INCR0 ;修改指针

ADDA,@R0 ;N1+N2

INCR0

MOV@R0,A ;存结果

流程图如图4-1所示。图4-1单字节加法程序流程图例4-5

将两个半字节数合并成一个1字节数。

设片内RAM50H,51H单元各存放1个8位二进制数,要求取出两个单元中的低半字节,合并成一个字节后,存入52H单元(50H单元的内容为86H,51H单元的内容为73H,结果52H单元的内容为63H),程序流程图如图4-2所示。

程序如下:

ORG0000H

START:MOV R1,#50H;(R1)=50H

MOV A,@R1;(A)=((R1))=(50H)=86H

ANL A,#0FH;取第一个半字节,

(A)=06H

SWAP A;(A)=60H

INC R1;(R1)=51H

XCHA,@R1;取第二字节,(A)=73H(51H)

=60H

ANLA,#0FH;取第二个半字节,(A)=03H

ORL A,@R1;拼字,(A)=63H

INC R1

MOV @R1,A;存放结果

END

此例相反的过程是将字节拆开分成两个半字节,例如将50H单元中的内容拆开后分别送51H、52H单元中。

图4-2半字节处理程序流程图4.2.2分支程序结构

在处理实际事务中,只用简单程序设计的方法是不够的。因为大部分程序总包含有判断、比较等情况,并根据判断、比较的结果转向不同的分支。下面举两个分支程序的例子。

例4-6

两个无符号数比较大小。

设在两个连续外部RAM单元ST1(8040H)和ST2(8041H)中存放两个不带符号的二进制数,找出其中的大数存入ST3(8042H)单元中。程序流程图如图4-3所示。图4-3例4-6程序流程图程序如下:

ORG8000H

ST1 EQU8040H

START1:CLRC;进位位清0

MOVDPTR,#ST1;设数据指针

MOVXA,@DPTR;取第一数

MOVR2,A ;暂存R2

INCDPTR

MOVXA,@DTPR;取第二个数

MOV R3,A ;暂存

SUBBA,R2 ;两数比较

MOV A,R3

JNCBIG0

MOVA,R2;第一数大

BIG0:INCDPTR

MOVX@DPTR,A;存大数 SJMP$上面程序中,用减法指令SUBB来比较两数的大小。由于这是一条带借位的减法指令,在执行该指令前,先把进位位清0。用减法指令通过借位(CY)的状态判断两数的大小,是两个无符号数比较大小时常用的方法。设两数X,Y,当X≥Y时,X-Y无借位(CY)产生;反之借位为1,表示X<Y。用减法指令比较大小,会破坏累加器中的内容,故做减法前先保存累加器中的内容。执行JNC指令后,形成了分支。执行SJMP指令后,实现程序的转移。

例4-7

将ASCII码表的ASCII码转换为十六进制数,如果ASCII码不能转换成十六进制数,用户标志位置1。

由ASCII码表可知,30H~39H为0~9的ASCII码,41H~46H为A~F的ASCII码。在这一范围内的ASCII码减30H或37H就可以获得对应的十六进制数。设ASCII码放在累加器A中,转换结果放回A中。程序流程图如图4-4所示。图4-4ASCII码转换为十六进制数程序流程图程序如下:

ORG0000H

START:CLRC;CY清0

SUBBA,#30H;A=(A)-30H-CY

JCNASC;(A)<0,不是十

六进制数

CJNEA, #0AH,MM ;(A)与0AH比较

大小

MM:JC ASC ;0≤(A)<0AH,

是十六进制数

SUBB

A,#07H;A=(A)-07H-CY(A>09H)

CJNEA,#10H,NN;(A)与10H比较大小

NN:JC ASC;(A)<10H

NASC:SETB F0

ASC: SJMP ASC例4-8

求单字节有符号二进制数的补码。

正数补码是其本身,负数的补码是其反码加1。因此,程序首先判断被转换数的符号,负数进行转换,正数即为补码。设二进制数放在累加器A中,其补码放回到A中。程序如下:

ORG

0000H

CMPT:JNB

ACC.7,NCH;(A)>0,不需转换

CPL

A;A求反

ADD A,#1;A加1

SETB ACC.7;保存符号

NCH: SJMP NCH

分支程序在实际使用中用处很大,除了用于比较数的大小之外,常用于控制子程序的转移。4.2.3循环程序结构

在程序设计中,只有简单程序和分支程序是不够的。因为简单程序的每条指令只执行一次,而分支程序则根据条件的不同,会跳过一些指令,执行另一些指令。它们的特点是,每一条指令至多执行一次。在处理实际事务时,有时会遇到多次重复处理的问题,用循环程序的方法来解决就比较合适。循环程序中的某些指令可以反复执行多次。采用循环程序,使程序缩短,节省存储单元。重复次数越多,循环程序的优越性就越明显。但是程序的执行时间并不节省。由于要有循环准备、结束判断等指令,速度要比简单程序稍慢些。

1.循环程序的组成部分

循环程序一般由5部分组成:

(1)初始化部分:为循环程序做准备。如:设置循环次数计数器的初值,地址指针置初值,为循环变量赋初值等。

(2)处理部分:为反复执行的程序段,是循环程序的实体。

(3)修改部分:每执行一次循环体后,对指针作一次修改,使指针指向下一个数据所在位置,为进入下一轮处理做准备。

(4)控制部分:根据循环次数计数器的状态或循环条件,检查循环是否能继续进行,若循环次数到或循环条件不满足,应控制退出循环,否则继续循环。

通常(2)、(3)、(4)部分又称为循环体。

(5)结束部分:分析及存放执行结果。

2.循环程序的结构形式

循环程序有如下两种形式:

(1)先进入处理部分,再控制循环。即至少执行一次循环体。如图4-5(a)所示。

(2)先控制循环,后进入处理部分。即先根据判断结果,控制循环的执行与否,有时可以不进入循环体就退出循环程序,如图4-5(b)所示。图4-5循环程序流程图循环结构的程序,不论是先处理后判断,还是先判断后处理,其关键是控制循环的次数。根据需要解决问题的实际情况,对循环次数的控制有多种。循环次数已知,用计数器来控制循环;循环次数未知,可以按条件控制循环。

3.循环程序的分类

循环程序分为单循环程序和多循环程序。

1)单循环程序

单循环程序又可分为两种。

(1)循环次数已知的循环程序。例4-9

工作单元清0。

在程序设计时,有时需要将存储器中的部分地址作为工作单元,存放程序执行的中间值和结果,此时常需要对这些工作单元清0。例如:将40H为起点的8个单元清“0”的程序段如下:

ORG 0000H

CLEAR:CLR A ;A清0

MOV R0,#40H ;确定清0单元起 始地址

MOV R7,#08 ;确定要清除的单元个数

LOOP: MOV @R0,A ;清单元

INC R0 ;指向下一个单元

DJNZ R7,LOOP ;控制循环

SJMP $

此程序的2~4句为设定循环初值,5~7句为循环体。

此程序也可写成通用子程序形式(使用时只要给定入口参数及被清0单元个数,调用此子程序就行):

ORG 0100H

CLEAR: CLR A

LOOP: MOV @R0,A

INC R0 ;修改地址指针

DJNZ R2,LOOP ;控制循环

RET若要求调用该子程序,则必须给R0、R2赋值。入口参数是由实际需要而定,若要清50H为起点的16个单元,则需编制下列程序:

ORG 0000H

MOV

R0,#50H

MOV

R2,#10H

ACALL

CLEAR

SJMP$

入口参数由实际需要而定,若要清40H为起点的20个单元,只要改动前面两句就行。例4-10

多个单字节数据求和。

已知有n个单字节数据,依次存放在片内RAM40H单元开始的连续单元中。要求把计算结果存入R2、R3中(高位存R2,低位存R3)。

程序如下:NUNEQU0AH

ORG1000H

SAD:MOVR0,#40H ;设数据指针

MOVR5,#NUN ;计数值0AH→R5

SAD1:CLR A ;A清0

MOVR2,A ;和的高8位清0

LOOP:ADDA,@R0

JNCLOOP1 ;

INCR2 ;有进位,和的高8位+1

LOOP1:INCR0 ;指向下一数据地址

DJNZR5,LOOP

MOVR3,A

SJMP$

上述程序中,用R0作间址寄存器,每做一次加法,R0加1,数据指针指向下一数据地址。R5为循环次数计数器,控制循环的次数。

(2)循环次数未知的循环程序。以上介绍的几个循环程序的例子,它们的循环次数都是已知的,适合用计数器置初值的方法。而有些循环程序事先不知道循环次数,不能用以上方法。这时需要根据判断循环条件的成立与否,或用建立标志的方法,控制循环程序的

结果。例4-11测试字符串长度。

设有一串字符依次存放在从40H单元开始的连续单元中,该字符串以回车符为结束标志,测得的字符串长度存入R2中。

测字符串长度程序是将该字符串中的每一个字符依次与回车符相比:若比较不相等,则统计字符串长度的计数器加1,继续比较;若比较相等,则表示该字符串结束,计数器中的值就是字符串的长度。程序如下:

ORG0000H

CONT:MOVR2,#0FFH;初始长度设置

LMOV R0,#3FH;数据指针R0置初值

LOOP1: INC R0

INC R2

NEXT: CJNE@R0,#0DH,LOOP

;((R0))=0DH?

SJMP $待测字符以ASCII码形式存放在片内RAM中,回车符的ASCII码为0DH,程序中用一条CJNE@R0,#0DH,LOOP指令实现字符比较及控制循环的任务。当循环结束时,R2的内容为字符串长度。

2)多重循环程序

如果在一个循环体中又包含了其他的循环程序,即循环中还套着循环,这种程序称为多重循环程序。

例4-12

10s延时程序。

延时程序与MCS-51执行指令的时间有关,如果使用6MHz晶振,一个机器周期为2μs,计算出执行一条指令以至一个循环所需要的时间,给出相应的循环次数,便能达到延时的目的。

程序如下:

ORG 1000H

DEL: MOV R5,#100

DEL0:MOV R6,#200

DEL1:MOV R7,#248

DEL2:DJNZ R7,DEL2 ;248*2

DJNZ R6,DEL1 ;(248*2+3)*200

DJNZ R5,DEL0 ;((248*2+3)*200+3)*100 RET 上例延时程序实际延时为10.000406s,它是一个三重循环程序。利用程序嵌套的方法对时间实行延迟是程序设计中常用的方法。使用多重循环程序时,必须注意以下几点:

(1)循环嵌套必须层次分明,不允许产生内外层循环交叉。

(2)外循环可以层层向内循环进入,结束时由里往外层层退出。

(3)内循环体可以直接转入外循环体,实现一个循环由多个条件控制的循环结构方式。4.2.4程序设计举例

1.BCD码转换为ASCII码

例4-13请编写能把20H单元内两个BCD数变换成相应ASCII码,放在22H(高位BCD数的ASCII码)和21H(低位BCD数的ASCII码)单元的程序。

根据ASCII字符表,0~9的BCD数和它们的ASCII码之间仅相差30H。因此,仅需把20H单元中两个BCD数拆开,分别与30H相加就行了。

程序如下: ORG 0000H

ASCH:MOVR0,#21H ;R0为地址指针,指向21H单元

MOV A,20H;取初值

ANL A,#0FH ;屏蔽高4位

ADD A,#30H ;加30H(即为ASCII码)

MOV @R0,A ;保存结果在21H单元

MOV A,20H ;取初值

SWAPA ;高4位和低4位交换

ANL A,#0FH ;保留低4位

ORL A,#30H ;相当于加30H

INCR0 ;修改指针

MOV @R0,A ;保存结果

SJMP $

2.加法运算

例4-14无符号的多个单字节数加法。多个单字节数(假设7个字节)依次存放在片内RAM21H开始的连续单元中,要求将计算结果存放在R1和R2中(假定相加的和为2字节数),其中R1为高位,R2为低位。

程序如下:

ORG 2000H

MOV R0,#21H;定义R0为地址指针

MOV R3,#07H;定义字节数

MOV R2,#00H;定义和的初值(低字节)

MOV R1,#00H;定义和的初值(高字节)

LOOP:MOVA,@R0 ;取加数

ADD A,R2 ;加法运算

MOV R2,A;存结果

JNC LOOP1;判断有进位?

INC R1;有进位高字节加1

LOOP1:INC R0;修改地址指针

DJNZ R3,LOOP;字节数减1,并判断为零?

JMP $

例4-15无符号的两个多字节数相加。设有两个多字节(7个)无符号数分别存放在片内RAM的单元21H和41H开始的单元中,低字节在前,高字节在后,和存放在21H开始的单元中,和的总字节个数存放在20H单元。

程序如下:

ORG 0000H

NADD:MOV R7,#07H;定义字节数

MOV R0,#21H;定义被加数的地址指针

MOV R1,#41H;定义加数的地址指针

CLR C ;进位标志清0

MOV R2,#00H

NADD1:MOV A,@R0;读被加数

ADDC A,@R1;带进位的加法运算

MOV @R0,A;存结果

INC R0 ;修改被加数的地址指针

INC R1 ;修改加数的地址指针

INC R2 ;字节数加1

DJNZ R7,NADD1

JNC LOOP ;判断有进位?

MOV @R0,#01H ;保存最高字节数运算的 进位

LOOP:MOV 20H,R2

SJMP $

3.查表程序

查表专用指令有:

MOVC A,@A+DPTR

MOVC A,@A+PC

这两条指令的功能完全相同,它们的共同优点在于不改变PC和DPTR的状态,只根据A的内容就可以查出所需数据。

例4-16

将1位十六进制数转换为ASCII码子程序。将20H单元的一个2位十六进制数转换为两个ASCII代码,并存放在20H和21H单元。方法1:

ORG 0000H

HASC:MOV R0,#20H ;R0为参数指针

MOV A,@R0 ;取出参数

ANL A,#0FH ;取字节的低4位

ADD A,#0BH ;加偏移量

MOVC A,@A+PC ;查表

XCH A,@R0 ;查表结果放回堆栈中,

重取值

SWAP A ;半字节交换ANL

A,#0FH ;取字节的高4位

ADD

A,#03H ;加偏移量

MOVC

A,@A+PC ;查表

INC

R0 ;修改指针

MOV @R0,A ;高位的ASCII码存放

SJMP $

DB ‘0123456789’

;十六进制数的ASCII字符表

DB 'ABCDEF'方法2:

ORG 2000H

HTA2:MOV R0,#20H

MOV DPTR,#STB ;表的首地址

MOVA,@R0 ;取出参数

ANL A,#0FH

MOVCA,@A+DPTR;查表

XCH A,@R0 ;低位HEX的ASCII码 放入堆栈中

SWAPAANL A,#0FH ;

MOVCA,@A+DPTR

INC R0

MOV @R0,A ;

SJMP $

ORG 2100H

STB: DB '0123456789'

DB 'ABCDEF

4.数据极值查找程序(在指定的数据区中找出最大值或最小值)

例4-17片内RAM20H单元开始存放10个无符号8位二进制数,找出其中的最大数,并存放在30H单元。极值查找流程图如图4-6所示。图4-6极值查找程序流程程序如下:

ORG 2000H

MOV R0,#20H ;数组的首地址

MOV R7,#09H ;数组长度-1

MOV A,@R0 ;读第一个数

LOOP:INC R0 ;修改指针

MOV 30H,@R0 ;读下一个数

CJNE A,30H,CHK;数值比较

CHK:JNC LOOP1;A值较大转移

MOV A,@R0 ;较大数送ALOOP1:DJNZ R7,LOOP ;继续

MOV 30H,A ;极值送30H单元

HERE: AJMP HERE

5.数据检索程序(在指定数据区中查找关键字)

一般有两种检索方法:顺序检索和对分检索。下面我们只介绍顺序检索。

例4-18

假定数据区首地址是片内RAM20H,数据的长度为10,关键字存放在30H单元中,把检索成功的数据序号放在31H单元中,若数据区中无该关键字,则31H单元存放00H标志。

检索开始应把31H单元设为00H。程序结束后,如31H单元的内容还为00H,则表示没有检索到关键字;否则即为检索成功,30H单元的内容即为关键字在数据区中的序号。程序如下:

ORG 2000H

MOV R0,#20H ;数据区的首地址

MOV R7,#0AH ;数据个数

MOV R2,#00H ;序号初值

MOV 30H,#KEY ;关键字

NEXT: INC R2

MOV A,30H ;取关键字

SUBB A,@R0 ;与关键字比较

JZ ENDP ;相同则转

INC

R0;修改指针

DJNZR7,NEXT;继续

MOVR2,#00H

ENDP:MOV31H,R2 ;送检索结果

HERE:AJMPHERE

4.2.5常用子程序

在实际程序中,常常会多次进行一些相同的计算和操作,如数制转换、函数式计算等。如果每次都从头开始编制一段程序,不仅麻烦,而且浪费存储空间。因此对一些常用的程序段,以子程序的形式,事先存放在存储器的某一区域。当主程序运行到需要用子程序时,只需执行调用子程序的指令,使程序转至子程序执行即可。子程序处理完毕,返回主程序,继续进行以后的操作。调用子程序有如下优点:

(1)避免对相同程序段的重复编制。

(2)简化程序的逻辑结构,同时也便于主程序调试。

(3)节省存储器空间。

MCS-51指令系统中,提供了两条调用子程序指令ACALL及LCALL,和一条返回主程序的指令RET。

调用子程序时,主程序应先把有关的参数(入口参数)存放在约定的位置。子程序执行时,可以从约定的位置取得参数。当子程序执行完,将得到的结果(出口参数)存入约定的位置,返回主程序后,主程序可以从这些约定的位置上取到需要的结果,这就是参数的传递。

例4-19

多字节BCD码加法。标号:BCDA。

入口条件:字节数在R7中,被加数在R0中,加数在R1中,多字节的存放高字节在前,低字节在后。出口信息:和在R0中,最高位进位在CY中。影响资源:PSW、A。BCDA: MOV A,R7

;取字节数至R2中

ADD

A,R0

;初始化数据指针

MOV

R0,A

MOV

A,R7

ADD

A,R1

MOV

R1,A

CLR

CBCD1:DEC

R0

;调整数据指针

DEC

R1

MOV

A,@R0

ADDC

A,@R1

;按字节相加

DA

A

;十进制调整

MOV

@R0,A

;和存入R0内容为地址的单

元中

DJNZ

R2,BCD1

;处理完所有字节

RET例4-20双字节二进制无符号数乘法。标号:MULD。

入口条件:被乘数在R2、R3中,乘数在R6、R7中。出口信息:乘积在R2、R3、R4、R5中。影响资源:PSW、A、B、R2~R7。堆栈需求:2字节。MULD:MOV

A,R3 ;计算R3乘R7

MOV

B,R7

MUL AB

MOV

R4,B

;暂存部分积

MOV

R5,A

MOV

A,R3

;计算R3乘R6

MOV

B,R6

MUL

AB

ADD

A,R4

;累加部分积

MOV

R4,A

CLR

A

ADDC

A,B

MOV

R3,AMOV

A,R2

;计算R2乘R7

MOV

B,R7

MUL

AB

ADD

A,R4

;累加部分积

MOV

R4,A

MOV

A,R3

ADDC

A,B

MOV

R3,A

CLR

A

RLC

A

XCH

A,R2

;计算R2乘R6

MOV

B,R6

MUL

AB

ADD

A,R3

;累加部分积

MOV

R3,A

MOV

A,R2

ADDC

A,B

MOV

R2,A

RET

例4-21ASCII码转换成十六进制数。标号:ASCH。

入口条件:待转换的ASCII码(30H~39H或41H~46H)在A中。出口信息:转换后的十六进制数(00H~0FH)仍在累加器A中。影响资源:PSW、A。堆栈需求:2字节。

ASCH:

CLR

C

SUBB

A,#30H

JNB

ACC.4,ASH1

SUBB

A,#7

ASH1: RET例4-22

单字节十六进制整数转换成单字节BCD码整数。标号:HBCD。

入口条件:待转换的单字节十六进制整数在累加器A中。出口信息:转换后的BCD码整数(十位和个位)仍在累加器A中,百位在R3中。影响资源:PSW、A、B、R3。堆栈需求:2字节。HBCD:

MOV

B,#100;分离出百位,存放在R3中

DIV

AB

MOV

R3,A

MOV

A,#10

;余数继续分离十位和个位

XCH

A ,B

DIV

AB

SWAP

A

ORL

A,B

;将十位和个位拼装成BCD码

RET

例4-23

单字节BCD码整数转换成单字节十六进制整数。标号:BCDH。

入口条件:待转换的单字节BCD码整数在累加器A中。出口信息:转换后的单字节十六进制整数仍在累加器A中。影响资源:PSW、A、B、R4。堆栈需求:2字节。BCDH:MOV

B,#10H

;分离十位和个位

DIV

AB

MOV

R4,B;暂存个位

MOV

B,#10;将十位转换成十六进制

MUL AB

ADDA,R4

;按十六进制加上个位

RET

例4-24

求单字节十六进制无符号数据块的极值。标号:MM。

入口条件:数据块的首址在DPTR中,数据个数在R7中。出口信息:最大值在R6中,地址在R2R3中;最小值在R7中,地址在R4R5中。影响资源:PSW、A、B、R1~R7。堆栈需求:4字节。MM:MOV B,R7;保存数据个数

MOVX

A,@DPTR ;读取第一个数据

MOV R6,A

;作为最大值的初始值

MOV

R7,A

;也作为最小值的初始值

MOV A,DPL ;取第一个数据的地址

MOV

R3,A

;作为最大值存放地址的初始值

MOV

R5,A;也作为最小值存放地址的初始值

MOV

A,DPH

MOV

R2,A

MOV R4,A

MOV

A,B

;取数据个数

DEC

A

;减1,得到需要比较的次数

JZ

MME

;只有一个数据,不需要比较

MOV

R1,A

;保存比较次数

PUSH

DPL

;保护数据块的首址

PUSH

DPH

MM1:INC DPTR

;指向一个新的数据

MOVXA,@DPTR ;读取这个数据

MOV

B,A

;保存

SETB

C

;与最大值比较

SUBB

A,R6

JCMM2

;不超过当前最大值,保持当前

最大值

MOV R6,B;超过当前最大值,更新最大值

存放地址

MOV

R2,DPH;同时更新最大值存放地址

MOV

R3,DPL

SJMP

MM3

MM2:MOV

A,B ;与最小值比较

CLR

C

SUBB

A,R7

JNC

MM3

;大于或等于当前最小值,保持

当前最小值

MOV

R7,B

;更新最小值

MOV

R4,DPH;更新最小值存放地址

MOV

R5,DPL

MM3:DJNZ

R1,MM1;处理完全部数据

POP DPH

;恢复数据首址

POP

DPL

MME:

RET4.3编程及开发环境

4.3.1WAVE6000MCS-51单片机编程开发环境

WAVE6000是MCS-51单片机的开发平台,其使用步骤如下:

(1)建立新程序。选择菜单[文件/新建文件],如图4-7所示。图4-7新建文件

(2)在出现的源程序窗口中输入所需编写的程序,如图4-8所示。图4-8编辑源程序

(3)保存程序。选择菜单[文件/保存文件],保存时文件名称必须带上后缀名[.asm],如图4-9所示。建议保存在一个文件夹下。

(4)仿真器设置。选择菜单[仿真器/设置/仿真器设置],在弹出的[仿真器设置]对话框中,按图4-10设置,选择[使用伟福软件模拟器]。图4-9保存文件图4-10仿真器设置

(5)编译程序。选择菜单[项目/编译],进行编译程序。若无错,则单击[好]进入下一步;若有错,则先修改程序,然后再进行编译程序。

(6)执行程序。选择菜单[执行],进行程序执行。执行方式可分为全速执行、跟踪执行、单步执行、执行到光标处,如图4-11所示。图4-11编辑窗口

(7)查看结果。选择菜单[窗口/数据窗口/DATA],如图4-12所示。DATA—片内RAM区域CODE—ROM区域

XDATA—片外RAM区域

PDATA—分页式数据存储器,51中无用BIT—位寻址区域图4-12观察结果窗口4.3.2MPLABPIC单片机集成开发环境

开发PIC单片机时使用Microchip公司提供的MPLAB集成开发环境(IDE),该环境是一个综合编辑器、项目管理器和设计平台。下面介绍其具体使用步骤。

1.创建项目

1)编写代码并保存

(1)编写代码。双击MPLABIDE编辑器桌面图标进入开发环境,选择[File/New],在工作区中打开一个空白的代码编写窗口。在该窗口中输入汇编代码,如图4-13所示。图4-13代码编写窗口

(2)文件保存。输入完代码后,选择[File/Save]保存文件。例如保存在名为D:\MyProj的新文件夹下,取名为test.asm,如图4-14所示。单击[保存]按钮,完成文件保存。图4-14文件保存窗口

2)创建项目

这里,举例使用MPLAB项目向导创建开发应用程序

项目。

(1)启动向导。选择[Project/ProjectWizard]启动项目向导,出现欢迎界面,如图4-15所示,单击[下一步]按钮继续,出现器件选择窗口,如图4-16所示。图4-15项目导向界面

3)

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论