外部中断理论部分

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

037.4.1 外部中断理论部分

#include "reg52.h"
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit LED1 = P1^0;
sbit LED2 = P1^1;

void Delay_xms(uint xms) // @11.0592MHz
{
    uchar data i, j;

    while (xms)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}

void main()
{
    IT0 = 1; //配置外部中断0为下降沿触发
    EX0 = 1; //打开外部中断0允许位
    IT1 = 1; //配置外部中断1为下降沿触发
    EX1 = 1; //打开外部中断1允许位

    EA = 1; //开总允许中断

    while (1)
    {
        Delay_xms(1000);
    }
}

void Int0_Routine(void) interrupt 0 //外部中断0 触发函数    注意是interrupt 0
{
    Delay_xms(10);
    LED1 = ~LED1;
}

void Int1_Routine(void) interrupt 2 //外部中断1 触发函数    注意是interrupt 2
{
    Delay_xms(10);
    LED2 = ~LED2;
}

一、外部中断理论部分

51 单片机的常用中断包括外部中断、定时器中断和串口中断。中断的核心作用不是让 CPU 同时执行两段程序,而是让 CPU 在主程序运行期间,能够暂停当前任务,优先处理突发事件,处理完成后再返回断点继续执行。

本节使用 STC89C52RC 的外部中断 0 和外部中断 1控制两个 LED。实验现象与轮询按键相同,都是“按一次改变一次灯的状态”,但 CPU 发现按键事件的方式不同:

轮询方式:主程序主动反复读取按键
中断方式:中断硬件监视引脚,事件发生后通知 CPU 执行中断服务程序

中断能显著改善长延时期间的事件响应,但要准确理解它的能力:本程序的 Delay_xms(1000) 仍是占用 CPU 的忙等待,并没有因为使用中断而节省这 1 秒的计算时间;外部中断只是能够在延时循环执行期间暂时打断它,避免 CPU 必须等到延时结束才读取按键。若要真正降低 CPU 占用,应进一步使用定时器、状态机或空闲/掉电模式。

1.1 实验演示

实验使用开发板上的 SW3、SW4 和 LED1、LED2:

SW3 ──► P3.2 / INT0 ──► interrupt 0 ──► LED1(P1.0)翻转
SW4 ──► P3.3 / INT1 ──► interrupt 2 ──► LED2(P1.1)翻转

两只按键的一端分别连接 P3.2、P3.3,另一端接 GND。P3 口复位后具有内部弱上拉,因此按键松开时引脚为高电平,按下时被开关拉到低电平。按下瞬间产生的 1 → 0 变化就是下降沿,可用于触发外部中断。

1.1.1 烧录之前写好的外部中断代码

打开配套代码中的 4.1外部中断_实验演示 工程,编译后在 Objects 目录生成 HEX 文件,再通过烧录软件下载到 STC89C52RC。烧录前应核对以下内容:

  • 当前打开的是 4.1 外部中断工程,而不是前面的按键轮询工程。
  • 工程编译无错误,Objects 中 HEX 文件的生成时间与本次编译一致。
  • 烧录软件选择了正确的芯片型号、串口和下载文件。
  • 开发板与下载器共地,下载完成后重新上电运行。

代码在进入无限循环之前先完成中断初始化:

IT0 = 1;
EX0 = 1;
IT1 = 1;
EX1 = 1;
EA  = 1;

这五个符号均由 reg52.h 映射到特殊功能寄存器中的某一位。它们不是五个普通变量,而是直接控制芯片内部中断硬件的位地址。

1.1.2 外部中断实验演示

程序运行后:

  • 按一下 SW3,P3.2 上出现下降沿,外部中断 0 被请求,LED1 状态翻转。
  • 再按一下 SW3,LED1 再次翻转。
  • 按一下 SW4,P3.3 上出现下降沿,外部中断 1 被请求,LED2 状态翻转。
  • 主程序即使正在执行 Delay_xms(1000),外部中断仍可在当前机器指令结束后获得响应。

按键与中断引脚的实际电路为:

                   P3 口内部弱上拉
                         │
                        VCC
                         │
P3.2 / INT0 ─────────────●──── SW3 ──── GND
P3.3 / INT1 ─────────────●──── SW4 ──── GND

松开:内部弱上拉使引脚为 1
按下:开关闭合,引脚由 1 变 0,产生下降沿
释放:开关断开,引脚由 0 变 1,产生上升沿

源程序设置 IT0 = 1IT1 = 1,所以只在按下产生的下降沿申请中断,释放按键产生的上升沿不会触发本例的外部中断。

