您好,欢迎来电子发烧友网! ,新用户?[免费注册]

当前位置:电子发烧友网 > 图书频道 > 电子 > 《单片机原理与应用》 > 第4章 汇编语言程序设计

第4节 程序设计举例

【例4.1】拆字程序。将一个字节的两个BCD码十进制数拆开并变成相应的ASCII码,并存入两个RAM单元中。

设两个BCD码(一个字节)已存入在内部RAM的30H单元中,变换后的ASCII 码分别存放在31H和32H单元,且高位BCD码的ASCII码的ASCII码存于31H单元。数字0~9的ASCII为30H~39H,完成拆字转换只需将一个字节的两个BCD码拆开存放在另两个单元的低4位,并在其高4位赋以0011即可。程序段清单如下:

MOV  R0,#32H        ;将32H单元地址送R0

MOV  @R0,#00H      ;32H单元清0

MOV   A,30H         ;将30H单元中的BCD送A

XCHD  A,@R0         ;将低位BCD码送32H单元

ORL   32H,#30H       ;完成低位BCD码转换

SWAP  A                ;将高位BCD码交换到低位

ORL  A,#30H           ;完成高位BCD码转换

MOV  31H,A            ;将高位BCD的ASCII码存入31H

上述程序段完成了将一个字节的BCD码转换成两个ASCII码的功能。共需占用15个程序存储器字节单元,用9个机器周期执行完毕。

【例4.2】 双字节加法程序段

设被加数存放在内部矛盾RAM的31H、32H单元,低位字节在前,加数存入于34H、35H单元(低字节在前),结果和存放于31H、32H、33H单元中。其程序段清单如下;

STRT:PUSH  A         ;将A内容进栈保护

MOV  R0­,#31H         ;将地址码送R0和R1

MOV   R1,#34H

MOV   33H,#00H  ;将33H单元清0,存放和的最高字节数

MOV   A,R0      ;两低字节数相加

ADD   A,@R1

MOV   @R0,A    ;低字节和存于31H单元

INC    R0            ;地址数分别加1

INC     R1

MOV   A,@R0      ;连用低位进位进行高字节数

ADDC  A,@R1      ;相加

MOV   @R0,A      ;高字节和存于32H单元

INC    R0           ;R0指针指向33H单元

MOV   A,#00H     ;清A为0

ADDC  A,#00H     ;求高字节和的进位

MOV   @R0,A      ;将高字节进位存于33H单元

POP    A            ;恢复A原内容

【例4.3 】 求双字节补码程序段

设:存于内部RAM的addr1和addr1+1单元的双字节数读出取补后存入addr2和addr2+1单元中,其中高字节数存于高地址单元,图4.7为双字节数求补流程图

 

图4.7双字节数取补流程图 

8位字长的单片机对双字节数取补需分两次进行,首先对低字节数取补后判其结果是否为0,若为全0,则要对高字节数取补,否则取反即可。双字节数取补程序估如下:

START:MOV  R0,#addr1     ;原码低字节数地址送R0

MOV  R1,#addr2     ;补码低字节数地址送R1

MOV  A,@R0       ;原码低字节数送A

CPL   A             ;对低字节数取补

INC   A              

MOV  @R1,A       ;低字节补码存入addr2单元

INC   R0           ;R0、R1内容分别加1

INC   R1         

JZ    ZERO         ;判(A)=0?当(A)=0,则转ZERO

MOV  A,@R0       ;原码高字节数送A

CPL   A             ;对A内容取反

MOV  @R1,A         ;高字节数补码存addr2+1

SJMP  LOOP1         ;转结束

ZERO:MOV  A,@R0       ;低字节取补后为0,则对高字节数取补

CPL   A

INC   A

MOV   @R1,A      ;将高字节补码存addr2+2

LOOP1:END                  ;结束

所有条件判跳指令均属相对寻址方式,其相对偏移量是一个带符号的8位二进制码,常以补码形式出现。其寻址范围为-128~+127B。编程时应多加注意。

【例4.4 】 由累加器A在动态运行中给出的结果值选择对应的转移指令,其对应关系为:

(A)=0,转向分反处理程序0

