首页 理论教育 单片机实例分析-单片机原理与应用实例

单片机实例分析-单片机原理与应用实例

时间:2023-10-22 理论教育 版权反馈
【摘要】:本参考程序是较典型的循环程序,包含了丰富的编程知识,虽然只有20条语句,但已能实现冒泡排序循环程序的各部分设计要求。其作用是对某一现象或状态进行记录,以便识别。

单片机实例分析-单片机原理与应用实例

本参考程序是较典型的循环程序,包含了丰富的编程知识,虽然只有20条语句,但已能实现冒泡排序循环程序的各部分设计要求。显然,在分析题意确定了程序设计方案之后,要想编写出一个简明有效的程序还必须在指令的应用上狠下工夫。为了帮助读者做到这一点,下面将冒泡排序程序的结构组成分内循环初始化、内循环体、内循环控制及外循环控制四部分来讨论分析本程序的编程要点与各相关指令的应用知识。

1.内循环初始化程序段分析

对照图4-14可知,进入外循环体后,首先要对冒泡排序内循环程序的R0、R2等有关参数设置初值,称之为内循环初始化程序段,包含语句(2)、(3)、(4)、(5),其特点如下。

(1)采用工作寄存器R0作为数据块指针

1)讨论几个问题:

①什么叫数据块指针?

答:当CPU要调用RAM数据存储器某一单元的数时,能提供此RAM单元地址的寄存器称为数据指针。这是用户根据程序需要,自己设定的一种地址指针。在本程序中设R0为数据块指针,作用是在内循环中不断提供要比较的数据在数组块中的地址。

②为什么用R0作为数据块的指针?

答:关键在于MSC-51系列单片机指令系统中有@Ri为操作数的寄存器间接寻址指令。R0作为数据块指针的示意如图4-15所示。由于R0中的数是活数,当用作片内RAM地址时,就是活地址,这样当改变R0中的值时,就可用同一指令访问不同的RAM单元,起到数据指针的作用。如本程序语句(6)“MOV 20H,@R0”指令中,R0是数据块指针,是数据块某一单元的地址,改变R0中值,就可将不同数据块单元的数送入20H。

③可以用R0、R1以外的内存单元做数据指针吗?

答:不可以用R0、R1以外的内存单元做数据指针。因为在MCS-51系列单片机指令系统中,只是R0与R1工作寄存器有间接寻址功能。

2)语句(2)“BUBBLE:MOV R0,#30H”分析。

978-7-111-46831-8-Chapter04-28.jpg

图4-15 R0作为数据块指针的示意图

①功能:立即型内部数据传送指令,功能是将立即数30H送入工作寄存器R0中,即R0←30H。

②使用要点:

a.R0中的数是存放在片内RAM中数据块的地址,本指令执行后,R0中的值是数据块的起始地址。

b.R0中的数据是活数,如用INC R0指令就可使R0中的值加1,从30H开始递增,则R0中存的是数据块单元的活地址。

c.标号BUBBLE是外循环地址的标号。

(2)采用工作寄存器R2作为内循环计数器

1)讨论几个问题:

①为什么用R2来做数据长度计数器?

答:R2中的数是活数,可方便在语句(17)“BUNEXT:DJNZ R2,BULOOP”指令中用作内循环计数器,如图4-16所示。另外,用工作寄存器作为计数器时采用寄存器寻址,可节省指令字节数。

②其他的片内RAM单元可做长度计数器吗?

答:可以,但采用直接寻址指令时,指令字节数增长1个字节,机器周期数增加2,如“MOV direct,#data”为三字节,双机器周期指令。

③本程序中的数据块占片内RAM的哪些单元?这样安排的利弊是什么?还有什么其他的安排方法?

答:占片内单元的30H~4FH,注意#32等于#20H。这样安排占用了大部分片内RAM便区,使堆栈区只能是50H~7FH,因而在实用中很少这样安排。一般应将数据块放在片外RAM中,但要采用片外RAM传送指令,如“MOVX A,@R0”等。

978-7-111-46831-8-Chapter04-29.jpg

图4-16 R2在DJNZ指令中用作内循环计数器示意图

a)R2递减计数不为0时,继续本轮内循环 b)R2递减计数为0时,本轮内循环结束

2)语句(3)“MOV R2,#32”分析。

①功能:立即型内部数据传送指令,其功能是将立即数#32送入工作寄存器R2中,标作R2←20H。

②使用要点:

a.本程序中,R2中的数是数据块长度,本指令执行后,R2中存入初值32是数据总长度。

b.R2中的数是活数,可对数组长度进行计数等操作,如本程序中的“DJNZ R2,BULOOP”指令,就是用来控制程序进行冒泡排序的循环次数。

