2、模式1
模式1(M1M0=01)除了使用了THn和TLn全部16位外,其它与模式0相同。
(1)计数工作方式
由于定时器/计数器以加1方式计数,假定计数值为X,则应装入定时器/计数器的初值为:
初值=216-计数值【216=初值+计数值】
所以方式1的计数值范围是:1~65536(216=65536),最大值为:65536
(2)定时工作方式
定时时间t的计算公式为:【t的时间单位为微秒(μs)】
计数值=216-初值
定时时间t=计数值×机器周期
=(216-初值)×(1/晶体振荡频率)×12
在模式1下的情况下,如果fosc=12MHz,最大定时时间为:
t=(65536-初值)×(1/12)×12=65536-0=65.536ms
在模式1下的情况下,如果fosc=6MHz,最大定时时间为:
t=(65536-初值)×(1/6)×12=(65536-0)×2=131.072 ms。
【例如】:若晶体振荡为12MHz,要定时2.5ms,计算初值。
要定时2.5ms,也可以用模式1。
2500=(216-初值)×(1/12)×12
初值=65536-2500=63036=32768+16384+8192+4096+1024+512+32+16+8+4=1111 0110 0011 1100
――> THn =0xF6 和TLn=0x3C
在fosc=12MHz时,如果定时时间大于65.536ms,这时用一个定时/计数器直接处理不能实现,这时可用:
1、2个定时/计数器共同处理;
2、1个定时/计数器配合软件计数方式处理。
3、模式2
方式0和方式1的最大特点是计数溢出后,计数器为全0。因此在循环定时或循环计数应用时就存在用指令反复装入计数初值的问题。这不仅影响定时精度,也给程序设计带来麻烦。方式2就是针对此问题而设置的。
该方式可省去用户软件中重装初值的指令执行时间,简化定时初值的计算方法,可以相当精确地确定定时时间。
此模式下定时器寄存器作为可自动重装载的8位计数器(TLn),如下图所示。
以T0为例,模式2把寄存器TH0作为一个存放初值的常数寄存器,TL0则成为一个可以自动重装载的8位计数器。TL0计数溢出时,不仅置位溢出标志TF0向CPU提出中断申请,同时还自动把TH0中的初值重新装载到TL0中。TH0中的内容靠软件预置,重新装载后其内容不变。
模式2的操作对于定时器0及定时器1是相同的。
假定计数值为X,则应装入定时器/计数器的初值为:
初值=28-计数值【28=初值+计数值】
所以方式2的计数值范围是:1~256(28=256),最大值为:256
定时时间t的计算公式为:【t的时间单位为微秒(μs)】
计数值=28-初值
定时时间t=计数值×机器周期
=(28-初值)×(1/晶体振荡频率)×12
在模式2下的情况下,如果fosc=12MHz,最大定时时间为:
t=(256-初值)×(1/12)×12=256μs
在模式2下的情况下,如果fosc=6MHz,最大定时时间为:
t=(256-初值)×(1/6)×12=(256-0)×2=512μs。
4、模式3
方式3只适用于定时/计数器T0,T1不能工作在方式3。方式3将T0分成为两个独立的8位计数器TL0和TH0。
模式3对定时器/计数器0和定时器/计数器1是不同的。将T/C0设置为模式3时,将使TH0和TL0成为2个互相独立的8位定时器/计数器。如上图所示。
由于TL0利用了定时器/计数器0的全部控制位:/C T 、TR0、GATE 、0INT 和TF0,它的操作情况与模式0、1类同,不同的仅为8位。
TH0则被固定作为一个8位定时器(计数机器周期),不能作为计数器方式,它使用定时器/计数器1的运行控制位TR1作为运行控制唯一条件,同时占用它的中断标志位TF1。
一般来说,当系统需要增加一个额外的8位定时器时,才设置定时器/计数器0工作于模式3。当定时器/计数器0工作于模式3时,由于TH0占用了定时器1的TR1的控制位和中断标志,虽然定时器/计数器1仍可定义为模式0、1和2,但只能用在不需要中断的场合。例如,工作于自动装载模式(模式2),作串行口的波特率发生器使用等等。
5.1.4 定时器/计数器T0和T1应用举例
定时器/计数器的编程注意点:
一是能正确写入控制字(初始化),二是计算定时和计数常数。
一般情况下,初始化程序应完成如下工作:
1.根据要求选择方式,确定方式控制字,写入方式控制寄存器TMOD ,以确定T0和T1的工作方式;
2.根据要求计算定时/计数器的计数值,再由计数值求得初值,写入初值寄存器TH0、TL0或TH1、TL1;
3.如果采用中断方式,则须编写中断服务程序,并且需要:
①对IE 寄存器的ETx 置位,允许定时/计数器中断;
②置位EA ,使CPU 开放总中断。
4.设置定时/计数器控制寄存器TCON 的值,启动定时/计数器开始工作。
5.等待定时/计数时间到,如果采用中断,则执行中断服务程序;如采用查询方式处理,则编写查询程序判断溢出标志,溢出标志等于1,则进行相应处理。
【例1】在AT89C51的P1口上接8个LED 。下面采用定时器T0的方式1
的定时中断方式,使P1口外接的8只LED每0.5s闪亮一次。
(1)设置TMOD寄存器
定时器T0工作在方式1,应使TMOD寄存器的M1、M0=01;定时器模式,
C T=0;如果对T0的运行控制仅由TR0来控制,那么应使GATE0=0。应设置/
定时器T1不使用,各相关位均设为0。所以,TMOD寄存器应初始化为0x01。
(2)计算定时器T0的计数初值
定时采用定时器和软件的方法,0.5s=500ms=5ms×100。
设定时时间5ms(即5000μs),设定时器T0的初值为X,假设晶振的频率为11.0592MHz,则定时时间为:定时时间=(216?X)?12/晶振频率
则5000=(216?X)?12/11.0592
得:X=60928,转换成十六进制后为:0xee00,其中0xee装入TH0,0x00装入TL0。
【最大定时71.11μs,最小定时1.085069444μs】
【注:60928=32768+16384+8192+2048+1024+512=1110111000000000】(3)设置IE寄存器
由于采用定时器T0中断,因此需将IE寄存器中的EA、ET0位置1。
(4)启动和停止定时器T0
在GATE0=0的条件下,将定时器控制寄存器TCON中的TR0=1,则启动定时器T0;TR0=0,则停止定时器T0定时。
定时器T0方式1中断定时的参考程序:
#include
unsigned char i=100; //给变量i赋初值
void main()
{
TMOD=0x01; //设置定时器T0为方式1
TH0=0xee; //向TH0写入定时初值的高8位
TL0=0x00;//向TL0写入定时初值的低8位
P1=0x00;//P1口8只LED点亮,共阴极接法
EA=1;//总中断允许
ET0=1;//定时器T0中断允许
TR0=1;//启动定时器T0,GATE=0,由TR0决定启动
while(1) ; //循环等待
{ ;}
}
void T0_int(void) interrupt 1 //定时器T0中断服务程序
{
TH0=0xee; //给T0重新装入16位初值,计数4608后,T0溢出
TL0=0x00; //计数4608后,即定时5ms到,100次,即500ms
i--; //循环次数减【4608=65536-60928】
if(i< =0) //如果到了100次的5ms,即500ms
{ P1=~ P1; //P1口按位取反
i =100; //重新设置循环次数
}
}
【例2】设单片机系统时钟频率为12MHz,试编程使P1.2引脚输出周期为5ms的方波。
当系统时钟为12MHz时,若定时/计数器工作于模式0,则最大定时时间为213=8192μs(8.192ms),满足周期为5ms的要求,可选用定时器T0,工作于模式0,定时时间为2.5ms。首先计算定时器的初值,根据下式:
定时时间t=计数值×机器周期
=(213-初值)×(1/晶体振荡频率)×12
2500=(213-初值)×(1/12)×12
可计算出定时器的初值为5692,换算成二进制数为TCB=1011000111100B,由于模式0用到了TH0的8位和TL0的低5位,于是可计算出TH0=10110001B=B1H,TL0=00011100B=1CH。
程序如下:
#include
sbit p1_2=P1^2; //应该可以直接用位INT0变量,因为reg51.h中有
void main()
{
TMOD=0x00; //GATE=0,定时器的启动由TR0决定
TH0=0xb1; //赋定时时间常数
TL0=0x1c;
TR0=1; //启动定时器
while(1) // TH0溢出时,置位TCON中的TF0标志
{
while(TF0==1)//while(TF0==0); 等待定时器溢出,查询方式
{ TF0=0;//如果采用中断,则不需清零,可以自动清零
TH0=0xb1; //重新赋定时时间常数
TL0=0x1c;
p1_2=!p1_2;
}
}
}
TF0没有定义,但可以使用,因为在“reg51.h”中已定义。
INT)引【例3】利用定时器T0测量正脉冲的宽度(时间),脉冲从P3.2(0
脚输入,设脉冲宽度不超过定时器的定时范围,且系统时钟为12MHz,要求把该脉冲宽度值存入变量pul_width中。
在模式0的介绍中提到过,利用门控位GATE可以测量外部脉冲的宽度。
图6-16 利用GATE位测量正脉冲的宽度
INT为1时(外部具体方法是:设GATE=1,然后软件置位TR0,这时当0
INT变为0时(外部脉冲的下降沿)脉冲的上升沿)就会自动启动定时器,当0
关闭定时器,于是定时器中的计数值就体现了外部脉冲的宽度。
程序如下:
#include
sbit p3_2=P3^2; /*定义定时器0的外部引脚*/
unsigned int pul_width; /*定义全局变量以保存脉宽结果*/
void main() /*主程序*/
{
unsigned char a; /*定义中间变量*/
TMOD=0x09; //T0工作为16位计数器方式1,GATE置1*/
C T=0定时,M1M0=01,方式1
//ox09=0000 1001 GATE=1,/
TL0=0x00; //计数器装入初值,相当于最大定时时间
TH0=0x00;
while(1)
{ // 单片机复位后,P3口都是高电平
INT变低,启动定时器
while(p3_2= =1); //等待0
INT的上升沿就启动TR0=1; //定时器准备好,等待0
INT的上升沿,开始计时
while(p3_2= =0); //等待0
INT上变为低电平时,为完整的正脉冲while(p3_2= =1); //在0
TR0=0; //测量结束,停止计时间,再来脉冲也不会测量了
Pul_width= TH0*256+TL0; //计算脉宽的时间宽度
// Pul_width= (TH0*256+TL0) *(12/晶体振荡频率)
}
}
【应该为:Pul_width=(TH0*256+TL0)*(12/晶体振荡频率); a变量可以不要】
【和晶体振荡频率有关】
如果定时时间大于65536μs,这时用一个定时/计数器直接处理不能实现,这时可用:【武汉纺织大学】
(1)2个定时/计数器共同处理;
(2)1个定时/计数器配合软件计数方式处理。
【例4】设系统时钟频率为12MHz,编程实现从P1.1输出周期为1s的方波。
应产生500ms的周期性的定时,定时到则对P1.1取反就可实现。由于定时时间较长,一个定时/计数器不能直接实现500ms定时,可用:
①定时/计数器T0产生周期性为10ms的定时,然后用一个寄存器R2对10ms 计数50次
②或用定时/计数器T1对10ms计数25次实现。
系统时钟为12MHz,定时/计数器T0定时10ms,计数值N为10000,只能选方式1,方式控制字为00000001B(01H),
求初值X:
X=65536-10000=55536 =1101100011110000B
则TH0=11011000B=D8H,TL0=11110000B=F0H。
# include
sbit P1_1=P1^1;
char i;
void main( )
{
TMOD=0x01;
TH0=0xD8;TL0=0xF0;
EA=1;