首页 理论教育 常见汇编程序设计应用in单片机原理与接口技术

常见汇编程序设计应用in单片机原理与接口技术

时间:2023-11-16 理论教育 版权反馈
【摘要】:顺序结构程序是所有程序设计中最基本的程序结构,是应用最普遍的程序结构。它是实际编写程序的基础。所以又称“分支程序”。分支程序结构分为有条件转移和无条件转移。汇编语言程序如下:设X=-6。汇编语言本身并不限制嵌套的层数,但过多的嵌套将使程序的结构变得十分复杂,以致造成逻辑上的混乱,因此应尽量避免过多的嵌套。它们是保证循环程序正确执行所必需的。

常见汇编程序设计应用in单片机原理与接口技术

1 顺序结构程序

顺序结构程序是按程序顺序一条指令紧接一条指令执行的程序。顺序结构程序是所有程序设计中最基本的程序结构,是应用最普遍的程序结构。它是实际编写程序的基础。

例3-37:设计一个顺序结构程序,将内部RAM的30H中的数据送到内RAM的40H和外RAM的40H中,再将内RAM的30H和31H的数据相互交换。设(30H)=16H,(31H)=28H。

解:汇编语言程序如下:

ORG 0000H

SJMP STAR

ORG 0030H

STAR:MOV 30H,#16H

MOV 31H,#28H

MOV A,30H;(A)←(30H)

MOV 40H,A;(40H)←(A),(A)=16H

MOV R0,#40H;(R0)←40H

MOV P2,#00H;(P2)←00H

MOVX @R0,A;外(0040H)←(A)

XCH A,31H;(A)与(31H)数据互换

MOV 30H,A;(30H)←(A)

SJMP $

END

结果:(40H)=16H,(0040H)=16H,(30H)=28H,(31H)=16H。

2 选择结构程序(分支程序)

选择结构程序是程序执行过程中,依据条件选择执行不同分支程序的程序。所以又称“分支程序”。为实现程序分支,编写选择结构程序时要合理选用具有判断功能的指令,如条件转移指令、比较转移指令和位转移指令等。分支程序结构分为有条件转移和无条件转移。有条件分支转移程序又可分为单分支转移和多分支转移。

间接转移指令JMP @A+DPTR由数据指针DPTR决定多分支转移程序的首地址,由A的内容选择对应分支。CJNE四条比较转移指令能对两个欲比较的单元内容进行比较,当不相等时,程序实现相对转移;若两者相等,则顺序往下执行。

简单的分支转移程序的设计,常采用逐次比较法,就是把所有不同的情况一个一个地进行比较,发现符合就转向对应的处理程序。缺点是程序太长,有n种可能的情况,就需有n个判断和转移。在实际应用中,典型例子就是当单片机系统中的键盘按下时,就会得到一个键值,根据不同的键值,跳向不同的键处理程序入口。此时,可用直接转移指令(LJMP或AJMP指令)组成一个转移表,然后把该单元的内容读入累加器A,转移表首地址放入DPTR中,再利用间接转移指令实现分支转移。

例3-38:根据寄存器R2的内容转向各处理程序PRGX(X=0~n),(R2)=0,转PRG0;(R2)=1,转PRG1;…;(R2)=n,转PRGn。

解:程序段如下:

JMP:MOV DPTR,#TAB50;转移表首地址送DPTR

MOV A,R2;分支转移参量送A

MOV B,#03H;乘数3送B

MUL AB;分支转移参量乘3

MOV R6,A;乘积的低8位暂存R6

MOV A,B;乘积的高8位送A

ADD A,DPH;乘积的高8位加到DPH中

MOV DPH,A

MOV A,R6

JMP @A+DPTR;多分支转移选择

TAB50:LJMP PRG0

LJMP PRG1

LJMP PRGn

例3-39:设计比较两个无符号8位二进制数的大小,并将较大数存入高地址中的程序。设两数分别存入30H和31H中,并设(30H)=42H,(31H)=30H。

解:汇编语言程序如下:

ORG 0000H

LJMP STAR

ORG 0200H

STAR:MOV 30H,#42H;(30H)←42H

MOV 31H,#30H;(31H)←30H