3)语句(5)“DEC R2”分析。

①功能:减1指令,寄存器寻址,功能为将工作寄存器R2中数减1,标作R2←R2-1。

②使用要点:每轮冒泡排序时,先拿出底层的数来与其他数相比较、交换,则需要比较的次数要比数组长度少1,采用减少1指令是最简便的方法。

③讨论:

a.本指令安排在外循环体后,本程序在每轮冒泡排序时的数字比较次数是多少?这意味着什么?

答:每轮都是比较31次,这意味着要浪费一半的循环次数,因为实际上每冒泡一次,数组长度就缩短。此后的比较次数也就应减1,以此类推,到最后一次冒泡比较时,只要比一次就够了。

b.本指令与“DJNZ R2,BULOOP”的功能有何区别?

答:本指令仅对R2中的数起减1作用,而“DJN2 R2,BULOOP”指令不仅减1,还要检查R2中的结果,当R2不为0时,则将标号BULOOP的值送入PC,使程序回至标号BULOOP处的指令进行循环。

(3)用位单元7FH作为外循环控制标志位 在图4-14中已说明过标志位7FH的作用,在初始化程序段中相关的语句(4)作用如下:

①功能:CLR 7FH是位操作指令,功能是对片内RAM位单元7FH清零。执行本指令后,7FH←0,表明尚未发生过换数据行为,因为7FH定义为在冒泡排序时发生数据交换7FH←1,则本指令的作用就是使7FH的初始状态为0,以表明尚未发生数据交换。

②使用要点:

a.位操作指令专用于对片内RAM的位寻址区20H~2FH和11个有位寻址单元的特殊功能寄存器进行位操作。

b.片内RAM的位寻址区中的位地址为00H~7FH,共128个位单元,因而编程时这些单元应留作标志位。

③讨论:

a.什么是标志位?有什么作用?

答:标志就是记号的意思,用来作是或否记号的位单元称之为标志位。其作用是对某一现象或状态进行记录,以便识别。

b.用户标志位与PSW状态标志位有什么区别?

答:片内RAM的128个位单元均可用作用户程序的标志位,其标志含义由用户根据程序需要自定,而PSW程序状态字的各位除PSW.5留作用户标志外,均已在单片机设计时规定其功能,如最高位为进位标志Cy等。

2.内循环体程序段分析

从图4-14可以看出,内循环体是整体程序的核心部分,通过重复执行内循环体,可以对数组中的数不断进行两两比较与交换,实现从小到大的排序。

按冒泡排序的工作过程来分,本程序的内循环体由取数、两数比较、两数交换排序三部分组成。下面对这三部分程序语句及其相关语句进行分析。

(1)取数语句分析 语句(6)、(7)、(8)、(9)为内循环体中的取数语句,其主要功能是从数组存储区中依次取出两个要比较的数。

语句(6)“BULOOP:MOV 20H,@R0”是用来取出数据区中的低字节数,并放入20H单元中。

语句(7)“MOV A,@R0”是用来将此低字节数存入累加器A,准备比较。

语句(8)“INC R0”是将数组指针R0加1,指向高字节单元。

语句(9)“MOV 21H,@R0”是将高字节数取出存入21H单元。

至此,已将数组中相邻两数分别存放在累加器A和21H单元中,为下一步两数比较与交换做好准备。取数语句作用示意如图4-17所示。

978-7-111-46831-8-Chapter04-30.jpg

图4-17 取数语句作用示意图

a)语句“MOV 20H,@R0”与“MOV A,@R0”作用示意图 b)语句“INC R0”与“MOV 21H,@R0”作用示意图

上述语句的主要特点是充分使用了数组指针来取出数据,如语句(6)“BU-LOOP:MOV 20H,@R0”是寄存器间接寻址型传送指令。其功能是以R0的内容为要找的内存单元地址,然后将该地址单元中的数送入20H单元,这是以寄存器内容为地址的寻址方式,即寄存器间接寻址。

那么,为什么要采用数组指针R0及@R0间接寻址指令来取数呢?这是因为R0作为数组指针,是活地址,通过INC R0等指令来改变R0的值,就可以在内循环体中对数组由低位到高位依次取数比较,也就是说只有采用@R0指令,才可能设计循环程序,而循环程序可大幅度缩短程序,所以@R0间接寻址的最大好处是可实行循环程序,简化程序。

顺便指出,语句(6)的标号BULOOP是程序内循环体的入口,每一轮冒泡排序过程中的31次比较与交换操作都从此标号开始。在汇编时根据“ORG 1000H”伪指令逐一存放程序指令后,前4条指令共7字节,所以标号BULOOP地址值为1007H。

