51打片机定时器中断分析

来源:互联网 发布:局域网视频网站源码 编辑:程序博客网 时间:2024/06/05 02:56
</pre><p align="left"><strong><span style="color:#333333;">中断的产生背景</span></strong></p><p align="left"><span style="color:#333333;background:#EEEEEE;">请设想这样一个场景:此刻我正在厨房用煤气烧一壶水,而烧开一壶水刚好需要 10 </span>分钟,我是一个主体,烧水是一个目的,而且我只能时时刻刻在这里烧水,因为一旦水开了,溢出来浇灭煤气的话,有可能引发一场灾难。但就在这个时候呢,我又听到了电视里传来《天龙八部》的主题歌,马上就要开演了,我真想夺门而出,去看我最喜欢的电视剧。然而,听到这个水壶发出的“咕嘟”的声音,我清楚:除非等水烧开了,否则我是无法享受我喜欢的电视剧的。<span style="color:#333333;"></span><span style="color:#333333;background:#EEEEEE;">这里边主体只有一个我,而我要做的有两件事情,一个是看电视,一个是烧水,而电视和烧水是两个独立的客体,它们是同时进行的。其中烧水需要 10 </span>分钟,但不需要了解烧水的过程,只需要得到水烧开的这样一个结果就行了,提下水壶和关闭煤气只需要几秒的时间而已。所以我们采取的办法就是:烧水的时候,定上一个闹钟,定时 10 分钟,然后我就可以安心看电视了。当 10 分钟时间到了,闹钟响了,此刻水也烧开了,我就过去把煤气灭掉,然后继续回来看电视就可以了。<span style="color:#333333;"></span><span style="color:#333333;background:#EEEEEE;">这个场景和单片机有什么关系呢?</span><span style="color:#333333;"></span><span style="color:#333333;background:#EEEEEE;">在单片机的程序处理过程中也有很多类似的场景,当单片机正在专心致志的做一件事情(看电视)的时候,总会有一件或者多件紧迫或者不紧迫的事情发生,需要我们去关注,有一些需要我们停下手头的工作去马上去处理(比如水开了),只有处理完了,才能回头继续完成刚才的工作(看电视)。这种情况下单片机的中断系统就该发挥它的强大作用了,合理巧妙的利用中断,不仅可以使我们获得处理突发状况的能力,而且可以使单片机能够“同时”完成多项任务。</span></p><p align="left"><strong><span style="color:#333333;"> </span><span style="color:#333333;">定时器中断的应用</span></strong></p><p align="left"><span style="color:#333333;background:#EEEEEE;">之前我们学过了定时器,而实际上定时器一般用法都是采取中断方式来做的,我是故意在之前用查询法,就是使用 if(TF0==1)</span>这样的语句先用定时器,目的是明确告诉同学们,<span style="color:red;background:#EEEEEE;">定时器和中断不是一回事,定时器是单片机模块的一个资源,确确实实存在的一个模块,而中断,是单片机的一种运行机制。</span><span style="color:#333333;background:#EEEEEE;">尤其是初学者们,很多人会误以为定时器和中断是一个东西,只有定时器才会触发中断,但实际上很多事件都会触发中断的,除了“烧水”,还有“有人按门铃”,“来电话了”等等。</span><span style="color:#333333;"></span><span style="color:#333333;background:#EEEEEE;">标准 51 </span>单片机中控制中断的寄存器有两个,一个是中断使能寄存器,另一个是中断优先级寄存器,这里先介绍中断使能寄存器,如表 6-1 和表 6-2 所示。随着一些增强型 51 单片机的问世,可能会有增加的寄存器,大家理解了我们这里所讲的,其它的通过自己研读数据手册就可以理解明白并且用起来了。<span style="color:#333333;"></span></p><table border="0" cellspacing="0" cellpadding="0" style="background:#EEEEEE;"> <tbody><tr>  <td colspan="9" style="background:#EDEDED;"><p align="center"><span style="color:#333333;">表 6-1 IE</span>——中断使能寄存器的位分配(地址 0xA8、可位寻址)</p></td> </tr> <tr>  <td style="background:#EDEDED;"><p align="center"><strong><span style="color:#444444;">位</span></strong></p></td>  <td><p align="left"><span style="color:#333333;">7</span></p></td>  <td><p align="left"><span style="color:#333333;">6</span></p></td>  <td><p align="left"><span style="color:#333333;">5</span></p></td>  <td><p align="left"><span style="color:#333333;">4</span></p></td>  <td><p align="left"><span style="color:#333333;">3</span></p></td>  <td><p align="left"><span style="color:#333333;">2</span></p></td>  <td><p align="left"><span style="color:#333333;">1</span></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td> </tr> <tr>  <td style="background:#EDEDED;"><p align="center"><strong><span style="color:#444444;">符号</span></strong></p></td>  <td><p align="left"><span style="color:#333333;">EA</span></p></td>  <td><p align="left"><span style="color:#333333;">--</span></p></td>  <td><p align="left"><span style="color:#333333;">ET2</span></p></td>  <td><p align="left"><span style="color:#333333;">ES</span></p></td>  <td><p align="left"><span style="color:#333333;">ET1</span></p></td>  <td><p align="left"><span style="color:#333333;">EX1</span></p></td>  <td><p align="left"><span style="color:#333333;">ET0</span></p></td>  <td><p align="left"><span style="color:#333333;">EX0</span></p></td> </tr> <tr>  <td style="background:#EDEDED;"><p align="center"><strong><span style="color:#444444;">复位值</span></strong></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td>  <td><p align="left"><span style="color:#333333;">--</span></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td>  <td><p align="left"><span style="color:#333333;">0</span></p></td> </tr></tbody></table><p align="left"> </p><table border="0" cellspacing="0" cellpadding="0" style="background:#EEEEEE;"> <tbody><tr>  <td colspan="3" style="background:#EDEDED;"><p align="center"><span style="color:#333333;">表 6-2 IE</span>——中断使能寄存器的位描述</p></td> </tr> <tr>  <td style="background:#EDEDED;"><p align="center"><strong><span style="color:#444444;">位</span></strong></p></td>  <td style="background:#EDEDED;"><p align="center"><strong><span style="color:#444444;">符号</span></strong></p></td>  <td style="background:#EDEDED;"><p align="center"><strong><span style="color:#444444;">描述</span></strong></p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">7</span></p></td>  <td><p align="left"><span style="color:#333333;">EA</span></p></td>  <td><p align="left"><span style="color:#333333;">总中断使能位,相当于总开关</span></p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">6</span></p></td>  <td><p align="left"><span style="color:#333333;">--</span></p></td>  <td><p align="left"><span style="color:#333333;">--</span></p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">5</span></p></td>  <td><p align="left"><span style="color:#333333;">ET2</span></p></td>  <td><p align="left"><span style="color:#333333;">定时器 2 </span>中断使能</p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">4</span></p></td>  <td><p align="left"><span style="color:#333333;">ES</span></p></td>  <td><p align="left"><span style="color:#333333;">串口中断使能</span></p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">3</span></p></td>  <td><p align="left"><span style="color:#333333;">ET1</span></p></td>  <td><p align="left"><span style="color:#333333;">定时器 1 </span>中断使能</p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">2</span></p></td>  <td><p align="left"><span style="color:#333333;">EX1</span></p></td>  <td><p align="left"><span style="color:#333333;">外部中断 1 </span>使能</p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">1</span></p></td>  <td><p align="left"><span style="color:#333333;">ET0</span></p></td>  <td><p align="left"><span style="color:#333333;">定时器 0 </span>中断使能</p></td> </tr> <tr>  <td><p align="left"><span style="color:#333333;">0</span></p></td>  <td><p align="left"><span style="color:#333333;">EX0</span></p></td>  <td><p align="left"><span style="color:#333333;">外部中断 0 </span>使能</p></td> </tr></tbody></table><p align="left"><span style="color:#333333;"></span><span style="color:#333333;background:#EEEEEE;">中断使能寄存器 IE </span>的位 0~5 控制了 6 个中断使能,而第 6 位没有用到,第 7 位是总开关。总开关就相当于我们家里或者学生宿舍里的那个电源总闸门,而 0~5 位这 6 个位相当于每个分开关。那么也就是说,我们只要用到中断,就要写 EA = 1 这一句,打开中断总开关,然后用到哪个分中断,再打开相对应的控制位就可以了。<span style="color:#333333;"></span><span style="color:red;background:#EEEEEE;">我们现在就把前面的数码管动态显示的程序改用中断再实现出来,同时数码管显示抖动和“鬼影”也一并处理掉了。程序运行的流程跟图 6-1 </span>所示的流程图是基本一致的,但因为加入了中断,所以整个流程被分成了两部分,秒计数和转换为数码管显示字符的部分还留在主循环内,而动态扫描部分则移到了中断函数内,并加入了消隐的处理。下面来看程序:</p><p align="left"></p><p align="left"></p><p align="left"><span style="color:red;background:#EEEEEE;">我们现在就把前面把不采用定时器中断的数码管多位显示程序和运用了定时器的数码管程序作一个对比:</span></p><p align="left"></p><p align="left"><span style="color:red;background:#EEEEEE;">不采用定时器中断的数码管多位显示程序:</span></p><p align="left"><span style="color:silver;"></span><pre name="code" class="objc">#include <reg52.h>/*共阴数码管位选*/unsigned char duanx[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,};/*数码管的数据段选*/unsigned char weix[]= {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe}; 
unsigned int a,b;void delay(unsigned int t){for(a=0;a<t;a++)for(b=0;b<200;b++);}void main(){unsigned char i;for(i=0;i<4;i++){P0=table[i];delay(1000);}}


 

