
3.2 单片机控制系统软件开发知识(1)
3.2.1 指令系统的概念与分类
1.指令系统的概念
我们知道,计算机如果只有硬件而没有软件(程序)是不能工作的,单片机也不例外。单片机之所以能够按照人们的意愿工作,是因为单片机运行了相应的程序,而程序是由单片机所能识别的指令组成的。
指令是CPU用来控制功能部件完成某一指定动作的指示和命令。指令是与计算机内部结构、硬件资源密切联系的,一条指令对应着一种基本操作。
某种计算机所有指令的集合称为指令系统。指令系统全面描述了CPU的功能,是由生产厂家确定的。不同的CPU具有不同的指令系统,指令系统越丰富,说明CPU的功能越强。例如,Z80单片机中没有乘法和除法指令,乘法和除法运算必须用软件来实现,因此执行速度相对较慢;而MCS-51单片机提供了乘法和除法指令,实现乘法和除法运算就要快得多。
MCS-51单片机指令系统共有33种功能,42种助记符,111条指令。单片机与一般通用微处理器指令系统的区别在于其突出了控制功能,具体表现为有大量的转移指令和位操作指令。
2.指令系统的分类
MCS-51单片机的指令系统内容丰富、完整,功能较强。
1)按指令功能分类
按指令的功能可以分为五大类:
(1)数据传送指令(29条);
(2)算术运算指令(24条);
(3)逻辑运算指令(24条);
(4)控制转移指令(22条);
(5)位操作指令(12条)。
2)按指令长度分类
单片机中的指令并不是固定的长度,对于不同的指令,在程序存储器ROM中占用的字节数是不同的。按指令长度可分为:
(1)单字节指令(49条);
(2)双字节指令(45条);
(3)三字节指令(17条)。
3)按指令执行时间分类
每条指令在执行时要消耗一定的时间,以机器周期为单位。按指令执行时间可分为:
(1)单周期指令(64条);
(2)双周期指令(45条);
(3)四周期指令(2条)。
3.2.2 指令格式及指令符号
计算机能直接识别和执行的指令是二进制代码编码指令,称为机器指令。机器指令不便于记忆和阅读。
为了编写程序的方便,人们采用有一定含义的符号(助记符)来表示机器指令,称为汇编指令。汇编指令是符号化的机器指令,与机器指令是一一对应的。汇编指令转换成机器指令之后,单片机才能识别和执行。
1.汇编指令格式
汇编指令是构成汇编语言程序的基本单元。汇编指令通常由标号、操作码助记符、操作数和注释几部分组成。一般格式为:
[标号:] 操作码助记符 [操作数1] [,操作数2] [,操作数3] [;注释]
1)标号
标号表示该指令所在的地址,是用户根据程序需要(子程序入口或转移指令目标地址)而设定的符号地址,“[ ]”表示可选项。汇编时,可以以该指令所在的首地址来代替标号。
标号是以英文字母开始的由1~8个字母或数字组成的字符串,但第1个字符必须是英文字母,不能是数字或其他符号。标号必须用冒号与操作码分隔开。
2)操作码
操作码助记符是表示指令操作功能的英文缩写,如MOV表示数据传送操作、ADD表示加法操作等。操作码是指令的核心部分,每条指令都必须有操作码,不能缺省。
3)操作数
操作数字段表示指令的操作对象,其表示形式与寻址方式有关。指令中的操作数一般有以下几种形式:
(1)没有操作数项,操作数隐含在操作码中,如指令“RET”;
(2)只有一个操作数,如指令“CPL A”;
(3)有两个操作数,如指令“MOV A,#00H”,操作数之间以逗号相隔;
(4)有三个操作数,如指令“CJNE A,#00H,NEXT”,操作数之间也以逗号相隔。
操作数和操作码之间以空格分隔,操作数之间以逗号分隔。双操作数时,逗号前面的操作数称为源操作数,逗号后面的操作数称为目的操作数。
4)注释
注释是编程者为该指令在程序中的作用或该程序段功能进行的说明,以方便程序的阅读。注释必须以“;”开始,注释的长度不限,一行不够时,可以换行书写,但换行时注意在开头仍应加“;”号。
2.指令中符号的约定
在介绍汇编指令系统时,指令中的符号约定如下。
(1)Rn(n=0~7):当前选中的工作寄存器组的工作寄存器R0~R7。
(2)@Ri(i=0、1):以R0或R1做寄存器间接寻址,“@”为间接寻址符。
(3)direct:8位直接地址。可以是内部RAM单元地址(00H~7FH),或特殊功能寄存器(SFR)地址(80H~FFH)。
(4)@DPTR:以数据指针DPTR的内容(16位)为地址的寄存器间接寻址,对外部RAM 64KB地址空间寻址。
(5)#data:8位立即数。“#”表示data是立即数。
(6)#data16:16位立即数。
(7)addr11:11位目标地址。短转移(AJMP)及短调用(ACALL)指令中为转移目标地址,2KB范围内转移,指令中用标号代替。
(8)addr16:16位目标地址。长转移(LJMP)及长调用(LCALL)指令中为转移目标地址,转移范围64KB,指令中用标号代替。
(9)rel:相对转移地址。8位补码地址,偏移量为-128~+127,指令中可用标号代替。
(10)DPTR:16位数据指针。
(11)bit:位地址。内部RAM(20H~2FH)中可寻址位和SFR中的可寻址位。
(12)A:累加器。
(13)B:B寄存器。
(14)C:即Cy位,进位标志或进位位,或位操作指令中的位累加器。
(15)/:位操作数的取反操作前缀,如“/bit”。
(16)(X):X中的内容。
(17)((X)):由X间接寻址的单元中的内容。
(18)←:箭头右边内容送入箭头所指的单元。
说明:由于历史原因,为了与大多数书籍、资料目前书写习惯一致,在表示符号位(Cy,OV,EA等)的内容时,在不会引起误解的前提下,采取BIT=X而非(BIT)=X的形式,请读者在阅读以下章节时留意。
3.2.3 寻址方式
从指令格式可知,操作数是指令的重要组成部分,指出了参与操作的对象。寻找操作数或操作数地址的方式称为寻址方式,一条指令采用什么样的寻址方式,是由指令的功能决定的。寻址方式越多,指令功能就越强。寻址方式不仅影响指令的长度,还影响指令的执行速度。
MCS-51指令系统共使用了7种寻址方式:立即寻址、寄存器寻址、直接寻址、寄存器间接寻址、变址寻址、相对寻址和位寻址。这7种寻址方式所对应的寻址空间如表3.3所示。
表3.3 寻址方式对应的寻址空间