关于语句(7)“MOV A,@R0”需要说明的是,由于累加器A是CPU进行算术逻辑运算的重要寄存器,专门用于存放一个操作数及算术运行结果,该语句就将低字节待比较数存入累加器A中,这样下一步在比较环节中将A与21H比较时,其结果会影响PSW状态字的标志位,程序就可据此进行冒泡排序。

关于语句(8)“INC R0”需要说明的是,这是寄存器寻址的增1指令,其功能为将工作寄存器R0中的数增1,标作R0←R0+1。

由于数组指针R0内容是活地址,在本程序中R0值增1,就为取下一个高字节的待比较数做好准备。应该指出:增1指令与减1指令是循环程序中经常使用的指令。

关于语句(9)“MOV 21H,@R0”需要说明是,本语句是以R0的内容为活地址,取出此活地址单元中的数,送入片内RAM的21H单元。

21H单元在程序中的作用是数据过渡区,用以存放一个高字节的待比较数,与20H的低字节数比较后,再将小的数放回数组低字节处,大的数放回数组高字节处,这样编程较清晰。过渡单元是编程中常用的手段。

(2)比较语句分析 语句(10)与(11)是内循环体中的两两比较语句,其功能是将累加器A中的低字节数(en)与21H单元中的高字节数(en-1)进行比较,然后分支。其中,语句(10)是比较条件转移指令,是本程序的核心指令;语句(11)是位控制转移指令,配合语句(10)进行分支。两数比较语句作用结果如图4-18所示。

单片机CPU执行语句(10)“CJNE A,21H,LOOP”时,将A与(21H)进行比较有以下三种结果:

978-7-111-46831-8-Chapter04-31.jpg

图4-18 两数比较语句作用结果示意图

a)CJNZ指令对PC和Cy的影响 b)A中的数小时JC作用示意图 c)A中的数大时的PC作用情况

①当比较结果为A<(21H)时,程序状态字中的进位标志位Cy置1,即Cy=1,表明en-1已经大于en,不需要再进行交换,PC指向下一条语句“LOOP:JC BUNEXT”,转至内循环控制语句(17),如图4-18b所示。

②当A=(21H)时,CPU执行语句(10)后,PSW中进位标志位Cy清零,Cy=0,同时程序不跳转,继续执行语句(11)、(12)后,进入两数交换过程(尽管A=(21H)并不需要交换)。

这里需要思考一下,如何修改程序,使得两数相等时,不进行交换?在比较语句之前加一条“INC,A”语句可以解决这一问题吗?

③当A>(21H)时,CPU执行语句(10)后,Cy=0,执行语句(11)后,程序不跳转,执行语句(12)后,开始两数交换,如图4-18c所示。

上述语句的特点是巧妙利用了MCS-51系列单片机的比较条件指令和位控制转移指令的配合使用。应该指出,今后编程时将会常常用到这一分支方法,下面再归纳说明一下这两条语句的功能特点。

1)语句(10)“CJNE A,21H,LOOP”是比较条件转移指令。其功能是根据A和21H中内容比较的结果来确定程序计数器PC的值。若二者相等,程序不转移,PC值为当前值不变;若二者不相等,程序转移PC值改为标号LOOP的值,并且影响PSW中Cy标志位:A≥(21H)时Cy←0;A<(21H)时,Cy←1,即有借位。(www.xing528.com)

该指令的使用要点是:

①仅对A与(21H)进行比较,不同于减法操作,即比较前后A,21H内容不变。

②要注意使用这类指令的技巧。由于此指令的主要功能是能影响Cy位来区分A>(21H)还是A<(21H),因而往往采用此指令跳转目标就是下一条指令的“怪招”,其实质是可利用JC或JNC指令来做进一步处理,进行程序分支。

针对该指令讨论3个问题:

①什么是PC当前值?执行本指令时的PC当前值是多少?

答:PC当前值就是CPU在执行某一条指令时,由PC自动加1功能预定的按顺序要取入的下一条指令在ROM中的地址。本指令在ROM中地址为100DH,本指令为3字节,所以执行本指令时的PC当前值=100DH+3=1010H。

②如何计算相对转移指令的相对地址偏移量rel,本指令的相对转移量是多少?

答:rel=转移目标地址-PC当前值,本指令转移目标地址LOOP的实际地址也是1010H,所以rel=00H。

③为什么不采用减法识别来比较A与21H?

答:因为这会改变A中内容,使程序复杂。

