8051有两个定时器/计数器,而8052则有三个定时器/计数器。定时器、计数器本质都是一样的,定时器通过对内部脉冲进行计数来达到计时的目的,而计数器是对外部输入脉冲进行计数,所以它们的本质都是一样的。
定时器/计数器的工作模式:
定时器/计数器0有四种工作模式,而定时器/计数器1只有三种工作模式,工作模式由TMOD寄存器的M1和M0位控制。
M1 M0 工作模式 0 0 0 13位定时器/计数器。 0 1 1 16位定时器/计数器。 1 0 2 8位自动重装定时器/计数器。 1 1 3 模式3只能用于定时器/计数器0,T0被分为两个8位定时器/计数器, TL0可以作为定时器/计数器来使用,由T0的控制位来控制,而TH0只 能用于计数器,有T1的控制位来控制。 GATE GATE为0时,由TRx(软件控制)来控制定时器/计数器的启动和停止,当GATE为 1时,定时器/计数器的启动由TRx和引脚INTx共同来控制(硬件来控制,仅当 TRx=1并且引脚INTx输入高电平时才工作)。 C/T 定时器/计数器选择位,0工作在定时器模式,1工作在计数器模式。 TCON寄存器: TFx 定时器/计数器溢出标志位,当定时器/计数器计数溢出时,由硬件自动置1,在 编写程序时可以通过检测该位来判断是否达到定时时间,需要通过软件来清零。 工作在中断方式下不需要清零,因为在执行中断服务程序时会自动给我们清零。 TRx 定时器/计数器运行控制位。 IE寄存器: ETx 定时器/计数器中断使能位。 EA 全局中断使能位。
THx、TLx初值计算:
假设8051外接12MHz的晶振,由于8051的机器周期是时钟周期的12分频,所以8051的机器周期为1MHz,那么计数一次的时间刚好为1us,如果定时器工作在模式0下,那么可以最长计时为8192us,工作模式1可以最长计时为65536us。那么假设8051工作在模式1下,定时1ms,那么THx、TLx的初值应该为多少呢?如果定时1ms,也就意味着需要计数1000次,那么初值就应该为65536-1000=64536,也就是THx=0xFC,TLx=0x18。
根据上面可以总结出如下公式:
定时时间=(最大计数值-计数器初值)*机器周期=(最大计数值-计数器初值)*(12000000/晶振频率(Hz))(us);
计数器初值=(最大计数值-定时时间*(晶振频率(Hz)/12000000));
套用这个公式,初值=(65536-1000*(12000000/12000000))=64536=0xFC18,那么TH就应该为0xFC,TL为0x18。
整个程序如下:
/* timer.c */ #include <at89x52.h> #define OSC_FREQ 12000000UL #define TIMER_MODE0 0x00 #define TIMER_MODE1 0x01 #define TIMER_MODE2 0x02 #define TIMER_MODE3 0x03 void timer0_init(void) { TMOD &= 0xF0; TMOD |= TIMER_MODE1; /* Timer 0 work on 16-bit timer mode */ TH0 = (65536-1000*(OSC_FREQ/12000000)) >> 8; TL0 = (65536-1000*(OSC_FREQ/12000000)) & 0x00FF; TR0 = 1; /* Turn on timer 0 */ } void delayms(unsigned int n) { while (n--) { while (!TF0); TF0 = 0; TH0 = (65536-1000*(OSC_FREQ/12000000)) >> 8; TL0 = (65536-1000*(OSC_FREQ/12000000)) & 0x00FF; } }
/* timer.h */ #ifndef __TIMER_H #define __TIMER_H extern void timer0_init(void); extern void delayms(unsigned int n); #endif /* __TIMER_H */
/* main.c */ #include <at89x52.h> #include "timer.h" #define led1 P2_0 void main(void) { timer0_init(); while (1) { led1 = 0; delayms(1000); led1 = 1; delayms(1000); } }