1.立即寻址(Immediate Addressing)
立即寻址是指操作数在指令中直接给出,该操作数我们称为立即数。立即数可以是一个字节,也可以是两个字节。汇编后,立即数存放在程序存储器中。立即数前应加“#”标记,以便和直接寻址方式相区分。
立即寻址一般用于为寄存器或存储器赋常数初值。
例如指令:
MOV A,#40H ;A←40H
指令中的“40H”为立即数,该指令的功能是将立即数40H送累加器A。指令执行后(A)=40H,指令中用“#”表示立即数。
2.直接寻址(Direct Addressing)
直接寻址方式是在指令代码中直接给出操作数的地址,操作数存放在该地址的存储单元中,这种寻址方式是对内部数据存储器进行访问。
直接寻址方式的寻址空间为:
(1)片内RAM的低128字节;
(2)特殊功能寄存器SFR。
注意:对片内RAM的低128字节进行直接寻址时,以直接地址形式表示;对SFR进行直接寻址时,通常以寄存器符号(符号地址)来表示,当然也可以用直接地址来表示。
例如指令:
MOV A,50H ;A←(50H)
指令中的“50H”为直接地址,该指令是把内部RAM中50H单元(直接寻址)的内容送入累加器A中。假设指令执行前A的内容为8BH(表示为(A)=8BH),50H单元的内容为36H(表示为(50H)=36H),则指令执行后(A)=36H,(50H)=36H(不变)。
例如指令:
MOV A,P1 ;A←(P1)
指令中的“P1”为I/O口P1的符号地址,该指令也可以写为:
MOV A,80H ;A←(80H)
3.寄存器寻址(Register Addressing)
寄存器寻址方式是在指令中给出寄存器名称,寄存器的内容作为操作数。
寄存器寻址方式的可寻址空间:
(1)当前工作寄存器R0~R7(当前工作寄存器区由RS1、RS0来选定);
(2)累加器A;
(3)寄存器B;
(4)数据寄存器DPTR。
寄存器寻址方式的操作码中隐含了寄存器的编码,对当前工作寄存器R0~R7进行访问时,指令操作码的低3位指明了所用的寄存器。
例如指令:
MOV A,R2 ;A←(R2)
该指令是将工作寄存器R2的内容送给累加器A。假设指令执行前A的内容为08H(表示为(A)=08H),R2的内容为4EH(表示为(R2)=4EH),则指令执行后(A)=4EH,(R2)=4EH(不变)。
工作寄存器是内部RAM的一部分,如果选择第0组工作寄存器,则R0就是内部RAM的00H单元。那么这样一来,“MOV A,00H”和“MOV A,R0”就没有区别了吗?
的确,这两条指令执行的结果是完全相同的,都是将00H单元中的内容送到A中去,但是执行的过程不同。执行第一条指令需要2个机器周期,而第二条指令则只需要1个机器周期。第一条指令变成最终的目标码要两个字节(E5H 00H),而第二条指令则只要一个字节(E8H)就可以了。寄存器寻址方式指令代码短,执行速度快。
在SFR中,除了A、B、R0~R7和DPTR以外,其余的特殊功能寄存器只能采用直接寻址方式。
4.寄存器间接寻址方式(Register Indirect Addressing)
寄存器间接寻址方式是在指令中给出寄存器,以寄存器的内容作为操作数的地址,把该地址对应单元的内容作为操作数。可用于寄存器间接寻址的寄存器有R0、R1和DPTR。为了不与寄存器寻址相混淆,寄存器间接寻址方式中寄存器前必须加“@”(@读成“at”)。
寄存器间接寻址的可寻址空间:
(1)内部RAM 00H~7FH(@R0、@R1);
(2)外部RAM 0000H~FFFFH(@DPTR、@R0、@R1)。
在访问外部RAM的页内256个单元××00H~××FFH时,用R0、R1工作寄存器间接寻址。
在访问外部RAM整个64KB(0000H~FFFFH)地址空间时,用数据指针DPTR来间接寻址。
例如指令:
MOV A,@R0 ;A←((R0))
指令中的“@R0”表示R0间接寻址单元的内容。该指令的操作是将寄存器R0的内容(设(R0)=50H)作为地址,把片内RAM 50H单元的内容(设(50H)=48H)送入累加器A,指令执行后(A)=48H。指令中“@”表示寄存器间接寻址,称为间址符。
5.变址寻址(Base-Register-plus-Index-Register-Indirect Addressing)
变址寻址以程序计数器PC或数据指针DPTR作为基址寄存器,以累加器A作为变址寄存器(存放8位无符号的偏移量),两者内容相加形成的16位程序存储器地址作为指令操作数的地址。变址寻址用于读取程序存储器中的常数表或原始的数据。
变址寻址的寻址空间:程序存储器ROM(@A+DPTR,@A+PC)。
例如指令:
MOVC A,@A+DPTR ;A←((A)+(DPTR))
指令中“@A+DPTR”表示变址寻址的程序存储单元的内容。该指令是把DPTR的内容作为基地址,把A的内容作为偏移量,两量相加形成16位地址,将该地址的程序存储器ROM单元的内容送给A。
假设指令执行前(DPTR)=2100H,(A)=56H,(2156H)=36H。则该指执行后(A)=36H。
又例如指令:
2040H: MOVC A,@A+PC ;A←((A)+(PC))
假设指令执行前(A)=0E0H,(2121H)=45H。该指令为单字节指令,所以执行此指令时程序指针PC的当前值(PC)=2041H,则该指执行后(A)=45H。
变址寻址过程如图3.7所示。

