00H~1FH, 通用工作寄存器组(4个8B的工作寄存器组合),最常用的运算数据暂存位置;
20H~2FH, 位寻址区, 用于位操作;
30H~7FH, 用户RAM区, 可用于暂存不常用的用户数据;
高128B用于SFR
片内128B RAM的访问使用MOV指令,片外数据存储器/IO的访问使用MOVX指令
特殊单元 | 意义 |
---|---|
0000H | PC复位值,程序的起始位置 |
0003H | INT0中断入口地址 |
000BH | T0中断入口地址 |
0013H | INT1中断入口地址 |
001BH | T1中断入口地址 |
0023H | UART中断入口地址 |
002BH | (52系列)T2中断入口 |
内外程序统一用MOVC查表指令进行访问, E A ‾ \overline{EA} EA引脚决定访问内部/外部程序存储器
对于可位寻址的SFR,其每一位都可以通过位操作进行访问
重点列表
SFR | 含义 | 使用特点 | 复位初始值 | 意义 |
---|---|---|---|---|
SP | 堆栈指针 | 先加后压,先弹后减 | 07H | 指示堆栈顶的位置 |
PSW | 程序状态字 | F0位未定义;P指示累加器A中1个数的奇偶性;RS1/RS0选择R区;Cy为进位位,也是位操作的常客 | 00H | |
PC | 程序计数器 | 指向下条语句的存储地址 | 00H | |
DPTR | 数据指针 | 16-bit,可分为两个8位寄存器DPL和DPH | 00H | |
P0~P3 | 4个P口 | FFH |
部件 | 区分方式 |
---|---|
外部ROM/RAM共用逻辑地址0000H~FFFFH | 外部ROM使用MOVC,外部RAM使用MOVX |
内外数据存储器 | 内部用MOV,外部用MOVX |
接收/发送SBUF | 接收SBUF只读不写,发送SBUF只写不读 |
部件 | 区分方式 |
---|---|
内/外程序存储器 | 取决于引脚 E A ‾ \rm \overline{EA} EA 的电平 |
外部数据存储器/IO | 引脚 I O / M ‾ \rm IO/\overline{M} IO/M 的电平可以区分 |
高/低128B RAM(52子系列) |
R0~R7共有四组,这样看是重叠的,即统一编址;单看每个区的R0~R7又是独立编址
给RST引脚加大于2个机器周期的高电平即可使其复位,复位时PC初始化为0000H
MCS-51的4个P口均为“准双向口”
每个口对应:
系统访问外部存储器(或IO),由P0给出低8位地址,P2给出高几位地址。
P0分时复用(此时P0为真正的双向口)
准双向口:IO口输出转为输入时,需先向口锁存器写1,使下拉FET截止
功能1:地址/数据总线(分时复用,真正的双向口)
功能2:通用IO口
作为通用IO口时,需外接上拉电阻
普通IO口
功能1:地址总线
功能2:通用IO口
访问外部存储器时,P2口用于输送高8位地址;访问结束后,原先锁存器的内容自动恢复
功能1通用IO口
功能2:第二功能
第二功能见后方
当片外已拓展程序存储器时,PC高8位自动从P2口输出,此时P2口不可做为一般IO口
当片外未拓展程序存储器而有数据存储器时,分下面两种情况
P2口节省的实现代码见程序示例
引脚 | 第二功能 | 描述 |
---|---|---|
P3.0 | RXD | 串行输入口 |
P3.1 | TXD | 串行输出口 |
P3.2 | I N T 0 ‾ \rm \overline{\small INT0} INT0 | 外部中断0输入 |
P3.3 | I N T 1 ‾ \rm \overline{\small INT1} INT1 | 外部中断1输入 |
P3.4 | T0 | 定时器0外部计数输入 |
P3.5 | T1 | 定时器1外部计数输入 |
P3.6 | W R ‾ \rm \overline{\small WR} WR | 外部RAM写选通输出 |
P3.7 | R D ‾ \rm \overline{\small RD} RD | 外部RAM读选通输出 |
硬件系统设计者考虑到使用者可能错误设计外部电路的引脚电平,导致引脚、锁存器输出电平不同,所以设计了两种方式,保证一定可以通过某种方式得到正确的结果。
GATE | C / T ‾ C/\overline{T} C/T | M1 | M0 | GATE | C / T ‾ C/\overline{T} C/T | M1 | M0 |
---|---|---|---|---|---|---|---|
控制T1 | 控制T0 |
TMOD字节地址为89H,不可位寻址
TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
---|---|---|---|---|---|---|---|
C/T相关 | 外部中断相关 |
TCON字节地址为88H,可位寻址
假设晶振频率为Y MHz,需定时的时长为T,定时器的位数为n
定时器跳变一次的时间是一个机器周期,则初装值X满足
T = 12 Y ( 2 n − X ) T = \frac{12}{Y}(2^n-X) T=Y12(2n−X)
解得
X = 2 n − T Y 12 X = 2^n -\frac{TY}{12} X=2n−12TY
需注意在各种方式下的最大/最小定时时间
由于确认一次负跳变需要两个机器周期,所以外部输入的计数脉冲的最高频率为
f o s c / 12 2 \frac{f_{osc}/12}{2} 2fosc/12
为了确保给定电平在变化之前能被采样一次,其至少要保持一个机器周期
SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
---|
位标志 | 作用 | 说明 |
---|---|---|
SM0, SM1 | 工作方式选择位 | |
SM2 | 多机通信控制位 | 方式0必须置为0;方式1时若SM2=1,则只有接收到有效的停止位才能使RI=1;方式2、3中用于指示可程控位的性质,第九位为1则为地址帧,只有SM2=1的从机才能接收,第九位为0则为数据帧,只有SM2=0的从机才能接收 |
REN | 接受允许位 | |
TB8 | 发送的第九位数据 | 方式0、1都不用 |
RB8 | 接收的第九位数据 | 方式0不用;方式1时若SM2=0则RB8就是接收到的停止位;方式2、3中就是接收到的第九数据位 |
TI/RI | 串行口(发送、接收完成)中断请求标志位,需要用户清楚 |
SMOD(PCON.7)又称‘波特率倍增位’
方式0:固定波特率 B A U D = f o s c 12 BAUD = \frac{f_{osc}}{12} BAUD=12fosc
方式2 B A U D = 2 S M O D 64 ⋅ f o s c BAUD = \frac{2^{SMOD}}{64}\cdot f_{osc} BAUD=642SMOD⋅fosc
方式1,33
B A U D = 2 S M O D 32 ⋅ f o s c 12 ( 256 − X ) BAUD = \frac{2^{SMOD}}{32}\cdot \frac{f_{osc}}{12(256-X)} BAUD=322SMOD⋅12(256−X)fosc
工作方式 | 功能 | 帧长 | 帧格式 |
---|---|---|---|
方式0 | 同步移位寄存器(IO拓展) | 8 | 8位数据 |
方式1 | 8位异步收发 | 10 | 1位起始位+8位数据+1位停止位 |
方式2 | 9位异步收发 | 11 | 1位起始位+8位数据+1位程控位(TB8/RB8)+1位停止位 |
方式3 | 9位异步收发 | 11 | 1位起始位+8位数据+1位程控位(TB8/RB8)+1位停止位 |
工作方式 | TB8/RB8 | 多机通信 | 产生最后一次移位脉冲时,装载SBUF和RB8并置RI=1的条件 |
---|---|---|---|
方式0 | 不用 | 不支持 | RI=0 |
方式1 | 停止位 | 不支持 | RI=0; SM2=0或接收到有效停止位1 |
方式2 | 程控第9位 | 支持 | RI=0; SM2=0接收到第九数据位1 |
方式3 | 程控第9位 | 支持 | 同方式2 |
方式2、3只是波特率不同,其它都是一样的
TF1 | TF0 | IE1 | IT1 | IE0 | IT0 | ||
---|---|---|---|---|---|---|---|
TCON.7 | TCON.5 | TCON.3 | TCON.1 |
TI | RI |
---|---|
SCON.1 | SCON.0 |
TI、RI是串行口中断请求标志位5
在中断模式下,只有TI、RI不会自动清零
在查询模式下,则在查询有效后必须手动清零
中断控制寄存器IE(可位寻址)
EA | ES | ET1 | EX1 | ET0 | EX0 |
---|
可见,MCS-51的中断系统具有两级中断允许控制: 总开关+分开关
中断优先级寄存器IP(可位寻址)
PS | PT1 | PX1 | PT0 | PX0 |
---|
注意在进入中断结束后,IP将被清0
在收到同一编程优先级的中断请求时,CPU响应中断请求存在以下的查询优先级
中断源 | 查询优先级 |
---|---|
INT0 | 最高 |
T0 | ↑ \uparrow ↑ |
INT1 | ↑ \uparrow ↑ |
T1 | ↑ \uparrow ↑ |
UART | 最低 |
CPU在每个机器周期内对所有中断请求源进行顺序检测,响应中断后,置位中断标志位
外部中断的最短响应时间为3个机器周期:
中断响应时,PC自动入栈,但PSW则需手动入栈
中断响应时,自动LCALL,所以在中断入口处设置跳转指令
,而非调用指令
堆栈作用:保护断点和现场保护
一般传送指令MOV
一旦使用MOVX指令,P0口的输出就自动作为低8位地址,P2口的输出就自动作为高8位地址
DPTR的间址方式可以访问64KB的片外RAM
R i _i i间址则只能正确访问256B的片外RAM
可见寄存器A的特殊性,此外R i _i i只能是R 0 _0 0或R 1 _1 1
指令 | 代码 | 注意 |
---|---|---|
近程查表指令 | MOVC A, @A+PC | 由于PC的值不能自己确定,而A为8-bit的寄存器,所以表格只能在该查表指令后面的256个单元之内 |
远程查表指令 | MOVC A, @A+DPTR |
指令 | 操作码 | 注意 |
---|---|---|
加法指令 | ADD | 目的操作数只能是寄存器A |
自增指令 | INC | 允许INC DPTR |
自减指令 | DEC | 不可以DEC DPTR |
指令 | 代码 | 注意 |
---|---|---|
乘法指令 | MUL AB | 乘积结果低8位在A中,高8位在B中 |
除法指令 | DIV AB | 商在A中,余数在B中 |
只有乘除指令占用4个机器周期
CLR(清零)
CPL:按位逻辑取反(不影响标志位)
ANL
ORL
XOR
无条件转移AJMP(2KB范围内)
相对转移SJMP(-128B~127B范围内)
长跳转LJMP(64KB范围内)
间接跳转JMP @A+DPTR8
短调用ACALL(2KB内)
长调用LCALL(64KB内)
调用子程序时,PC将被压入堆栈
子程序返回RET
中断返回RETI(返回时还将中断优先级控制寄存器IP清零)
子程序返回时,PC将被弹出堆栈
注意在中断入口设置中 应用
AJMP INT_XX
,而不是调用子程序。因为中断响应时已经LCALL一次了,此LCALL应与中断返回RETI匹配,否则中断程序实际并不会结束!
NOP
不进行任何实际操作,只消耗一个机器周期的时间,且执行PC+1–> PC
合法位操作区域:
除此之外的单元直接进行位操作都是非法的!
指令 | 含义 |
---|---|
JB | bit? Yes --> Jump |
JC | carry? Yes --> Jump |
JNC | not carry? Yes --> Jump |
JNB | not bit? Yes --> Jump |
JBC | bit? Yes --> Jump and Clear |
rel = 目的地址 - 转移指令所在地址 - 转移指令长度
偏移量rel是一个带符号的8位二进制数补码(-128~+127)
美元符号$表示当前指令所在地址,下面是示例
JNB F0, \$ ; 若F0=0恒成立,则反复执行该语句
SJMP \$ ; '原地踏步'
伪指令是为汇编服务的,在汇编时没有机器代码与之对应
指令 | 代码示例 | 代码含义 |
---|---|---|
汇编起始地址命令 ORG | ORG 100H | 表示下面的程序段从0100H开始 |
字节定义 DB | DB 0A6H, 0B1H | 定义TAB中第一个字节单元为A6H,第二个字节单元为B1H |
字定义 DW | DW 00A6H, 00B1H | 定义TAB中第一个字单元为00A6H,第二个字单元为00B1H |
位定义 BIT | EN BIT P1.0 | EN就表示端口位P1.0 |
字节定义 DATA | COUNTER DATA 30H | COUNTER就表示字节地址为30H的单元 |
赋值 EQU | NUM EQU 1000H | 标号NUM就是数值1000H |
汇编终止命令 END | END | 汇编结束 |
总线 | 构成 |
---|---|
地址总线AB | P0(输出低8位地址,需要地址锁存器)P2(输出高8位地址) |
数据总线DB | P0(分时复用) |
控制总线CB | P S E N ‾ , W R ‾ , R D ‾ \rm \overline{\small PSEN}, \overline{\small WR}, \overline{\small RD} PSEN,WR,RD |
分配方法 | 说明 |
---|---|
线选法 | 将未用的高位地址线作为存储器芯片的“片选”信号 |
译码法 | 使用译码器对未用的高位地址线进行译码后作为存储器芯片的“片选”信号 |
原则:输入缓冲,输出锁存
当MCS-51单片机串行口工作在方式0时,使用移位寄存器芯片可以拓展一个或多个8位并行IO口
优点:不占用片外RAM地址,节省单片机的硬件开销
缺点:操作速度较慢(拓展芯片越多,速度越慢)
C E ‾ \rm \overline{CE} CE | IO/ M ‾ \rm \small \overline{M} M | A7~A3 | A2A1A0 | 选中端口 |
---|---|---|---|---|
0 | 0 | ***** | *** | 256B RAM |
0 | 1 | 000 | 命令/状态寄存器 | |
0 | 1 | 001 | PA | |
0 | 1 | 010 | PB | |
0 | 1 | 011 | PC | |
0 | 1 | 100 | 计数器低8位 | |
0 | 1 | 101 | 计数器高6位 |
ORG 0000H
LJMP MAIN
ORG 0030H
MAIN:
MOV DPTR, #TAB
MOVC A, @A+DPTR
SJMP $
ORG 1000H
TAB:
DB 01H, 21H, 0F1H
END
设定属于<外部RAM > 256Bytes但不超过太多,需要11位地址>的情况
MOV R0, #add_L ; add_L是低8位地址
MOV A, #00000XXXB ; XXX是高3位地址
ANL P2, #11111000B ; 低3位清0,其余位保持不变
ORL P2, A ; 低3位地址写入P2低3位,其余位不变
MOVX A, @R0 ; @R0间址方式传送,低8位从P0口输出,高3位自动从P2口传输
使用定时器中断,外部引脚输入一个负跳变即进入中断
ORG 0000H
LJMP MAIN
ORG 001BH
AJMP INT_T1
ORG 0030H
MAIN:
MOV SP, #5FH ; 抬高堆栈
MOV TMOD, #01100000B ; 计数器模式/方式2,引脚P3.5用于计数器1的外部输入
MOV TH1, #FFH
MOV TL1, #FFH ; 外部负跳变一次,即进入计数器中断
SETB TR1
SJMP $
INT_T1: ; 拓展的外部中断处理程序
...
RETI
END
从P1.0输出高低电平为10:1、周期为400 μ s \mu s μs的矩形脉冲,系统振荡频率为6MHz
粗略计算,高电平应为364 μ s \mu s μs,低电平为36 μ s \mu s μs
使用T1的方式1
ORG 0000H
LJMP MAIN
ORG 000BH
AJMP INT_T0
ORG 0030H
MAIN:
MOV TMOD, #00010000B ; Timer1方式1
MOV TL0, #4AH
MOV TH0, #0FFH ; 赋初值0FF4AH
SETB TR1
SETB ET0
SETB EA
SETB P1.0 ; 输出高电平
SJMP $
INT_T0:
CLR EA
CLR P1.0 ; 输出低电平
MOV R0, #9
OUTPUT_VL:
DJNZ R0, OUTPUT_VL ; 双机器周期指令,执行9次耗时18个机器周期即36μs
MOV TL0, #4AH
MOV TH0, #0FFH ; 手动重载
SETB P1.0
SETB EA
RETI
END
ORG 0000H
LJMP MAIN
ORG 0030H
MAIN:
MOV TMOD, #00010000B ; Timer1方式1
HIGH:
MOV TL0, #4AH
MOV TH0, #0FFH ; 赋初值0FF4AH
SETB TR1
SETB P1.0 ; 输出高电平
JNB TF1, $ ; 等待溢出
CLR P1.0 ; 输出高时溢出,则转输出低电平
MOV R0, #9
LOW:
DJNZ R0, LOW ; 双机器周期指令,执行9次耗时18个机器周期即36μs
AJMP HIGH ; 转输出高电平
END
数据位于1000H开头的20个单元中,波特率为2400 bits/s,系统振荡频率为11.0592MHz
使用方式1进行发送
发送程序
ORG 0000H
LJMP MAIN
ORG 0030H
MAIN:
MOV SP, #5FH ; 抬高堆栈
MOV DPTR, #1000H
MOV R0, #20
ACALL INIT_UART
SETB TR1
LOOP:
MOVC A, @A+DPTR
ACALL SEND
INC DPTR
DJNZ R0, LOOP
SJMP $
INIT_UART:
MOV TMOD, #0010000B ; T1方式2
MOV TH1, #0F4H
MOV TL1, #0F4H
MOV SCON, #01000000B ; 串口方式1
RET
SEND:
MOV SBUF, A ; 传送数据
JNB TI, $ ; 传送完毕后退出
RET
END
发送程序
ORG 0000H
LJMP MAIN
ORG 0023H ; 串口中断入口
AJMP INT_TRANS
ORG 0030H
MAIN:
MOV SP, #5FH ; 抬高堆栈
MOV R0, #21
ACALL INIT_UART
SETB TR1
SETB ES
SETB EA
MOV SBUF, A ; 传送数据
SJMP $
INIT_UART:
MOV DPTR, #1000H
MOVC A, @A+DPTR
MOV TMOD, #0010000B ; T1方式2
MOV TH1, #0F4H
MOV TL1, #0F4H
MOV SCON, #01000000B ; 串口方式1
RET
INT_TRANS:
CLR TI
DJNZ R0, NEXT
AJMP FINISHED ; 完成所有数据的传送,跳转FINISHED语句
NEXT:
INC DPTR
MOVC A, @A+DPTR
MOV SBUF, A
RETI
FINISHED:
...
RETI
END
接收程序
ORG 0000H
LJMP MAIN
ORG 0023H ; 串口中断入口
AJMP INT_RECEIVE
ORG 0030H
MAIN:
MOV SP, #5FH ; 抬高堆栈
MOV R0, #30H
ACALL INIT_UART
SETB TR1
SETB ES
SETB EA
SETB REN ; 允许接收
MOV A, SBUF ; 接收数据
SJMP $
INIT_UART:
MOV TMOD, #0010000B ; T1方式2
MOV TH1, #0F4H
MOV TL1, #0F4H
MOV SCON, #01000000B ; 串口方式1
RET
INT_RECEIVE:
CLR RI
MOVX @R0, A
CJNE R0, #43H, NEXT
AJMP FINISHED ; 接收完毕
NEXT:
INC R0
MOV A, SBUF
RETI
FINISHED:
...
RETI
END
甲机发送:从内部RAM 20H-25H依次取6个ASCII码字符,在最高位加上奇校验位后发送给乙机
乙机接收:对接收的8位数据先进行奇校验,校验正确后存入本机内部RAM 20-25H,校验错则将FFH存入对应单元
由串口方式1进行发送,1200bits/s,系统时钟频率为11.0592MHz
ASCII码只有7位有效,最高位为0
发送端关键代码
MOV R0, #20H
LOOP:
MOV A, @R0
MOV P, C
CPL C
MOV ACC.7, C ; 最高位加上奇校验位,此时正确的发送数据若送A,P应为1
ACALL SEND
JNB TI, $ ; 等待发送完毕
CLR TI
INC R0
CJNE R0, #26H, LOOP
接收端关键代码
MOV R0, #20H
LOOP:
JNB RI, $ ; 等待接收完毕
CLR RI
MOV SBUF, A
MOV C, P
JNC ERROR ; 接收的数据不是奇数个1,则数据有误
MOV @R0, A
NEXT:
INC R0
CJNE R0, #26H, LOOP
AJMP FINISHED ; 接收完毕
ERROR:
MOV @R0, #0FFH
AJMP NEXT
读修改写指令示例
INC P1
XOR P3, A
ORL P2, A
ANL P1, A
CPL P3.0 ↩︎
在MCS-51中,计时的本质是计数 ↩︎
前面提到定时器的方式2常用作波特率发生器,其实256就是 2 8 2^8 28 ↩︎
在查询模式下,查询有效后需手动清零 ↩︎
它们共用同一个中断入口0023H,所以在进入中断后,应当判断中断源到底是接收中断RI还是发送中断TI ↩︎
注意,若为同一编程优先级,则不可被打断(即便其查询优先级可能更高) ↩︎
中断系统规定,在执行完上述指令后,需要再执行一条指令,才能响应新的中断请求(即当前不是请求中断的时机) ↩︎
DPTR为定义的表位置,比如说第一个单元跳转A1处理,第二个单元跳转A2处理,这样就实现了程序的多分支转移 ↩︎