外部中断理论部分

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 0 Likes: 0 Comments: 0

037.4.1 外部中断理论部分

/*
 * 源码位置:源码/2、51单片机部分/2、51单片机教学篇/代码/4.1外部中断_实验演示/main.c。
 * 整体目的:把 SW3/P3.2 配置为 INT0、SW4/P3.3 配置为 INT1;按键产生下降沿时,由 ISR 翻转对应 LED。
 * 中断关系:INT0 使用 Keil C51 的 interrupt 0 并控制 LED1,INT1 使用 interrupt 2 并控制 LED2。
 * 主循环作用:主程序只进行 1 秒忙等待,用来直观看出外部中断能暂停当前流程、执行 ISR 后再返回原处。
 * 实验限制:ISR 内的 10ms 忙等待只是简单消抖演示,会延长中断占用时间,实际项目通常应把消抖移出 ISR。
 */
#include "reg52.h"    /* 引入 Keil C51 的 8052 特殊功能寄存器及位定义,使代码可以使用 IT0、EX0、EA 等名称。 */
#include <intrins.h>   /* 引入 Keil C51 内建函数声明,为软件延时中的 _nop_() 提供正确原型。 */

typedef unsigned char uchar;  /* 为 8 位 unsigned char 创建简写 uchar,供延时函数的短计数器使用。 */
typedef unsigned int uint;    /* 为 Keil C51 的 16 位 unsigned int 创建简写 uint,供毫秒参数使用。 */

sbit LED1 = P1^0;     /* 将 LED1 绑定到 P1.0;本板 LED 低电平有效,端口位翻转会改变其亮灭状态。 */
sbit LED2 = P1^1;     /* 将 LED2 绑定到 P1.1,供外部中断1的服务程序单独控制第 2 盏灯。 */

void Delay_xms(uint xms) // @11.0592MHz
{                                           /* 通用毫秒延时函数体开始;xms 保存还需执行的约 1ms 延时单元数。 */
    uchar data i, j;                         /* 定义两个 8 位局部计数器,并用 data 指定片内直接寻址 RAM。 */

    while (xms)                              /* xms 非 0 时重复一个约 1ms 的忙等待单元。 */
    {                                        /* 一次基础延时单元开始。 */
        _nop_();                              /* 执行一条空操作指令,用来补足延时生成器计算出的指令时间。 */
        i = 2;                                /* 设置外层计数器初值,与 j 的循环次数共同形成基础延时。 */
        j = 199;                              /* 设置内层计数器初值,让空循环通过递减消耗 CPU 时间。 */
        do                                    /* 开始由 i 控制的后判断循环,循环体至少执行一次。 */
        {                                     /* 外层计数循环体开始。 */
            while (--j);                      /* 先递减 j,非 0 就继续空转;末尾分号表示循环体故意为空。 */
        } while (--i);                        /* 一轮 j 计数结束后递减 i;j 利用 8 位无符号回绕完成后续计数。 */
        xms--;                                /* 完成一个基础单元后减少剩余次数,减到 0 时退出外层 while。 */
    }                                        /* 本轮约 1ms 延时结束。 */
}                                            /* 指定时长的忙等待结束,CPU 返回调用处。 */

void main()                                  /* 定义 Keil C51 应用程序入口;启动代码完成初始化后进入这里。 */
{                                            /* 主函数体开始,先配置中断再进入永久循环。 */
    IT0 = 1; //配置外部中断0为下降沿触发
    EX0 = 1; //打开外部中断0允许位
    IT1 = 1; //配置外部中断1为下降沿触发
    EX1 = 1; //打开外部中断1允许位

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

    while (1)                                /* 中断初始化完成后保持主程序持续运行。 */
    {                                        /* 主循环体开始;这里故意没有轮询 SW3、SW4。 */
        Delay_xms(1000);                      /* 忙等待约 1 秒;有效中断到来时 CPU 可暂停它并转去执行 ISR。 */
    }                                        /* 延时结束后立即开始下一轮,因此主循环不会退出。 */
}                                            /* 主函数的语法边界;正常运行时不会返回。 */

void Int0_Routine(void) interrupt 0 //外部中断0 触发函数    注意是interrupt 0
{                                            /* INT0 服务程序开始;P3.2 出现有效下降沿后由硬件向量进入。 */
    Delay_xms(10);                            /* 在 ISR 内忙等待约 10ms,作为课程演示用的简单按键消抖。 */
    LED1 = ~LED1;                             /* 翻转 P1.0,使 SW3 每次有效触发后改变 LED1 的亮灭状态。 */
}                                            /* INT0 服务结束,编译器生成中断返回指令,使 CPU 回到被打断处。 */

void Int1_Routine(void) interrupt 2 //外部中断1 触发函数    注意是interrupt 2
{                                            /* INT1 服务程序开始;外部中断1在 C51 向量序列中的编号是 2。 */
    Delay_xms(10);                            /* 忙等待约 10ms,滤掉部分按键抖动,但同时延长了 ISR 执行时间。 */
    LED2 = ~LED2;                             /* 翻转 P1.1,使 SW4 每次有效触发后改变 LED2 的亮灭状态。 */
}                                            /* INT1 服务结束并返回被中断的主程序位置。 */

