缘缘学单片—经验手记—11、中断,想说爱你并不是很容易的事(定时器/计数器)

  • 缘缘
  • LV3工程师
  • |      2013-07-21 21:19:13
  • 浏览量 20529
  • 回复:225

      通过上一节,我们知道了中断是怎么回事,懂得了如何去操作外部中断了,那么,这节缘缘就给你说说中断中最头疼的一个东西——定时器/计数器,又称特殊功能寄存器,是不是在别人的程序中经常看到类似这样的程序,

那么它们是什么意思呢,在弄懂这个之前,我们就在当今中国房价居高不下的时代里,拿房子说说事吧:

通过这们的情况,我们可以得出下面这种关系:

假设我们造了一个这样的小区,这个小区有高低两个区域,其中一个是用大房子做的,称为高级区,只有10个房子(房价贵,地皮更贵),一个是小房子做的,也有10个房子:

那么,这么些房子有多少个空间,究竟能住多少人呢?

通过上面的说明,我们得知,其实这个小区也就是个能住10×10=100人的房子,我们装每个房子定义成一个空间,那么就是它最大就是100个空间了,那么这些人是如何住进这个房子(空间)的,又有何种规定呢?

通过这个,我们知道了这么一种情况,我们要让房子腾空一次,就必须要住100人,那么,我们把全部房子腾空做为一个参考事件,假设让这些房子全部腾空时,全都来听缘缘讲故事,目前我们要得到房子腾空一次,那就得让100个人去住,如果有63个就腾空一次该怎么办呢:


       我们得知了,在定义的情况下,就产生一个腾房子的事件,如果我们把这个腾房子记成一个时间,即一个人住一次就是1分钟,那么刚才的63个人住了并腾出来就是63分钟了,如果我们的这些房子是一个特殊的存储空间,那么怎么去算或者如何去定义时间呢:  

假设这是个只能装100个数的存存储空间,当数值超过100时会产生一个中断响应,并将空间置空。

10个数的空间为一个,有两个位,高位和低位,高位装的是低位的全部,低位装的是单个的数,假设低位每一位装的是1,那么高位的一位就装的是10,如果把数据装满,便如下表一样:

 

这里有个问题,就是装数是必须从低位依次向高位装,而且顺序是不能变的,其*面的数字代表的是占用的空间情况,不存在其数值意义,也就是说10也占用着一个空间,1也占用着一个空间,其代表着一个空间,但实际上,每个位都会空着一个空间,最多只能装99个数,即高位9个,低位9个,这时,只要再装进来一个数,低位的空间就会先装满,然后全部溢出去,再把高位的全部填满,高位的也溢出去,这样就会产生一次中断。

如果我们只要装63个数就要产生一次响应,该怎么做呢?

解答如下:因为这个空间必须要装满100个才能产生一次响应,那么我们就要在装这63个数之前把其它的空间要装上,不让其空着,那么装63个数就会产生一次中断响应,之前要装多少数呢?答案就是100-63=37,于是我们就先要装37个数的空间,然后再进行63个数空间的计算,那么这63个数怎么装呢,我们就先对这100个数的空间的高位先装,因为这100个数的空间的空间是由两个10位的高低空间组成的,那么装数时,我们对要装的数进行求整,装入高位,把剩下的(也称求余),装到低位里面,其装法如下(以下有颜色的代表是装了数的空间,没有颜色的是空着的空间):

我们要一个装63个数的中断响应,那么我们要装的数就是100-63=37了;

