单片机和数码管设计的电子时钟.docx
《单片机和数码管设计的电子时钟.docx》由会员分享,可在线阅读,更多相关《单片机和数码管设计的电子时钟.docx(21页珍藏版)》请在冰豆网上搜索。
![单片机和数码管设计的电子时钟.docx](https://file1.bdocx.com/fileroot1/2023-2/26/282841e6-2c92-4e87-a5a0-692087150135/282841e6-2c92-4e87-a5a0-6920871501351.gif)
单片机和数码管设计的电子时钟
单片机数码管
显示电子时钟论文
学 校:
黑龙江科技学院
专业名称:
通信工程
班 级:
10-2班
姓 名:
刘振涛
日 期:
2012/11/11
摘要
此设计以at89c52单片机为核心控制元件,与数码管和蜂鸣器和按键结合,在应用c语言来实现电子时钟的显示和调制功能。
本次设计运用了KEIL和protues仿真软件来进行调试,并给出了完整的设计电路和KEIL程序代码,并画出了编程的逻辑流程图。
通过调试和运行,最终完成了一个完整的电路仿真,其功能是实现时间和日历的现实并且都能进行相应的调整,同时还能进行闹铃的设置并在达到预定的时间时闹铃。
关键字:
at89c51单片机、数码管、时钟
一、电子时钟功能概述
本次电子时钟作品分由单片机部分、数码管显示部分、按键部分和蜂鸣器电路相互连接组成。
这样硬件部分就组成了一个电子时钟的基本完整的电路。
电子时钟的工作过程是:
接通电源时时钟开始工作,时钟分为三个状态:
正常计时、显示日历、显示闹铃,分别按下闹铃按键和日历按键就可以显示当前的日历和设置的闹铃时间。
在这三个状态下按下设置键就可以对相应的状态进行设置,同时对应的数码管位开始闪烁,当按下调时键时就可以对相应的位进行调整,当再次切换相应的功能时数码管就停止闪烁,同时设置完成。
当时间到达闹铃设置时间视蜂鸣器就会响起并响一分钟结束。
二、硬件电路和资料
1、AT89C52单片机
本设计采用Atmel公司生产的单片机AT89C51实现主要功能,AT89C51的引脚图如2-1所示:
图2-1单片机的引脚
各引脚功能情况为:
(1)Vcc:
供电电压。
(2)GND:
接地。
(3)P0口:
P0口为一个8位漏级开路双向I/O口,每脚可吸收8TTL门流。
当P1口的管脚第一次写1时,被定义为高阻输入。
(4)P1口:
P1口是一个内部提供的上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4TTL门电流。
P1口管脚写入1后,被内部上拉为高,可用作输入,P1口被外部下拉为低电平时,将输出电流,这是由于内部上拉的缘故。
(5)P2口:
P2口为一个内部上拉电阻的8位双向I/O口,P2口缓冲器可接收,输出4TTL门电流,当P2口被写“1”时,其管脚被内部上拉电阻拉高,且作为输入。
并因此作为输入时,P2口的管脚被外部拉低,将输出电流。
这是由于内部上拉的缘故。
在给出地址“1”时,它利用内部上拉优势,当对外部八位地址数据存储器进行读写时,P2口输出其特殊功能寄存器的内容。
(6)P3口:
P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。
当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。
作为输入,由于外部下拉为低电平,P3口将输出电流(ILL)这是由于上拉的缘故。
P3口功能引脚简介:
P3.0:
RXD(串行口输入)
P3.1:
TXD(串行口输出)
P3.2:
INT0(外部中断0)
P3.3:
INT1(外部中断1)
P3.4:
T0(定时器0外部脉冲输入)
P3.5:
T1(定时器1外部脉冲输入)
P3.6:
WR(外部数据存储器写选通)
P3.7:
RD(外部数据存储器读选通)
P3口同时为闪烁编程和编程校验接收一些控制信号。
(7)RST:
复位输入。
当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。
(8)ALE/RPOG:
当访问外部存储器时,地址锁存允许的输出电平由于锁存地址的地位字节。
(9)/PSEN:
外部程序存储器的选通信号。
在由外部程序存储器取指期间,每个机器周期两次/PSEN有效。
但在访问外部数据存储器时,这两次有效的/PSEN信号将不出现。
(10)/EA/VPP:
当/EA保持低电平时,则在此期间外部程序存储器(0000H-FFFFH),不管是否有内部程序存储器。
注意加密方式1时,/EA将内部锁定为RESET;当/EA端保持高电平时,此间内部程序存储器。
(11)XTAL1:
反向振荡放大器的输入及内部时钟工作电路的输入。
(12)XTAL2:
来自反向振荡器的输出。
2、LED数码管
数码管显示器内部由7个条形发光二极管和一个小圆点发光二极管组成,根据各管的亮暗组合成字符。
常见LED的管脚排列如图1中c所示。
根据内部发光二极管的接线形式,可分成共阴极型和共阳极性,如图2-2中a、b所示。
LED数码管的g--a,dp8个发光二极管因不同亮暗的组合就能形成不同的字形,这种组合称为字形码。
共阳极和共阴极的字形码是不同的。
图2-2LED数码管显示器
点亮LED显示器分为静态和动态两种显示方法,为了节省I/O口的使用,本设计采用的是动态显示,其工作原理为:
采用各数码管循环轮流显示的方法,当循环显示的频率较高时,利用人眼的暂留特性,看不出闪烁显示现象。
将所有LED的段选线并联在一起,由一个八位I/O口控制,而位选线分别由相应的I/O口线控制。
如:
8位LED动态显示电路只需要两个八位I/O口。
其中一个控制段选码,另一个控制位选。
动态显示器电路如图2-3所示。
图2-3八位LED动态显示电路
3、独立键盘
利用4个自复式常开按钮开关作为功能键,KEY0~KEY3分别为设置、调时、闹铃、日历。
三、硬件电路设计图
1、总体电路设计
3-1总体电路图
2、外部震荡电路
图3-2外部振荡电路
一般选用石英晶体振荡器。
此电路在加电大约延迟10ms后振荡器起振,在XTAL2引脚产生幅度为3V左右的正弦波时钟信号,其振荡频率主要由石英晶振的频率确定。
电路中两个电容C1,C2的作用有两个:
一是帮助振荡器起振;二是对振荡器的频率进行微调。
C1,C2的典型值为30PF。
3、复位电路设计
单片机的第9脚RST为硬件复位端,只要将该端持续4个机器周期的高电平即可实现复位,复位后单片机的各状态都恢复到初始化状态,其电路图如图3-3所示:
图3-2复位电路
在方案中使用到了硬件复位和软件复位两种功能,由上面的硬件复位可使寄存器及存储器的值都恢复到初始值,而前面的功能提到了倒计时间需要有记忆功能,该功能实现的前提条件就是不能对单片机进行硬件复位,所以设定了软复位功能。
软复位实际上就是当程序执行完毕之后,将程序指针通过一条跳转指令让它跳转到程序执行的起始地址。
4、按键电路设计
按钮选用常开开关,如图3-4所示:
图3-3抢答按键
这些常开开关组成了电子时钟的功能按键,硬件电路简单,在程序设计上也不复杂,只要在程序中消除在按键过程中进行“消抖”就可以了。
这里采用最常用的方法即延时法,其原理为:
当单片机检测到有按键动静后再延时一段时间后再判断此电平是否保持原状态,如果是则为有效按键,否则无效。
5、显示电路设计
图3-4显示部分
显示部分用LED和两片74HC573组成,用74HC573来控制数码管的段选和位选,当74HC573的使能端开启时传送P0口的数据。
四、程序流程图
根据以上硬件电路和单片机控制原理,搭建编程思路,程序流程图如图4-1所示:
图4-1程序流程图
五、仿真及其结果
仿真方法是:
(1)在KeilC51仿真软件下创建项目,并把上述源程序添加到项目中,通过编译产生一个".hex"为后缀的文件,此文件就是用于烧写到Proteus软件中AT89C52芯片的文件,参考文献[2]。
(2)把生成的".hex"为后缀的文件添加到Proteus软件中绘制的AT89C52芯片中作为控制程序。
(3)在Proteus软件仿真电路图中点击左下角的运行按钮,电子时钟就会开始计时,并显示如图所示:
图5-1显示时间
图5-2显示闹铃
图5-3显示日期
图5-4调整日期
六、总结
本设计以AT89C52单片机为核心,运用Proteus仿真软件来实现整个设计流程,借助其功能强大的仿真系统对设计的系统进行实时仿真,以检测系统设计的正确性与合理性,并可以根据仿真电路图制作硬件电路,这种设计具有传统逻辑设计方法所无法比拟的优越性。
设计整体性好、人性化强、可靠性高,实现了数字显示控制的智能化。
参考文献:
[1]周润景.基于Proteus的电路与单片机系统设计与仿真[M].北京:
北京航空航天大学出版社,2005.
[2]金炯泰,金奎焕.如何使用KEIL8051C编译器[M].北京:
北京航空航天大学出版社,2002.
[3]程相波,卫安军.基于MCS-51单片机的八路抢答器设计方法研究文献名[J].北京工业职业技术学院学报,2007
(2).
[4]张齐.单片机应用系统设计技术———基于C语言编程[M].北京:
电子工业出版社,2004.
[5]潘永雄.新编单片机与应用[M].西安:
西安电子科技大学出版社,2003.
附录:
完整c语言代码如下:
/***************************************
项目名称:
数码管电子时钟
实现功能:
数码管显示时间、日期,时间、日
期可调,可设置闹钟,闹铃响一分钟。
作者:
通信10-2班11号刘振涛
版本号:
2.0 时间2012.11.5
****************************************/
#include
#defineuintunsignedint
#defineucharunsignedchar
uintnumwe,tt,t,numdu,set,aclock,rili;
unsignedlonginttime,time1=0;
unsignedlongintsecond,minute,hour,year=2012,month=12,day=31;
ucharcodedu[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
ucharcodewe[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
uchar Mth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};//每月对应的天数
uchar YMth[13]={0,2,9,2,1,2,1,2,2,1,2,1,2};
sbitdula=P2^0;
sbitwela=P2^1;
sbitKEY0=P3^2;//选位
sbitKEY1=P3^1;//加一
sbitKEY2=P3^0;//设闹铃
sbitKEY3=P3^3;
sbitbeep=P2^2;//响铃
voidDelay(uintdel);//延时程序
voidDisplay(uintsd,uintmm,uinthy);//显示程序
voidadd();//加一
voidalarmclock();//闹钟
voidinit();//初始化
intclock();//计算时间
voidCalender();//计算日期
voidmain()
{
P2=0xff;
init();
while
(1){
add();
if(rili==1)//分日历和时间显示
Display(day,month,year);
else
Display(second,minute,hour);
alarmclock();
}
}
voidDisplay(uintsd,uintmm,uinthy)
{ inty0,y1,y2;
if(rili==1)
{
y0=hy/1000,y1=hy%1000;y2=hy%100;
}
for(numwe=0;numwe<8;numwe++)
{
P0=0xff;
wela=1;
P0=we[numwe];
wela=0;
P0=0xff;
dula=1;
switch(numwe)
{
case0:
P0=(tt==1&set==1)?
0x00:
(rili==1)?
du[y0]:
du[hy/10];break;
case1:
P0=(tt==1&set==2)?
0x00:
(rili==1)?
du[y1/100]:
du[hy%10];break;
case2:
P0=(rili!
=1)?
0x40:
(tt==1&set==3)?
0x00:
du[y2/10];break;
case3:
P0=(tt==1&set==4)?
0x00:
(rili==1)?
du[hy%10]+0x80:
du[mm/10];break;
case4:
P0=(tt==1&set==5)?
0x00:
(rili==1)?
du[mm/10]:
du[mm%10];break;
case5:
P0=(rili!
=1)?
0x40:
(tt==1&set==6)?
0x00:
du[mm%10]+0x80;break;
case6:
P0=(tt==1&set==7)?
0x00:
du[sd/10];break;
case7:
P0=(tt==1&set==8)?
0x00:
du[sd%10];break;
}
dula=0;
Delay(5);
}
}
voidinit()//初始化
{
wela=0;
dula=0;
TMOD=0x01;//设置定时器0为工作方式1
TH0=(65536-50000)/256;//定时器高八位//定时5ms
TL0=(65536-50000)%256;//定时器低八位
IE=0x8f;//开启中断
TCON=0x15;//启动定时器及工作方式
}
intclock()//计算时间
{
if(t==10|t==20)tt=!
tt;//闪烁标志位
if(t==20)
{
t=0;
if(!
(set!
=0&aclock==0))
time++;
if(time>=time1&timebeep=0;
else
beep=1;
Calender();
if(aclock==0)
{
second=time%60;
minute=(time%3600)/60;
hour=time/3600;
if(hour==24)
{
time=0;
hour=0;
}
}
else
{
second=time1%60;
minute=(time1%3600)/60;
hour=time1/3600;
if(hour==24)
{
time=0;
hour=0;
}
}
}
}
voidDelay(uintdel)
{
uinti,j;
for(i=0;ifor(j=0;j<100;j++);
}
voidtimer0()interrupt1//定时器0中断
{
TH0=(65536-50000)/256;//定时器高八位//定时5ms
TL0=(65536-50000)%256;//定时器低八位
t++;
clock();
}
void settime()interrupt0//it0
{
if(KEY0==0)
{
Delay
(2);
if(KEY0==0)
{ if(rili!
=1)
{if(set==2|set==5)set=set+2;elseset++;}
else
set++;
if(set==9)
{set=0;t=0;}
//while(!
KEY0);
}
}
}
voidcalendar()interrupt2//日历
{
if(KEY3==0)
{
Delay
(2);
if(KEY3==0)
rili=!
rili;
set=0;aclock=0;
}
}
voidCalender()//计算日历
{
if(time==86400)
{
day++;
if((year%4==0&year%100!
=0)|(year%400==0))
Mth[2]=29;
else
Mth[2]=28;
if(day>Mth[month])
{
day=1;
month++;
if(month>12)
{month=1;
year++;}
}
}
}
voidadd()//自加一
{
intge,ge1,ge2,shi,shi1,shi2,bai2,qian2;
if(rili==1)
{
ge=day%10;ge1=month%10;ge2=year%10;
shi=day/10;shi1=month/10;shi2=year%100/10;
bai2=year%1000/100;qian2=year/1000;
}else
{
ge=second%10;ge1=minute%10;ge2=hour%10;
shi=second/10;shi1=minute/10;shi2=hour/10;
}
if(KEY1==0)
{
Delay
(2);
if(KEY1==0)
{
if(rili==1)
{
if((year%4==0&year%100!
=0)|(year%400==0))
YMth[2]=10;
else
YMth[2]=9;
switch(set)
{
case1:
qian2=(qian2+1)%10;break;
case2:
bai2=(bai2+1)%10;break;
case3:
shi2=(shi2+1)%10;break;
case4:
ge2=(ge2+1)%10;break;
case5:
shi1=(shi1+1)%2;break;
case6:
ge1=(shi1==1)?
(ge1+1)%3:
(ge1+1)%10;break;
case7:
shi=(month==2)?
(shi+1)%3:
(shi+1)%4;break;
case8:
ge=(shi==3)?
(ge+1)%YMth[month]:
(month==2)?
(ge+1)%YMth[2]:
(ge+1)%10;break;
}
year=qian2*1000+bai2*100+shi2*10+ge2;
month=shi1*10+ge1;
day=shi*10+ge;
}else
{
switch(set)
{
case1:
shi2=(shi2+1)%3;break;
case2:
ge2=(ge2+1)%4;break;
case4:
shi1=(shi1+1)%6;break;
case5:
ge1=(ge1+1)%10;break;
case7:
shi=(shi+1)%6;break;
case8:
ge=(ge+1)%10;break;
}
second=shi*10+ge;minute=shi1*10+ge1;hour=shi2*10+ge2;
if(aclock==0)
time=hour*3600+minute*60+second;
else
time1=hour*3600+minute*60+second;
}
while(!
KEY1);
}
}
}
voidalarmclock()//设置闹铃
{
if(KEY2==0)
{
Delay
(2);
if(KEY2==0)
{
while(!
KEY2);
aclock=1;
rili=0;
set=0;
while
(1)
{
if(rili==1)
Display(day,month,year);
else
Display(second,minute,hour);
add();
if(KEY2==0)
{
while(!
KEY2);
aclock=0;
set=0;
break;
}
}
}
}
}