1.Preview
要用汇编语言编程,前提必须先熟知计算机的内部结构,尤其是要了解寄存器。本文将详细介绍MCS-51系列单片机的系统结构。
2.Content
> 1.总体架构
(图来自吉林大学计院单片机控制与应用实验附录资料):

外部ROM和外部RAM是虚线,是因为他们不一定存在,取决于我们是否外接外部RAM和外部ROM。 网上有很多类似的结构图,但大同小异,这种图更多像是一种概念图,是根据功能,把各部件抽象出来形成的一种抽象概念图。 所以我搬来了Wikipedia上MCS-51词条下的结构示意图,

这个看起来更具体、更接近真实的硬件电路实现一些。 而如果要真的对51单片机建立起一个十分具象的认知,恐怕唯有拿起螺丝刀去拆机器了。
在对总体结构有了初步认识后,接下来我们将分开介绍51单片机的各个模块。
> 2.运算器(not that important)
不用多说,就是运算器(ALU, arithimetic and logic unit),支持加减乘除移位等运算的运算器。
实际上看此文的多数人想必都是学过常规的处理器架构的,窃以为运算器并不是单片机的特别之处,其就和一般的8086等CPU的运算器没有太大区别,初学可不必将重点放于此。
> 3.寄存器/数据存储器RAM
重点来了,刚才说运算器不是重点,那么此时重点来了——寄存器。
其实,个人认为,寄存器是我们在学习一款计算机架构时最应该首先关注的部分,也是汇编语言编程最重要、最常用的部分。我们学过大学的硬件课程都有体验,要用汇编写出加减、分支、循环等简单程序,可以对存储器、接口不太了解,但必须清楚每一个寄存器的具体功能,一般掌握了寄存器之后,课程之初的一些简单任务也能基本完成。
单片机的寄存器,有着区别于常规CPU的特殊之处,那就是没有寄存器堆,寄存器是存储在RAM里的,也就是在数据存储器里。是的,我们曾经在学习计组时按照大黑书的讲解用Logisim自己搭建过CPU,也曾学过8086:
(我根据计算机组成与设计 硬件/软件接口自己搭建的CPU)

8086的系统结构:
我们可以明显的看见,以前学过的CPU中,都有一个“寄存器堆”,寄存器和存储器是分离的,而单片机的特殊之处就在于,寄存器和存储器是合并的。 这也就是为什么把寄存器和RAM放在一起讲的原因。
MCS-51单片机的内部RAM的寻址空间为256字节,一个单元长度为1字节,总共256个地址,从00H到FFH。
寄存器长度1字节。
RAM总共分为四个区域:内部工作寄存器区、位寻址区、堆栈区、通用数据区、特殊寄存器区,总共256字节,但实际上一般提供给用户使用的只有前128字节,因为后128字节为特殊寄存器保留,不得随意修改
)1.工作寄存器区
工作寄存器区,从00H到1FH,总共占用32个单元,每个单元是一个工作寄存器,也就是32个工作寄存器,分为四组,每组8个。结构如图:

所以,使用某工作寄存器,其实和使用其对应位置的存储单元是等价的,
在同一时刻,只能使用一组工作寄存器,这一组数有PSW的第三、四位指出,三四位为00,01,10,11时,分别代表正在使用第1组,第2组,第3组,第4组。
程序可以通过修改PSW的第三第四位,来实现对工作寄存器组的选择。
)2.位寻址区
紧接着内部工作寄存器区,20H到2FH是位寻址区,由于此区域结构简单,就不再画图了,
位寻址,即你可以具体操作到某一单元的某一位,只需通过特定的指令,我们在将MCS-51汇编时会讲。
)3.堆栈区
MCS-51 的堆栈理论上可以设置在内部 RAM 的任意区域,但由于 0-1FH 和 20-2FH 区域有上面说的特殊功能,因此一 般设置在 30H 以后。堆栈的栈顶位置由堆栈指针寄存器 SP 指出,SP 在复位后为 07H,一般在程序初始化阶段将其调整到需要的位置。堆栈是向上增长的,也就 是从当前栈顶位置向地址增加的位置扩展,而这种扩展没有限制,在实际编程中 要注意留出足够的区域以免发生冲突。也就是说,30H~7FH都可以作为堆栈区。
)4.通用数据区
实际上,前128字节都可以作为通用数据区,即刚才提到的,00H到1FH的工作寄存器区,20H到2FH的位寻址区,以及30H以后的堆栈区,虽然我们刚才介绍那些区域有属于自己的功能,但如果我们不使用它们,那么它们中任何一个区域、任何一个单元,都可以用来存储通用数据。所以,严格来说,通用数据区,最大可以是00H到7FH这整整前128字节,不过实际使用要视具体情况而定,我们实际占用了多少单元来存储通用数据,那么通用数据区就是多大。可见通用数据区的大小是不固定、视实际而定的。
)5.特殊寄存器区
特殊寄存器区从80H到FFH,128字节,留给系统中有特殊功能的寄存器,如图:

