CPU的中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。
具体过程:
某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程成为中断。
优势:
1.分时操作。CPU可以分时为多个I/O设备服务,提高了计算机的利用率;
2.实时响应。CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
3.可靠性高。CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。
原则:
1.CPU同时接收到几个中断时,首先响应优先级别最高的中断请求;
2.正在进行的中断过程不能被新的同级或低优先级的中断请求所中断;
3.正在进行的低优先级中断服务,能被高优先级中断请求所中断。(中断嵌套)
中断响应条件:(需同时满足)
1.中断源有中断请求;
2.此中断源的中断允许位为1;
3.CPU开中断(即EA=1)。
中断请求源的数量,体现了单片机能处理任务量的多少,代表了单片机性能的高低,或者说资源的丰富程度。
省赛一般用到的是定时器中断(另外外部中断可用可不用一般不用),串口中断一般在国赛中用。
外部中断(了解)(以外部中断0即INT0为例)
以下是一段以独立按键控制LED流水灯的程序,要实现的是按下S5时,开始流水灯,按其他独立按键时停止流水灯。
#include<STC15F2K60S2.H>
unsigned char KEY_RUN=0;
unsigned char LED=0;
void KEY_Scan(void);
void Delayms(int ms);
void main(void)
{
P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //初始化程序
while(1)
{
KEY_Scan();
if(KEY_RUN==1) //KEY_RUN=1时开始流水
{
P0=~(0X01<<LED);
LED++;
if(LED==8)LED=0;
Delayms(1000);
}
}
}
void KEY_Scan(void)
{
if(P30==0) //按下S7,停止流水
{
Delayms(5);
if(P30==0)
{
KEY_RUN=0;
}
while(!P30);
}
if(P31==0) //按下S6,停止流水
{
Delayms(5);
if(P31==0)
{
KEY_RUN=0;
}
while(!P31);
}
if(P32==0) //按下S5,开始流水
{
Delayms(5);
if(P32==0)
{
KEY_RUN=1;
}
while(!P32);
}
if(P33==0) //按下S4,停止流水
{
Delayms(5);
if(P33==0)
{
KEY_RUN=0;
}
while(!P33);
}
}
void Delayms(int ms)
{
int i,j;
for(i=0;i<ms;i++)
for(j=845;j>0;j--);
}
经上电实验我们会发现一个奇怪的现象,当按下S5时可以开启流水灯,但是按下另外3个独立按键时不能立即停止流水灯,而需要按下超过1s的时间,这是因为在main函数中每实现流水一次后会有1s的延迟,延迟结束才能再次判断按键状态,而我们按下按键的时间显然少于1s。程序逻辑本身没有问题,但是给工程的应用中带来一些麻烦,为了解决这一问题,我们下面将采用外部中断的方式来控制LED灯的流水。
由INT0对应于芯片引脚P32知,当P32引脚发生触发,INT0中断起作用。(P32引脚对应于独立按键S5,即当S5按下或抬起的时候,会触发外部中断0)
同上一节定时器中断的使用一样,使用前需要先对外部中断0进行配置(配置见下方程序)。
下面我们尝试用中断的方式来写一段控制LED流水灯的程序,我们使用到了外部中断0。
//用外部中断0来控制LED流水灯
#include<STC15F2K60S2.H>
unsigned char LED=0;
unsigned char LED_RUN=0;
void Delayms(int ms);
void main(void)
{
P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //初始化程序
//配置外部中断0
IT0=0; //选定触发方式,上升沿和下降沿均可触发
EX0=1; //外部中断0中断允许位
EA=1; //CPU总中断允许控制位
while(1)
{
if(LED_RUN==1)
{
P0=~(0X01<<LED);
LED++;
if(LED==8)LED=0;
Delayms(1000);
}
}
}
void Delayms(int ms)
{
int i,j;
for(i=0;i<ms;i++)
for(j=845;j>0;j--);
}
void exint0(void) interrupt 0
{
if(LED_RUN==0)LED_RUN=1;
else LED_RUN=0;
}
程序使用了外部中断0,上电实验后我们发现,当我们按下S5且不抬手的时候,流水灯开启,但是当我们抬手之后就会停止,这个现象的原因是:按下和抬起分别都触发了外部中断0,相当于按一下触发两次,LED_RUN的状态将发生两次反转,为了解决这一问题,我们修改了程序如下:
#include<STC15F2K60S2.H>
unsigned char LED=0;
unsigned char LED_RUN=0;
void Delayms(int ms);
void main(void)
{
P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //初始化程序
//配置外部中断0
IT0=0; //选定触发方式,上升沿和下降沿均可触发
EX0=1; //外部中断0中断允许位
EA=1; //CPU总中断允许控制位
while(1)
{
if(LED_RUN==1)
{
P0=~(0X01<<LED);
LED++;
if(LED==8)LED=0;
Delayms(1000);
}
}
}
void Delayms(int ms)
{
int i,j;
for(i=0;i<ms;i++)
for(j=845;j>0;j--);
}
void exint0(void) interrupt 0
{
if(P32==1)
{
if(LED_RUN==1)LED_RUN=1;
else LED_RUN=0;
}
}
在外部中断0函数中增加了对于抬手的判断,只有抬手触发的时候才会执行LED_RUN的取反。解决了一次按键两次触发的问题。此时可实现按下一次S5按键可开启LED流水灯,再按一下可以停止流水灯。体现出了中断的优势,可以触发后立即做出响应,中断当前所执行的程序转而进行中断的程序,执行结束再返回原程序断点位置。
在此基础上,用用硬件延时代替软件延时,使用定时器0代替延时函数,程序如下:
#include<STC15F2K60S2.H>
unsigned char LED=0;
unsigned char LED_RUN=0;
unsigned char tt=0;
void Timer0Init(void);
void main(void)
{
P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //初始化程序
Timer0Init();
ET0=1;
EX0=1;
EA=1;
IT0=0;
while(1)
{
}
}
void Timer0Init(void) //5毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x28; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void exint0(void) interrupt 0
{
if(P32==1)
{
if(LED_RUN==0)LED_RUN=1;
else LED_RUN=0;
}
}
void Timer0(void) interrupt 1
{
tt++;
if(tt==200)
{
if(LED_RUN==1)
{
P0=~(0X01<<LED);
LED++;
if(LED==8)LED=0;
}
}
}
上电后,按下S5,开始流水灯,再次按下,停止。