单片机的定时与中断
详细内容
单片机的定时与中断实验
前面的两节课程我们介绍了单片机定时/计数器和中断的原理及结构,这节课我们就来做几个实验验证一下前面所学的内容。实验一、利用定时器实现灯的闪烁在开始学单片机时我们所做的第一个实验就是LED 灯的闪烁,不过那是用延时程序做的,现在回想起来,这样做不是很恰当,为什么呢?因为我们的主程序做了灯的闪烁,就不能再干其它的事情了,难道单片机只能这样工作吗?当然不是,我们可以用定时器来完成灯的闪烁功能。
程序如下:
0000H ;
AJMP START ;
30H ;
START:MOV P1,#0FFH ;关所有的灯
MOV TMOD,#00000001B ; 定时/计数器0 工作于方式1
MOV TH0,#15H ;
MOV TL0,#0A0H ;立即数(15AH+0A0H=5536)
SETB TR0 ; 定时/计数器0 开始运行
LOOP:JBC TF0,NEXT ;如果TF0 等于1,则清TF0 并转NEXT 处
此处可以加入其他的任何指令
AJMP LOOP ;否则跳转到LOOP 处运行
NEXT:CPL P1.0 ;
MOV TH0,#15H ;
MOV TL0,#9FH ;重置定时/计数器的初值
AJMP LOOP ;
END。
把程序下载到实验板,看到了什么?灯开始闪烁了,这可是用定时器做的,不再是主程序的循环了。简单分析一下程序,为什么用JBC 呢?TF0 是定时/计数器0 的溢出标记位,当定时器产生溢出后,该位由“0”变“1”,所以查询该位就可知道定时时间是否已到,该位为“1”后,要用软件将标记位清“0”,以便下一次定时时间到了将该位由“0”变为“1”,所以用了JBC 指令,该指令前面已经学过--判“1”转移的同时,将该位清“0”。
以上程序可以实现灯的闪烁了,可是主程序除了让灯闪烁外,还是不能做其他的事啊!不对,我们可以在LOOP:JBC TF0,NEXT 和AJMP LOOP 指令之间插入一些指令来做其他的事情,只要保证执行这些指令的执行时间少于定时时间就可以了。
提个问题,我们在用软件延时程序的时候不是也可以用DJNZ 替代一些指令吗?是的,但是那就要求你精确计算所用指令的时间,然后再减去相应的DJNZ 循环次数,很不方便,而现在只要求所用指令的时间少于定时时间就行,显然要求低了。当然,这样的方法还不是最好,所以我们常用下面的方法来实现:
实验二、利用中断方式实现灯的延时

