51系列单片机第4讲定时器/计数器AtmelAT89C51AtmelAT89C52定时器/计数器的主要特性•C51系列有2个定时/计数器(T0、T1)•C52系列有3个定时/计数器(T0、T1、T2)•通过编程设置,每个定时器/计数器可以根据系统时钟实现定时,也可以对外部信号计数(T0/P3.4、T1/P3.5)•每个定时器/计数器都有多种工作方式•每个定时器/计数器都会在预设定时计数时间到时产生溢出,可以通过查询或中断方式处理使用定时/计数器的步骤•方式选择——TMOD•初值设置——TH0、TL0(TH1、TL1)•中断允许——EA、ET0(ET1)•启动操作——TR0(TR1)定时/计数器的方式寄存器TMOD•可以一次定义两个定时/计数器的工作方式GATEC/TM1M0GATEC/TM1M0D7D6D5D4D3D2D1D0定时器1定时器0M1M0工作方式方式说明00013位定时/计数器01116位定时/计数器1028位自动重置定时/计数器113两个8位定时/计数器(仅T0有此方式)定时/计数器的方式寄存器TMOD•C/T=1计数方式•C/T=0定时方式•GATE——控制定时/计数器的启动是否受外部中断请求信号的影响•GATE=1——外部中断请求高电平启动计数•GATE=0——定时/计数器启动与外部中断请求无关一般情况下GATE=0定时/计数器的控制寄存器TCON用于控制定时/计数器的启动与溢出•TF1——溢出标志•TR1——启动位(=1:启动=0:停止)•TF0——溢出标志•TR0——启动位(=1:启动=0:停止)TF1TR1TF0TR0IE1IT1IE0IT0D7D6D5D4D3D2D1D0外部中断控制方式2——自动重置8位计数•M1M0=10•TL0——计数(最大定时256μs)•TH0——保存初值,便于自动重置例:•TH0=0x06•TL0=0x06•TL0计数到256发生溢出,计数值为250•然后重新置入0x06,继续计数在P1.0端口输出周期为500μs的方波//查询方式(晶振频率12MHz)#includereg52.hsbitP1_0=P1^0;voidmain(){TMOD=0x02;//定时器T0设置为方式2TH0=0x06;//256-6=250,定时250μsTL0=0x06;TR0=1;//启动定时器T0while(1){if(TF0==1)//如果检测到溢出,意味着定时时间到{TF0=0;//取消溢出标志P1_0=!P1_0;//输出高/低电平转换}}}250μs250μs说明•晶振频率12MHz,作为定时/计数器的基准信号•定时/计数器进行12分频,定时工作频率1MHz,定时工作周期1us•TMOD=0x02(00000010B)–T0工作于方式2(8位自动重置定时)–定时器方式GATEC/TM1M0GATEC/TM1M0D7D6D5D4D3D2D1D0定时器1定时器0说明•TL0=0x06•TH0=0x06•计数从6开始,每个工作周期(1us)加1,经过250us之后产生溢出,TF0==1•方式2为自动重置,将TH0的值再次装入TL0,继续计数•每次检测到TF0==1,就使P1^0的状态改变一次,从而产生了周期为500us的对称方波思考•设单片机晶振频率为12MHz,对于前例,如果要求T0输出的方波周期为200μs,计数初值应该怎样设置?机器周期=时钟周期*12=12/晶振频率=1μs计数初值=256-200/2=156(0x9c)TH0=0x9c;TL0=0x9c;在P1.0端口输出周期为500μs的方波//中断方式#includereg52.hsbitP1_0=P1^0;voidmain(){TMOD=0x02;//T0设置为方式2TH0=0x06;TL0=0x06;EA=1;//中断允许总控ET0=1;//允许定时器T0中断TR0=1;//启动定时器while(1);//等待中断的发生}voidtime0_int(void)interrupt1//定时器T0对应interrupt1{P1_0=!P1_0;}LED0以200ms间隔闪烁(方法一)#includereg52.hsbitLED0=P0^0;//LED0接在P0^0端口chari;voidmain(){TMOD=0x01;//方式1,16位定时/计数TH0=0xd8;TL0=0xf0;//0xd8f0=55536计数初值,定时10msEA=1;ET0=1;//定时器T0允许中断i=0;TR0=1;//启动定时器T0while(1);}voidtime0_int(void)interrupt1//定时器T0对应的就是interrupt1{TH0=0xd8;TL0=0xf0;//恢复计数初值,继续延时i++;//每计数一次,延时10msif(i==20)//若满足200ms{LED0=!LED0;//LED0闪烁一次i=0;//恢复初始状态}}LED0以200ms间隔闪烁(方法二)#includereg52.hsbitLED0=P0^0;chari;voidmain(){TMOD=0x01;//方式1,16位定时/计数TH0=0x3c;TL0=0xb0;//0x3cb0=15536计数初值,定时50msEA=1;ET0=1;i=0;TR0=1;while(1);}voidtime0_int(void)interrupt1//定时器T0对应的就是interrupt1{TH0=0x3c;TL0=0xb0;//恢复计数初值,继续延时i++;//每计数一次,延时50msif(i==4)//若满足200ms{LED0=!LED0;//LED0闪烁一次i=0;//恢复初始状态}}思考•方法一:定时10ms,i计数20次•方法二:定时50ms,i计数4次•这两种做法,哪种更好些?方法二更好些,因为充分利用了定时器的最大定时能力,减少了对CPU时间的占用练习•利用定时器T1产生定时时钟,由P0口控制8个LED依次逐个闪动,频率为10次/秒,循环重复程序代码(主程序部分)#includereg52.h#includeintrins.hunsignedcharLED,i;voidmain(){i=0;LED=0xfe;//LED0首先发光P0=LED;TMOD=0x01;//定时器方式1(16位定时)TH1=0x3c;//定时初值0x3cb0=15536TL1=0xb0;EA=1;//中断总控允许ET1=1;//定时器1中断允许TR1=1;//启动定时器1while(1);//等待中断发生}程序代码(中断服务程序部分)voidtime1_int(void)interrupt3//定时器1允许{TH1=0x3c;//重置定时50ms的初值TL1=0xb0;i++;if(i==20)//定时时间到,闪动一次{LED=_cror_(LED,1);P0=LED;i=0;//恢复初始状态}}练习•利用定时器产生定时控制信号,在8个LED上稳定地显示“01234567”•分析:–要实现稳定地显示,根据以往经验,切换速度要求不大于2ms–定时器完全可以代替以前使用的延时程序,这样就节省了CPU时间程序代码#includereg52.hunsignedcharcodetable[11]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};unsignedchari;voidmain(){i=0;TMOD=0x01;TH1=0xf8;TL1=0x30;//延时2ms,0xf830=63536EA=1;ET1=1;//允许定时器1中断TR1=1;//启动定时器1while(1);}voidtime1_int(void)interrupt3//定时器1产生interrupt3中断{TH1=0xf8;TL1=0x30;P0=table[i];//获取段位码P2=i;//选择LEDi++;if(i==8)i=0;//轮流显示了全部8个LED之后,回归原位}练习•利用定时器实现的定时间隔,在8个LED上稳定地显示电话号码“68752219”程序代码#includereg52.hunsignedcharcodetable[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};unsignedcharcodeNumber[8]={6,8,7,5,2,2,1,9};unsignedchari,j;voidmain(){i=0;TMOD=0x01;TH1=0xf8;TL1=0x30;EA=1;ET1=1;TR1=1;while(1);}voidtime1_int(void)interrupt3{TH1=0xf8;TL1=0x30;j=Number[i];P0=table[j];P2=i;i++;if(i==8)i=0;}练习•在8个LED数码管的右端从0开始显示数字,利用定时器控制,以200ms的间隔逐次加1,到达255之后归0,循环往复•分析:–本题的任务最好使用两个定时器来完成–T1的定时间隔为200ms,用来修正计数–T0的定时间隔为2ms,用来刷新LED主程序#includereg52.h#defineucharunsignedcharucharcodetable[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};ucharmydata[3]={0,0,0};uchari,number,d0;voidmain(){i=0;number=0;TMOD=0x01;TH1=0x3c;TL1=0xb0;EA=1;ET1=1;TR1=1;TH0=0xf8;TL0=0x30;ET0=1;TR0=1;d0=0;while(1);}定时器1的中断服务程序voidtime1_int(void)interrupt3{TH1=0x3c;TL1=0xb0;i++;if(i==40)//定时间隔5ms,乘以40倍{number++;mydata[2]=number/100;mydata[1]=number/10%10;mydata[0]=number%10;i=0;}}定时器0的中断服务程序voidtimer0_int(void)interrupt1{TH0=0xf8;TL0=0xf0;P0=table[mydata[d0]];//获取段位码if(d0==2)P2=5;//百位数显示在LED5elseP2=(d0==1)?6:7;//十位数显示在LED6d0=(d0+1)%3;}练习•在8个LED数码管上按右对齐的方式从0开始显示数字,利用定时器控制,以200ms的间隔逐次加1,到达255之后归0,循环往复练习•LED0的亮度分为0~9共10档可调,每按一次独立开关S17(连接到P34),LED0亮度增加一个档次,增加到10时则回到最低亮度数据定义与准备#includereg52.h#defineucharunsignedchar#defineuintunsignedintsbitLED0=P0^0;//要控制的LED灯sbitK1=P3^4;//控制亮度,每按一次增加一档亮度ucharscale=5;//占空比的低电平时间份额,总共10份uintn=0;定时器初始化voidtimer0_init(){TMOD=0x02;//T0为方式2,自动重置8位计数TH0=0x06;//晶振频率12MHz,定时250μsTL0=0x06;TR0=1;//启动定时器0ET0=1;//开放定时器0中断EA=1;//开放总控中断}定时器中断timer0()interrupt1//定时器T0中断服务程序{//每10次中断,LED0发光保持scale次staticuchartt;//保存当前时间在一秒中的比例位置TF0=0;//清除溢出标志,迎接下次中断tt++;//统计中断发生的次数if