基于51单片机的万年历设计
单片机经过几十年的发展,已经广泛应用于生活中的各个领域。单片机以其体积小、功能全、性价比高等诸多优点,在许多行业都得到了广泛应用。在工业控制、家用电器、通信设备、信息处理、尖端武器等各种测控领域的应用中独占鳌头,单片机开发技术已成为电子信息、电气、通信、自动化、机电一体化等专业技术人员必须掌握的技术。
基于单片机的万年历作为设计的课题,因为它有很好的开放性和可发挥性,对作者的要求比较高,不仅考察了对单片机的掌握能力而且强调了对单片机扩展的应用。另外液晶显示的万年历已经越来越流行,特别适合在家庭居室、办公室、大厅、会议室、车站和广场等地方使用,它具有显示清晰直观、走时准确、可以进行夜视等功能,并且还可以扩展出其它多种功能。所以,电子万年历作为设计课题很有价值。
现在对于电子万年历的设计大多运用51单片机。主要是因为51单片机种类齐全、结构体系完整、指令系统功能完善、性能优越、具有较高可靠性和高性价比等特点。
本篇论文主要介绍了运用单片机实现电子万年历的设计,万年历系统拟用STC89C51单片机控制,以DS1302时钟芯片计时、1602液晶屏显示。系统主要由单片机控制电路,显示电路以及校正电路三个模块组成。本文阐述了系统的硬件工作原理,所应用的各个接口模块的功能以及其工作过程,论证了设计
方案
气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载
理论的可行性。
目 录
第一章 绪论 ...................................... 1 1.1 本课题主要的研究工作 .................................. 1 第二章 系统的硬件设计与实现 ....................... 2 2.1 电路设计框图 .......................................... 2 2.2 系统硬件概述 .......................................... 2 2.3 主要单元电路的设计 .................................... 2
2.3.1 单片机主控制模块的设计 ............................ 2
2.3.2时钟电路模块的设计 ................................. 3
2.3.3独立式键盘设计 ..................................... 4
2.3.4显示模块的设计 ..................................... 4 第三章 系统的软件设计 ............................ 6 3.1程序流程图 ............................................ 6
3.1.1 系统总流程图 ...................................... 6
3.1.2 DS1302时钟程序流程图 .............................. 7
3.1.3 LCD显示程序流程图 ................................ 8 3.2 程序的设计 ............................................ 9
3.2.1 DS1302读写程序 .................................... 9
3.2.2 液晶显示程序 ..................................... 11 第四章 仿真与调试 ............................... 13 4.1 KEIL软件调试流程 ..................................... 13 4.2 PROTEUS软件运行流程 ................................... 13 4.3 万年历的功能仿真 ..................................... 13 致 谢 .......................................... 15 参考文献 ........................................ 16 附录:主程序 .................................... 17
第一章 绪论
1.1 本课题主要的研究工作
本项目是一种基于AT89C51片机的万年历设计,本方案以AT89C51片机作为主控核心,与时钟芯片DS1302、按键、LCD1602液晶显示器组成硬件系统。在硬件系统中设有独立按键和LCD1602显示器,能显示丰富的信息,根据使用者的需要可以随时对时间进行更改,读取方便、显示直观。
当程序执行后,LCD显示即时时间、年月日、星期。设置4个操作键:K1:选
:加键;K3:减键;K4:确定键。 择键;K2
1本设计的主要内容:
(1)熟悉万年历各模块的工作原理;
(2)选择适当的芯片和元器件,确定系统电路,绘制电路原理图,尤其
是各接口电路;
(3)熟悉单片机使用方法和C语言,编写出相应模块的应用程序;
(4)分别在各自的模块中调试出对应的功能,在Proteus软件上进行仿
真,并实现各种功能。
2设计目标:万年历实现以下三个功能:
(1)具有年、月、日、星期、时、分、秒等功能;
(2)具备年、月、日、星期、时、分、秒校准功能,具备自动调节闰年
的功能;
(3)具有与即时时间同步的功能。
1
第二章 系统的硬件设计与实现
2.1 电路设计框图
根据设计方案给出了系统总体的设计框图,如图1所示:
主LCD液晶模块
控
制键盘模块
模
DS1302模块 块
STC89C51
图1
为使时钟走时与
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
时间一致,校时电路是必不可少的,键盘模块用来校正液晶上显示的时间; STC89C51单片机通过输出各种电脉冲信号来驱动控制各部分正常工作;而系统的时间、等数据则最终通过液晶模块显示出来。 2.2 系统硬件概述
本电路是以STC89C51单片机为控制核心,该芯片具有在线编程功能,功耗低,能在3.3V的超低压下工作;
时钟芯片采用DS1302,它是一款高性能、低功耗、自带RAM的实时时钟芯片,具有掉电自动保存功能。可自动对秒、分、时、日、周、月、年进行计数,具有闰年补偿功能,而且精度高位的RAM做为数据暂存区,工作电压2.5V,5.5V范围内,2.5V时耗电小于300nA。
显示模块采用1602显示屏,它内置192种字符,可显示32个符号或数字,清晰可见,而且功率消耗小寿命长抗干扰能力强。其工作电压为5v。 2.3 主要单元电路的设计
2.3.1 单片机主控制模块的设计
一个典型的单片机最小系统一般由时钟电路、复位电路、电源指示灯和外部扩展接口等部分组成。
2
单片机的最小系统如下图所示,单片机的XTAL0和XTAL1引脚用于连接晶振电路。RESET为复位引脚,连接复位电路,用于初始化MCU。
2.3.2时钟电路模块的设计
DS1302内含有一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。图中VCC1为后备电源,VCC2为主电源。DS1302由VCC1或VCC2两者中的较大者供电。所以在主电源关闭的情况下,也能保持时钟的连续运行。X1和X2是振荡源,外接32.768KHz晶振用来为芯片提供计时脉冲。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。DS1302 的硬件接线图如图2所示:
C2
U1
P1.221RSTGND20
I/OX2Y1P1.1SLCKX1P1.0C1VCC1VCC2
32.768KHZ
DS1302VCCP1
201
2
Header 2
图2
时钟芯片DS1302的工作原理:
(1) DS1302的控制字节:DS1302控制字节的高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果为0,则
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出
(2) 数据输入输出(I/O):在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
(3) DS1302的寄存器:DS1302有12个寄存器,其中有7个寄存器与日历、
3
时钟相关,存放的数据位为BCD码形式。“CH”是时钟暂停标志位,当该位为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位为0时,时钟开始运行。“WP”是写保护位,在任何的对时钟和RAM的写操作之前,“WP”必须为0。当“WP”为1时,写保护位防止对任一寄存器的写操作。
2.3.3独立式键盘设计
独立式键盘每个键单独占用一根I/O口线。键盘的工作方式可分为编程控制方式和中断控制方式。CPU在一个工作周期内,利用完成其他任务的空余时间,调用键盘扫描子程序,经程序查询,若无键操作,则返回;若有键操作,则进而判断是哪个键,并执行相应的键处理程序。这种方式为编程扫描方式。由于单片机在正常应用过程中,可能会经常进行键操作,因而编程控制方式使CPU经常处于工作状态, 在进行本次设计中,只涉及到了选择、加、减、确定四个功能。因此采用独立式键盘。
2.3.4显示模块的设计
本设计中要对时间、温度进行显示,液晶显示屏1602模块作为输出。1602字符型LCD通常有14条引脚线或16条引脚线的LCD,多出来的2条线是背光电源线。它可以显示两行,每行16个字符,采用单+5V电源供电,外围电路配置简单。1602液晶模块内部的字符发生存储器已经存储了160个不同的点阵字符图形,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。LCD1602引脚功能:
1 VSS 一般接地
2 VDD 接电源(+5V)
3 V0 液晶显示器对比度调整端。
4 RS RS为寄存器选择。
5 R/W R/W为读写信号线。
6 E E(或EN)端为使能(enable)端,下降沿使能。
1602主要管脚介绍:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生鬼影使用时可以通过一个10K的电位器调整对比度。RS为寄存器选择端,高电平时选择数据寄存器,低电平时选择
4
指令寄存器。R,W为读写信号线端,高电平时进行读操作,低电平时进行写操作。当RS和R,W共同为低电平时可以写入指令或者显示地址;当RS为高电平R,W 为低电平时可以写入数据。E为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。将L1602的RS端和P2.0,R/W端和P2.1, E 端和P2.2相连,当RS=0时,对LCD1602写入指令;当RS=1时,对LCD1602写入数据。当R/W端接高电平时芯片处于读数据状态,反之处于写数据状态,E端为使能信号端。当R/W为高电平,E端也为高电平,RS为低电平时,液晶显示屏显示需要显示的示数。显示模块电路图如图3所示: VCC
J1
1VSSVCC2VDD10K3V0U14RS5RW1406P1.0(T2)VCCE2397P1.1(T2EX)P0.0DB03388P1.2P0.1DB14379P1.3P0.2DB253610P1.4P0.3DB363511P1.5P0.4DB473412P1.6P0.5DB583313P1.7P0.6DB693214RESETP0.7DB7103115(RXD)P3.0EA/VPPE1113016(TXD)P3.1ALE/PROGE21229P3.2(INT0)PSEN1328LCD1602P3.3(INT1)P2.71427P3.4(T0)P2.61526P3.5(T1)P2.51625P3.6(WR)P2.4 1724P3.7(RD)P2.31823图3 XTAL0P2.21922XTAL1P2.12021VCCVSSP2.0
ST8C9C52
5
第三章 系统的软件设计
3.1程序流程图
3.1.1 系统总流程图
系统总流程图分析:首先系统初始化,系统开始运行,当有设置键按下时进
开始入修改时间模式,无按键按下时读取时间、温度等数据送入液晶屏显示;在修改时间模式下设置时间完成后再送数据到液晶屏显示,如图4所示:
系统初始化
设置键N
有否按下,
Y
进入修改时间
读时间
模式
设置时间读温度N
设置完成,Y
图4
6 显示
3.1.2 DS1302时钟程序流程图
入液晶屏显示。这时若有设置键按下时,进行时间修改,完成后将数据送入
时钟芯片;若没有按键按下,则直接存入EPROM,送入液晶屏显示。时钟程序S1302
开始计时时,首先进行初始化,当有中断信号时,读取时钟芯片的数据,如图5
所示:
开始
初始化
开中断
读时钟芯片
送显示
设置键是N否有按下,
Y
送时钟芯是否修改Y当前时间,片
N
送EPROM
图5
7
3.1.3 LCD显示程序流程图
显示程序流程图分析:首先对1602显示屏进行初始化(初始化大约持续10ms
左右),然后检查忙信号,若BF=0,则获得显示RAM的地址,写入相应的数据显示;
若BF=1,则代表模块正在进行内部操作,不接受任何外部指令和数据,直到BF=0
为止,如图6所示:
开始
对1602初始化
写入显示设置命令
延时5msN
检查忙信号
NY
BF=0?
获得显示
RAM地址
延时5ms
写入相应
的数据
图6
Y数据显示
完毕,
8
结束
3.2 程序的设计
3.2.1 DS1302读写程序
DS1302是SPI总线驱动方式。它不仅要向寄存器写入控制字,还需要读取相应寄存器的数据。要想与DS1302通信,首先要先了解DS1302的控制字。DS1302
的控制字如图7:
图7
控制字总是从最低位开始输出。在控制字指令输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从最低位(0位)开始。同样,在紧跟8位的控制字指令后的下一个SCLK脉冲的下降沿,读出DS1302的数据,读出的 数据也是从最低位到最高位。依据读写时序如图8和图9:
图8 单字节读
图9 单字节写
在进行任何数据传输时,RST必须被置高电平,每个SCLK为上升沿时数据被输入,下降沿时数据被输出。先把RST置低,禁止数据传输,SCLK置低,清零时钟总线,RST再置高,允许数据传输。传送完成后,RST置低,禁止字节的传送。部分源程序如下:
void write_byte(uchar dat) //写一个字节
{
ACC=dat;
RST=1;
9
for(a=8;a>0;a--)
{
IO=ACC0;
SCLK=0;
SCLK=1;
ACC=ACC>>1;
}
}
uchar read_byte() //读一个字节 {
RST=1;
for(a=8;a>0;a--)
{
ACC7=IO;
SCLK=1;
SCLK=0;
ACC=ACC>>1;
}
return (ACC); }
void write_1302(uchar add,uchar dat) //向1302芯片写函数,指定写入地址,
数据
{
RST=0;
SCLK=0;
RST=1;
write_byte(add);
write_byte(dat);
SCLK=1;
RST=0;
10
}
uchar read_1302(uchar add) //从1302读数据函数,指定读取数据来源地址
{
uchar temp;
RST=0;
SCLK=0;
RST=1;
write_byte(add);
temp=read_byte();
SCLK=1;
RST=0;
return(temp);
}
3.2.2 液晶显示程序
1602通过D0~D7的8位数据端传输数据和指令,其模块内的控制器有11条控制指令。当液晶显示屏的接口电路与单片机系统I/O按照并行数据传输方式连接完成以后,即可以对STC89C51单片机进行编程。在液晶屏完成显示之前首先要对液晶进行初始化。部分源程序如下:
lcd_init() //***液晶初始化函数**** {write_1602com(0x38); //设置液晶工作模式, 16*2行显示,5*7点阵,8位数据
write_1602com(0x0c); //开显示不显示光标
write_1602com(0x06); //整屏不移动,光标自动右移
write_1602com(0x01); //清显示
write_1602com(yh+1); //日历显示固定符号从第一行第1个位置之后开始显示
for(a=0;a<14;a++)
{
write_1602dat(tab1[a]); //向液晶屏写日历显示的固定符号部分//delay(3);
}
11
write_1602com(er+2); //时间显示固定符号写入位置,从第2个位置后开始显示
for(a=0;a<8;a++)
{
write_1602dat(tab2[a]); //写显示时间固定符号,两个冒号 delay(3);
}
}
12
第四章 仿真与调试
4.1 Keil软件调试流程
建立新工程并保存,保存后会弹出一个设备选择对话框,选择MCU后点确定返回主界面。新建文件并保存,以.C为后缀。接着选择Source Group1,右击鼠标弹出快捷菜单,选择“Add File to Group ‘Source Group1’”,出现一个对话框,加入文件后点close返回主界面。再打开工程属性设置对话框,主要设置工作包括在Target选项卡中设置晶振频率、在Debug选项卡中设置生成hex文件等。然后编写程序并调试。在模拟调试程序后,还须通过编程器将.hex目标文件写入单片机中才能观察仿真的效果。
4.2 Proteus软件运行流程
运行Proteus程序后,进入软件的主界面,过左侧工具栏中的P(从库中选择
命令,在Pick Devices 左侧窗口中选择所需元件的关键字,然后放置元件命令)
元件并调整方向和位置以及参数设置,最后进行连线。按P在库中查找元器件,将所需要的元器件放置好后,绘制成原理图。
4.3 万年历的功能仿真
当电路搭建好之后,选中单片机STC89C51,左键点击STC89C51,在出现的对话框里点击Program File按钮,找到刚才Keil软件编译得到的HEX文件,载入然后点击“OK”按钮就可以模拟了。点击模拟调试按钮的运行按钮,进人调试状态。按相应的按键可调节分,时,星期,日期,月份,年份,并且可以自动闰年调整。按键功能自上而下依次为:选择键、加、减、确定。在Protues中仿真,如图10,图11,图12所示:
13
图10 电路图
图11 键盘调节
图12 调节键盘后
14
致 谢
至此,关于毕业设计的所有内容就介绍完了,系统的仿真电路和程序设计合理。
在整个设计过程中,充分发挥了人的主观能动性,自主学习,学到了许多没学到的知识。程序编写中,由于思路不清晰,开始时遇到了很多的问题,经过静下心来思考查资料,和同学讨论,理清了思路,完成对程序的编写。通过设计提高了对单片机的认识,进一步熟悉和掌握单片机的结构及工作原理。通过实际程序设计和调试,逐步掌握模块化程序设计方法和调试技术,提高软件设计、调试能力;通过这次设计熟悉以单片机核心的应用系统开发的全过程,掌握硬件电路设计的基本方法和技术,掌握相关电路参数的计算方法。最终较好的完成了设计,达到了预期的目的,完了最初的设想。但是由于时间和个人暂时的能力的原因,整个系统看起来还是显得非常的简单,只实现了一些最基本的功能,还有许多不足和可以扩展的地方。
毕业设计的完成,为我的大学学习生活画下了最后一笔。应该感谢的人很多,首先感谢学院给我们提供了一个展现自己的舞台,在长江职业技术学院这个良好的学习环境中,我们得到了很多锻炼的机会,使得我们的动手能力和专业技能都有了很大的提高。其次要感谢教单片机的**老师的悉心教导和全程负责毕业
报告
软件系统测试报告下载sgs报告如何下载关于路面塌陷情况报告535n,sgs报告怎么下载竣工报告下载
完成的**老师,还有帮助我完成的学校电子竞赛组的同学。是你们为我创造了良好的学习和完成毕业设计的环境,你们的支持和鼓励使我对这次的设计完成有了信心和动力,使我能够顺利地完成课题的设计工作,在此向这些帮助我的人致以诚挚的谢意。最后,还要特别感谢我的父母,养育之恩,无以回报,你们永远健康快乐是我最大的心愿。正是在爸妈身体和精神的养育下才让我得以全身心投入学习和研究中,顺利完成学业。
即将踏上社会了,在人生的又一个十字路口上,我将带着你们的期望和关怀,在将来的工作岗位上勤奋实干,积极进取,用优异的成绩来回报大家对我的关心和帮助~
15
参考文献
1彭伟 单片机C语言程序设计实例100例 电子工业出版社 2009年5月; 2王文杰 许文斌 主编 单片机应用技术 冶金工业出版社 2008年6月; 3张福祥 C语言程序设计 辽宁大学出版社 2007年2月; 4郭天祥 十天学会单片机 哈尔滨工程大学出版社 2007年6月;
16
附录:主程序
// 名称:用DS1302与1602LCD设计的可调式电子日历与时钟
// 说明:本例会自动调节合法日期时间,对于星期的调节会在
// 调整年月日时自动完成,闰年问题也会自动判断。
//-------------------------------------------------------
#include
#include
#define uchar unsigned char
#define uint unsigned int
sbit SDA=P1^0; //DS1302数据线
sbit CLK=P1^1; //DSB1302时钟线
sbit RST=P1^2; //DS1302复位线
sbit RS=P2^0; //LCD寄存器选择
sbit RW=P2^1; //LCD读/写控制
sbit EN=P2^2; //LCD启用
sbit K1=P3^4; //选择
sbit K2=P3^5; //加
sbit K3=P3^6; //减
sbit K4=P3^7; //确定
uchar tCount=0;
//一年中每个月的天数,二月的天数由年份决定
uchar MonthsDays[]={0,31,0,31,30,31,30,31,31,30,31,30,31};
//周日,周一到周六{0,1-6} [读取DS1302时分别是1-7]
uchar *WEEK[]={"SUN","MON","TUS","WEN","THU","FRI","SAT"};
//LCD显示缓冲
uchar LCD_DSY_BUFFER1[]={"DATE 00-00-00 "}; uchar LCD_DSY_BUFFER2[]={"TIME 00:00:00 "}; uchar DateTime[7]; //所读取的日期时间
char Adjust_Index=-1; //当前调节的时间对象:秒,分,时,日,月,年
17
(0,1,2,3,4,6)
uchar Change_Flag[]="-MHDM-Y"; //(分,时,日,月,年)(不调节秒周) //延时,向DS1302写、读一字节以及从DS1302指定位置度、写数据的程序 //延时----------------------------------------------------
void DelayMS(uint x)
{
uchar i;
while(x--) for(i=0;i<120;i++); }
//向DS1302写入一个字节
void Write_A_Byte_TO_DS1302(uchar x) {
uchar i;
for(i=0;i<8;i++)
{
SDA=x&1; CLK=1; CLK=0; x>>=1;
}
}
//从DA1302读取一字节----------------------------------------
uchar Get_A_Byte_FROM_DS1302() {
uchar i,b,t;
for(i=0;i<8;i++)
{
b>>=1;t=SDA;b|=t<<7;CLK=1;CLK=0;
}
//BCD码转换
return b/16*10+b%16;
}
//从DS1302指定的位置读数据---------------------------------------
18
uchar Read_Data(uchar addr)
{
uchar dat;
RST=0;CLK=0;RST=1;
Write_A_Byte_TO_DS1302(addr);
dat=Get_A_Byte_FROM_DS1302();
CLK=1;RST=0;
return dat;
}
//向DS1302某地址写入数据--------------------------------------------
void Write_DS1302(uchar addr,uchar dat) {
CLK=0;RST=1;
Write_A_Byte_TO_DS1302(addr);
Write_A_Byte_TO_DS1302(dat);
CLK=0;RST=0;
}
//--------------------------------------------
//------设置时间--------------------------------------
void SET_DS1302()
{
uchar i;
Write_DS1302(0x8E,0x00);//写控制字,取消写保护
for(i=1;i<7;i++) //分时日月年依次写入
{
//分的起始地址10000010(0x82),后面续依次是时,日,月,周,年,写入地址每次递增2
Write_DS1302(0x80+ 2*i,(DateTime[i]/10<<4)|(DateTime[i]%10));
}
Write_DS1302(0x8E,0x80);//加保护
19
}
//----------------------------------------------------
//读取当前日期时间
//----------------------------------------------------
void GetTime()
{
uchar i;
for(i=0;i<7;i++)
{
DateTime[i]=Read_Data(0x81+2*i);
}
}
//----------------------------------------------------
//1602LCD的若干显示控制代码。
//uchar Read_LCD_State() 读LCD状态
uchar Read_LCD_State()
{
uchar state;
RS=0;RW=1;EN=1;DelayMS(1);state=P0;EN=0;DelayMS(1);
return state;
}
//void LCD_Busy_Wait() 忙等待
void LCD_Busy_Wait()
{
while((Read_LCD_State()&0x80)==0x80);
DelayMS(5);
}
//void Write_LCD_Data(uchar dat) 向LCD写数据
void Write_LCD_Data(uchar dat) {
20
LCD_Busy_Wait();
RS=1;RW=0;EN=0;P0=dat;EN=1;DelayMS(1);EN=0;
}
//void Write_LCD_Command(uchar cmd) 写LCD指令
void Write_LCD_Command(uchar cmd) {
LCD_Busy_Wait();
RS=0;RW=0;EN=0;P0=cmd;EN=1;DelayMS(1);EN=0;
}
//void Init_LCD() LCD初始化
void Init_LCD()
{
Write_LCD_Command(0x38);DelayMS(1);
Write_LCD_Command(0x01);DelayMS(1);
Write_LCD_Command(0x06);DelayMS(1);
Write_LCD_Command(0x0c);DelayMS(1); }
//void Set_LCD_POS(uchar P) 设置液晶显示位置
void Set_LCD_POS(uchar p)
{
Write_LCD_Command(p|0x80);
}
//-------------------------------------------------------
//在LCD上显示字符串
void Display_LCD_String(uchar p,uchar *s) {
uchar i;
Set_LCD_POS(p);
for(i=0;i<16;i++)
{
21
Write_LCD_Data(s[i]); DelayMS(1);
}
}
//------------------------------------------------------------
//日期与时间值转换为数字字符
void Format_DateTime(uchar d,uchar *a) {
a[0]=d/10+'0';a[1]=d%10+'0';
}
//-----------------------------------------------------------
//判断是否为闰年
uchar isLeapYear(uint y)
{
return(y%4==0&&y%100!=0)||(y%400==0); }
//------------------------------------------------------------
//求自2000.1.1开始的任何一天是星期几
//函数没有通过,求出总天数后再求星期几,
//因为求总天数可能会超越uint的范围。
void RefreshWeekDay()
{
uint i,d,w=5;//已知1999.12.31是周五
for(i=2000;i<2000+DateTime[6];i++) {
d=isLeapYear(i)?366:365;
w=(w+d)%7;
}
d=0;
for(i=1;i0) DateTime[6]--;
//获取2月天数
MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
//如果年份变化后当前月份的天数大于上限则设为上限
if(DateTime[3]>MonthsDays[DateTime[4]])
DateTime[3]=MonthsDays[DateTime[4]];
RefreshWeekDay();//刷新星期
break;
case 4://月01-12
if(x==1&&DateTime[4]<12) DateTime[4]++;
if(x==-1&&DateTime[4]>1) DateTime[4]--;
//获取2月天数
MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
//如果月份变化后当前月份的天数大于上限则设为上限
if(DateTime[3]>MonthsDays[DateTime[4]])
DateTime[3]=MonthsDays[DateTime[4]];
RefreshWeekDay();//刷新星期
break;
case 3://日00-28/29/30/31;调节之前首先根据年份得出该年中2月的
23
天数
MonthsDays[2]=isLeapYear(2000+DateTime[6])?29:28;
//根据当前月份决定调节日期的上限
if(x==1&&DateTime[3]0) DateTime[3]--;
RefreshWeekDay();//刷新星期
break;
case 2://时
if(x==1&&DateTime[2]<23) DateTime[2]++;
if(x==-1&&DateTime[2]>0) DateTime[2]--;
break;
case 1://分
if(x==1&&DateTime[1]<59) DateTime[1]++;
if(x==-1&&DateTime[1]>0) DateTime[1]--;
break;
}
}
//----------------------------------------------------------
//定时器0每秒刷新LCD显示
void T0_INT() interrupt 1
{
TH0=-50000/256;
TL0=-50000%256;
if(++tCount!=2) return;
tCount=0;
//按指定的格式生成待显示的日期时间串
Format_DateTime(DateTime[6],LCD_DSY_BUFFER1+5);
Format_DateTime(DateTime[4],LCD_DSY_BUFFER1+8);
Format_DateTime(DateTime[3],LCD_DSY_BUFFER1+11);
24
//星期
strcpy(LCD_DSY_BUFFER1+13,WEEK[DateTime[5]-1]);
//时分秒
Format_DateTime(DateTime[2],LCD_DSY_BUFFER2+5);
Format_DateTime(DateTime[1],LCD_DSY_BUFFER2+8);
Format_DateTime(DateTime[0],LCD_DSY_BUFFER2+11);
//显示年月日,星期,时分秒
Display_LCD_String(0x00,LCD_DSY_BUFFER1);
Display_LCD_String(0x40,LCD_DSY_BUFFER2); }
//---键盘中断(INT0)------------------------------------------ void EX_INT0() interrupt 0
{
if(K1==0) //选择调整对象(Y M D H M)
{
while(K1==0);
if(Adjust_Index==-1||Adjust_Index==1) Adjust_Index=7;
Adjust_Index--;
if(Adjust_Index==5) Adjust_Index=4;//跳过对星期的调节
LCD_DSY_BUFFER2[13]='[';
LCD_DSY_BUFFER2[14]=Change_Flag[Adjust_Index];
LCD_DSY_BUFFER2[15]=']';
}
else if(K2==0)//加
{
while(K2==0); DateTime_Adjust(1);
}
else if(K3==0)//减
{
while(K3==0); DateTime_Adjust(-1);
25
}
else if(K4==0)//确定
{
while(K4==0);
SET_DS1302(); //将调整后的时间写入1302
LCD_DSY_BUFFER2[13]=' ';
LCD_DSY_BUFFER2[14]=' ';
LCD_DSY_BUFFER2[15]=' ';
Adjust_Index=-1;//操作索引重设为-1,时间继续正常显示
}
}
//----------------------------------------------------------------
//主程序
void main()
{
Init_LCD(); //液晶初始化
IE=0x83; // 允许INT0,T0中断
IP=0x01;
IT0=0x01;
TMOD=0x01;
TH0=-50000/256;
TL0=-50000%256;
TR0=1;
while(1)
{ //如果为执行调整操作则正常读取当前时间
if(Adjust_Index==-1) GetTime(); }
}
26