30H ;START:MOV P1,#0FFH ;关所有灯MOV TMOD,#00000001B ; 定时/计数器0 工作于方式1 MOV TH0,#15H ; MOV TL0,#0A0H ;立即数5536 SETB EA ;开总中断允许SETB ET0 ;开定时/计数器0 允许SETB TR0 ;定时/计数器0 开始运行LOOP:AJMP LOOP ;真正工作时,这里可写任意程序TIME0:PUSH A ;将A 推入堆栈保护PUSH PSW ;将PSW 推入堆栈保护CPL P1.0 ;取反P1.0 MOV TH0,#15H ; MOV TL0,#0A0H ;重置定时常数POP PSW ;POP A ;RETI ;END。
上面的例子中,定时时间一到,TF0 由“0”变“1”,就会引发中断,CPU 将自动转至000B 处寻找程序并执行,由于留给定时器中断的地址空间只有8 个字节,显然不足以写下所有的中断处理程序,所以在 000BH 后安排了一条长跳转指令,转到实际处理中断的程序处,这样,中断程序可以写在任意地方,也可以写任意长度了。单片机进入定时中断后,首先要保存当前的一些状态,在这里程序只演示了保存A 和PSW,实际工作中应该根据需要将可能会改变的单元的值都推入堆栈进行保护(本程序中实际不需保护任何值,这里只作个演示)。
上面的两个程序运行后,我们发现灯的闪烁非常快,根本分辨不出来,只是视觉上感到灯有些晃动而已,为什么呢?我们可以计算一下,定时器中预置的数是5536 ,所以每计60000(65536-5536 )个脉冲就是定时时间到,这60000 个脉冲的时间是多少呢?我们的晶振是12 兆的,所以就是60000 微秒,即60 毫秒,因此速度是非常快的。如果我想实现一个1 秒的定时器,该怎么办呢?在该晶振频率下,最长的定时也就是65.536 个毫秒啊!请看第三个实验:
实验三、延长定时时间的方法
程序如下:
0000H ; AJMP START ; 000BH ;定时器0 的中断向量地址AJMP TIME0 ;跳转到真正的定时器程序处 30H ;START:MOV P1,#0FFH ; 关所有的灯MOV 30H,#00H ;软件计数器预清0 MOV TMOD,#00000001B; 定时/计数器0 工作于方式1 MOV TH0,#3CH ;MOV TL0,#0B0H ;立即数(3CH+0BH=15536)SETB EA ;开总中断允许SETB ET0 ;开定时/计数器0 允许SETB TR0 ;定时/计数器0 开始运行
LOOP:AJMP LOOP ;真正工作时,这里可写任意程序TIME0:PUSH A ;将A 推入堆栈保护PUSH PSW ;将PSW 推入堆栈保护INC 30H ;MOV A,30H ;CJNE A,#20,TIME1 ;30H 单元中的值到了20 了吗? CPL P1.0 ;到了,取反P1.0 MOV 30H,#0 ;清软件计数器TIME1:MOV TH0,#15H ;给T0 重新赋值MOV TL0,#9FH ;重置定时常数POP PSW ;POP A ;RETI ;END。
先自己分析一下程序,看看是怎么实现的?这里采用了软件计数器的概念,思路是这样的,先用T0 做一个50 毫秒的定时器,定时时间到了之后并不是立即取反P1.0,而是将软件计数器中的值加1,如果软件计数器计到了20,就取反一次P1.0,并清掉软件计数器中的值;否则直接返回。这样,就变成了20 次定时中断才取反一次P1.0,因此定时时间就延长了成了20*50mS ,即1000 毫秒了。这个思路在实际的工程应用中是非常有用的,比如有时候我们需要若干个定时器,可89C51 中总共才有2 个,怎么办呢?其实,只要这几个定时器在时间上有一定的公约数,我们就可以用软件定时器加以实现。例如要实现P1.0 口所接灯按1S/次,而P1.1 口所接灯按2S/次闪烁,怎么实现呢?我们可以用两个计数器,一个在它计到20 时,取反一次P1.0,并清“0”,如上面所示;而另一个则计到40 取反一次P1.1,然后清“0”,不就行了吗?看下面的实验:
实验四、软件定时器的实现方法
程序如下:
0000H ; AJMP START ; 000BH ;定时器0 的中断向量地址AJMP TIME0 ;跳转到真正的定时器程序处 0030H ; START:MOV P1,#0FFH ; 关所有的灯MOV 30H,#00H ;软件计数器预清0 MOV TMOD,#00000001B ; 定时/计数器0 工作于方式1 MOV TH0,#3CH ;MOV TL0,#0B0H ;立即数15536 SETB EA ;开总中断允许SETB ET0 ;开定时/计数器0 允许SETB TR0 ;定时/计数器0 开始运行LOOP:AJMP LOOP ;真正工作时,这里可写任意程序TIME0: PUSH A ;将A 推入堆栈保护PUSH PSW ;将PSW 推入堆栈保护INC 30H ;INC 31H ;两个计数器都加1
MOV A,30H ; CJNE A,#20,TNEXT ;30H 单元中的值到了20 了吗CPL P1.0 ;到了,取反P1.0
这就是软件定时器的用法,试试看,用软件定时器做一个交通信号灯的实验,要求红灯亮2 分钟,黄灯亮1 分钟,绿灯亮3 分钟,然后红灯再亮2 分钟,黄灯再亮1 分钟,绿灯再亮3 分钟,不断的循环⋯⋯。通过上面的几个实验,大家对定时器的使用方法是不是有了一个初步认识,接下来让我们共同来做一个更为有趣的实验:用单片机做一个乐曲编奏器。实验五、可编程乐曲演奏器
单片机用作乐曲编奏器的原理是,通过控制定时器的定时时间来产生不同频率的方波,驱动喇叭发出不同音阶的声音,再利用延迟来控制发音时间的长短,就能控制音调中的节拍。编程时,把乐谱中的音符和相应的节拍变换为定时常数和延迟常数,把数据表格存放在存储器中,由查表程序得到相应的定时常数和延迟常数,分别用定时器控制产生方波的频率和发出该频率方波的持续时间,当延迟时间到了之后,再查下一个音符的定时常数和延迟常数。依次进行下去,就可自动演奏出悦耳动听的乐曲来。
程序如下:
001BH ;定时器T1 的中断入口
MOV TH1,R1 ;重装定时初值
MOV TL1,R0 ;
CPL P3.7 ;P1.0 输出方波
RETI ;中断返回
100H ;主程序
START:MOV TMOD,#10H ; 定时器T1 工作方式1
MOV IE,#88H ;允许T1 中断
MOV DPTR,#TAB ;表格首地址
LOOP:CLR A ;
MOVC A,@A+DPTR ;查表
MOV R1,A ;定时器高8 为存R1
INC DPTR ;
CLR A ;
MOVC A,@A+DPTR ;查表
MOV R0,A ;定时器低8 为存R0
ORL A,R1 ;
JZ NEXT0 ;全0 为休止符
MOV A,R0 ;
ANL A,R1 ;
CJNE A,#0FFH,NEXT ;全1 表示乐曲结束SJMP START ;从头开始,循环演奏NEXT:MOV TH1,R1 ;装入定时值MOV TL1,R0 ; SETB TR1 ;启动定时器SJMP NEXT1 ; NEXT0:CLR TR1 ;关闭定时器,停止发音NEXT1:CLR A ; INC DPTR ; MOVC A,@A+DPTR ;查延迟常数MOV R2,A ; LOOP1:LCALL D200 ;调用延时200mS 子程序
上述程序是歌曲“新年好”的一段简谱:1=C 1 1 1 5 │ 3 3 3 1 │ 1 3 5 5 │ 4 3 2 —│,如何来实现的呢?。单片机的晶振为12M,则乐曲、频率及定时常数三者之间的对应关系如下表所示:

我们用定时器T1 工作方式1 来产生歌谱中各音符对应频率的方波,由P3.7 输出驱动蜂鸣器,节拍的控制通过调用延时子程序D200 (延时200mS )的次数来实现,以每拍800mS 的节拍时间为例,一拍需要调用D200 子程序4 次,同样的道理,半拍就需要调用2 次,由此就演奏出了上面的乐曲。
