一、定时器介绍
定时/计数器T0和T1分别是由两个8位的专用寄存器组成,即定时/计数器T0由TH0和TL0组成,T1由TH1和TL1组成。此外,其内部还有2个8位的特殊功能寄存器TMOD和TCON,TMOD负责控制和确定T0和T1的功能和工作模式,TCON用来控制T0和T1启动或停止计数,同时包含定时/计数器的状态。 [1]
TF1:定时器1溢出标志。定时/计数器溢出时由硬件置位。中断处理时由硬件清除。或用软件清除。
TF0:定时器0溢出标志。定时/计数器溢出时由硬件置位。中断处理时由硬件清除,或用软件清除。
二、单片机的内部框图与定时器工作原理分析
从上面的图中红线可以看出由TMOD选择由那个定时器工作,工作于什么方式;
从上面的图中蓝线可以看出由TCON决定定时器是否启动;
从上面的图中黄线可以看出外部技术输入由TH和TL进行累计;
从上面的图中紫线可以看出当TH和TL计数溢出时会向TCON进行申请报告;
从上面的图中绿线可以看出所有的定时中断都由TCON向CPU进行中断申请;
从上面的图中黑线可以看出外部中断直接向CPU进行中断申请;
三、寄存器介绍
TCON:
TF0和TF1:定时器/计数器溢出标志位。
当定时器/计数器0(或定时器/计数器1)溢出时,由硬件自动使TF0(或TF1)置1,并向CPU申请中断。
CPU响应中断后,自动对TF1清零。TF1也可以用软件清零。
TR0和TR1:定时器/计数起运行控制位。
TR0(或TR1)=0,停止定时器/计数器0(或定时器/计数器1)工作。
TR0(或TR1)=1,启动定时器/计数器0(或定时器/计数器1)工作。
TMOD:
GATE:门控位。
GATE=0,只要用软件使TR0(或TR1)置1就能启动定时器/计数器0(或定时器/计数器1);
GATE=1,只有在(或)引脚为高电平的情况下,且由软件使TR0(或TR1)置1时,才能启动定时器/计数器0(或定时器/计数器1)工作。不管GATE处于什么状态,只要TR0(或TR1)=0定时器/计数器便停止工作。
C/T: 定时器/计数器工作方式选择位。
C/T =0,为定时工作方式;
C/T=1,为计数工作方式。
M0、M1:工作方式选择位,确定4种工作方式。
IE:
EA = 1, CPU开放总中断;
ET0 = 1,允许T0中断;
TR TL:定时器寄存器
注意:由于reg52.h的库定义了大部分的寄存器所以可以直接给TCON TMOD IE赋值操作或者是单独寄存器操作。
四、中断程序执行方法和定时计数公式
中断程序执行方法有直接中断和查询两种方法,查询即查询TF位是否置位为1
定时初值计算公式为:
定时时间=(计数最大值 – 计数初值)×机器周期
机器周期T = 12/晶振频率
或者是用取模的方式
TH0=(计数最大值 – 定时时间)/256;
TL0=(计数最大值 – 定时时间)%256;
首先解析为什么要设定初值?
当设定定时器为16为计数器时,则计数最大值为2^16 = 65536;
TH和TL分别为8位,即TL每逢256进一位,所以除掉256就能看出TH进了多少位,取模则是余下多少位
举个例子,2位计数器最大计数为4,要将初值6分到两个2位计数器,由于低位逢四进1所以剩下2
即TL = 2然后高位是1即TH=1,按照上面的方法:2^4 = 16,2^2 = 4; TH = (16-(16-6))/4=1(整型不算小数);
TL = (16-(16-6))%4=2;可见结果是一样的。
一般的我们选取初值 TH0 = 0x3c,TLO = 0xb0,这时寄存器跑满时的时间为50ms,可以使这个过程进行20次,间隔也就是1s。
五、程序设计
//通过定时器中断实现数码管显示从0~59,间隔为1s
#include
sbit LED1 = P1^0;
#define DataPort P1 //宏定义,如果换接口,需要修改这里
sbit Seg_Latch = P2^2; //段锁存
sbit Bit_Latch = P2^3; //位锁存
unsigned char code Seg_Code[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //段码从0~9
unsigned char code Bit_Code[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //位码,控制8个LED灯
void delayls(void);
void Display(unsigned char m,unsigned char num,unsigned int n);
static unsigned int n = 0;
int main(void)
{
unsigned char i;
TMOD = 0x01;
TH0 = 0x3c;
TL0 = 0xb0;
EA = 1; //开中断
ET0 = 1;
//因为只有一个中断,所以IP不用设置
TR0 = 1; //开启定时器0,等待中断到来
while(1)
{
Display(1,2,n);
}
}
void T0_ISR(void) interrupt 1
{
unsigned char i;
EA = 0; //屏蔽其他中断
TH0 = 0x3c; //重新赋值,保证每次都是 50ms
TL0 = 0xb0;
i++;
if(i == 20)
{
i = 0;
if(n < 60)
{
n++;
}
else
{
n = 0;
}
}
EA = 1;
}
void Display(unsigned char m,unsigned char num,unsigned int n) //m表示从第几个数码管开始控制,num表示到第几个数码管,n表示要显示的数字
{
unsigned char i,j,a[5],l;
a[0] = n / 10;
a[1] = n % 10;
for(j = m-1;j < num;j++)
{
DataPort = 0; //初始化
Seg_Latch = 1;
Seg_Latch = 0;
DataPort = Bit_Code[j]; //送位码
Bit_Latch = 1; //位开门
Bit_Latch = 0; //关门
DataPort = Seg_Code[a[j]]; //送段码
Seg_Latch = 1; //段开门
Seg_Latch = 0; //关门
}
}