图3.7 变址寻址过程示意图
实际上指令中有两个或三个操作数时,往往具有几种类型的寻址方式。例如指令:
MOV B,#4FH 寄存器寻址 立即寻址
3.2.4 数据传送类指令
数据传送指令共有29条,分为片内寄存器、数据存储器传送(MOV)指令,片外数据存储器传送(MOVX)指令,程序存储器访问(MOVC)指令,数据交换(XCH、XCHD、SWAP)指令和堆栈操作(PUSH、POP)指令。源操作数可以采用寄存器、寄存器间接、直接、立即、变址5种寻址方式,目的操作数可以采用前3种寻址方式。数据传送指令是编程时使用最频繁的一类指令。
数据传送指令一般的操作是将源操作数传送到目的操作数,指令执行后,源操作数不改变,目的操作数修改为源操作数。如果要求在数据传送时不丢失目的操作数,则可以用交换指令。
数据传送指令不影响专用寄存器PSW中的各标志位,即Cy、AC和OV,这里不包括检验累加器奇偶的标志位P,以后对于P一般也不加以说明。
1.片内RAM及寄存器间的数据传送指令——MOV
1)以累加器A为目的操作数的指令(4条)
以累加器A为目的操作数的指令如表3.4所示。
表3.4 以累加器A为目的操作数的指令

