一.中断系统基础知识:
1.中断系统结构示意图:
2.中断源(5个):
外部中断0(INT0)、定时器中断0(T0)、外部中断1(INT1)、定时器中断1(T1)、串行口中断(RXD、TXD)。
3.中断源的优先级:
默认情况下按INT0—>T0—>INT1—>T1—>(RXD、TXD) 的顺序划分优先级,INT0优先级最高,(RXD、TXD)优先级最低。如若改变优先级,需要对IP优先级寄存器单独配置,下面会介绍。
4.中断处理过程示意图:
**过程简述:**在执行主程序(main函数)的时候,收到中断响应,立即去执行中断的程序,执行完返回来继续执行主函数(main函数)里的程序的过程。 //结合左图更好理解
5.各个中断源触发中断的条件:
INT0: P3.2引脚输入低电平 / 触发下降沿,标志位IE0=1(编程中不用配置),申请中断;
**T0:**当产生计数溢出时触发标志位TF0=1(编程中不用配置),申请中断;
INT1: P3.3引脚输入低电平 / 触发下降沿,标志位IE1=1(编程中不用配置),申请中断;
**T1:**当产生计数溢出时触发标志位TF1=1(编程中不用配置),申请中断;
**串行口:**当一个串行帧发送或接受完毕时,RI或TI由0变到1(编程中要重置),申请中断;
6.中断系统的相关寄存器:
6.1 必有的寄存器:
顾名思义就是不管外部中断、定时器还是串行口 大家都有的:
①IE(中断允许寄存器):
**EA:**相当于总开关,EA=1,所有中断源的中断将被打开,所以编程中要使用任何一个中断的话,EA是必须要 打开。
其他的: ES、ET1、EX1、ET0、EX0 分别对应串行口、定时器1、外部中断1、定时器0、外部中断0。相当于小开关,单独控制一个中断源。所以编程中要使用任一中断的话,都必有EA+( ES、ET1、EX1、ET0、EX0 )对应的一个打开。
②IP(中断优先级寄存器):
PS、PT1、PX1、PT0、PX0 分别对应串行口、定时器1、外部中断1、定时器0、外部中断0。如果只用到一个 中断源,这个IP可以不配置,如果用到不止一个中断源,而又没配置这个IP,那么默认为自然级别顺序(前面 讲过,从INT0到串行口由高到低)。如果配置了优先级该怎么进行前面也讲了(第4点)。
6.2 中断源间共有:
意思就是外部中断、定时器、串行口之间两两共有的
TCON寄存器:(外部中断和定时器共有)
等讲到每个中断源再具体展开讲。
6.3 独有的寄存器:
顾名思义也就是在中断系统属于外部中断、定时器、串行口单独拥有的。
①TMOD(定时器特有):
只能在用到定时器中断时使用,外部中断,串行口用不了,具体的放在每个中断源里讲。
②SCON和PCON(串行口特有):
只能用于串行口中断,其他中断用不了,等讲到串行口再展开讲。
7.中断函数:
中断函数是启用某个中断必不可少的部分,它的格式为 “void+中断函数名(void)+interrupt+中断号”,我这里用代码敲出来,方便大家复制,这里举例5个中断源就写5个中断函数。
void Int0_Routine(void) interrupt 0; //外部中断0的中断函数,最后的“0”很重要
void Timer0_Rountine(void) interrupt 1; //定时器0的中断函数,最后的“1”很重要
void Int1_Rountine(void) interrupt 2; //外部中断1的中断函数,最后的“2”很重要
void Timer1_Routine(void) interrupt 3; //定时器1的中断函数,最后的“3”很重要
void UART_Routine(void) interrupt 4; //串行口的中断函数,最后的“4”很重要
其中 Int0_Routine(void) 所在列只是函数名,你想取什么名字,只要编译器允许的情况下随便你取。主要关注(0-4)即中断号,切记要一一对应,避免张冠李戴。
二、外部中断(INT0和INT1)
这里先补充个知识点: 可位寻址和不可位寻址:可位寻址就是一个寄存器里的任何一个标志位可以单独拎出来赋值(例:TCON里的IT0,可以初始化的时候直接使IT0=1),它也可以整个寄存器一起赋值;而不可位寻址就是一个寄存器必须整个赋值,不能单独拎出一个标志位来赋值(例:TMOD寄存器,初始化的时候只能TMOD=0x~~)。
1.相关寄存器的分析:
外部中断、定时器、串行口都共有的就不展开讲了,前面也已介绍完。
我们可以先看中断系统的示意图,要想使用外部中断 INT0 ,从左往右看我们要配置3个寄存器:TCON、IE和IP。TCON是外部中断和定时器共有的,如下图所示:
左边属于定时器部分,右边才是外部中断部分,这里只看右边部分,IT0表示外部中断0,IT1表示外部中断1。(属于可位寻址)。
红色圈出来的部分IT0和IT1是编程时需要赋值的,虽然中断系统示意图上标了IE0和IE1,但它们只是中断申请标志位,并不用实际赋值,它们由IT0和IT1决定,也就是给IT0和IT1赋值后他们就为1,然后提出中断。外部中断0中IT0=0表示低电平触发,随后IE0就等于1,即申请中断,IT0=1表示下降沿触发,同时IE0等于1申请中断。所谓下降沿触发,看如下电路图可以看到外部中断0接了P3.2引脚,外部中断1接了P3.3引脚。同时P3.2和P3.3引脚也接了独立按键,所以简单来说选择下降沿触发中断就是通过独立按键来启动中断。(比如用的是外部中断0,那么执行主函数的时候按一下P3.2,就会跳出主函数到外部中断0那部分去执行程序,等外部中断的程序执行完了再回到主程序接着执行剩下的)。外部中断1同理,只是按键不同。
接下来看IE(中断允许寄存器)寄存器,首先总中断EA是必须要开的,所以EA=1,如果要使用外部中断0(INT0)就让EX0=1,如果要使用外部中断1(INT1)就让EX1=1。
然后是这个IP(优先级寄存器),如果只是用到某个中断源,可以不配置。若是两个以上,不配置就是默认自然优先级,建议还是要配置的。配置的话就是PX0或PX1=0是低优先级,PX0或PX1=1是高优先级。
最后就是中断函数了,要使用外部中断0(INT0),那么中断函数就是 void Int0_Routine(void) interrupt 0;
如果要使用外部中断1,那么中断函数是void Int1_Routine(void) interrupt 2。注意:使用多少个中断就有多少个中断函数。
2.外部中断代码框架:
2.1 伪代码:
{
1.选择低电平触发还是下降沿触发;
2.确定优先级;
3.开启总中断和对应的中断允许开关;
}
2.2.初始化代码:(这里以外部中断0为例)
#include<reg52.h>
int main()
{
IT0=1; //选择下降沿触发。
PX0=0; //低优先级,高优先级为1
EX0=1; //开启外部中断0允许
EA=1; //开启总中断。
while(1)
{
}
}
void Int0_Routine(void) interrupt 0 //因为是外部中断0,所以对应中断号为0
{
}
3.外部中断案例分享:
题目:让8位数码管的前3位显示数字,触发外部中断0时,让这个数字每按一下按键,数字加1。触发外部中断1时,每按一下按键,数字减1.(用外部中断完成)
最好使用模块化编程。
#include <REGX52.H>
#include<INTRINS.H>
sbit C = P2^4;
sbit Ba = P2^3;
sbit A = P2^2;
void int0();
void wei_num(char number);
void Delay(int xms);
int shu=0;
char num_shu[]={
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
void main()
{
int0();
while(1)
{
wei_num(1);
P0=num_shu[shu/100];
Delay(1);
P0=0x00;
wei_num(2);
P0=num_shu[(shu/10)%10];
Delay(1);
P0=0x00;
wei_num(3);
P0=num_shu[shu%10];
Delay(1);
P0=0x00;
}
}
void int0() //初始化外部中断0
{
EA=1;
EX0=1;
IT0=1;
EX1=1;
IT1=1;
}
void wei_num(char number) //这里我的是普中51单片机,数码管接了个74HC138芯片,得这样去实现
{
switch(number)
{