1. 中断的基本概念
中断系统是为使 CPU 具有对外界紧急事件的实时处理能力而设置的。当中央处理 CPU 正在处理某件事的时候,外界发生了紧急事件请求,要求 CPU 暂停当前的工作,转而去处理这个紧急事件,处理完成以后,再回到原来被中断的地方继续原来的工作,这样的过程被称为中断。实现这种功能的部件称为中断系统,请求 CPU 中断的被称为中断源。
当几个中断源同时向 CPU 请求中断,CPU 会优先处理紧急事件的中断请求源,即规定每一个中断源有一个优先级别,CPU 总是先响应优先级别最高的中断请求。
2. 中断处理流程
一次完整的中断处理过程可以分为以下六个步骤:
- 中断源发起中断请求:外部或内部事件触发中断标志位置位,向 CPU 发出中断请求。
- 检查中断是否需要被屏蔽 :通过
EA、EX0等中断允许寄存器判断该中断是否被允许响应。 - 中断优先级判断 :当多个中断同时请求时,通过
IP、IPH、XICON寄存器设置优先级,CPU 优先响应优先级最高的中断。STC89C51RC 单片机支持中断嵌套,可实现两层嵌套。 - 保护现场:中断会打断主程序,而中断函数和主程序可能共用同一套硬件资源,如果不保护,中断返回后主程序的数据就被破坏了。
- 执行中断服务函数 :CPU 根据异常向量表找到中断对应的处理函数并自动调用,例如
void Eint0Handler(void) interrupt 0;。 - 恢复现场:中断处理完毕后,恢复之前保存的现场数据,返回主程序继续执行。
3. 中断允许控制(IE 寄存器)
中断是否需要被屏蔽,由中断允许寄存器 IE 控制。IE 寄存器中每一位对应一个中断源的允许位:
| 位 | 名称 | 功能 |
|---|---|---|
| 0 | EX0 | 外部中断 0 允许位,置 1 允许,置 0 禁止 |
| 1 | ET0 | 定时器 0 中断允许位,置 1 允许 T0 中断,置 0 禁止 T0 中断 |
| 7 | EA | 总中断允许位,置 1 时 CPU 开放中断,置 0 时 CPU 关闭中断并屏蔽所有中断申请 |
c
EX0 = 1; // 允许外部中断 0
ET0 = 1; // 允许定时器 0 中断
EA = 1; // CPU 开放总中断
4. 外部中断 0 与 TCON 寄存器
外部中断 0 的控制与标志位位于 TCON 寄存器中,主要包括中断标志位和触发方式控制位:
| 位 | 名称 | 功能 |
|---|---|---|
| IE0 | 外部中断 0 标志 | 当 P3.2 引脚有下降沿时 IE0 置为 1,向 CPU 发送外部中断 0 请求。CPU 响应外部中断时自动清零(下降沿触发方式) |
| IT0 | 触发方式控制 | 置 0 时 P3.2 引脚低电平触发外部中断 0;置 1 时下降沿触发外部中断 0 |
c
IT0 = 1; // 下降沿触发外部中断 0
需要注意的是,当 P3.2 引脚出现下降沿时,IE0 会被硬件置为 1,但不会自动清零。CPU 执行完中断处理函数后会自动将 IE0 清零,无需用户手动操作,为下次中断做好准备。
5. 中断优先级
STC89C51RC 单片机共有 8 个中断请求源,所有中断都有 4 个中断优先级,通过 IP 寄存器、IPH 寄存器、XICON 寄存器修改对应的值来设置中断源的优先级。
CPU 会根据异常向量表找到中断对应的处理函数来执行该函数。中断处理函数需要提前编写好并放在中断向量表中对应的位置,CPU 会自动帮用户调用写好的处理函数,例如:
c
void Eint0Handler(void) interrupt 0
{
// 中断处理代码
}
CPU 处理完毕、执行完中断处理函数后,会将 IE0 清零,表示该次中断处理完成,为下次中断做准备,无需用户手动操作。
6. 保护现场
保护现场是中断处理中非常关键的一步。中断会打断主程序,而中断函数和主程序可能共用同一套硬件资源,如果不保护,中断返回后主程序的数据就被破坏了。
需要保护的主要资源包括:
- PSW 程序状态字:包含 CY、AC、OV、RS0/RS1、P 等标志位
- ACC(A)累加器:运算核心寄存器
- B 寄存器:乘除法运算辅助寄存器
- DPTR(DPL+DPH)数据指针:用于外部数据存储器的寻址
- R0~R7 工作寄存器:通用寄存器组
7. 定时器 / 计数器原理
51 单片机的定时器 / 计数器通过 TMOD 寄存器设置工作模式,通过单片机晶振频率获得时钟频率(默认 12T),实际计数频率为晶振频率除以 12。
| 控制位 | 位置 | 功能 |
|---|---|---|
| TR0 | TCON | 是否开始计数,置 1 开始计数,置 0 禁止计数 |
| C/T | TMOD.2(位 6) | 控制定时器或计数器,置 0 为定时器,置 1 为计数器(P3.4 输入) |
| GATE | TMOD.3(位 7) | 门控位,置 1 时只有 P3.2 为高电平且 TR0 为 1 才开始定时 / 计数 |
| MODE.0 / MODE.1 | TMOD 低两位 | 设置计数器或定时器的模式,支持 13 位或 16 位模式 |
TL0 与 TH0 各 8 位拼为 16 位,计数终点为 FFFF。通过控制初值进行定时,当计数器溢出时触发中断。
c
// 设置定时器 0 为 16 位定时模式
TMOD &= ~0x3;
TMOD |= 0x1;
// 定时器赋初值
TH0 = 0x4B;
TL0 = 0xFE;
// 开启定时器中断和总中断
ET0 = 1;
EA = 1;
// 启动定时器 0
TR0 = 1;
8. 晶体管显示原理
在 51 单片机数码管显示电路中,晶体管(三极管)通常作为位选驱动开关使用。由于单片机 I/O 口的驱动能力有限,无法直接点亮多位数码管,因此通过晶体管放大电流来驱动每一位数码管的公共端(共阴或共阳),实现动态扫描显示。
以共阴数码管为例,其显示原理如下:
- 段选(P0 口) :P0 口输出段码(如
0x3F表示数字 0),控制数码管中哪几段 LED 点亮。 - 位选(晶体管) :通过
Led1En~Led4En控制晶体管导通,选中当前要显示的某一位数码管。 - 动态扫描:依次快速轮流点亮每一位,利用人眼视觉暂留效应,看起来四位数码管同时显示。
在代码中,Led1En、Led2En、Led3En、Led4En 分别控制四个位选晶体管。当某一位的使能引脚为低电平时,对应晶体管导通,该位数码管被选中;此时 P0 口输出的段码就会显示在该位上。
c
// 显示个位:选中第 1 位数码管,输出个位段码
Led1En = 1;
Led2En = 0;
Led3En = 0;
Led4En = 0;
P0 = NumCode[Ge];
Delay1ms();
// 显示十位:选中第 2 位数码管,输出十位段码
Led1En = 0;
Led2En = 1;
Led3En = 0;
Led4En = 0;
P0 = NumCode[Shi];
Delay1ms();
通过 DisplayNum 函数,将四位数字拆分为千位、百位、十位、个位,再配合晶体管位选和 P0 口段码输出,即可完成四位数码管的动态显示。
9. 完整代码示例
下面给出结合定时器中断与晶体管数码管显示的完整 51 单片机代码,实现每秒数字加一并在四位数码管上显示:
c
#include <reg52.h>
#include <intrins.h>
sbit Led1En = P1^0;
sbit Led2En = P1^1;
sbit Led3En = P1^2;
sbit Led4En = P1^3;
unsigned char NumCode[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
volatile int cnt = 0;
volatile int Num = 0;
void Timer0Handler(void) interrupt 1
{
// 定时器赋初值
TH0 = 0x4B;
TL0 = 0xFE;
cnt++;
if (20 == cnt)
{
Num++;
cnt = 0;
}
return;
}
void Delay1ms(void) //@11.0592MHz
{
unsigned char data i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
void DisplayNum(int TmpNum)
{
int Qian = 0;
int Bai = 0;
int Shi = 0;
int Ge = 0;
Qian = TmpNum / 1000;
Bai = TmpNum % 1000 / 100;
Shi = TmpNum % 100 / 10;
Ge = TmpNum % 10;
// 显示个位
Led1En = 1;
Led2En = 0;
Led3En = 0;
Led4En = 0;
P0 = NumCode[Ge];
Delay1ms();
// 显示十位
Led1En = 0;
Led2En = 1;
Led3En = 0;
Led4En = 0;
P0 = NumCode[Shi];
Delay1ms();
// 显示百位
Led1En = 0;
Led2En = 0;
Led3En = 1;
Led4En = 0;
P0 = NumCode[Bai];
Delay1ms();
// 显示千位
Led1En = 0;
Led2En = 0;
Led3En = 0;
Led4En = 1;
P0 = NumCode[Qian];
Delay1ms();
return;
}
int main(void)
{
// 设置定时器工作模式(16 位定时器)
TMOD &= ~0x3;
TMOD |= 0x1;
// 定时器赋初值
TH0 = 0x4B;
TL0 = 0xFE;
// 定时器中断开启
ET0 = 1;
EA = 1;
// 开启定时器 0
TR0 = 1;
while (1)
{
DisplayNum(Num);
}
return 0;
}
代码中,定时器 0 每 50ms 产生一次中断,中断 20 次(即 1 秒)后 Num 加一;主循环不断调用 DisplayNum,通过晶体管位选和 P0 口段码输出,将当前计数值动态显示在四位数码管上。
8. 关键知识点总结
| 模块 | 关键点 | 说明 |
|---|---|---|
| 中断概念 | 实时处理 | CPU 暂停当前工作,转去处理紧急事件,处理完再返回 |
| 中断流程 | 六步处理 | 请求 → 屏蔽检查 → 优先级判断 → 保护现场 → 执行服务函数 → 恢复现场 |
| IE 寄存器 | 中断允许 | EX0 控制外部中断 0,ET0 控制定时器 0,EA 为总开关 |
| TCON 寄存器 | 标志与触发 | IE0 为外部中断 0 标志,IT0 控制触发方式(电平 / 下降沿) |
| 中断优先级 | IP / IPH / XICON | 8 个中断源,4 个优先级,支持两层嵌套 |
| 保护现场 | 硬件资源 | PSW、ACC、B、DPTR、R0~R7 等寄存器需要保护 |
| 定时器 | TMOD / TCON | C/T 选择定时或计数,GATE 门控,MODE 选择 13 位或 16 位 |
| 计数原理 | 初值控制 | TL0 + TH0 拼为 16 位,溢出触发中断,通过初值控制定时时长 |
1.中断源发起中断请求 IT0
2.检查中断是否需要被屏蔽 EA, EX0
3.中断优先级判断(中断嵌套,单片机支持两层嵌套)IP, IPH, XICON
4.保护现场,为什么?具体保护什么?
5.执行中断服务函数 void Eint0Handler(void) interrupt 0;
6.恢复现场
2
中断是否需要被屏蔽,IE寄存器,
0(EX0),置1,1允许T0中断,0禁止T0中断
EX0 = 1;
7(EA), 置1,1CPU开放中断,0CPU关闭中断屏蔽所有中断申请
EA = 1;
TCON
IE0:外部中断0标志,1向CPU发送外部中断0请求
CPU响应外部中断时,物理清0(下降沿)
当P32引脚有下降沿时将IE0置为1,IE0不会请0,CPU执行中断处理函数后自动清0
0(IT0), 置1 触发方式, 0P3.2引脚低电平触发外部中断0
1下降沿触发外部中断0 IT0 = 1;
3
中断优先级:
STC89C51RC单片机共有8个中断请求源,所有中断都有4个中断优先级,
通过IP寄存器,IPH寄存器,XICON寄存器,修改对应的值来设置中断源的优先级
CPU会根据异常向量表来找到中断对应的处理函数来执行该函数
提前编写好中断处理函数放在中断向量表中对应的位置,CPU会自动帮用户调用
写好的处理函数
void Eint0Handler(void) interrupt 0;
CPU处理完毕执行完毕中断处理函数后,会将IE0清零,表示该次中断处理完成
为下次中断做准备,无需用户手动操作
中断:
中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。
当中央处理CPU正在处理某件事的时候,外界发生了紧急事件请求,要求CPU暂停
当前的工作,转而去处理这个紧急事件,处理完成以后,再回到原来被中断的地方
继续原来的工作,这样的过程被称为中断。实现这种功能的部件称为中断系统,请求
CPU中断的被称为中断源。当几个中断源同时向CPU请求中断,CPU会优先处理紧急
事件的中断请求源,即规定每一个中断源有一个优先级别,CPU总是先响应优先
级别最高的中断请求。
保护现场:
中断会打断主程序,而中断函数和主程序可能共用同一套硬件资源,如果不保护,
中断返回后主程序的数据就被破坏了。
PSW 程序状态字(含 CY、AC、OV、RS0/RS1、P)
ACC(A) 累加器
B B寄存器
DPTR(DPL+DPH) 数据指针
R0~R7 工作寄存器
计数器:
通过TMOD寄存器设置计数器或定时器的模式
通过单片机晶振频率获得时钟频率(默认12T)时钟频率÷12
TCON-TR0是否开始计数1计数0禁止
TMOD.2 6(1)C/T控制定时器或计数器0为定时器或计数器,0定时器,1计数器(P3.4)
TMOD.3 7(1)GATE控制定时器0, 1只有P3.2高电平,TR0为1开始定时/计数
TL0与TH0各8位拼为16位,终点FFFF,通过控制初值进行定时
溢出触发中断
通过MODE.0以及MODE.1来设置计数器或定时器的模式
13位or16位