然后我们来逐个介绍每个特殊寄存器的作用:
1.P0~P3 :IO端口
如图,我们可见封装好的51单片机有4个输入输出端口,P0、P1、P2、P3

这些端口和特殊寄存器区中的P0,P1,P2,P3寄存器是对应的,从P0端口的8个管脚输入的数据,会存入P0寄存器。然后便可供CPU进行各种运算。如果要输出,只需将要输出的数据存入某端口的寄存器(比如P0),那么P0端口外接的部件就能接收到输出的数据了。
2.SP :栈顶指针
存放方才提到的堆栈区的栈顶在RAM中的位置,即栈顶指针
3.ACC, B:通用寄存器
ACC和B就像8086中的AX,BX一样,是通用数据寄存器,可以存放任意的一个临时数据,作为操作数来使用,但两者不同在于ACC寄存器支持的指令比B寄存器要多,而B寄存器更多存储一个offset和DPTR搭配使用来间接寻址。
4.PSW:程序状态字
就是其他CPU中都会有的程序状态字,与8086的16位PSW相比,51单片机的8位PSW显得结构更简单好记。(此处借用别人一张图哈,感谢【MCS-51】51单片机结构原理_8032单片机 寄存器-CSDN博客)

D7: Carry Flag,进位标志位
D6: Auxiliary Carry Flag,辅助进位标志位
D5:User Flag, 用户标志位
D4,D3:我们刚才提到的,用于指出当前在使用哪一组工作寄存器
D2: Overflow 溢出标志位
D1:undefined,即没有功能,没有实际意义
D0: Parity Flag,奇偶校验位
这里的D5:User Flag值得我们特别注意,因为在8086中,是没有这个东西的,什么是User Flag,简单来说就是我们在编程的时候有时候想要使用一个标志变量来辅助编程(常常在判断情况时),这一位就是给我们干这个用的,我们可以去认为设定User Flag的值。
其他位嘛,意义就都和一般的CPU的一样了。
5.IP:中断优先级

定义各个中断源的优先级,从 D4 开始,按顺序分别为串行中断、T1、INT1、T0,INT0 的中断优先级标 志,若为 1,表示此中断的优先级为高,否则为低。 当 CPU 接收到同样优先级的几个中断申请时,按照以下的次序决定中断响 应的优先次序:INT0,T0,INT1,T1,串行中断。 CPU 复位后,IE,IP 的内容均为 0,禁止所有中断。由初始化程序对 IE、IP 编程,以根据需要决定中断的允许与优先级。
6.IE:中断允许

EA 是整个 CPU 的中断允许标志。当 EA=1 时,CPU 可以响应中断;当 EA= 0 时,屏蔽所有中断申请。 ES 是串行口中断允许位;ET1 和 ET0 是 T1 和 T0 的中断允许位;EX1 和 EX0 是外部中断 INT1 和 INT0 的中断允许位。这些位为 1 时允许响应对应的中断申请, 为 0 时屏蔽之。
7.DPL和DPH:数据指针
我们在写汇编程序时,可以操作一个寄存器叫DPTR(Data Pointer),数据指针寄存器,它常被用于存储某段数据区域的基地址,比如:
; 数据表:7段数码管显示代码
CODE_TABLE: DB 0C0H, 0F9H, 0A4H, 0B0H, 099H, 092H, 082H, 0F8H, 080H, 090H
MOV DPTR, #CODE_TABLE
MOV B, A
MOVC A, @A+DPTR
我们在选取了内存中的某段来存储七段数码管的显示代码CODE_TABLE,然后用MOV指令将这段区域的基址存入DPTR,而A中存储的是偏移量(即要显示的数字),然后基址加上相应偏移,就可以显示拿到对应代码。比如要显示数字"1",那么A中存储1,DPTR+A,正好是0F9H的地址,那么用MOVC指令拿出0F9H这个数字存入A,然后把A输出给七段数码管,就可以显示数字"1"了(0F9H,写成二进制后低7位是1111001,只有两位有效,对应七段数码管只有右侧的两根竖着的管会亮,就是"1",具体用法实例可参见:吉林大学单片机实验一:投币机 [预备知识+电路图+代码+思考题]-CSDN博客)
以上这个例子就是DPTR的最常见用法,但是我们会发现,在图5中,我们可没看见有DPTR这个寄存器!这是怎么回事?
实际上,DPTR是个16位寄存器,由DPL和DPH两个8位寄存器共同组成,DPH就是16位中的高8位,DPL就是16位中的低8位。只不过在51汇编语言中,我们直接操作DPTR这个寄存器,这相当于汇编语言为我们提供的硬件之上的一层抽象。
8.PCON:电源控制
Power Control Register,电源控制寄存器,包含8个控制位

