中断标志位在TCON和SCON中。【必须记住】但在下列任何一种情况存在时,中断响应会被阻止:(1)CPU正在执行同级或高一级的中断服务程序;(2)现行机器周期不是正在执行指令的最后一个机器周期,即现行指令完成前,不响应任何中断请求;(3)当前正在执行的是中断返回指令或访问专用寄存器IE或IP的指令。也就是说,在执行中断返回指令或是访问IE、IP的指令后,至少需要再执行一条其他指令,才会响应中断请求。中断查询在每个机器周期都要重复执行。如果CPU响应中断的基本条件已满足,但由于上述三个封锁条件之一而未被及时响应,待封锁中断的条件撤消后,若中断标志也已消失,则本次被拖延的中断申请就不会再被响应。各中断源的中断服务程序入口地址见表5-3。表5-3中断服务程序入口地址中断源入口地址外部中断0(IE0)03H定时器T0溢出中断(TF0)0BH外部中断1(IE1)13H定时器T1溢出中断(TF1)1BH串行口中断(RI/TI)23H两个中断入口间只相隔8字节,难以安放一个完整的中断服务程序。因此,通常在中断入口地址处放置一条无条件转移指令,使程序执行转向中断服务程序入口。中断服务与返回在用C语言编程时,中断处理程序中的现场保护是由集成开发环境(IDE)自动完成的,返回指令也是编译系统自动处理的,用C语言编程时只要注意其书写格式即可。5.3.2外部中断的响应时间使用外部中断时,需考虑从外部中断请求到转向中断入口地址所需的时间。外部中断的最短响应时间为3个机器周期。其中中断请求标志位查询占1个机器周期,而这个机器周期恰好处于指令的最后一个机器周期。在这个机器周期结束后,中断即被响应,CPU接着执行一条硬件子程序调用指令LCALL到相中断服务程序入口,需要2个机器周期。外部中断响应的最长时间为8个机器周期。在CPU进行中断标志查询时,刚好才开始执行RETI或访问IE或IP的指令,需执行完指令再继续执行一条指令后,才响应中断。执行RETI或访问IE或IP的指令,最长需要2个机器周期。接着再执行一条指令,最长指令(乘法指令MUL和除法指令DIV)来算,也只有4个机器周期。再加上硬件子程序调用指令LCALL的执行,需要2个机器周期,所以,外部中断响应的最长时间为8个机器周期。如果已经在处理同级或更高级中断,外部中断请求的响应时间取决于正在执行的中断服务程序的处理时间,这种情况下,响应时间就无法计算了。这样,在一个单一中断的系统里,80C51单片机对外部中断请求的响应时间总是在3~8个机器周期之间。5.3.3跳沿触发方式外部中断申请触发器能锁存外部中断输入线上的负跳变。即使不能响应,中断请求标志不丢失。相继连续两次采样,一个机器周期为高,下一个机器周期采样为低,则中断申请触发器置1,直到CPU响应此中断时,才清0。输入的负脉冲宽度至少保持12个时钟周期,才能被采样到。适合于以负脉冲形式输入的外部中断请求。【4、中断响应时间【何立民P128】中断响应时间是指从中断请求产生到CPU转向中断服务程序入口处所花费的时间。CPU不是在任何情况下对中断请求都予以响应,且不同的情况对中断响应的时间也是不一样的。下面以外部中断为例,说明中断响应的时间。外部中断INT0和INT1的电平在每个机器周期的S5P2期间,经反相后锁存到IE0和IE1标志位,CPU在下一个机器周期才会查询到新置入的IE0和IE1。如果这时满足中断响应条件,则CPU响应中断(即执行一条由硬件生成的LCALL指令),转入中断服务程序入口。由于执行LCALL指令要花费2个机器周期,因此,从外部中断请求有效到开始执行中断服务程序的第一条指令,中间要隔3个完整的机器周期,这是最短的响应时间。若中断响应条件得不到满足,则需要更长的中断响应时间。可分为以下3种情况:(1)若现行查询周期不是现行指令最后一个机器周期,那么增加的等待时间不会超过3个周期,因为一条指令的最长时间为4个周期(乘法和除法指令)。(2)若当前指令为RETI或访问IE或IP指令,执行该类指令占一个机器周期,其后需再执行一条指令(最多占4个机器周期),才能响应中断请求,则增加的等待时间不会超过5个周期。在上述两种情况下,中断响应时间在3-8个机器周期之间。(3)若当前正在处理同级或更高级中断,则额外等待时间取决于所执行的中断服务程序的长短。】5.3.4中断请求的撤销【P102】某个中断请求被响应后,就存在着一个中断请求的撤销问题。CPU响应中断请求,转向中断服务程序执行,在其执行中断返回之前【在C51中,就是中断服务程序返回】,中断请求信号必须撤除,否则将再一次引起中断而出错。中断请求撤除的方式有三种:(1)由单片机内部硬件自动复位的有:对于定时器/计数器T0、T1的溢出中断和采用跳变方式触发的外部中断请求,在CPU响应中断后,由内部硬件自动复位中断标志TF0、TF1、IE0和IE1,而实现自动撤除中断请求。(2)需用软件清除相应标志的有:【用查询方式时,定时器T0、T1的TF0和TF1也需要用软件来清零;】响应串行口的中断后,CPU无法知道是接收中断还是发送中断,还需测试这两个中断标志位,以判定是接收操作还是发送操作,然后才清除。所以串行口中断请求的撤销只能使用软件的方法,即用如下指令在中断服务程序中对串行口中断标志位进行清除:TI=0;//清TI标志位【在reg51.h中,已经定义】RI=0;//清RI标志位(3)既无硬件也无软件措施的:对于采用电平触发方式的外部中断请求,CPU对0INT、1INT引脚上的中断请求信号无控制能力,IE0或IE1受外部引脚中断信号(0INT、1INT)直接控制,CPU无法控制IE0或IE1,也无应答信号。为保证在CPU响应中断后、中断服务程序返回前,要撤除中断请求,需要另外考虑撤除中断请求信号的方法,一般通过外加硬件电路,并配合软件来解决,如图5-8所示。图5-8电平方式的外部中断请求的撤销电路由图5-8,用D触发器锁存外来的中断请求低电平,并通过D触发器的输出端Q接到0INT(或1INT)。所以,增加的D触发器不影响中断请求。中断响应后,利用D触发器的SD端接80C51的P1.0端。因此,只要P1.0端输出一个负脉冲就可以使D触发器置“1”,撤销低电平的中断请求信号。5.4中断服务子程序的设计中断系统的运行必须与中断服务子程序配合才能正确使用。设计中断服务子程序需要首先明确以下几个问题。1.中断服务子程序设计的任务任务有下列4条:(1)设置中断允许控制寄存器IE,允许相应的中断请求源中断。(2)设置中断优先级寄存器IP,确定所使用的中断源的优先级。(3)若是外部中断源,还要设置中断请求的触发方式决定采用电平触发方式还是跳沿触发方式。【IT0和IT1】(4)编写中断服务子程序,处理中断请求。前3条必须放在主程序的初始化程序段中。(2)关中断和开中断现场保护前和现场恢复前关中断,是为防止此时有高一级的中断进入,避免现场被破坏。在现场保护和现场恢复之后的开中断是为下一次的中断做好准备,也为了允许有更高级的中断进入。这样,中断处理可以被打断,但原来的现场保护和现场恢复不允许更改,除了现场保护和现场恢复的片刻外,仍然保持着中断嵌套的功能。但有时候,一个重要的中断,必须执行完毕,不允许被其他的中断嵌套。可在相应的中断服务程序中,先关闭总中断开关位EA,待中断处理完毕后再开总中断开关位EA。5.5中断的编程和应用举例8051的中断系统十分重要,C51能够很容易地用C语言来声明中断和编写中断服务程序。中断过程通过使用interrupt关键字和中断号(0到31,实际只有0~4有用,其他保留)来实现。中断号告诉编译器中断程序的入口地址,这种关系示于表5-4。表5-4中断号与中断源的对应关系C51的中断号01234对应的中断源外部中断0定时器0溢出外部中断1定时器1溢出串行口中断在编制中断服务程序时,可以指定所使用的寄存器组。方法是使用关键字using,后跟一个从0到3的数,对应着4组工作寄存器。当指定工作寄存器组的时候,默认的工作寄存器组就不会被推入堆栈,这将节省一些处理周期,因为入栈和出栈都需要2个处理周期。中断服务程序的格式如下:返回值函数名(参数)interruptnusingm其中,n是中断号,标准中断的对应关系见表5-4;m是所使用的寄存器组。下面列举2个中断系统应用的实例。【例1】外部中断的使用外部中断一般采用下降沿的触发方式,而且0INT为同级中的最高优先权,在实际应用中,对需要及时做出反应或异常处理的情况,可以考虑用外部中断方式。设有如下图所示电路,要求编制中断服务程序,实现以下功能:按下按键一次,则LED指示灯闪烁一次。程序如下:#includereg51.hunsignedcharj;sbitP10=P1^0;voiddelay(unsignedintcount)//延时函数{unsignedchari;while(count--)for(i=0;i120;i++)//在时钟为12MHz时,循环120次,约为1ms}voidmain(void){IT0=1;//设置INT0为下降沿触发方式EX0=1;//IE^0,开INT0中断,允许INT0中断申请EA=1;//IE^7(EA=1)开所有中断P10=1;//熄灭LEDwhile(1);}voidint0func(void)interrupt0using3//中断处理函数{EX0=0;//关INT0中断P10=0;//点亮LEDdelay(500);//延时500msP10=1;//熄灭LEDdelay(500);//延时500msEX0=1;//开INT0中断}【例2】在单片机系统的P2口上接有8个LED,LED的阳极接+5V,阴极接P2口的引脚。在外部中断0输入脚P3.2(0INT)引脚接上拉电阻并接有一只按钮开关K1。用K1按钮来产生外部中断0的输入信号。要求将外部中断0设为负跳沿触发。在程序刚启动时,P2口上的8只LED亮。按一次按钮开关K1,让低4位LED和高4位LED间隔1s交替闪烁一次。【已经调通CAP_EX2】图5-X1控制8只LED交替闪烁的电路#includereg51.hvoidmain()//主函数{EA=1;//总中断允许EX0=1;//外部中断0中断允许IT0=1;//选择外部中断0为负跳沿触发方式P2=0;//P1口的8个LED全亮While(1);//循环等待,在本例等待中断的发生}voidint0()interrupt0using0//外中断0的中断服务函数{EX0=0;//禁止外部中断0中断,(再按开关也不会引起中断)P2=0x0f;//低4位LED灭,高4位LED亮delay(1000);//延时1sP2=0xf0;//低4位LED亮,高4位LED灭delay(1000);//延时1sEX0=1;//中断返回前,开放外部中断0,允许其中断}voiddelay(unsignedintcount){unsignedchari;while(count--)for(i=0;i120;i++);//在时钟为12MHz时,循环120次,约为1ms}【实例3】中断扫描法行列式键盘【P105】#includereg51.hcharled_mod[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//大Ccharkey_buf[]={0xee,0xde,0xbe,0x7e,0xed,0xdd,0xbd,0x7d,0xeb,0xdb,0xbb,0x7b,0xe7,0xd7,0xb7,0x77};voidgetkey()interrupt0{charkey_scan[]={0xef,0xdf,0xbf,0x7f};chari=0;charj=0;for(i=0;i4;i++){P1=key_scan[i];for(j=0;j16;j++){if(key_buf[j]==P1){P2=le