说明:
(1)指令的功能是将源操作数的内容送入累加器A中。源操作数可以为寄存器寻址、直接寻址、寄存器间接寻址和立即寻址方式。
(2)源操作数是Rn时,属寄存器寻址。MCS-51内部RAM区中有4组工作寄存器,每组由8个寄存器组成。可通过改变PSW中的RS0、RS1来切换当前工作寄存器组。
【例3.1】 注释下列指令操作功能。
MOV A,R4 ;A←(R4),即R4寄存器内的数据传递给累加器A MOV A,70H ;A←(70H),即70H单元内的数据传递给累加器A MOV A,@R1 ;A←((R1)),即R1寄存器内的数据作为地址,该地址内的数据传递给 ;累加器A MOV A,#88H ;A←88H,即88H这个数据传递给累加器A
【例3.2】 已知(A)=40H,(R0)=50H,内部RAM(40H)=30H,内部RAM(50H)=10H,解释下列指令的执行结果。
① MOV A,#20H
② MOV A,40H
③ MOV A,R0
④ MOV A,@R0
解:① MOV A,#20H 执行后(A)=20H。
② MOV A,40H 执行后(A)=30H。
③ MOV A,R0 执行后(A)=50H。
④ MOV A,@R0 执行后(A)=10H。
2)以Rn为目的操作数的指令(3条)
以Rn为目的操作数的指令如表3.5所示。
表3.5 以Rn为目的操作数的指令

该组指令的功能是把源操作数的内容送到当前工作寄存器区中的R7~R0中的某一个寄存器。源操作数可以为寄存器寻址、直接寻址和立即寻址方式。
【例3.3】 将A的内容传送至R1,30H单元的内容传送至R3,立即数80H传送至R7,用指令完成并注释操作功能。
MOV R1,A ;R1←(A) MOV R3,30H ;R3←(30H) MOV R7,#80H ;R7← 80H
3)以直接地址为目的操作数的指令(5条)
以直接地址为目的操作数的指令如表3.6所示。
表3.6 以直接地址为目的操作数的指令

