【C51】单片机定时器介绍

来源:互联网 发布:游戏编程入门 pdf 编辑:程序博客网 时间:2024/04/28 20:32

【C51】单片机定时器介绍http://www.cnblogs.com/lulipro/p/5064099.html

标准51架构的单片机有2个定时器 :T0  和  T1,他们2个的用法几乎一样。下面主要讲T0定时器的用法。

 

初步认知

 

定时器 和 计数器 都是单片机中同一个模块。他们的实质都是: 加法存储计数器。对于计数器很好理解,每来一个信号(信号从P3.4 或者P3.5输入),就加1,以此达到计数的目的。
对于定时器,每隔1个机器周期 加 1,假如(只是假如)一个机器周期为 1ms , 当加到1000时,我们就认为经过了1s,这就是定时器的原理。

 

加法存储寄存器THx & TLx

 
定时器依赖计数,需要把累计增加的那个量存储在某个地方,这就是THx和TLx(x 可以是 0 或者1)2个8位寄存器的的职责。
T0和T1都拥有一对加法存储寄存器。
T0 对应:TH0,TL0
T1 对应 : TH1 , TL1
 
在reg51.h头文件中我们发现这4个寄存器的定义:
 
sfr TL0  = 0x8A;              //  TL中的L是LOW的意思,代表低位,同理H代表HIGH高位。sfr TL1  = 0x8B;sfr TH0  = 0x8C;sfr TH1  = 0x8D; 

 

他们可以在程序中直接使用,复位值都是 0 。
当一直累加,使得他们保存不了太大的数据而发生溢出时,就会引发中断(后面讲中断)。并且对应的TFx溢出标志位会置为1,(没有溢出的情况下是0)。
如果不使用中断去处理溢出这个事件,那么我们就必须通过代码指令让TFx重置为 0 ,并让THx和TLx回归初始值,准备然后进入下一轮周期的计数。
 
 
复制代码
....if(TF0==1)    //如果T0 溢出了{     TF0=0;     //重新初始化 TH0  和 TL0     //说明过了一个溢出周期了} 
复制代码

 

 

 2个重要的寄存器:TMOD 和 TCON

 
 

 

复位时所有位全  为 0

TFx:溢出标志位。溢出时置1。正常为0。
 
TRx:计数器/定时器 启动停止控制位 。R是run的意思。  TR0 = 1  开启定时器0,为TR0 = 0 则停止。
 
低4位与外部中断相关,这里用不到,先不用看。

 

 

 

 

 

复位时所有位全  为 0

高4位是定时器T1相关的,低4位是T0 相关的。
以T0来说明。
 
GATE:      门控制位
C/T    :     定时器/计数器切换位。 1为计数器模式, 0 为定时器模式。

 

②处 C/T = 0 表示为定时器模式,触发信号为①处的单片机内部时钟信号。(若②处CT = 1,则触发信号为Tn脚,信号从P3.4 或者P3.5输入单片机)
 
③处表明,信号能触发使加法计数器加1,还得受④处控制。不然时钟信号是不能让加法计数器累加的。 ④处这个是与门,TRn必须为1,表明我们要开启定时器。同时GATE为0,通过非门后为1,再通过或门,也是1,那么就让③处控制起来了。
 
(若GATE为1,那么,定时器的启动停止受 TRx和 INTx 共同控制。 )
 
 
于是我们需要:
TRn    为 1
GATE  为 0
INTn   为 X(X表示任意的意思,do not care)
 
 
 
加法存储寄存器的工作模式,是由M0和M1共同来决定的
M1M0模式01TH和TL2个组成16位计数存储器模式10TH负责初始化TL,TL计数。8位重装模式00THx的8位和TLx的位5组成13位加法计数器(很少用)11基本不用

 

 

 

时钟周期和机器周期

 
顺便提一下:标准C51的1个机器周期为12个时钟周期(增强型51单片机的机器周期会短一些,cc2530只有的机器周期只等于1个时钟周期)。
如果晶振的频率是11.0592MHz,那么时钟周期就是   1 / (11.0592x10^6) 秒   (1MHz = 10^6Hz)

那么,无论是定时器,还是计数器,每隔1个机器周期 ,加法存储器就1,代表时间经过了  12  x     1 / (11.0592x10^6) 秒。这就是我们衡量的基础依据。

 
 

为THx和TLx赋初始值