SMOD:Serial Mode,串行口波特率倍增控制位,为1则串行口波特率翻倍,为0则不翻倍
GF1,GF0:General Purpose Flag bit,通用标志位,用户可以自由使用,就是平时我们编程时有时候想用一个标志,比如以前用C语言刷题时经常会bool flag=true,设一个bool变量来使用,这个GF1和GF0就是干这个用的。
PD:Power Down bit,掉电模式设定位,PD=0,单片机处于正常工作状态,PD=1,单片机进入掉电(Power Down)模式,可由外部中断或硬件复位模式唤醒,进入掉电模式后,外部晶振停振,CPU、定时器、串行口全部停止工作,只有外部中断工作。
IDL:Idle Mode bit,空闲模式设定位,IDL=0 单片机处于正常工作状态。IDL=1 单片机进入空闲(Idle)模式,除CPU不工作外,其余仍继续工作,在空闲模式下可由任一个中断或硬件复位唤醒。
9.TCON:定时器/计数器控制寄存器
Timer/Counter Control,定时器/计数器控制寄存器。主要用于控制两个定时器/计数器(T0和T1)的工作方式和中断

TF1、TF0:Timer/Counter Flow
TF1是T1溢出标志位,当T1计数满溢出时,TF置1,表示T1有中断产生。进入中断服务程序后,由硬件自动清零。TF0和T0同理。
TR1和TR0:Timer/Counter Run
TF1是T1运行控制位,由软件控制来置1/清理来控制T1运行还是关闭
IE1和IE0:Interrupt Enable
外部中断请求标志,当INT1有效时,IE1由硬件自动置1,当进入中断处理程序后又由硬件自动清零。IE0同理
IT1和IT0:Interrupt Type
外部中断类型选择,
当IT1=0,选择为电平触发方式,输入为低电平时INT1有效
当IT1=1,选择为下降沿触发方式,输入为下降沿时INT1有效
IT0和INT0同理。
10.TMOD:定时器/计数器模式寄存器
Timer/Counter Mode,定时器/计数器模式寄存器,它主要用于 设置两个定时器/计数器(T0和T1)的工作模式。

M1和M0:
Mode Selector bit,用于选定工作方式,具体如何选定可参见图11的NOTE1,如果觉得英文不好理解,可看下面的中文版:

