《基于AT89C51单片机的七段数码管电子表》

仿真工具

硬件:Proteus8.9

代码:keil4.0

软件下载可参考:(15条消息) Win10 安装Proteus 8.9的安装资源分享安装步骤,以及可能会出现的问题和问题的解决方法_打大大怪的博客-CSDN博客_win10安装proteus

实现功能

通过LED数码管显示时分秒,通过模式按键和加减按键实现时分秒调节。通过闹钟按键定时并使蜂鸣器报警。

所用元器件

AT89C51单片机,共阳极LED集合数码管,按键,电阻块,反向器

主要元器件AT89C51单片机:

是一种带4K字节闪烁可编程可擦除只读存储器(FPEROM—Falsh Programmable and Erasable Read Only Memory)的低电压,高性能CMOS8位微处理器,俗称单片机。该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。由于将多功能8位CPU和闪烁存储器组合在单个芯片中,ATMEL的AT89C51是一种高效微控制器,为很多嵌入式控制系统提供了一种灵活性高且价廉的方案。

 

引脚功能说明:

        VCC(40引脚):接+5V电源。

        VSS(20引脚):接地。

        XTAL1:(19引脚):反向振荡放大器的输入及内部时钟电路的输入。

        XTAL2:(18引脚):来自反向振荡器的输出。

        RST:复位引脚,高电平有效。

        EA:外部程序存储器访问允许控制端。

        ALE:低8位地址锁存允许信号端。

        PSEN:读外部程序存储器的选通信号端。

        P0口:漏极开路的双向I/O口。

        P1口:8位,准双向I/O口,具有内部上拉电阻。

        P2口:8位,准双向I/O口,具有内部上拉电阻。

        P3口:8位,准双向I/O口,具有内部上拉电阻。

工作原理简述

时间的显示控制由单片机的内部延时程序和定时器中断实现。延时程序和循环程序产生一秒的定时,六十秒为一分钟,六十分钟为一小时,二十四小时为一天,以此达到计时的目的。演示程序和循环程序又根据数码管闪烁中断(即秒表中断)来计算秒的时间,闪烁两次为一秒。数码管采用动态扫描显示方案。

AT89C51的P0端口全部作为数码管的段选端,P2.0到P2.5端口作为数码管的位选端。P3.2端口位闹钟按键,P3.3端口为模式按键,P3.4、P3.5分别是加键减键。

模式键下一共有7种模式切换,每按下一次切换一种模式:模式0为电子表正常显示状态。模式1为秒的时间调整,调整时使用加减键进行调整。模式2为分的时间调整,调整同样使用加减键。模式3为小时的模式调整。模式4为闹钟秒的定时。模式5为闹钟分的定时。模式6为闹钟标志位的状态改变字符。模式7为复位状态,六个数码管全为0。十秒不对按键进行操作则返回正常显示模式。

硬件电路图

自检程序 

#include<reg51.h>   //51单片机的头文件
sbit DU=P0^0 ;	    //P0^0端口控制段选端
sbit WEI=P2^0 ;	   //P2^0端口控制位选端
void main()
{
WEI=1;             //位选端打开
P0=0xfe;           //送入所选位
WEI=0;             //位选端关闭
DU=1;            //段选端打开
P0=0xc0;         //送入所选段(显示数字0)
DU=0;            //位选端关闭
while(1);
}

自检程序目的

使第一位数码管显示数字零,验证硬件的可用性

自检结果

代码编写 及注释

#include <REG51.H>	
unsigned char code LEDDATA[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
0x82,0xf8,0x80,0x90,0xff,0x8e,0x86};
//数码管显示的代码表,后三个为灭灯、"F"、"E"
unsigned char code LEDBITDATA[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,}; 
//数码管扫描代码表
unsigned char LEDBuffer[6];			//定义显示缓冲区数组
unsigned char Hour;					//时单元
unsigned char Minute; 				//分单元
unsigned char Second;				//秒单元
unsigned char Hourrom; 				//定时时单元
unsigned char Minuterom;	 		//定时分单元
unsigned char Minutesw;        		//秒表分单元
unsigned char Secondsw;        		//秒表秒单元
unsigned char Centsw;         		//秒表百分之一秒单元
unsigned char Beepflag;	 			//定时响铃标志
unsigned char SETFlag=0;			//模式标志
unsigned char second_tick;	 		//闪动标志
unsigned char Time;					//按键操作超时计数
unsigned char ALMFlag=0;			//定时开启标志

