用简单明了的方法教你使用定时器中断

电子说

1.3w人已加入

描述

前排提示

以下定时器的设置例程是以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后面接的数字是中断源的序号:

  • 1 为定时器0;*
  • 3 为定时器1;
  • 有的单片机还有定时器2,定时器2的序号为5。*

中间其他数是其他中断的序号。

要注意的是,中断是有优先级的,序号越高则优先级越高。例如,定时器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)的话,要完成像上面这样的多任务执行可能就要写比较复杂的程序了,因为在软件延时的期间单片机是无法执行其他程序的。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分