万年历设计终极.docx
《万年历设计终极.docx》由会员分享,可在线阅读,更多相关《万年历设计终极.docx(25页珍藏版)》请在冰豆网上搜索。
万年历设计终极
工程综合设计
设计报告
万年历时钟的设计
班级:
电气071班
专业:
电气工程及其自动化
设计人(学号):
完成日期:
2010年12月10日
成都信息工程学院控制工程学院
一、设计目的:
1、掌握C51程序用于实践并实现相应的功能;
2、掌握时钟程序的使用方法;
3、掌握时间函数的使用方法;
4、掌握键盘的程序使用方法;
二、设计任务:
设计一个智能化万年历时钟电路,LED数码管作为电路的显示部分,按钮开关作为调时部分,通过与单片机连接数码管动态显示年、月、日、时、分、秒、星期等功能,并能准确计算闰年闰月的显示。
三、设计要求:
通过与单片机连接数码管动态显示年、月、日、时、分、秒、星期等功能,并能准确计算闰年闰月的显示,七个按钮连接P0口可以精确调整每一个时间数值,通过对所设计的万年历时钟电路进行实验测试,达到了动态显示时间,随时调整时间等技术指标。
四、系统方案设计:
1、系统总体设计:
1)原理构成框图
本设计用AT89C51作为核心控制部分,外接晶振电路与复位电路,74LS138作为位选扩展电路,P0口接七个开关作为时间调整部分,以两个四位数码管作为显示部分,开关控制显示时间与日期,P2口作为段选,P1口作为位选部分。
具体框图如图1所示:
单片机
AT89C51
晶振电路
复位电路
开关调整时间部分
数码管段选部分
三八译码器,扩展位选部分
四位数码管显示部分
图1原理框图
2)设计思路
通过一段时间对专业书籍及多种设计方案的研究分析,我采用了比较常用的AT89C51作为核心控制芯片,用C语言进行编程来满足设计的要求。
用LED数码管来实现年、月、日、时、分、秒、星期的显示,通过七个开关可以在日期与时间间切换和对时钟进行调整,其他外接电路还有晶振电路、复位电路等等。
3)主程序的设计
系统程序采用C语言按模块化方式进行设计,然后通过KeilC51L软件开发平台将程序转变成十六进制程序语言,得到HEX文件,接着使用Proteous进行仿真,其次,按照Proteous的仿真电路图,在Protel99SE中完成电路板的逻辑布局及布线。
系统程序主要包括主程序,读出时钟子程序、计算时间子程序、键盘扫描子程序和显示数据刷新子程序。
主程序流程图如图2所示:
开始
清显示缓冲区
初始化定时计数器
赋时钟初值
开定时器
调整时间
数码管显示当前时钟值勤
定时中断
恢复初值,保留
中断
秒单元清零,60秒到
秒单元清零,60分到
分单元清零,24小时到
时单元清零
堆栈返回
N
50ms加1
秒加1
分加1
时加1
Y
N
Y
N
Y
N
Y
图2程序框图
4)时间调整电路的设计
采用按键设计,独立式按键直接与单片机I/O口相连构成键盘,每个按键不会相互影响,按下SHIJIAN键两个四位数码管显示时间和星期,按下RIQI键两个数码管显示日期,按下TIAOSHI键可以对时间和日期进行调节,JIA键表示加,JIAN键表示减,YIWEI表示选择所要调节的位置,QUEDING按键按下,时间调节结束。
所连线路和单片机接口仿真图如图3所示:
图3仿真按键
5)LED数码管的选择
LED数码管分为共阴和共阳两种,以利用AT89C51的P2口作为LED显示的段选部分,以P1口的P1.0、P1.1、P1.2、三个口作为显示部分的位选,通过三八译码器扩展为八位的位选分别接在两个四位数码管的位选部分。
详细电路图如图4-5所示:
图4时间显示
图5日期显示
6)仿真实验
整个电路仿真图如下图6所示所示:
图6仿真电路图
7)实物的制作与调试
(1)原理图的绘制与PCB的制作
(2)原理图的绘制
(3)在Protel99se中先新建一个工程,把所需要的元件载入到文档里面。
(4)按照所设计的电路画原理图
(5)通过电气检查是否有错,并修改完善。
原理图如图7所示:
图7原理图
8)PCB的绘制
将画好的原理图转为PCB图,设置好规则开始布线,由于边线比较的复杂,采用手动布线的方式进行布线,PCB图如图8所示:
图8PCB图
五、系统实现:
1、方案设计、方案比较
方案一:
采用实时时钟芯片
实时时钟芯片具备年、月、日、时、分、秒计时功能和多点定时功能,计时数据的更新每秒自动进行一次,不需程序干预。
计算机可通过中断或查询方式读取数据进行显示,因此计时功能的实现无需占用CPU的时间,程序简单。
此外,实时时钟芯片多数带有锂电池做后备电源,具备永不停止的计时功能;具有可编程方波输出功能,可用做实时测控系统的采样信号等;有的实时时钟芯片内部还带有非易失性RAM,可用来存放需长期保存但有时也需变更的数据。
由于功能完美,精度高,软件程序设计相对简单,且计时不占用CPU时间,因此,在工业实时测控系统中多采用这一类专用芯片来实现实时时钟功能。
方案二:
软件控制
利用单片机内部的定时/计数器进行中断定时,配合软件延时实现时、分、秒的计时,该方案节省硬件成本,且能使设计者对单片机的指令系统能有更深入的了解,从而掌握单片机应用技术MCS-51汇编语言程序设计方法,因此,本系统设计采用此种软件控制方法来实现时,而由于ATMEL公司的AT89C51单片机是低功耗的具有4KB在线可编程FLASH存储器的单片机。
它与通用80C51系列单片机的指令第和引脚兼容。
片内的FLASH集成在一个芯片上,形成了功能强大、使用灵活和具有较高性能价格比的微控制器。
它的功能强大,而且也较容易购买。
总结:
所要实现的功能通过单片机编程就可以达到,不需要额外的时钟芯片来增加成本,并使外围电路更加简单明了。
2、复位电路的选择
目前为止,单片机复位电路主要有四种类型:
(1)微分型复位电路;
(2)积分型复位电路;(3)比较器型复位电路:
(4)看门狗型复位电路。
方案一:
采用手动复位,该方法线路简单。
在系统运行过程中,有时可能需要对系统进行复位,以避免对硬件经常加电或断电而造成的伤害,我们可以采用手动复位的方式。
如图9所示:
图9手动上电复位电路
方案二:
阻容上电自动复位电路,这种电路线路也简单,它利用电容上电压不能突变而是按指数规律上升或下降的特性,产生所需要的复位脉冲。
优点:
使用最为普遍且成本低廉的复位电路。
如图10所示:
图10自动复位电路
总结:
这两种方案对设计的影响其实差别不大,根据实验需要选择方案一可手动复位电路。
3、实验步骤
(1)、给电路通上电源,打开总电源开关,LED显示是否通电;
(2)、电路板上分别有复位、日期、时间、调整、加、减、选位和确定按键,可以对时间和日期进行任意的调整;其中日期或时间按键按下,数码管分别显示日期和时间;调整按键按下表示对时间开始进行调整,通过选位来确定所要调整的位置,分别可以加和减,最后确定按键按下表示时间调整完成。
(3)、用两个四位数码管通过时间和日期按键来切换,可显示时间和日期。
实验实物图如图11-12所示:
图11时间显示
图12日期显示
总结:
经过测试,我们所做的万年历十分稳定,并且能准确的计时以及调整时间,经过这次试验懂得了仿真与实物是有差别的,也学会了很多,本次完成得也相当不错。
4、设备选择
本次实验所用设备如下表1所示:
序号
名称
型号、规格
数量
价格(元)
1
四位十进制数码管
XDSM420361K
2
5
2
上拉电阻
A103J
1
3
电阻
1K9个、10K、100欧
12
4
电容
22PF、103、100UF、10UF
4
5
按键
电源按键、其它按键8个
9
6
晶振
12M
1
7
三八译码器
SN74LS138N
1
8
单片机座
1
1
9
单片机
89C52RC
1
表1实验所用材料
六、设计过程评估与改进:
个人心得体会、改进的意见和建议。
这次我们组的设计的任务是万年历时钟的设计,对于时钟以前学习C51时有些接触,通过C51芯片程序控制显示万年历。
本次实验是设计一个智能化万年历时钟电路,LED数码管作为电路的显示部分,按钮开关作为调整时间和日期部分,可任意的对时间进行所需的调整,通过与单片机连接数码管动态显示年、月、日、时、分、秒、星期等功能,并能准确计算闰年闰月的显示。
我们采用的是12MHZ的晶振,通过计算比11.0592MHZ的晶振时间更加的准确,定时初值为50ms,通过20个周期时间为一秒,再通过一系列的计算从而实现万年历时钟的基本功能。
在实验调试过程中,出现了很多的问题,仿真时不需要数码管驱动依然能够实现所需的功能,但实验时出现了数码管亮度达不到要求的情况。
我们采用了以下三种方案来驱动数码管:
1、用74HC573作为段选驱动,74LS138作为位选驱动;
2、因为我们采用的是共阴数码管,所以用三极管作为段选驱动;
3、在P2口接的上拉电阻;
最后我们选择了采用比较简单的第三种方法来实现增加数码管的亮度,在这个过程中又涉及了上拉电阻的大小,开始用10K的电阻,因为电阻过大,使电流不能达到要求,以至于不能增加数码管的亮度。
最后我们换用1K的上拉电阻,成功的实现了我们所要得到的结果,数码管的亮度达到了所需的要求。
为了保护整个电路,我们加了一个总电源开关;还在电源处接了一个发光二极管,这样更能清楚的知道电路是否处于通电状态;在电源处还加了103、100uf电容来滤波,阻止信号的干扰。
七、参考文献
[1]于京,张景璐主编.51系列单片机C语言程序设计于应用案例.北京:
中国电力出版社,2006
[2]张洪润,刘秀英,张亚凡著.单片机应用设计200例.北京:
北京航空航天大学出版社,2006
[3]杨志忠主编.数字电子技术基础.北京:
高等教育出版社,2004
附录源程序如下所示:
#include
#include
unsignedcharcodeduanma[]={0x3F,0x6,0x5B,0x4F,0x66,0x6D,0x7D,0x7,0x7F,0x6F,0x40};//共阴的数码管段选
unsignedchardatatianshu[]={31,28,31,30,31,30,31,31,30,31,30,31};//每月的天数
unsignedcharcodeweima[]={0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff};//位码,控制74hc138
unsignedintbdatanian=2010;//初始化时间
unsignedchardatajianzhi=16,yue=11,ri=28,zhou=7,shi=19,fen=30,miao=0;
unsignedchardatahuanwei=0;
unsignedcharbdatacishu=0,temp[8]={0};
bitt_or_d=0;//控制显示时间还是日期标志,只有8个数码管所以只有分开显示了
bittiaozheng=0;//记录调整按钮的状态
bitkey_up=0;//按键抬起标志
sbitP00=P0^0;
sbitP01=P0^1;
sbitP02=P0^2;
sbitP03=P0^3;
voidmain()
{
voiddisplayd();
voiddisplay();//显示temp的内容
voiddelay();//软件延时
voiddis_time();//显示时间
voidcount_week();//计算某某年月日是星期几
voiddis_date();//显示日期
bitrunian(unsignedint);//判断闰年
voidreadkey16();//16键盘扫描,键值保存在jianzhi中
voidmanage_key();//给相应按键赋予动作
IE=0x82;//打开定时器0中断
TMOD=1;//让定时器0工作在1方式
TH0=0x3C;
TL0=0x1F;//时间常数50ms
TR0=1;
loop:
count_week();
manage_key();
if(t_or_d)
dis_date();//显示日期
else
dis_time();//显示星期和时间
gotoloop;
}
///////////////////按键管理//////////////////
voidmanage_key()
{
readkey16();//读取键值
if(jianzhi==0)t_or_d=0;
if(jianzhi==1)t_or_d=1;
if(jianzhi==2)
{
if(t_or_d==0)//调整时间时才关闭定时器
TR0=0;
tiaozheng=1;
huanwei=0;
}
if(key_up)//按键抬起后才做一次动作
{
///////////////////////////调整时间//////////////////////////////
if(t_or_d==0&&tiaozheng==1)//在显示时间状态下调整时间
{
if(jianzhi==15)
{
huanwei++;//在时、分之间切换
if(huanwei==2)//只调整时分调整秒没意义
huanwei=0;
}
if(huanwei==0&&jianzhi==7)//响应时加调整
{
shi++;
if(shi>=24)
shi=0;
}
if(huanwei==0&&jianzhi==11)//响应时减调整
{
shi--;
if(shi==0)
shi=24;
}
if(huanwei==1&&jianzhi==7)//响应分加调整
{
fen++;
if(fen>=60)
fen=0;
}
miao=0;//秒不必调整直接归零
}
if(huanwei==1&&jianzhi==11)//响应分加调整
{
fen--;
if(fen==0)
fen=60;
}
///////////////////////////////调整日期/////////////////////////////
if(t_or_d==1&&tiaozheng==1)
{
if(jianzhi==15)
{
huanwei++;
if(huanwei==3)
huanwei=0;
}
if(huanwei==0&&jianzhi==7)nian++;//年加、减调整
if(huanwei==0&&jianzhi==11)nian--;
if(huanwei==1&&jianzhi==7)//月加、减调整
{
yue++;
if(yue==13)yue=1;
}
if(huanwei==1&&jianzhi==11)
{
yue--;
if(yue==0)yue=12;
}
if(huanwei==2&&jianzhi==7)//日加、减调整
{
ri++;
if(ri>tianshu[yue-1])ri=1;
}
if(huanwei==2&&jianzhi==11)
{
ri--;
if(ri>tianshu[yue-1])ri=1;
}
}
if(jianzhi==3){TR0=1;tiaozheng=0;}
jianzhi=16;
}
}
/////////////////////计算某年月日是星期几///////////////////////
voidcount_week()
{
unsignedintweek=0;
unsignedchari;
if(runian(nian))
tianshu[1]=29;
elsetianshu[1]=28;
if(nian==2010)
{
for(i=1;iweek=tianshu[i-1]%7+week;
week=(week+4+ri)%7;
}
if(nian>2010)
{
for(i=0;i{
if(runian(2010+i))
week=week+2;
elseweek=week+1;
}
for(i=1;iweek=tianshu[i]%7+week;
week=(week+4+ri)%7;
}
if(nian<2010)
{
for(i=1;i<=2010-nian;i++)
{
if(runian(2010-i))
week=week+2;//闰年余两天
elseweek=week+1;//平年余一天
}
week=6-(week+2)%7;
for(i=1;iweek=tianshu[i]%7+week;
week=(week+ri)%7;
}
if(week==0)week=7;
zhou=week;
}
/////////////////////////键盘扫描///////////////////
voidreadkey16()
{
unsignedchara,RL,i;
P0=0x0f;
delay();
if(P0!
=0x0f)
{
i=0;
RL=0xef;
ZERO:
P0=RL;
if(!
P00){a=0;gotokeyval;}
if(!
P01){a=4;gotokeyval;}
if(!
P02){a=8;gotokeyval;}
if(!
P03){a=12;gotokeyval;}
i++;
RL=_crol_(RL,1);
gotoZERO;
keyval:
jianzhi=a+i;
key_up=0;
}
elsekey_up=1;
}
///////////////////////显示///////////////////////////
voiddis_date()//存储日期数据
{
temp[0]=nian/1000;
temp[1]=nian%1000/100;
temp[2]=nian%1000%100/10;
temp[3]=nian%1000%100%10;
temp[4]=yue/10;
temp[5]=yue%10;
temp[6]=ri/10;
temp[7]=ri%10;
displayd();
}
voiddis_time()//存储时间数据
{
temp[7]=zhou;
temp[6]=10;
temp[0]=shi/10;
temp[1]=shi%10;
temp[2]=fen/10;
temp[3]=fen%10;
temp[4]=miao/10;
temp[5]=miao%10;
display();
}
voiddisplayd()//显示日期程序
{
unsignedchari;
for(i=0;i<=7;i++)
{
P1=weima[i];
P2=duanma[temp[i]];
if(i==3)P2=duanma[temp[i]]|0x80;
if(i==5)P2=duanma[temp[i]]|0x80;
delay();
}
}
voiddisplay()//显示时间、星期
{
unsignedchari;
for(i=0;i<=7;i++)
{
P1=weima[i];
P2=duanma[temp[i]];
if(i==1)P2=duanma[temp[i]]|0x80;
if(i==3)P2=duanma[temp[i]]|0x80;
delay();
}
}
//////////////////////////////////////////////////////////////////
voiddelay()//延时子程序
{
unsignedchara,b;
for(a=40;a>0;a--)
for(b=30;b>0;b--);
}
/////////////////////////////////////////////////////////////////
bitrunian(unsignedintnian)//闰年的算法程序
{
if(nian%4==0&&nian%100!
=0||nian%400==0)
return
(1);
elsereturn(0);
}
/////////////////////中断服务////////////////////////////////
voidtimer_wannianli()interrupt1
{
TH0=0x3C;//定时50毫秒
TL0=0x1F;
cishu++;
if(cishu==20){cishu=0;miao++;}
if(miao==60){miao=0;fen++;}
if(fen==60){fen=0;shi++;}
if(shi==24){ri++;shi=0;}
if(ri>tianshu[yue-1]){yue++;ri=1;}
if(yue==13){yue=1;nian++;}