跳转到内容

三、中断系统

下面讲复习提纲第三部分:三、中断系统。这一部分常考中断概念、5个中断源、优先级、中断开启三要素、中断程序编写,也经常和定时器、串口、按键控制结合在一起出程序题。


复习提纲中的意思是:

单片机正在执行主程序时,遇到紧急突发事件,暂停主程序,跳转执行中断服务程序,执行完返回主程序继续运行。

可以拆成三个动作理解。

比如单片机正在执行流水灯程序:

LED0亮 -> 延时 -> LED1亮 -> 延时 -> LED2亮 -> 延时

主程序按原定顺序一条一条执行。

例如:

外部按键按下
定时器计满溢出
串口收到一个字节
外部传感器产生脉冲

这些事件不一定刚好发生在主程序查询它们的时候。如果只靠主程序循环查询,响应可能会很慢。

第三层:CPU暂停当前任务,先处理紧急事件

Section titled “第三层:CPU暂停当前任务,先处理紧急事件”

中断发生后,CPU 会临时放下主程序,转去执行对应的中断服务程序。

主程序执行中
中断请求发生
保存主程序断点
执行中断服务程序
中断返回
继续执行主程序

所以中断不是把主程序取消,而是临时插队处理更紧急的事情

考试定义可以这样写:

中断是指CPU在执行当前程序过程中,因内部或外部事件请求,暂停当前程序,转去执行相应的中断服务程序,处理完毕后再返回原程序继续执行的过程。

如果没有中断,单片机只能用查询方式:

while (1)
{
if (key == 0) { ... }
if (TF0 == 1) { ... }
if (RI == 1) { ... }
}

这种方式的问题是:

CPU要不断检查每个事件
没有事件发生时也在浪费时间
如果主程序某处延时很长,响应就会变慢

中断的好处是:

事件不发生时,CPU正常执行主程序
事件发生时,硬件自动通知CPU
CPU能及时响应突发事件
提高实时性
减少无意义查询

PPT 第6章也强调,中断技术主要用于实时监测与控制,可以避免大量定时查询操作,提高单片机工作效率和实时性。


复习提纲要求掌握 5 个中断源:

外部中断0 INT0
定时器0中断 T0
外部中断1 INT1
定时器1中断 T1
串口中断 UART

整理成表:

中断源标志位C51中断号入口地址典型用途
外部中断0 INT0IE0interrupt 00003H按键、外部紧急信号
定时器0 T0TF0interrupt 1000BH周期定时、延时、计时
外部中断1 INT1IE1interrupt 20013H第二路外部事件
定时器1 T1TF1interrupt 3001BH定时、计数、串口波特率相关
串行口 UARTRI/TIinterrupt 40023H串口接收和发送

PPT 讲的是 AT89S52,它比基础 51 多了定时器2中断,所以 AT89S52 有 6 个中断源。但如果题目问“51单片机5个中断源”,就按上面 5 个答。

考试注意:

标准51:常说5个中断源
AT89S52:可说6个中断源,多了T2

外部中断0来自 P3.2 引脚,也就是:

P3.2 / INT0

它适合接外部按键、传感器、紧急信号。

例如交通灯系统中:

救护车紧急按钮按下 -> INT0触发 -> 单片机立即进入紧急通行状态

定时器0计数溢出后产生中断,对应标志是 TF0

典型用途:

每1ms中断一次
累计1000次得到1秒
控制LED闪烁
控制交通灯倒计时
扫描按键
刷新显示

这类题常和定时器初值计算一起考。

外部中断1来自 P3.3 引脚:

P3.3 / INT1

作用和 INT0 类似,只是多一路外部事件输入。

定时器1溢出后产生中断,对应标志是 TF1

用途与 T0 类似,但 T1 在串口通信中常被用作波特率发生器,所以在串口题中经常看到:

TMOD = 0x20; // T1方式2
TH1 = 0xFD; // 产生9600波特率
TR1 = 1;

此时 T1 不一定开中断,而是给串口提供波特率。

串口中断比较特殊,它同时包含发送和接收:

RI = 1:接收完成
TI = 1:发送完成

它们共用同一个中断入口:

void uart_isr(void) interrupt 4

所以中断服务程序中必须判断到底是 RI 还是 TI:

void uart_isr(void) interrupt 4
{
if (RI)
{
RI = 0;
// 读取SBUF
}
if (TI)
{
TI = 0;
// 发送完成处理
}
}

特别注意:

串口中断标志RI、TI必须由软件清0。