机械按键在闭合和断开时会抖动,一个肉眼看来只有一次的按下动作,电气上可能产生多个下降沿。中断函数中的 10 ms 延时是一种简单的演示性消抖,但它没有等待按键完全释放,也会延长中断服务时间。更稳妥的工程做法是让外部中断只记录事件或暂时屏蔽该中断,再由定时器在若干毫秒后确认按键电平并恢复中断。

1.1.3 外部中断代码演示

主循环只执行 1 秒延时,没有查询 P3.2P3.3

while (1)
{
    Delay_xms(1000);
}

当 INT0 或 INT1 产生有效下降沿时,CPU 暂停延时函数,跳转到相应的中断服务程序:

void Int0_Routine(void) interrupt 0
{
    Delay_xms(10);
    LED1 = ~LED1;
}

void Int1_Routine(void) interrupt 2
{
    Delay_xms(10);
    LED2 = ~LED2;
}

LED1 = ~LED1LED2 = ~LED2 对单独的位变量取反,使低电平有效的 LED 在亮、灭之间切换。中断服务程序结束后,Keil C51 编译器生成 RETI 中断返回指令,CPU 回到被打断的 Delay_xms() 内部继续运行。

中断响应并不是“完全不影响主程序时间”。中断服务程序执行期间,主程序处于暂停状态,所以这 10 ms 消抖及进出中断的时间会叠加到主循环这次延时的实际墙钟时间中。中断的优势是响应及时,而不是让 ISR 的执行时间凭空消失。

1.2 中断原理

一个中断请求要真正进入中断服务程序,必须同时满足以下条件:

中断事件成立
      │
      ▼
请求标志位置 1
      │
      ├── 本中断允许位 = 1
      ├── 总允许位 EA = 1
      └── 当前优先级允许响应
              │
              ▼
        CPU 跳转到中断向量
              │
              ▼
         执行中断服务程序

可以把中断响应条件简化为:

能够响应 = 请求标志 × 本地允许 × EA总允许 × 优先级条件满足

任意一项为 0,CPU 都不会进入相应的中断服务程序。例如只写 EX0 = 1 而忘记 EA = 1,INT0 即使检测到下降沿也不会被 CPU 响应。

1.2.1 中断原理分析

用“打游戏时水壶响了”理解中断,可以对应为:

生活场景单片机场景
正在打游戏CPU 正在执行 while (1) 主程序
水壶响了中断源产生请求
暂停游戏并记住进度完成当前指令,把返回地址压入堆栈
去倒水执行中断服务程序 ISR
倒完水回来执行 RETI
从暂停位置继续游戏从主程序断点继续运行

更准确的硬件流程如下:

  1. 中断源产生请求并置位相应标志。
  2. CPU 完成当前正在执行的机器指令,不会在一条指令执行到一半时跳走。
  3. 中断控制器检查本地允许位、EA、当前中断状态和优先级。
  4. CPU 把返回地址 PC 压入堆栈,并装载对应的中断向量地址。
  5. Keil C51 生成的 ISR 入口代码保存本函数需要使用的工作寄存器。8051 硬件并不会自动保存所有通用寄存器。
  6. 执行中断服务程序。
  7. 恢复保存的寄存器并执行 RETI,从断点继续主程序。

RETI 不能简单换成普通函数返回指令 RET。两者虽然都会从堆栈恢复 PC,但 RETI 还会通知中断控制器“当前中断服务已经结束”,解除相应的中断优先级占用状态。

中断服务程序应尽量短:读取或清除必要标志、保存少量数据、设置事件标志后立即返回。长延时、等待按键释放、大量打印和复杂计算都会延迟其他中断,严重时会造成数据丢失或系统实时性下降。

1.3 实现过程

STC89C52RC 的中断实现可以按四步理解:

  1. 确定使用哪个中断源及其触发条件。
  2. 配置触发方式、清理可能遗留的请求标志。
  3. 打开本中断允许位,最后打开 EA
  4. 编写与中断编号匹配的中断服务程序。
1.3.1 中断源

STC89C52 系列提供 8 个中断源。前五项是传统 8051 学习中最常用的外部中断、定时器和串口中断,STC89C52 还包括定时器 2以及扩展外部中断 2、3。

查询顺序/C51中断号中断源产生请求的条件请求标志本地允许位中断向量
0INT0,外部中断0P3.2 下降沿或低电平IE0EX00003H
1Timer0T0 溢出TF0ET0000BH
2INT1,外部中断1P3.3 下降沿或低电平IE1EX10013H
3Timer1T1 溢出TF1ET1001BH
4UART发送完成或接收完成TIRIES0023H
5Timer2T2 溢出或 T2EX 事件TF2EXF2ET2002BH
6INT2,外部中断2P4.3 下降沿或低电平IE2EX20033H
7INT3,外部中断3P4.2 下降沿或低电平IE3EX3003BH