sbit ALM_KEY=P3^2;					//闹钟模式键与端口P3.2相连
sbit SET_KEY=P3^3;			 	 	//模式按键与端口P3.3相连
sbit UP_KEY=P3^4;					//加计数键与端口P3.4相连
sbit DOWN_KEY=P3^5;					//减计数键与端口P3.5相连
sbit Beep=P1^7;						//蜂鸣器接口引脚P1.7

void init()                         //定时器T0初始化
{ 	TMOD=0x01;						//T0初始化方式1,定时
	TH0=(65536-2000)/256;			//TH0,TL0装入定时2mS的初值
	TL0=(65536-2000)%256;
	TR0=1;							//启动T0工作
	ET0=1;							//允许T0溢出中断
	
	TH1=(65536-50000)/256;        
	TL1=(65536-50000)%256; 
	TR1=0;
	ET1=1;
	EA=1;							//CPU开中断
}

void Delay(unsigned int t)			//去按键抖动延时子程序
{ while(t)t--;
}
void key()							//键盘操作子程序
{	unsigned char i;				//缓冲数组LEDBuffer【i】的位数标志
	char Num;						//临时数字,存储数组合并值
	if(SET_KEY==0)					//判断模式键是否按下
	{	Delay(5000);					//排除是按键抖动的情况
		if(SET_KEY==0)				//再判断是否真得按下了
		{ 	SETFlag++;				//模式改变:调节时间位置(指时分秒的调节)状态改变
			if(SETFlag==8) SETFlag=0;	//返回正常模式
			if(SETFlag==1) i=4;			//调节读取显示数组的位数
			if(SETFlag==2) i=2;
			if(SETFlag==3) i=0;
			if(SETFlag==4) i=4;
			if(SETFlag==5) i=2;
			if(SETFlag==6) i=0;
			if(SETFlag==7) ;
		}
	while(SET_KEY==0);					//等按键释放
	}

	if((UP_KEY==0)&&(SETFlag!=0))		//判断加计数键是否按下
	{
		Delay(5000);					//去按键抖动
		if(UP_KEY==0)					//再判断是否真得按下了
		{	Num=(LEDBuffer[i+1]+LEDBuffer[i]*10);
			Num++;						//时单元的数值加1
			if((Num==24)&&((SETFlag==3)||(SETFlag==5))) Num=0;	//加到24归0
			if((Num==60)&&((SETFlag==1)||(SETFlag==2)||(SETFlag==4))) Num=0;			//加到60归0
			switch(SETFlag)				//把修改值写回时分秒单元和定时时分单元
			{	case 0: ;break;
				case 1: Second=Num;break;
				case 2: Minute=Num;break;
				case 3: Hour=Num;break;
				case 4: Minuterom=Num;break;			
				case 5: Hourrom=Num;break;
				case 6: ALMFlag=!ALMFlag;break;             //定时的开启与暂停
				case 7: TR1=!TR1;break;						//秒表开始与暂停
			}
		}
	 }

	if((DOWN_KEY==0)&&(SETFlag!=0))	//判断减计数键是否按下
	{
		Delay(5000);				//去按键抖动
		if(DOWN_KEY==0)				//再判断是否真得按下了
		{	Num=(LEDBuffer[i+1]+LEDBuffer[i]*10);
			Num--;					//时单元的数值减1
			if((Num<0)&&((SETFlag==3)||(SETFlag==5))) Num=23;	//到24归0
			if((Num<0)&&((SETFlag==1)||(SETFlag==2)||(SETFlag==4))) Num=59;			//到60归0
			switch(SETFlag)				//把修改值写回
			{	case 0: ;break;
				case 1: Second=Num;break;
				case 2: Minute=Num;break;
				case 3: Hour=Num;break;	
				case 4: Minuterom=Num;break;
				case 5: Hourrom=Num;break;				
				case 6: ALMFlag=!ALMFlag;break;
				case 7: TR1=0;Minutesw=0;Secondsw=0;Centsw=0;break;		//秒表复位(reset)			
			}
		}
	}
	if(ALM_KEY==0)                      //闹钟按键被按下
	{	SETFlag=0;						//状态返回
		if(ALMFlag==0)LEDBuffer[0]=11;	//根据闹铃状态显示F或者E
		else LEDBuffer[0]=12;			//将时,分,秒单元内容送入暂存区
		LEDBuffer[1]=10;				//关闭该数码管显示
		LEDBuffer[2]=Hourrom/10;        //定时时单元十位
		LEDBuffer[3]=Hourrom%10;        //定时时单元个位
		LEDBuffer[4]=Minuterom/10;      //定时分单元十位
		LEDBuffer[5]=Minuterom%10;      //定时分单元个位
		
		if(ALMFlag==1)                 //定时关闭
		{	Beep=1;                    //响铃关闭
			Beepflag=0;                //定时响铃标志开启
		}
	}
}			 