对此表补充一点,M1M0为11时,T0分为两个8位定时器/计数器,即TL0和TH0,TL0被T0的控制位所控制,TH0被T1的控制位所控制。
C/~T:
Counter or Timer Selector,之前我们一直说T0和T1是“定时器/计数器”,那么这一位就用于选择T0和T1到底是作为计数器Counter还是定时器Timer。
当C/~T为0,T0和T1为定时器Timer, 这时,以内部振荡器的 12 分频信号作为计数信号,也就是每一个机器周期定时器内的值加 1。若晶振为 12MHz,则定时器的计数频率为 1MHz。定时器从初值开始加 1 到溢出所需的时间是固定的。
当C/~T为1,T0和T1为计数器Counter,这种方式采用外部引脚(T0 为 P3.4,T1 为 P3.5) 的输入脉冲作为计数脉冲。
GATE:门控制
当GATE=1,当TR1=1且INT1有效时,T1才被允许运行。T0同理。
当GATE=0,当TR1=1时,T1就能被允许运行。T0同理。
11.TL0和TH0,TL1和TH1:定时器/计数器
51单片机中,有两个16位的定时器/计数器(即他们即可做定时器又可做计数器),分别是T0和T1,T0对应TL0和TH0这两个寄存器,T1对应TL1和TH1这两个寄存器。
13.SCON:串行口控制寄存器
Serial Port Control Register,存放串行口的状态和控制信息,
SM0和SM1:Serial Port Mode Specifier
串行口工作方式控制位,对应4种工作方式:
中文版:(来自TMOD、TCON、SCON、PCON、SBUF寄存器说明_画出scon,tmod 和pcon寄存器的结构并加以说-CSDN博客
)
SM2:
多机通信控制位,SM2=1,则允许多机通信。
REN:Read Enable
允许接受控制位,
当REN=1时,允许接收;当REN=0时,禁止接收,此位由软件置1或清零。
TB8:Transmitted Bits 8th
在方式2和方式3中,此位为发送数据的第9位,在多机通信中作为发送地址帧或数据帧的标志。TB8=1,说明该发送帧为地址帧;TB8=0,说明该发送帧为数据帧。在许多通信协议中,它可作为奇偶校验位。此位由软件置1或清零。
RB8:Received Bits 8th
在方式2和方式3中,此位为接收数据的第9位,在方式1中,如果不允许多机通信,那么RB8是接受数据的停止位
TI:Transmit Interrupt
发送中断标志位。在模式0下,在一帧数据发送完时由硬件置位。在其他模式下,在停止位的开始由硬件置位。中断被响应后,TI不能自动清零,必须由软件清零。
RI:Receive Interrupt
接收中断标志位。在模式0下,在一帧数据接收完时由硬件置位。在其他模式下,在停止位的半途由硬件置位。中断被响应后,RI不能自动清零,必须由软件清零。
14.SBUF:串行口缓冲
串行口的内部有数据接收缓冲器和数据发送缓冲器,数据接收缓冲器只能读 出不能写入,数据发送缓冲器只能写入不能读出,这两个数据缓冲器都用符号 SBUF 表示,地址是 99H。
> 4.IO接口/引脚
)1.P0-P3
MCS-51 单片机有 4 个双向 8 位输入输出口 P0-P3 口。每一个口由口锁存 器、输出缓冲器、输入缓冲器组成。这些位的复位后的状态都是 1。其中 P0 口 是三态双向 I/O 口,P1、P2、P3 口内部有提升电阻,称为准双向口。
1.P0
P0 口为三态双向 I/O 口。和 P2 口类似,当外接程序/数据存储器时,必须作 为地址/数据口使用,否则也可以作为普通输入输出口使用。当 P0 口作为地址/ 数据口使用时,在机器周期的前半时刻输出程序地址(或外部数据地址)的地址 低 8 位,后半部分输入输出数据。这是通过 ALE 线来控制的。
2. P1
P1 口为准双向口。它的每一位都可以分别定义为输入或输出线。作为输出 时,程序直接向 P1 口的该位输出 0 或 1,外部电路就锁存为低或高电平。作为 输入端使用时,必须向其输出 1,读取时,此位的状态就由外电路决定。
3.P2
P2 口也有两种功能,一种功能是作为外接程序/数据存储器的地址高位线输 出,一种是普通的 I/O 口。对于内部没有程序存储器或需要外接的应用,P2 口就 不能作为输入输出口使用。
4.P3
P3 口为多功能口,用户可以使用系统预设的功能,这时必须向其输出 1;也 可以作为普通 I/O 口使用,这时使用方法与 P1 口一致。当系统定义的功能如下表所示:
)2.XTAL:External Osciilator
对于非专业人士和初学者,XTAL接口和晶振以及振荡电路有关,事关51单片机的时间准确度,可不必过多关注。
1.XTAL1
用于连接外部晶振,也是内部振荡电路的输入端
2.XTAL2
用于连接外部晶振,也是内部振荡电路的输出端
)3.~RST
Reset,复位,该位一旦有效,则单片机复位
)4.ALE
Address Latch Enable, 地址锁存允许信号, 该引脚输入为1,则锁存访问外部存储器的地址的低8位
)5.~PSEN
Program Store Enable,
当单片机正在处理外部程序存储器,此引脚有效(低电平),
当单片机正在处理外部程序存储器中的代码,此引脚每个机器周期被激活两次。
)6.~EA
External Access enable
当此引脚为低电平有效时,允许访问外部程序存储器
>5.振荡电路
单片机内部有一个振荡电路用于产生时钟信号,Keil里面可以查看CPU的主频,我们实验室用的CPU是12MHZ的,对应的机器周期是1us,振荡电路的频率可以用来计算机器周期,用定时器时会用到。
3.Conclusion&References:
如果你使用英语足够熟练,我建议你去MIT官网看Intel公司官方发布的MCS-51单片机的用户手册,非常详细和准确,可以解答你的几乎所有疑问,我这篇博客中的所有英文内容均来自于此。
MCS® 51 Microcontroller Family User's Manual (mit.edu)
当然国内也有很多优秀的博客值得参考,Thanks to all of you :
TMOD、TCON、SCON、PCON、SBUF寄存器说明_画出scon,tmod 和pcon寄存器的结构并加以说-CSDN博客
51单片机之 IE,TCON,TMOD 寄存器 (超详细解读-内含详细代码)_tcon寄存器-CSDN博客【MCS-51】51单片机结构原理_mcs51-CSDN博客
51单片机入门 - 定时/计数器原理及结构(T0和T1)_51单片机定时器t0是什么-CSDN博客
51单片机中的PCON寄存器(电源控制及波特率选择寄存器)_51单片机中smod为0-CSDN博客