表中的 0~7 同时是同一优先级下的固定查询顺序和 Keil C51 的 interrupt n 编号。数字越小,同一时刻、同一优先级的多个请求中越先被查询。它不是用户配置的优先级等级;真正的可编程优先级由 IPIPHXICON 中的优先级位决定。

外部中断的两种触发方式如下:

ITx触发方式特点
0低电平触发INTx 只要保持低电平,请求条件就持续存在;ISR 返回前应确保外部信号已经释放,否则可能再次进入中断
1下降沿触发只记录一次 1 → 0 变化;适合本节按键演示,但需要考虑按键抖动产生多个边沿

INT0、INT1 的触发位是 TCON.IT0TCON.IT1;INT2、INT3 的触发位是 XICON.IT2XICON.IT3

1.3.2 中断触发函数

每个中断源都有固定向量和固定 C51 中断编号:

void Int0_Routine(void)   interrupt 0; // INT0
void Timer0_Routine(void) interrupt 1; // 定时器0
void Int1_Routine(void)   interrupt 2; // INT1
void Timer1_Routine(void) interrupt 3; // 定时器1
void UART_Routine(void)   interrupt 4; // 串口
void Timer2_Routine(void) interrupt 5; // 定时器2
void Int2_Routine(void)   interrupt 6; // INT2
void Int3_Routine(void)   interrupt 7; // INT3

因此,外部中断 1虽然名称中带有数字 1,C51 声明却必须使用 interrupt 2,因为查询序列中还有一个 Timer0 位于 INT0 和 INT1 之间。interrupt 2 表示第三个中断向量 0013H,并不表示“优先级为 2”。函数名可以自行命名,interrupt n 后面的编号必须正确。

多个中断同时出现时先比较可编程优先级。STC89C52 每个中断源可由一对高、低优先级位编码为 0~3 共四级:

高位 H低位 L优先级
000,最低
011,较低
102,较高
113,最高

高优先级中断可以打断正在执行的低优先级 ISR;同级中断不能相互嵌套。若多个待处理中断处于同一优先级,则按 INT0 → Timer0 → INT1 → Timer1 → UART → Timer2 → INT2 → INT3 的固定顺序查询。

1.3.3 中断相关寄存器

与中断直接相关的寄存器可以先按职责分类:

寄存器地址主要职责是否可位寻址
IEA8H总允许以及 INT0、T0、INT1、T1、UART、T2 的本地允许
XICONC0HINT2、INT3 的触发方式、请求标志、本地允许和低优先级位
IPB8HINT0~INT1、T0~T2、UART 的优先级低位
IPHB7H八个中断源的优先级高位否,只能按字节读写
TCON88HT0/T1 运行与溢出标志;INT0/INT1 请求标志与触发方式
SCON98H串口工作方式、收发控制以及 TIRI 中断标志
T2CONC8H定时器2运行方式及 TF2EXF2 中断标志

1. IE:中断允许寄存器

IE(A8H)   B7   B6   B5   B4   B3   B2   B1   B0
            EA    -  ET2   ES  ET1  EX1  ET0  EX0
复位值:    0     x   0    0    0    0    0    0
含义置 1 的作用清 0 的作用
EACPU 总中断允许开放中断总闸门屏蔽全部可屏蔽中断
B6保留位不使用,应保持规定状态不使用
ET2定时器2中断允许允许 TF2/EXF2 请求进入中断系统禁止定时器2中断
ES串口中断允许允许 RI/TI 请求串口中断禁止串口中断
ET1定时器1中断允许允许 T1 溢出中断禁止 T1 中断
EX1外部中断1允许允许 INT1 请求禁止 INT1 中断
ET0定时器0中断允许允许 T0 溢出中断禁止 T0 中断
EX0外部中断0允许允许 INT0 请求禁止 INT0 中断

EA 是总闸门,各中断源的允许位是分闸门。两级都打开才能响应:

INT0请求 ──► [EX0分闸门] ──► [EA总闸门] ──► 优先级仲裁 ──► CPU

本例复位后所有有效允许位均为 0。执行 EX0 = 1EX1 = 1EA = 1 后,有效控制位得到:

IE = 1x00 0101B
     │    │ │
     EA   EX1 EX0

