仿真工具
硬件: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口,内部上拉电阻。
P三口八位,准双向I/O口,内部上拉电阻。
简述工作原理
时间显示控制由单片机内部延迟程序和定时器中断实现。延迟程序和循环程序产生一秒定时,60秒为一分钟,60分钟为一小时,24小时为一天,以达到计时的目的。演示程序和循环程序根据数字管闪烁中断(即秒表中断)计算秒,闪烁两次为一秒。数字管采用动态扫描显示方案。
AT89C51的P0端口全部作为数码管的段选端,P2.0到P2.5端口作为数码管的位选端。P3.2端口闹钟按钮,P3.三个端口是模式按钮,P3.4、P3.分别是加键减键。
模式键下一共有7种模式切换,每按下一次切换一种模式:模式0为电子表正常显示状态。模式1为秒时间调整,调整时用加减键调整。模式2是分时间调整,调整也使用加减键。模式3为小时模式调整。模式4是闹钟秒的定时。模式5是定时闹钟。模式6是改变闹钟标志位的状态。模式7为复位状态,六个数字管均为0。如果按钮在10秒内不操作,则返回正常显示模式。
硬件电路图
自检程序
#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)%6; TR0=1; //启动T0工作 ET0=1; //允许T0溢出中断 TH1=(65536-50000)/256; TL1=(65536-50000)%6; TR1=0; ET1=1; EA=1; //CPU开中断 } void Delay(unsigned int t) //去按键抖动延迟子程序 { while(t)t--; } void key() //键盘操作子程序 { unsigned chari; //缓冲数组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秒