一、外部中断理论部分

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;  /* 将 TCON.0 置 1,使 P3.2/INT0 选择下降沿触发,而不是低电平持续触发。 */
EX0 = 1;  /* 将 IE.0 置 1,打开外部中断0的本地允许通道。 */
IT1 = 1;  /* 将 TCON.2 置 1,使 P3.3/INT1 同样选择下降沿触发。 */
EX1 = 1;  /* 将 IE.2 置 1,打开外部中断1的本地允许通道。 */
EA  = 1;  /* 将 IE.7 置 1,最后打开 CPU 总中断允许;本地允许与 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 = 1、IT1 = 1,所以只在按下产生的下降沿申请中断,释放按键产生的上升沿不会触发本例的外部中断。

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

1.1.3 外部中断代码演示

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

while (1)            /* 主程序永久运行,但不主动查询两个按键的端口电平。 */
{                    /* 主循环体开始。 */
    Delay_xms(1000);  /* CPU 在此忙等待时仍可响应已开启且优先级允许的 INT0/INT1 请求。 */
}                    /* 一秒延时结束后重新开始下一轮。 */

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

void Int0_Routine(void) interrupt 0  /* 把本函数绑定到 INT0 的 0003H 向量;函数名本身可以自定义。 */
{                                    /* INT0 服务程序开始。 */
    Delay_xms(10);                    /* 在中断中延时约 10ms,作为简单消抖演示;正式项目应避免长时间阻塞 ISR。 */
    LED1 = ~LED1;                     /* 翻转 P1.0,让 SW3 的一次有效下降沿改变 LED1 状态。 */
}                                    /* INT0 服务结束,返回主程序被打断的位置。 */

void Int1_Routine(void) interrupt 2  /* 把本函数绑定到 INT1 的 0013H 向量;编号 2 不是优先级。 */
{                                    /* INT1 服务程序开始。 */
    Delay_xms(10);                    /* 在中断中延时约 10ms,滤掉一部分 SW4 触点抖动。 */
    LED2 = ~LED2;                     /* 翻转 P1.1,让 SW4 的一次有效下降沿改变 LED2 状态。 */
}                                    /* INT1 服务结束,返回被打断的主程序。 */

LED1 = ~LED1 和 LED2 = ~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发送完成或接收完成TI 或 RIES0023H
5Timer2T2 溢出或 T2EX 事件TF2 或 EXF2ET2002BH
6INT2,外部中断2P4.3 下降沿或低电平IE2EX20033H
7INT3,外部中断3P4.2 下降沿或低电平IE3EX3003BH

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

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

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

INT0、INT1 的触发位是 TCON.IT0、TCON.IT1;INT2、INT3 的触发位是 XICON.IT2、XICON.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串口工作方式、收发控制以及 TI、RI 中断标志是
T2CONC8H定时器2运行方式及 TF2、EXF2 中断标志是

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 = 1、EX1 = 1、EA = 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 = 1、IT1 = 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 总允许位控制。只设置 EX2 或 EX3 而没有设置 EA,同样不能响应。

4. IP 与 IPH:中断优先级寄存器

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 = 1;IPH 不可位寻址,通常要用掩码按字节修改,避免覆盖其他中断的高位。例如需要把 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串口模式位1与 SM0 组合选择方式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 后,TI 或 RI 任意一个为 1都会申请串口中断。进入串口 ISR 后必须分别判断并清除标志:

if (RI)       /* 检查串口接收完成标志;RI 为 1 表示接收路径产生了本次中断请求。 */
{             /* 接收事件处理分支开始。 */
    RI = 0;   /* 软件清除接收标志,避免退出 ISR 后因 RI 仍为 1 而立即再次进入中断。 */
    // 读取接收数据
}             /* 接收事件处理结束。 */

if (TI)       /* 独立检查串口发送完成标志;RI 与 TI 可能同时置位,因此这里不用 else if。 */
{             /* 发送完成事件处理分支开始。 */
    TI = 0;   /* 软件清除发送标志,确认本次发送完成事件已经处理。 */
    // 处理发送完成
}             /* 发送事件处理结束。 */

CPU 响应串口中断时不会自动清除 TI、RI。若软件不清零,退出 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;当 RCLK 或 TCLK 为 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 的 TF2、EXF2 共用 interrupt 5。与 Timer0、Timer1 的 TF0、TF1 不同,TF2、EXF2 需要在软件中根据实际中断原因清零。

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 单片机中断都可以依次回答五个问题:事件怎样产生、请求标志在哪里、本地允许位在哪里、优先级由哪些位决定、对应哪个中断编号和向量地址。这样就不会把触发位、标志位、允许位和优先级位混为一谈。