精华内容
下载资源
问答
  • 利用51单片机定时器设计一个时钟

    千次阅读 2020-06-07 10:05:58
    利用51单片机定时器设计一个时钟 一、功能要求 1602液晶显示时间,且每秒更新,自动计时。 3个按键实现调节时、分、秒更能,可以定义为功能键、增加键、减小键。 当按键被按下时,蜂鸣器响一声提示。 利用AT24C...

    利用51单片机的定时器设计一个时钟
    一、功能要求

    1. 1602液晶显示时间,且每秒更新,自动计时。
    2. 用3个按键实现调节时、分、秒更能,可以定义为功能键、增加键、减小键。
    3. 当按键被按下时,蜂鸣器响一声提示。
    4. 利用AT24C02实现掉电记忆功能。
      二、 原理图设计
      在这里插入图片描述
      在这里插入图片描述
      三、源程序
    5. 在Keil中的同一个文件夹下新建一个AT24C02.h文件和Clock.c文件
    //*********************** AT24C02.h源程序***************************//
     bit write=0;		//写AT24C02的标志
    sbit SDA=P2^0;		//定义数据串行口,用于单片机通信
    sbit SCL=P2^1;	//定义时钟串行口,用于单片机通信
    
    void delayus()		//定义us级延时函数
    {;;}
    
    void start()	//起始信号
    {
    	SDA=1;
    	delayus();
    	SCL=1;
    	delayus();
    	SDA=0;
    	delayus();
    }
    
    void stop()//终止信号
    {
    	SDA=0;
    	delayus();
    	SCL=1;
    	delayus();
    	SDA=1;
    	delayus();
    }
    
    void ack()//应答信号
    {
    	uchar i;
    	SCL=1;
    	delayus();
    	while((SDA=1)&&i<250)i++;																	
    	SCL=0;
    	delayus();
    }
    
    
    void init_AT24C02()	//初始化
    {
    	SDA=1;
    	delayus();
    	SCL=1;
    	delayus();	
    }
    
    void write_byte(uchar dat)	//定义写1个字节程序
    {
    	uchar i,temp;
    	temp=dat;				//将数据赋值给temp。例如:dat为0x01,则temp=0x01=0000 0001。方便后续移位操作用。
    	for(i=0;i<8;i++)		//1个字节包括8位二进制数
    	{
    		temp=temp<<1;		//将temp左移1位,最低位补0
    		SCL=0;					//将SCL置0允许SDA数据变化
    		delayus();
    		SDA=CY;				//将最高位移出的CY位赋给SDA
    		delayus();
    		SCL=1;					//置1将数据写入
    		delayus();
    	}
    	SCL=0;					//置0允许数据变化,用于SDA置1释放总线。
    	delayus();
    	SDA=1;					//释放总线
    	delayus();			
    }
    
    uchar read_byte()	//读1个带返回值的字节
    {
    	uchar i,k;
    	SCL=0;				//置0允许数据变化
    	delayus();
    	SDA=1;				//假设SDA为1,也可以SDA=0。当SDA=0时,下面for语句会有所差别。
    	delayus();
    	for(i=0;i<8;i++)
    	{
    		SCL=1;					//上升沿时,IIC设备将数据放在sda线上,并在高电平期间数据已经稳定,可以接收啦
    		delayus();
    		k=(k<<1)|SDA;		//将k左移一位跟SDA或运算,再赋给k	
    		SCL=0;					//拉低SCL,使发送端可以把数据放在SDA上  
    		delayus();		
    	}
    	return k;
    }
    
    void write_add(uchar address, uchar dat)			//指定地址写一个字节
    {
    	start();
    	write_byte(0xa0);
    	ack();
    	write_byte(address);
    	ack();
    	write_byte(dat);
    	ack();
    	stop();
    }
    
    char read_add(uchar address)				//随机读取一个字节
    {
    	uchar dat;
    	start();
    	write_byte(0xa0);
    	ack();
    	write_byte(address);
    	ack();
    	start();
    	write_byte(0xa1);
    	ack();
    	dat=read_byte();
    	stop();
    	return dat;
    }
    //****************************Main主程序**************************************//
    #include <reg52.h>
    #define uchar unsigned char
    #define uint unsigned int
    #include"AT24C02.h"		//包含AT24C02头文件
    sbit s1=P3^0;
    sbit s2=P3^1;
    sbit s3=P3^2;
    sbit rd=P3^7;
    sbit lcden=P3^4;
    sbit rs=P3^5;
    sbit dula=P2^6;
    sbit wela=P2^7;
    sbit beep=P2^3;
    char shi,fen,miao;
    uchar s1num,count;
    uchar code table0[]="  2020-6-4 MON";			//字符2020前面3个空格
    uchar code table1[]="00:00:00";
    void delay_us()					//us级延时程序
    {;;}
    void delay_ms(uchar z)	//ms级延时程序
    {
    	uint i,j;
    	for(i=z;i>0;i--)
    		for(j=110;j>0;j--);
    }
    
    void buzzer()				//定义蜂鸣器程序
    {
    	beep=0;
    	delay_ms(100);
    	beep=1;
    
    }
    
    void write_com(uchar com)	//液晶写指令
    {
    	rs=0;
    	lcden=0;
    	P0=com;
    	delay_ms(5);
    	lcden=1;
    	delay_ms(5);
    	lcden=0;
    }
    
    void write_dat(uchar dat)	//液晶写数据
    {
    	rs=1;
    	lcden=0;
    	P0=dat;
    	delay_ms(5);
    	lcden=1;
    	delay_ms(5);
    	lcden=0;
    }
    
    void write_sfm(uchar add,uchar dat)		//写时分秒程序
    {
    	uchar shi,ge;
    	shi=dat/10;				//将时分秒分别分解成一个两位数,以便后续的位操作
    	ge=dat%10;
    	write_com(0x80+0x40+add);			//在液晶第2行写指令
    	write_dat(0x30+shi);						//0x30地址的数据为0
    	write_dat(0x30+ge); 
    }
    void init_1602()							//初始化液晶
    {
    	uchar num;
    	dula=0;
    	wela=0;
    	num=0;
    	s1num=0;
    	count=0;
    	rd=0;
    	lcden=0;
    	shi=0;
    	fen=0;
    	miao=0;
    	init_AT24C02_02();			
    	write_com(0x38);
    	write_com(0x0c);
    	write_com(0x06);
    	write_com(0x08);
    	write_com(0x01);
    	write_com(0x80);
    	for(num=0;num<14;num++)		//显示日期,加空格总共14个字节
    	{
    		write_dat(table0[num]);
    		delay_ms(5);
    	}
    	write_com(0x80+0x40+4);
    	for(num=0;num<8;num++)		//显示时分秒
    	{
    		write_dat(table1[num]);	
    		delay_ms(5);	
    	}
    	miao=read_add(1);	//首次上电从AT24C02中读取出存储器的数据,地址可以随机定义,只要整个程序用的地址相同即可。
    	fen=read_add(2);		
    	shi=read_add(3);
    	write_sfm(10,miao);	//在第十位写秒数据
    	write_sfm(7,fen);
    	write_sfm(4,shi);
    	TMOD=0x01;		//设置T0定时器为工作方式1
    	TH0=(65536-50000)/256;	//装初值
    	TL0=(65536-50000)%256;
    	EA=1;			//开总中断
    	ET0=1;		//开定时器0中断
    	TR0=1;		//启动定时器0
    }
    
    void keyscan()	//按键扫描程序
    {
    	if(s1==0)		//先定义功能键被按下,不然加减键为无效。
    	{
    		delay_ms(5);
    		if(s1==0)		//再次确认被按下,即去抖动
    		{
    			s1num++;	//记录功能键被按下次数
    			while(!s1);	//按键释放
    			buzzer();		//蜂鸣器短鸣提示
    			if(s1num==1)		//功能键按下1次
    			{
    				TR0=0;		//定时器停止计时
    				write_com(0x80+0x40+11);		//光标在11位处,调节11的大小可以进行光标位置设置
    				write_com(0x0f);						//光标闪烁
    			}	
    			if(s1num==2)
    			{
    				write_com(0x80+0x40+8);
    				write_com(0x0f);
    			}
    			if(s1num==3)
    			{
    				write_com(0x80+0x40+5);
    				write_com(0x0f);
    			}
    			if(s1num==4)		//功能键按下4次
    			{
    				s1num=0;		//按键次数清0
    				write_com(0x0c);		//取消光标闪烁
    				TR0=1;			//定时器继续计数
    			}
    		}
    	}
    	if(s1num!=0)		//只有功能键被按下,加减键才有效
    	{
    		if(s2==0)		//增加键被按下
    		{
    			delay_ms(5);
    			if(s2==0)		//再次确认增加键按下,去抖动
    			{
    				while(!s2);		//按键释放,只有按键释放了才可以认为一个完整的按键完成
    				buzzer();
    				if(s1num==1)		//功能键按下一次,此时选择调整秒
    				{
    					miao++;
    					if(miao==60)		//当秒数大于60就清0
    						miao=0;
    					write_sfm(10,miao);	//在第10位写秒,此时写的时一个两位数,调用的write_sfm()函数
    					write_com(0x80+0x40+11);	//光标定位在11位,由于前面已经定义光标闪烁,所以这里不用写write_com(0x0f)
    					write_add(1,miao);		//将变化的值写入AT24C02记忆起来,掉电恢复时可以显示此值
    				}
    				if(s1num==2)
    				{
    					fen++;
    					if(fen==60)
    						fen=0;
    					write_sfm(7,fen);
    					write_com(0x80+0x40+8);
    					write_add(2,fen);
    				}
    				if(s1num==3)
    				{
    					shi++;
    					if(shi==24)
    						shi=0;
    					write_sfm(4,shi);
    					write_com(0x80+0x40+5);
    					write_add(3,shi);
    				}
    			}
    		}
    		if(s3==0);		//减少键被按下
    		{
    			delay_ms(5);
    			if(s3==0)	//再次确认,去抖
    			{
    				while(!s3);
    				buzzer();
    				if(s1num==1)
    				{
    					miao--;	
    					if(miao==-1)//当秒数被减到-1时,秒数清0。
    						miao=59;
    					write_sfm(10,miao);
    					write_com(0x80+0x40+11);
    					write_add(1,miao);
    				}
    				if(s1num==2)
    				{
    					fen--;
    					if(fen==-1)
    						fen=59;
    					write_sfm(7,fen);
    					write_com(0x80+0x40+8);
    					write_add(2,fen);
    				}
    				if(s1num==3)
    				{
    					shi--;
    					if(shi==-1)
    						shi=23;
    					write_sfm(4,shi);
    					write_com(0x80+0x40+5);
    					write_add(3,shi);
    				}
    			}
    		}			
    	}		
    }
    
    void main()		//主程序
    {
    	init_1602();		//初始化1602液晶
    	while(1)	//进入while语句不停的进行按键扫描,来确认是否有按键按下
    	{
    		keyscan();
    	}
    }
    
    void timer0() interrupt 1	//定时器0工作方式1
    {
    	TH0=(65536-50000)/256;	//装初值,因为工作方式1无法自动重装初值,所以这里再次装初值。
    	TL0=(65536-50000)%256;
    	count++;								//将1s拆分成20个50ms,这里对50ms进行计数来累加到1s
    	if(count==20)						//当计数20次时,即为1s
    	{
    		count=0;							//清0重新计数
    		miao++;
    		if(miao==60)					//这里是没有按键被按下时,时钟自动的计数
    		{
    			miao=0;
    			fen++;
    			if(fen==60)
    			{
    				fen=0;
    				shi++;
    				if(shi==24);
    				{
    					shi=0;
    				}
    				write_sfm(4,shi);			//在第4位写小时的数据
    				write_add(3,shi);			//写入AT24C02进行掉电记忆,这里的3为地址,整个程序都要相同。
    			}
    			write_sfm(7,fen);
    			write_add(2,fen);
    		}
    		write_sfm(10,miao);
    		write_add(1,miao);
    	}
    }
    

    1602字符库
    在这里插入图片描述

    展开全文
  • 基于51单片机定时器做普通时钟可调.rar
  • C51单片机运用定时器T0中断实现电子时钟,含有原理图和相关程序设计。
  • 51单片机定时器做时钟

    千次阅读 多人点赞 2019-02-24 13:01:29
    2、用定时器 定时器代码 #include &lt;reg52.h&gt; sbit LED=P1^0; unsigned char numl=0; int flag=0; void main() { TMOD=0x01; //定时器工作模式 TH0=(65536-50000)/256; ...

    要求

    1、用8位数码显示管显示00-00-00表示小时-分钟-秒。

    2、用定时器

    定时器代码

    #include <reg52.h>
    
    sbit LED=P1^0;
    unsigned char numl=0;
    int flag=0;
    
    void main()
    {
    	TMOD=0x01;										//定时器工作模式
    	TH0=(65536-50000)/256;				//定时高8位
    	TL0=(65536-50000)%256;				//定时低8位
    	TR0=1;												//打开定时器0
    	ET0=1;												//打开定时器0中断
    	EA=1;													//打开总中断
    	while(1)
    	{
    		if(flag)
    		{
    			flag=0;										//flag清零,下一次flag=1的时候进入
    			LED=!LED;									//LED取反输出
    		}
    	}
    }
    
    void timer0() interrupt 1
    {
    	TH0=(65536-50000)/256;				//重置定时高8位
    	TL0=(65536-50000)%256;				//重置定时低8位(50ms)
    	numl++;
    	if(numl==20)										//判断计数到1s
    	{
    		numl=0;
    		flag=1;											//flag置1 --> 主函数里执行取反
    	}
    }
    

    时钟程序:

    #include <reg52.h>
    sbit duan=P2^2;
    sbit wei=P2^3;
    unsigned char num[11]=//0到9和-显示数据提前储存
    {
    	0xc0,0xf9,0xa4,0xb0,0x99,
    	0x92,0x82,0xf8,0x80,0x90,
    	0xbf
    };
    void delay_ms(unsigned int num)//1ms延时
    {
    	unsigned int x,y;
    	for(x=0;x<110;x++)
    		for(y=0;y<num;y++);
    }
    void delay_10us(unsigned int num)//10us延时
    {
    	unsigned int x;
    		for(x=0;x<num;x++);
    }
    unsigned char numl=0;
    int flag=0;
    void timer0() interrupt 1
    {
    	TH0=(65536-50000)/256;				
    	TL0=(65536-50000)%256;				
    	numl++;
    	if(numl==20)										
    	{
    		numl=0;
    		flag=1;											
    	}
    }
    void main()
    {
    	int i,hg=0,hd=0,mg=0,md=0,sg=0,sd=0,pd;
    	wei=0;
    	duan=0;
    	TMOD=0x01;										
    	TH0=(65536-50000)/256;				
    	TL0=(65536-50000)%256;				
    	TR0=1;												
    	ET0=1;												
    	EA=1;													
    	while(1)
    	{
    		if(flag)
    		{
    			flag=0;										
    			sd++;									
    		}
    			if(sd==10)
    			{
    				sd=0;
    				sg++;
    			}
    			if(sg==6)
    			{
    				sg=0;
    				md++;
    			}	
    			if(md==10)
    			{
    				md=0;
    				mg++;
    			}	
    			if(mg==6)
    			{
    				mg=0;
    				hd++;
    			}
    			if(hd==10)
    			{
    				hd=0;
    				hg++;
    			}
    			if(hg==2&&hd==4)
    			{
    				hd=0;
    				hg=0;
    			}
    			for(i=0;i<8;i++)
    			{
    				wei=1;
    				P0=0x01<<i;
    				wei=0;
    				duan=1;
    				switch(i)
    				{
    					case 0:P0=num[hg];break;
    					case 1:P0=num[hd];break;
    					case 2:P0=num[10];break;//输出-
    					case 3:P0=num[mg];break;
    					case 4:P0=num[md];break;
    					case 5:P0=num[10];break;
    					case 6:P0=num[sg];break;
    					case 7:P0=num[sd];break;
    				}		
    				duan=0;
    				delay_10us(150);
    				wei=1;
    				P0=0x00;
    				wei=0;
    				duan=1;
    				P0=0xff;
    				duan=0;
    				delay_10us(30);
    			}
    	}	
    }

     

    展开全文
  • 里面有俩个源程序,一个定时器时钟一个是定时器LED。
  • 使用51单片机内部定时器实现时间计算,显示时分秒
  • 实现数字时钟的方式是使用单片机定时器来实现的,让数码管动态显示
  • 51单片机定时器时钟程序、C语言、LCD1062显示
  • 通过51单片机定时器做一个时钟能显示24小时,比较精确,8个八段数码管显示,数码管动态扫描
  • 一个很精准的定时,数字时钟,60秒的时钟
  • 基于51单片机(AT89C52)设计的时钟,资源包含C语言源代码,可烧写的Hex文件,Proteus仿真图。
  • 该资料为单片机定时器制作的实时时钟仿真,还包含了C程序,对学习单片机的朋友非常的有用。
  • 基于单片机定时器/计数器的时钟设计以及计数设计第部分 前言第二部分 定时器/计数器的原理MCS-51定时器/计数器的结构定时器/计数器相关特殊功能寄存器定时器/计数器工作方式第三部分 定时器/计数器编程方法第四...

    第一部分 前言

      本次单片机基础实验将会通过在Proteus软件中画原理图,然后Keil软件下编写源程序并编译形成可执行文件.hex,下载源程序,进行Protues和Keil的联合仿真运行,最终对单片机仿真有初步的认识。 通过本篇文章定时器/计数器T0、T1的工作方式选择和编程方法。学习Proteus了解定时器/计数器中断服务程序的设计方法

    以下是本篇文章正文内容,所设计的案例可供参考


    第二部分 定时器/计数器的原理

    MCS-51定时器/计数器的结构

      MCS-51单片机根据不同型号,其片内定时器/计数器数目不同。8051单片机有两个16位定时器/计数器寄存器:Timer0(T0定时器)与Timer1(T1定时器1)。8052除这两个定时器/计数器外还增加了1个Timer2(T2定时器),而这3个都可设置为定时器或事件计数器。当其作为“定时器”功能时,它是对标准时钟计数,每个时钟周期寄存器自动增1。由于MCS-51单片机的一个机器周期由12个振荡器周期组成,计数速率是振荡器频率的1/12。作为“计数器”功能,寄存器在响应相应的外部输入引脚T0、T1或T2(在8052中)由1至0的转变而增1。不论是“定时器”还是外部事件“计数器”,其工作原理是一样的,即定时器/计数器电路中的内部计数器从某一预定值(此值是可编程的)开始计数,当累计到最大值时产生溢出,并同时会建立一个相应的溢出标志(即中断标志位)。除了“定时器”或“计数器”选择外,定时器0与定时器1有4种工作方式需要选择。在8052单片机中的定时器T2有3种操作方式:“捕获”、“自动重装入”与“波特率生成器”。下面主要以AT89C51单片机为例讲解定时器/计数器的基本结构。
    在这里插入图片描述

    图1 单片机的定时器/计数器结构框图

      AT89C51单片机片内定时器/计数器的结构如图1所示。定时器内部实质上是16位加法计数器,其控制电路受软件控制。当用作定时器时,对机器周期计数,每过一个机器周期,计数器加1。由于每个机器周期包含12个振荡信号周期,所以加1计数器的计数频率为振荡器信号频率的1/12。当用作计数器功能时,加1计数器的计数脉冲取自外部输入端T0(P3.4)和T1(P3.5),只要这些引脚上有从“1”到“0”的负跳变,计数器就加1。CPU在每个机器周期的S5P2时刻对外部输入状态进行采样,计数器加1的执行是在检测到跳变后的下一个机器周期的S3P1时刻。由于需要两个机器周期来识别一个从“1”到“0”的负跳变,所以最大计数频率为振荡信号频率的1/24。而外部时钟脉冲持续为0和为1的时间不能少于一个机器周期。
      两个可编程的16位定时器 /计数器T0 和 T1
      - T0 = TH0(高8位) + TL0(低8位)
      - T1 = TH1(高8位) + TL1(低8位)

    定时器/计数器相关特殊功能寄存器

    • TMOD:控制定时器/计数器的工作方式
    • TCON:控制定时器/计数器的运行
    • IE、IP:定时器/计数器的中断控制

    1. TMOD
      TMOD用来选择定时器/计数器的工作模式和工作方式,它的字节地址是89H,但该寄存器不能进行位寻址。在这里插入图片描述

    • GATE=0,运行控制位 TR0/TR1(=1) 控制 T0/T1 启动;
    • GATE=1,INT0/1 (=1) 及运行控制位TR0/TR1(=1)控制T0/T1启动,可用于外部脉冲宽度测量
    • C/T=0,用作定时器(定时功能通过计数实现,计数脉冲来自内部系统时钟输入,一个机器周期产生一个计数脉冲);
    • C/T=1,用作计数器(对外部脉冲计数,外部输入脉冲发送负跳
      变时加1)

    T0模式

    M1M0功能
    0013 位定时器/ 计数器,TL1 低5 位 + TH1全8 位,最大值为8192
    0116 位定时器/ 计数器,TL1、TH1 全用,最大值65.536ms;
    108 位自动重装载定时器,当溢出时将TH1 存放的值自动重装入TL1,最大值为256;
    11定时器/ 计数器1 此时无效(停止计数)

    T1模式

    M1M0功能
    0013 位定时器/ 计数器,TL1 低5 位 + TH1全8 位,最大值为8192
    0116 位定时器/ 计数器,TL1、TH1 全用,最大值65.536ms;
    108 位自动重装载定时器,当溢出时将TH1 存放的值自动重装入TL1,最大值为256;
    11定时器0 为双8 位定时器/ 计数器,TL0 作为一个8 位定时器/ 计数器,通过标准定时器0 的控制位控制,TH0 仅作为一个8 位定时器,由定时器1 的控制位控制。

    2.TCON
      TCON寄存器的字节地址为88H,可进行位寻址。高4位分别为定时器/计数器的启动控制和溢出中断标志,低4位与外部中断控制有关,
    在这里插入图片描述
    各标志位的功能:

    1. IT0—选择外部中断请求0为跳沿触发方式还是电平触发方式:
    IT0=0,为电平触发方式。
    IT0=1,为跳沿触发方式。
      可由软件置“1”或清“0”。
    2. IE0—外部中断请求0的中断请求标志位。
    IE0=0,无中断请求。
    IE0=1,外部中断0有中断请求。
    当CPU响应该中断,转向中断服务程序时,由硬件清“0”IE0
    **3. IT1—外部中断请求1为跳沿触发方式还是电平触发方式,意义与IT0类似。

    1. IE1—外部中断请求1的中断请求标志位,意义与IE0类似。
    2. TF0 —T0溢出中断请求标志位。**
        T0计数后,当最高位产生溢出时,由硬件置“1”TF0,向CPU申请中断,CPU响应TF0中断时,清“0”TF0,TF0也可由软件清0。

    6. TF1—T1的溢出中断请求标志位,功能和TF0类似。
       TR1、TR0 2个位与中断无关,仅与定时器/计数器T1和T0有关。

    3.IE
      中断允许寄存器:CPU对中断源的开放或屏蔽,由片内的中断允许寄存器IE控制。字节地址为A8H,可位寻址。格式如下:
    在这里插入图片描述
    IE中各位的功能如下:

    1. EA:中断允许总控制位
    EA=0:CPU屏蔽所有的中断请求(CPU关中断) ;
    EA=1:CPU开放所有中断(CPU开中断) 。
    五个中断源的中断请求是否允许,还要由IE中对应的5个中断请求允许控制位的状态来决定。

    2. ES:串行口中断允许位
    ES=0:禁止串行口中断;
    ES=1:允许串行口中断。

    3. ET1:定时器/计数器T1的溢出中断允许位
    ET1=0:禁止T1溢出中断;
    ET1=1:允许T1溢出中断。

    4. EX1:外部中断1中断允许位
    EX1=0:禁止外部中断1中断;
    EX1=1:允许外部中断1中断。

    5. ET0:定时器/计数器T0的溢出中断允许位
    ET0=0:禁止T0溢出中断;
    ET0=1:允许T0溢出中断。

    6. EX0:外部中断0中断允许位。
    EX0=0:禁止外部中断0中断;
    EX0=1:允许外部中断0中断。

    MCS-51复位后,IE清0,所有中断请求被禁止。若使某一个中断源被允许中断,除了IE相应的位被置“1” ,还必须使EA位=1。改变IE的内容,可由位操作指令或字节操作指令来实现。


    定时器/计数器工作方式

      在MCS-51系列单片机中,定时器/计数器(T0、T1)具有多种工作方式,当选择工作方式不同,定时/计数器的使用方法差别很大。MCS-51单片机片内的定时器/计数器可以通过对特殊功能寄存器TMOD中的控制位 的设置来选择定时器方式或计数器方式;通过对M1 M0两位的设置来选择定时器/计数器的四种工作方式,下面具体讲解定时器/计数器的工作方式。

    1. 工作方式0

      定时器/计数器0、1在工作方式0时的电路逻辑结构见图9-2。工作方式0(M1 M0 = 0 0)是13位计数结构的工作方式,其计数器由TH的全部8位和TL的低5位构成,TL的高3位不使用。当 =0时,定时器/计数器0、1处于定时工作方式,多路开关接通振荡脉冲的12分频输出,13位计数器依次进行计数。当 =1时,定时器/计数器0、1处于计数工作方式,多路开关接通计数引脚(T0),外部计数脉冲由脚T0输入。当计数脉冲发生负跳变时,计数器加1。
      当TL的低5位溢出时,都会向TH进位,而全部13位计数器溢出时,则会向计数器溢出标志位TF0进位。同时,GATA位的状态决定定时器运行控制取决于TR0一个条件还是TR0和/INT0引脚这两个条件。当GATA=1时,由于GATA信号封锁了与门,使引脚/INT0信号无效。而这时候如果TR0=1,则接通模拟开关,使计数器进行加法计数,即定时/计数器工作。而TR0=0,则断开模拟开关,停止计数,定时器/计数器不能工作。当GATA=0时,与门的输出端由TR0和INT0电平的状态确定,此时如果TR0=1,INT0=1与门输出为1,允许定时器/计数器计数,在这种情况下,运行控制由TR0和INT0两个条件共同控制,TR0是确定定时/计数器的运行控制位,由软件置位或清“0”。

    在这里插入图片描述
      在工作方式0下,计数器的计数值X范围为1~8192(2131)。由于MCS-51单片机的T0和T1采用加计数,因此TH0(TH1),TL0(TL1)的初值N=8192-X。如当计数值X=1000,则计数初值N=7192=1C18H,那么TH0(TH1),TL0(TL1)的值分别为0E0H和18H。由于TL0(TL1)为低5为有效,所以该计数初值N不能简单地分成高8位和低8位赋值给TH0(TH1),TL0(TL1)。当计数初值N=7192=1C18H,其二进制编码如下

    1C18H=0001 1100 0001 1000B

      再将16位的二进制编码去除最高的3位,保留后面13位,并取低5位写入到TL0(TL1),高8位写入到TH0(TH1),具体操作如下

    1C18H=0001 1100 0001 1000 = 1110 0000 11000B

      其中,11000B是TL0(TL1)对应的低5位,其16进制编码为18H,而1110 0000这8位为TH0(TH1)的内容,其16进制编码为0E0H。
      当定时器/计数器工作于方式0且确定了定时时间T1后,其计数初值N的计算公式为

    N=8192-(T1×fosc/12)

      式中,fosc为系统时钟振荡频率。
      假设单片机的晶振选为12MHz,需要用T0进行2ms定时控制,则T0的初值N计算为

    N=8192-T1×fosc/12=8192-2×10-3×12×106/12=6192=1830H=0001 1000 0011 0000B

      则对应的13位二进制编码为1100 0001 1 0000则TH0=0C1H,TL0=10H

    2. 工作方式1

    定时器/计数器0、1工作于方式1时,其电路逻辑结构如图所示。
    在这里插入图片描述

      工作方式1(M1 M0=0 1)是16位计数结构的工作方式。方式0和方式1的区别仅在于计数器的位数不同,方式0为13位,而方式1则为16位,由TH0作为高8位,TL0为低8位,有关控制状态字(GATA、TF0、TR0)和方式0相同。
      在工作方式1下,计数器的计数值X范围是:1~65536
      当定时器/计数器工作于方式1且确定了定时时间T1后,其计数初值N的计算公式为:

    N=65536-(T1×fosc/12)

      则写入到8位寄存器TH0(TH1),TL0(TL1)值分别为
      TH0(TH1)=N/256
      TL0(TL1)=N%256

    3. 工作方式2

      当M1 M0=1 0时,定时器/计数器0、1处于工作方式2,此时其电路逻辑结构如图9-4所示。以定时/计数器0为例,定时/计数器1与之完全一致。
    在这里插入图片描述

    4. 工作方式3

      当M1 M0 =1 1时,定时器/计数器工作于方式3下。方式3只适用于定时器T0,若将T1置为方式3,则它将停止计数,其效果相当于置TR1=0,即关闭定时器T1。当T0工作在方式3时,TH0和TL0被分成两个相互独立的8位计数器,其电路逻辑结构如图所示。
    在这里插入图片描述
      在工作方式3模式下,TL0既可以作为计数器使用,也可以作为定时器使用,定时器/计数器0的各控制位和引脚信号全归它使用。其功能和操作与方式0或方式1完全相同。但TH0的功能受到限制,只能作为简单的定时器使用,而且由于定时器/计数器0的控制位已被TL0占用,因此只能借用定时器/计数器1的控制位TR1和TF1,也就是以计数溢出去置位TF1,TR1则负责控制TH0定时器的启动和停止。由于TL0既能作定时器也能作计数器使用,而TH0只能作定时器使用而不能作计数器使用,因此在方式3模式下,定时/计数器0可以构成两个定时器或者一个定时器和一个计数器。

    第三部分 定时器/计数器编程方法

    定时器的初始化编程包括:

    1. 设置定时器工作模式(设置TMOD);
    2. 设置定时器计数初值(设置THx/TLx);
    3. 允许定时器中断(IE);
    4. 启动定时器(TCON)。

    例如:

    • 设置定时器工作模式(TMOD不能位寻址)
      TMOD = 0x01; T0工作在模式 1下,16位定时器
    • 设置定时器计数初值
      计算初值公式 (T0定时模式2,8):
      TH0=(28- 计数值) ; \ 8192 1s, 1us=110^6 ,50000,50ms
      TL0=(28 -计数值);
      计数值=定时时间/ 机器周期; 如果时钟频率fosc=12MHZ,则机
      器周期=12
      时钟周期=12*1/fosc=1us。 6mhz,
      PS:如果要求定时器按某固定时间间隔不断触发,需要在中断服务程
      序中再次设置定时器计数初值(模式2除外),否则,中断服务程序以该模
      式下的最大定时值工作。
    • 允许定时器中断(中断号)
      EA=1;ET0=1
    • 启动定时器
      定时器T0:TCON=0x10 或者 TR0=1

    第四部分 定时器/计数器仿真设计

    设计案例一

      基于上述原理可以设计一个60s计时的秒表,完成秒表电路的设计与编程调试。

    原理图

    在这里插入图片描述
      定时器T0或T1实现秒表的计时功能,工作模式不限,计时60s后自动从0开始重新计时, INT0中断方式实现秒表的启动和暂停计时; INT1中断方式实现秒表的计时重置。

    动态仿真结果

    在这里插入图片描述
    在这里插入图片描述

    代码

    Keil代码如下:

    #include<reg51.h>
    #define uchar unsigned char
    #define uint unsigned int
    sbit duan = P2^0;
    sbit wei = P2^1;
    sbit int0=P3^2;
    sbit int1=P3^3;
    uchar code LED_D[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
    uchar code LED_W[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
    uchar count=0;
    uchar Time=0;
    
    void Delay(uint x);//延迟函数
    void Display2(uchar W1, Time);//显示函数
    void QP();//清屏函数
    //主函数
    void main()
    {
       QP(); 
       TMOD=0x01;//设置定时器工作方式  
       TH0=(65536-50000)/256;//计算初值,高8位
       TL0=(65536-50000)%256;//计算初值,第8位
       ET0=1;
       EA=1;  //开总中断 
       while(1)
        {            
           if(int0==0)
       {Delay(100);
       if(int0==0){
       TR0=!TR0;
       while(int0==0);}}
       if(int1==0){
       Delay(100);
       if(int1==0)
       {Time=0;
       }}
       Display2(6,Time);    
     }
     }
    void LED_Flash() interrupt 1//中断函数
    {
       EA=0;//关总中断
       TH0=(65536-50000)/256;//重装初值 
       TL0=(65536-50000)%256;//重装初值
       if(++count==20)     //开始计数
         {
    	  count=0;
    	 Time++;
    	 if(Time==61)
    	 Time=0;  
     }
        EA=1;
    }
    void Int0() interrupt 0
    {
        if(int0==0)
       {Delay(100);
       if(int0==0)
       {
       TR0=!TR0;
       while(int0==0);
       }
       }
    }
    void Int1() interrupt 2//中断函数
    { EA=1;
     ET0=1;
        if(int1==0){
       Delay(10);
       if(int1==0)
       {Time=0;
       }
       }
    }
    void Display2(uchar W1, Time)//显示函数
    {
    	 uchar shi,ge;
     	 shi = Time/10;
    	 ge = Time%10;
    	 P0 = LED_D[shi];
    	 duan = 1;   duan = 0;
    	 P0 = LED_W[W1];
    	 wei = 1; 
    	 wei = 0;
    	 Delay(3);
    	 QP(); 
    	 P0 = LED_D[ge];
    	 duan = 1;
    	 duan = 0; 
    	 P0 = LED_W[W1+1];
    	 wei = 1;
    	 wei = 0; 
    	 Delay(3); 
    	 QP(); 
    } 
    void QP()
    {
    	 P0=0x00;
    	 duan = 1; duan = 0; 
    	 wei = 1; wei = 0;
    }
    void Delay(uint x)   //延时
    {
    	 uchar t;
    	 while(x--) for(t=0;t<110;t++);
    }
    

    设计案例二

    利用T0、T1计数器功能,实现按键计数,计数值分别在2个4位数码管上显示。

    原理图

    • 利用定时器T0工作模式2,中断方式,计数初值为0,数码管显示3位数
    • T1工作模式不限,非中断方式,计数初值为256,数码管显示3位数
    • INT0中断方式实现T0、T1计数清零
      在这里插入图片描述

    动态仿真结果

    在这里插入图片描述
      通过不同的按键来实现对应的计数功能,利用定时器的计数功能可以使得单片机得到快速响应。

    代码

    #include<reg51.h>
    #define uchar unsigned char
    #define uint unsigned int
    sbit duan = P2^0;
    sbit wei = P2^1;
    uchar code LED_D[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
    uchar code LED_W[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
    uint count1,count2;   
    void Delay(uint x);
    void Display2(uchar W1,uint count);
    void QP();
    void main()
    {
    	   QP(); 
    	   EA=1; //开总中断
    	   TMOD=0x46; //设置定时器工作方式   
    	   TH0=TL0=0; //设定初值
    	   TH1=1;TL1=0;
    	   ET0=1;
    	   EX0=1;
    	   IT0=1;
    	   TR0=1;
    	   TR1=1;
    	   while(1)
    	    { 
    	      if(TF1==1) TF1=0;
    	 count1 = TL0;
    	 count2 = TH1*256+TL1;
    	 Display2(1,count1);          
    	 Display2(5,count2);       
     }
    }
    void Flash() interrupt 1
    {
    	   EA=0;
    	   TL1=0;
    	   EA=1;
    }
    void Clear() interrupt 0
    {
       TL0=0; 
       TL1=0;
    }
    void Display2(uchar W1, uint count)
    {
    	 uchar  bai,shi,ge;
    	 bai = count%1000/100;
    	 shi = count%100/10;
    	 ge = count%10;
    	 P0 = LED_D[bai];
    	 duan = 1;   duan = 0;
    	 P0 = LED_W[W1];
    	 wei = 1; 
    	 wei = 0;
    	 Delay(3);
    	 QP(); 
    	 P0 = LED_D[shi];
    	 duan = 1;
    	 duan = 0; 
    	 P0 = LED_W[W1+1];
    	 wei = 1;
    	 wei = 0;
    	 Delay(3); 
    	 QP();
    	 P0 = LED_D[ge];
    	 duan = 1;
    	 duan = 0;
    	 P0 = LED_W[W1+2];
    	 wei = 1;
    	 wei = 0;
    	 Delay(3); 
    	 QP(); 
    }  
    void QP()
    {
    	 P0=0x00;
    	 duan = 1; duan = 0; 
    	 wei = 1; wei = 0;
    }
    void Delay(uint x) 
    {
    	 uchar t;
    	 while(x--) for(t=0;t<110;t++);
    }
    

      以上就是本次定时器/计数器的全部内容,希望通过本次的讲解能够加深大家对于定时器/计数器以及中断的理解。文章的内容可能存在部分不足之处,如有错误,请在评论区指出,谢谢。如果大家觉得文章有用,麻烦点赞关注,感谢支持,后续将会推出单片机的进阶设计文章。

    展开全文
  • 利用51单片机自带定时器,实现时钟,可调时。电路简单。
  • 单片机定时器中的实时时钟(RTC)

    千次阅读 2020-09-03 16:37:43
    、实时时钟概述实时时钟的基本功能是产生一秒的间隔并保持连续计数。如下图所示,该时序图描述了RTC的基本功能。 还显示了程序功能A,该功能读取秒计数器并安排事件B在未来三秒发生。此操作称为警报。请注意...

    实时时钟(RTC)是专用于维持一秒时基的计时器。此外,RTC通常用于在软件或硬件中跟踪时钟时间和日历日期。RTC的许多功能是非常专业的,是维持高精度和非常可靠的操作所必需的。单片机外部有RTC设备,可与I2C或SPI总线接口。

    一、实时时钟概述

    实时时钟的基本功能是产生一秒的间隔并保持连续计数。如下图所示,该时序图描述了RTC的基本功能。

     

    还显示了程序功能A,该功能读取秒计数器并安排事件B在未来三秒发生。此操作称为警报。请注意,秒计数器连续运行,并且不会停止和启动。对于RTC有两个主要要求,分别是准确性和连续运行。下图为RTC的常见硬件功能:

     

    RTC通常具有自己的内部振荡器和外部晶体,并可以选择使用外部频率基准。所有时钟源均以32768Hz运行。外部时钟源允许使用非常精确和稳定的设备,例如TCXO(温度补偿晶体振荡器)。

    使用多路复用器选择时钟源,并将其输入到预分频器中,该预分频器将时钟分频为32768(215),以产生一秒的时钟。

    基本上RTC都具有秒计数器,通常为32位或更多。一些RTC具有专门的计数器来跟踪一天中的时间和日历日期。

    没有时间和日期计数器的基本RTC为此目的使用软件。常见的选择是来自输出引脚的1Hz方波。RTC将具有多个可能的事件来生成处理器中断。

    RTC通常具有专用的电源引脚,以允许在单片机的其余部分掉电时进行操作。该电源引脚通常连接到电池或单独的电源。

    二、RTC精度和频率补偿

    RTC的精度取决于32,768Hz时钟源。在设计良好的晶体振荡器中,误差的主要来源是晶体。外部TCXO可用于高度精确的定时,或者特殊的频率补偿技术可用于较便宜的晶体和内部振荡器。晶体误差的三个主要来源:

    1.初始电路和晶体容差。
    2.晶体随温度漂移。
    3.晶体老化。

    下图为一些与RTC精度有关的概念:

     

    该图上的深蓝色迹线显示了典型的初始公差以及随温度的变化。粉色轨迹仅显示温度误差。补偿温度的关键在于,晶体的行为是众所周知的,并可以通过二次方程式进行预测。如果在制造电路板并且已知温度之后测量初始误差,则可以补偿最大的误差源。

    经过仔细补偿后,黄色面积是准确度的合理目标。请记住,一年中1 ppm大约需要30秒。晶体老化难以弥补。幸运的是,老化通常每年只有几ppm。

    三、如何更改RTC时间

    目前,有两种方法可以更改RTC的时序,作为系统的一部分以补偿错误。如前面所讲的,在秒计数器的每个周期内,可得到预分频器所计数的振荡器周期数。

    前两秒是通常的32768个周期。该软件使用温度读数和初始误差来确定振荡器正在快速运行,并且32768个周期实际上为0.99990秒。为了补偿这个小误差,该软件会告诉RTC每四秒钟将预分频器的模数更改为32781,以增加一些时间。 下图为预分频器计数的振荡器周期:

     

    此技术的优势在于,从第二秒到第二秒的时间间隔变化很小。但是,该技术需要一个可调节的预分频器和其他寄存器来保存特殊的预分频计数和特殊计数应用之间的秒数。

    如果RTC没有特殊的预分频器来调整时序怎么办?下图为另一种方法,该方法没有预分频器:

     

    在这种情况下,框中的数字是秒计数器。显示的计数是100251,后跟100252。软件一直在连续计算调整并跟踪RTC秒计数。当错误累积到精确的一秒时,软件会增加或减少一秒以调整累积的错误。

    该技术的缺点是,在进行调整时,从秒到秒的变化很大。该技术具有与任何RTC兼容的优势。

    四、 实时时钟的安全性

    安全性是一个有趣的要求。在某些应用程序中,时间用于为客户计费以使用服务或消耗资源。关于防止或检测RTC的黑客攻击,有广泛的实践体系。技术的范围从外壳的入侵检测到单片机的特殊功能。

    如果单片机上的RTC具有特殊的寄存器,可以允许软件永久锁定关键寄存器。一旦锁定,就无法更改它们,并且可以防止它们被黑客入侵或失控。需要注意的是,更改时间需要完全复位单片机。

    五、时间和日期

    一些RTC具有硬件计数器,可以维护一天中的时间和日历日期。这需要分钟、小时、天、月、年的计数器,并考虑润年。时间和日历日期也可以通过软件保存。

    一个突出的例子是time.h文件中C标准库中的函数。对于微控制器,该系统可以基于RTC的秒计数器。必须编写四个小的自定义函数以完全支持time.h库。

    此处感兴趣的一个函数由库中的time()函数调用,该函数以从称为“纪元”的起点开始的秒数返回时间,通常是1970年1月1日。通常,要读取的自定义函数硬件计时器名为get_time()或类似的变体。get_time()所做的所有操作都会读取秒计数器并返回该值。图书馆会做剩下的事情,以秒为单位将此时间转换为当前日期和日期。

    展开全文
  • 基于MCS-51汇编语言编写一个时钟显示程序,使用定时器0的方式1实现。内含思路、程序框图、源代码等。
  • PIC系列单片机片内定时器实时时钟的实现 PIC系列单片机片内定时器实时时钟的实现 PIC系列单片机片内定时器实时时钟的实现 PIC系列单片机片内定时器实时时钟的实现 PIC系列单片机片内定时器实时时钟的实现
  • 功能:定时器定时,数码管显示
  • 此时钟利用的是定时器的简单时钟,可以利用独立键盘进行分,时的调整,并利用数码管进行显示 #include #include #define uint unsigned int #define uchar unsigned char sbit wela=P2^7; sbit dula=P2^6; sbit s2=...
  • C51单片机 使用定时器

    2020-10-24 09:50:19
    、晶振、时钟周期、机器周期 C51单片机的晶振是11.0592MHz,时钟周期是 1/11059200,机器周期是 12/11059200 二、定时器初值计算 如果要定时0.02秒,就需要x*12/11059200=0.01,解得x=9216 16位定时器得溢出...
  • //设置定时器初值,1ms产生次中断 ET0 = 1; //打开定时器中断 TR0 = 1; // 打开定时器,开始每隔1us加1 } void timer1_init(void) { EA = 1; //打开总中断 TMOD |= 0x10; // TMOD = TMOD | 0x10 = 0x11...
  • C51单片机定时器数码管秒时钟显示——编程题.pdf
  • 单片机电子定时器设计 制作一个实时时钟,并且可以设置在某时间内对某一电器实行操作控制。
  • 由51单片机初学者一个闹钟,目前秒位和分位满60加1, 时位满24清零,但时间不准,还望各位大佬指点,目前在考虑简化。
  • STC51单片机定时器写的电子时钟,在数码管上显示
  • 是当年学习单片机时的练手之作,时钟没有采用专用时钟芯片,仅用单片机搭建。有意者可借鉴我的源码添加时钟芯片,以达到断电不复原初始时间的目的。写过专门的1302的时钟。不过这比较个性,外面有圈秒针...
  • 51单片机——定时器

    千次阅读 2016-03-15 09:02:22
    机器周期:单片机完成一个操作的最短时间,=12个时钟周期 定时器:打开定时器后,定时器“存储寄存器”的值经过一个机器周期自动加1,也就是说,机器周期是定时器的计数周期。 2、定时器的寄存器 TMOD .....
  • 单片机定时器

    2019-12-18 21:40:30
    定时器:当脉冲信号是来自单片机内部的时钟信号,则由于单片机的振荡周期非常精准,故溢出时统计的脉冲数便可换算成定时时间因此可做定时器使用。 注意:定时器/计数器本质都是计数器,只是脉冲信号来源不同。两种...
  • 这是有本实验室设计的电子时钟仿真程序,正规的设计,完全可以作为毕业设计使用

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,117
精华内容 5,246
关键字:

单片机用定时器做一个时钟