上期我们学习了独立按键&矩阵按键,这次我们来学习外部中断。
当CPU正在处理某个事件的时候,外界发生了紧急事件请求,CPU需要暂停当前的工作,转而去处理这个紧急事件,处理完之后,再次回到之前被中断的地方,继续执行原来的工作,这样的过程就叫做中断。
举个栗子:你现在正在家里面看电视,这个时候,你妈妈让你去把水果洗掉,这个时候你就需要去把水果洗掉,洗完之后再回来继续看电视。看电视就是当前正在处理的事情,洗水果就是中断请求,或者叫中断源,你洗完水果回来继续看电视这个过程就是中断。
我们把引起中断的原因,或者能够发出中断请求信号的来源统称为中断源。洗水果是一种中断源,拿外卖也一样可以,也就是说中断源可以有多个。
STC15F2K60S2系列单片机的中断源也一样有多个,最主要的有外部中断0(INT0)、定时器0中断、外部中断1(INT1)、定时器1中断、串口1中断这五个,本篇我们先介绍外部中断0、1,其他的后面再讲。
单片机的P3.2/INT0和P3.3/INT1引脚除了作为IO口使用外,还分别对应着外部中断0和外部中断1,当检测到对应引脚的电平发生变化时,可以产生中断请求,进而执行相应的中断函数。
举个栗子:这两个引脚在单片机上分别连接按键S5和S4,当我们按下或松开按键时,会在对应引脚上产生一个电平变化,进而在单片机内部产生中断请求,单片机就可以选择是否要产生中断去处理相应的事件。
我们要使用中断,那就就需要对中断相关的寄存器进行配置。
比较常用的中断寄存器有以下几个:
1.中断允许寄存器IE(可以位寻址)
很明显:我们需要使用外部中断0、1,就需要吧EX0(外部中断0允许位)和EX1(外部中断1允许位)都置为1,同时要把EA(总中断允许控制位)置为1。
PS:中断允许可以理解为单片机检测到了对应引脚的到底电平的变化,但这个电平变化是否需要我们产生中断去响应它,这些是根据我们程序来决定的,因此存在中断允许位来决定是否需要产生中断。
2.定时器/计数器控制寄存器TCON(可位寻址)
PS:前面我们讲过单片机可以检测对应引脚的电平变化,那么究竟时怎样的电平变化呢?
这里提供了两种选择:当IT0/IT1为0时,对应的触发方式为上升沿和下降沿都可以触发。
当为1时,则只可以下降沿触发。
3.中断优先级控制寄存器IP/IP2
前面我们讲过,中断源是有多个的,那么,单片机同时面对多个中断时,究竟该先相应哪一个请求呢?这个时候就需要用到我们的优先级控制寄存器。当对应位置为1时,可以设计其优先级为高优先级。当同时存在多个中断请求时,单片机会首先响应优先级高的中断,在处理完优先级高的中断之后再去处理优先级低的中断请求,最后在处理完所有的中断请求之后再去处理主函数。
中断优先级的特点:
1.低优先级的中断可以被高优先级的中断所中断(中断嵌套),反之则不可以。
2.任何一个中断,一旦被响应之后,就不可以被同优先级的中断所打断。
前面我们提到了中断优先级控制寄存器,可以设置中断的优先级。
但是在面对比较复杂的程序时,往往会出现同时存在多个中断优先级相同的情况,那么在我面对多个优先级相同的中断请求时,单片机该如何应对呢?
在单片机的内部存在一个辅助优先级结构,在面对相同优先级的中断请求时,会按照如图所示的顺序进行响应。
讲了那么多,一个完整的中断流程时怎么样的?
在使用中断之前,我们需要:
1.选择中断触发方式
2.开启中断允许
3.设置中断优先级(这一步在中断比较少的情况下可以省略)
4.编写中断服务函数。
前三步可以在中断初始化函数中实现,第四步则需要编写单独的中断服务函数。
1.初始化函数
//外部中断0初始化函数
void IT0_Init(void)
{
EX0 = 1; //开启外部中断0允许
EA=1; //开启中断总允许
IT0=1; //设置触发方式为下降沿触发,为0时触发方式为下降沿和上升沿都触发
PX0 = 0; //设置低优先级,为1时为高优先级
}
//外部中断1初始化函数
void IT1_Init(void)
{
EX1 = 1; //开启外部中断0允许
EA=1; //开启中断总允许
IT1=1; //设置触发方式为下降沿触发,为0时触发方式为下降沿和上升沿都触发
PX1 = 1; //设置低优先级,为1时为高优先级
}
2.中断服务函数
中断服务函数的写法,不同于普通的函数,具体写法如下:
void 函数名(void) interrupt 中断号
{
//函数内容
}
中断服务函数的特点:
1.中断服务函数不能带有参数和返回值。
2.中断服务函数后面需要带有 interrupt关键字 ,并且标注中断号
使用时需要注意的点:
1.中断函数讲究的时快进快出,不要进行延时,大量浮点运算等耗时操作。
2.不能够再中断函数中递归,调用自身。
3.中断函数中不宜写太多的代码,否则可能导致中断服务函数还未执行完,因为符合条件,又触发了一次中断,程序就一直卡在中断服务程序中,导致整个系统崩溃;
4.当系统有多个中断时,为防止在执行当前中断服务函数时,被另一个不必要的中断打断,一个可以参考的解决方法时在进入中断时关闭其他中断,并在中断服务函数的最后一句再次开启中断开关
//外部中断0服务函数
void External_Hander0() interrupt 0
{
//函数内容
}
//外部中断1服务函数
void External_Hander2() interrupt 2
{
//函数内容
}
中断嵌套是指 中断系统 正在执行一个中断服务时,有另一个优先级更高的中断提出 中断请求 ,这时会暂时终止当前正在执行的级别较低的 中断源 的服务程序,去处理级别更高的中断源,待处理完毕,再返回到被中断了的 中断服务程序 继续执行的过程。
举个栗子:你现在再看看电视,如你妈妈叫你去洗水果,在你洗水果的时候,你定的外卖到了,这个时候,你要停下洗水果,去拿外卖,拿完外卖之后再去继续洗水果,洗完水果才能去继续看电视。
看电视就是主程序,洗水果就是低级的中断,拿外卖就是高级的中断。
1.main.c
#include
#include "LS138.h"
#include "Interrupt.h"
static unsigned int Count = 0;
void main()
{
IT0_Init();
IT1_Init();
LS138_Init();
while(1)
{
if(Count>= 9999)
{
Count=9999;
}
SEG_Write_Num(Count);
}
}
//外部中断0服务函数
void External_Hander0() interrupt 0
{
Delayxms(20);
if(P3^2==0)
{
Count++;
}
}
//外部中断1服务函数
void External_Hander2() interrupt 2
{
Delayxms(20);
if(P3^3==0)
{
Count--;
}
}
PS:SEG_Write_Num(Count)这个函数在LS138.h头文件中,不过时我自己后面加上去的,可在数码管上显示一个四位数的数字,
具体代码如下:
//在数码管的前四位上显示对应的数字
//显示数字大小为0~9999
void SEG_Write_Num(unsigned int Data)
{
unsigned char i =0;
unsigned char arr[4];
arr[0]=Data/1000%10;
arr[1]=Data/100%10;
arr[2]=Data/10%10;
arr[3]=Data%10;
for(i=0;i<4;i++)
{
LS138_Clear();
LS138_Set(7); //ls138译码器位选为数字输入
P0 = NixieTube[arr[i]];
LS138_Set(6); //ls138译码器位选为管脚选择,对应位为1,则对应的数码管亮起
P0 = (unsigned char )0x01<<i;
LS138_Clear();
Delayxms(2);
P0=0xFF;
}
}
2.Interrupt.h
#ifndef __INTERRUPT_H_
#define __INTERRUPT_H_
#include
//外部中断0初始化函数
void IT0_Init(void);
//外部中断1初始化函数
void IT1_Init(void);
外部中断0服务函数
//void External_Hander0() interrupt 0
//{
//
//}
外部中断1服务函数
//void External_Hander2() interrupt 2
//{
//
//}
#endif /*__INTERRUPT_H_*/
3.Interrupt.c
#include "Interrupt.h"
//外部中断0初始化函数
void IT0_Init(void)
{
EX0 = 1; //开启外部中断0允许
EA=1; //开启中断总允许
IT0=1; //设置触发方式为下降沿触发,为0时触发方式为下降沿和上升沿都触发
PX0 = 0; //设置低优先级,为1时为高优先级
}
//外部中断1初始化函数
void IT1_Init(void)
{
EX1 = 1; //开启外部中断0允许
EA=1; //开启中断总允许
IT1=1; //设置触发方式为下降沿触发,为0时触发方式为下降沿和上升沿都触发
PX1 = 1; //设置低优先级,为1时为高优先级
}
之前我们在独立按键&矩阵按键中提到过按键抖动的问题,由于我们的外部中断依然是通过按键的按下和弹起带来的电平变化引起中断的,所以,我们还是毫不意外的遇到的按键抖动的问题。
之前我们都是通过软件延时或者频繁的进入按键扫描函数来消抖的,但是我们前面讲到过,最好不要在中断函数里面进行延时等耗时操作,同时,中断服务函数是在中断触发之后才能进入的,不允许我们随意的调用,这一下就给我难住了…
左思右想,我也没有想出一个好的解决方案,只能等后面学习了定时器再看看有没有解决方法,或者有没有大佬能够评论区指点一二。
外部中断最主要的就是要理解中断的含义,并且会配置中断相关的寄存器,这一期我们也只是简单的介绍了一下中断的相关概念,其实还有相当多的中断内容没有提及到,毕竟我的能力也有限,其他的内容,感兴趣的可以自己去找资料学习,我这里就不再继续讲下去了。