承接上文,今天我们来学习一下中断服务函数,对中断不了解的朋友可以回顾一下笔者之前的文章
中断系统结构与控制寄存器
中断服务函数是嵌入式系统中用于处理中断事件的函数,在原版的C语言中并不存在。当发生中断事件时,系统将会跳转到相应的中断服务函数来处理该事件。
中断服务函数的调用过程可以参照一般的函数调用,但两者之间也是有区别的:
- 一般函数的调用在程序中是事先安排好的,何时调用中断服务函数事先却无法确定,因为中断的发生是由外部因素决定的,程序中无法事先安排调用语句,因此调用中断服务函数的过程是由硬件自动完成的。换而言之,我们并不需要像之前点亮LED或数码管那样在主程序里补充一条LED_ function()来调用封装函数,中断服务函数在控制寄存器允许的情况下会自动执行。
- 中断函数具有特殊关键字interrupt,这也导致中断函数的定义方法也与普通函数不同。
定义语法如下:函数类型 函数名 (形式参数)[interrupt n] [using m]
函数类型多为void,就笔者目前所学而言,中断函数不需要返回值;函数名自拟,其后的形式参数为空;关键字interrupt表示中断,其后的n对应中断源的编号,5个中断源编号根据优先级从小到大排序,如下表所示
中断编号 | 中断源 |
0 | 外部中断0 |
1 | 定时/计数器0溢出中断 |
2 | 外部中断1 |
3 | 定时/计数器1溢出中断 |
4 | 串行口中断 |
m对应寄存器组号,取值范围是0~3,我们也可以不指定执行中断服务的寄存器组号,由编译器自动分配。
下面我用中断函数来尝试改变LED的点亮状态。
#include <reg51.h>
#include<intrins.h>
#define uchar unsigned charvoid Delay(unsigned int i)
{ unsigned int j;for(;i > 0;i--) for(j=0;j<333;j++);
}void main( )
{ //开始设置中断允许控制寄存器,使用外部中断0EA=1; //开启总中断允许EX0=1; //允许外部中断0中断//IE=0x81; //可以用字节赋值IE代替上述位赋值//开始设置定时/计数器控制寄存器中的触发方式IT0=1; //选择外部中断0为跳沿触发方式while(1) { uchar temp,i;temp=0xfe;for(i=0;i<8;i++) //主函数为库函数流水灯{P1=temp;Delay(400);temp=_crol_(temp,1);}
}
}
//在允许外部中断0中断的情况下,P3.2引脚上检测到低电平,执行外部中断0服务函数
void int0( ) interrupt 0 //外部中断0的中断服务函数,定义时省略了using
{ uchar m;EX0=0; //禁止下一次外部中断0中断信号的输入,防止打断本次中断,可以省略for(m=0;m<2;m++) //LED闪烁{P1=0x0f; Delay(400) ; P1=0xf0; Delay(400); }EX0=1; //执行完毕后,打开外部中断0中断,等待下一次中断请求
}
大家可以看到在仿真软件中有一枚按键与P3.2引脚相连,当按下按键时,引脚便会检测到低电平,执行中断服务函数。
由于引脚只要检测到低电平便会触发中断,因此中断按键无法消抖。
此外,我们在设计单片机时,通常希望触发中断函数时程序执行得越快越好,如报警装置应尽量及时让人们注意到异常状态,因此我们可以简化中断服务函数,将执行主体置于主函数中,中断函数只起到一个修改状态值,以便主函数在检测到该值时及时切换执行程序。
#include <reg51.h>
#include<intrins.h>
#define uchar unsigned char
uchar flag1=0; //加入中断标志
void Delay(unsigned int i)
{ unsigned int j;for(;i > 0;i--) for(j=0;j<333;j++);
}void main( )
{ IE=0x81; IT0=1; while(1)
{ uchar temp,i,m;temp=0xfe;if(flag1==0) //常态下标志位为0,执行主函数{for(i=0;i<8;i++) //主函数为库函数流水灯{P1=temp;Delay(400);temp=_crol_(temp,1);} }if(flag1==1) //触发中断后更改标志位{for(m=0;m<2;m++) //中断函数LED闪烁{P1=0x0f; Delay(400) ; P1=0xf0; Delay(400); } flag1=0; //还原标志位,继续执行主函数}
}
}void int0( ) interrupt 0 //中断服务函数化简
{flag1=1; //只有一个标志位赋值语句
}
不过这样做也存在缺点,中断函数虽然更改了标志位数值,但是两个if函数是平行关系,也就是说只有主函数执行完一次代码块后才能切换为中断服务函数,我在视频中也演示了这两种方法的区别。
外部中断控制LED点亮模式改变
感谢大家,之后会为大家演示多种中断方式的嵌套应用。