中断源发生事件后,不是凭空进入中断,而是先置位对应的中断请求标志。

TCON 既和定时器有关,也和外部中断有关。

常用位:

名称作用
TF1T1溢出标志T1溢出时置1
TR1T1运行控制1启动T1,0停止T1
TF0T0溢出标志T0溢出时置1
TR0T0运行控制1启动T0,0停止T0
IE1INT1中断请求标志外部中断1触发时置1
IT1INT1触发方式0电平触发,1下降沿触发
IE0INT0中断请求标志外部中断0触发时置1
IT0INT0触发方式0电平触发,1下降沿触发

例如:

IT0 = 1; // 设置INT0为下降沿触发

串口控制寄存器 SCON 中的低两位是:

TI:发送中断标志
RI:接收中断标志

接收一帧数据完成后:

RI = 1

发送一帧数据完成后:

TI = 1

但它们不会自动清零,所以必须写:

RI = 0;
TI = 0;

复习提纲中“中断开启三要素”非常重要,程序题几乎都要用。

可以记成:

总开关 + 分开关 + 触发/模块设置

EA 是总中断开关。

EA = 1; // 允许总中断
EA = 0; // 禁止所有中断

如果 EA=0,即使某个中断源自己的允许位开了,也不会响应中断。

6.2 第二要素:开对应中断允许位

Section titled “6.2 第二要素:开对应中断允许位”

不同中断源有不同允许位。

中断源允许位
外部中断0EX0
定时器0ET0
外部中断1EX1
定时器1ET1
串口ES

例如开 T0 中断:

ET0 = 1;
EA = 1;

开串口中断:

ES = 1;
EA = 1;

6.3 第三要素:设置触发方式或模块工作方式

Section titled “6.3 第三要素:设置触发方式或模块工作方式”

不同中断还要做不同初始化。

外部中断要设置触发方式:

IT0 = 1; // INT0下降沿触发

定时器中断要设置定时器方式、初值、启动:

TMOD = 0x01;
TH0 = 0xFC;
TL0 = 0x18;
TR0 = 1;

串口中断要设置串口方式、允许接收、波特率:

SCON = 0x50;
TMOD |= 0x20;
TH1 = 0xFD;
TR1 = 1;

IE 是中断允许寄存器,控制中断是否开放。

常用位:

名称含义
IE.7EA总中断允许
IE.4ES串口中断允许
IE.3ET1定时器1中断允许
IE.2EX1外部中断1允许
IE.1ET0定时器0中断允许
IE.0EX0外部中断0允许

如果用位操作:

EA = 1;
EX0 = 1;

如果用字节操作,要能看懂:

IE = 0x81; // 1000 0001,EA=1,EX0=1
IE = 0x82; // 1000 0010,EA=1,ET0=1
IE = 0x90; // 1001 0000,EA=1,ES=1

复位后 IE 通常为 0,所以所有中断默认关闭。


复习提纲给出的默认优先级是:

外部中断0 > 定时器0 > 外部中断1 > 定时器1 > 串口

这句话要背。

如果两个中断几乎同时发生,CPU 必须决定先处理谁。

例如:

定时器0刚好溢出
同时外部中断0按键触发

如果优先级相同或默认情况下,会按内部查询顺序响应。外部中断0排在前面。

PPT 第6章总结的规则可以这样记:

低优先级可以被高优先级打断
高优先级不能被低优先级打断
同级中断之间不能互相打断

例如:

CPU正在执行低优先级T0中断
此时高优先级INT0到来
CPU可以暂停T0中断,先执行INT0中断

但如果正在执行高优先级 INT0,中途来了低优先级串口中断,就不会立刻打断 INT0。

IP 用于设置哪些中断为高优先级。

常用位:

名称作用
PS串口优先级
PT1定时器1优先级
PX1外部中断1优先级
PT0定时器0优先级
PX0外部中断0优先级

例如设置外部中断0为高优先级:

PX0 = 1;

设置 T0 为高优先级:

PT0 = 1;

如果不设置 IP,复位后各中断默认低优先级。


中断响应不是“跳过去就完事”,内部有一套过程。

可以按下面顺序理解:

1. 中断源产生中断请求,置位标志
2. CPU检查EA和对应允许位
3. 判断优先级和当前是否允许响应
4. 当前指令执行完毕
5. 保存断点地址到堆栈
6. 跳转到对应中断入口
7. 执行中断服务程序
8. 执行RETI返回
9. 从堆栈取回断点,继续主程序

