什么是中断?
中断的作用只有一个,那就是打断单片机中别的程序,先执行自身的程序,执行完之后再让单片机继续执行原来的程序。
一般分为几个步骤:中断请求-》记录现在的状态-》中断响应-》中断返回,恢复之前的状态。
用最简单的话来说,就是你在干一件事,在这期间突然电话响了,这时你放下你手中的活去接电话,接完之后你又回去干你之前干的活。上述的这个事例中,电话响了可以看作是中断请求,放下手中的活是记录现在的状态,去接电话是中断响应,接完电话之后你又回去干你之前的活可视为中断返回,具有这些特性的事件称之为中断(其过程如图)。
STC89C52RC单片机内部有个中央处理器,即CPU,它每次只能处理一个事件,想像一下,如果单片机的主函数(main函数)中有一段接收数据的函数时,这时就必须用while语句来判断是否接收到数据,我们都知道,只要while语句的判断条件不成立,程序将会在while语句中不断循环,这样程序就不会执行下去了,CPU也会处于一个等待的过程,这样CPU就得不到有效的利用,而中断却能解决这个问题,只要将接收数据的函数写在中断服务函数(下面会介绍),当单片机接收到数据的时候,就会立马跳到中断服务函数进行数据的接收,这样就不会出现CPU等待的过程,从而提高CPU的利用率。
单片机的中断系统就是单片机的核心,很多非常重要的功能都离不开中断系统的支持。说了这么久中断系统,那么51单片机中断系统的中断源(也就是触发中断的条件)都有什么呢?
单片机的中断源种类繁多,有外部中断0,在单片机中的字母代号就是,外部中断1就是INT0,定时器0就是T0,定时器1即T1,还有串口中断,ADC中断等等数不胜数。中断的优先级如上图所示,从上到下为外部中断,定时器0,外部中断1,定时器1,串行口。
常见使用的就是外部中断源和定时器中断源。
观察下面的中断结构图,这张图涵盖了外部中断和定时器中断等中断源的核心部分
以INT0为例,若要使硬件查询能够查询到的中断触发源,那么就需要将IT0配置成0/1,1代表下降沿触发中断源。0代表低电平触发外部中断。然后将EX0和EA置一,最后再将PX0处设置成0/1。PX0为0时,代表这个外部中断的优先级为低优先级。如果PX0为1时,代表这个外部中断的优先级为高优先级。只有将这些全部都配置好之后,外部中断信号才能从INT0这里进入,在硬件通过查询得到。但凡有一个寄存器没有配置正确,使用时就会出现没有现象的情况出现。
在上一页的图中,IT0和IT1上电时默认就是低电平触发状态,IP寄存器默认就是所有的中断源都是低优先级。一般在写程序时,我们默认不修改IP寄存器。而IT0和IT1则是看情况来配置1或者0。注意,无论打开哪一个中断,都必须将IE寄存器中断和该中断相关的位全部置1。否则该中断无法正常工作。
下降沿触发和低电平触发的使用和区别:
下降沿:信号从高电平向低电平转变的过程,可以称为信号的下降沿。
上升沿:信号从低电平向高电平转变的过程,可以称为信号的上降沿。
高电平:信号为“1”时,称为高电平。
低电平:信号为“0”时,称为低电平。
现在市场上有很多数字型模块,比如红外模块,比如声敏模块等等,他们在识别到物品或者声音响度达到一定值时,就会被触发,进而输出低电平。这种情况下,我们一般选择使用单片机的外部中断,模式设置为低电平触发,高电平则是关闭中断不触发。
另外按键的使用,一般是写在主函数里面,通过不断的进行扫描判断,但是这样极大的占用了单片机的CPU资源,所以大部分时候都是将按键连接在外部中断触发引脚上,将模式配置为下降沿触发模式,检测到外部中断的引脚从高电平被拉低为低电平时,触发中断。
51单片机中断的使用及优先级设置
51单片机有两个外部中断,两个定时器/计数器,两个外部中断分别是int0,int1。定时器/计数器分别是t0,t1,还有一个串口中断TI/RI,加起来有五个中断。它们在硬件上的排列顺序是INT0,T0,INT1,T1,TI/RI,这5个中断源的中断顺序号依次就是interrupt 后面的0,1,2,3,4。其中定时器可以选择工作方式,因为我们使用定时器的方式不一而足,有的程序会用来计时,有的程序用来计数,根据程序要求设置工作方式各取所需。
下图显示了51单片机中的4个寄存器,分别是TCON,SCON,IE,IP,还有我们常用的定时器模式控制寄存器TMOD。
一、TMOD:定时器/计数器工作方式控制寄存器
TMOD中存放两个定时器/计数器,每个定时器都可以设置它们的工作方式,如定时器0的工作方式1设置就是TMOD=0x01,定时器1设置方法同定时器0,不过设置位数变到前4位TMOD=0x10。如果两个都用方式1的话就是TMOD=0x11了吧。
二、IP:优先级寄存器
IP寄存器是控制中断优先级的寄存器,在51单片机中可以设置中断为高优先级或低优先级,以达到嵌套的目的。想要嵌套中断,必须使用到我们前面说过一嘴的IP寄存器,否则int0的优先级只是查询优先级最高,而不是中断优先级最高。IP寄存器结构如下图,通过设置IP寄存器可以设定由哪个中断最先运行,51单片机可以设置二级中断服务嵌套。通过IP寄存器结构我们可以明显看到下面5个中断所处的位置,因此可以轻易的设置想要的中断优先级,例如设置PX0,即外部中断int0优先级最高,就是IP=0x01;或者PX0=1;效果相同。举一反三,其他中断也是如此设置。
一般情况下我们什么时候要用到中断?
举个简单的例子:
假如我写了一个8个灯的流水灯程序,从头开始依次亮灭。但现在我想要让通过按键来让它停下来,即中断它的流水效果,当我按下键的时候,它停止流动,当我松开按键时,它继续流动,这里的按键按下对流水灯来说就是一个中断。
实现中断的步骤:
先配置外部中断0,即中断初始化:
void init_inter0() // 配置和启动外部中断0,即INT0
{
IT0 = 0; // 选择低电平触发
EX0 = 1; // 启用外部中断0
EA = 1; // 启用中断,EA相当于所有中断的总开关
}
设置外部中断0预设的程序:
void ex0_inter() interrupt 0 // interrupt 0表示中断优先级为0,即第一(C语言中0的排序最大)
{
while(P32 == 0 && P34==0); // 当KEY9和S1键同时按下时,中断流水灯。
}
中断服务函数的固定格式:
void XXXX(中断源名称随便取) interrupt X(X表示中断优先等级:0,1,2,3......)
{
写入你想要让主程序中断后做的事情
}
流水灯按键中断程序:
#include <STC89C5xRC.H>
void delay(unsigned char xms) //延迟函数 ms
{
char j;
for ( ; xms > 0 ; xms-- )
for( j = 110 ; j > 0 ; j-- );
}
void init_inter0 () //中断初始化: 配置和启动外部中断0,即INT0
{
IT0 = 0; // 低电平触发
EX0 = 1; // 启用外部中断0
EA = 1; // 启用总中断,EA相当于所有中断的总开关
}
void ex0_inter() interrupt 0 //设置外部中断0预设的程序, interrup表示//中断优先级为0,即第一优先级
{
while(P32 == 0 && P34==0); // 当KEY9和S1键同时按下时,中断流水灯,直到按键抬起
}
void main()
{
P1 = 0x01; // 初始化为00000001
init_inter0(); // 初始化外部中断零INT0
while(1) // 让程序死循环,不断重复执行{ }里的程序
{
P1 = (P1<<1)|(P1>>7); //实现从头开始依次亮灭的流水灯功能
delay(500);
}