基于单片机的电子万年历设计制作1111111.doc
计算机控制技术课程答辩论文题 目基于单片机的万年历设计 学号: 姓名: 日期:2015.12.28摘 要单片机就是微控制器,是面向应用对象设计、突出控制功能的芯片。单片机接上晶振、复位电路和相应的接口电路,装载软件后就可以构成单片机应用系统。本设计就是应用单片机强大的控制功能制作而成的电子万年历,该电子万年历包括三大功能:实时显示年、月、日、星期、时、分、秒;计时芯片采用DALLAS公司的涓细充电时钟芯片DS1302,该芯片通过简单的串行通信与单片机进行通信,时钟/日历电路能够实时提供年、月、日、时分、秒信息,采用双电源供电,当外部电源掉电时能够利用后备电池准确计时。显示器件采用通用型1602液晶,可显示32个字符,如果使用数码管来做显示器件需消耗大量的系统资源,因此采用低功耗的1602液晶,该液晶显示方便,功能强大,完全能满足数字万年历的显示要求。通过此次设计能够更加牢固的掌握单片机的应用技术,增强动手能力、硬件设计能力以及软件设计能力。关键词:AT89C51、1602液晶、DS1302、万年历目录第1章 绪论51.1选题的背景51.2课题的研究目的与意义5第2 章 总体方案论证与设计62.1液晶显示模块62.2实时时间计算模块62.3设置模块6第3章 系统硬件设计83.1LCD显示模块设计83.2时间计算模块设计93.3设置模块123.4硬件电路设计13第4章 系统软件设计144.1程序框图如下:14第5章 系统硬件PROTEUS仿真原理图155.1显示实时时间155.2时间设置155.3星期设置165.4年月日设置165.5原理图17结论18参考文献19附录一20源程序20IV第1章 绪论1.1选题的背景 随着科技的快速发展,时间的流逝,从观太阳、摆钟到现在电子钟,人类不断研究,不断创新纪录。它可以对年、月、日、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小。对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、时、分、秒等信息,还具有时间校准等功能。该电路采用AT89C51单片机作为核心,功耗小,能在3V的低压工作,电压可选用35V电压供电。 此万年历具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。 1.2课题的研究目的与意义 二十一世纪是数字化技术高速发展的时代,而单片机在数字化高速发展的时代扮演着极为重要的角色。电子万年历的开发与研究在信息化时代的今天亦是当务之急,因为它应用在学校、机关、企业、部队等单位礼堂、训练场地、教学室、公共场地等场合,可以说遍及人们生活的每一个角落。由于社会对信息交换不断提高的要求及高新技术的逐步发展,促使电子万年历发展并且投入市场得到广泛应用。第2 章 总体方案论证与设计 本系统以AT89C51单片机为控制核心,通过与DS1302和DS18B20通信获取实时时间和实时环境温度,并将得到的数据通过1602液晶显示出来,同时通过相应的按键调整相应的值。因此本设计可分为一下模块:显示模块、实时时间计算模块、设置模块(时间设置模块)下面对各个模块逐一进行论证分析:2.1液晶显示模块方案一:数码管是利用发光二极管的特性组合而成数字显示器件,通过控制相应的二极管的状态显示相应的数字。要使数码管正常显示就得有驱动电路驱动相应的段码,数码管的现实方式可分为静态显示和动态显示,静态显示方式只适合显示单个的数字,因此本设计应采用动态显示方式。由于动态显示方式利用的是人眼视觉暂留的特性,扫描的时间应不大于20毫秒,占用系统资源大,而且显示的个数和字型有限,在本设计中不易采用。方案二:1602液晶也叫1602字符型液晶 它是一种专门用来显示字母、数字、符号等的点阵型液晶模块 它有若干个5X7或者5X11等点阵字符位组成,每个点阵字符位都可以显示一个字符。每位之间有一个点距的间隔,每行之间也有也有间隔,起到了字符间距和行间距的作用。1602的驱动电路带有11条指令,可以很方便的控制液晶的现实效果如:清屏、左移右移、光标显示。而且1602显示的字符在下一条指令为到来之前不会改变,也就是能够维持显示的字符,1602液晶占用的系统资源也少。综合比较上述两种方案,应采用1602液晶组成本设计的显示模块。2.2实时时间计算模块方案一:AT89C51单片机内部带有定时/计数功能,此定时功能是通过对外部晶振的脉冲进行计数,从而达到计时功能,只要使用11.0592的晶振就能实现零误差的计时,因此可以利用此功能实现计时,但因为只有单一的计时功能要实现“万年历”的功能需要较复杂的程序,而且如果单片机掉电无法继续进行计时,所以使用不便。方案二:DS1302是美国DALLAS公司推出的一种高性能、低功耗的实时时钟芯片,附加31字节静态RAM,采用SPI三线接口与CPU进行通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。实时时钟可提供秒、分、时、日、星期、月和年,一个月小与31天时可以自动调整,且具有闰年补偿功能。工作电压宽达2.55.5V。采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后备电源进行涓细电流充电的能力。利用单片机强大的控制功能就可实现实时计时的功能,而且消耗的系统资源少,程序简单。综合上述两种方案,宜采用方案(2)实现实时计时功能。2.3设置模块因设置模块只需编写相应的程序外加相应的按键即可实现,实现方法较简单,在此不再论述。总体方案组成框图显示模块(1602液晶)AT89C51时间计算模块DS1302电源(5v)设置模块(独立按键)第3章 系统硬件设计3.1LCD显示模块设计(1)、1602液晶功耗较小可直接与单片机接口相接,电源直接与电源电路相接,使用单片机的P0口和P1口与1602进行通信。(2)、1602相应功能特性介绍+5V电压,对比度可调内含复位电路提供各种控制命令,如:清屏、字符闪烁、光标闪烁、显示移位等多种功能有80字节显示数据存储器DDRAM内建有160个5X7点阵的字型的字符发生器CGROM8个可由用户自定义的5X7的字符发生器CGRAM(3)、1602引脚介绍(4)、1602液晶与单片机接口电路3.2时间计算模块设计DS1302通过三根口线实现与单片机的通信,因DS1302功耗很小,即使电源掉电后通过3V的纽扣电池仍能维持DS1302精确走时。(1)DS1302特性介绍DS1302是美国DALLAS公司推出的一种高性能、低功耗的实时时钟芯片,附加31字节静态RAM,采用SPI三线接口与CPU进行通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。实时时钟可提供秒、分、时、日、星期、月和年,一个月小与31天时可以自动调整,且具有闰年补偿功能。工作电压宽达2.55.5V。采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后备电源进行涓细电流充电的能力。(2)DS1302引脚介绍 各引脚的功能为:8 、Vcc1:备用电池端;1、Vcc2:5V电源。当Vcc2>Vcc1+0.2V时,由Vcc2向DS1302供电,当Vcc2< Vcc1时,由Vcc1向DS1302供电。7、 SCLK:串行时钟,输入; 6、I/O:数据输入输出口;5、CE/RST:复位脚2 3、X1、X2 是外接晶振脚 (32.768KHZ的晶振)4、地(GND)(3)DS1302有关日历、时间的寄存器 寄存器的说明如下:1、秒寄存器(81h、80h)的位7定义为时钟暂停标志(CH)。当初始上电时该位置为1,时钟振荡器停止,DS1302处于低功耗状态;只有将秒寄存器的该位置改写为0时,时钟才能开始运行。2、小时寄存器(85h、84h)的位7用于定义DS1302是运行于12小时模式还是24小时模式。当为高时,选择12小时模式。在12小时模式时,位5是 ,当为1时,表示PM。在24小时模式时,位5是第二个10小时位3、控制寄存器(8Fh、8Eh)的位7是写保护位(WP),其它7位均置为0。在对任何的时钟和RAM的写操作之前,WP位必须为0。当WP位为1时,写保护位防止对任一寄存器的写操作。也就是说在电路上电的初始态WP是1,这时是不能改写上面任何一个时间寄存器的,只有首先将WP改写为0,才能进行其它寄存器的写操作。 (4)DS1302控制字介绍控制字的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入到DS1302中。位6:如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1(A4A0):指示操作单元的地址;位0(最低有效位):如为0,表示要进行写操作,为1表示进行读操作。读数据:读数据时在紧跟8位的控制字指令后的下一个SCLK脉冲的下降沿,读出DS1302的数据,读出的数据是从最低位到最高位。写数据:控制字总是从最低位开始输出。在控制字指令输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入也是从最低位(0位)开始。位0(最低有效位):为1表示进行读操作。 如为0,表示要进行写操作,控制字后 SCLK 下降沿 读数据 SCLK上升沿写数据。(5)DS1302单字节读写时序介绍DS1302的数据读写是通过I/O串行进行的。当进行一次读写操作时最少得读写两个字节,第一个字节是控制字节,就是一个命令,告诉DS1302是读还是写操作,是对RAM还是对CLOK寄存器操作,以及操作的址。第二个字节就是要读或写的数据了。我们先看单字节写:在进行操作之前先得将CE(也可说是RST)置高电平,然后单片机将控制字的位0放到I/O上,当I/O的数据稳定后,将SCLK置高电平,DS1302检测到SCLK的上升沿后就将I/O上的数据读取,然后单片机将SCLK置为低电平,再将控制字的位1放到I/O上,如此反复,将一个字节控制字的8个位传给DS1302。接下来就是传一个字节的数据给DS1302,当传完数据后,单片机将CE置为低电平,操作结束。单字节读操作的一开始写控制字的过程和上面的单字节写操作是一样,但是单字节读操作在写控制字的最后一个位,SCLK还在高电平时,DS1302就将数据放到I/O上,单片机将SCLK置为低电平后数据锁存,单机机就可以读取I/O上的数据。如此反复,将一个字节的数据读入单片机。读与写操作的不同就在于,写操作是在SCLK低电平时单片机将数据放到IO上,当SCLK上升沿时,DS1302读取。而读操作是在SCLK高电平时DS1302放数据到IO上,将SCLK置为低电平后,单片机就可从IO上读取数据。(6)DS1302操作指令介绍操作说明:1 首先要通过8EH将写保护去掉,将日期,时间的初值写时各个寄存器。2 然后就可以对80H、82H、84H、86H、88H、8AH、8CH进行初值的写入。同时也通过秒寄存器将位7的CH值改成0,这样DS1302就开始走时运了。3 将写保护寄存器再写为80H,防止误改写寄存器的值。4 不断读取80H8CH的值,将它们格式化后显示到1602LCD液晶上(7)DS1302与单片机接口电路3.3设置模块设置模块采用四个按键与P14、P15、P16、P17相接组成独立按键,接口电路如下;3.4硬件电路设计第4章 系统软件设计 软件设计是本设计的关键,软件程序编写的好坏直接影响着系统运行情况的良好。因本程序涉及的模块较多,所以程序编写也采用模块化设计,C语言具有编写灵活、移植方便、便于模块化设计的特点,所以本系统的软件采用C51编写。4.1程序框图如下:判断是否有按键按下时间、日期、上限温度设置模块没有按键按下开机初始化显示欢迎界面从DS1302读取信息1602液晶显示相关信息第5章 系统硬件PROTEUS仿真原理图5.1显示实时时间5.2时间设置5.3星期设置5.4年月日设置5.5原理图结论本课题从理论到实际应用,用AT89C51单片机和时钟芯片ds1302成功的设计出了一个电子万年历。而且所设计的电子万年历设计也按当初要求的能够在PROTEUS中进行仿真,并且能够很精确的显示年历。利用DS1802测温元件进行温度实时监测。可以说该电子万年历具有很高的实用价值。 在软件设计的过程中,利用了Keil这个软件在程序录入和调试的时候的优越性,让我能够在编写软件的时候很方便的发现软件中的错误,现在已经能够使用Keil对所设计的程序进行调试。用keil生成hex文件烧录到单片机中,从而进行电路设计仿真。本次设计的电子万年历也存在的不足的地方,有待于以后的改进。参考文献1 赵长德.微型计算机原理与接口技术M.北京:机械工业出版社,1999:98-350. 2 李晓林.单片机的原理与接口技术M.北京:电子工业出版社,2015.1 3 王忠民.微型计算机原理M.西安:西安科技大学出版社,2003:15-55. 4 胡戴明.计算机组成原理M.北京:经济科学出版社,2005:43-56. 附录一源程序20#include<reg51.h>#include<intrins.h>#define uint unsigned int#define uchar unsigned charunsigned char code displaywelcome=" Welcome To My Lcd Timer"/欢迎界面/unsigned char code displaywish=" Happy Every Day _" /欢迎界面/unsigned char code overtemperature="OVERTEMPERATURE!"unsigned char code digit="0123456789"unsigned char mode,TH,TL,TN,TD,length,tempswitch,Maxtemp=40,amode,alarmmode,minutes,hours, minutea,seconds,houra=12;sbit SCLK=P10;sbit DATE=P11;sbit REST=P12;sbit SET=P14;sbit ADD=P15;sbit RED=P16;sbit CANL=P17;void delay1ms(uint i)/1ms延时/ uint j,k; while(i-) for(j=76;j>1;j-); for(k=29;k>1;k-);void delaynus(uchar n)/延时若干微妙/uchar i;for(i=0;i<n;i+);/蜂鸣器模块/ sbit beep=P36; /位定义,定义P.6位fmp/ void dely500(void) uchar i; for(i=250;i>0;i-) _nop_(); void baojing(uchar n) uchar x,i; while(n-) for(i=0;i<5;i+) for(x=0;x<200;x+) beep=beep; dely500(); for(i=0;i<3;i+) for(x=0;x<200;x+) beep=beep; dely500(); dely500(); void Write1302(uchar date)/向1302写数据/ uchar i;SCLK=0;delaynus(2);for(i=0;i<8;i+) DATE=date&0x01; SCLK=1; delaynus(2); SCLK=0; delaynus(2); date>>=1; void WriteSet1302(uchar cmd,uchar date)/根据相应命令输入相应数据/REST=0;SCLK=0;REST=1;Write1302(cmd);delaynus(5);Write1302(date);SCLK=1;REST=0;uchar Read1302(void)/读取1302数据/uchar i,date;delaynus(2);for(i=0;i<8;i+) date>>=1; if(DATE=1) date|=0x80; SCLK=1; delaynus(2); SCLK=0; delaynus(2); return date;uchar ReadSet1302(uchar cmd)/根据命令读取1302相应的值/uchar date;REST=0;SCLK=0;REST=1;Write1302(cmd);delaynus(2);date=Read1302();SCLK=1;REST=0;return date;void IntDS1302(void)/DS1302初始化/uchar flag;flag=ReadSet1302(0x81);if(flag&0x80)/判断时钟芯片是否关闭/WriteSet1302(0x8E,0x00);/根据写状态寄存器命令字,写入不保护指令/WriteSet1302(0x80,(0/10)<<4|(0%10);/根据秒寄存器命令字,写入秒的初始值;以下为分时日月/WriteSet1302(0x82,(0/10)<<4|(0%10);WriteSet1302(0x84,(0/10)<<4|(0%10);WriteSet1302(0x86,(0/10)<<4|(0%10);WriteSet1302(0x88,(0/10)<<4|(0%10);WriteSet1302(0x8c,(10/10)<<4|(0%10);WriteSet1302(0x90,0xa5);/打开充电功能 选择2k电阻充电方式/WriteSet1302(0x8E,0x80);/根据写状态寄存器命令字,写入保护指令/sbit RS=P25;sbit RW=P26;sbit E=P27;sbit BF=P07;/液晶忙检测/bit BusyTest(void)bit result;RS=0;RW=1;E=1;_nop_();_nop_();_nop_();result=BF;_nop_();_nop_();_nop_();E=0;return result;/写指令/void Write_com(uchar command)while(BusyTest()!=0);RS=0;RW=0;E=0;_nop_();_nop_();_nop_();P0=command;_nop_();_nop_();_nop_();E=1;_nop_();_nop_();_nop_();E=0;/写地址/ void Write_Address(uchar address) Write_com(address|0x80) ; delay1ms(1) ; /写数据/void Write_Date(uchar date)RS=1;RW=0;E=0;_nop_();_nop_();_nop_();P0=date;_nop_();_nop_();_nop_();E=1;_nop_();_nop_();_nop_();E=0;delay1ms(1);/初始化/void Lcd_Int(void)Write_com(0x38);delay1ms(1);Write_com(0x38);delay1ms(1);Write_com(0x06);delay1ms(1);Write_com(0x0c);delay1ms(1);Write_com(0x01);void displaymainpart(void)/显示液晶主要部分(不变化部分)/Write_Address(0x01);delay1ms(1);Write_Date('D');Write_Date('A');Write_Date('T');Write_Date('E');Write_Date(':');delay1ms(1);Write_Address(0x0A);delay1ms(1);Write_Date('-');Write_Address(0x0D);Write_Date('-');Write_Address(0x42);Write_Date('T');Write_Date('I');Write_Date('M');Write_Date('E');Write_Date(':');Write_Address(0x4A);Write_Date('-');Write_Address(0x4D);Write_Date('-');Write_Address(0x06);Write_Date('2');Write_Date('0');void display_Second(uchar second)/在液晶上显示秒/uchar i,j;i=second/10;j=second%10;Write_Address(0x4E);Write_Date(digiti);Write_Date(digitj);delay1ms(1);void display_Minute(uchar minute)/在液晶上显示分/uchar i,j;i=minute/10;j=minute%10;Write_Address(0x4B);Write_Date(digiti);Write_Date(digitj);delay1ms(1);void display_Hour(uchar hour)/在液晶上显示时/uchar i,j;i=hour/10;j=hour%10;Write_Address(0x48);Write_Date(digiti);Write_Date(digitj);delay1ms(1);void display_Day(uchar day)/在液晶上显示日/uchar i,j;i=day/10;j=day%10;Write_Address(0x0E);Write_Date(digiti);Write_Date(digitj);delay1ms(1);void display_Month(uchar month)/在液晶上显示月/uchar i,j;i=month/10;j=month%10;Write_Address(0x0B);Write_Date(digiti);Write_Date(digitj);delay1ms(1);void display_Year(uchar year)/在液晶上显示年/uchar i,j;i=year/10;j=year%10;Write_Address(0x08);Write_Date(digiti);Write_Date(digitj);delay1ms(1);void display_houra(uchar x) /闹钟小时部分显示/ uchar i,j; i=x/10; j=x%10; Write_Address(0x44); Write_Date(digiti); Write_Date(digitj); void display_minutea(uchar x)/闹钟分钟部分显示/ uchar i,j; i=x/10; j=x%10; Write_Address(0x47); Write_Date(digiti); Write_Date(digitj); void display_Time(void)/显示实时时间/uchar value,day, month,year;Write_com(0x0c);value=ReadSet1302(0x81);seconds=(value&0x70)>>4)*10+(value&0x0f);display_Second(seconds);value=ReadSet1302(0x83);minutes=(value&0x70)>>4)*10+(value&0x0f);display_Minute(minutes);value=ReadSet1302(0x85);hours=(value&0x70)>>4)*10+(value&0x0f);display_Hour(hours);value=ReadSet1302(0x87);day=(value&0x70)>>4)*10+(value&0x0f);display_Day(day);value=ReadSet1302(0x89);month=(value&0x70)>>4)*10+(value&0x0f);display_Month(month);value=ReadSet1302(0x8D);year=(value&0xf0)>>4)*10+(value&0x0f);display_Year(year);void displaystar(void) /显示欢迎界面/ uchar i,j; Write_Address(0x0f); while(displaywelcomei!='0') Write_Date(displaywelcomei); i+; delay1ms(1); i=0; Write_Address(0x4f); while(displaywishi!='0') Write_Date(displaywishi); i+; delay1ms(1); j=40; while(j-) Write_com(0x18);/循环左移 / delay1ms(700); Write_com(0x01); delay1ms(10); void gbdisplay(uchar address)/时间调整时光标闪烁/Write_Address(address);delay1ms(5);Write_com(0x0f);delay1ms(5);void displaymaxt(uchar x) /显示最大温度/ uchar i,j,k; Write_com(0x0c); delay1ms(2); Write_Address(0x44); i=x/100; j=x/10; k=x%10; Write_Date(digiti); Write_Date(digitj); Write_Date(digitk); void hourset(void)/调时/uchar timevalue,hour;delay1ms(500);/防止多次触发/WriteSet1302(0x8e,0x00);/将写保护去掉,确保能正常将调整后的数值写入ds1303/timevalue=ReadSet1302(0x85);/读取此时的数值/hour=(timevalue&0x70)>>4)*10+(timevalue&0x0f);while(1) if(ADD=0) delay1ms(50); if(ADD=0) hour+; delay1ms(300); while(ADD=0); if(RED=0)delay1ms(50);if(RED=0) hour-; delay1ms(300); if(hour=0) hour=23; while(RED=0); timevalue=(hour/10)<<4|(hour%10);WriteSet1302(0x84,timevalue);delay1ms(1);display_Hour(hour);Write_Address(0x49);delay1ms(5);if(hour>=24) hour=0;delay1ms(5);if(CANL=0) mode=0; Write_com(0x0c); break; if(SET=0) break;WriteSet1302(0x8e,0x80);void minuteset(void)/调分/uchar timevalue,minute;delay1ms(500);/防止多次触发/WriteSet1302(0x8e,0x00);/将写保护去掉,确保能正常将调整后的数值写入ds1303/timevalue=ReadSet1302(0x83);/读取此时的数值/minute=(timevalue&0x70)>>4)*10+(timevalue&0x0f);while(1) if(ADD=0) delay1ms(10); if(ADD=0) minute+; if(minute>=60) timevalue=0; while(ADD=0); if(RED=0)delay1ms(10);if(RED=0) minute-; delay1ms(300); if(minute=0) minute=59; while(RED=0); timevalue=(minute/10)<<4|(minute%10);WriteSet1302(0x82,timevalue);delay1ms(1);display_Minute(minute);Write_Address(0x4c);delay1ms(5);if(minute>=60) minute=0;delay1ms(5);if(CANL=0)mode=0;Write_com(0x0c);break; if(SET=0) break;WriteSet1302(0x8e,0x80);void secondset(void)/秒归零/uchar second;delay1ms(500);WriteSet1302(0x8e,0x00);while(1) if(ADD=0) delay1ms(10); if(ADD=0) second=0; WriteSet1302(0x8e,0x00); while(ADD=0);