CLR;C←0

MOV A,30H;(A)←(30H)

SUBB A,31H;做减法比较两数

JC NEXT;(31H)≥(30H)转

MOV A,30H

XCH A,31H;大数存入31H中

MOV 30H,A;小数存入30H中

NEXT:SJMP $

END

结果:(31H)=42H,(30H)=30H。

例3-40:已知XY均为8位二进制数,分别存在R0、R1中,试编制能实现下面函数的程序,结果送入R1中。

978-7-111-54295-7-Chapter03-49.jpg

978-7-111-54295-7-Chapter03-50.jpg

图3-18 有嵌套分支程序流程图

解:程序设计流程图如图3-18所示,为选择结构程序中的有嵌套的分支程序。

汇编语言程序如下:设X=-6(补码为FAH)。

ORG 0000H

MOV R0,#0FAH;X数赋给R0

CJNE R0,#00H,MP1;R0≠0转MP1

MOV R1,#00H;(R0)=0,则(R1)=0

SJMP MP3;转程序尾

MP1:MOV A,R0;(A)←(R0)

JB ACC.7,MP2;A的符号位=1转MP2,表明(A)<0

MOV R1,#01H;A的符号位=0,则(R1)=1

SJMP MP3;转程序尾

MP2:MOV R1,#0FFH;送-1的补码0FFH到R1

MP3:SJMP $

END

结果:(R1)=FFH。

选择结构程序允许嵌套,即一个程序的分支又由另一个分支程序所组成,从而形成多级

选择程序结构。汇编语言本身并不限制嵌套的层数,但过多的嵌套将使程序的结构变得十分复杂,以致造成逻辑上的混乱,因此应尽量避免过多的嵌套。

3 循环结构程序

循环程序按结构形式,有单重循环与多重循环。在多重循环中,只允许外重循环嵌套内重循环。不允许循环相互交叉,也不允许从循环程序的外部跳入循环程序的内部。

循环程序的结构主要由以下4部分组成:

(1)循环初始化(赋初值)

完成循环前的准备工作。例如,在进入循环体之前需给用于循环过程的工作单元设置初值,循环控制计数初值的设置、地址指针起始地址的设置、为变量预置初值等。它们是保证循环程序正确执行所必需的。

(2)循环处理(循环体)

完成实际的处理工作,反复循环执行的部分,故又称循环体。它是循环结构的核心部分。在循环体中,有的还包括改变循环变量、地址指针等有关修改循环参数部分。

(3)循环控制

在重复执行循环体的过程中,不断修改循环控制变量,直到符合结束条件就结束循环程序的执行。是控制循环与结束的部分,通过循环变量和结束条件进行控制。

(4)循环结束

这部分是对循环程序执行的结果进行分析、处理和存放。

循环处理程序的结束条件不同,相应的控制部分的实现方法也不一样,分为计数循环控制法和条件控制法。判断是否符合结束条件,若符合就结束循环程序的执行。有的修改循环参数和判断结束条件由一条指令完成,如DJNZ指令。经常使用的延时程序便是其中的典型。

1)计数循环控制结构。依据计数器的值来决定循环次数,一般为减1计数器,计数器减到0时,结束循环。计数器初值在初始化时设定。MCS-51系列单片机的指令系统提供了功能极强的循环控制指令。

DJNZ Rn,rel;以工作寄存器作控制计数器

DJNZ direct,rel;以直接寻址单元作控制计数器

计数控制只有在循环次数已知的情况下才适用。循环次数未知,不能用循环次数来控制,往往需要根据某种条件来判断是否应该终止循环。

例3-41:在内RAM的40H开始存放了一串单字节数,串长度为8,编程求其中最大值并送R7中。对数据块中的数逐一两两相比较,较大值暂存于A中,直到整串比完,A中的值就为最大值。

解:程序如下。

MOV R0,#40H;数据块首址送地址指针R0

MOV R2,#7;循环次数送R2

MOV A,@R0;取第一个数,当作极大值

LOOP:INC R0;修改地址指针

MOV B,@R0;暂存B中

CJNE A,B,$+3;比较后产生标志(Cy),$+3为下条指令的地址