该组指令的功能是将源操作数的内容送入直接地址所指的存储单元。源操作数有寄存器寻址、直接寻址、寄存器间接寻址和立即寻址方式。
【例3.4】 将A的内容传送至30H单元,R7的内容传送20H单元,立即数0FH传送至27H单元,40H单元内容传送至50H单元,用指令完成并注释操作功能。
MOV 30H,A ;30H← (A) MOV 20H,R7 ;20H← (R7) MOV 27H,#0FH ;27H← 0FH MOV 50H,40H ;50H ←(40H)
【例3.5】 若(R1)=50H,(50H)=18H,执行指令:
MOV 40H,@R1
结果:(40H)=18H。
4)以寄存器间接地址为目的操作数的指令(3条)
以寄存器间接地址为目的操作数的指令如表3.7所示。
表3.7 以寄存器间接地址为目的操作数的指令

该组指令的功能是将源操作数的内容送入R0或R1寄存器间接寻址所确定的内部RAM单元中。源操作数也可以为寄存器寻址、直接寻址和立即寻址方式。
【例3.6】 已知(R0)=40H,(30H)=12H,(A)=78H,求分别执行下列指令后的结果。
① MOV @R0,A
② MOV @R0,30H
③ MOV @R0,#56H
解:指令 ① MOV @R0,A 执行后(40H)=78H。
指令 ② MOV @R0,30H 执行后(40H)=12H。
指令 ③ MOV @R0,#56H 执行后(40H)=56H。
5)16位数据传送指令(1条)
16位数据传送指令如表3.8所示。
表3.8 16位数据传送指令

该指令是指令系统中唯一的一条16位数据传送指令,通常用来设置地址指针。DPTR由DPH和DPL组成,该指令传送时,把高8位立即数送入DPH中,低8位立即数送入DPL中。
【例3.7】 执行指令:MOV DPTR,#1234H后DPH中的值为12H,DPL中的值为34H。如果我们分别向DPH、DPL送数,则结果也一样。
如下面两条指令:
MOV DPH,#12H MOV DPL,#34H
相当于指令:
MOV DPTR,#1234H
【例3.8】 注释出下列程序段运行时的每一步执行结果。
MOV A,#30H ;(A)=30H MOV 4FH,A ;(4FH)=30H MOV R0,#20H ;(R0)=20H MOV @R0,4FH ;(20H)=30H MOV 21H,20H ;(21H)=30H
MOV指令用于寻址内部RAM和SFR,对内部RAM和SFR的操作功能如图3.8所示。

图3.8 MOV数据传送指令操作功能示意图
2.ROM读取指令——MOVC
程序存储器读取指令的助记符为MOVC,如表3.9所示。程序存储器为只读存储器,除了存放程序代码以外,还可以存放一些原始数据、固定表格。程序运行中所需的这些常数、表格数据,通常是由用户先将其写在程序存储器中。程序需从程序存储器中读出数据时,只能采用变址寻址方式将数据读入到累加器A中。
程序存储器(ROM)使用基址变址寻址,指令中以DPTR为ROM的16位地址指针,由P0口送出低8位地址,由P2口送出高8位地址,寻址范围为64KB。
表3.9 ROM读取指令

