1、项目三 电子钟的设计 能力目标 1、能够使用单片机的定时/计数器实现定时、计数等应用。 2、能够完成电子钟硬件电路与控制程序的设计、制作与调试。 知识目标 1、了解单片机定时器/计数器的内部结构。 2、掌握单片机定时器的各种工作方式及特点、应用场合。 3、掌握定时器中断服务程序的编写。,任务一、认识单片机的定时器/计数器,一、定时器/计数器及其应用在单片机应用系统中,定时或计数是必不可少的。例如:测量一个脉冲信号的频率、周期,或者统计一段时间里电机转动了多少圈等。常用的定时方法有:,1、软件定时软件定时是依靠执行一段程序来实现的,这段程序本身没有具体的意义,通过选择恰当的指令及循环次数实现所需
2、的定时,由于执行每条指令都需一定的时间,执行这段程序所需总的时间就是定时时间。软件定时的特点是无需硬件电路,但定时期间CPU被占用,增加了CPU的开销,因此定时时间不宜过长,而且定时期间如果发生中断,定时时间就会出现误差。,3、可编程定时器定时这种定时方法是通过对系统时钟脉冲的计数来实现的。通过程序来设置计数初值,改变计数初值也就改变了定时时间,使用起来非常灵活。由于定时器可以与CPU并行工作,因此不影响CPU的效率,且定时时间精确。,2、硬件定时硬件定时通常由小规模集成电路555外加电阻、电容构成,电路简单,不占CPU资源,但定时时间的调节不够灵活方便。,(一)定时器/计数器概述在51系列单
3、片机中有两个16位的加法计数器,分别叫做T0和T1。在计数脉冲的作用下,其计数值不断加1,在此过程中,计数器可能产生溢出,产生溢出后,可以向CPU发出中断请求。,如果计数脉冲来自系统时钟,称之为“定时器”,每个机器周期计数器加1;,如果计数脉冲来自外部电路,称之为“计数器”,一旦计数器产生溢出,TF0变为1 ,向CPU发出中断请求,二、定时器/计数器的控制寄存器 1、定时器控制寄存器TCON,TF0/TF1:定时器/计数器T1和T0的溢出中断标志。为1,表示定时器/计数器的计数值已由全1变为全0,正向CPU发中断请求。,TR0/TR1:定时器/计数器T0和T1的启停控制位:为0时,定时器/计数
4、器停止工作;为1时,启动定时器/计数器工作。,2、定时方式寄存器TMOD,选择定时/计数器的工作方式,M1M0= 00,方式0 01,方式1 10,方式2 11,方式3,定时方式/计数方式的选择控制位。为0,选定时方式,计数脉冲来自系统时钟的12分频;为 1,选计数方式,计数脉冲来自外部电路,当GATE=0时,只要TR0=1,与门的输出就为1,计数开始。 如果GATE为1,只有TR0=1,并且/INT0=1时,才允许计数。,C/T=0,选择定时方式,C/T=1,选择计数方式,GATE=0时,由TR0控制计数器启停 GATE=1时,由TR0和INT0一同控制启停,TR0=1时,启动定时器,定时器
5、溢出时,TF0置1,三、T0、T1的工作方式,1、方式0方式0的计数器由13位构成,其中高8位在TH中,低5位在TL中。当计数器产生溢出时,TF位被置1,向CPU发出中断请求。在方式0下,计数器产生溢出时,不能进行初始计数值的自动重装(有关自动重装的问题参见方式2),所以方式0不能用于精确定时。方式0的所有功能都可以用方式1代替,方式0的存在,是因为兼容早期的MCS-48单片机的原因,所以一般不使用方式0。,2、方式1方式1与方式0工作形态基本相同,只是方式1的计数器由16位构成,其中高8位在TH中,低8位在TL中,当计数器产生溢出时,TF位被置1,向CPU发出中断请求。在方式1下,计数器产生
6、溢出时,也不能进行初始计数值的自动重装所以方式1也不能用于精确定时。,3、方式2方式2是可以自动重装的工作方式:初始化时一般将8位计数初值同时放入TH、TL中,其中,TH存放的是初值的备份,TL用来计数,当8位计数器TL产生溢出时,除了可以向CPU发中断请求外,单片机的硬件部分还立即把TH中的备份送入TL中。由于重新赋值是硬件自动进行的,所以避免了重新赋值的时间不一,所以方式2可以用于精确定时。,4、方式3T1经常用于串行口的波特率发生器,为了让系统中保持两个计数器,可以让T0工作在方式3,这时,T0被分成两个8位计数器,分别位于TH0和TL0中,其中TL0使用T0的中断、启动控制资源,而TH
7、0则借用T1的中断、启动控制资源,而且TH0只能工作在定时方式下,不能工作在计数方式下。,(四)时间常数的计算如果单片机需要进行周期性的工作,就应该让定时器/计数器T0或T1工作在定时方式,并且给T0或T1赋以一个初始计数值,在T0或T1被启动后,每个机器周期使计数器中的计数值加1,计数器产生溢出后,将再次给计数器赋值(该值被称为时间常数)。显然计数器溢出时间(又称定时时间)与时间常数直接相关:时间常数越大,定时时间就越短;时间常数越小,定时时间就越长。同时系统时钟的频率也直接影响定时时间的长短,时钟的频率越高,定时时间越短;时钟的频率越低,定时时间越长。,设系统时钟的频率为fosc,计数器的
8、初始值为N,定时器工作于方式1,则定时时间:T=(216-N)12/fosc (1)如果定时器工作于方式2或方式3,定时时间为:T=(28-N)12/fosc (2),当初始值N=0时,如果fosc=12MHZ,最大定时时间为:方式1为: Tmax=21612/fosc=65536us=65.536ms方式2、方式3为:Tmax=2812/fosc=256us,根据定时时间T,及公式(1)、(2)分别可以求出初值N为:方式1: N=216-Tfosc/12 (3)方式2、方式3 :N=28-Tfosc/12 (4)如果fosc=12MHZ,以上公式可简化为方式1: N=216-T 方式2、方式
9、3 :N=28-T,例如:系统的时钟频率是12MHz,在方式1下,如果希望定时器/计数器T0的定时时间T为10ms,则初值N =216-T=65536-10000=55536如何将55536给两个8位寄存器TH0、TL0赋值呢?可将十进制数55536转换成四位十六进制数,将高2位送TH0,低2位送TL0。更简单的方法是:对于16位计数器来讲, 216等效为0,对于8位计数器,28等效为0,这样公式(3)、(4) 可简化为 N=-T,直接用下面两条语句就可以完成计数器初值的设置:TH0=-10000/256; /取-N的高8位TL0=-10000%256; /取-N的低8位例如:设系统的时钟频率
10、是12MHz,定时器工作于方式2,定时时间200us。根据前面分析,N=-T=-200,可直接用以下语句实现:TH0=-200;TL0=-200;,任务二、定时器应用,案例1 设时钟频率fosc=12MHz,用定时器T0在P1.0脚产生频率为20Hz的方波。,设计思想:在使用定时器/计数器时,首先应根据要求对工作方式进行初始化,然后计算出初始值。初始化的步骤通常是:,(1)向TMOD寄存器写入工作方式控制字。,(2)将计数器的初值写入TH0、TL0/TH1、TL1。,(3)启动定时器/计数器:将TR0/TR1置1。,(4)如果采用中断方式,还应将ET0/ET1、EA置1。,本题中,方波的频率为
11、20HZ,对应的周期为50ms,如果将定时时间设置为25ms,每隔25ms将P1.0脚取反即可产生如图3-3所示的方波。因为定时时间为25ms,如果采用方式2或方式3,其定时的最大时间不超过256us,因此选方式1比较恰当,根据前面的分析,计数器初值N=-25000。本例题是定时而非计数,且无需脚参与启停控制,故C/ =0,GATE为0,这样TMOD取0x01。,void main(void) TMOD=0x01; TH0=-25000/256; TL0=-25000%256;TR0=1;ET0=1; EA=1; while(1);void timer0(void) interrupt 1 T
12、H0=-25000/256;TL0=-25000%256;P10=P10;,T0工作于定时方式1,定时时间为25ms,启动定时器T0,允许定时器T0中断,单片机开中断,定时器T0中断服务程序,重新装入时间常数,P1.0求反,案例2 如图所示,P0口接8只发光二极管,编程使发光管轮流点亮,点亮时间为500ms,要求使用定时器T0来控制,设晶振为12MHz,设计思想我们可将P0口的初值设置为0xFE,对应于发光管D1亮,每隔500ms将P0的值循环左移一位,这一周期性的定时作业用T0来完成。当时钟频率为12MHZ时,在定时器的4种工作方式中,方式1的最大溢出时间最长,但即使在方式1,最大的溢出时间
13、也只有65.536ms,所以我们不能在每次中断时都执行上述移位操作,可以这样处理:将定时器T0的溢出时间设定为50ms,累计满10次中断正好500ms,才允许程序执行1次移位动作,,主函数 uchar count=0; /50ms定时中断次数计数器 void main(void) led=0xfe;TMOD=0x01; /T0工作于方式1TH0=-50000/256; /定时时间为50msTL0=-50000%256;ET0=1; /允许T0中断TR0=1; /启动T0定时EA=1; /CPU开中断while (1);,void time0(void) interrupt 1 TH0=-500
14、00/256; TL0=-50000%256;count+; if(count=10) count=0;led=_crol_(led,1);P0=led;,定时器T0中断服务程序,重新装入时间常数,每中断一次,计数器加1,10次中断为0.5秒,满10次变量led左移1位送P0口,案例3 用定时器的计数方式实现外部中断。如图所示,P0口控制8只发光管轮流点亮,发光管点亮时间为500ms,单脉冲电路控制发光管的移动方向,按下单脉冲按钮,发光管左移,再按下发光管右移 。,设计思想:当定时器/计数器工作于计数方式2时,如果将计数器的初值设置为全1,只要在计数输入端(T0或T1)送入一个计数脉冲,就可使
15、计数器产生溢出中断。如果将外部中断请求作为计数脉冲输入,就可利用计数中断的名义去完成外部中断服务。当应用中外部中断请求较多时,而单片机内部的定时器/计数器还有富余时,可采用这种方法来扩展外部断。因此本例的实质是将定时器的计数输入端T1扩展为外部中断请求输入,来控制发光管的移动方向。,具体方法:,1、初始化定时器/计数器T1计数工作方式2,即每次溢出中断后能自动装入计数初值。,2、计数器TH1、TL1的初值预置为0FFH。,3、程序中设置一个位变量,每当计数器T1产生溢出中断时求反,发光管左移还是右移取决于该变量是0还是1 。,void main(void) DIR=0;led=0xfe;TMO
16、D=0x61; TH0=-50000/256; TL0=-50000%256;TH1=0xff; TL1=0xff;ET0=1; ET1=1; TR0=1; TR1=1; EA=1; while (1);,T0为定时方式0,T1为计数方式2,定时时间为50ms,T1的初值为0xff,允许T0、T1中断,启动T0定时,启动T1计数,CPU开中断,void time0(void) interrupt 1 TH0=-50000/256; TL0=-50000%256;count+; if(count=10)count=0;if(DIR=1)led=_crol_(led,1);elseled=_cro
17、r_(led,1);P0=led;,重新装入时间常数,满10次为0.5秒,计数器清0,DIR为1左移,DIR为0右移,每中断一次,计数器加1,定时器T0中断服务程序,void time1(void) interrupt 3 DIR=DIR; ,每次按下按钮产生T1的溢出中断,将DIR求反,定时器T1中断服务程序,案例4 用定时器来控制数码管的动态显示 。设计思想:前面我们介绍LED动态显示程序时,每一位点亮后都需延时1ms左右,由于使用的是软件延时,大大降低了CPU的效率,而且当CPU因处理某件事务在一段时间内无法调用显示程序时,LED显示器将会出现黑屏或产生闪烁现象。用定时器控制数码管的动态
18、显示的方法:设定时器的定时时间为1ms,每次产生溢出中断,就根据位选变量bsel(主程序中将其初值设置为0xfe)送位选口,点亮一位数码管,并将bsel左移一位,为下一位的显示做好准备,4次定时中断分别完成4位数码管的显示,之后将bsel的值再次初始化为0xfe,准备下一轮显示。因此这种显示方式由定时中断自动完成,无需在主程序中调用。,主程序 uchar bsel , n; void main(void) n=0;bsel=0xfe; /首先显示数码管的最低位TMOD=0x01; /T0工作于定时方式2TH0=-1000/256; /T0的定时时间为1000usTL0=-1000%256; E
19、T0=1; /允许T0中断TR0=1; /启动定时EA=1; /开中断while(1); ,void time0(void) interrupt 1 TH0=-1000/256; TL0=-1000%256; P0=0xff; P2=bsel; P0=segtabdbufn; bsel=_crol_(bsel,1); n+; if(n=4) n=0;bsel=0xfe;,重新装入时间常数,定时器T0中断服务程序,熄灭数码管,防止上一位字符在当前位置显示出来。,点亮当前位,根据显示缓存查字段码,准备显示下一位,指向下一位要显示的数据,如果四位已显示完,重新从最低位开始,案例5 如图3-8所示,设
20、计一个电子秒表,每隔一秒加1,用三位数码管显示秒表的当前值(0-255),单脉冲按键用于启动、停止、清0控制,第一次按下该按键时启动秒表,第二次按键将暂停秒表,第三次按键将秒表清0,设置晶振频率为12MHZ。,设计思想:我们可用一个变量记录秒表的当前值,为了提高秒表精度,我们可令定时器T0工作于定时方式2,定时时间为250us,4000次溢出中断正好满1秒,让变量加1,同时将该变量转换为三位BCD码送到显示缓存,供显示程序显示。单脉冲直接和外部中断0的输入端INT0相连,每次按键产生中断请求,程序中设置一个变量,对按键动作进行计数,取值为1-3,分别对应于启动、停止和清0,程序根据该变量的值进
21、行相应的控制。,主程序 void main(void) count=0;sec=0;mode=0;IT0=1; /外部中断负跳变触发EX0=1; /允许外部中断0中断TMOD=0x02; /T0工作于定时方式2TH0=-250; /T0的定时时间为250usTL0=-250; ET0=1; /允许T0中断TR0=0; /停止定时器EA=1; /开中断while(1) disp(); ,定时器T0中断服务程序 void time0(void) interrupt 1 count+; /中断次数加1if(count=4000) count=0;sec+;dbuf2=sec/100; /将秒数计换成
22、三位BCD码dbuf1=(sec%100)/10;dbuf0=sec%10;,void int0(void) interrupt 0 mode+; /每次产生中断,mode加1if(mode=1) /mode为1,启动定时器工作TR0=1;else if(mode=2) /如果mode等于2,停止定时器工作TR0=0;else if(mode=3) /如果mode为3, mode=0;TR0=0;sec=0; /将秒计数器清0dbuf0=0;dbuf1=0;dbuf2=0; ,外部中断0中断服务程序,案例6 用单片机演奏歌曲兰花草,设计思想:单片机可以根据音符产生不同频率的脉冲信号,经驱动后来
23、控制蜂鸣器,让蜂鸣器产生与音符相应的声音,再把它们有机地组合起来,就可以发出一段音乐。,1、要产生与某音符相应的脉冲信号,只要算出该音频 的频率和周期,然后将该周期除2,即为半周期的时间。 将定时器的定时时间设定为这半周期时间,一旦定时器 溢出,就将P1.6脚求反,这样就能产生与音符频率对应 的脉冲信号。设单片机定时器工作在定时方式1,改变 时间常数TH0、TL0就可以产生与各种音符频率相应的脉 冲信号。设要产生的频率为fr,单片机机器周期的频率为fi,则 对应定时器时间常数为N。N=65536-fi2fr,2、用单片机演奏音乐时,可以这样来表示乐谱:乐谱由多个简谱码组成,以00结束,每个简谱
24、码用 一个字节来表示,字节的高4位为音符码,代表音符的 高低,低4位为节拍码,表示音符的节拍,下表3-2为节 拍与节拍码的对照。如果1拍为0.4秒,则1/4拍为0.1秒, 2/4拍为0.2秒,所以只要根据节拍来设定延时时间。设定时器T1的定时时间为50ms,二次溢出中断正好 为1/4拍,将节拍码乘2即为该节拍所需溢出中断的次数。,程序中,定时器T0用于控制音调的高低,定时器T1用于控制节拍的长短,各音符对应的时间常数放在数组tab1中,乐谱以简谱码的形式存储在数组tab3中,高四位为音符码,低四位为节拍码,0x00表示乐谱结束。变量m指向tab3中当前需演奏的简谱码,从中分解出高四位的音符码,
25、并从tab1中取出时间常数控制定时器T0产生与音符频率相应的方波;从简谱码的低四位分解出节拍码,控制定时器T1延时节拍所需的时间。,程序流程图,void main(void) uchar m; /用于指向音符的下标变量uchar c;TMOD=0x11; /定时器设定为方式1ET0=1; /允许T0中断ET1=1; /允许T1中断TH1=-50000/256; /50ms定时,节拍长短控制TL1=-50000%256;TR1=1;EA=1; ,while(1) m=0; /指向音符的下标变量while(tab3m) /如果乐谱末结束 c=tab3m4; /取高四位的音符 if(c!=0) /音
26、符不为0 th=tab1c-18; /根据音符取出时间常数tl=tab1c-1 /指向下个音符,TR0=0; /暂停5S后,重新开始 time=100; while(time!=0); ,void timer0(void) interrupt 1 using 1 TH0=th; /装入时间常数TL0=tl;SOUND=SOUND; /产生方波 ,void timer1(void) interrupt 3 TH1=-50000/256; /重新装入50ms的时间常数TL1=-50000%256;time-; /溢出中断次数减1 ,定时器T0中断服务程序,用于产生产生与音符频率相应的方波,定时器T
27、1中断服务程序,延时当前节拍所需的时间,案例7 调节发光二极管的亮度。亮度分为0-7共8级,亮度等级为0时,发光管熄灭;亮度等级为7时,发光管最亮。用KINC、KDEC两个按键调节当前的亮度。,设计思想:如图所示,如果在P0.0引脚产生一个周期为2ms的脉冲,如果每个周期内脉冲的低电平时间少,高电平时间长,发光管的亮度显然暗些,反之发光管要亮些,当P 0.0输出全为高,发光管就熄灭了。因此只要调节脉冲低电平的时间就可改变发光管的亮度。,程序中变量light表示亮度等级,取值在0-7之间,用KINC、KDEC按键对其加减调整,当light为0时,P0.0始终输出高电平。定时器T0的定时时间为25
28、0us,变量count对定时中断进行计数,取值也在0-7之间,当countlight时,是发光管点亮时间,P0.0输出低电平;当countlight时,P0.0输出高电平,发光管熄灭。,发光管熄灭,发光管亮度最低,发光管亮度最高,void main(void) TMOD=0x02; /T0工作于方式2TH0=-250;TL0=-250; /定时时间为250usET0=1; /允许T0中断TR0=1; /启动定时EA=1; /CPU开中断while (1) while(P2 /去抖动,void time0(void) interrupt 1 count+; /定时中断计数器加1count=cou
29、nt /否则,关闭发光管,定时器T0中断服务程序,任务四、电子钟的设计,设计一个电子钟,用四位LED显示器显示当前时、分,时、分之间用闪烁的分隔,按KSET键设置当前时间,长按KSET键设置闹铃时间,进入设置方式后,用KSET键来选择对时、分进行修改,当前修改的对象闪烁,KINC、KDEC分别对修改对象加/减1,长按KSET键结束设置。如果当前时间与闹铃时间一致,蜂鸣器发出“嘟”、“嘟”的闹铃声,LED显示器闪烁,闹铃时按任意一键,关闭蜂鸣器。,设计思想根据设计要求,我们可以将电子钟分为三种工作状态,用变量state表示:,正常走时状态LED显示器显示当前时间,系统上电后自动进入该方式。,闹铃
30、状态如果当前时、分与闹铃时、分相同,进入闹铃状态,此时蜂鸣器发出“嘟”、“嘟”的闹铃声,LED显示器闪烁,闹铃时按任意一键,关闭蜂鸣器,LED显示器停止闪烁,返回正常走时状态。,设置状态在走时状态用户按下kSET键则进入设置状态,用户通过KINC、KDEC键对当前时间或闹铃时间进行修改,最后长按KSET键退出设置状态,并返回到正常走时方式。在设置当前时间时应将定时器关闭,否则LED显示器上的显示值将被定时中断服务程序刷新;在设置闹铃时间时,当前时间仍需被定时中断服务程序刷新,只是不在LED显示器上显示出来。,定时器T0用来产生时钟和方波,程序中每隔250uS就产生一次定时中断, 4000次中断
31、正好满1秒。定时器T0每两次中断(500us)就令计数变量n加1,n满2000正好是1S,当n500后,令P1.0保持高电平,鸣叫停止,这样在1秒钟内 “嘟”持续1/4秒,停顿3/4秒,整个闹铃状态将产生60次“嘟”,除非用户按键关闭蜂鸣器。,调试方法与步骤,1、仔细检查硬件电路,确认硬件连接无误后,开始软件调试。,2、在Keil下新建项目,选择硬件仿真,设置好串口号及波特率,输入源程序,编译成功后进入调试状态。,3、全速运行程序,此时数码管应显示初始时间“12.25”,如果没有显示,应检查电路中的显示端口与程序中的定义是否一致,显示程序是否有问题。,4、按下(或长按)KSET键,显示器的高2
32、位应该闪烁,如果按键后无反应,应重点检查键盘电路的连接以及键盘程序getkey(),直到按下各键时,能得到对应的键值返回。,5、如果按下(或长按)KSET键后,程序能够进入当前时间或闹铃时间设置方式,应仔细检查KSET、KINC、KDEC键的功能是否与设计要求一致,最后长按KSET键能否退出设置方式。如果有问题,应进入set_time()仔细调试。,6、显示程序、键盘处理程序调试好后,全速运行程序,此时数码管显示的时间应每分 钟更新一次,如果时间始终不变,可检查程序是否能进入定时器中断服务程序,如果不能,说明定时器及中断的初始化可能有问题,否则可进一步观察当前时间time3及update标志是否相应变化。,7、如果能够正常走时,需进一步检测到了闹铃时间,数码管是否闪烁,蜂鸣器有无鸣叫,以及鸣叫时按键能否关闭声音。,