若TH0 和 TL0 以 16位 模式工作,那它的计数范围为   [0 , 65535 ]  ,  也就是累加 65536次发生溢出。 每累加一次是  12 / (11.0592x10^6) 秒。
那么从 0 累加到溢出 历时  ≈ 0.071s = 71ms 。
我们一般需要延时 10的整数倍ms,以便用倍数控制更长的延时时间。所以,我么要给 TH0 和  TL0赋一个初始值,使他们的溢出周期(TH0,TL0从初始值到溢出所用的时间)减少到 10ms,或者1ms。
就像一个瓶子,开始装了2/3,再来就只能装1/3就溢出了。
 

 12 / (11.0592x10^6) s       -----     1   次

 
 10x 10-3   s                        ------          x  次         (求出 x = 9216次 ,计数9216次后溢出)
 
65536 - 9216 = 56320  =  二进制( 11011100   00000000)
 
也就是  TH0 = 11011100 , TL0 = 00000000

 

 

代码例子验证

 

复制代码
#include<reg51.h>typedef unsigned int uint; /**************函数声明******************/void delay10ms(uint m) ;void delay1ms(uint m) ;/********************************/ /*****************************/sbit LED = P0^0; /*****************************/void main() {      while(1)     {         LED = 1;         delay10ms(100);   // delay1ms(1000)         LED = 0;         delay10ms(100);   // delay1ms(1000)                        }} void delay10ms(uint m)     //溢出周期为10ms{  /********************   使用到的寄存器(位)      TH0   TL0   TCON:  TR0    TF0   TMOD:  ***********************/    uint count =0;    TMOD = 1;        //GATE = 0    C/T =0    M1 = 0  M0 = 1;  16位计数器    TL0 = 0 ;    TH0 =220 ;    TR0 = 1;    for(;count<m;)    {         if(TF0 == 1)         {              TF0 = 0;              TL0 = 0;              TH0 = 220;              ++count;         }    }    TR0   =  0;   //关闭定时器    }void delay1ms(uint m)       //溢出周期为1ms{     uint count=0;     TMOD = 1;   //计时器0以16为存储计时器工作     TH0 = 252 ;     TL0 = 102;     TR0 = 1;         for(;count<m;)         {          if(TF0==1)        //发生一次溢出,也就是过了1ms                    {               TF0=0;       //溢出位清零,取消警报               TH0 = 252 ;  //重新配置初始值               TL0 = 102;               count++;     //溢出次数加1 ,溢出1次是1ms,溢出t次就是t ms                     }        }           TR0 = 0;}
复制代码

 

 

 

8位重装模式

8位重装模式是:只有TL0计数,TH0不变,他只为TL0提供初始值。当TL计数溢出后,TF0就为1,如果继续工作,TH0就把自己的值赋给TL0,再开始计数,如此循环下去。

上面些写了一个毫秒级的delay函数,下面用8位重装模式写一个控制微秒级别的函数。并控制P0.0的LED实现呼吸灯。

 

计算方法和上面一样,大家可以自己算

 

复制代码
#include<reg51.h>typedef unsigned int uint;# define TRUE 1# define FALSE 0/**************函数声明******************/
void
delay1ms(uint t);/********************************/sbit LED = P0^0;void main(){ int step = 0; int again = FALSE; while(1) { LED = again?0:1; delay1ms(step); LED = again?1:0;; delay1ms((500-step)); step+=1; if(step>500) { step =0; again = !again; } }}void delay1ms(uint m) //延时t微秒{ int count=0; TMOD = 2; TH0= 255 ; TL0= 255; TR0=1; for(;count!=m;) { if(TF0==1) { TF0=0; //自动重装 count++; } } TR0=0; }
复制代码

 

 

值得注意的地方

我们应该尽量让溢出周期 越长越好。溢出周期为10ms 的优于 1ms 的。因为,在同样的延时时间下,如100ms,溢出周期为10ms 的 只需要溢出10次,为TH0 和 TL0重新赋值10次,而溢出周期为1ms的要溢出100次,为TH0 和 TL0重新赋值100次。减少溢出次数和赋值次数,可以减轻单片机的负担,提高定时的准确性。

 

 

 

/***************************************************/

 欢迎转载,请注明出处:www.cnblogs.com/lulipro

 为了获得更好的阅读体验,请访问原博客地址。

 代码钢琴家

/***************************************************/

 

作者:代码钢琴家 
出处:http://www.cnblogs.com/lulipro/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。为了获得更好的阅读体验,请访问原博客地址。限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

原创粉丝点击