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、INT0和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】在ATC51的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=00000】 (3)设置IE寄存器
由于采用定时器T0中断,因此需将IE寄存器中的EA、ET0位置1。 (4)启动和停止定时器T0
在GATE0=0的条件下,将定时器控制寄存器TCON中的TR0=1,则启动定时器T0;TR0=0,则停止定时器T0定时。 定时器T0方式1中断定时的参考程序: #includeunsigned 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=00B,由于模式0用到了TH0的8位和TL0的低5位,于是可计算出TH0=10110001B=B1H,TL0=00011100B=1CH。
程序如下:
#includesbit 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”中已定义。
【例3】利用定时器T0测量正脉冲的宽度(时间),脉冲从P3.2(INT0)引
脚输入,设脉冲宽度不超过定时器的定时围,且系统时钟为12MHz,要求把该脉冲宽度值存入变量pul_width中。
在模式0的介绍中提到过,利用门控位GATE可以测量外部脉冲的宽度。
图6-16 利用GATE位测量正脉冲的宽度
具体方法是:设GATE=1,然后软件置位TR0,这时当INT0为1时(外部脉冲的上升沿)就会自动启动定时器,当INT0变为0时(外部脉冲的下降沿)关闭定时器,于是定时器中的计数值就体现了外部脉冲的宽度。
程序如下:
#includesbit p3_2=P3^2; /*定义定时器0的外部引脚*/ unsigned int pul_width; /*定义全局变量以保存脉宽结果*/ void main() /*主程序*/ {
unsigned char a; /*定义中间变量*/
TMOD=0x09; //T0工作为16位计数器方式1,GATE置1*/ //ox09=0000 1001 GATE=1,C/T=0定时,M1M0=01,方式1 TL0=0x00; //计数器装入初值,相当于最大定时时间 TH0=0x00; while(1)
{ // 单片机复位后,P3口都是高电平 while(p3_2= =1); //等待INT0变低,启动定时器
TR0=1; //定时器准备好,等待INT0的上升沿就启动 while(p3_2= =0); //等待INT0的上升沿,开始计时
while(p3_2= =1); //在INT0上变为低电平时,为完整的正脉冲 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 =10000B
则TH0=11011000B=D8H,TL0=11110000B=F0H。
# include //包含特殊功能寄存器库 sbit P1_1=P1^1; char i;void main( ) {
TMOD=0x01;
TH0=0xD8;TL0=0xF0; EA=1;
ET0=1; i=0; TR0=1; while(1); }
void time0_int(void) interrupt 1 //中断服务程序 {
TH0=0xD8; //重新装入计数初值 TL0=0xF0; i++;
if (i= =50)
{ P1_1=! P1_1; i=0; } }
(2)用定时/计数器T1计数实现:
定时/计数器T1工作于计数方式时,计数脉冲通过T1(P3.5)输入,设定时/计数器T0定时时间到对T1(P3.5)取反一次,则T1(P3.5)每20ms产生一个计数脉冲,那么定时500ms只须计数25次。
设定时/计数器T1工作于方式2,初值X=256-25=231= 11100111B=E7H,TH1=TL1=E7H。
定时/计数器T0工作于方式1,定时,则这时方式控制字为01100001B(61H)。 定时/计数器T0和T1都采用中断方式工作。 # include //包含特殊功能寄存器库 sbit P1_1=P1^1; sbit P3_5=P3^5; void main( ) {TMOD=0x61;
TH0=0xD8;TL0=0xF0; TH1=0xE7; TL1=0xE7; EA=1; ET0=1; ET1=1; TR0=1; TR1=1; while(1); }
void time0_int(void) interrupt 1 //T0中断服务程序 {
TH0=0xD8; TL0=0xF0; P3_5=!P3_5; }
void time1_int(void) interrupt 3 //T1中断服务程序 {
P1_1=! P1_1; }
【例5】P1.7驱动LED亮1秒灭1秒地闪烁,设时钟频率为6MHz。
长定时方法:增加一个软件计数器或一个硬件计数器。
本题采用硬件方式:T0定时,定时10ms; T1计数T0的定时跳变信号P1.0的负跳变次数,计满50个跳变为1秒。(查询方法)
T=2µs,X=5×105个T,而最大只能65536个T,不能满足要求,必须借助硬件计数器或软件循环。
T0定时初值: (方式1)
t=10ms,X=5000D=1388H,C=216- 5000=EC78H T1计数初值: (方式2) X=50D,C=28- 50=CDH
#include sbit P1_0=P1^0; sbit P1_7=P1^7;timer0 interrupt 1 using 1 /*T0中断服务程序*/ {
P1_0=! P1_0; /*10ms定时时间到, P1.0反相*/ TH0=(65536-5000)/256; /*计数初值重装载*/ TL0=(65536-5000)%256;
}
timer1 interrupt 3 using 2 /*T1中断服务程序入口*/ {
P1_7=! P1_7; /*1s定时时间到, 灯改变状态*/ }
main( ) {
P1_7=0; /*置灯初始灭*/
P1_0=1; /*保证第一次反相便开始计数*/ TMOD=0x61; /*定时器0工作在方式1定时,定时器1工作在方式2计数 */ TH0=(65536-5000)/256; /*预置计数初值*/ TL0=(65536-5000)%256; TH1=256-50; TL1=256-50;
IP=0x08; /*置优先级寄存器*/ EA=1; /*CPU开中断*/ ET0=1; /*开T0中断*/ ET1=1; /*开T1中断*/ TR0=1; /*启动T0*/ TR1=1; /*启动T1*/ for (;;)
{ } }
定时/计数器的主要特性 1、MCS-51系列中:
51子系列有2个16位的可编程定时/计数器:定时/计数器T0和定时/计
数器T1;
52子系列有3个:还有一个定时/计数器T2。 2、每个定时/计数器通过编程设定来实现:
对系统时钟(fosc)计数实现定时;
对外部信号(T0/T1引脚)计数实现计数功能。
3、每个定时/计数器都有多种工作方式,通过编程可设定工作于某种方式。
T0有4种工作方式; T1有3种工作方式; T2有3种工作方式。
4、每一个定时/计数器定时计数时间到时产生溢出,使相应的溢出位置位,溢出可通过查询或中断方式处理。
TMOD用于选择定时器/计数器T0、T1的工作模式和工作方式。
TCON用于控制T0、T1的启动和停止计数,同时包含了T0、T1的状态。 T0、T1不论是工作在定时器模式还是计数器模式,都是对脉冲信号进行计数,
只是计数信号的来源不同。
计数器模式是对加在T0(P3.4)和T1(P3.5)两个引脚上的外部脉冲进行计数。
定时器工作模式是对单片机的时钟振荡器信号经片12分频后的部脉冲信号计数。由于时钟频率是定值,所以可根据计数值可计算出定时时间。
计数器的起始计数都是从计数器初值开始的。单片机复位时计数器的初值为0,也可用指令给计数器装入一个新的初值。ATC51的定时器/计数器属于增1计数器。
【6.3 对外部输入的计数信号的要求
当定时器/计数器工作在计数器模式时,计数脉冲来自外部输入引脚T0或T1。
当输入信号产生负跳变时,计数器的值增1。
每个机器周期的S5P2期间,都对外部输入引脚T0或T1进行采样。如在第一个机器周期中采得的值为1,而在下一个机器周期中采得的值为0,则在紧跟着的再下一个机器周期S3P1,期间,计数器加1。由于确认一次负跳变要花2个机器周期,因此外部输入的计数脉冲的最高频率为系统振荡器频率的1/24。
对于外部输入信号的占空比并没有什么,但为了确保某一给定电平在变化之前能被采样一次,则这一电平至少要保持一个机器周期。
故对外部输入信号的要求如图6-12所示,图中,Tcy为机器周期。
图6-12 对外部计数输入信号的要求