这里有两个重要点。

CPU 不会把正在执行的一条指令切成两半。一般是当前指令执行完后才响应中断。

汇编中断服务程序结尾用 RETI,不是普通 RET。C51 中写 interrupt n 后,编译器会自动生成正确的中断返回代码。


C51 中断函数格式:

void 函数名(void) interrupt 中断号
{
// 中断服务程序
}

常用中断号:

0:外部中断0
1:定时器0
2:外部中断1
3:定时器1
4:串口

例子:外部中断0控制 LED 翻转。

#include <reg51.h>
sbit LED = P1^0;
void main(void)
{
LED = 1; // 低电平点亮,初始熄灭
IT0 = 1; // INT0下降沿触发
EX0 = 1; // 开外部中断0
EA = 1; // 开总中断
while (1)
{
// 主程序可执行其他任务
}
}
void int0_isr(void) interrupt 0
{
LED = !LED; // 每触发一次翻转LED
}

中断用于快速处理紧急事件,不适合写长延时。

不推荐:

void int0_isr(void) interrupt 0
{
delay(50000); // 不推荐在中断里长时间延时
}

更推荐:

void int0_isr(void) interrupt 0
{
key_flag = 1; // 设置标志,主程序再处理
}

11.2 中断和主程序共享变量要小心

Section titled “11.2 中断和主程序共享变量要小心”

例如:

volatile unsigned char key_flag = 0;

volatile 的意思是这个变量可能在中断中被改变,提醒编译器不要错误优化。

if (RI)
{
RI = 0;
}
if (TI)
{
TI = 0;
}

忘记清零会导致中断反复进入。

11.4 定时器方式1通常要重装初值

Section titled “11.4 定时器方式1通常要重装初值”

定时器方式1溢出后从 0 开始,如果要周期定时,需要在中断里重新装初值:

void timer0_isr(void) interrupt 1
{
TH0 = 0xFC;
TL0 = 0x18;
}

方式2自动重装则不需要手动重装 TLx。


题目:

什么是中断?

答案:

中断是CPU执行当前程序时,由于内部或外部事件请求,暂停当前程序,转去执行相应中断服务程序,处理完后再返回原程序继续执行的过程。

答案:

外部中断0、定时器0中断、外部中断1、定时器1中断、串行口中断。

答案:

外部中断0 > 定时器0 > 外部中断1 > 定时器1 > 串行口。

答案:

开总中断EA,开对应中断允许位,如EX0、ET0、ES等,并设置中断触发方式或相关功能模块工作方式。
TMOD = 0x01;
TH0 = 0xFC;
TL0 = 0x18;
ET0 = 1;
EA = 1;
TR0 = 1;

要点:

判断RI
读取SBUF
清RI
判断TI
清TI

EA:总开关
EX0/ET0/ES:分开关

只有总开关和分开关都打开,中断才可能响应。

2. 中断标志和中断允许位不是一回事

Section titled “2. 中断标志和中断允许位不是一回事”
标志位:事件是否发生
允许位:是否允许CPU响应这个事件

例如 TF0=1 表示 T0 溢出,但如果 ET0=0,CPU 仍不会进入 T0 中断。

虽然有 RITI 两个标志,但它们共用:

interrupt 4

所以程序里必须判断来源。

IT0=0:低电平触发
IT0=1:下降沿触发

按键中断常用下降沿触发。

5. 中断函数不要写返回值和参数

Section titled “5. 中断函数不要写返回值和参数”

C51 中断函数通常写:

void timer0_isr(void) interrupt 1

不要写成:

int timer0_isr(int x)

中断是指CPU在执行主程序过程中,由于内部或外部事件请求,暂停当前程序,转去执行相应的中断服务程序,处理完后再返回原程序继续执行的过程。中断可以提高系统实时性,减少CPU查询等待。
51单片机常见5个中断源为:外部中断0、定时器0中断、外部中断1、定时器1中断和串行口中断。其默认优先级顺序为:外部中断0 > 定时器0 > 外部中断1 > 定时器1 > 串口。外部中断0对应P3.2/INT0,外部中断1对应P3.3/INT1,串口中断由RI和TI共同触发。
开启中断通常需要三个条件:开总中断EA,开对应中断允许位,如EX0、ET0、ES等,并设置触发方式或相关模块工作方式。外部中断触发方式由IT0、IT1设置,0为低电平触发,1为下降沿触发。串口中断服务程序中必须判断RI和TI,并用软件清零。