void display()							//显示暂存区内容对应的代码显示
{ 	unsigned char LEDScanCount,i;		//位选扫描计数器
P0=0xff;								//消影,适应仿真需要
if((UP_KEY==0)||(DOWN_KEY==0)) i=0x00;	//加减键有操作放弃闪烁
 	else i = 0xff*second_tick;			//设定闪烁变量
P2= LEDBITDATA[LEDScanCount];			//送出位选数据(P2端口控制位选数据)
switch(SETFlag)
	{	case 0:	P0=LEDDATA[LEDBuffer[LEDScanCount]];break; 	//送出段选数据 (P0端口控制段选数据)
	 	case 1:	if(LEDScanCount>=4)							//判断出是最高两位数码管
					 P0=i|LEDDATA[LEDBuffer[LEDScanCount]];	//使小时闪烁
				else P0=LEDDATA[LEDBuffer[LEDScanCount]];break;	//其他位正常显示
	 	case 2:	if((LEDScanCount==2)||(LEDScanCount==3))	 	//判断出是中间两位数码管
					 P0=i|LEDDATA[LEDBuffer[LEDScanCount]];	//使分钟闪烁
				else P0=LEDDATA[LEDBuffer[LEDScanCount]];break;	//其他位正常显示
		case 3:	if(LEDScanCount<=1)                           //判断出是最低两位数码管
					 P0=i|LEDDATA[LEDBuffer[LEDScanCount]];   //使秒闪烁
				else P0=LEDDATA[LEDBuffer[LEDScanCount]];break;  //其他位正常显示
		case 4:	if(LEDScanCount>=4)
					 P0=i|LEDDATA[LEDBuffer[LEDScanCount]];
				else P0=LEDDATA[LEDBuffer[LEDScanCount]];break;
	 	case 5:	if((LEDScanCount==2)||(LEDScanCount==3))
					 P0=i|LEDDATA[LEDBuffer[LEDScanCount]];
				else P0=LEDDATA[LEDBuffer[LEDScanCount]];break;
		case 6:	if(LEDScanCount<=1)
					 P0=i|LEDDATA[LEDBuffer[LEDScanCount]];
				else P0=LEDDATA[LEDBuffer[LEDScanCount]];break;
		case 7:	P0=LEDDATA[LEDBuffer[LEDScanCount]];break; 
	}
LEDScanCount++; 						//扫描指针加计数
if(LEDScanCount==6) LEDScanCount=0 ;	//扫描完从头开始;
}