2)语句(11)“LOOP:JC BUNEXT”是位控制转移指令。其功能是当Cy=1时,使程序跳转到标号为BUNEXT的指令处,即PC当前值为标号BUNEXT地址值,而当Cy=0时,PC当前值不变,程序不跳转。

该指令的使用要点是:

①应用本指令的前提必须是先采用能影响Cy位的指令。在本程序中,本指令与上一条比较转移指令“CJNE A,21H,LOOP”配合使用,才能区分A≥(21H)或A<(21H)

②本指令标号LOOP的实际地址就是取执行上一句指令“CJNE A,21H,LOOP”时的PC当前值,这样可保证在任何情况下都由本指令按Cy值进行程序分支。

有2个问题值得讨论:

①本指令的相对转移量是多少?

答:相对转移量rel=转移目标地址-PC当前值,rel=1019H-1012H=07H。

②本指令若改为“JNC NEXT”指令,程序需作何修改?

答:改用JNC指令后,逢Cy=0转移,则紧接着JNC指令的应该是处理A<(21H)情况的指令,应将A送入数组低字节单元。有:

978-7-111-46831-8-Chapter04-32.jpg

可见要比原程序多用一句“SJMP BUNEXT”指令

(3)两数交换语句分析 语句(12)至(17)组成内循环体中的第三部分,即数据交换程序。这部分语句的主要功能是将数据区中要交换的数据进行交换,使大数“上浮”一层,即冒泡排序。两数交换语句作用示意如图4-19所示。

由图4-19可知,两数交换过程分为三步:

第一步,CPU执行语句(12)“MOV@R0,20H”,将内存单元20H中的大数按数据指针R0送入数据存储区的高字节中。这里,不要忘记在内循环体第一部分的取数环节中已预先将低字节中的大数存入20H这个过渡单元中,另外,数据指针R0也指向高字节单元。

第二步,CPU先执行语句(13)“DEC R0”,使数据指针指向数据区的低字节单元,然后CPU执行语句(14)“MOV@R0,21H”,就会将内存单元21H中的较小的数送入数据区的低字节单元,从而实现两数交换。

第三步,CPU执行语句(15)“INC R0”,使数组指针重新指向本次比较的高字节单元,从而为在内循环体中下一次比较(en-1)和(en-2)做好准备。

另外,语句(17)“SETB 7FH”用以记录已发生两数交换操作,供外循环控制语句(18)判断用。

978-7-111-46831-8-Chapter04-33.jpg

图4-19 两数交换语句作用示意图

a)第一步大数送入高字节 b)第二步小数送入低字节 c)第三步恢复数据指针

对照图4-19,再简要归纳一下上述语句的功能与特点。

1)语句(12)“MOV@R0,20H”是寄存器间接寻址型传送指令,其功能是将20H内容送入以R0内容为地址的内存单元。同时使用要注意:数组指针R0已指向数组高字节单元,所以本指令能将大数送入数组中高字节单元,此时20H中的数已被确定为大数。

这里有一个问题:能否用其他指令来取代本指令?

答:可以用指令“MOV@R0,A”来代替,因为此时A中内容与(20H)相同,也是低字节数。

2)语句(13)“DEC R0”是减1指令,寄存器寻址。其功能是将工作寄存器R0中数减1,标作R0←R0-1。

使用要点:

①注意数组指针R0内容是活地址,本指令使R0又指向低地址,为下一条指令把小数交换入低单位单元作准备。

②对数组指针进行减1或者增1的指令是循环程序中常用的指令。

③记住本指令又使R0指向数组低字节单元,为使程序正常进行,本程序完成交换数据后就必须使R0重新指向高字节单元。

3)语句(14)“MOV@R0,21H”的使用要点是在执行此指令之前,数组指针R0已指向数组低字节单元,所以本指令能将小的数送入数组的低字节单元。

4)语句(15)“INC R0”是针对前面交换数据时采用“DEC R0”后的必要措施,以恢复数组指针。

5)语句(16)“SETB 7FH”是在交换数据程序段的最后一句,这样在每轮冒泡排序过程哪怕只发生过1次数据交换,也必有7FH←1,表明还需进行下一轮比较。换言之,只有当一轮冒泡比较过程结束后7FH为0时,才表明已完成“冒泡”任务。所以7FH可作为程序循环是否结束的标志位。

3.内循环控制语句分析

内循环控制语句(17)“BUNEXT:DJNZ R2,BULOOP”是减1条件转移指令,如图4-14所示。其功能是将操作数R2中内容减1,若R2不为零,程序跳转到标号为BULOOP的指令处继续进行本轮冒泡排序的下一次内循环。否则,PC当前值不变,程序不跳转,准备进行下一轮冒泡排序。