JNC NEXT;Cy=0?

MOV A,@R0;更大数送A

NEXT:NOP

DJNZ R2,LOOP;循环次数结束?

MOV R7,A;存最大值

SJMP $

2)条件控制结构。在该循环控制中,设置一个条件,判断是否满足该条件,若满足,则循环结束,若不满足该条件,则循环继续。

例3-42:一串字符依次存放在内部RAM从30H单元开始的连续单元中,字符串以0AH为结束标志,测试字符串长度。

采用逐个字符依次与0AH比较(设置的条件)的方法。如果字符与0AH不等,则长度计数器和字符串指针都加1;如果比较相等,则表示该字符为0AH,字符串结束,计数器值就是字符串的长度。

解:程序段如下。

MOV R4,#0FFH;长度计数器初值送R4

MOV R1,#2FH;字符串指针初值送R1

NEXT:INC R4

INC R1

CJNE @R1,#0AH,NEXT;比较,不等则进行下一字符比较

END

最常见的多重循环是由DJNZ指令构成的软件延时程序。

例3-43:50ms延时程序。

解:软件延时程序与指令执行时间有很大的关系。在使用12MHz晶振频率时,一个机器周期为1μs,执行一条DJNZ指令的时间为2μs,可用双重循环方法的延时50ms程序。

DEL:MOV R7,#200;本指令执行时间1μs

DEL1:MOV R6,#125;本指令执行时间1μs

DEL2:DJNZ R6,DEL2;执行内循环125次,共125×2μs=250μs

DJNZ R7,DEL1;指令执行时间2μs,执行外循环共200次

RET;指令执行时间2μs

它的延时时间为[1+(1+250+2)×200+2]μs=50.603ms

注意,用软件实现延时程序不允许有中断,否则将严重影响定时的准确性。对于延时更长的时间,可采用多重的循环。

多重循环就是循环的嵌套,即一个循环程序包含了其他循环程序,也就是循环内套循环的结构形式,也称多重循环。一般内层循环完成后,外层才执行一次,然后再逐次类推,层次分明。

例3-44:设在外RAM的TAB处开始有一个ASCII字符串,该字符串以0结尾,编程把它们从P1口输出。

解:程序如下:

ORG 0000H

MOV DPTR,#TAB;设字符串首地址指针

STOUT:MOVX A,@DPTR;取字符

JZ NEXT;整串结束则转跳

MOV P1,A;

INC DPTR;修改地址指针

SJMP STOUT;没结束继续取数发送

NEXT:…;结束处理

ORG 2000H

TAB:DB XXH,XXH,…;定义ASCII字符串

DB XXH,XXH,…,00H;以0结尾

END

4 子程序结构

将那些需多次应用的、完成相同的某种基本运算或操作的程序段从整个程序中独立出来,单独编制成一个程序段,需要时进行调用,这样的程序段称为子程序。其优点是可使程序结构简单,缩短程序的设计时间,减少占用的程序存储空间。

子程序是可在主程序中通过LCALL、ACALL等指令调用的程序段,该程序段的第一条指令地址称为子程序入口地址,它的最后一条指令必须是RET返回指令,即返回到主程序中调用子程序指令的下一条指令。

(1)子程序的设计原则

1)子程序的入口地址,子程序首条指令前必须有标号。

2)主程序调用子程序,是通过调用指令来实现。

3)子程序结构中必须用到堆栈,用来进行断点和现场的保护。

4)子程序返回主程序时,最后一条指令必须是RET指令,其功能是把堆栈中的断点地址弹出送入PC指针中,从而实现子程序返回后能从主程序断点处继续执行主程序。

5)子程序可以嵌套,即主程序可以调用某子程序,该子程序又可调用另外的子程序。(www.xing528.com)

(2)子程序设计应注意的问题

1)参数的传递:在调用子程序前,主程序应先把有关参数(即入口参数)放到某些约定的位置,子程序在运行结束返回前,也应该把运算结果(出口参数)送到约定的位置/单元。可以用Rn或A传参数;用指针传参数,如DPTR、R0、R1;用堆栈传参数,如PUSH、POP指令。

2)信息的保护(现场的恢复和保护)。

