先上代码

#include <reg52.h>

void Init();

void main(){
    Init();
}

void Init(){
    TMOD = 0x01;
    TH0 = 0x4b;
    TL0 = 0xfc;    //定时器时间50ms,针对11.0592MHz频率CPU
    ET0 = 1;
    EA = 1;
    TR0 = 1;
}

void Timer0() interrupt 1 {
    TH0 = 0x4b;
    TL0 = 0xfd;
    //Timer循环体,运行过程放这里
}

示例代码就要有示例代码的样子,简简单单的才能把问题说清楚!

先解释下几个变量,TMOD,TH0,TL0,ET0,EA,TR0,这些变量不是我定义的,而是<reg52.h>头文件中的,先掌握用法,再深究原理。

 

TMOD:选择定时器的模式,不同的模式主要是能数到的最大数不同,一般就用模式1,最大可数到65535

TH0:TL0:设置起始值,TH0 故名思议就是数字转为16进制的高8位,TL0为低八位

TR0:启动、停止定时器,1启动,0停止,这个比较好理解吧

EA:允许系统进行中断,1允许,0禁止,算是个权限之类的东西

ET0:允许定时器0进行中断,1允许,0禁止

 

到这里你肯定还会有疑问,我接着给你解释!

 

1.定时器定时时间怎么算的啊,0x4bfc转为10进制并不是50啊?

 

——确实不是50,在讲定时器时间的算法之前,先得说下这个定时器的原理。

在我们高级语言习惯中,定时器就是给他设定一个数,他一秒数一下,数到那个值后进行一次定时操作。但是在嵌入式中并不是这样,也是因为这样错误的想法,我不理解了很久。

  在嵌入式中,定时器的实现原理是,他从某个数开始数,一直数到上限(如65535),到65536的时候定时器溢出,进行一次操作,而我们给的0x4bfc是定时器的起始值,也就是说定时器将从这个值开始数,一直数到65535,中间所耗费的时间就是50ms。

 

2.似乎明白了,那这个时间具体怎么算啊?

 

——恩,这个问题稍有些复杂,回答这个问题之前,还是要继续引入几个概念。

  时钟周期T1:晶振振荡周期,公式  T1 = 1/频率  ,如11.0592MHz的晶振频率  T1 = 1/11.0592 us

  机器周期T2:机器执行一条基本指令的时间,公式  T2 = 12 * T1  ,如11.0592MHz的机器周期约为  1.085 us

  

  所以,要定时50ms的计算过程

  50ms = 50000us = 50000/1.085 机器周期 = 46083 次

  也就是说,要让计时器数 46083 次就好了,要数到65535,那么很自然就知道是要从 65535 - 46083 = 19452 数起

  19452D = 0x4bfc

  所以 TH0 = 0x4b,  TL0 = 0xfc

 

3.我有注意到,TH0 TL0 TR0 ET0 后面都有0,感觉挺奇怪的?

 

——你看的很仔细,没错,这个0是有意义的。事实上,单片机里有两个定时器,TH0表示第一个定时器,TH1表示第二个,另外几个以T开头的都表示定时器变量,也都有T_0和 T_1两种,E开头的表示与中断相关。

 

4.interrupt 1 里面的1是什么意思,能换成其他数字吗?

——后面的1是中断号,Timer0 这个函数名称你可以随便取,但是后面这个数字却是固定的,因为它是用来说明这个函数是谁的中断函数,1表示是定时器1来中断,3表示定时器2中断。事实上,还有几个额外的中断类型,但是作为入门,就不在这里列举了。

 

5.为什么在中断函数 Timer0 里又重新设置了一次 TH0 和 TL0 呢,这是必须的吗?

 

——上面有提到过,这个函数里面的过程是在定时器数到65536溢出后执行的,但是有个问题是溢出完后TH0 和 TL0就会被重置为0,如果你不重新设定的话它会从0开始数起,所以是必须的,定时器1和定时器2都是这样的。

 

定时器简单的理解到这就差不多了,关于中断还需要继续学习,另外几种中断方式原理上都是有共通点的!