标准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共同来决定的
M1 M0 模式
0 1 TH和TL2个组成16位计数存储器模式
1 0 TH负责初始化TL,TL计数。8位重装模式
0 0 THx的8位和TLx的位5组成13位加法计数器(很少用)
1 1 基本不用
时钟周期和机器周期
顺便提一下:标准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 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 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 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 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次。减少溢出次数和赋值次数,可以减轻单片机的负担,提高定时的准确性。
上一篇:C51联盟 —— 外部中断+定时器中断
下一篇:【51单片机实验】INT0中断计数
推荐阅读最新更新时间:2024-11-08 12:49