该指令的使用要点:

1)本指令可以说是多次循环程序的专用指令,操作数R2是计数器,其跳转目标是内循环首地址。

2)注意本指令的判据是操作数R2-1是否等于零,而不是PSW字的标志位。

3)记住,本指令是内循环体的最后一句指令。

4)使用本指令的前提是,在程序初始化部分对操作数R2赋初值,也就是给定循环次数,相对转移地址标号=循环体的首地址标号。

下面讨论几个相关问题:

①能否用其他指令来取代本指令?

答:减1条件转移指令的操作数可以是Rn,direct,则可有许多其他指令来取代,如“DJNZ R3,BULOOP”或“DJNZ 22H,BULOOP”等。但是此时在程序其他处要统一用R3来取代R2或用22H取代R2。

②若执行本指令时,程序不跳转,R2、R0中内容分别是多少?为什么?

答:R2=0,R0=6FH。因为此时应完成内循环最后一次冒泡排序,计数器R2必为零,数组指针R0应指向最高位。

4.外循环控制语句分析

外循环控制语句(18)“JB 7FH,BUBBLE”是位控制转移指令。其功能是当内存位单元7FH置1时,程序跳转到标号BUBBLE的指令处,重复进入外循环,PC当前值改为标号BUB-BLE的ROM单元地址,若7FH为0时程序不跳转,PC当前值不变。外循环控制语句作用分析如图4-20所示。

978-7-111-46831-8-Chapter04-34.jpg

图4-20 外循环控制语句作用分析示意图

a)7FH置1进入下一次内循环 b)7FH=0冒泡结束

该指令的使用要点:

1)本指令在内循环体之后,程序进入到本指令,意味着一轮内循环(每轮要循环n-1次)结束。

2)7FH是标志位,在本程序中定义为数交换操作的标志位,若7FH为0,表明在冒泡排序过程中已无交换发生,则数组冒泡排序完毕,整个程序可结束。

3)本指令是外循环体的最后一句指令,用以决定外循环是否可以提前结束,当7FH为0时,表明数组冒泡排序完毕,则程序进入下一条指令,即跳出外循环体。

4)本指令的使用前提是:在内循环中设置数交换标志位7FH,发生数交换时7FH置1,指令的相对转移地址标号等于外循环体的首地址标号。

这里有一个问题需要讨论:为什么采用数交换标志位7FH来判断冒泡排序是否结束,而不用计数法,这样做有什么好处?

答:采用标志位时,若7FH为零就表明冒泡排序完成,这是直接检测法;而采用计数法,只能等到外循环n-1次才能结束,在程序提前完成冒泡排序时,也要继续空转下去,浪费许多程序执行时间。

5.程序结束语句分析

本程序结束语句有两条,即语句(19)和(20),下面对其作用和使用要点进行分析。

(1)语句(19)这是无条件相对转移指令,其功能是无条件跳转到标号为$的指令,由于$代表本指令地址标号,则程序在此无限循环。

该指令的使用要点:

1)MCS-51系列单片机指令系统中无停止指令,则本指令常被用作程序的结尾指令。

2)本指令使CPU处于无限循环的等待状态,此时如有中断发生,则CPU可立即响应中断,执行中断子程序后,再重新回到循环执行本指令的等待状态。

3)本指令是一条无限循环指令,相当于程序在此原地踏步,不再前进。但CPU并不停止工作,处于动态等待状态,可响应中断。而真正的停止指令,应使CPU停止工作,处于静态等待状态。

4)本指令也可写成“HERE:SJMP HERE”。

5)本指令的相对地址偏移量rel=目标地址-PC当前值=-2=0FEH(补码)。

(2)语句(20)“END”是汇编程序伪指令。其功能是在汇编时,表明程序到此结束。

该伪指令的使用要点:

1)END伪指令必须放在整个程序的最后,而不是放在主程序或某一程序的末尾。

2)在机器汇编时,汇编程序检测到END时,就不再汇编不去。

3)由于是伪指令,汇编无相应的机器码。

MCS-51系列单片机指令集中没有暂停或程序结束指令,所以在上机调试程序时,一定要注意在源程序末尾加上“SJMP$”等原地循环指令,或者采用程序末尾加设断点的调试方法。如果不这样,程序执行完最后一条指令后,PC继续加1,指向ROM的下一个单元,而该单元存放的是随机二进制数码,译码执行后,会出现死机等各种不正常现象。

END属于伪指令,没有相应的机器码,不能替代“SJMP$”指令来为CPU提供执行结束命令,其作用是源程序汇编到此结束,是汇编系统的汇编结束命令。

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

我要反馈