赞
踩
通过前面4位计算机的结构、指令和程序,我们对计算机这一事物有了比较深入的理解,但这个4位机结构简单、指令集不丰富、运算速度慢、不支持高级语言编程,基本没有多少实际应用价值,仅能用来理解计算机工作原理。
这一章我们将对Intel公司出品的8051架构单片微型计算机进行讲解,并在后续章节中陆续设计出基于8051单片机的个人计算机,并且为其开发一款简单的操作系统,最终此8051计算机可以运行SD上的应用程序,可以在8051计算机上完成BASIC程序的编辑和运行。这台个人计算机将具备硬件、操作系统和应用软件的层次架构。
下面我们从结构、指令和程序三个方面来理解8051单片机。
5.1 8051单片机体系结构
8051单片机仍然符合冯诺依曼计算机设计方案。
l 二进制表示指令和数据;
l 存储程序,自动运行;
l 运算器、控制器、存储器、输入设备和输出设备。
5.1.1 8051片上资源
l 面向控制的8位CPU;
l 布尔量处理能力;
l 64KB程序地址空间;
l 64KB数据地址空间;
l 4KB片上程序存储器;
l 128字节片上数据存储器;
l 32路I/O通道;
l 2路16位定时器/计数器;
l 1个全双工串行通信口;
l 5个中断源。
5.1.2 8051结构方框图
8051单片机的功能方框图如图5-1所示,8051单片机也包括CPU(运算器和控制器)、数据存储器、程序存储器和输入/输输出5大部分。
图5-1 8051单片机功能框图
前面我们自己设计的4位计算机,每一部分都能精确到用三极管怎样实现,8051单片机具体的硬件细节无从得知,除非Intel公司完全披露,好在我们不是设计或实现8051单片机,只是大体弄明白它的硬件结构和工作原理。
图5-2 8051单片机引脚图
8051单片机常见的产品外观就是DIP40双列直插封装形式,示意图如图5-2所所示。把整个单片微型计算机封装在一个芯片内,所以简称单片机。P00~P07、P10~P17、P20~P27、P30~P37为四个8位并行的输入/输出口,其中P0、P2、P3为双功能口。P0、P2口也可用于扩展外部程序和数据存储器使用,当用于外部扩展存储器使用时,ALE、PSEN、EA、WR、RD为系统扩展的控制线。XTAL2和XTAL1为时钟输入线。RESET为复位线。Vcc为电源、Vss为地。
5.1.3 中央处理器(CPU)
中央处理器包括运算器和控制器。运算器主要功能:
l 加、减、乘、除算数运算;
l 与、或、非等逻辑运算;
l 数据传送操作;
l 位操作;
l 十进制数调整;
l 自加1、自减1运算。
控制器主要作用是协调单片机各功能部件的动作,包括取指令、指令译码、发出微操作信号、控制存储器地址/数据传送等功能。
5.1.4 程序存储器
8051单片机的程序计数器是16位的,所以可访问的程序存储器最大为64KB,地址范围为0000H到FFFFH。实际的8051芯片片内不一定有64KB ROM,可能只有4KB、8KB等,如果需要更大的程序存储器,可以扩展片外程序存储器。如图5-3所示,8051内部有4KB ROM,外扩了60KB外部ROM。当引脚EA=1时,8051先访问片内的4KB ROM,超出4KB地址范围则访问外部ROM芯片;当EA=0时,直接忽略内部ROM,完全访问外部ROM。
图5-3 8051存储器结构图
外部程序存储器的扩展方法如图5-4所示,当访问外部程序存储器时,P0自动输出低8位的地址信息到锁存器,ALE引脚输出锁存信号,低8位地址被锁存,P2口再输出高8位地址,和锁存器的低8位地址共同构成外部ROM的地址,PSEN引脚输出读信号,ROM数据通过P0口输入8051单片机,完成外部程序存储器的读。
图5-4 8051扩展外部程序存储器
5.1.5 数据存储器
指令要对数据进行操作,指令给出参与运算数据的方式称为寻址方式,8051单片机支持的寻址方式有:寄存器寻址、直接寻址、寄存器间接寻址、立即寻址和基址寄存器加变址寄存器间接寻址。
l MOV A,#20H 把20H数据直接送入累加器A,立即寻址;
l MOV A,20H 把20H地址单元里存的数据送入累加器A,直接寻址;
l MOV A,@R0 把R0寄存器里存的地址指向内存单元里的数据送入累加器A,寄存器间接寻址;
l MOV A,R0 把R0里存的数据送入累加器A,寄存器寻址;
l MOVC A,@A+DPTR 把DPTR寄存器里的值加上累加器A里的值作为新的地址,此地址指向的存储单元里的数据送入累加器,基址寄存器加变址寄存器间接寻址。
8051数据存储器空间可分成4部分:外部64KB地址空间、内部低128字节数据存储区、内部高128字节特殊功能寄存器区、内部高128字节数据存储区,数据存储区结构如图5-5所示。
图5-5 8051单片机数据存储区结构
其中内部低128字节数据存储区可以直接地址访问,内存高128字节数据存储区只能寄存器间接访问,内部高128字节特殊功能寄存器区可以直接地址访问,外部64KB存储区使用寄存器间接访问。
8051内部有一个低128字节的直接地址访问的数据存储区和一个高128字节直接地址访问的特殊功能寄存器区,更详细的划分如图5-6所示。
图5-6 8051内部直接地址访问存储区结构
1. 工作寄存器区
内部数据存储区的00H~0FH区域为四组工作寄存器区,每组有8个寄存器,用R0~R7表示,R0~R7具体位于哪一组工作寄存器区,通过PSW程序状态寄存器的第4位(RS1)和第3位(RS0)的组合指定,如果RS1RS0=“01”,则R0~R7位于第一组工作寄存器区,即R0的地址是08H,R7的地址是0FH。一旦设定好R0~R7位于的工作寄存器区的哪一组,那么剩下的其它地址单元就可以当普通RAM使用。
2. 位寻址区
内部数据存储区的20H~2FH共16字节的存储区,除了有字节地址外,还有位地址,位地址的范围是00H~7FH。8051单片机可以用字节地址访问这段内存,也可以使用位地址访问这段内存。
比如说学生宿舍楼,每个房间都有一个房间编号,假设一个房间有8个床位,我们给床位再编一个床位号,100房间床位号是00H~07H,101房间床位号就是07H~0FH,其它房间以此类推。这样编完号后,每个床位可以用两种方式表示,一种是房间号加上第几个床位,另一种是直接的床位号,房间号好比字节地址,床位号好比位地址,表示方式不同,但访问的是同一个地方。
3. 堆栈
8051单片机在中断、子程序调用、传递参数等实际应用中,通常会用到一种称之为“栈”的数据存储结构。“栈”其实就是数据存储器中的一段内存,这段内存通过一个称为“栈指针”的特殊寄存器SP记录地址,当往“栈”里写入数据时,先把SP加1,然后把数据写入SP指向的内存单元,当从“栈”里读出数据时,把SP指向的内存单元数据读出,然后SP减1。8051单片机复位时,SP指向07H地址单元,用户可根据实际情况设置SP的指向。
片内数据存储区的低128字节除了工作寄存器区和堆栈区外,其它区域都可以作为普通的内存使用。
4. 外部RAM和IO口
8051可以扩展64KB外部数据存储器或者I/O口,其中00H~FFH范围可以用R0、R1寄存器间接寻址,0000H~FFFFH范围可以使用16 位DPTR寄存器访问。8051单片机只管访问片外地址空间,至于访问是数据存储器还是外扩的I/O设备,由用户自己设计的硬件电路决定。
5. 特殊功能寄存器
8051内部的CPU寄存器、I/O端口锁存器、串口寄存器、定时器寄存器、中断控制寄存器等特殊功能寄存器字节地址编号范围是80H~FFH,地址分配如表5-1所示。
表5-1 8051特殊功能寄存器表
累加器ACC用于存放算数逻辑运算操作中的输入数和保存运算结果。
寄存器B在乘法和除法运算中作为辅助的寄存器使用,因为两个8位数相乘结果可能是16位数,只有累加器A存不下,所以使用B寄存器配合。
PSW程序状态寄存器,主要用于存储加法/减法的进位/借位信息,指定R0~R7位于哪一组工作寄存器区,存储溢出标志位和奇偶校验位等。
IP和IE都是和中断相关的寄存器,中断机制在后面讲解。
SBUF和SCON是串口通信相关寄存器,串口也在后面讲解。
TH1、TH0、TL1、TL0、TMOD和TCON是计数器寄存器,计数器后面讲解。
DPH和DPL组合到一起就是16位的DPTR寄存器,访问片外程序存储器和片外数据存储器时,DPTR用于存储地址信息。
SP是堆栈指针,用于指示栈顶的地址。
P0、P1、P2、P3是通用I/O的寄存器,可以输出数据和读入数据。
带星花的特殊功能寄存器除了可以字节寻址外,还可以位寻址,也就是说它们还有一个位地址,位地址编号为80H~FFH,与前面可位寻址的RAM共同构成00H~FFH的256个位寻址空间。
6. 外部数据存储器的扩展
当需要外部扩展数据存储器时,接口可参考图5-7所示。P0输出低8位地址,ALE输出锁存信号,锁存器锁存低8位地址,然后P2口输出的高地址和锁存器里的低8位地址组成外部RAM的地址,如果是写入RAM数据,那么P0口输出待写入数据,WR引脚输出写信号,数据写入RAM相应地址单元,如果是读出数据,那么RD引脚输出读信号,RAM读出的数据通过P0口输入8051单片机。
图5-7 外部数据存储器的扩展结构
5.1.6 中断
CPU正在按顺序一条条执行指令的时候,有紧急事件发生了,CPU被迫中断当前正在执行的程序,转而去处理紧急事件,处理完紧急事件后再返回中断处继续执行,这种机制就叫中断,能够请求CPU中断的来源称为中断源,8051单片机中断源及入口地址如表5-2所示。
表5-2 8051单片机中断源和中断入口
中断源 | 中断入口 |
外中断0 | 0003H |
定时器0 | 000BH |
外中断1 | 0013H |
定时器1 | 001BH |
串口中断 | 0023H |
以定时器0溢出产生中断为例,当定时器0记数达到最大值溢出后,会产生一个中断标志,CPU硬件会在每一个机器周期(标准8051的机器周期是12个时钟周期)末尾检查是否有中断需要处理,如果有中断到来,就把当前执行到的程序地址压入堆栈保存,然后把定时器0对应的中断入口000BH输入到程序计数器PC,也就是CPU跳转到000BH处执行,000BH处往往安排一条跳转指令,直接跳转到定时器0的中断处理子程序并执行,在子程序末尾会安排一条中断返回指令,中断返回指令会把压入栈的地址弹出到程序计数器PC中,也就是返回到中断前程序处继续执行。
5.1.7 外围设备
8051单片机一般都有并行输入/输出口、定时器和串行通信口等几种常见的外围设备,一般外围设备都是由寄存器和功能部件构成,寄存器都有地址,往寄存器里写入/读出数据,寄存器再和功能部件产生电路上联系,最终通过控制寄存器就行控制这种外围设备。
1. P口
8051单片机有P0、P1、P2和P3共32个通用输入/输出口,输入输出口的功能,可以理解为:往P1特殊功能寄存器里写入数据,数据会通过芯片P1口的8个引脚输出,读取P1特殊功能寄存器里的数据(MOV A,P1),就相当于把P1口的8个引脚上的高低电平读入计算机。P1、P2和P3都具有第二功能,比如外扩程序和数据存储器时用到的地址和数据功能,只有P1口是纯粹的输入输出口,P1口的结构如图5-8所示。
图5-8 P1口结构图
由图5-8可见,当把数据写入特殊功能寄存器时,实际上就是写入到了图中的D触发器中,如果写入1,Q—输出0,再经过一个反相器后,引脚上正好输出一个1。如果用MOV A,P1指令,则只是读取数据,会读引脚数据;如用OR P1,#0FFH等指令,则是读出-运算-写回,会读取寄存器里数据。
另外需要注意,P0口是开漏输出,做I/O口输出1时,实际输出的是高阻态,必须接上拉电阻后才能有效输出1的功能;P1、P2、P3内部都有一个比较大的上拉电阻,也就意味着当输出1时,实际是串联了一个大电阻再输出的,所以驱动电流比较小。如果要驱动较大功率的器件,可以使用“灌电流”的方式,也就是引脚输出0,外部器件接高电平,让电流流进引脚,形成灌电流;也可以在引脚外再用一个小电阻与片内的上拉电阻并联,并联后再接外部器件,因为并联后总电阻小于外接的小电阻,所以电流驱动能力也比较大。
2. 定时器
定时器配合定时器中断,可以实现类似闹钟提醒的功能。定时器包括一个记数时钟和一个计数器,可以给计数器设置一个初始值,记数时钟频率也可选择,这样在记数时钟驱动下,计数器从初始值开始累加,当记数到最大值后发生溢出,并会产生一个定时器中断,定时器中断的间隔可以通过记数时钟频率和计数器初始值计算得出,有了定时器就可以实现定时处理事情了,比如定时扫描键盘、输出设定占空比的脉冲信号等功能。
3. 串口
不同计算机之间往往需要传递信息或传输数据,那么就需要通信,串口可以实现串行通信,并口(前面提到的P口)可以实现并行通信。一般串行通信速率较慢,但连线少,并行通信则相反。
8051单片机最简单的串口通信只需一根地线,一根数据接收线,一根数据发送线。通信双方设置好发送和接收数据的波特率,发送方把待发送的字节数据写入发送寄存器SBUF,串口设备把SBUF里数据转换成8个脉冲信号,并且添加上起始位和停止位发送出去,接收方再把收到的脉冲信号还原成字节,并存入接收方的SBUF接收寄存器,接收单片机通过读取SBUF寄存器接收数据。异步串行通信的一种帧格式如图5-8所示。
图5-8 异步串行通信帧格式
从异步串行通信的帧格式可以看出,一个数据帧通常包括1位起始位、8位二进制数据位、1位停止位。一秒钟能传输的二进制位的个数称为波特率,波特率确定了两个二进制位之间的时间间隔,接收双方只有相同的波特率才能通信成功。两个数据帧之间的时间间隔没有要求,一个数据帧除了数据位还包括起始位等无效数据,所以最终的有效数据传输速率肯定小于波特率。
常用的波特率有9600、19200、38400、57600、115200等,波特率可以通过设置8051单片机的相关寄存器进行设定。
5.2 8051指令集
二进制的指令编码方便记忆,所以设计了助记符与二进制编码对应,助记符也就是汇编语言,8051单片机总共有111条汇编指令。按指令编码字节数分类,有49条单字节指令、45条双字节指令和17条3字节指令;按指令执行周期分类,有64条单周期指令、45条双周期指令和2条4周期指令;按照指令完成的功能分类,有数据传送类、算术运算类、逻辑运算类、位操作类和控制转移类指令。
1. 8051指令集介绍
8051指令的16进制编码、助记符和功能解释如表5-3所示,其中:
l Rn 表示寄存器 R0-R7;
l Ri 表示 R0、R1;
l addr 表示直接8位地址;
l addr 11 表示 11 位地址,addr 16 表示 16 位地;
l #data 表示8位立即数,#data16表示16位立即数;
l bit 表示位地址;
l rel 表示相对地址,范围是-128到127;
l @表示间接寻址;
l ->表示数据传送;
l <->表示数据交换
l (R0)表示R0里的数据;
l (addr)表示addr地址指向的内存单元里的数据;
l ((R0))表示R0里存储的地址指向的内存单元里的数据;
l data7~0表示data的第0位到第7位。
表5-3 8051指令集表
助记符 | 指令编码 | 说明 | ||
第1字节 | 第2字节 | 第3字节 | ||
MOV A,Rn | E8~EF | 无 | 无 | (Rn)->A |
MOV A,addr | E5 | 无 | 无 | (addr)->A |
MOV A,@Ri | E6、E7 | 无 | 无 | ((Ri))->A |
MOV A,#data | 74 | 无 | 无 | data->A |
MOV Rn,A | F8~FF | 无 | 无 | (A)->Rn |
MOV Rn,addr | A8~AF | 无 | 无 | (addr)->Rn |
MOV Rn,#data | 78~7F | 无 | 无 | data->Rn |
MOV addr,A | F5 | addr | 无 | (A)->addr |
MOV addr,Rn | 88~8F | addr | 无 | (Rn)->addr |
MOV addr2,addr1 | 85 | addr1 | addr2 | (addr1)->addr2 |
MOV addr,#data | 75 | addr | data | data->addr |
MOV addr,@Ri | 86,87 | addr | 无 | ((Ri))->addr |
MOV @Ri,A | F6,F7 | 无 | 无 | (A)->(Ri) |
MOV @Ri,addr | A6,A7 | addr | 无 | (addr)->(Ri) |
MOV @Ri,#data | 76,77 | data | 无 | data->(Ri) |
MOV DPTR,#data16 | 90 | data1615~8 | data167~0 | data1615~8->DPH data167~0->DPL |
PUSH addr | C0 | addr | 无 | (SP)+1->SP (addr)->(SP) |
POP addr | D0 | addr | 无 | ((SP))->addr (SP)-1->SP |
XCH A,Rn | C8~CF | 无 | 无 | (A)<->(Rn) |
XCH A,addr | C5 | addr | 无 | (A)<->(addr) |
XCH A,@Ri | C6,C7 | 无 | 无 | (A)<->((Ri)) |
XCHD A,@Ri | D6,D7 | 无 | 无 | (A)3~0<->((Ri))3~0 |
MOVX A,@DPTR | E0 | 无 | 无 | ((DPTR))->A |
MOVX A,@Ri | E2,E3 | 无 | 无 | ((Ri))->A |
MOVX @DPTR,A | F0 | 无 | 无 | (A)->(DPTR) |
MOVX @Ri,A | F2,F3 | 无 | 无 | (A)->(Ri) |
MOVC A,@A+PC | 83 | 无 | 无 | ((A)+(PC))->A, 访问的是程序存储器 |
MOVC A,@A+DPTR | 93 | 无 | 无 | ((A)+(DPTR))->A, 访问的是程序存储器 |
ADD A,Rn | 28~2F | 无 | 无 | (Rn)+(A)->A 进位->CY |
ADD A,addr | 25 | addr | 无 | (addr)+(A)->A 进位->CY |
ADD A,@Ri | 26,27 | 无 | 无 | ((Ri))+(A)->A 进位->CY |
ADD A,#data | 24 | data | 无 | data+(A)->A 进位->CY |
ADDC A,Rn | 38~3F | 无 | 无 | (CY)+(Rn)+(A)->A 进位->CY |
ADDC A,addr | 35 | addr | 无 | (CY)+(addr)+(A)->A 进位->CY |
ADDC A,@Ri | 36,37 | 无 | 无 | (CY)+((Ri))+(A)->A 进位->CY |
ADDC A,#data | 34 | data | 无 | (CY)+data+(A)->A 进位->CY |
INC A | 04 | 无 | 无 | (A)+1->A |
INC Rn | 08~0F | 无 | 无 | (Rn)+1->Rn |
INC addr | 05 | addr | 无 | (addr)+1->addr |
INC @Ri | 06,07 | 无 | 无 | ((Ri))+1->(Ri) |
INC DPTR | A3 | 无 | 无 | (DPTR)+1->DPTR |
DA A | D4 | 无 | 无 | (A)BCD调整->A |
SUBB A,Rn | 98~9F | 无 | 无 | (A)-(Rn)-(CY)->A 借位->CY |
SUBB A,addr | 95 | addr | 无 | (A)-(addr)-(CY)->A 借位->CY |
SUBB A,@Ri | 96,97 | 无 | 无 | (A)-((Ri))-(CY)->A 借位->CY |
SUBB A,#data | 94 | data | 无 | (A)-data-(CY)->A 借位->CY |
DEC A | 14 | 无 | 无 | (A)-1->A |
DEC Rn | 18~1F | 无 | 无 | (Rn)-1->A |
DEC addr | 15 | addr | 无 | (addr)-1->addr |
DEC @Ri | 16,17 | 无 | 无 | ((Ri))-1->(Ri) |
MUL AB | A4 | 无 | 无 | (A)乘以(B),积的高字节送入A,低字节送入B |
DIV AB | 84 | 无 | 无 | (A) 除以(B),商送入A, 余数送入B |
CLR A | E4 | 无 | 无 | 0->A |
CPL A | F4 | 无 | 无 | (A)按位取反->A |
RL A | 23 | 无 | 无 | 左环移1位,第7位移入第0位 |
RLC A | 33 | 无 | 无 | 左环移1位,第7位移入CY,(CY)移入第0位 |
RR A | 03 | 无 | 无 | 右环移1位,第0位移入第7位 |
RRC A | 13 | 无 | 无 | 右环移1位,第0位移入CY,(CY)移入第7位 |
SWAP A | C4 | 无 | 无 | (A)3~0<->(A)7~4 |
ANL A,Rn | 58~5F | 无 | 无 | (A)与(Rn)->A |
ANL A,addr | 55 | addr | 无 | (A)与(addr)->A |
ANL A,@Ri | 56,57 | 无 | 无 | (A)与((Ri))->A |
ANL A,#data | 54 | data | 无 | (A)与data->A |
ANL addr,A | 52 | addr | 无 | (addr)与(A)->addr |
ANL addr,#data | 53 | addr | data | (addr)与data->addr |
ORL A,Rn | 48~4F | 无 | 无 | (A)或(Rn)->A |
ORL A,addr | 45 | addr | 无 | (A)或(addr)->A |
ORL A,@Ri | 46,47 | 无 | 无 | (A)或((Ri))->A |
ORL A,#data | 44 | data | 无 | (A)或data->A |
ORL addr,A | 42 | addr | 无 | (addr)或(A)->addr |
ORL addr,#data | 43 | addr | data | (addr)或data->addr |
XRL A,Rn | 68~6F | 无 | 无 | (A)异或(Rn)->A |
XRL A,addr | 65 | addr | 无 | (A)异或(addr)->A |
XRL A,@Ri | 66,67 | 无 | 无 | (A)异或((Ri))->A |
XRL A,#data | 64 | data | 无 | (A)异或data->A |
XRL addr,A | 62 | addr | 无 | (addr)异或(A)->addr |
XRL addr,#data | 63 | addr | data | (addr)异或data->addr |
MOV C,bit | A2 | Bit | 无 | (bit)->CY |
MOV bit,C | 92 | Bit | 无 | (CY)->bit |
CLR C | C3 | 无 | 无 | 0->CY |
CLR bit | C2 | Bit | 无 | 0->bit |
CPL C | B3 | 无 | 无 | (CY)取反->CY |
CPL bit | B2 | Bit | 无 | (bit)取反->bit |
SETB C | D3 | 无 | 无 | 1->CY |
SETB bit | D2 | Bit | 无 | 1->bit |
ANL C,bit | 82 | Bit | 无 | (CY)与(bit)->CY |
ANL C,/bit | B0 | Bit | 无 | (bit)先取反,然后与(CY)->CY |
ORL C,bit | 72 | bit | 无 | (CY)或(bit)->CY |
ORL C,/bit | A0 | bit | 无 | (bit)先反,然后或(CY)->CY |
AJMP addr11 | a10a9a80 0001 | a7a6a5a4 a3a2a1a0 | 无 | (PC)+2->PC addr1110~0->PC10~0 |
SJMP rel | 80 | rel | 无 | (PC)+2->PC,(PC)+rel->PC |
LJMP addr16 | 02 | addr1615~8 | addr167~0 | addr16->PC |
JMP @A+DPTR | 73 | 无 | 无 | (A)+(DPTR)->PC |
JZ rel | 60 | rel | 无 | (PC)+2->PC 如果(A)=0,则(PC)+rel->PC; 否则执行下一条指令 |
JNZ rel | 70 | rel | 无 | (PC)+2->PC 如果(A)不为0,则(PC)+rel->PC; 否则执行下一条指令 |
JC rel | 40 | rel | 无 | (PC)+2->PC 如果(CY)=0,则(PC)+rel->PC; 否则执行下一条指令 |
JNC rel | 50 | rel | 无 | (PC)+2->PC 如果(CY)不为0,则(PC)+rel->PC; 否则执行下一条指令 |
JB bit,rel | 20 | bit | rel | (PC)+3->PC 如果(bit)=1,则(PC)+rel->PC; 否则执行下一条指令 |
JNB bit,rel | 30 | bit | rel | (PC)+3->PC 如果(bit)=0,则(PC)+rel->PC; 否则执行下一条指令 |
JBC bit,rel | 10 | bit | rel | (PC)+3->PC 如果(bit)=1,则(PC)+rel->PC, 0->bit;否则执行下一条指令 |
CJNE A,addr,rel | B5 | addr | rel | (PC)+3->(PC) 如果(A)不等于(addr), 则(PC)+rel->(PC); 否则执行下一条指令 |
CJNE A,#data,rel | B4 | data | rel | (PC)+3->(PC) 如果(A)不等于data, 则(PC)+rel->(PC); 否则执行下一条指令 |
CJNE Rn,#data,rel | B8~BF | data | rel | (PC)+3->(PC) 如果(Rn)不等于data, 则(PC)+rel->(PC); 否则执行下一条指令 |
CJNE @Ri,#data,rel | B6,B7 | data | rel | (PC)+3->(PC) 如果((Ri))不等于data, 则(PC)+rel->(PC); 否则执行下一条指令 |
DJNZ Rn,rel | D8~DF | rel | 无 | (PC)+2->PC,(Rn)-1->Rn 如果(Rn)不等于0, 则(PC)+rel->(PC); 否则执行下一条指令 |
DJNZ addr,rel | D5 | addr | rel | (PC)+2->PC,(addr)-1->addr 如果(addr)不等于0, 则(PC)+rel->(PC); 否则执行下一条指令 |
ACALL addr11 | a10a9a81 0001 | a7a6a5a4 a3a2a1a0 | 无 | (PC)+2->PC (PC)->SP addr1110~0->PC10~0 |
LCALL addr16 | 12 | addr1615~8 | addr167~0 | (PC)+3->PC (PC)->SP addr16->PC |
RET | 22 | 无 | 无 | (SP)->PC,调用返回 |
RETI | 32 | 无 | 无 | (SP)->PC,中断返回 |
NOP | 00 | 无 | 无 | 空操作 |
2. 调用与转移
调用指令(LCALL)和转移(LJMP)指令都会带来程序的跳转,但调用指令会把当前地址压入堆栈保存,所以调用指令通过RET指令可以返回,但转移指令则不行。
3. 调用与中断
调用和中断都把当前地址压入堆栈保存,都可以返回。调用是软件行为,而中断是硬件行为,并且中断还要清除中断标志、设置中断优先级状态寄存器等操作。调用用RET返回,中断用RETI返回,RETI指令除了把保存的地址弹出到PC外,还要清除中断响应时所置位的中断优先级状态寄存器。
4. 时钟周期、机器周期、指令周期
时钟周期就是最基本的晶振周期;8051单片机的1个机器周期包括12个时钟周期,完成指令的取指、译码、读操作数、执行、写结果等流程;指令周期就是一条指令的执行需要几个机器周期,比如乘除法需要4个机器周期,其它指令都是1到2个机器周期完成。
5. 汇编伪指令
在讲解简单的4位计算机时,汇编代码是通过手工翻译成机器码的,耗时费力且容易出错,8051这里将用汇编器完成汇编语言的翻译工作。8051汇编器除了能翻译汇编指令为机器码外,还定义了很多伪指令,伪指令是用户想要传给汇编器的信息,通常不对应专门的机器码,伪指令主要有:
l ORG addr:指出后面的代码从程序存储器的addr地址处开始放置;
l DB d0,d1,d2,……,dn:把d0~dn字节数存入程序存储器,地址不确定;
l LABEL:一段汇编程序的标号,汇编器会计算出来LABEL后第一条指令的地址,代码中就可以使用LABEL,如LJMP LABEL;
l P0 EQU 80H:P0符号等价于80H,汇编器会把P0自动替换成80H;
l LED bit 87H:LED标号等价于位地址87H,汇编器把LED替换成87H;
l END:汇编代码结束,后面即使还有程序也不会再处理;
l 注释:一行汇编指令后面用“;”注释,汇编器不处理分号后内容。
5.3 汇编小程序
5.3.1 终端命令控制LED灯
通过串口终端发送命令控制两个LED的亮灭,Proteus仿真原理图如图5-9所示。
图5-9 命令控制LED灯仿真原理图
1. 程序原理
无论是写汇编程序,还是高级语言程序,基本思路都是化繁为简。在主函数中把复杂问题化为较简单的若干模块,在子函数中实现每个模块,如果模块仍然复杂,那么就进一步简化,总之就是简化、简化,一直简化到能实现为止。
比如我们这个汇编小程序,可以在主函数中分成串口初始化和接收命令,在子函数中实现命令处理,直接处理命令不好解决,那么再写一个字符串比较子函数,一步步细化到好实现为止。
主程序流程图,命令处理子程序流程图、字符串比较子程序流程图如图所示。。。。。。
2. 源代码及注释
LED1 BIT 90H
LED2 BIT 91H
;*******主程序开始*************************
ORG 0
MAIN:
;串口初始化
MOV TMOD,#20H ;选择定时器1,处于定时器工作方式2
MOV TL1,#0FDH ;根据计算公式,初始值为FDFDH时,波特率是9600
MOV TH1,#0FDH
MOV SCON,#50H ;串口工作方式1,即1-8-1,允许接收数据
MOV PCON,#00H ;波特率不加倍
SETB TR1 ;允许定时器1开始计数
;先关闭LED灯
CLR 90H
CLR 91H
;接收命令并回显,保存命令至RAM 70~7F
RCMD: MOV R0,#70H
WAIT1: JNB RI,WAIT1 ;等待接收命令
CLR RI
MOV A,SBUF
MOV @R0,A ;保存接收到的一个字符
INC R0 ;指向下一个地址
MOV SBUF,A ;发送回显
WAIT2: JNB TI,WAIT2 ;如果发送没有完成,则等待发送完
CLR TI
CJNE A,#0DH,WAIT1 ;如果没有接收到回车符,那么继续接收下一个字符
LCALL PCMD ;如果接收到回车符,认为收到了一条完整的命令,处理命令
LJMP RCMD ;重新接收新命令
;**********主程序结束**************************************************
;********命令处理子程序开始****************
PCMD: MOV R0,#60H
MOV R1,#0H
RON1: MOV A,R1 ;把on 1字符串读取到到RAM 60H处
MOV DPTR,#ON1
MOVC A,@A+DPTR
MOV @R0,A
INC R0
INC R1
CJNE A,#0DH,RON1 ;字符串以回车结尾,没回车则继续读取
LCALL CMPCMD ;命令比较,返回值存入R3,字符串相等,则R3=0
MOV A,R3
JNZ CMPON2 ;如果不是on 1,则继续比较是不是其它命令
SETB LED1 ;是on 1,则开LED1
RET ;返回,继续接收新命令
CMPON2: MOV R0,#60H
MOV R1,#0H
RON2: MOV A,R1 ;判断是不是on 2命令
MOV DPTR,#ON2
MOVC A,@A+DPTR
MOV @R0,A
INC R0
INC R1
CJNE A,#0DH,RON2
LCALL CMPCMD
MOV A,R3
JNZ CMPOF1
SETB LED2
RET
CMPOF1: MOV R0,#60H
MOV R1,#0H
ROFF1: MOV A,R1 ;判断是不是off 1命令
MOV DPTR,#OFF1
MOVC A,@A+DPTR
MOV @R0,A
INC R0
INC R1
CJNE A,#0DH,ROFF1
LCALL CMPCMD
MOV A,R3
JNZ CMPOF2
CLR LED1
RET
CMPOF2: MOV R0,#60H
MOV R1,#0H
ROFF2: MOV A,R1 ;判断是不是off 2命令
MOV DPTR,#OFF2
MOVC A,@A+DPTR
MOV @R0,A
INC R0
INC R1
CJNE A,#0DH,ROFF2
LCALL CMPCMD
MOV A,R3
JNZ CMDERR
CLR LED2
RET
CMDERR: MOV R0,#0 ;如果哪个命令都不是,那就是命令错误
SENT: MOV A,R0
MOV DPTR,#ERROR
MOVC A,@A+DPTR
JNZ PRINT
RET ;返回重新接收新命令
PRINT: MOV SBUF,A
WAIT: JNB TI,WAIT ;如果发送没有完成,则等待发送完
CLR TI
INC R0
LJMP SENT
;**************命令处理程序结束**********************
;*******字符串比较函数****************************
;70H和60H开始挨个做差,差存入R3,遇到回车返回,
;差不为0也返回,R3=0为相等,否则不等
CMPCMD: MOV R3,#0H
MOV R0,#70H
MOV R1,#60H
LOOP: MOV A,@R0
SUBB A,@R1
MOV R3,A
CJNE @R0,#0DH,CON ;回车表示比较完成
RET
CON: INC R0
INC R1
JZ LOOP ;字符相等,则比较下一个字符
RET ;只要遇到不相等,则比较完成
;***********预存入ROM中的数据************
ON1: DB 'on 1'
DB 0DH
ON2: DB 'on 2'
DB 0DH
OFF1: DB 'off 1'
DB 0DH
OFF2: DB 'off 2'
DB 0DH
ERROR: DB 'cmd error'
DB 0DH
END
3. 仿真步骤
l 新建proteus工程,并绘制如图所示原理图;
l 在新建工程目录下创建code目录,code目录里创建led.asm文件文件,把汇编源代码录入led.asm;
l Proteus->source->add/remove source files里添加源文件led.asm,并选择ASEM51,然后build all;
l 仿真原理图中双击80c51单片机,并选择程序led.hex,执行仿真;
l 通过终端输入led开关的命令,查看程序执行的情况;
l 可以任意时刻暂停仿真,单片机上右键,选择查看特殊功能寄存器、内存单元里的内容,也可以调试汇编代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。