其中 B6 是保留位,数据手册用 x 表示其值不应作为程序依据;按常用写法直接装入 IE = 0x85 时,有效控制位与上图相同。按位赋值只改变目标位,不会无意覆盖同一寄存器中的其他中断允许状态。若直接写入整个 IE,则会同时改写其他中断允许位,工程中应根据整体需求选择写法。

2. TCON:定时器0/1与外部中断0/1控制寄存器

TCON(88H) B7   B6   B5   B4   B3   B2   B1   B0
            TF1  TR1  TF0  TR0  IE1  IT1  IE0  IT0
复位值:     0    0    0    0    0    0    0    0
含义详细作用
TF1定时器1溢出标志T1 溢出时由硬件置 1;CPU 响应 T1 中断时硬件清 0,也可由软件清 0
TR1定时器1运行控制置 1 启动 T1,清 0 停止 T1;它本身不是中断允许位
TF0定时器0溢出标志T0 溢出时由硬件置 1;CPU 响应 T0 中断时硬件清 0,也可由软件清 0
TR0定时器0运行控制置 1 启动 T0,清 0 停止 T0
IE1外部中断1请求标志INT1 条件成立时置 1;下降沿模式响应后由硬件清 0
IT1INT1 触发方式0 为 P3.3 低电平触发,1 为 P3.3 下降沿触发
IE0外部中断0请求标志INT0 条件成立时置 1;下降沿模式响应后由硬件清 0
IT0INT0 触发方式0 为 P3.2 低电平触发,1 为 P3.2 下降沿触发

在低电平触发模式中,即使响应过程清除了请求状态,只要 INTx 引脚仍为低电平,请求条件仍然成立,ISR 返回后就可能再次进入。因此低电平触发的外设必须在返回前撤销请求,或者由软件屏蔽并正确处理。

本例设置 IT0 = 1IT1 = 1。在其他 TCON 位均保持复位值的前提下:

TCON = 0000 0101B = 0x05
              │ │
             IT1 IT0

3. XICON:扩展外部中断控制寄存器

XICON(C0H) B7   B6   B5   B4   B3   B2   B1   B0
             PX3  EX3  IE3  IT3  PX2  EX2  IE2  IT2
复位值:      0    0    0    0    0    0    0    0
含义详细作用
PX3INT3 优先级低位IPH.PX3H 组合决定 INT3 的 0~3级优先级
EX3INT3 允许位置 1 允许 P4.2/INT3 中断,清 0 禁止
IE3INT3 请求标志INT3 触发条件成立时硬件置 1,响应后硬件清 0
IT3INT3 触发方式0 为 P4.2 低电平触发,1 为下降沿触发
PX2INT2 优先级低位IPH.PX2H 组合决定 INT2 的 0~3级优先级
EX2INT2 允许位置 1 允许 P4.3/INT2 中断,清 0 禁止
IE2INT2 请求标志INT2 触发条件成立时硬件置 1,响应后硬件清 0
IT2INT2 触发方式0 为 P4.3 低电平触发,1 为下降沿触发

INT2、INT3 的控制位集中在 XICON,但它们仍受 IE.EA 总允许位控制。只设置 EX2EX3 而没有设置 EA,同样不能响应。

4. IPIPH:中断优先级寄存器

IP(B8H)    B7   B6   B5   B4   B3   B2   B1   B0
              -    -  PT2   PS  PT1  PX1  PT0  PX0

IPH(B7H)  B7    B6    B5    B4    B3    B2    B1    B0
            PX3H  PX2H  PT2H  PSH   PT1H  PX1H  PT0H  PX0H

IP 中每一位是相应中断的优先级低位:

IP对应中断
B7、B6保留位,不作为优先级控制位
PT2Timer2
PSUART
PT1Timer1
PX1INT1
PT0Timer0
PX0INT0

IPH 中每一位是相应中断的优先级高位:

IPH对应中断
PX3HINT3;低位为 XICON.PX3
PX2HINT2;低位为 XICON.PX2
PT2HTimer2;低位为 IP.PT2
PSHUART;低位为 IP.PS
PT1HTimer1;低位为 IP.PT1
PX1HINT1;低位为 IP.PX1
PT0HTimer0;低位为 IP.PT0
PX0HINT0;低位为 IP.PX0

以 INT0 为例,优先级由 [PX0H, PX0] 两位共同决定:00 为 0级,01 为 1级,10 为 2级,11 为 3级。其他中断完全按照同样的方法组合各自的 H、L 位。

