所谓循环是指计算机反复执行某一段程序,这个程序段通常称为循环体。循环是在一定条件控制下进行的,这些条件决定是继续执行循环或是结束循环。程序循环是通过条件转移指令进行控制的。
循环程序的结构一般包括下面几个部分。
(1)置循环的初值。
用于循环过程的工作单元,在循环开始时应置初值。例如,工作寄存器设置计数初值、累加器A清零,以及设置地址指针、长度等。置初值是循环程序中的一个重要部分。
(2)循环体(循环工作部分)。
重复执行的程序段可分为循环工作部分和循环控制部分。循环控制部分每循环一次检查一次结束条件,当满足条件时,就停止循环向下继续执行其他程序。
(3)修改控制变量。
在循环程序中,必须给出循环结束条件。常见的是计数循环,当循环了一定的次数后就停止循环。在单片机中,一般用一个工作寄存器或内部RAM单元作为计数器,给这个计数器赋初值作为循环次数,每循环一次令其减“1”,即修改循环控制变量,当计数器减为零时,就停止循环。
(4)循环控制部分。
根据循环结束条件,判断是否结束循环。MCS-51可采用“DJNZ”指令来自动修改控制变量并能结束循环。
上述4个部分有两种组织方式,如图4-7所示。
(a)先循环后判断 (b)先判断后循环
图4-7 循环组织方式流程图
【例4-9】 将内部数据RAM中20H~3FH单元的内容传送到外部数据存储器以2000H开始的连续单元中去。
解:20H~3FH共计32个单元,需传送32次数据。将Rl作为循环计数器,程序流程图如图4-7所示。具体程序如下。
START: MOV R0,#20H ;设置R0为内部RAM首地址
MOV DPTR,#2000H ;设置外部RAM首地址
MOV R1,#32 ;设Rl为计数器
LOOP: MOV A,@R0 ;取内部RAM数
MOVX @DPTR,A ;送外部RAM
INC R0 ;调整内部RAM指针,指向下一个数据
INC DPTR ;调整外部RAM指针
DJNZ R1,LOOP ;未完继续
SJMP $ ;暂停
如果一个循环中包含了其他的循环程序,则称该循环程序为多重循环程序。
【例4-10】 设计20 ms延时程序。
解:延时程序与MCS-51指令执行时间有很大的关系。在使用12MHz晶振时,一个机器周期为lms,执行一条DJNZ指令的时间为2ms,20ms=2ms×10 000,由于8位的计数值最大为256,这时可用双重循环方法20ms=2ms×100×100。延时20ms的程序如下。
DL20MS:MOV R4,#100 ;20 ms=2μs×100×100,外循环初值=100
DELAY1:MOV R3,#100 ;内循环初值=100
DELAY2:DJNZ R3,DELAY2 ;100×2=200=0.2ms
DJNZ R4,DELAY1 ;0.2×100=20ms
RET
上述程序中,第2、4句要运行100次,100×(1+2)=300=0.3 ms,该段程序的延时时间约20.3 ms。若需要延时更长时间,可采用多重循环,如1秒延时可用3重循环,而用7重循环可延时几年!
【例4-11】 排序。把片内RAM 40H~49H单元中的10个无符号数逐一比较,并按从小到大的顺序依次排列在这片单元中。
解:为了把10个单元中的数按从小到大的顺序排列,可从40H单元开始,取前数与后数比较,如果前数小于后数,则顺序继续比较下去;如果前数大于后数则前数和后数交换后再继续比较下去。第一次循环将在最后单元中得到最大的数,要得到所有数据从小到大的升序排列(冒泡法)需要经过多重循环。程序流程图如图4-9所示。具体程序如下。
图4-8 程序流程图 图4-9 数据排序程序流程图
START: CLR F0 ;清除交换标志位F0
MOV R3,#9 ;10个数据循环次数
MOV R0,#40H ;数据存放区首址
MOV A,@R0 ;取前数
L2: INC R0
MOV R2,A ;保存前数
SUBB A,@R0 ;前数减后数
MOV A,R2 ;恢复前数
JC L1 ;顺序则继续比较
SETB F0 ;逆序则建立标志位
XCH A,@R0 ;前数与后数交换
DEC R0 ;指向前数单元
XCH A,@R0
INC R0 ;仍指向后数单元
L1: MOV A,@R0 ;取下一个数
DJNZ R3,L2 ;依次重复比较
JB F0,START ;交换后重新比较
RET
从上面几个例子不难看出,循环程序的结构大体上是相同的,在编写这类程序时有以下几个问题要注意。
(1)在进入循环之前,应合理设置循环初始变量。
(2)循环体只能执行有限次,如果无限执行的话,称为“死循环”,应尽量避免。
(3)不能破坏或修改循环体,尤其应避免从循环体外直接跳转到循环体内。
(4)多重循环的嵌套,应当是图4-10(a)、(b)所示的两种形式,应避免图4-10(c)的情况。由此可见,多重循环是从外层向内层一层层进入,从内层向外层一层层退出。
(a) (b) (c)
图4-10 多重循环嵌套的形式
(5)循环体内可以直接转移到循环体外或外层循环中,实现一个由多个条件控制结束的循环结构。
(6)循环体的编程要仔细推敲、合理安排。对其进行优化时,应主要放在缩短执行时间上,其次是程序的长度。
【例4-12】 在片内RAM30H~3FH连续16个单元中存放单字节无符号数。求16个无符号数之和(假定和≤0FFFFH),并存入片内RAM 41H、40 H单元中。
分析:这是重复相加问题。设R0作加数地址指针,R7作循环次数计数器,R2作和的高字节寄存器,则程序流程图如图4-11所示。编程如下。
解:
MAIN: MOV R7,#15 ;R7作循环次数计数器
MOV R2,#0 ;R2作和高字节寄存器
MOV A,30H ;取被加数
MOV R0,#31H ;R0作加数地址指针
LOOP: ADD A,@R0 ;加法运算
JNC NEXT ;CY=0,转移
INC R2 ;CY=1,高字节加1
NEXT: INC R0 ;修改R0地址指针
DJNE R7,LOOP ;未完,重复加
MOV 41H,R2 ;存和数的高8位
MOV 40H,A ;存和数的低8位
SJMP $
END
【例4-13】 把片内RAM中60H~6FH单元中的16个补码数逐一取出,若为正数则放回原单元,若为负数则求补后放回原单元。
分析:本题是求机器数的真值。补码定义:正数的补码是其本身,负数的补码是其反码加1。求机器数的真值流程图如图4-12所示。
图4-11 多字节加法程序流程图 图4-12 求机器数的真值流程图
解:编程如下。
ORG 30H
MAIN: MOV R0,#60H ;设置数据指针
MOV R7,#10H ;设置循环控制数
LOOP: MOV A,@R0 ;取数
JNB ACC.7,NEXT ;判断该数是否为负数
CPL A ;是负数,求反
INC A ;该数求反后,再加1,求出其真值
MOV @R0,A ;把该数的真值放回原处
NEXT: INC R0 ;修改数据指针,指向下一个要转换的数
DJNE R7,LOOP ;所有的数转换完否?没有,则继续
SJMP $ ;转换完,则停止
END