7-《电子入门趣谈》第一章_一切从单片机开始-1.3.4中断

2019-04-15 17:42发布

1.3.4 中断     正如上文所说的,如果我个告诉我你,上面利用按键控制一个灯的程序依然存在问题,您了会不会疯掉呢?是的,它确实还存在问题。     以上所有程序,单片机都是运行在查询方式下,所谓查询方式就是在死循环里一句一句地运行,一回一回地跑程序,一次一次地检测,一遍一遍的执行,你可以假想一个程序指针,它从main()主函数开始,从上往下一步一步地走,指到哪,程序就运行到哪,对于这种方式,写按键程序有一个特别大的隐患。就是通常情况下,主程序里会写很长一段程序,假如运行一遍需要一百秒的话,一个按键检测的程序在其中只占了很小一部分,也就几微秒,看看下面的例程。delay(10000)用来模拟其他程序段运行所需的时间,假如为100秒,当我们按下按键的时候,程序指针很可能正指在delay(10000)里呢,D1根本没有反应,除非你一直按着按键不放。     这就是查询方式的一个弊端。 /******************************************************** **函数名:main(void) **返回:无 **函数功能描述:模拟查询方式下的程序运行时间 **********************************************************/ voidmain(void) //程序开始运行 { D1=1; //程序初始化,令二极管不发光 while(1) //循环检测 { delay(10000);//模拟其他程序的运行时间 if(S2==0) //如果检测到P2.0管脚为低电平,即按键按下 { delay(10); //延时一下 if(S2==0) { while(S2==0); D1=~D1; //二极管转换发光状态,返回继续循环检测 } } } }

    单片机开发者们肯定也遭遇到了这个难题,于是这帮人茶不思饭不想,日夜烧香拜佛祈求找出一个解决方法来,终于皇天不负有心人,最后还真就想出来一个解决方案,这就是传说中的“中断”。     同志们,下文要讲的“中断”内容,非常重要,如果你看着看着困了,就用锥子扎自己的大腿,千万不要趴在这本书上睡着了,尤其是喜欢流哈拉子的美女,那样很不雅观的。     上过单片机实训的孩子们应该都听老师举过这样一个例子:老师给大家答疑,先给第一个同学答疑,再给第二个同学答疑,以此类推,全班一共30个人,假使每个人答疑时间10分钟,那么全班一共需要300分钟也就是5小时(这个不太可能哈……老师举例子真不讲究)。突然,有一个同学举手说,老师我想上厕所,你希望老师是什么反应呢,是不是应该立马停下答疑,告诉举手的同学“去把去吧”,然后再返回来继续给同学答疑,应该是这样吧(除非你想憋死他?)但是如果万一老师偏偏是先完成给全班同学答疑的任务,再来管这个同学,那这个可怜孩子肯定就完蛋了。     为什么说这个例子呢,光看它我们会觉得这个老师好白痴啊。但是现在我们假设这个老师就是一个单片机,单片机的主函数就是给同学答疑,当程序进入main()的while()循环中,它就会从头运行到尾,而按键按下就是那个让小便憋坏了的同学,在这个悲情的故事中,这个主程序需要300分钟。我们希望的是,当按键按下时,单片机放下手里的工作,转先去完成二极管D1的状态转换然后再回来继续运行程序,这个过程,就叫“中断”。     这是一个非常人性化的编程理念。“中断”就是在程序运行中打断它的运行,先去完成实现中断函数里的任务。一般来说,中断函数里的任务是比主程序任务更高级、更紧急的。另外,不同的中断类型优先级也不一样,就像上课答疑过程中,有的同学要去厕所,突然有个同学因为听不懂课晕厥了,那么老师此时需要做的肯定是先处理这个晕厥的同学,然后再告诉那个需要上厕所的同学“去吧去吧”,然后再回来继续答疑。这就是中断优先级的概念了。下图是单片机运行中断的示意图。在单片机里要想实现一个“中断”功能,有两个必要的条件,1、有中断请求(中断源);2、必要的中断开关全部开启。
    下图是标准51单片机中断系统的一个示意图。编程的时候需要参考它。51单片机比较简单,它的中断源就这么几个,其中INT0和INT1分别对应在P3.2和P3.3管脚,名字叫做“外部中断”接口,T0和T1分别代表“定时器中断”,RX和TX代表“串口通信中断”。     需要说明一下,52型号的单片机由于多了一个T2定时器(T2定时器跟T0,T1功能相差很大,T2要强大很多),因此多了一个中断向量2个中断标志(溢出中断和T2外部中断),而且需要注意,T2中断标志必须由软件清除标志位。
    以中断源INT0为例。看它从左至右有一条线,只有当这条线都导通了之后,中断才能产生。 IT0:外部中断产生方式。若IT0=1,代表“下降沿”产生中断,即P3.2管脚若感觉到电平从高电平变化到低电平那一刹那,单片机就产生一个中断响应,去处理响应的中断函数。若IT0=0,代表“低电平”产生中断,即P3.2管脚若感觉到电平状态为低电平时,单片机就会产生中断,直到电平变高,中断才会停止。 IE0:中断标志位。它平时为0,单片机不允许你用软件编程方式让它置1,但若INT0产生了一个中断源(来了一个下降沿或低电平),则IE0被单片机自动置1,响应中断后由硬件清0,当然IE0也可以通过查询方式来编程,但此时需要软件清0了。需要说明的是并不是所有单片机的所有中断标志位在响应中断函数后都能被硬件自动清0,有的确实需要你来手动编程清0,要不然单片机只要看到标志位是1就会一直产生中断,所以好的编程习惯是,进入中断函数时一定别忘了将中断标志位清0。再强调一下,只要IE0=1,则不管你P3.2管脚如何变化、是啥样电平,中断一直会进行下去,可以说IE0置1是INT0产生中断的根本原因。 EX0:INT0中断允许位。作用就类似于一个中断小阀门,EX0=1时,才能进中断,它可以通过软件编程来置1或者清0; EA:51单片机总中断允许未。作用类似于中断总阀门,只有让EA=1时,51单片机的所有中断才能启动。 PX0:优先级。可以自己设置中断优先级,通常情况下默认即可。     以INT0为例配合下面的电路图我们来说明一下外部中断该如何编程,其他中断源的编程思想与之相似。       如图所示P3.2口接在按键S2上(原理图中凡是有同样标注的地方都代表它们连接在一起),按键没按下时P3.2电平为高电平,按键按下时P3.2为低电平,由于按键上加了一个电容来滤除抖动,所以下降沿不是很明显,我们这里选用低电平进入中断的方式,即令IT0=0;另外需要说明一点,中断函数是独立于主函数之外的一个函数,它的写法很讲究,一般格式是这样 void  function_name(void)interrupt 0 {     /*里面写中断函数主程序*/ }     首先前面是固定的void,紧接着是中断函数名字function_name,可以随便起,然后后面是关键字interrupt 最后跟 着一个中断号,中断号是中断类型标志。 interrupt 0  指明是外部中断0; interrupt 1  指明是定时器中断0;   interrupt 2  指明是外部中断1;  interrupt 3  指明是定时器中断1;  interrupt 4  指明是串行口中断; 根据要求,编写程序如下所示: #include #define uint unsigned int sbit D1=P2^0; /******************************************************** **函数名: delay(uint x) **返回:无 **函数功能描述:延时函数,延时大概x毫秒 **********************************************************/ void delay(uint x) //延时函数。 { uinti,j; for(i=0;i<=x;i++) for(j=0;j<=100;j++); } /******************************************************** **函数名:main(void) **返回:无 **函数功能描述:主函数,验证按键中断控制灯的亮灭过程。 **********************************************************/ void main(void) //程序开始运行 { D1=1; //程序初始化,令二极管不发光 IT0=0; //选择低电平触发方式 EX0=1; //打开外部中断0的中断开关 EA=1; //打开全局中断开关 while(1) //循环检测 { delay(1000); //空跑 } } /******************************************************** **函数名: INT_0(void) **返回:无 **函数功能描述:INT0外部中断控制灯的亮灭的中断函数。 **********************************************************/ void INT_0(void)interrupt 0 { EX0=0; //进中断函数后先关中断,养成这个习惯 IE0=0; //虽然IE0由硬件清0,但是为了养成好习惯,这里最好手写一下 delay(100); //延时一会,越过按键的低电平区,具体延时多长时间还真得多试试 D1=~D1; //改变D1发光状态 EX0=1; //出中断函数前再打开中断,养成这个习惯 }

    写到这,一个外部中断函数就完成了。运行思路是这样的,程序从main()主函数开始,进入主程序while(1)中开始无限循环的空跑,此时若P3.2管脚感觉到低电平时,程序立刻蹦到INT_0(void)中断函数中,处理完中断函数后,程序又跳回主程序继续从刚刚的位置继续运行。所有单片机的中断编程思想都与之类似,下文要提到的定时器和串口通信都可以用中断思想来完成相应的功能。     上面的内容也许不是那么好理解,没关系,多看两遍,一般学单片机都要学个三四遍才能小有造诣。不过话说回来,如果您早就知道中断是啥意思了,那上文书就可以略过不看哈。