外部中断代码实现
039.4.3 外部中断代码实现
#include "reg52.h"
#include <intrins.h>
sbit LED1 = P1^0;
sbit LED2 = P1^1;
sbit LED3 = P1^2;
sbit LED4 = P1^3;
//sbit KEY1 = P3^2;
//sbit KEY2 = P3^3;
//sbit SUN = P3^7;
typedef unsigned char uchar;
typedef unsigned int uint;
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;
EX0 = 1;
IT1 = 1;
EX1 = 1;
EA = 1;
while (1)
{
//Delay_xms(1000);
}
}
void Int0_Routine(void) interrupt 0
{
Delay_xms(10);
LED3 = ~LED3;
}
void Int1_Routine(void) interrupt 2
{
Delay_xms(10);
LED4 = ~LED4;
}
一、外部中断代码实现
这段程序使用 STC89C52 的两个外部中断完成按键控制:按下 SW3,P3.2/INT0 产生下降沿,进入 Int0_Routine() 并翻转 LED3;按下 SW4,P3.3/INT1 产生下降沿,进入 Int1_Routine() 并翻转 LED4。主循环不轮询按键,CPU 只在外部事件出现时转去执行中断服务程序(ISR)。
SW3 → P3.2/INT0 → interrupt 0 → LED3(P1.2)翻转
SW4 → P3.3/INT1 → interrupt 2 → LED4(P1.3)翻转
一次外部中断能被响应,需要同时满足四类条件:引脚出现匹配的电气变化、请求标志有效、局部允许和总允许均打开、优先级仲裁允许 CPU 响应。IT0 = 1 只选择触发方式,不能代替 EX0 = 1 和 EA = 1。
1.1 相关寄存器的配置
本例显式使用 TCON 与 IE 中的五个位:
| 代码 | 寄存器位 | 本例中的作用 |
|---|---|---|
IT0 = 1 | TCON.0 | INT0 选择下降沿触发 |
EX0 = 1 | IE.0 | 允许外部中断0请求进入中断系统 |
IT1 = 1 | TCON.2 | INT1 选择下降沿触发 |
EX1 = 1 | IE.2 | 允许外部中断1请求进入中断系统 |
EA = 1 | IE.7 | 打开 CPU 中断总允许开关 |
初始化代码写在 main() 的 while (1) 之前,只执行一次。通常先配置触发方式和各中断源,再打开局部允许位,最后设置 EA = 1,这样可以避免初始化尚未完成时进入 ISR。
1.1.1 中断允许寄存器
IE 是地址为 A8H 的 8 位、可位寻址中断允许寄存器。它相当于中断系统的“总闸门 + 分闸门”:EA 是总闸门,EX0、EX1 等是各中断源的分闸门。
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 的含义 |
|---|---|---|---|
| B7 | EA | 开放 CPU 总中断允许;各中断仍需自己的允许位为 1 | 屏蔽所有可屏蔽中断 |
| B6 | 保留 | 数据手册用 x 表示,程序不要依赖其读值 | 不作为功能位使用 |
| B5 | ET2 | 允许定时器2中断 | 禁止定时器2中断 |
| B4 | ES | 允许串口中断 | 禁止串口中断 |
| B3 | ET1 | 允许定时器1中断 | 禁止定时器1中断 |
| B2 | EX1 | 允许外部中断1 | 禁止外部中断1 |
| B1 | ET0 | 允许定时器0中断 | 禁止定时器0中断 |
| B0 | EX0 | 允许外部中断0 | 禁止外部中断0 |
本例将 EA、EX1、EX0 置 1,其余有效允许位保持 0:
IE有效位 = 1x00 0101B
│ │ │
EA EX1 EX0
B6 是保留位,因此不应把整个寄存器严格描述成固定的 1000 0101B。若忽略保留位,常用字节写法 IE = 0x85 能得到相同的有效控制状态,但它会同时重写其他允许位;本例逐位赋值更直观,也不容易误伤其他已经启用的中断源。
1.1.2 定时器/计数器中断控制寄存器
TCON 位于 88H,可位寻址。它的高四位控制定时器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
| 位 | 名称 | 完整含义 | 典型控制者 |
|---|---|---|---|
| B7 | TF1 | 定时器1溢出请求标志,溢出后为 1 | 硬件置位;CPU 响应 T1 中断时硬件清零,也可由软件清零 |
| B6 | TR1 | 定时器1运行控制,1启动、0停止 | 软件 |
| B5 | TF0 | 定时器0溢出请求标志,溢出后为 1 | 硬件置位;CPU 响应 T0 中断时硬件清零,也可由软件清零 |
| B4 | TR0 | 定时器0运行控制,1启动、0停止 | 软件 |
| B3 | IE1 | 外部中断1请求标志,对应 P3.3/INT1 | 外部条件成立时由硬件置位 |
| B2 | IT1 | INT1 触发类型;0低电平、1下降沿 | 软件配置 |
| B1 | IE0 | 外部中断0请求标志,对应 P3.2/INT0 | 外部条件成立时由硬件置位 |
| B0 | IT0 | INT0 触发类型;0低电平、1下降沿 | 软件配置 |
ITx、IEx 和 EXx 的名字相近,但职责不同:
ITx:Trigger Type,规定“什么变化算触发”
IEx:Interrupt Request Flag,记录“请求已经发生”
EXx:Enable External Interrupt,决定“是否允许响应”
在下降沿模式下,外部请求得到响应后,IE0 或 IE1 由硬件清零。在低电平模式下,即使响应时标志被清除,只要引脚仍保持低电平,请求条件就会继续存在,CPU 可能再次进入 ISR。因此本例使用下降沿模式,更符合“一次按下产生一次事件”的预期。
执行 IT0 = 1、IT1 = 1 后,在没有定时器运行和其他请求的前提下:
TCON = 0000 0101B = 0x05
│ │
IT1 IT0
1.2 任务信息资源
程序从按键轮询工程整理而来:保留 LED 定义和毫秒延时函数,注释不再使用的 KEY1、KEY2 与光敏输入定义,把一次性的外部中断初始化放到 while (1) 之前,再在 main() 外编写两个 ISR。
外部中断硬件直接监视 P3.2、P3.3,所以主循环可以为空。while (1) 仍然必须保留,使主程序持续运行;中断发生时 CPU 暂停当前主程序流程,执行 ISR 后再返回被打断的位置,而不是由 while 主动调用 ISR。
1.2.1 外部中断代码过程
完整实现可以按以下顺序理解:
- 建立并重命名
4.2外部中断工程,保留 LED 与延时相关代码。 - 删除按键轮询和光敏传感器等与本实验无关的业务代码。
- 在
main()中配置 INT0、INT1 的触发方式和允许位。 - 最后打开
EA总中断允许。 - 在
main()外定义interrupt 0与interrupt 2两个 ISR。 - 编译、下载并分别按下 SW3、SW4,观察 LED3、LED4 翻转。
若编译器提示某一行语法错误,还要检查它的前一行。例如输入法产生的中文分号 ; 不能代替 C 语言半角分号 ;,编译器常常会到下一行才发现上一条语句没有正确结束。
1.2.1.1 外部中断零配置
INT0 的初始化由三层配置组成:
IT0 = 1; // TCON.0:P3.2/INT0 下降沿触发
EX0 = 1; // IE.0:打开 INT0 的局部允许
EA = 1; // IE.7:打开总中断允许
相应的 ISR 为:
void Int0_Routine(void) interrupt 0
{
Delay_xms(10);
LED3 = ~LED3;
}
interrupt 0 告诉 Keil C51 编译器:这个函数服务于中断号0,即 INT0 的 0003H 向量。Int0_Routine 只是便于阅读的函数名,硬件不靠函数名匹配中断源。
按下 SW3 后的执行过程是:
P3.2:1 → 0
│ IT0=1,下降沿匹配
▼
IE0由硬件置1
│ EX0=1,局部允许
│ EA=1,总允许
▼
CPU响应0003H向量
▼
Int0_Routine():延时10 ms,翻转LED3
▼
RETI返回主程序,继续执行空循环
只配置 INT0 时,SW3 能翻转 LED3,SW4 不会触发本程序,因为 INT1 的触发方式和局部允许尚未配置。
ISR 中的 Delay_xms(10) 是便于入门观察的简单按键消抖:机械触点闭合时可能在几毫秒内多次通断,产生多个下降沿,延时可以滤掉一部分抖动。但 ISR 内延时会占用 CPU,并阻塞同级和更低优先级中断;工程项目更适合让 ISR 只记录事件或暂时关闭该中断,再由定时器或主循环完成延时确认。
1.2.1.2 外部中断一配置
INT1 与 INT0 对称配置:
IT1 = 1; // TCON.2:P3.3/INT1 下降沿触发
EX1 = 1; // IE.2:打开 INT1 的局部允许
它的 ISR 必须使用 interrupt 2:
void Int1_Routine(void) interrupt 2
{
Delay_xms(10);
LED4 = ~LED4;
}
interrupt 1 属于定时器0,不属于外部中断1。STC89C52 的八个中断号和向量如下:
| C51中断号 | 中断源 | 请求标志 | 允许位 | 向量地址 |
|---|---|---|---|---|
| 0 | INT0 | IE0 | EX0 | 0003H |
| 1 | Timer0 | TF0 | ET0 | 000BH |
| 2 | INT1 | IE1 | EX1 | 0013H |
| 3 | Timer1 | TF1 | ET1 | 001BH |
| 4 | UART | RI 或 TI | ES | 0023H |
| 5 | Timer2 | TF2 或 EXF2 | ET2 | 002BH |
| 6 | INT2 | IE2 | EX2 | 0033H |
| 7 | INT3 | IE3 | EX3 | 003BH |
如果把两个 ISR 声明中的 interrupt 0 与 interrupt 2 对调,INT0 向量就会进入翻转 LED4 的函数体,INT1 向量则会进入翻转 LED3 的函数体,两个按键的控制对象随之交换。这进一步说明:函数名服务于程序员阅读,interrupt n 决定中断向量与函数体的绑定。一个工程中不能让两个 ISR 使用同一个中断号。
1.2.1.3 外部中断配置回顾
reg52.h 已经把特殊功能寄存器及其可位寻址位定义成名称。在 Keil 中对 IT0、EX0 等名称使用 “Go To Definition”,可以看到类似定义:
sfr TCON = 0x88;
sbit IE1 = TCON^3;
sbit IT1 = TCON^2;
sbit IE0 = TCON^1;
sbit IT0 = TCON^0;
sfr IE = 0xA8;
sbit EA = IE^7;
sbit EX1 = IE^2;
sbit EX0 = IE^0;
sfr 用于声明特殊功能寄存器,sbit 用于给可位寻址寄存器中的某一位命名。例如 sbit IT0 = TCON^0 表示 IT0 就是 TCON 的 B0;这和 sbit LED3 = P1^2 给 P1.2 起名为 LED3 的原理相同。赋值 IT0 = 1 最终只改变 TCON.0,不会改动同一寄存器的其他位。
本例完成初始化、且没有外部请求正在等待时,各寄存器的关键状态为:
TCON = 0000 0101B = 0x05
IE有效位 = 1x00 0101B
IP有效位(B5~B0) = 00 0000B
IPH = 0000 0000B
程序没有设置 IP、IPH,因此 INT0、INT1 使用复位后的优先级0。没有配置优先级不代表中断不能运行,优先级寄存器只负责多个请求之间的仲裁。
另外,P3 口复位后准双向口锁存值为 1,P3.2、P3.3 处于释放的输入状态,所以本例无需再写 GPIO 模式切换代码。如果其他代码曾把 P3.2 或 P3.3 输出锁存为 0,应先写 1 将引脚释放,否则外部按键无法形成正常的高低电平变化。
1.2.2 中断系统结构图
以 INT0 为例,中断系统结构可以简化为:
P3.2 / INT0
│
├── IT0=0:检测低电平
└── IT0=1:检测下降沿(本例)
│
▼
TCON.IE0 请求标志
│
▼
IE.EX0 局部允许开关
│
▼
IE.EA 总允许开关
│
▼
[IPH.PX0H, IP.PX0] 优先级仲裁
│
▼
0003H → interrupt 0 ISR
INT1 的路径完全对应:P3.3 → IT1 → IE1 → EX1 → EA → [PX1H,PX1] → 0013H → interrupt 2。允许外部中断0的位是 EX0,不是 ES;ES 只允许串口中断。
STC89C52 中与中断系统直接相关的寄存器总览如下,位名均按 B7 到 B0 排列:
| 寄存器 | 地址 | B7 → B0 | 主要职责 |
|---|---|---|---|
IE | A8H | EA、保留、ET2、ES、ET1、EX1、ET0、EX0 | 总允许与各中断源允许 |
IP | B8H | 保留、保留、PT2、PS、PT1、PX1、PT0、PX0 | INT0~Timer2 的优先级低位 |
IPH | B7H | PX3H、PX2H、PT2H、PSH、PT1H、PX1H、PT0H、PX0H | 八个中断源的优先级高位 |
TCON | 88H | TF1、TR1、TF0、TR0、IE1、IT1、IE0、IT0 | T0/T1 控制及 INT0/INT1 请求与触发方式 |
SCON | 98H | SM0/FE、SM1、SM2、REN、TB8、RB8、TI、RI | 串口控制及收发请求标志 |
T2CON | C8H | TF2、EXF2、RCLK、TCLK、EXEN2、TR2、C/T2、CP/RL2 | 定时器2控制及请求标志 |
XICON | C0H | PX3、EX3、IE3、IT3、PX2、EX2、IE2、IT2 | INT2/INT3 的优先级低位、允许、标志和触发方式 |
IE 与 TCON 的每一位已在前文展开。其余寄存器各位含义如下。
XICON:INT2、INT3 控制
| 位 | 名称 | 含义 |
|---|---|---|
| B7 | PX3 | INT3 优先级低位,与 IPH.PX3H 组成四级优先级 |
| B6 | EX3 | INT3 局部允许,1允许、0禁止 |
| B5 | IE3 | INT3 请求标志,外部条件成立时硬件置 1 |
| B4 | IT3 | P4.2/INT3 触发类型,0低电平、1下降沿 |
| B3 | PX2 | INT2 优先级低位,与 IPH.PX2H 组成四级优先级 |
| B2 | EX2 | INT2 局部允许,1允许、0禁止 |
| B1 | IE2 | INT2 请求标志,外部条件成立时硬件置 1 |
| B0 | IT2 | P4.3/INT2 触发类型,0低电平、1下降沿 |
IP 与 IPH:优先级的低位和高位
| 中断源 | IP 或 XICON 中的低位 L | IPH 中的高位 H |
|---|---|---|
| INT0 | IP.B0 = PX0 | IPH.B0 = PX0H |
| Timer0 | IP.B1 = PT0 | IPH.B1 = PT0H |
| INT1 | IP.B2 = PX1 | IPH.B2 = PX1H |
| Timer1 | IP.B3 = PT1 | IPH.B3 = PT1H |
| UART | IP.B4 = PS | IPH.B4 = PSH |
| Timer2 | IP.B5 = PT2 | IPH.B5 = PT2H |
| INT2 | XICON.B3 = PX2 | IPH.B6 = PX2H |
| INT3 | XICON.B7 = PX3 | IPH.B7 = PX3H |
IP.B6、IP.B7 是保留位。每一组 [H,L] 都按二进制编码优先级:00 为0级最低,01 为1级,10 为2级,11 为3级最高。高优先级中断可以打断低优先级 ISR,同级中断不能互相打断。若同级请求同时出现,固定查询顺序为 INT0 → Timer0 → INT1 → Timer1 → UART → Timer2 → INT2 → INT3。
SCON:串口控制与中断标志
| 位 | 名称 | 含义 |
|---|---|---|
| B7 | SM0/FE | 作为 SM0 时与 SM1 选择串口方式;在相应控制条件下作为帧错误标志 FE |
| B6 | SM1 | 串口工作方式选择位 |
| B5 | SM2 | 多机通信及接收筛选控制 |
| B4 | REN | 串口接收允许,1允许接收 |
| B3 | TB8 | 方式2、3发送的第9位数据 |
| B2 | RB8 | 方式2、3接收的第9位;方式1中可反映停止位 |
| B1 | TI | 发送中断标志,硬件置位,ISR 中需软件清零 |
| B0 | RI | 接收中断标志,硬件置位,ISR 中需软件清零 |
TI 和 RI 共用 IE.ES、同一个 UART 中断向量以及 interrupt 4,所以串口 ISR 要判断是哪一个标志发起请求并分别清零。
T2CON:定时器2控制与中断标志
| 位 | 名称 | 含义 |
|---|---|---|
| B7 | TF2 | 定时器2溢出标志,通常由软件清零;作为波特率发生器时行为受 RCLK/TCLK 影响 |
| B6 | EXF2 | T2EX 外部事件标志,满足捕获或重装条件时置位,需软件清零 |
| B5 | RCLK | 选择定时器2作为串口接收波特率时钟 |
| B4 | TCLK | 选择定时器2作为串口发送波特率时钟 |
| B3 | EXEN2 | 允许 T2EX 负跳变触发捕获或重装 |
| B2 | TR2 | 定时器2运行控制,1启动、0停止 |
| B1 | C/T2 | 0选择内部时钟定时,1选择外部事件计数 |
| B0 | CP/RL2 | 选择捕获或自动重装方式,并受串口时钟相关位共同影响 |
TF2 与 EXF2 共用 IE.ET2、Timer2 中断向量和 interrupt 5。它们不会因为 CPU 进入 ISR 就自动清零,服务程序需要判断来源并软件清零。
从结构图可以归纳出三个不能混淆的概念:请求标志负责报告事件,允许位负责开关通道,优先级位只负责仲裁。打开 EX0、EA 不会凭空产生中断;出现下降沿但 EX0 = 0 时,请求也不能进入 CPU。
1.2.3 按键触发中断
开发板上用于外部中断实验的是 SW3 和 SW4:
STC89C52
内部弱上拉 ── P3.2 / INT0 ── SW3 ── GND
内部弱上拉 ── P3.3 / INT1 ── SW4 ── GND
按键松开时,P3 口的内部弱上拉使引脚保持高电平;按下后触点闭合,引脚被直接接到 GND,产生 1 → 0 的下降沿。机械按键的四脚中,同侧两脚通常内部相连,按下后把两侧导通;看原理图时应沿网络标号确认信号端和 GND 端,不能只凭按键外形判断。
本例输出电路是低电平点亮:
+5V ──► LED3 ── R10(10 kΩ)── P1.2
+5V ──► LED4 ── R11(10 kΩ)── P1.3
电阻与 LED 串联,用来限制电流。P1.2 输出 0 时,电流路径为 +5V → LED3 → R10 → P1.2 内部下拉管 → GND,LED3 点亮;P1.2 输出 1 时,两端电位接近,LED3 熄灭。LED4 与 P1.3 的原理相同。
P1 口复位后通常为高电平,所以 LED 初始熄灭。第一次进入 ISR,LED3 = ~LED3 或 LED4 = ~LED4 将端口位由 1 翻转为 0,对应 LED 点亮;下一次按键又翻转为 1,对应 LED 熄灭。
完整的软硬件对应关系如下:
按下SW3
│
├─ 电路:P3.2被SW3接地,形成下降沿
├─ 寄存器:IT0检测 → IE0置位 → EX0与EA放行
├─ CPU:进入interrupt 0
└─ 输出:P1.2翻转,LED3改变亮灭状态
按下SW4
│
├─ 电路:P3.3被SW4接地,形成下降沿
├─ 寄存器:IT1检测 → IE1置位 → EX1与EA放行
├─ CPU:进入interrupt 2
└─ 输出:P1.3翻转,LED4改变亮灭状态
经典 8051 常讲 INT0、INT1 两个外部中断,本开发板也只把两个独立按键接到 P3.2、P3.3。但本板使用的 STC89C52 系列还提供 P4.3/INT2 和 P4.2/INT3,因此芯片本身共有四个外部中断源。INT2、INT3 由 XICON 控制,不要把“板上只有两个外部中断按键”误解成“芯片只有两个外部中断源”。
这类 8051 端口没有 STM32 式的 GPIO/复用功能选择矩阵。本例五条语句的作用分别是选择触发方式、打开两个局部允许和一个总允许;它们使外部中断硬件开始响应 P3.2、P3.3,但不是把引脚从某个独立的 GPIO 模式切换到 EXTI 模式。
1.3 STM32思维导图
STM32 的外部中断/事件控制器通常称为 EXTI。多数 GPIO 引脚可以经由 AFIO 或 SYSCFG 选择到 EXTI0~EXTI15,但同编号引脚共享同一条 EXTI 线:例如 PA0、PB0、PC0 都竞争 EXTI0,通常只能选择其中一个作为该线的输入。具体可用引脚和中断向量分组还要以对应 STM32 型号的参考手册为准。
| 对比项 | STC89C52 外部中断 | STM32 EXTI 的常见实现 |
|---|---|---|
| GPIO对应关系 | INT0=P3.2、INT1=P3.3、INT2=P4.3、INT3=P4.2 | 多数 GPIO 可映射到 EXTI0~EXTI15,同编号端口脚共享一条线 |
| 触发方式 | 低电平或下降沿 | 常见为上升沿、下降沿或双边沿;部分系列还支持更多事件配置 |
| 请求标志 | IE0~IE3 | EXTI pending 标志 |
| 允许路径 | EXx + EA | EXTI 屏蔽/使能 + NVIC 使能 |
| 优先级 | IP/IPH 四级 | NVIC 可编程优先级,位数因芯片而异 |
| ISR处理 | 边沿模式请求标志在响应时由硬件清除 | 通常要在 ISR 中按芯片规定清除 pending 标志 |
STM32 中的三种边沿触发可以用波形理解:
低 ─────┌──────── 高 上升沿:0 → 1
高 ─────┐________ 低 下降沿:1 → 0
双边沿:上述两种变化都产生请求
两类单片机的底层思路相通:外部引脚变化被检测,硬件置请求标志,允许与优先级系统决定 CPU 是否响应,CPU 进入 ISR 处理事件,最后清理请求状态并返回主程序。先把 STC89C52 的 ITx → IEx → EXx → EA → 优先级 → ISR 链路理解清楚,再学习 STM32 的 GPIO 映射、EXTI 和 NVIC,就能把大量寄存器配置归入同一套逻辑。