【例3.9】 已知(A)=38H,程序存储器ROM(1039H)=78H,写出指令:
1000H:MOVC A,@A+PC
的执行结果。
解:该指令以PC内容加1后的当前值作为基址寄存器,累加器A为变址寄存器(8位无符号整数),两者内容相加得到一个16位地址,将该地址对应的程序存储器单元的内容送给累加器A。所以上面指令的操作是将程序存储器1039H单元的内容送给累加器A。
(A)=78H。
说明:累加器A为8位无符号整数,表格位置(查表指令后255字节内)和表格长度(小于255字节)受限制。
【例3.10】 已知(A)=50H,(DPTR)=5000H,程序存储器ROM(5050H)=52H,写出指令:
MOVC A,@A+DPTR
的执行结果。
解:该指令以DPTR为基址寄存器,累加器A为变址寄存器。因此,该指令执行的操作是将程序存储器中5050H单元的内容送入A中。
(A)=52H。
“MOVC A,@A+PC”与“MOVC A,@A+DPTR”两条指令的区别:前者访问ROM的范围是相对PC当前值以后的256字节地址空间,而后者范围可达整个程序存储器64K字节的地址空间。累加器A的内容是一个8位无符号数。
注意:由于PC的内容不能利用数据传送指令进行随意更改,所以读取ROM指定单元内容的操作常用“MOVC A,@A+DPTR”这条指令。
“MOVC A,@A+DPTR”查取ROM中的数据表的使用方法为:
首先将要查表的数据字作为偏移量送累加器A中,然后将表首地址送DPTR中,最后执行该指令即可获得所需的内容。
3.伪指令——DB、ORG、END
在汇编语言中,除了可执行的指令外,为方便程序的编写,还定义了一些伪指令。伪指令是对汇编语言程序做出的一些必要说明。在汇编过程中,伪指令为汇编程序提供必要的控制信息,不产生任何指令代码,因此也称为不可执行指令。常见的伪指令如下。
1)ORG(oRigin):汇编起始地址命令
格式:ORG nn
ORG后面16位地址表示此语句后的程序或数据块在程序存储器中的起始地址。例如:
ORG 1000H START:MOV A,#32H …
上述指令说明:START表示的地址为1000H,MOV指令从1000H存储单元开始存放。
2)DB(Define Byte):定义字节数据命令
格式:[名字:] DB n1,n2,n3,…,nN
该命令表示将DB后面的若干个单字节数据存入指定的连续单元中。每个数据(8位)占用一个字节单元,通常用于定义一个常数表。
注意:“名字”也是一个符号地址,但以名字表示的存储单元之中存放的是数据,而不是指令代码,故不能作为转移指令的目标地址,这一点与标号不同。
例如:
ORG 2000H TAB1:DB 01H,04H,09H,10H …
以上伪指令汇编后从2000H单元开始定义(存放)4个字节数据(平方表),即:
(2000H)=01H,(2001H)=04H,(2002H)=09H,(2003H)=10H。
3)END:汇编结束命令
END表示汇编语言源程序到此结束,一定要放在程序末尾。
3.2.5 算法与结构化程序设计
1.算法
做任何事情都有一定的步骤。例如你想要出门旅行,总要依次确定旅游目的地、旅游路线,购买交通车票,出发等若干步骤,这些步骤都是按一定顺序进行的,缺一不可,次序错了也不妥。算法是对计算机操作步骤的描述。计算机操作的目的是对各种数据进行处理,以期得到预定的工作效果。
算法是一个良定义的计算过程,所谓良定义是指:
(1)算法应当是正确的。因为错误的算法不能达到预期的工作目的。
(2)算法应当是有穷的。即一个算法的步骤应当是有限的,同时一个算法所运行的时间也应当是有限的。
(3)算法应当是有效的。即一个算法所对应的计算机程序运行后应当输出有效的运行结果,没有效果的算法是没有实际意义的。
算法由一个或多个值作为输入,并产生一个或多个输出。算法的表示方法很多,对单片机而言,目前多以流程图来表示算法。流程图实际上就是用一些图框来表示计算机的操作步骤。用流程图表示算法具有直观形象、易于理解等特点。
美国国家标准化协会ANSI(American National Standard Institute)设定了一些常用的流程图符号,如图3.9所示。

图3.9 常用流程图符号
2.程序基本结构——顺序结构
传统的流程图对用流程线指示各框的执行顺序没有严格限制,这样容易使流程图变得毫无规律,给阅读者理解带来很大困难,也使算法的可靠性和可维护性难以保证。因此,为提高算法的质量,使算法设计和阅读方便,人们设想规定了几种基本结构,然后由这些基本结构按一定规律组成算法结构,这样就对保证和提高算法质量有很大好处。常用的基本结构由顺序结构、分支结构和循环结构等组成。
顺序结构是指程序中每一条指令都是按指令的排列顺序执行。顺序结构是最简单的程序结构,不出现分支、循环等。如图3.10所示为顺序结构示意图。

图3.10 顺序结构示意图