IP 可位寻址,可以写 PX0 = 1IPH 不可位寻址,通常要用掩码按字节修改,避免覆盖其他中断的高位。例如需要把 PX0H 置 1时,可使用读—改—写方式保留其余位。复位后 IP 的有效优先级位和 IPH 均为 0,本例没有改动它们,因此 INT0、INT1 都处于 0级;如果两个请求同时到来,INT0 因固定查询顺序靠前而先被处理。

5. SCON:串口控制寄存器

SCON(98H) B7      B6   B5   B4   B3   B2   B1  B0
            SM0/FE  SM1  SM2  REN  TB8  RB8  TI  RI
含义详细作用
SM0/FE模式位0/帧错误标志PCON.SMOD0=0 时与 SM1 组合选择串口模式;当 SMOD0=1 时用于帧错误检测标志 FE
SM1串口模式位1SM0 组合选择方式0~3
SM2多机通信控制在方式2、3中可用于多机通信筛选;不同模式下含义应结合串口章节判断
REN接收允许置 1 允许串口接收,清 0 禁止接收
TB8发送第9位在方式2、3中保存要发送的第9位数据
RB8接收第9位/停止位方式2、3中保存接收的第9位;方式1中可反映停止位状态
TI发送中断标志一帧发送到规定位置后由硬件置 1;必须由软件清 0
RI接收中断标志一帧接收完成并满足条件后由硬件置 1;必须由软件清 0

UART 的发送与接收共用一个中断向量,ES = 1 后,TIRI 任意一个为 1都会申请串口中断。进入串口 ISR 后必须分别判断并清除标志:

if (RI)
{
    RI = 0;
    // 读取接收数据
}

if (TI)
{
    TI = 0;
    // 处理发送完成
}

CPU 响应串口中断时不会自动清除 TIRI。若软件不清零,退出 ISR 后中断请求仍然存在,程序可能不断重新进入串口中断。

6. T2CON:定时器2控制寄存器

T2CON(C8H) B7   B6    B5    B4    B3     B2   B1    B0
             TF2  EXF2  RCLK  TCLK  EXEN2  TR2  C/T2  CP/RL2
含义详细作用
TF2定时器2溢出标志T2 溢出时置 1;当 RCLKTCLK 为 1时不置位;需要软件清 0
EXF2定时器2外部标志EXEN2=1 时,T2EX 负跳变触发捕获或重装并置位;需要软件清 0
RCLK串口接收时钟选择置 1 时用 T2 溢出作为串口方式1、3的接收时钟;清 0 时使用 T1
TCLK串口发送时钟选择置 1 时用 T2 溢出作为串口方式1、3的发送时钟;清 0 时使用 T1
EXEN2T2 外部事件允许置 1 后允许 T2EX 负跳变触发捕获或重装,具体行为由 CP/RL2 等位决定
TR2定时器2运行控制置 1 启动 T2,清 0 停止 T2
C/T2定时/计数选择0 使用内部时钟作定时器,1 对外部下降沿计数
CP/RL2捕获/自动重装选择置 1 选择捕获,清 0 选择自动重装;作为串口波特率发生器时受其他位共同约束

Timer2 的 TF2EXF2 共用 interrupt 5。与 Timer0、Timer1 的 TF0TF1 不同,TF2EXF2 需要在软件中根据实际中断原因清零。

7. 本节五条配置语句的寄存器结果

代码寄存器位作用
IT0 = 1TCON.0INT0 选择下降沿触发
EX0 = 1IE.0允许 INT0 中断
IT1 = 1TCON.2INT1 选择下降沿触发
EX1 = 1IE.2允许 INT1 中断
EA = 1IE.7打开 CPU 中断总闸门

从复位状态出发,执行完初始化后:

TCON               = 0000 0101B = 0x05
IE有效位            = 1x00 0101B
IP有效位(B5~B0)  = 00 0000B    ┐
IPH                 = 0000 0000B  ┴─ 所有中断保持优先级0

最终信号链可以完整写成:

按下 SW3
  │
  ▼
P3.2/INT0:1 → 0
  │ IT0=1,识别下降沿
  ▼
IE0 请求标志置 1
  │ EX0=1,INT0分闸门打开
  │ EA=1,总闸门打开
  │ [PX0H,PX0]=00,优先级0
  ▼
CPU 跳到向量 0003H
  │
  ▼
执行 Int0_Routine(interrupt 0)
  │
  ▼
LED1 翻转,RETI 返回主程序

掌握这条链路后,面对任何 51 单片机中断都可以依次回答五个问题:事件怎样产生、请求标志在哪里、本地允许位在哪里、优先级由哪些位决定、对应哪个中断编号和向量地址。这样就不会把触发位、标志位、允许位和优先级位混为一谈。