大家可以先把程序抄下来,编译下载到单片机里运行,看看实际效果。是否可以看到,近乎完美的显示效果经过我们的努力终于做成功了。下面我们还要再来解析一下这个程序。

在这个程序中,有两个函数,一个是主函数,一个是中断服务函数。主函数 main()我们就不用说了,重点强调一下中断服务函数,它的书写格式是固定的,首先中断函数前边 void表示函数返回空,即中断函数不返回任何值,函数名是InterruptTimer0(),这个函数名在符合函数命名规则的前提下可以随便取,我们取这个名字是为了方便区分和记忆,而后是 interrupt这个关键字,一定不能错,这是中断特有的关键字,另外后边还有个数字 1,这个数字 1 怎么来的呢?我们先来看表 6-3。

表 6-3 中断查询序列

中断
函数编号

中断名称

中断
标志位

中断
使能位

中断
向量地址

默认
优先级

0

外部中断 0

IE0

EX0

0x0003

1(最高)

1

T0 中断

TF0

ET0

0x000B

2

2

外部中断 1

IE1

EX1

0x0013

3

3

T1 中断

TF1

ET1

0x001B

4

4

UART 中断

TI/RI

ES

0x0023

5

5

T2 中断

TF2/EXF2

ET2

0x002B

6


这个表格同样不需要大家记住,需要的时候过来查就可以了。我们现在看第二行的 T0中断,要使能这个中断那么就要把它的中断使能位 ET0 置 1,当它的中断标志位 TF0 变为 1时,就会触发 T0 中断了,那么这时就应该来执行中断函数了,单片机又怎样找到这个中断函数呢?靠的就是中断向量地址,所以 interrupt 后面中断函数编号的数字 x 就是根据中断向量得出的,它的计算方法是 x*8+3=向量地址。当然表中都已经给算好放在第一栏了,我们可以直接查出来用就行了。到此为止,中断函数的命名规则我们就都搞清楚了。

中断函数写好后,每当满足中断条件而触发中断后,系统就会自动来调用中断函数。比如我们上面这个程序,平时一直在主程序 while(1)的循环中执行,假如程序有 100 行,当执行到 50 行时,定时器溢出了,那么单片机就会立刻跑到中断函数中执行中断程序,中断程序执行完毕后再自动返回到刚才的第 50 行处继续执行下面的程序,这样就保证了动态显示间隔是固定的 1ms,不会因为程序执行时间不一致的原因导致数码管显示的抖动了。


0 0
原创粉丝点击