中断与定时器

中断

什么是中断?中断就是暂停正在执行的程序,去执行其他程序,等其他程序执行完后,再继续执行之前的程序。

8051 的中断系统由以下几部分组成:

  1. 中断源:
  2. 中断响应
  3. 中断返回
  4. 优先级控制
  5. 中断嵌套

中断源

中断源就是中断的来源,包括:

  1. 外部:
    • 引脚:/INT0, /INT1
  2. 内部:
    • 定时器(计数器):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置位)

但是,就算上述条件都满足了,也只是“会”响应中断信号,但不一定“立即”响应。下面三种情况都会使得中断会延迟响应:

  1. 同级或高级中断正在处理
  2. 当前指令未执行完最后一个机器周期
  3. 若当前指令为 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 ; 循环输出方波