1、 第第7章章 AT89S51单片机的单片机的 定时器定时器/计数器计数器17.1 定时器定时器/计数器的结构计数器的结构 AT89S51定时器/计数器结构见图 7-1,定时器/计数器T0由特殊功能寄存器TH0、TL0构成,T1由特殊功能寄存器TH1、TL1构成。2图图7-1 定时器定时器/计数器结构框图计数器结构框图TMOD用于选择定时器/计数器T0、T1的工作模式和工作方式。TCON用于控制T0、T1的启动和停止,同时包含T0、T1状态。计数器起始计数从初值开始。单片机复位时计数器初值为0。7.1.1 工作方式控制寄存器工作方式控制寄存器TMOD 图图7-2 寄存器寄存器TMOD格式格式(1
2、)GATE门控位门控位GATE=0,是否计数,由控制位TRx(x=0,1)来控制。GATE=1,是否计数,由外中断引脚INTx*上的电平与运行控制位TRx共同控制。4(2)M1、M0工作方式选择位工作方式选择位 M1、M04种编码,对应于4种工作方式的选择,见表7-1。表7-1M1、M0工作方式选择5 (3)C/T*计数器模式和定时器模式选择位C/T*=0,定时器模式,对系统时钟12分频的脉冲进行计数。C/T*=1,计数器模式,计数器对外部输入引脚T0(P3.4)或T1(P3.5)的外部脉冲(负跳变)计数。7.1.2 定时器定时器/计数器控制寄存器计数器控制寄存器TCON TCON控制T0、T
3、1的启动和停止,同时包含T0、T1状态。6图图7-3 TCON格式格式这里仅介绍高4位功能。(1)TF1、TF0计数溢出标志位 当计数器计数溢出时,该位置“1”。使用查询方式时,此位可供CPU查询,但应注意查询后,用软件及时将该位清“0”。使用中断方式时,作为中断请求标志位,进入中断服务程序后由硬件自动清“0”。(2)TR1、TR0计数运行控制位 TR1位(或TR0)=1,启动计数器计数的必要条件。TR1位(或TR0)=0,停止计数器计数。该位可由软件置“1”或清“0”。7.2 定时器定时器/计数器的计数器的4种工作方式种工作方式 4种工作方式,分别介绍如下。78图图7-5 方式方式1的逻辑结
4、构框图的逻辑结构框图方式1和方式0差别仅仅在于计数器的位数不同7.2.2 方式方式1当M1、M0=01时,工作于方式1,等效电路逻辑结构见图7-5。7.2.1 方式方式07.2.3 方式方式2 方式0和方式1最大特点是计数溢出后,计数器为全0。因此在循环定时或循环计数应用时就存在用指令反复装入计数初值的问题,这会影响定时精度,方式2就是为解决此问题而设置的。当M1、M0=10时,工作方式2,等效逻辑结构见图7-6(以T1为例,x=1)。工作方式2为自动恢复初值(初值自动装入)的8位定时器/计数器,TLx(x=0,1)作为常数缓冲器,当TLx计数溢出时,在溢出标志TFx置“1”的同时,还自动将T
5、Hx中的初值送至TLx,使TLx从初值开始重新计数。定时器/计数器方式2工作过程见图7-7。910图图7-67-6 方式2逻辑结构框图11图7-7 方式2工作过程方式2可省去用户软件中重装初值的指令执行时间,简化定时初值的计算方法,可相当精确地定时。7.2.4 方式方式3 方式3是为增加一个附加的8位定时器/计数器而设置的,从而使AT89S51具有3个定时器/计数器。方式3只适用于T0,T1不能工作在方式3。T1方式3时相当于TR1=0,停止计数(此时T1可作为串口波特率产生器)。1工作方式工作方式3下的下的T0 当TMOD的低2位为11时,T0被选为方式3,各引脚与T0的逻辑关系见图7-8。
6、T0分为两个独立的8位计数器TL0和TH0,TL0使用T0的状态控制位C/T*、GATE、TR0,而TH0被固定为一个8位定时器(不能作为外部计数模式),并使用定时器T1的状态控制位TR1,同时占用定时器T1的中断请求源TF1。12 图7-8 T0方式3的逻辑结构框图2T0工作在方式工作在方式3时时T1的各种工作方式的各种工作方式 一般情况下,当T1用作串口波特率发生器时,T0才工作在方式3。T0方式3时,T1可为方式0、1、2,作为串口波特率发生器,或不需要中断的场合。(1)T1工作在方式工作在方式0 (2)T1工作在方式工作在方式1 当T1的控制字中M1、M0=01时,T1工作在方式1,工
7、作示意见图7-10。14(3)T1工作在方式工作在方式2 当当T1控制字中控制字中M1、M0=10时,时,T1为方式为方式2,工作示意如,工作示意如图图7-11所示。所示。15图7-10 T0方式3时T1为方式1工作示意图 图7-11 T0方式3时T1为方式2工作示意图(4)T1设置在方式3 T0方式3时,再把T1也设置成方式3,此时T1停止计数。7.3 对外部输入的计数信号的要求对外部输入的计数信号的要求 计数器模式时,计数脉冲来自外部输入引脚T0或T1。当输入信号产生负跳变时,计数值增1。每个机器周期S5P2期间,都对外部输入引脚T0或T1进行采样。如在第1个机器周期中采得值为1,而在下一
8、个机器周期中采得的值为0,则在紧跟着的再下一个机器周期S3P1期间,计数器加1。由于确认一次负跳变要花2个机器周期,即24个振荡周期,因此外部输入的计数脉冲的最高频率为系统振荡器频率1/24。16 如选用6MHz晶体,允许输入脉冲频率最高为250kHz。如选用12MHz频率晶体,则可输入最高频率500kHz外部脉冲。对外输入信号占空比没有限制,但为确保某一给定电平在变化前能被采样1次,则该该电平至少保持电平至少保持1个机器周期个机器周期。故对外部输入信号要求见图7-12,图中Tcy为机器周期。图图7-12 对外部计数输入信号的要求对外部计数输入信号的要求187.4 7.4 定时器定时器/计数器
9、的编程和应用计数器的编程和应用 4种工作方式中,方式0与方式1基本相同,只是计数位数不同。方式0为13位,方式1为16位。由于方式0是为兼容MCS-48而设,计数初值计算复杂,所以在实际应用中,一般不用方式0,常采用方式1。7.4.1 P1口控制口控制8只只LED每每0.5s闪亮一次闪亮一次 【例7-1】在AT89S51的P1口上接有8只LED,原理电路见图图7-137-13。采用T0方式1的定时中断方式,使P1口外接的8只LED每0.5s闪亮一次。19图7-13 方式1定时中断控制LED闪亮(1)设置)设置TMOD寄存器寄存器(2)计算定时器)计算定时器T0的计数初值的计数初值(3)设置)设
10、置IE寄存器寄存器(4)启动和停止定时器)启动和停止定时器T0 20#includechar i=100;void main()TMOD=0 x01;/定时器T0为方式1TH0=0 xee;/设置定时器初值TL0=0 x00;P1=0 x00;/P1口8个LED点亮EA=1;/开总中断ET0=1;/开定时器T0中断TR0=1;/启动定时器T0while(1);/循环等待;21void timer0()interrupt 1/T0中断程序TH0=0 xee;/重新赋初值TL0=0 x00;i-;/循环次数减一if(i=0)P1=P1;/P1口按位取反i=100;/重置循环次数7.4.2 计数器的
11、应用计数器的应用【例7-2】如图7-14,T1的采用计数模式,方式1中断,计数输入引脚T1(P3.5)上外接按钮开关,作为计数信号输入。按4次按钮开关后,P1口的8只LED闪烁不停。(1)设置TMOD寄存器(2)计算定时器T1的计数初值(3)设置IE寄存器(4)启动和停止定时器T1。23图图7-14 由外部计数输入信号控制由外部计数输入信号控制LED的闪烁的闪烁#include void Delay(unsigned int i)/定义延时函数Delay(),i是形式参数,不能赋初值unsigned int j;for(;i0;i-)/变量i由实际参数传入一个值/因此i不能赋初值for(j=0
12、;j125;j+);/空函数 void main()/主函数 TMOD=0 x50;/设置定时器T1为方式1计数TH1=0 xff;/向TH1写入初值的高8位TL1=0 xfc;/向TL1写入初值的低8位EA=1;/总中断允许 ET1=1;/定时器T1中断允许 TR1=1;/启动定时器T1 while(1);/无穷循环,等待计数中断 2627void T1_int(void)interrupt 3 /T1中断函数for(;)/无限循环 P1=0 xff;/8位LED全灭 Delay(500);/延时500ms P1=0;/8位LED全亮 Delay(500);/延时500ms 287.4.3
13、控制控制P1.0产生周期为产生周期为2ms的方波的方波 【例7-3】假设系统时钟为12MHz,设计电路并编写程序实现从P1.0引脚上输出一个周期为2ms的方波,见图7-15。要在P1.0上产生周期为2ms的方波,定时器应产生1ms的定时中断,定时时间到则在中断服务程序中对P1.0求反。使用定时器T0,方式1定时中断,GATE不起作用。图图7-15 定时器控制定时器控制P1.0输出一个周期输出一个周期2ms方波方波30图7-16 定时器控制P1.0输出周期2ms的方波的原理电路31#include /头文件reg51.hsbit P1_0=P10;/定义特殊功能寄存器P1的位变量P1_0void
14、 main(void)/主程序TMOD=0 x01;/设置T0为方式1TR0=1;/接通T0while(1)/无限循环 TH0=0 xfc;/置T0高8位初值TL0=0 x18;/置T0低8位初值do while(!TF0);/判TF0是否为1,为1则T0溢出,往下/执行,否则原地循环P1_0=!P1_0;/P1.0状态求反TF0=0;/TF0标志清零32仿真时,右键单击虚拟数字示波器,出现下拉菜单,点击“Digital oscilloscope”选项,就会在数字示波器上显示P1.0引脚输出周期为2ms方波,如图7-17所示。33图7-17 虚拟数字示波器显示的2ms的方波波形7.4.4 利用
15、利用T1控制发出控制发出1kHz的音频信号的音频信号 【例7-4】利用T1的中断控制P1.7引脚输出频率为1kHz方波音频信号,驱动蜂鸣器发声。系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。电路见图7-18。先计算T1初值,系统时钟为12MHz,则方波周期为1s。1kHz音频信号周期为1ms,要定时计数的脉冲数为a。则T1初值:TH1=(65 536 a)/256;TL1=(65 536 a)%2563435图图7-18 控制蜂鸣器发出控制蜂鸣器发出1kHz的音频信号的音频信号参考程序如下:#include /包含头文件
16、sbit sound=P17;/将sound位定义为P1.7脚#define f1(a)(65536-a)/256/定义装入定时器高8位时间常数#define f2(a)(65536-a)%256 /定义装入定时器低8位时间常数unsigned int i=500;unsigned int j=0;void main(void)EA=1;/开总中断.ET1=1;/允许定时器T1中断 .TMOD=0 x10;/TMOD=0001 000B,使用T1的方式1定时 TH1=f1(i);/给定时器T1高8位赋初值.TL1=f2(i);/给定时器T1低8位赋初值.TR1=1;/启动定时器T1 while
17、(1)37 /循环等待 i=460;while(j2000);j=0;i=360;while(j 2000);j=0;void T1(void)interrupt 3 using 0/定时器T1中断函数 TR1=0;/关闭定时器T1 sound=sound;/P1.7输出求反 TH1=f1(i);/定时器T1的高8位重新赋初值.TL1=f2(i);/定时器T1的低8位重新赋初值.38 j+;TR1=1;/启动定时器T17.4.5 LED数码管秒表的制作数码管秒表的制作 【例7-5】用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.19.9s。当第1次按一下计时功能键时,秒表开始计
18、时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。本秒表应用定时器模式,计时范围0.19.9s。此外还涉及如何编写控制LED数码管显示的程序。本例原理电路见图7-19。39图图7-19 LED数码管显示的秒表原理电路及仿真数码管显示的秒表原理电路及仿真40参考程序如下:#include /包含51单片机寄存器定义的头文件unsigned char code discode1=0 xbf,0 x86,0 xdb,0 xcf,0 xe6,0 xed,0 xf
19、d,0 x87,0 xff,0 xef;/数码管显示09的段码表,带小数点unsigned char code discode2=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f;/数码管显示09的段码表,不带小数点unsigned char timer=0;/记录中断次数unsigned char second;/储存秒unsigned char key=0;/记录按键次数main()/主函数 TMOD=0 x01;/定时器T0方式1定时 ET0=1;/允许定时器T0中断41EA=1;/总中断允许second=0;/设初始
20、值P0=discode1second/10;/显示秒位0P2=discode2second%10;/显示0.1s位0while(1)/循环if(P3&0 x80)=0 x00)/当按键被按下时 key+;/按键次数加1switch(key)/根据按键次数分三种情况case 1:/第一次按下为启动秒表计时TH0=0 xee;/向TH0写入初值的 /高8位42TL0=0 x00;/向TL0写入初值的低8位,定时5msTR0=1;/启动定时器T0break;case 2:/按下两次暂定秒表TR0=0;/关闭定时器T0break;case 3:/按下3次秒表清0key=0;/按键次数清second=0
21、;/秒表清0P0=discode1second/10;/显示秒位0 P2=discode2second%10;/显示0.1s位0break;while(P3&0 x80)=0 x00);/如果按键时间过长在此循环void int_T0()interrupt 1 using 0/定时器T0中断函数TR0=0;/停止计时,执行以下操作(会带来计时误差)TH0=0 xee;/向TH0写入初值的高8位TL0=0 x00;/向TL0写入初值的低8位,定时5mstimer+;/记录中断次数if(timer=20)/中断20次,共计时20*5ms=100ms=0.1s timer=0;/中断次数清0seco
22、nd+;/加0.1sP0=discode1second/10;/根据计时,即时显示秒位P2=discode2second%10;/根据计时,即时显示0.1s位 4344if(second=99)/当计时到9.9s时TR0=0;/停止计时second=0;/秒数清0key=2;/按键数置2,当再次按下按键时,/key+,即key=3,秒表清0复原 else/计时不到9.9s时TR0=1;/启动定时器继续计时7.4.6 测量脉冲宽度测量脉冲宽度门控位门控位GATEx的应用的应用 介绍门控位GATE应用。利用GATE测量INT1*脚上正脉冲宽度。【例7-6】门控位GATE1可使T1启动计数受INT1
23、*控制,当GATE1=1,TR1=1时,只有INT1*引脚输入高电平时,T1才被允许计数。利用该功能,可测量INT1*脚正脉冲宽度,方法见图7-20。原理电路见图7-21,图中省略复位电路和时钟电路。利用门控位GATE1来测量INT1*脚上正脉冲宽度,并在6位数码管上以机器周期数显示。对被测量脉冲信号宽度,要求能通过旋转信号源旋钮可调。45图图7-20 利用利用GATE位测量正脉冲的宽度位测量正脉冲的宽度47图图7-21 利用利用GATE位测量位测量 引脚上正脉冲的宽度的原理电路引脚上正脉冲的宽度的原理电路参考程序如下:#include#define uint unsigned int#def
24、ine uchar unsigned charsbit P3_3=P33;/位变量定义uchar count_high;/定义计数变量,用来读取TH0uchar count_low;/定义计数变量,用来读取TL0uint num;uchar shiwan,wan,qian,bai,shi,ge;uchar flag;uchar code table=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f;/共阴极数码管段码表void delay(uint z)/延时函数48uint x,y;for(x=z;x0;x-)for(y=
25、110;y0;y-);void display(uint a,uint b,uint c,uint d,uint e,uint f)/数码管显示函数P2=0 xfe;P0=tablef;delay(2);P2=0 xfd;P0=tablee;delay(2);P2=0 xfb;P0=tabled;49delay(2);P2=0 xf7;P0=tablec;delay(2);P2=0 xef;P0=tableb;delay(2);P2=0 xdf;P0=tablea;delay(2);void read_count()/读取计数寄存器的内容5051do count_high=TH1;/读高字节
26、count_low=TL1;/读低字节 while(count_high!=TH1);num=count_high*256+count_low;/*可将两字节的机器周期数进行 显示处理*/void main()while(1)flag=0;TMOD=0 x90;/设置定时器T1为方式1定时TH1=0;/向定时器T1写入计数初值 TL1=0;while(P3_3=1);/等待INT1*变低TR1=1;/如果INT1*为低,启动T1(未真正开始计数)while(P3_3=0);/等待INT1*变高,变高后T1真正开始计数while(P3_3=1);/等待INT1*变低,变低后T1停止计数TR1=0
27、;read_count();/读计数寄存器内容的函数shiwan=num/100000;wan=num%100000/10000;qian=num%10000/1000;bai=num%1000/100;shi=num%100/10;52ge=num%10;while(flag!=100)/减小刷新频率flag+;display(ge,shi,bai,qian,wan,shiwan);执行上述程序仿真,把INT1*引脚上出现的正脉冲宽度显示在LED数码管显示器上。晶振频率为12MHz,如果默认信号源输出频率为1kHz的方波,则数码管显示为500。注意:在仿真时,偶尔显示501是因为信号源的问题
28、,若将信号源换成频率固定的激励源则不会出现此问题。53547.4.7 LCD时钟的设计时钟的设计 【例【例7-77-7】使用定时器实现一个使用定时器实现一个LCDLCD显示时钟。采用显示时钟。采用LCD 1602LCD 1602,具体见第具体见第5 5章的绍。章的绍。LCDLCD时钟的原理电路时钟的原理电路见见图图7-227-22。最小计时单位是秒,如何获得最小计时单位是秒,如何获得1s1s的定时?可将的定时?可将T0T0定时时定时时间定为间定为50ms50ms,采用中断方式进行溢出次数累计,满,采用中断方式进行溢出次数累计,满2020次,则秒次,则秒计数变量计数变量secondsecond加
29、加1 1;若秒计满;若秒计满6060,则分计数变量,则分计数变量minuteminute加加1 1,同时将秒计数变量同时将秒计数变量secondsecond清清0 0;若分钟计满;若分钟计满6060,则小时计数变,则小时计数变量量hourhour加加1 1;若小时计数变量满;若小时计数变量满2424,则将小时计数变量,则将小时计数变量hourhour清清0 0。图图7-22 LCD时钟的原理电路时钟的原理电路先将定时器以及各计数变量设定完毕,然后调用时间显示子程序。秒计时由T0中断服务子程序来实现。参考程序如下:#include#include#define uchar unsigned ch
30、ar#define uint unsigned intuchar int_time;/定义中断次数计数变量uchar second;/秒计数变量56uchar minute;/分钟计数变量uchar hour;/小时计数变量uchar code date=H.I.T.CHINA ;/LCD第1行显示的内容uchar code time=TIME 23:59:55;/LCD第2行显示的内容uchar second=55,minute=59,hour=23;void clock_init()uchar i,j;for(i=0;i16;i+)write_data(datei);write_com(0
31、 x80+0 x40);for(j=0;j16;j+)write_data(timej);void clock_write(uint s,uint m,uint h)write_sfm(0 x47,h);write_sfm(0 x4a,m);write_sfm(0 x4d,s);void main()init1602();/lcd初始化clock_init();/时钟初始化TMOD=0 x01;/设置定时器T0为方式1定时EA=1;/总中断开 ET0=1;/允许T0中断 TH0=(65536-46483)/256;/给T0装初值TL0=(65536-46483)%256;TR0=1;int_t
32、ime=0;/中断次数、秒、分、时单元清0second=55;minute=59;hour=23;while(1)clock_write(second,minute,hour);void T0_interserve(void)interrupt 1 using 1/定时器T0中断服务子程序int_time+;/中断次数加1 if(int_time=20)/若中断次数计满20次 int_time=0;/中断次数变量清0 second+;/秒计数变量加 1 if(second=60)/若计满60s second=0;/秒计数变量清0 minute+;/分计数变量加 1 if(minute=60)/若计满60分 minute=0;/分计数变量清0hour+;/小时计数变量加1if(hour=24)hour=0;/小时计数计满24,将小时计数变量清0TH0=(65536-46083)/256;/定时器T0重新赋值TL0=(65536-46083)%256;执行上述程序仿真运行,就会在LCD上显示实时时间。