11.中断系统

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,开始流水灯,再次按下,停止。

猜你喜欢

转载自blog.csdn.net/qq_44628230/article/details/104273450