(A)=1,转向分支处理程序1

(A)=n,转向分支处理程序n

一般转移指令均为有条件转移指令,而MCS-51单片机有两条无条件转移指令:AJMP和LJMP。前者为双字节指令,后者为三字节指令,因此,需视选用何种跳转指令而应对A中值作相应变换。如选用AJMP指令,则应对A值变换成偶数值;如选用LJMP指令,则应对A值乘3的变换。每个分支处理程序均为各自独立的等程序段,分散在各自的程序存储器区段。要皮,必须有一个中转站,轩向各自的分支处理程序。这个无条件转移指令串的首地址由DPTR指示。现以AJMP为例,其程序段如下:

START:MOV  DPTR,#addr16     ;跳转指令串首址送DPTR

           CLR  C                   ;清C为0

           RLC  A                   ;将A值变换成偶数

           JNC  TABEL               ;判(C)=0?不为0则转

           INC  DPH                 ;(C)=1,则DPH内容+1

TABEL:JMP  @A+DPTR            ;散转

ADDR16:AJMP LOOP0             ;无条件转移指令串

          AJMP LOOP1

          AJMP LOOPn

LOOP0:…                       ;分支程序段0

LOOP1:…                       ;分支程序段1

由于上例选用绝对值转移指令AJMP是双字节指令,因此,要求A中内容必须换算成偶数。换算方式可以乘2,而这里采用左移一位的办法来实现。如果n值等于或大于128,则左移一位将产生高位进位,将进位值加到DPH中去,等于将转移指令串首址延伸256个存储单元,所以在程序段中对C进行测判。这样,保证分支处理程序段可以在0-255个中任选。

如果选用长调用LJMP,它是怎么样三字节指令,在进A值换算时应乘3处理,将积的高字节值加到DPH中去。一般,DPTR+A的最终值应不超过64KB范围。

【例4.5】 采用循环程序实现延时,一般可达到任意延时要求,但需牺牲CPU的工作。

MOV   40H,#data           ;设置计数初值

AGIN:   NOP

NOP

DJNZ   40H,AGIN           ;当(40H)-1≠0则继续循环

上例每循环一次共需4个机器周期,实际总延时由40H单元内容和主频(fosc)的频率所设置决定。

【例4.6】 数据块搜索

设外部RAM从BLOCK单元开始有一个无符号数据块,其长度(即数据块个数)存于LEN单元,试求出数据块 最大值数据,并存于MAX单元中。

寻找最大值的方法很多,最基本的方法是比较和交换依次进行,即先读取第一个数与第二个数相比较,并把前一个数作为基准。比较结果:若基准数在,则不作交换,再取下一个数进行比较;若基准数小,则将大数取代原基准数,即作一次交换,然后再以新的基准数与下一个数作比较,直到全部比较完毕。基准数始终保持为最大数值。图7.6为数据块搜索流程图。

设R1中存放基准数,R3为数据长度,R2中存放每次读出的新的数据块。

其循环程序段如下;

START:CLR   A               ;A清0

CLR    R1                    ;清R1为0

MOV   R3,LEN                ;数据块长度送R3作控制计数

LOOP:MOVX A,@DPTR         ;读数据块

INC   DPTR                    指向下一个单元

MOV   R2 ,A                 ;将读出的灵敏据块送R2

CLR   C                      ;清C为0

MOV   A ,R1                 ;基准值送A

SUBB  A,R2                  ;基准数一读出数

JNC   NEXT                   ;(C)=0,即(A)≥(R2),跳转

MOV  R1,A   

NEXT:DJNZ   R3LOOP           ;判搜索完否

MOV  MAX,R1                  ;最大数据存入MAX单元

END                           ;结束

【例4.7 】 工作单元清0

设R1中存放被清0单元首地址,R3中存放清0字节数,则其循环程序如下:

START;MOV  R1,#addr         ;清0单元首地址送R1

MOV  R3#data                 ;清0字节数送R3

CLR   A                      ;A清0

LOOP:、MOV  @R1A          ;指定单元清0

INC   R1工                 ;指向下一个单元

DJNZ   R3,LOOP               ;(R3)-1≠0,继续清0

