中断
什么是中断?中断就是暂停正在执行的程序,去执行其他程序,等其他程序执行完后,再继续执行之前的程序。
8051 的中断系统由以下几部分组成:
- 中断源:
- 中断响应
- 中断返回
- 优先级控制
- 中断嵌套
中断源
中断源就是中断的来源,包括:
- 外部:
- 引脚:/INT0, /INT1
- 内部:
- 定时器(计数器):T0, T1, T2(8052才有 T2)
- 串行口:TXD, TXD
这里我们要介绍一个特殊功能寄存器:TCON(88H),它每一位的功能如下:
TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
---|---|---|---|---|---|---|---|
溢出 标志 |
运行 标志 |
溢出 标志 |
运行 标志 |
中断 标志 |
触发 方式 |
中断 标志 |
触发 方式 |
- 溢出标志:当计数器溢出时,溢出标志置 1,产生中断信号
- 运行标志:定时器(计数器)开始运行
- 中断标志:当引脚满足“触发方式”时,置 1,产生中断信号
- 触发方式:0 时为低电平触发;1 时为下降沿触发。(两者都要保持一个机器周期才能检测到)
由上可见,通过配置 TCON,可以对外部中断和定时器中断进行设置。
中断响应
只有打开了中断开关的情况下,才会响应中断信号。这个开关由 IE(0A8H)寄存器控制,它每一位的功能如下:
EA | / | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
---|---|---|---|---|---|---|---|
总开关 | T2 | UART | T1 | /INT1 | T0 | /INT0 |
- 总开关:所有中断的总开关
- 后面是每个中断自己的开关
也就是说,中断响应有三个条件
- 中断请求(相应中断标志置位)
- 允许中断(IE 对应位置位)
- CPU开中断(EA置位)
但是,就算上述条件都满足了,也只是“会”响应中断信号,但不一定“立即”响应。下面三种情况都会使得中断会延迟响应:
- 同级或高级中断正在处理
- 当前指令未执行完最后一个机器周期
- 若当前指令为 RETI 或访问 IE、IP 的指令,要等执行完当前指令的下一条指令完
好了,等上面都做完后,终于可以响应中断了!于是就保护断点(PC 入栈),然后清除对应的中断标志位,然后就跳转到中断程序的入口地址(如下)
中断源 | 中断标志位 | 入口地址 |
---|---|---|
/INT0 | IE0 | 0003H |
T0 | TF0 | 000BH |
/INT1 | IE1 | 0013H |
T1 | TF1 | 001BH |
RXD、TXD | RI、TI | 0023H |
T2 | TF2 或 EXF2 | 002BH |
等到中断执行完后,用 RETI
返回,继续执行原来的程序。
中断优先级
如果有多个中断同时触发,则优先处理高级的中断;并且低级的中断可以被高级的中断打断。
我们可以通过 IP(0B8H)寄存器来设置中断优先级:
/ | / | ET2 | PS | PT1 | PX1 | PT0 | PX0 |
---|---|---|---|---|---|---|---|
T2 | UART | T1 | /INT1 | T0 | /INT0 |
- 我们可以设置两级优先级:0 和 1,0 为低级,1 为高级。
- 优先级相同,则 IP 右边的比左边的高级
中断嵌套
由于存在两级优先级,所以可以嵌套两个中断:
- 主程序
- 被低级中断打断
- 执行低级中断程序
- 被高级中断打断
- 执行高级中断程序
- 返回低级中断程序
- 继续执行低级中断程序
- 返回主程序
- 继续执行主程序
定时器/计数器
定时器和计数器是一个东西,只不过前者是每过一个机器周期加一,后者是每有一个上升沿脉冲加一。我们后面都用定时器来指代。8051 有两个定时器,每个定时器有两位 THx、TLx(x 为 0 或 1)
要使用定时器,首先要设置定时方式,我们通过 TMOD 寄存器来设置:
GATE | C/T’ | M1 | M0 | GATE | C/T’ | M1 | M0 |
---|---|---|---|---|---|---|---|
门控位 | 定时/计数选择位 | 工作方式选择位 | 工作方式选择位 | ||||
T1 | T0 |
- GATE:0 时以 TRx 启动;1 时以外部中断信号启动
- C/T’:0 时是定时器;1 时是计数器
- 工作方式选择位:00、01、10、11 对于 0、1、2、3 四种方式
- 方式 0:13 位计数器
- 方式 1:16 位计数器
- 方式 2:8 位计数器,有自动装入
- 方式 3:T0 分为两个 8 位,TH0 只能用于定时,TL0 都行
然后就要设置定时时间,我们可以用下面公式计算:
\[T = (2^\text{bits} - x)\times 晶振周期 \times 12\\ T:定时时间 \quad x:定时器初值\]然后打开中断开关,并将 TCON 位中的 TRx 位置 1,就开始计时啦!当计时计到最大值时,就会溢出,然后就触发中断,执行对应的中断方式。
当然,不用中断也行,但这样的话主程序就要不断溢出标志,来判断是否到时间,比如下面这段程序:
MOV TMOD, #00H ; T1 工作在方式 0
SETB TR1 ; 启动 T1
Loop:
MOV TL1, #03H ; 设定时初值
MOV TH1, #0FCH
Wait:
JNB TF1, Wait ; 定时未到,循环等待
CLR TF1 ; 定时到,清 TF1
CPL P1.0 ; 对输出信号取反
SJMP Loop ; 循环输出方波