算出了这个,该怎么装呢,我们先对它的高位进行装数,因位高位装的数都是10低位的倍数,只有低位有10个,所以装数的公式为:(100-63/10=3.7,对3.7取整,就是取小数点前的那个计算的值,取整就是3,因为我们是以10为基数算的,因为高位最前面的那个空间满了会直接溢出去产生中断,所以在装数时空着的,我们就与那个挨着,把这3个高位(每位是低位的全部,即为10)装满,如下表所示:

 我们将剩下的数怎么装呢,因为是10为基数算的,那么剩下的0.7要乘上10就是我们要装的数了,0.7*10=7,与高位挨上,装在低位上:

 

我们看到,有问号的红色空间是空着的,在高位有6个空着的,那么就是6×10=60了,低位有3个空着的,总共就是63个空间了。

那么对它如何进行装数运算,其进行方式如下表所示:

我们先从它的低位的最后一位向高位装数: 

到这里是低位的空间满了,它会溢出去,低位就全部腾空了,把溢出去的这10个数装在高位的一个空间上:

 

 

 当高位溢出时,就会产生中断响应了,因这这个空间只能装100个数,在运算前我们就把37个空间填满了,所以只在装63次数时就会产生一个中断响应,这达到了我们要装63个数就产生一次中断响应的要求。

我们做到了用63个数产生一次中断响应的要求了,那么我们要以630个数产生一次中断响应该怎么办呢?

前面说过,当产生一次中断响应时,数据空间会清空,继续装入初值,再进行63个中断的过程,依次循环,那么,我们让这个循环进行10次,那不就是我们要得到的63个数的中断了吗,这该怎么做呢,我们引入一个数记这个中断,当这个中断产生时,就会计数一次,其基本结构如下:

 

那么在程序中该如何表达呢,如下所示:

这时在定时器函数里面:

高位=100-63/10前面已述

低位=100-63%10%是求余的意思;

M++这是引入的一个值;

在主函数中只做个判断就行了:

If(M==10)因为我们要用的是630个数的中断,所以当M=10时,正好就是63*10=630个数的中断。

M=0    意思是只要10次够了就置0重新计数,要不然就一直加,

这样就失去了630个数产生一次中断的效果了。

********这里写上处理的事件,如灯亮了,开关打开了等处理事件

看了是不是很简单,明白了这个,那么我们回过头来看看这是开头的这个是怎么回事,先来看看我们的定时器/计数器吧是个啥概念吧:

    以下所有时间的计算为了方便,用12M的晶震计算,其它的计算方法与此一样。        

    我们知道,单片机在处理一个事件是会占用一个机器周期,即在寄存器中装入一个数时,它就会占用一个机器周期,通过上图我们得知,装满这个寄存器时,需要装65536个数值,这样就会占用65536个机器周期。

在前面说过,一个机器周期是晶震的12分频,如果晶震是12MHZ,那么一个机器周期就是12*1/12=1uS,那么65536个机器周期就是65536uS=65.536mS=0.065536S。

现在我们得知了寄存器全部装满时需要的时间了,当寄存器装满时,就会产生一个中断响应,这个响应是我们在程序中定义的处理事件。

但是,我们一般不会以65536这样的时间去定时,因为这样定时不好算,比如我们要定一秒钟,结果是1÷0.065536=15.2587(次)了,这样就产生了小数,对于定时时,不太精确了,假设是我们定时1秒采取20次计算是多少呢,就是1÷20=0.05S了,把这个换算成US就是50000了,那么我们的定时器是如何做到的呢:   

特殊寄存器

TH0

TL0


因为通过前面的图介绍,我们知道T1T0都有两个8位的特殊寄存器,共计16位,分为高位TH0与低位TL0,我们就以T0做说明,T1与其相同:

低位是8位,用二进制制表示装的数,如果8位全部装满就是1111 1111,那换算成16进制就是0xff,这个数换算成10进制等于多少呢,就是255,但是这个空间还少一个,如果是256才会向上溢,所以记为256

现在问题来了,因为在这个存储器的定义上,高位的一个数是低位的全部总和,即把高位的一个数装一次就是256个低位数,如果把高位的全部装满应该是多少呢,那就是256*256,这个值是多少呢,算下后即为65536,也就是说当T0中的这两个寄存器装65536个数时,T0会发生一次中断,那么T0是怎么定义时间的呢?

我们知道,一个机器周期是是12个时钟周期,那么机器周期就是晶震的12分之1了,如果以12M的晶震算的话,那么单片机的时钟周期就是12M*1/12=1US(微秒),T0中的寄存器每装入一个数的时间就是一个机器周期,即1US,那么把T0的寄存器全部装满就是65536US,如果换算成MS(毫秒)就是65536US/100065.536MS,如果换算成秒的话就是65.536MS10000.065536 S(秒)

假设,我们要定时50mS,应该怎么办呢,很简单,我们让65536先把50000(我们要定时50MS的数)减去,将减去的值先装入寄存器,装值时我们要对高位进行256的求模(整)操作,(为什么是256呢,因为这个低位是256的存储空间,高位的一个数就是低位的所有数,所以高位装的全是低位的倍数,)把剩下的数装到低位里面,那么这个数倒底是怎么装的呢:

首先,我们让65536先把要定时的这50000减去,就变成了15536了。

接下来,我们给高位装,因为高位是256的倍数,所以,这里就成了15536÷256=60.6875,我们对这个数先取整,就是60了,即我们在T0的高位的256个空间装了60个空间(每个空间包含256个数)。

再接着,我们对60.6875取余,就是0.6875了,我们是以256为基数取余的,所以在装低位时,我们用这个余数与256相乘,即为0.6875×256=176,这个数正好能装在256的低位空间中。

所以,经过上面的过程,我们对T0空间装数的情况是:

高位:60,低位:176,那么,我们这样装合适吗?

我们反验证一下,就是看高位与低位的数加到一起是多少:

算法是高位×256+低位,即为60×256+176=15360+176=15536,那么我们让65536把这个数减去,看是不是50000了,不用算就能看出来了,就是50000了,这样,我们先给T0用上述方法装15536个数,把空间先占上,然后对剩下的空间进行50000的填数而产生中断响应操作,即可得到50MS的中断,算过了50000US一次的中断,那么我们再来算算10000US是怎么装初值的:

高位:(65536-10000)/256,即为55536÷256=216.9375,取整后即为216个空间;

低位:(65536-10000)%256,即为55536÷256=216.9375,取余后为0.9375,我们让这个数与256相乘,即0.9375*256=240,也正好装下了。

我们再来反验证一下,看是不是能达到10000US的定时要求:

高位×256+低位,即为256×216+240=55296+240=55536,65536-55536=10000US,所以算法是没有错的,那么,这个初值如何在程序中表现呢,为了方便说明,仍以50000US为例说明:

void timer0( ) interrupt 1

{

    TH0=(65536-50000)/256;

    TL0=(65536-50000)%256;

  ***********这里写上中断时产生的响应就行了。

 }

我们知道了一个50MS的中断是怎么产生的,那么我要定时1秒该怎么办呢,我们就引入一个值,让它计算这个中断多少次会达到1秒这个时间,通过S(秒)与MS(毫秒)的换算我们得知,1S=1000MS,那么1S的中断就是1000/50=20(),这时我们引入一个值来让它计算这个中断的次数,如果到20次了(可以定义多个计数中断的数,以完成各种不同的任务),就会产生中断响应,下面是完整程序,晶震为12M的,可以充分地说明这一点:

#include < reg52.h >

sbit LED=P1^5;//接继电器、LED或蜂鸣器

sbit teep=P1^6;//接继电器、LED或蜂鸣器

int i=0,j=0;//定义计算中断次数的数

void timer0() interrupt 1 ////定时50MS的中断函数

{

   TH0=(65536-50000)/256;

   TL0=(65536-50000)%256;

 i++;

 j++;

 }

 void main()

{

 TMOD = 0x01;//定时器的工作模式

 TH0  =(65536-50000)/256;

 TL0  =(65536-50000)%256;

 EA=1;

 ET0=1;

 TR0=1;

 TR1=0;

      while(1)

     {

        if(j==20)//1秒的时间(50MS*20=1000MS=1S

        {

         j=0;

         LED=!LED;//1秒亮1秒灭循环           

        }

        if(i==40)//2秒的时间(50MS*40=2000MS=2S

        {

        i=0;

        teep=!teep;//2秒开2秒关循环 

        }

 }  

 }

    怎么样,是不是很简单,现在看到开头的那种程序还觉得难吗?当然也有写不一样的,它们是把这个10进制数换算成十六进制赋值的,但进无论如何,其原理还是一样的。

    以上就是对定时器的说明,计数器的原理是一样的,只不过把定时当计数使用罢了。 




以上就是对定时器的啰嗦解释,如果不啰嗦,这块很抽象,很难理解,所以啰嗦点,希望与我一样有困难的朋友一个帮助,对于中断定时,一定要先算好一个机器周期的时间,才能做好对定时器的定时操作,如果机器周期没算合适,那么肯定定出来的时间不准,时间怎么算,就是晶震/12就行了,如11.0592M的,他的机器周期就是11.0592/12=0.9216,那么定时50MS就是概是(50+50-50*0.9216))*0.9216=49.9999999MS,所以算这个,如果是低于12就用加的,如是果高于12就用减的(红色标记处)。