END                            ;结束

【例4.8】  4位BCD码整数转换成二进制整数

入口参数:BCD码字节地址指针R0,位数存于R2中。

出口参数:二进制数存于R3R4中。

算法:A=103a3+102­a2+10a1+a0

程序流程如7.7所示。

子程序清单如下:

BCDA:PUSH  PSW               ;现场保护

PUSH  A

PUSH   B

MOV   PSW,#08H

MOV   R3,#00H

MOV   R2,#3          ;BCD码D的位数

MOV   A,@R0             ;a0-R4

MOV   R4,A

BCKB:MOV   A,R3           ;(R3R4)×10

MOV   B,#10          ;R4

MUL   AB

MOV   R4,A

XCH   A,B

MOV   B,#10

XCH   A,R3

MUL   AB

ADD   A,R3

XCH   A,R4

INC    R0                           ;(R0)+1-R0

ADD   A,@R0           ;( R3R4)- ((R0))-RR3R4

XCH   A,R4

ADDC  A,#0

MOV   R3,A

DJNZ   R2,BCDB             ;循环n-1次

POP    B                    ;恢复现场

PIP     A

POP    PSW

RET                          ;返回

上例中的R2内容是BCD码的位数n,本例中n=4,即两个字节4位BCD码,在程序中作为循环控制寄存器的计数值为n-1=4-1=3,即本例循环3次即完成二次字节的BCD码转换。

本例采用乘10运算,也可采用除2运算进行转换。

【例4.9】 多字节十进制数加法子程序

入口参数:将R0指针的内部RAM中N个字节的BCD码被加数,R1指针的内产RAM中N个字节的BCD码中数进行相加。R2中存放字数N。

出口参数:相加结果的BCD码和数存入R0指针的内部RAM(原被加数单元)中。

图4.8为多字节BCD码加法子程序流程图。子程序清单如下:

 

图4.8多字节BCD码加法子程序流程图

BCDADD:PUSH  PSW

PUSH  A

MOV   PSW,#08H

MOV   A,R0

ADD   A,R2

MOV   R0,A

DEC   R0

MOV   A,R1­

ADD   A,R2

MOV   R1,A

DEC   R1

CLR   C

ADDA:MOV   A,@R0                                                         

ADDC  A,@R1

DA     A

MOV   @R0,A

DEC    R1

DEC    R0

DJNZ   R2,ADDA       ;判加完否

JNC    ADDB           ;若(C)=0则转ADDB

MOV   A,#00H         ;清A为0

ADDC  A,#00H         ;若(C)=1,则为最高位进位

MOV   @R0,A

ADDB:POP   A             ;现场恢复

POP   PSW

RET                   ;返回

本例中两数均按高位字节数存放于低地址单元,而相国运算则从低位字节数开始,R0、R1指针在运算前均指向最高字节数的地址,故需转换成指向最低字节数的地址,然后进行相加运算。

【例4.10】 应用系统采用LED七段显示,可将BCD码或十六进制码以及某些符号通过查找LED显示编码进行显示输出。

七段LED显示器有共阴极和共阳极之分,其显示编码是不同的,共阳极是低电平为有效输入;共阴极则是高电平输入有效,LED的编码不是序码,即表格无规律可寻,可根据各自方便排列。例如BCD码显示,同是其显示码可按0~9的顺序排列。假如显示器是共阳极的,则BCD码与显示器编码的对应关系为:0的显示编码为0100 0000,即40H,1的编码为0111 1001,即79H;2的编码为24H,3的编码为30H,…,对于这类问题,通过查表来实现比较方便。具体程序段如下:

MOV  DPTR,#LEDA         ;表格首址送DPTR

MOV  A,@A+DPTR      ;变址寄存器A中内容为查表主序号,                                                                即要显示的内容,如0,1,2,…,9

MOVC   A,@A+DPTR         ;读出要显示的内容

                             ;进行LED显示

LEDA:DB    40H,79H,24H,30H,12H

DB    20H,78H,00H,18H,…

对于这类问题,一般均选用DPTR作为基址指针的查表指令为宜。这样,表格串可设置在64KB的任何地址段,可实现任意次查表。