返回主页 单片机教程XL2000开发板 单片机学习 自制编程器 单片机资料 软件下载 电子技术产品介绍如何购买 进入论坛

单片机教程第1课  教学资料单片机教程第18课  算术指令
单片机教程第2课  好书推荐单片机教程第19课  逻辑指令
单片机教程第3课  单片机不易掌握的概念单片机教程第20课  逻辑指令
单片机教程第4课  实战一:流水灯单片机教程第21课  转移指令
单片机教程第5课  实战二:唱歌单片机教程第22课  位操作
单片机教程第6课  测试一单片机教程第23课  计数定时器
单片机教程第7课  新教程前言单片机教程第24课  计数定时器
单片机教程第8课  总体规划单片机教程第25课  中断系统
单片机教程第9课  概述单片机教程第26课  中断练习
单片机教程第10课  单片机结构单片机教程第27课  定时计数实验2
单片机教程第11课  基本概念单片机教程第28课  串行口
单片机教程第12课  延时程序分析单片机教程第29课  串口实例
单片机教程第13课  延时程序分析单片机教程第30课  数码管编程
单片机教程第14课  并口结构单片机教程第31课  动态数码管编程
单片机教程第15课  结构分析单片机教程第32课  键盘接口编程
单片机教程第16课  寻址方式单片机教程第33课  键盘扫描编程
单片机教程第17课  上机练习 单片机教程第34课  指令介绍        平凡的单片机教程

定时、中断练习一

1、利用定时器实现灯的闪烁

在学单片机时我们第一个例子就是灯的闪烁,那是用延时程序做的,现在回想起来,这样做不很恰当,为什么呢?我们的主程序做了灯的闪烁,就不能再干其它的事了,难道单片机只能这样工作吗?当然不是,我们可以用定时器来实现灯的闪烁的功能。

1:查询方式

ORG       0000H

AJMP     START

ORG       30H

START:

       MOV       P1,#0FFH;关所 灯

       MOV       TMOD,#00000001B;定时/计数器0工作于方式1

       MOV       TH0,#15H    

       MOV       TL0,#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       AJMP     LOOP

END

键入程序,看到了什么?灯在闪烁了,这可是用定时器做的,不再是主程序的循环了。简单地分析一下程序,为什么用JBC呢?TF0是定时/计数器0的溢出标记位,当定时器产生溢出后,该位由01,所以查询该位就可知宇时时间是否已到。该位为1后,要用软件将标记位清0,以便下一次定时是间到时该位由01,所以用了JBC指令,该指位在判1转移的同时,还将该位清0

以上程序是可以实现灯的闪烁了,可是主程序除了让灯闪烁外,还是不能做其他的事啊!不,不对,我们可以在LOOP……AJMPLOOP指令之间插入一些指令来做其他的事情,只要保证执行这些指令的时间少于定时时间就行了。那我们在用软件延时程序的时候不是也可以用一些指令来替代DJNZ吗?是的,但是那就要求你精确计算所用指令的时间,然后再减去相应的DJNZ循环次数,很不方便,而现在只要求所用指令的时间少于定时时间就行,显然要求低了。当然,这样的方法还是不好,所以我们常用以下的方法来实现。

程序2:用中断实现

ORG       0000H

AJMP     START

ORG       000BH ;定时器0的中断向量地址

AJMP     TIME0    ;跳转到真正的定时器程序处

ORG       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:                 ;定时器0的中断处理程序

       PUSH     ACC

PUSH     PSW      ;将PSW和ACC推入堆栈保护

       CPLP1.0      

       MOV       TH0,#15H

       MOV       TL0,#0A0H    ;重置定时常数

       POP       PSW

       POP       ACC

       RETI

END

上面的例子中,定时时间一到,TF001,就会引发中断,CPU将自动转至000B处寻找程序并执行,由于留给定时器中断的空间只有8个字节,显然不足以写下所有有中断处理程序,所以在000B处安排一条跳转指令,转到实际处理中断的程序处,这样,中断程序可以写在任意地方,也可以写任意长度了。进入定时中断后,首先要保存当前的一些状态,程序中只演示了保存存ACCPSW,实际工作中应该根据需要将可能会改变的单元的值都推入堆栈进行保护(本程序中实际不需保存护任何值,这里只作个演示)。

上面的两个程序运行后,我们发现灯的闪烁非常快,根本分辨不出来,只是视觉上感到灯有些晃动而已,为什么呢?我们可以计算一下,定时器中预置的数是5536,所以每计60000个脉冲就是定时时间到,这60000个脉冲的时间是多少呢?我们的晶振是12M,所以就是60000微秒,即60毫秒,因此速度是非常快的。如果我想实现一个1S的定时,该怎么办呢?在该晶振濒率下,最长的定时也就是65536个毫秒啊!上面给出一个例子。

ORG       0000H

AJMP     START

ORG       000BH ;定时器0的中断向量地址

AJMP     TIME0    ;跳转到真正的定时器程序处

ORG       30H

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:                 ;定时器0的中断处理程序

       PUSH     ACC

PUSH     PSW      ;将PSW和ACC推入堆栈保护

       INC30H

       MOV       A,30H

       CJNE     A,#20,T_RET       ;30H单元中的值到了20了吗?

T_L1:     CPL P1.0              ;到了,取反P10

       MOV       30H,#0;清软件计数器

T_RET:

       MOV       TH0,#15H

       MOV       TL0,#9FH      ;重置定时常数

       POP       PSW

       POP       ACC

       RETI

END

先自己分析一下,看看是怎么实现的?这里采用了软件计数器的概念,思路是这样的,先用定时/计数器0做一个50毫秒的定时器,定时是间到了以后并不是立即取反P10,而是将软件计数器中的值加1,如果软件计数器计到了20,就取反P10,并清掉软件计数器中的值,否则直接返回,这样,就变成了20次定时中断才取反一次P10,因此定时时间就延长了成了20*501000毫秒了。

这个思路在工程中是非常有用的,有的时候我们需要若干个定时器,可51中总共才有2个,怎么办呢?其实,只要这几个定时的时间有一定的公约数,我们就可以用软件定时器加以实现,如我要实现P10口所接灯按1S每次,而P11口所接灯按2S每次闪烁,怎么实现呢?对了我们用两个计数器,一个在它计到20时,取反P10,并清零,就如上面所示,另一个计到40取反P11,然后清0,不就行了吗?这部份的程序如下

ORG       0000H

AJMP     START

ORG       000BH;定时器0的中断向量地址

AJMP     TIME0    ;跳转到真正的定时器程序处

ORG       30H

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:                 ;定时器0的中断处理程序

       PUSH      ACC

PUSH      PSW       ;PSWACC推入堆栈保护

       INC 30H

       INC 31H;两个计数器都加1

       MOV      A,30H

       CJNE      A,#20,T_NEXT     ;30H单元中的值到了20了吗?

T_L1:      CPL P1.0        ;到了,取反P10

       MOV      30H,#0 ;清软件计数器

T_NEXT:

       MOV      A,31H

       CJNE      A,#40,T_RET ;31h单元中的值到40了吗?

T_L2:

       CPL P1.1

       MOV      31H,#0           ;到了,取反P11,清计数器,返回

T_RET:

MOV      TH0,#15H

       MOV      TL0,#9FH      ;重置定时常数

       POP PSW

       POP ACC

       RETI

END

程序一下载代码下载程序二下载代码下载程序三下载代码下载程序四下载代码下载

您能用定时器的方法实现前面讲的流水灯吗?试试看。