万年历程序和电路图Word下载.docx
《万年历程序和电路图Word下载.docx》由会员分享,可在线阅读,更多相关《万年历程序和电路图Word下载.docx(45页珍藏版)》请在冰豆网上搜索。
Lcd液晶能一屏把本设计所要显示的信息表达完整。
而且lcd耗电小,体积小很适合与室使用。
本设计的要求日期时间以数字表示、星期用3位英文字符表示共21个字符,lcd1602能显示32个字符,足以完成显示功能。
Lcd1602有16个引脚,各个引脚功能见下表:
引脚号
引脚名
电平
输入/输出
作用
1
VSS
电源地
2
VDD
Vcc
电源〔+5v〕
3
VEE
比照调整电压
4
RS
0/1
输入
0=输入指令,1=输入数据
5
R/W
0=向lcd写入数据或指令
1=从lcd读取数据
6
E
1,1->
使能信号,1=读取信息
1->
0〔下降沿〕执行指令
7
DB0
数据总线line0
8
DB1
数据总线line1
9
DB2
数据总线line2
10
DB3
数据总线line3
11
DB4
数据总线line4
12
DB5
数据总线line5
13
DB6
数据总线line6
14
DB7
数据总线line7
15
A
Lcd背光正极
K
Lcd背光负极
表1-1lcd1602引脚及功能
2〕.按键与蜂鸣器
本设计选用轻触按钮,型号为XDJT1102S。
选用压电式蜂鸣器型号PT-1540PRoHS。
3〕.控制芯片
根据lcd1602液晶显示器与按键的引脚I/O数量以及扩展功能的要求,所选控制芯片至少需要三组I/O口,本设计功能简单,所以程序量较小,AT89C51是一种带4K字节闪存只读存储器的低电压、高性能CMOS8位微处理器。
AT89c51是51系列根本的控制芯片,能满足本设计的要求,性价比也要低于51系列其他的芯片。
4〕.日期时间生成模块
为了减少本钱,使用51的部定时器中断再配合软件计数的方式来生成一秒的时间。
1.2软件设计
1.2.1编程环境及语言:
本设计的原理图实现是在proteus中实现的,本设计的程序在keilUvision4环境中进展编程与调试的,keilUvision4与proteus联调能有效的对各个设计进展测试。
编程语言51C。
1.2.2程序框图
图1-2程序框图
1〕.time0中断用于生成时间,具体由程序和硬件共同产生1秒的时间。
2〕.两个外部中断能进展时间日期的切换,和对时间日期进展调整,播放音乐,能满足对按键设置的要求。
3〕.主函数能及时在液晶屏上显示默认的时间和调整后的时间。
第二章系统设计
2.1硬件设计
2.1.1振荡电路
本设计51芯片选用部振荡器方式。
由于本设计的时间由部定时器中断与软件计数相结合产生的,所以从计算方便以及系统的效率上考虑,本设计选用12MHz频率的晶振,电路原理图如下:
图2-1AT89C51的振荡电路
经厂家推荐稳定电路的电容C1,C2=30pF+-10p〔附录资料AT89C51.PDF第4-32页〕。
2.1.2复位电路
本设计使用上电复位电路。
单片机晶振为12MHz,起振时间将近1ms,单片机1个机器周期的时间为1us。
单片机每次上电复位所需的最短延时应该不小于treset。
这里,treset等于上电延时与起振延时之和。
从实际上讲,延迟一个treset往往还不够,不能够保障单片机有一个良好的工作开端。
复位电路把单片机锁定在复位状态上并且维持一个延时〔记作TRST〕,以便给予电源电压从上升到稳定的一个等待时间;
在电源电压稳定之后,再插入一个延时,给予时钟振荡器从起振到稳定的一个等待时间;
在单片机开场进入运行状态之前,还要至少推迟2个机器周期的延时间。
单片机是高电平的时候复位,一般是用电阻和电容组成的,电容充电的时RST复位端为高电平,此时单片机开场复位..电容充电完成,此时单片机复位完成。
由此电容值可取22uf,电阻值取10KΩ。
详细见附录文件〔电容充电时间的计算方法.pdf〕
图2-2AT89C51上电复位电路
2.1.3按键
选用两个轻触按键与单片机P3^2〔int0〕、P3^3〔int1〕两个引脚相接组成的独立按键。
2.1.4lcd显示电路
1〕.lcd的选用
本设计选用型号为LCD-016M002L显示器。
具体资料见附录文件LCD-016M002L.pdf。
2〕.lcd与51引脚的连接
Lcd1602引脚
AT89C51引脚
LCD1602引脚
P2^0
P0^3
P2^1
P0^4
P2^2
P0^5
P0^0
P0^6
P0^1
P0^7
P0^2
表2-1lcd1602与51的连接
VEE为液晶显示器比照度调整端,接正电源时比照度最弱,接地时比照度最高,比照度过高时会产生“鬼影〞,使用时可以通过一个10K的电位器调整比照度。
图2-31602液晶与单片机接口电路
2.1.5音乐播放电路
此模块采用蜂鸣器实现,蜂鸣器驱动电流为20mA,单片机的P1口输出电流为26mA能够直接驱动蜂鸣器。
只要编写相应的程序即可实现发出不同频率的声音,蜂鸣器与单片机的接口电路,蜂鸣器接线与单片机P1^7引脚相连。
图2-5蜂鸣器电路
2.1.6P0上拉电阻计算
由于lcd数据口是与51的P0口连接的,在P0口做一般I/O使用时要外接上拉电阻。
根据LCD-016M002L显示器的伏安特性,要保证上拉电阻明显小于lcd的阻抗,以使高电平时输出有效
Lcd工作电压:
+5V
Lcd工作电流:
20mA
lcd阻抗=5v/20mA=2.5kΩ。
上拉电阻值可以取2.2kΩ〔详细见附录资料上拉电阻下拉电阻的总结.doc〕,由于需要八个一样的上拉电阻如图2-6所示,也可用一个排阻。
具体型号见附录表。
图2-6上拉电阻
2.2软件设计
2.2.1程序流程图
图2-6主函数流程图2-7time0中断效劳程序流程
是
否
图2-8int1中断效劳流程
图2-9int0中断效劳流程
2.2.2流程的各个模块设计
全局变量
datetime数组保存日期时间星期的容器。
min[2],hour[2],day[2],mon[2],year[4];
调整时候使用的中间变量。
buffer_date[16],buffer_time[16]显示缓冲区。
1〕.定时器中断效劳程序
定时器1计时一秒。
计数初值的计算方法如下:
设晶振频率为f,那么定时/计数器计数频率为f/12,定时/计数器的计数总次数T_all在方式1为216=65536,定时间隔为T,计数初值为a,那么有a=-T×
f/12
将计数初值a分别赋给加1计数器TH0、TL0:
TH0=〔T_all-a〕/256;
TL0=〔T_all-a〕%256;
本设计晶振频率为12MHz,定时间隔为50ms,所以
a=-0.05*12000000/12
a=50000
TH0=〔65536-50000〕/256
TL0=〔65536-50000〕%256
1s就要计20次,中断效劳程序如下:
voidtime0()interrupt1using3
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
if(++count==20)//计时一秒
{
count=0;
DATATIME();
//按实际规那么日期时间更新
}
}
2〕.int0中断效劳程序
按键1接在了int0对应的引脚P3^2。
按键2接在了int1对应的引脚P3^3。
Int0为电平触发方式,int1为边沿触发方式。
由于按键2是一键多功能,具体功能由状态标志FLAG说明。
所以在int0中断效劳程序中要有对FLAG标志的置位及清位的操作。
在调整期间为了时间准确定时器不能工作,为了对日期时间的各位值进展循环调整,设置了一个计数器,它的值说明要调整的是哪位。
终端效劳程序如下:
voidint0_K1()interrupt0
TR0=0;
//定时器停顿
um++;
//计数器加1
FLAG=1;
//标志位置位
datetime[0]=0;
while(K1==0);
//等待按键松开
if(um>
12)//调整完毕,各个状态复原
TR0=1;
um=-1;
FLAG=0;
3〕.int1中断效劳程序
Int1只要判断标志位执行任务就行了,中断效劳程序如下:
voidint1_K2()interrupt2
if(FLAG==1)//调整状态下那么位对应调整位的增值,正常状态那么播放音乐
EDIT_datetime(um);
//按计数器的值调整对应位
else
ShowString(0x00,0,"
"
);
//播放音乐是屏幕上的字幕
ShowString(0x00,1,happy);
music();
Delayms(500);
4〕.主函数程序
主函数对各个中断及lcd1602初始化后,就一直进展显示工作了,显示的时候判断标志FLAG以区别是正常显示还是闪烁显示。
主程序如下:
voidmain()
IE=0X87;
//开中断ea=1,et0=1,et1=1,ex0=1
TCON=0X04;
//it1为边沿触发,it0电平触发
IP=0X0a;
//定时器0,1为高优先级
TMOD=0X01;
//设置定时器0方式1
TH0=(65536-50000)/256;
Initialize_LCD();
//初始化LCD
TR0=1;
while
(1)
buffer_value();
//设置显示缓存区的容
if(FLAG)//调整时对应调整位置闪烁
if(++refresh==5)
{
refresh=0;
Flash(um);
}
DIS_buffer();
//将显示缓冲区的容输出到lcd上
5〕.星期在调整是是自动更新的,这要归功于蔡勒公式。
算法如下〔C51表示〕:
unsignedcharweek()
{
unsignedinty,m,c,s;
y=datetime[5]%100;
c=datetime[5]/100;
if(datetime[4]==1)
{m=13;
y--;
if(datetime[4]==2)
{m=14;
}
s=y+y/4+c/4-2*c+26*(m+1)/10+datetime[3]-1;
//蔡勒公式
s=s%7;
returns;
函数返回值:
0-星期日,1-星期一……6-星期六
6〕.闰年是自动判断的,算法:
(year%4==0)&
&
(year%100!
=0)||year%400==0
函数代码如下:
unsignedcharYnian(unsignedcharyear)
if((year%400==0||year%4==0)&
(year%100!
=0))
return1;
return0;
是闰年返回1,否那么返回0
7〕.月的天数判断,由于1,3,5,7,8,10,12每月有31天;
4,6,9,11每月有30天;
2月闰年29天,平年28天。
所以只要判断月号是哪一类的,就返回那类的值,2月用闰年判断算法判断。
unsignedcharMON(unsignedcharmonth)
switch(month)
case1:
case3:
case5:
case7:
case8:
case10:
case12:
return31;
case4:
case6:
case9:
case11:
return30;
default:
if(Ynian(datetime[5]))//二月特殊,闰年判断
return29;
else
return28;
}
返回各月的天数上限。
8〕.日期时间更新
按照实际规那么更新,60秒一分,60分一时,24小时一日,30日或31日或28日或29日一月,12月一年。
voidDATATIME()
datetime[0]++;
if(datetime[0]>
59)//秒判断
datetime[0]=0;
datetime[1]++;
if(datetime[1]>
59)//分
datetime[1]=0;
datetime[2]++;
}
if(datetime[2]>
23)//时
datetime[2]=0;
datetime[3]++;
if(datetime[3]>
MON(datetime[4]))//日
datetime[3]=1;
datetime[4]++;
if(datetime[4]>
12)//月
datetime[4]=1;
datetime[5]++;
//年
//时刻为进入调整状态作准备,将当前日期时间值赋给调整时的变量
datetime[6]=week();
min[0]=datetime[1]%10;
min[1]=datetime[1]/10;
hour[0]=datetime[2]%10;
hour[1]=datetime[2]/10;
day[0]=datetime[3]%10;
day[1]=datetime[3]/10;
mon[0]=datetime[4]%10;
mon[1]=datetime[4]/10;
year[0]=datetime[5]%10,year[1]=datetime[5]%100/10;
year[2]=datetime[5]%1000/100,year[3]=datetime[5]/1000;
9〕.调整日期时间函数,利用um计数器指定当前的调整属性,函数源代码如下:
voidEDIT_datetime(charum)
switch(um)
case0:
min[0]++;
if(min[0]>
9)//分低位调整限制
min[0]=0;
break;
min[1]++;
if(min[1]>
5)//分高调整限制
min[1]=0;
case2:
hour[0]++;
if(hour[0]>
9)//时低位调整限制
hour[0]=0;
hour[1]++;
if(hour[1]>
2)//时高位调整限制
hour[1]=0;
day[0]++;
if(day[0]>
9)//日低位调整限制
day[0]=0;
day[1]++;
if(day[1]>
3)//日高位调整限制
day[1]=0;
mon[0]++;
if(mon[0]>
9)//月低位调整限制
mon[0]=0;
mon[1]++;
if(mon[1]>
1)//月高位调整限制
mon[1]=0;
year[0]++;
//年调整
if(year[0]>
9)
year[0]=0;
year[1]++;
if(year[1]>
year[1]=0;
year[2]++;
if(year[2]>
year[2]=0;
year[3]++;
if(year[3]>
year[3]=0;
}
datetime[1]=min[0]+min[1]*10;
//调整后将datetime实时更新
datetime[2]=hour[0]+hour[1]*10;
datetime[3]=day[0]+day[1]*10;
datetime[4]=mon[0]+mon[1]*10;
datetime[5]=year[0]+year[1]*10+year[2]*100+year[3]*1000;
调整后能及时让datetime更新。
10〕.将datetime的日期时间星期按照格式放到缓冲区上,格式为:
YYYY-MM-DDweek
HH:
MM:
函数源代码如下:
voidbuffer_value()
Format_Datetime(datetime[0],buffer_time+6);
//将年月日时分秒星期,
Format_Datetime(datetime[1],buffer_time+3);
//放到对应缓冲区的位置上
Format_Datetime(datetime[2],buffer_time+0);
//以待显示
Format_Datetime(datetime[3],buffer_date+8);
Format_Datetime(datetime[4],buffer_date+5);
Format_Datetime(datetime[5]%100,buffer_date+2);
Format_Datetime(datetime[5]/100,buffer_date+0);
buffer_date[13]=week1[week()];
buffer_date[14]=week2[week()];
buffer_date[15]=week3[week()];
Lcd1602显示的是字符,所以用函数转换。
Format_Datetime(数组1,数组2)
功能:
将数组1的数值转换为字符放到数组2。
11〕.闪烁显示,在主函数显示配合延时清空,写入缓冲区的容,到达闪烁效果。
具体哪位闪烁还是有um决定。
voidFlash(charn)
switch(n)
buffer_clr(0,4);
//对应分低位置,清空
buffer_clr(0,3);
buffer_clr(0,1);
buffer_clr(0,0);
buffer_clr(1,9);
buffer_clr(1,8);
buffer_clr(1,6);
buffer_clr(1,5);
buffer_clr(1,3);
buffer_clr(1,2);