电子说
前排提示
以下定时器的设置例程是以12MHz频率运行为模板的
STC15F2K60S2单片机内部晶振频率设置方法如下:
什么是中断
首先,我们先来模拟一个情景:
在某天的晚上,你正在为今晚截止上交的作业绞尽脑汁,同时你还有一桶的衣服没洗(嗯,假设是个懒癌,假设)**。但有一个好消息,洗衣机还有5分钟就洗完了上一位同学的衣服,如果你足够及时的话就能够用上洗衣机。因此,你用手机设了一个5分钟后的提醒,时间一到你就暂停做作业,并马上去使用洗衣机洗衣服,完成这项任务后回去继续写作业。
分析一下上面的情景,我们可以得到以下信息:
1、你现在主要的任务是完成作业,而且现在就需要进行;
2、你需要洗你的衣服,但不是现在;
3、在洗衣机前一直等待的话,将阻碍你其他任务的进行;
4、你设置了一个定时器,会提醒你进行另一项任务。
得到这些信息,对于定时器中断我们就很好理解了。
将上面的情景套到单片机中的话:
1、主函数(main)是你现在正在做的事(做作业);
2、中断函数是你另外需要做的事(洗衣服)但不是现在需要做的;
3、主函数需要一直执行,而且无法与其他函数同时执行(只有一个线程);
4、定时器中断可以暂停主函数的执行,并进入中断函数,待中断函数完成后就回到主函数,从暂停的位置继续执行。
如果你理解了什么是中断,那么请接着往下看。
怎样使用中断
使用定时器中断分为两部分:打开定时器中断与编写中断函数,下面以定时器0为例,讲解如何编写和使用。
打开定时器中断
打开定时器中断分为以下三个步骤:
1、打开定时器;
2、设置定时器工作模式;
3、设置定时时长(设置初值)。
下面是设置定时50ms的例程:
void Timer0_Start() //中断设置函数
{
EA = 1;
ET0 = 1;
TMOD |= 0x09;
TR0 = 1;
TH0 = (65536-50000)/256;
TL0 = (65536-50000)%256;
}
我们按上面所隔开的三部分,来逐句分析上面的程序:
EA:中断的总开关,置1使中断可用;
ET0:定时器0的开关,置1使定时器0可用。
我们可以这样理解,EA家里的总电闸,ET0是家里其中一盏电灯的开关,只有打开了EA,ET0才能用;只用ET0打开了,定时器0才能用。
TMOD:定时器共有4种工作模式(模式0~模式3),其中模式1较为常用,具体的设置方法以后更新,本文只讲较常用的模式。
TR0:控制TR0的运行,置1时定时器开始运行(ET0是使定时器0能用,并不代表着定时器0已经开始工作)。
TH0:相当于分,计满(65536)时进入中断;
TL0:相当于秒,计满(65536)时分(TH0)加一。
如果不设置TH0和TL0,则默认从0开始计。
其中,50000为计时时长(50000微妙,即50毫秒)具体的为啥酱紫设置将在下篇细讲。
编写中断函数
中断函数的编写与一般子函数的编写差别不大:
void xxx() interrupt 1 //中断函数
{
TH0 = (65536-50000)/256;
TL0 = (65536-50000)%256;
//balabalabala...
}
函数名同样是可以自定义的,不过中断函数并没有返回值与参数。
函数名后面的interrupt关键字可以看成中断函数的标志,带这东西的就是中断函数。
进入中断后,首先要做的就是将TH0、TL0恢复初值(如果这个中断只执行一次,那么可以忽略这个步骤)。
原来定时的时长已经完成了,如果不重新设定时间,那么就不会再继续定时。
interrupt后面接的数字是中断源的序号:
中间其他数是其他中断的序号。
要注意的是,中断是有优先级的,序号越高则优先级越高。例如,定时器0与定时器1同时发生了中断,则优先执行定时器0的中断函数,执行完后再执行定时器1的中断函数,最后回到主函数。
注意:
上面所使用的ET0、TR0、TH0、TL0中,最后的数字代表着定时器0,
如果使用定时器1,相应的就改成ET1、TR1、TH1、TL1。
写个程序试试看
上面就是定时器中断的设置与使用了,我们来试试用定时器做一个LED闪烁的小程序吧。
例程:
#include < reg52.h >
#define TIME_US 50000 //中断间隔,50000即每隔50ms发生一次中断
#define LED P1 //LED与单片机所连接的引脚
int count = 0; //用于记录进入中断次数的全局变量
void Timer0_Start() //中断设置函数
{
EA = 1;
ET0 = 1;
TMOD |= 0x09;
TR0 = 1;
TH0 = (65536-TIME_US)/256;
TL0 = (65536-TIME_US)%256;
}
void main()
{
Timer0_Start(); //调用中断设置函数,若不调用则与没写没有区别
LED = 0x00; //设置LED的初值
while(1); //死循环,让程序在此停止,否则将一直循环令LED为0x00
}
void Timer0() interrupt 1 //中断函数
{
TH0 = (65536-TIME_US)/256;
TL0 = (65536-TIME_US)%256;
count++; //进入了一次中断,变量count加1
if(count == 20) //如果进入了20次中断,即过了1秒(50ms*20=1s),
{
count = 0; //将count归0,下次中断开始重新计数
LED = ~LED; //将LED进行取反,原来亮的就变灭,原来灭的就变亮
}
}
(程序下载效果,LED亮一秒灭一秒)
在使用中断时,有几个需要注意的地方:
1、中断设置函数( Timer0_Start() )只需要进行一次,若循环进行则相当于一直给TH、TL赋值,TH、TL不能计满溢出则无法进入中断。
2、中断里用来计数的变量(count)不能在中断里进行定义(例如把int count写在中断函数里),因为每次退出中断后里面的局部变量都会被清除,这个变量也就无法用于计数。
使用中断的好处是,你可以在上面主函数的while循环里执行其他程序,且不影响LED闪烁的执行,可以完成多项任务(例如动态数码管与LED闪烁同时工作)。
相比之下,使用软件延时(delay)的话,要完成像上面这样的多任务执行可能就要写比较复杂的程序了,因为在软件延时的期间单片机是无法执行其他程序的。
全部0条评论
快来发表一下你的评论吧 !