利用堆栈:

PUSH ACC

PUSH PSW

PUSH B

POP B

POP PSW

POP ACC

(3)子程序的基本结构

典型的子程序的基本结构如下:

MAIN: ;MAIN为主程序入口标号

LCALL SUB;调用子程序SUB

SUB:PUSH PSW;现场保护

PUSH ACC;子程序处理程序段

POP ACC;现场恢复,注意要先进后出

POP PSW

RET;最后一条指令必须为RET

注意:在上述子程序结构中,现场保护与现场恢复不是必需的,要根据实际情况而定。

例3-45:设计一程序,由它的主程序循环调用子程序SHY。子程序SHY使连接到单片机P1口上的8个LED灯中的某个闪烁5次。主程序中的指令RL A将确定某个LED灯闪烁。

解:程序段如下:

ORG 0000H

MOV A,#0FEH;灯亮初值

STAR:ACALL SHY;调用闪烁子程序

RL A;左移

SJMP STAR;短跳到STAR,循环以上程序段为主程序,

;以下程序段为子程序,标号SHY为其入口

SHY:MOV R2,#5;闪烁子程序,闪烁5次计数

SHY1:MOV P1,A;点亮

NOP;延时

MOV P1,#0FFH;熄灭

NOP;延时

DJNZ R2,SHY1;循环

RET;子程序返回

END

本例中的子程序入口地址是标号SHY地址,子程序返回指令是RET,主程序调用该子程序的调用指令是ACALL SHY。为观察到LED灯的闪烁,要求状态时钟信号频率低,为此,单片机可采用频率很低的外部振荡器信号。

子程序内主要是一个循环结构程序,很简单。实际应用中,大多数子程序的结构具有复杂程度不等的结构。主程序调用的子程序运行时有可能改变主程序中某些寄存器的内容,PSW、A、B、工作寄存器等。这样就必须先用PUSH指令将相应寄存器内容压入堆栈保护起来(保护现场),然后再用POP指令将压入堆栈的内容弹回到相应的寄存器中(恢复现场)。一般地,保护、恢复现场方法有两种:①调用前由主程序保护,返回后由主程序恢复;②在子程序开头保护现场,在子程序末尾恢复。

5 查表程序

查表程序是一种常用程序,可避免复杂的运算或转换过程,可完成数据补偿、修正、计算、转换等各种功能,具有程序简单、执行速度快等优点。

查表是根据自变量x,在表格寻找y,使y=fx)。在单片机中,数据表格存放于程序存储器内,在执行查表指令时,发出读程序存储器选通脉冲。两条极为有用的查表指令如下:①MOVC A,@A+DPTR;②MOVC A,@A+PC。

例3-46:设计一子程序,功能是根据累加器A中的数x(0~9之间)查x的平方表y,即根据x的值查出相应的平方值y。本例中的xy均为单字节数。

解:使用指令MOVC A,@A+DPTR时不必计算偏移量,表格可以设在64KB程序存储器空间内的任何地方,而不像MOVC A,@A+PC那样只设在PC下面的256个单元中。

程序指令可以用如下形式:

PUSH DPH;保存DPH

PUSH DPL;保存DPL

MOV DPTR,#TAB

MOVC A,@A+DPTR

POP DPL;恢复DPL

POP DPH;恢复DPH

RET

TAB:DB 00H,01H,04H,09H,10H;平方表

DB 19H,24H,31H,40H,51H

说明:如果DPTR已被使用,则在查表前必须保护DPTR,且结束后恢复DPTR。实际查表程序设计中,有时x为单字节数,y为双字节数,也有时xy都是双字节数,如下例所示。

例3-47:以STC89C52为核心的温度控制器,温度传感器输出的电压与温度为非线性关系,传感器输出的电压已由A-D转换为10位二进制数。测得不同温度下的电压值数据构成一个表,表中温度值为y(双字节无符号数),x(双字节无符号数)为电压值数据。设测得电压值x放入R2、R3中,根据电压值x查找对应的温度值y,仍放入R2、R3中。

解:参考程序段如下:

LTB:MOV DPTR,#TAB1

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;(R2R3)+(DPTR)→(DPTR)