void time0() interrupt 1               //第一个中断:用于按键调整时间时的中断
{   unsigned int SecondCount;
	unsigned int timercp;				//秒计数器
	TH0=(65536-2000)/256;				//TH0,TL0装入定时2mS的初值
	TL0=(65536-2000)%256;	 
	display();							//调用显示函数
	timercp++;		
	if((SETFlag!=0)&&(SETFlag!=7)) 						//SETFlag:模式标志。
		{	if((SET_KEY==0)||(UP_KEY==0)||(DOWN_KEY==0)) Time=0;	//任意键有操作放弃按键超时计数
		 	if (Time>=10){SETFlag=0;Time=0;}                        //10秒不操作自动返回
		}
	if (timercp == 250)				
		{	timercp= 0; 
			second_tick = !second_tick;		//设定0.5秒闪动一次 
			SecondCount++;
			if(SecondCount==2) 				//1秒的时间
			{	SecondCount=0;				//计数器清零
				Time++;					
	 			Second++;					//时间的秒加1
				if(Second==60)
				{	Second=0;		 		//秒清零
					Minute++;		 		//分进一
					if(Minute==60)
					{	Minute=0;
						Hour++;
						if(Hour==24)Hour=0;	//小时清零
					}
				}
			}
	if(SETFlag<=3)				 			//状态模式小于3是加载时钟时间
			{ LEDBuffer[0]=Hour/10;
				LEDBuffer[1]=Hour%10;
				LEDBuffer[2]=Minute/10;
				LEDBuffer[3]=Minute%10;
				LEDBuffer[4]=Second/10;
				LEDBuffer[5]=Second%10; 
			
			}
			else							 //调整闹钟时间时加载闹钟时间
			{	if(ALMFlag==0)LEDBuffer[0]=11; //ALMFlag==0:定时开启标志。显示闹钟为激活状态
		 		else LEDBuffer[0]=12;
				LEDBuffer[1]=10;			 //关闭倒数第二位
				LEDBuffer[2]=Hourrom/10;     //定时时单元的十位
				LEDBuffer[3]=Hourrom%10;     //定时时单元的个位
				LEDBuffer[4]=Minuterom/10;   //定时分单元十位
				LEDBuffer[5]=Minuterom%10;	 //定时分单元个位	
			}
			if(SETFlag==7)                        //秒表模式:电子表在计时的时候是秒表模式一秒一秒加上去的。
			{
				LEDBuffer[0]=Minutesw/10;    //秒表分单元十位
				LEDBuffer[1]=Minutesw%10;    //秒表分单元个位
				LEDBuffer[2]=Secondsw/10;    //秒表秒单元十位
				LEDBuffer[3]=Secondsw%10;    //秒表秒单元个位
				LEDBuffer[4]=Centsw/10;      //秒表百分之一秒单元十位
				LEDBuffer[5]=Centsw%10;      //秒表百分之一秒单元个位
			}
	}
	
}
void time1() interrupt 3      //秒表中断程序
{
	unsigned int Count;
	TH1=(65536-50000)/256;        
	TL1=(65536-50000)%256; 
	display();	
	Count++;
	if(Count==2)
	{
	Count=0;            //计数清零
	Centsw++;           //秒表百分之一秒单元++
	if(Centsw==100)     //判断是否到一秒
	{
		Centsw=0;       //到一秒则秒表百分之一秒单元清零
		Secondsw++;     //秒表秒单元加一
		if(Secondsw==60)     //判断是否到一分钟
		{
			Secondsw=0;      //到一分钟则秒表秒单元清零
			Minutesw++;         //秒表分单元加一
			if(Minutesw==60)    //判断是否到一小时
			{
				Minutesw=0;    //到一小时则秒表分单元清零
			}
		}
	}
	

	}
	LEDBuffer[0]=Minutesw/10;            //数据缓存
	LEDBuffer[1]=Minutesw%10;
	LEDBuffer[2]=Secondsw/10;
	LEDBuffer[3]=Secondsw%10;
	LEDBuffer[4]=Centsw/10;
	LEDBuffer[5]=Centsw%10;
	
}
	



void main()
{	init();								//初始化
	while(1)
{		key();							//调用键盘
		if(ALMFlag==1)                  //判断定时是否关闭,如果关闭则进入下边的判断语句。
		{	if(Minute!=Minuterom) Beepflag=1;	//定时和现在不同,关闭蜂鸣器
			if((Hour==Hourrom)&&(Minute==Minuterom)&&(Beepflag==1)) Beep=0;
					//时分相同并闹铃打开就响铃
		}
} 
}

运行结果展示

上图为59分58秒 

物联沃分享整理
物联沃-IOTWORD物联网 » 《基于AT89C51单片机的七段数码管电子表》

发表评论