单片机课设万年历报告
单片机课设万年历报告
一、系统简介:
电子万年历是一种非常广泛日常计时工具,对现代社会越来越流行。它可以对年、月、日、时、分、秒进行计时等很多种功能,而且DS1302的使用寿命长,误差小。对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、时、分、秒等信息,还具有时间校准等功能。
本设计是基于51系列的单片机进行的电子万年历设计,可以显示年月日时分秒信息,具有可调整日期和时间功能。在设计的同时对单片机的理论基础和外围扩展知识进行了比较全面准备。在硬件与软件设计时,没有良好的基础知识和实践经验会受到很大限制,每项功能实现时需要那种硬件,程序该如何编写,算法如何实现等,没有一定的基础就不可能很好的实现。在编写程序过程中发现以现有的相关知识要独自完成编写任务困难重重,在老师和同学的帮助下才完成了程序部分的编写。
万年历的设计过程在硬件与软件方面进行同步设计。硬件部分主要由AT89C51单片机,LED显示电路,以及调时按键电路等组成。在单片机的选择上本人使用了AT89C51单片机,该单片机适合于许多较为复杂控制应用场合。软件方面主要包括日历程序、时间调整程序,显示程序等。程序采用高级语言C进行编写,以便更简单地实现调整时间。所有程序编写完成后,在确定没有问题后,在Proteus软件中嵌入单片机内进行仿真。最后总在老师同学的帮助以及自己的努力下完成
了此次电子万年历的设计。
二、系统功能说明
1、设计要求:
1、具有年、月、日、星期、时、分、秒等功能; 2、具备年、月、日、星期、时、分、秒调整校准功能;
2、单片机芯片的选择方案和论证:
采用89C51芯片作为硬件核心,数码管显示,为使用户更清楚更明白,万年历的日期和时间是分开显示,同时分别对应两个74LS138芯片用于控制数码管显示,还有74LS373译码器芯片,74LS21芯片是用于控制键盘的功能。如图所示:
74LS138的八个输出引脚,任何时刻要么全为高电平1也就是芯片处于不工作状态,要么只有一个为低电平0,其余7个输出引脚全为高电平1。如果出现两个输出引脚同时为0的情况,说明该芯片已经损坏。
74LS373原理图
当74LS373用作地址锁存器时,应使OE为低电平,此时锁存使能端G为高电平时,输出Q1~Q8 状态与输入端D1~D8状态相同;当G发生负的跳变时,输入端D1~D8 数据锁入Q1~Q8。51单片机的ALE信号可以直接与74LS373的G连接。在MCS-51单片机系统中,常采用74LS373作为地址锁存器使用,其连接方法如上图所示。其中输入端1D~8D接至单片机的P0口,输出端提供的是低8位地址,G端接至单片机的地址锁存允许信号ALE。输出允许端OE接地,表示输出三态门一直打开。
显示模块选择方案:
采用LED数码管动态扫描,LED数码管价格适中,对于显示数字最合适,而且采用动态扫描法与单片机连接时,占用的单片机口线少。使用多个数码管显示LED数码管是利用二极管发光显示数字和字母,具有亮度大、接口设计比较容易,价格相对较便宜等优点,所以采用了LED数码管作为显示。
数码管电路
3、键盘的选择
采用独立式按键电路。每个键单独占有一根I/O接口线,每个I/O口的工作状态互不影响,此类键盘采用端口直接扫描方式。而且在本次实验课设用的键盘很少,所以采用独立式按键比较实用。
4、实现定时:
直接采用单片机定时计数器提供秒信号,使用程序实现年、月、日、时、分、
秒计数。采用此种方案虽然减少芯片的使用,节约成本,但是,实现的时间误差较大。
5、电路设计最终方案决定
综上各方案所述,对此次作品的方案选定: 采用AT89S51作为主控制系统; LED数码管动态扫描作为显示。两个74LS138和一个74LS21芯片。
图-5 LED动态扫描显示
实验心得:
经过了两个星期的学习和实验,我终于完成了单片机万年历课程设计。从开始接到课设要求到万年历的实现,再到报告的完成,每走一步对我来说都是新的尝试与挑战,这也是我在大学期间独立完成的最大的项目。在这段时间里,我学到了很多知识也有很多感受,我开始了独立的学习和试验,查看相关的资料和书籍,让自己头脑中模糊的概念逐渐清晰,使自己非常稚嫩作品一步步完善起来,每一次改进都是我学习的收获。
这次报告的书写也会使我终身受益,我感受到做报告是要用心去做的一件事情,是真正的自己学习的过程和研究的过程,没有学习就不可能有研究的能力,没有自己的研究,就不会有所突破。通过本次单片机课设,获得了丰富的理论知识,极大地提高了实践能力,单片机领域这对我今后进一步学习计算机方面的知识有极大的帮助。
参考文献
[1]李建忠.单片机原理及应用.西安电子科技大学出版 2006.02 [2]张俊谟.单片机中级教程.北京航空航天大学出版 2006.03 [3]余发山.单片机原理及及应用技术.中国矿业大学出版社 2003.12 [4]杨凌霄.微型计算机原理及应用.中国矿业大学出版社 2004.08 [5]何立民.单片机应用技术选编.北京航空航天大学出版社 2000.08 [6]万光毅.单片机实验与实践教程.北京航空航天大学出版社 2005.01 [7]周航慈.单片机程序设计基础.北京航空航天大学出版社 1991.11 [8]黄遵熹.单片机原理接口与应用.西北工业大学出版社 1997.05
[9]杨振江,杜铁军,李群.流行单片机实用子程序及应用实例[M].西安:西安电子科技大学出版社,2002 [10]杨子文 编 单片机原理及应用 西安电子科技大学出版社 2006 [11]王法能 编 单片机原理及应用 科学出版社 2004
附源代码:
#include sbit p1_0=P1^0; sbit p1_1=P1^1; sbit p1_2=P1^2; sbit p1_3=P1^3; sbit p1_4=P1^4; sbit p3_2=P3^2;
unsigned char code zimal[13]={0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F,
0x40,0x80,0x00,};
/*共阴极字形代码,显示0-9字形码,最后三个代码分别表示‘-’,‘.’‘熄灭’*/ unsigned int year,month,day,hour,minute,second;//定义变量年,月,日,时,分,秒; void delay(); //延时函数;
void xianshi(unsigned int h,min,s); //显示时间函数;
void ymdxianshi(unsigned int year,month,day); //显示年月日; void fengming(); //蜂鸣器函数; int leap(unsigned int year); //判断闰年函数; unsigned int h,m,s=5; //记录定时时间; unsigned int mode=1,td=0,yw=0; //状态; void main()
{ year=2010;month=5;day=20;hour=23;minute=59; second=0; /*初始化年,月,日,时,分,秒*/ TMOD=0x10; //定时器1;模式1;
TL1=0xb0;TH1=0x3c;定时时间为50ms TR1=1;//打开定时器1
ET1=1;//定时器1溢出中断允许位 EX0=1;//外部中断0允许
IT0=1;//外部中断0触发方式为边沿触发
EA=1;
while(1) //主循环;
{
if(hour==h&&minute==m&&second==s) fengming(); //定时时间到,蜂鸣器响; if(mode%2==0) ymdxianshi(year,month,day); //显示年月日 else
{
if(td%3==2) xianshi(h,m,s); // 定时状态;
else xianshi(hour,minute,second); //正常显示时间状态; } }
}
void delay() //延时函数;
unsigned int i;
for(i=0;i
void ymdxianshi(unsigned int year,month,day) //年月日的显示与调整 {int i,j; i++;
if(td%2==1&&yw%8==0&&i%2==0) //点亮年的第一个 {
P2=0x7f; P0=zimal[12]; for(j=0;j
} else {
P2=0x7f; }
P0=zimal[year/1000]; delay();
if(td%2==1&&yw%8==1&&i%2==0) //点亮年的第二个 {
P2=0xbf;
P0=zimal[12];
for(j=0;j
else {
P2=0xbf;
P0=zimal[year%1000/100]; delay();
}
if(td%2==1&&yw%8==2&&i%2==0)//点亮年的第三个 { P2=0xdf;
P0=zimal[12];
for(j=0;j
else {
P2=0xdf; P0=zimal[year%100/10];
delay(); }
if(td%2==1&&yw%8==3&&i%2==0) //点亮年的第四个
}
P2=0xef;
P0=zimal[12];
for(j=0;j
else //显示年 {
P2=0xef;
P0=zimal[year%10]; delay(); }
if(td%2==1&&yw%8==4&&i%2==0)//显示月点亮 {
P2=0xf7; P0=zimal[12]; P2=0xfb;
P0=zimal[12];
for(j=0;j
else {
P2=0xf7;
P0=zimal[month/10]; delay(); P2=0xfb;
P0=zimal[month%10]; delay();
}
P2=0xef;//显示点 P0=zimal[11];
for(j=0;j
P0=zimal[11];
for(j=0;j
if(td%2==1&&yw%8==5&&i%2==0)//显示日的第一个 {
P2=0xfd;
P0=zimal[12]; for(j=0;j
P2=0xfd;
P0=zimal[day/10]; delay();
}
if(td%2==1&&yw%8==6&&i%2==0)//显示日的第二个
{
P2=0xfe;
P0=zimal[12];
for(j=0;j
else
{
} } P2=0xfe; P0=zimal[day%10]; delay();
void xianshi(unsigned int h,min,s) //显示时间函数;
{ unsigned int i,j;
i++;
if(td%3!=0&&yw%6==0&&i%2==0)
{ P2=0x7f; //显示时的第一个;
P0=zimal[12]; for(j=0;j
}
else
{ P2=0xbf; P0=zimal[h%10]; delay(); }
//显示-
P2=0xdf;
P0=zimal[10];
delay();
if(td%3!=0&&yw%6==2&&i%2==0) //显示分的第一个;
P2=0xef; P0=zimal[12]; for(j=0;j
}
P2=0xfb; //显示-
P0=zimal[10];
delay();
if(td%3!=0&&yw%6==4&&i%2==0) //显示秒的第一个;
{
P2=0xfd; P0=zimal[12]; for(j=0;j
}
else
{
}
P2=0xfe; P0=zimal[s%10]; delay(); }
void ex0()interrupt 0 //外部中断零;
{
unsigned int i;
if(p1_3==0)delay();
if(p1_3==0) {mode++;td=0;}
if(p1_1==0)delay();
if(p1_1==0)yw++; //移位;
if(p1_0==0)delay();
if(p1_0==0)td++; //修改调整时间状态;
if(mode%2==0) //调整日期;
{ if(td%2==1) { for(i=0;i=2500)year=0; //年份小于2500; if(month>12)month=1; //月份小于12; switch(month) //日期限制 { case 4: case 6: case 9: case 11:if(day>30)day=1;break;//4,6,9,11月30天; case 2 : {if (leap(year)) //闰年2月29天; while(day>29)day=1; else while(day>28)day=1;//非闰年28天;
} }break; default:if(day>31)day=1; //1,3,5,7,8,10,12月31天; }
}
//else if(td%3==1) //调整时间;
else if(td%3==1)
{
ET1=0; //时钟中断 if(p1_2==0)delay(); for (i=0;i
if(p1_2==0)
{
} if(yw%6==1){hour++;if(hour%10==0) hour-=10;} if(yw%6==0)hour+=10; if(yw%6==3){minute++;if(minute%10==0)minute-=10;} if(yw%6==2)minute+=10; if(yw%6==5){second++; if(second%10==0)second-=10;} if(yw%6==4)second+=10; if(hour>23)hour=0; if(minute>59)minute=0; if(second>59)second=0; }
else //定时状态;
{
ET1=1; if(td%3==2) { for(i=0;i23)h=0; } if(m>59)m=0; if(s>59)s=0;
}
}
void time1()interrupt 3 //定时器1中断; {
unsigned char i;
TF0=0; TL0=0xb0; TH0=0x3c; i++; if(i==20) //1秒钟到; { second++; i=0; } if(second==60)//1分钟; { second=0; minute++; } if(minute==60)//1小时; { minute=0; hour++; } if(hour==24) //1天; { hour=0;
day++;
}
switch(month) //1月;
{ case 4: case 6: case 9: case 11:if(day>30){month++;day=1;}break;//4,6,9,11月30天; case 2:{if(leap(year)) if(day>29){month++;day=1;} //闰年29天; else if(day>28){month++;day=1;}//非半闰年28天; }break; default:if(day>31){month++;day=1;}//1,3,5,7,8,10,12月30天; if(month>12) //1年; {year++; month=1;
}
}
}
void fengming() //蜂鸣器;
{
int m,n;
for(m=0;m
{p1_4=!p1_4;
for(n=0;n
}
}
int leap(unsigned int year) //判断闰年函数; {
int leap;
if(year%4==0&&year%100!=0||(year%400==0)) leap=1;
else leap=0;
return(leap);
}