CLR A

MOVC A,@A+DPTR;查第一字节

MOV R2,A;第一字节存入R2中

CLR A

INC DPTR

MOVC A,@A+DPTR;查第二字节

MOV R3,A;第二字节存入R3中

RET

TAB1:DW;温度值表

例3-48:将1位十六进制数转换为ASCII码。设1位十六进制数放在R0的低4位,转换为ASCII后再送回R0。(用查表法设计程序,使用查表指令MOVC A,@A+DPTR)

解:设计程序如下:

ORG 0000H

MOV R0,#0BH;设(R0)=BH

MOV A,R0;读数据

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

MOV DPTR,#TAB;置表格首地址

MOVC A,@A+DPTR;查表

MOV R0,A;回存

SJMP $

ORG 0050H

TAB:

DB 30H,31H,32H,33H,34H,35H,36H,37H,38H,39H;0~9的ASCII码

DB 41H,42H,43H,44H,45H,46H;A~F的ASCII码

END

结果:用查表法查得1位十六进制数B的ASCII码为42H。

6 延时程序

在单片机应用系统中,延时程序是经常使用的程序,一般设计成具有通用性的循环结构延时子程序。在设计延时子程序时,延时的最小单位为机器周期,所以要注意晶振频率。

例3-49:当晶振频率为12MHz时,设计延时20ms的子程序。

解:为便于理解调用子程序过程和子程序的通用性,将程序设计为主程序中调用子程序方式。

程序设计如下:

ORG 0000H

LCALL YASH20;调用延时20ms子程序

SJMP $

YASH20:MOV R7,#100;延时20ms子程序

AA0:MOV R6,#49

AA1:NOP

NOP

DJNZ R6,AA1

NOP

DJNZ R7,AA0

NOP

RET;子程序返回

END

结果:经Keil软件运行测得延时20.006ms。

7 码制转换程序

在单片机应用程序的设计中,经常涉及各种码制的转换问题。在单片机系统内部进行数据计算和存储时,多采用二进制码。二进制码具有运算方便、存储量小的特点。在输入/输出中,按照人的习惯多采用代表十进制数的BCD码(用4位二进制数表示的十进制数)表示。

(1)二进制(或十六进制)数转换成BCD码

十进制数常用BCD码表示。而BCD码又有两种形式:一种是1个字节放一位BCD码,它适用于显示或输出;另一种是压缩的BCD码,即1个字节放两位BCD码,高4位、低4位各存放一个BCD码,可以节省存储单元

单字节二进制(或十六进制)数在0~255之间,设单字节数在累加器A中,转换结果的百位数放在R3中,十位和个位同时放入A中。除法指令完成的操作为:A除以B的商放入A中,余数放入B中。

例3-50:将单字节二进制数转换成BCD码。

解:设计程序如下:

ORG 0000H

MOV A,#89H;十六进制数89H送A中

MOV B,#100;100作为除数送入B中

DIV AB;十六进制数除以100

MOV R3,A;百位数送R3,余数在B中

MOV A,#10;分离十位和个位数

XCH A,B;余数送入A中,除数10在B中

DIV AB;分离出十位在A中个位在B中

SWAP A;十位数交换到A中的高4位

ADD A,B;将个位数送入A中的低4位

SJMP $

END

结果:(R3)=1,(A)=37H,89H的BCD码为137。

(2)BCD码转换成二进制(或十六进制)数

例3-51:将两位压缩BCD码按其高、低4位分别转换为二进制数。

解:设计程序如下,将两位压缩BCD码存放在R2中,将其高、低4位分别转换为二进制数,并存于R3中。

STAR:MOV R2,#89H;表示BCD码为89

MOV A,R2;(A)←(R2)

ANL A,#0F0H;屏蔽低4位

SWAP A;高4位与低4位交换

MOV B,#10;乘数

MUL AB;相乘

MOV R3,A;(R3)←(A)

MOV A,R2;(A)←(R2)

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

ADD A,R3;(A)←(A)+(R3)

MOV R3,A;(R3)←(A)

SJMP $

END

结果:BCD码89,转换为十六进制为59H,放在R3中。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