基于单片机的万年历的液晶显示.docx
《基于单片机的万年历的液晶显示.docx》由会员分享,可在线阅读,更多相关《基于单片机的万年历的液晶显示.docx(23页珍藏版)》请在冰豆网上搜索。
基于单片机的万年历的液晶显示
基于单片机的万年历的液晶显示
一引言
单片机应用技术飞速发展,纵观我们现在生活的各个领域,从导弹的导航装置,到飞机上各种仪表的控制,从计算机的网络通讯与数据传输,到工业自动化过程的实时控制和数据处理,以及我们生活中广泛使用的各种智能IC卡、电子宠物等,这些都离不开单片机。
单片机是集CPU,RAM,ROM,定时,计数和多种接口于一体的微控制器。
它体积小,成本低,功能强,广泛应用于智能产业和工业自动化上。
而STC系列单片机是各单片机中最为典型和最有代表性的一种。
这次毕业设计通过对它的学习,应用,从而达到学习、设计、开发软、硬的能力。
本设计通过对一个基于单片机的能实现万年历功能电子时钟的设计,从而达到学习、了解单片机相关指令在各方面的应用。
系统由主控制器STC89C54、发声电路、1602、按键电路、晶振电路和复位电路等部分构成,能实现时钟日历显示的功能,能进行时、分、秒的显示。
STC89C54是89C51的内核,ROM容量为16K。
电子万年历是一种非常广泛日常计时工具,对现代社会越来越流行。
它可以对年、月、日、周日、时、分、秒进行计时,对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、周日、时、分、秒和温度等信息,还具有时间校准等功能。
该电路采单片机作为核心,功耗小,电压可选用5V电压供电。
二硬件结构
电源,复位电路和晶振是构成单片机运作的最基本的3要素,缺一不可。
没有复位电路,程序将不能正常运行甚至不能运行,晶振提供了单片机的振荡周期。
复位电路为一个RC电路。
本设计的晶振为12M,执行一个指令的周期为1US,也可以更换晶振来改变单片机的机器周期。
显示电路用的是1602液晶显示,液晶显示器以其微功耗、体积小、显示内容丰富、超薄轻巧的诸多优点,在各类仪表和低功耗系统中得到广泛的应用。
1602能显示数字和字母两行20字等等。
与数码管相比该模块有如下优点:
1.位数多,可显示32位,32个数码管体积相当庞大了2.显示内容丰富,可显示所有数字和大、小写字母3.程序简单,如果用数码管动态显示,会占用很多时间来刷新显示,而1602自动完成此功能。
因此在本设计中用1602液晶显示来代替数码管。
蜂鸣器为闹钟,本设计可以设置多个报警时间,报警时间为1分钟。
按键的设置用来调整时间,一共三个按键,按键K1用来选择要调的对象,比如时分秒,每按一次K2,所选择的对象加一,每按一次K3,所选择的对象加减一。
在进行调试的时候,单片机的中断停止。
三电路构成及其连接
1复位电路:
复位电路的应用对象一般是带有复位功能的集成电路(比如单片机之类的)。
复位电路的功能就是根据芯片的要求,产生一个高电平或者低电平,并且保持一定的时间,激发芯片的复位功能,达到使芯片产生复位动作的效果。
至于电路,非常复杂,多种多样,同样的芯片都有N种电路,像单片机的就有上电复位,按键阻容复位等多种电路,还有在此基础上衍生出来的各种性能更加优良、可靠性更高的改进型电路。
复位就是使一个系统进入某个已知的状态而已。
通常的习惯,数字电路复位是指使其进入“0”状态。
复位电路的作用是为了使电路从一个“已知”并且“确切”的地方开始运作。
复位电路为RC电路,当按钮按下去的时候,引脚保持高电平,当连续2个机器周期为高电平时,电路复位。
该引脚接在单片机的RST端口。
2晶振电路:
晶振在电气上可以等效成一个电容和一个电阻并联再串联一个电容的二端网络,电工学上这个网络有两个谐振点,以频率的高低分其中较低的频率为串联谐振,较高的频率为并联谐振。
由于晶体自身的特性致使这两个频率的距离相当的接近,在这个极窄的频率范围内,晶振等效为一个电感,所以只要晶振的两端并联上合适的电容它就会组成并联谐振电路。
这个并联谐振电路加到一个负反馈电路中就可以构成正弦波振荡电路,由于晶振等效为电感的频率范围很窄,所以即使其他元件的参数变化很大,这个振荡器的频率也不会有很大的变化。
晶振有一个重要的参数,那就是负载电容值,选择与负载电容值相等的并联电容,就可以得到晶振标称的谐振频率。
晶振电路由石英晶振并联2个电容,2个电容的容值为30PF,作用是让工作在并联谐振状态。
晶振电路分别接在单片机的18,19脚。
3STC89C545单片机:
STC89C54可以代替AT系列,功能更强,速度更快,寿命更长,价格更低。
外型:
40个引脚,双列直插DIP-40。
STC89C54可以完成ISP在线编程功能。
在这个设计中,利用定时器来进行计时,定时器中断一次,为50MS,中断20次就为1S,这是做万年历最基本的思路。
得到一秒,然后计时,60进位,分加一,分到了60,时加一,以此类推。
这样就实现了内部的计时。
41602液晶显示:
1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,这些字符有:
阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。
1602识别的是ASCII码,试验可以用ASCII码直接赋值。
1
VSS
一般接地
2
VDD
接电源(+5V)
3
V0
液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度)。
4
RS
RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器。
5
R/W
R/W为读写信号线,高电平
(1)时进行读操作,低电平(0)时进行写操作。
6
E
E(或EN)端为使能(enable)端,下降沿使能。
7
DB0
底4位三态、双向数据总线0位(最低位)
8
DB1
底4位三态、双向数据总线1位
9
DB2
底4位三态、双向数据总线2位
10
DB3
底4位三态、双向数据总线3位
11
DB4
高4位三态、双向数据总线4位
12
DB5
高4位三态、双向数据总线5位
13
DB6
高4位三态、双向数据总线6位
14
DB7
高4位三态、双向数据总线7位(最高位)(也是busyflag)
15
BLA
背光电源正极
16
BLK
背光电源负极
1602通过D0~D7的8位数据端传输数据和指令。
显示模式设置:
00110000[0x38]设置16×2显示,5×7点阵,8位数据接口;显示开关及光标设置:
00001DCBD显示(1有效)、C光标显示(1有效)、B光标闪烁(1有效)000001NSN=1(读或写一个字符后地址指针加1&光标加1),N=0(读或写一个字符后地址指针减1&光标减1),S=1且N=1(当写一个字符后,整屏显示左移),S=0当写一个字符后,整屏显示不移动数据指针设置:
数据首地址为80H,所以数据地址为80H+地址码(0-27H,40-67H),其他设置:
01H(显示清屏,数据指针=0,所有显示=0);02H(显示回车,数据指针=0)。
写指令08H是关闭显示,写指令01H显示清屏,写指令06H光标移动设置,写指令0cH显示开及光标设置。
5发声电路:
蜂鸣器采用ULN2003驱动,ULN2003的每一对都串联一个2.7K的基极电阻,在5V的工作电压下它能与TTL和CMOS电路直接相连,可以直接处理原先需要标准逻辑缓冲器来处理的数据。
ULN2003工作电压高,工作电流大,灌电流可达500mA,并且能够在关态时承受50V的电压,输出还可以在高负载电流并行运行。
ULN2003内部还集成了一个消线圈反电动势的二极管,可用来驱动继电器。
它是双列16脚封装,NPN晶体管矩阵,最大驱动电压=50V,电流=500mA,输入电压=5V,适用于TTLCOMS,由达林顿管组成驱动电路。
ULN是集成达林顿管IC,内部还集成了一个消线圈反电动势的二极管,它的输出端允许通过电流为200mA,饱和压降VCE约1V左右,耐压BVCEO约为36V。
用户输出口的外接负载可根据以上参数估算。
采用集电极开路输出,输出电流大,故可直接驱动继电器或固体继电器,也可直接驱动低压灯泡。
通常单片机驱动ULN2003时,上拉2K的电阻较为合适,同时,COM引脚应该悬空或接电源。
ULN2003是一个非门电路,包含7个单元,单独每个单元驱动电流最大可达350mA,9脚可以悬空。
当1脚输入高电平,16脚输出为低电平。
蜂鸣器发声。
6按键电路
本设计一共有3个按钮,分别为K1,K2,K3,按键的设置用来调整时间,一共三个按键,按键K1用来选择要调的对象,比如时分秒,每按一次K2,所选择的对象加一,每按一次K3,所选择的对象加减一。
在进行调试的时候,单片机的中断停止,时间是不会计时的,光标闪烁。
四电路原理图
5软件设计
在这个设计中,利用定时器来进行计时,定时器中断一次,为50MS,中断20次就为1S,这是做万年历最基本的思路。
得到一秒,然后计时,60进位,分加一,分到了60,时加一,以此类推。
这样就实现了内部的计时。
中断程序interrupt1就是计时程序。
在MAIN函数中,通过不断查询keyscan函数;来判断是否有键按下,如果K1键按下,进入keyscan函数,次是中断关闭,每按一次则计数加1,每次加一则选择不同的对象,递推,一个周期,中断打开,时间正常运行,在调时期间,K2为加1功能,K3为减1功能。
六总结
通过一个月的努力,在老师与同学们的指导帮助下,本次的设计顺利的完成了。
这次做论文的经历也会使我终身受益,我感受到做论文是要真真正正用心去做的一件事情,是真正的自己学习的过程和研究的过程,没有学习就不可能有研究的能力,没有自己的研究,就不会有所突破。
希望这次的经历能让我在以后学习中激励我继续进步。
不积跬步何以至千里,本设计能够顺利的完成,也归功于导师的认真负责,使我能够很好的掌握和运用专业知识,并在设计中得以体现。
正是有了他们的悉心帮助和支持,才使我的毕业论文工作顺利完成。
附录
#include"reg52.h"
#defineucharunsignedchar
#defineuintunsignedint
ucharcodetable[]="0000-00-00000";//初始化日期和星期
ucharcodetable1[]="00:
00:
00";//初始化时间
ucharcodetable2[][3]={"MOD","TUE","WED","THU","FRI","SAT","SUN"};//定义星期数组
uintcodetable3[]={31,31,28,31,30,31,30,31,31,30,31,30,31};//定义月数组
//以下三个是定义LCD的引脚
sbitlcden=P2^6;
sbitlcdwrite=P2^5;
sbitlcdrs=P2^4;
//定义四个功能开关
sbits1=P1^1;
sbits2=P1^2;
sbits3=P1^3;
sbits=P1^7;
sbitlb=P1^4;
//定义时间、时、分、秒、年、月、日等变量
uchartime;
charhour,min,sec,day,mon,year1,year2,xingqi,num;
//延时程序
voiddelay(uintz)
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
//lcd的写指令
voidwrite_com(ucharcom)
{
lcdrs=0;
lcden=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
//lcd的写数据
voidwrite_date(uchardate)
{
lcdrs=1;
lcden=0;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
voidinit()
{
ucharnum;
lcden=0;
lcdwrite=0;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
for(num=0;num<15;num++)
{
write_date(table[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<12;num++)
{
write_date(table1[num]);
delay(5);
}
TMOD=0x11;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
TH1=(65536-500)/256;
TL1=(65536-500)%256;
ET1=1;
}
//时间走动的显示
write_hms(ucharadd,ucharda)
{
ucharshi,ge;
shi=da/10;
ge=da%10;
write_com(0x80+0x40+add);
write_date(0x30+shi);
write_date(0x30+ge);
}//日期走动的显示
write_ymd(ucharadd,ucharda)
{
ucharshi,ge;
shi=da/10;
ge=da%10;
write_com(0x80+add);
write_date(0x30+shi);
write_date(0x30+ge);
}
//星期走动的显示
voidwrite_xingqi(charxingqi)
{
charnum;
write_com(0x80+12);
for(num=0;num<3;num++)
write_date(table2[xingqi][num]);
}
//键盘扫描
voidkeyscan()
{
s=0;
if(s1==0)//如果S1按下去,执行相应的操作
{
delay(5);//消除抖动
if(s1==0)
{
num++;
while(!
s1);
if(num==1)
{
TR0=0;//关定时器
write_com(0x80+0x40+11);//选定秒
write_com(0x0f);
}
}
if(num==2)//选定分
{
write_com(0x80+0x40+8);
}
if(num==3)//选定时
{
write_com(0x80+0x40+5);
}
if(num==4)//选定星期
{
write_com(0x80+12);
write_com(0x0f);
}
if(num==5)//选定日
{
write_com(0x80+10);
}
if(num==6)//选定月
{
write_com(0x80+7);
}
if(num==7)//选定年后两位
{
write_com(0x80+4);
}
if(num==8)//选定年前两位
{
write_com(0x80+2);
}
if(num==9)//恢复
{
num=0;
write_com(0x0c);
TR0=1;//重复开定时器
}
}
if(num!
=0)
{
if(s2==0)//如果s2按下调理参数
{
delay(5);
if(s2==0)
{
while(!
s2);
if(num==1)
{
sec++;//调秒并显示加1
if(sec==60)
sec=0;
write_hms(10,sec);
write_com(0x80+0x40+11);
}
if(num==2)//调分并显示加1
{
min++;
if(min==60)
min=0;
write_hms(7,min);
write_com(0x80+0x40+8);
}
if(num==3)//调时并显示加1
{
hour++;
if(hour==24)
hour=0;
write_hms(4,hour);
write_com(0x80+0x40+5);
}
if(num==4)//调星期并显示加1
{
xingqi++;
if(xingqi==7)
xingqi=0;
write_xingqi(xingqi);
write_com(0x80+14);
}
if(num==5)
{
day++;//调日并显示加1
if(day==table3[mon]+1)
day=1;
write_ymd(9,day);
write_com(0x80+10);
}
if(num==6)//调月并显示加1
{
mon++;
if(mon==13)
mon=1;
write_ymd(6,mon);
write_com(0x80+7);
}
if(num==7)//调年前位并显示加1
{
year1++;
if(year1==100)
year1=0;
write_ymd(3,year1);
write_com(0x80+4);
}
if(num==8)//调年后位并显示加1
{
year2++;
if(year2==100)
year2=0;
write_ymd(1,year2);
write_com(0x80+2);
}
}
}
if(s3==0)
{
delay(5);
if(s3==0)
{
while(!
s3);
if(num==1)//调秒并显示减1
{
sec--;
if(sec==-1)
sec=59;
write_hms(10,sec);
write_com(0x80+0x40+11);
}
if(num==2)//调分并显示减1
{
min--;
if(min==-1)
min=59;
write_hms(7,min);
write_com(0x80+0x40+8);
}
if(num==3)//调时并显示减1
{
hour--;
if(hour==-1)
hour=23;
write_hms(4,hour);
write_com(0x80+0x40+5);
}
if(num==4)//调星期并显示减1
{
xingqi--;
if(xingqi==-1)
xingqi=6;
write_xingqi(xingqi);
write_com(0x80+14);
}
if(num==5)//调日并显示减1
{
day--;
if(day==0)
day=table3[mon];
write_ymd(9,day);
write_com(0x80+10);
}
if(num==6)//调月并显示减1
{
mon--;
if(mon==0)
mon=12;
write_ymd(6,mon);
write_com(0x80+7);
}
if(num==7)//调年前两拉并显示减1
{
year1--;
if(year1==-1)
year1=0;
write_ymd(3,year1);
write_com(0x80+4);
}
if(num==8)//调年后两拉并显示减1
{
year2--;
if(year2==-1)//减1到0重设为0
year2=0;
write_ymd(1,year2);
write_com(0x80+2);
}
}
}
}
}
//主函数
voidmain()
{
init();
while
(1)
{if(min==1)
{TR1=1;}
if(min==2)
{TR1=0;
}
keyscan();
}
}
voidtimer1()interrupt3
{TL1=(65536-500)%256;
TH1=(65536-500)/256;
lb=~lb;}
//中断定时
voidtimer0()interrupt1
{
TL0=(65536-50000)%256;//定时50ms
TH0=(65536-50000)/256;
time++;
if(time==20)//1s定时到
{
time=0;
sec++;//秒加1
if(sec==60)
{
sec=0;
min++;//分钟加1
if(min==60)
{
min=0;
hour++;//小时加1
if(hour==24)
{
hour=0;
day++;//日期的日加1
xingqi++;//星期加1
if(day==table3[mon]+1)
{
day=1;
mon++;//月加1
if(mon==13)//加到13重设为1
{
mon=1;
year1++;
if(year1==100)//年前两位加到100重设为0
{
year1=0;
year2++;//年后两位加1
if(year2==100)//年后两位加到100重设为0
{
year2=0;
}
write_ymd(1,year2);
}
write_ymd(3,year1);
}
write_ymd(6,mon);
}
write_ymd(9,day);
if(xingqi==7)//如果星期到七就设为0
xingqi=0;
write_xingqi(xingqi);
}
write_hms(4,hour);
}
write_hms(7,min);
}
write_hms(10,sec);
}
}