本片篇只是将学习过程中的经验汇总分享,由于本人才学孰,还望各位发现问题后及时给予指正。


 




  • 0
  • 收藏
  • 举报
  • 分享
我来回复

登录后可评论,请 登录注册

所有回答 数量:200
越良机器人 2017-03-19
通俗易懂 形象生动 谢谢你如此细心的讲解,给我们打开了入门的大门
0   回复
举报
发布
2016大圣 2017-02-23
楼主:学习了,谢谢!
0   回复
举报
发布
heaton426 2017-01-04
缘缘这个教程真不错,可以合集弄一本书啊
0   回复
举报
发布
消防君 2016-12-29
很好,非常好
0   回复
举报
发布
dadaxiu 2016-12-22
讲的很不错很好
0   回复
举报
发布
石头 2016-12-22
讲的太棒了,我自己都可以独立开发了
0   回复
举报
发布
艾笠调查团 2016-04-21
好理解!赞一个
0   回复
举报
发布
443551555@qq.co 2016-04-15
中断很难懂
0   回复
举报
发布
诺诺诺 2015-12-31

0

0   回复
举报
发布
诺诺诺 2015-12-31
怎么不能发了啊?
0   回复
举报
发布
查看更多
x
收藏成功!点击 我的收藏 查看收藏的全部帖子