51基于单片机的电子万年历毕业设计[管理资料]
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
单片机课程设计报告
电子万年历设计
姓名:周义
学号:0520010058 专业班级:05电气(6)班指导老师:张宏伟
所在学院:万方科技学院
摘要
随着科技的快速发展,时间的流逝,至从观太阳、摆钟到现在电子钟,人类不断研究,不断创新纪录。
美国DALLAS公司推出的具有涓细电流充电能的低功耗实时时钟电路DS1302。
它可以对年、月、日、周日、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小。
对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、周日、时、分、秒和温度等信息,还具有时间校准等功能。
该电路采用AT89S52单片机作为核心,功耗小,能在3V的低压工作,电压可选用3~5V电压供电。
综上所述此万年历具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。
本设计是基于51系列的单片机进行的电子万年历设计,可以显示年月日时分秒及周信息,具有可调整日期和时间功能。
在设计的同时对单片机的理论基础和外围扩展知识进行了比较全面准备。
在硬件与软件设计时,没有良好的基础知识和实践经验会受到很大限制,每项功能实现时需要那种硬件,程序该如何编写,算法如何实现等,没有一定的基础就不可能很好的实现。
具体实现功能:
(1)显示年月日时分秒及星期信息
(2)具有可调整日期和时间功能
(3)与即时时间同步
目录
1方案论证 (3)
(3)
(3)
(4)
(4)
2系统的硬件设计与实现 (5)
(5)
(5)
(5)
(5)
(6)
(7)
(8)
3系统的软件设计 (9)
(9)
4测试与结果分析 (11)
(12)
(12)
(12)
测试结果分析 (12)
测试结论 (12)
5prodeus软件仿真........................................ ..........错误!未定义书签。
ISIS简介 (12)
(13)
(13)
6课程设计总结与体会.......................................... .....错误!未定义书签。
参考文献...........................................................错误!未定义书签。
附录一:系统电路图 (18)
附录二:系统程序...................................................错误!未定义书签。
1 方案论证
单片机芯片的选择方案和论证
方案一:
采用89C51芯片作为硬件核心,采用Flash ROM,内部具有4KB ROM 存储空间,能于3V的超低压工作,而且与MCS-51系列单片机完全兼容,但是运用于电路设计中时由于不具备ISP在线编程技术, 当在对电路进行调试时,由于程序的错误修改或对程序的新增功能需要烧入程序时,对芯片的多次拔插会对芯片造成一定的损坏。
方案二:
采用AT89S52,片内ROM全都采用Flash ROM;能以3V的超底压工作;同时也与MCS-51系列单片机完全该芯片内部存储器为8KB ROM 存储空间,同样具有89C51的功能,且具有在线编程可擦除技术,当在对电路进行调试时,由于程序的错误修改或对程序的新增功能需要烧入程序时,不需要对芯片多次拔插,所以不会对芯片造成损坏,所以选择采用AT89S52作为主控制系统。
显示模块选择方案和论证
方案一:
采用LED数码管动态扫描,LED数码管价格适中,对于显示数字合适,采用动态扫描法与单片机连接时,虽然占用的单片机口线少,但连线还需要花费一点时间,所以也不用此种作为显示。
方案二:
采用点阵式数码管显示,点阵式数码管是由八行八列的发光二极管组成,对于显示文字比较适合,如采用在显示数字显得太浪费,且价格也相对较高,所以也不用此种作为显示。
方案三:
采用LCD液晶显示屏,液晶显示屏的显示功能强大,可显示大量文字,图形,显示多样,清晰可见,并且我做的最小系统上带一个TS1620-1,和AT89S52已经接好,省了很多麻烦,所以在此设计中采用LCD液晶显示屏。
时钟芯片的选择方案和论证
方案一:
直接采用单片机定时计数器提供秒信号,使用程序实现年、月、日、星期、时、分、秒计数。
采用此种方案虽然减少芯片的使用,节约成本,但是,实现的
时间误差较大,所以不采用此方案。
方案二:
采用DS1302时钟芯片实现时钟,DS1302芯片是一种高性能的时钟芯片,可自动对秒、分、时、日、周、月、年进行计数,而且精度高,位的RAM做为数据暂存区,~,。
电路设计最终方案决定
综上各方案所述,对此次作品的方案选定: 采用AT89S52作为主控制系统; DS1302提供时钟;LCD液晶显示屏作为显示。
2 系统的硬件设计与实现
电路设计框图
图1系统原理图
系统硬件概述
本电路是由AT89S52单片机为控制核心,具有在线编程功能,低功耗,能在3V超低压工作;时钟电路由DS1302提供,它是一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,~。
采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。
DS1302内部有一个31*8的用于临时性存放数据的RAM寄存器。
可产生年、月、日、周日、时、分、秒,具有使用寿命长,精度高和低功耗等特点,同时具有掉电自动保存功能;温度的采集由DS18B20构成;显示部份由LCD液晶显示屏显示。
主要单元电路的设计
单片机主控制模块的设计
AT89S52单片机为40引脚双列直插芯片,有四个I/O口P0,P1,P2,P3, MCS-51单片机共有4个8位的I/O口(P0、P1、P2、P3),每一条I/O线都能独立地作输出或输入。
单片机的最小系统如下图所示,18引脚和19引脚接时钟电路,XTAL1接外部晶振和微调电容的一端,在片内它是振荡器倒相放大器的输入,XTAL2接外部晶振和微调电容的另一端,在片内它是振荡器倒相放大器的输出。
第9引脚为复位输入端,接上电容,电阻及开关后够上电复位电路,20引脚为接地端,40引脚为电源端。
如图2 所示:
图2 主控制系统
时钟电路模块的设计
,其中Vcc1为后备电源,Vcc2为主电源。
在主电源关闭的情况下,也能保持时钟的连续运行。
DS1302由Vcc1或Vcc2两者中的较大者供电。
当Vcc2大于Vcc1+,Vcc2给DS1302供电。
当Vcc2小于Vcc1时,DS1302由Vcc1供电。
X1和X2是振荡源,。
RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。
RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。
当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。
如果在传送过程中RSTS置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。
上电动行时,,RST必须保持低电平。
中有在SCLK 为低电平时,才能将RST置为高电平,I/O为串行数据输入端(双向)。
SCLK始终是输入端。
图3 DS1302的引脚图
电路原理及说明
(1) 时钟芯片DS1302的工作原理:
DS1302在每次进行读、写程序前都必须初始化,先把SCLK端置“0”,接着把RST端置“1”,最后才给予SCLK脉冲;读/写时序如下图4所示。
DS1302的控制字的位7必须置1,若为0则不能把对DS1302进行读写数据。
对于位6,若对程序进行读/写时RAM=1,对时间进行读/写时,CK=0,位1至位5指操作单元的地址。
位0是读/写操作位,进行读操作时,该位为1;该位为0则表示进行的是写操作。
控制字节总是从最低位开始输入/输出的。
、时间寄存器内容:“CH”是时钟暂停标志位,当该位为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位为0时,时钟开始运行。
“WP”是写保护位,在任何的对时钟和RAM的写操作之前,WP必须为0。
当“WP”为1时,写保护位防止对任一寄存器的写操作。
(2) DS1302的控制字节:
DS1302控制字节的高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果0,则表示存取日历时钟数据,为1表示存取RAM 数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出
(3) 数据输入输出(I/O)
在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。
同样,在紧跟8位的控制指令字后的下一个SCLK 脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
(4) DS1302的寄存器
DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式。
此外,DS1302 还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。
时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容。
DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操作,偶数为写操作;另一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。
显示模块的设计
,和最小系统上的连线一样,无需修改。
图4 LCD液晶显示屏显示模块
3 系统的软件设计程序流程框图
图5主程序流程图
图6 时间调整程序流程图
4 测试与结果分析
硬件测试
电子万年历的电路系统较大,对于焊接方面更是不可轻视,庞大的电路系统中只要出于一处的错误,则会对检测造成很大的不便,而且电路的交线较多,对于各种锋利的引脚要注意处理,否则会刺被带有包皮的导线,则会对电路造成短路现象。
在本成电子万年历的设计调试中遇到了很多的问题。
回想这些问题只要认真多思考都是可以避免的,以下为主要的问题:
(1)LCD液晶显示屏显示部分已经连在最小系统上,节省了不少时间和精力。
(2)对万年历修改时间或日期时,有时LCD液晶显示屏被屏蔽掉,造成不亮现象。
解决:根据仪器的测试,发现电路的驱动能力不足,最后在DS1302时钟芯片的/CS、SCLK、,电路的驱动能力才能满足,即可解决不亮现象。
软件测试
电子成年历是多功能的数字型,可以看当前日期,时间。
电子成年历功能很多,所以对于它的程序也较为复杂,所以在编写程序和调试时出现了相对较多的问题。
最后经过多次的模块子程序的修改,一步一步的完成,最终解决了软件。
在软件的调试过程中遇到的主要问题是:
烧入程序后,LCD液晶显示屏显示亮度不好。
解决:一遍旋转10K欧的滑动变阻器,一遍观看LCD显示屏,知道看到合适的亮度为止。
测试结果分析与结论
测试结果分析
,首先使用试测仪对电路进行测试,观察是否存在漏焊,虚焊,或者元件损坏,滑动变阻器器没有调好:查看烧写的程序是否正确无误,对程序进行认真修改。
测试结论
经过多次的反复测试与分析,可以对电路的原理及功能更加熟悉,同时提高了设计能力与及对电路的分析能力。
同时在软件的编程方面得到更到的提高,对编程能力得到加强,同时对所学的知识得到很大的提高与巩固。
5 Proteus软件仿真
ISIS简介
Proteus ISIS是英国Labcenter公司开发的电路分析与实物仿真软件。
它运行于Windows操作系统上,可以仿真、分析(SPICE)各种模拟器件和集成电路。
该软件的特点是:
(1)全部满足我们提出的单片机软件仿真系统的标准,并在同类产品中具有明显的优势。
(2)具有模拟电路仿真、数字电路仿真、单片机及其外围电路组成的系统的仿真、RS-232动态仿真、I2 C调试器、SPI调试器、键盘和LCD系统仿真的功能;有各种虚拟仪器,如示波器、逻辑分析仪、信号发生器等。
(3)目前支持的单片机类型有:ARM7系列、68000系列、8051系列、AVR 系列、PIC12系列、PIC16系列、PIC18系列、Z80系列、HC11系列以及各种外围芯片。
(4)支持大量的存储器和外围芯片。
总之,该软件是一款集单片机和SPICE分析于一身的仿真软件,功能极其强大,可仿真ARM、51、AVR、PIC。
Proteus启动画面:
图7Proteus启动画面
Proteus ISIS的工作界面是一种标准的Windows界面,如图所示。
包括:标题栏、主菜单、标准工具栏、绘图工具栏、状态栏、对象选择按钮、预览对象方位控制按钮、仿真进程控制按钮、预览窗口、对象选择器窗口、图形编辑窗口。
运行Proteus程序后,进入软件的主界面。
通过左侧工具栏中的P(从库中选择元件命令)命令,在Pick Devices 左侧窗口中选择所需元件的关键字,然后放置元件并调整方向和位置以及参数设置,最后进行连线。
Proteus ISIS的工作界面:
图8Proteus ISIS的工作界面
Proteus仿真效果
最后载入hex文件后可以进行模拟仿真,可以全速运行也可以单步调试运行。
图9运行按键
上电后后LED显示:
图10 调节秒钟:
图11 调节分钟:
图12 调节时钟:
图13 调节星期:
图14
调节日期:
图15
调节月份:
图16
调节年份:
图17
点击确定键显示:
图18
按键功能自上而下依次为:选择修改项键、增大键、减小键、确定键。
图19
6课程设计总结与体会
制作这次课程设计一切都是从零开始,从最简单的画流程图起步,这次课程设计可以圆满完成,跟单片机课程里面的课外作业是离不开的。
这次课程设计从开始到制作成功前后超过了一个星期。
当电子万年历可以成功实现时,那种激动和喜悦只有自己可以体会。
在整个设计过程中,充分发挥人的主观能动性,自主学习,学到了许多没学到的知识。
较好的完成了设计,达到了预期的目的,完了最初的设想。
对电路的设计、布局要先有一个好的构思,才显得电路板美观、大方。
程序编写中,由于思路不清晰,开始时遇到了很多的问题,经过静下心来思考,和同学讨论,理清了思路,反而得心应手。
在此次设计中,知道了做凡事要有一颗平常的心,不要想着走捷径,一步一脚印。
也练就了我的耐心,做什么事都在有耐心。
此次课程设计中学到了很多很多东西,这是最重要的。
总之,此次课设使我的能力得到了全方位的提高,使得我的操作能力和专业技能都有了很大的提高。
在做课程设计的日子里得到了张宏伟老师的悉心指导和同学的帮助,在此向他们致以诚挚的谢意。
感谢提供相关技术帮助的老师和同学,你们的支持和鼓励使我对这次的设计完成有了信心和动力,我在此深表谢意。
参考文献
[1]刘勇编数字电路电子工业出版社 2004
[2]陈正振编电子电路设计与制作广西交通职业技术学院信息工程系 2007
[3]杨子文编单片机原理及应用西安电子科技大学出版社 2006
[4]王法能编单片机原理及应用科学出版社 2004
附录一:系统电路图
附录二:系统程序
#include <>
#include <>
//#include ""
//#include ""
#define uint unsigned int
#define uchar unsigned char
sbit DS1302_CLK = P1^7; //实时时钟时钟线引脚
sbit DS1302_IO = P1^6; //实时时钟数据线引脚
sbit DS1302_RST = P1^5; //实时时钟复位线引脚
sbit ACC0 = ACC^0;
sbit ACC7 = ACC^7;
char hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year; //秒,分,时到日,月,年位闪的计数
sbit Set = P2^0; //模式切换键
sbit Up = P2^1; //加法按钮
sbit Down = P2^2; //减法按钮
sbit out = P2^3; //立刻跳出调整模式按钮
char done,count,temp,flag,up_flag,down_flag;
uchar TempBuffer[5],week_value[2];
void show_time(); //液晶显示程序
/***********1602液晶显示部分子程序****************/
//Port Definitions**********************************************************
sbit LcdRs = P2^5;
sbit LcdRw = P2^6;
sbit LcdEn = P2^7;
sfr DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=
//内部等待函数
************************************************************************** unsigned char LCD_Wait(void)
{LcdRs=0;LcdRw=1; _nop_();LcdEn=1; _nop_(); LcdEn=0;return DBPort; }
//向LCD写入命令或数据
************************************************************
#define LCD_COMMAND 0 // Command
#define LCD_DATA 1 // Data
#define LCD_CLEAR_SCREEN 0x01 // 清屏
#define LCD_HOMING 0x02 // 光标返回原点
void LCD_Write(bit style, unsigned char input)
{LcdEn=0;LcdRs=style;LcdRw=0; _nop_();DBPort=input; _nop_();//注意顺序LcdEn=1; _nop_();//注意顺序
LcdEn=0; _nop_();LCD_Wait();
}
//设置显示模式************************************************************ #define LCD_SHOW 0x04 //显示开
#define LCD_HIDE 0x00 //显示关
#define LCD_CURSOR 0x02 //显示光标
#define LCD_NO_CURSOR 0x00 //无光标
#define LCD_FLASH 0x01 //光标闪动
#define LCD_NO_FLASH 0x00 //光标不闪动
void LCD_SetDisplay(unsigned char DisplayMode)
{LCD_Write(LCD_COMMAND, 0x08|DisplayMode); }
//设置输入模式************************************************************ #define LCD_AC_UP 0x02
#define LCD_AC_DOWN 0x00 // default
#define LCD_MOVE 0x01 // 画面可平移
#define LCD_NO_MOVE 0x00 //default
void LCD_SetInput(unsigned char InputMode)
{LCD_Write(LCD_COMMAND, 0x04|InputMode);}
//初始化LCD************************************************************ void LCD_Initial()
{LcdEn=0;
LCD_Write(LCD_COMMAND,0x38); //8位数据端口,2行显示,5*7点阵LCD_Write(LCD_COMMAND,0x38);
LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR); //开启显示, 无光标
LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN); //清屏
LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE); //AC递增, 画面不动}
//液晶字符输入的位置************************
void GotoXY(unsigned char x, unsigned char y)
{if(y==0)LCD_Write(LCD_COMMAND,0x80|x);
if(y==1)LCD_Write(LCD_COMMAND,0x80|(x-0x40));}
//将字符输出到液晶显示
void Print(unsigned char *str)
{while(*str!='\0')
{LCD_Write(LCD_DATA,*str);str++;}}
/***********DS1302时钟部分子程序******************/
typedef struct __SYSTEMTIME__
{
unsigned char Second;
unsigned char Minute;
unsigned char Hour;
unsigned char Week;
unsigned char Day;
unsigned char Month;
unsigned char Year;
unsigned char DateString[11];
unsigned char TimeString[9];
}SYSTEMTIME; //定义的时间类型
SYSTEMTIME CurrentTime;
#define AM(X) X
#define PM(X) (X+12) // 转成24小时制
#define DS1302_SECOND 0x80 //时钟芯片的寄存器位置,存放时间
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_WEEK 0x8A
#define DS1302_DAY 0x86
#define DS1302_MONTH 0x88
#define DS1302_YEAR 0x8C
void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数)
{ unsigned char i;ACC = d; for(i=8; i>0; i--)
{DS1302_IO = ACC0; //相当于汇编中的RRC
DS1302_CLK = 1; DS1302_CLK = 0; ACC = ACC >> 1; } }
unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数)
{ unsigned char i; for(i=8; i>0; i--)
{ ACC = ACC >>1; //相当于汇编中的RRC
ACC7 = DS1302_IO; DS1302_CLK = 1;DS1302_CLK = 0; }
return(ACC); }
void Write1302(unsigned char ucAddr, unsigned char ucDa) //ucAddr: DS1302地址, ucData: 要写的数据
{ DS1302_RST = 0;
DS1302_CLK = 0;
DS1302_RST = 1;
DS1302InputByte(ucAddr); // 地址,命令
DS1302InputByte(ucDa); // 写1Byte数据
DS1302_CLK = 1;
DS1302_RST = 0;
} unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据
{ unsigned char ucData;
DS1302_RST = 0;
DS1302_CLK = 0;
DS1302_RST = 1;
DS1302InputByte(ucAddr|0x01); // 地址,命令
ucData = DS1302OutputByte(); // 读1Byte数据
DS1302_CLK = 1;
DS1302_RST = 0;
return(ucData);}
void DS1302_GetTime(SYSTEMTIME *Time) //获取时钟芯片的时钟数据到自定义的结构型数组
{unsigned char ReadV alue;
ReadValue = Read1302(DS1302_SECOND);
Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_MINUTE);
Time->Minute = ((ReadV alue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_HOUR);
Time->Hour = ((ReadV alue&0x70)>>4)*10 + (ReadV alue&0x0F);
ReadValue = Read1302(DS1302_DAY);
Time->Day = ((ReadV alue&0x70)>>4)*10 + (ReadV alue&0x0F);
ReadValue = Read1302(DS1302_WEEK);
Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadV alue&0x0F);
ReadValue = Read1302(DS1302_MONTH);
Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_YEAR);
Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadV alue&0x0F); }
void DateToStr(SYSTEMTIME *Time) //将时间年,月,日,星期数据转换成液晶显示字符串,放到数组里DateString[]
{ if(hide_year<2) //这里的if,else语句都是判断位闪烁,<2显示数据,>2就不显示,输出字符串为2007/07/22
{ Time->DateString[0] = '2';
Time->DateString[1] = '0';
Time->DateString[2] = Time->Year/10 + '0';
Time->DateString[3] = Time->Year%10 + '0';}
else{ Time->DateString[0] = ' '; Time->DateString[1] = ' ';
Time->DateString[2] = ' '; Time->DateString[3] = ' ';}
Time->DateString[4] = '/';
if(hide_month<2)
{Time->DateString[5] = Time->Month/10 + '0';
Time->DateString[6] = Time->Month%10 + '0';}
else{ Time->DateString[5] = ' '; Time->DateString[6] = ' ';}
Time->DateString[7] = '/';
if(hide_day<2)
{Time->DateString[8] = Time->Day/10 + '0';
Time->DateString[9] = Time->Day%10 + '0';}
else{Time->DateString[8] = ' ';Time->DateString[9] = ' '; }
if(hide_week<2){
week_value[0] = Time->Week%10 + '0'; //星期的数据另外放到week_value[]数组里,跟年,月,日的分开存放,因为等一下要在最后显示}
else { week_value[0] = ' ';}
week_value[1] = '\0';
Time->DateString[10] = '\0'; //字符串末尾加'\0' ,判断结束字符}
void TimeToStr(SYSTEMTIME *Time) //将时,分,秒数据转换成液晶显示字符放到数组TimeString[];
{ if(hide_hour<2)
{ Time->TimeString[0] = Time->Hour/10 + '0';
Time->TimeString[1] = Time->Hour%10 + '0';}
else{ Time->TimeString[0] = ' ';Time->TimeString[1] = ' ';}
Time->TimeString[2] = ':';
if(hide_min<2){ Time->TimeString[3] = Time->Minute/10 + '0';
Time->TimeString[4] = Time->Minute%10 + '0';}
else {Time->TimeString[3] = ' ';Time->TimeString[4] = ' '; }
Time->TimeString[5] = ':';
if(hide_sec<2)
{Time->TimeString[6] = Time->Second/10 + '0';
Time->TimeString[7] = Time->Second%10 + '0';}
else{Time->TimeString[6] = ' ';Time->TimeString[7] = ' '; }
Time->DateString[8] = '\0';}
void Initial_DS1302(void) //时钟芯片初始化
{ unsigned char Second=Read1302(DS1302_SECOND);
if(Second&0x80) //判断时钟芯片是否关闭
{Write1302(0x8e,0x00); //写入允许
Write1302(0x8c,0x07); //以下写入初始化时间日期:07/07/: 3. 时间: 23:59:55
Write1302(0x88,0x07);Write1302(0x86,0x25);
Write1302(0x8a,0x07);Write1302(0x84,0x23);Write1302(0x82,0x59);
Write1302(0x80,0x55);Write1302(0x8e,0x80); //禁止写入}}
void Delay1ms(unsigned int count)
{unsigned int i,j;for(i=0;i<count;i++)for(j=0;j<120;j++);}
/*延时子程序*/
void mdelay(uint delay)
{ uint i;for(;delay>0;delay--){for(i=0;i<62;i++) //1ms延时.
{;}}}
void outkey() //跳出调整模式,返回默认显示
{ uchar Second;if(out==0) { mdelay(8); count=0;
hide_sec=0,hide_min=0,hide_hour=0,hide_day=0,hide_week=0,hide_month=0,hide_year=0;
Second=Read1302(DS1302_SECOND);
Write1302(0x8e,0x00); //写入允许
Write1302(0x80,Second&0x7f);
Write1302(0x8E,0x80); //禁止写入
done=0; while(out==0); }}
///////////////////////////////////////////////////////////////////////////////
void Upkey()//升序按键
{ Up=1;if(Up==0)
{
mdelay(8);
switch(count)
{case 1:
temp=Read1302(DS1302_SECOND); //读取秒数
temp=temp+1; //秒数加1
up_flag=1; //数据调整后更新标志
if((temp&0x7f)>0x59) //超过59秒,清零
temp=0; break;
case 2:
temp=Read1302(DS1302_MINUTE); //读取分数
temp=temp+1; //分数加1
up_flag=1;
if(temp>0x59) //超过59分,清零
temp=0;break;
case 3:
temp=Read1302(DS1302_HOUR); //读取小时数
temp=temp+1; //小时数加1
up_flag=1;
if(temp>0x23) //超过23小时,清零
temp=0; break;
case 4:
temp=Read1302(DS1302_WEEK); //读取星期数
temp=temp+1; //星期数加1
up_flag=1;
if(temp>0x7)
temp=1;
break;
case 5:
temp=Read1302(DS1302_DAY); //读取日数
temp=temp+1; //日数加1
up_flag=1;
if(temp>0x31)
temp=1;
break;
case 6:
temp=Read1302(DS1302_MONTH); //读取月数
temp=temp+1; //月数加1
up_flag=1;
if(temp>0x12)
temp=1;
break;
case 7:
temp=Read1302(DS1302_YEAR); //读取年数
temp=temp+1; //年数加1
up_flag=1;
if(temp>0x85)
temp=0;
break;
default:break;}
while(Up==0); }}
///////////////////////////////////////////////////////////////////////////////
void Downkey()//降序按键
{ Down=1;if(Down==0)
{ mdelay(8);
switch(count)
{case 1:
temp=Read1302(DS1302_SECOND); //读取秒数
temp=temp-1; //秒数减1
down_flag=1; //数据调整后更新标志
if(temp==0x7f) //小于0秒,返回59秒
temp=0x59;
break;
case 2:
temp=Read1302(DS1302_MINUTE); //读取分数
temp=temp-1; //分数减1
down_flag=1;
if(temp==-1)
temp=0x59; //小于0秒,返回59秒
break;
case 3:
temp=Read1302(DS1302_HOUR); //读取小时数
temp=temp-1; //小时数减1
down_flag=1;
if(temp==-1)
temp=0x23;
break;
case 4:
temp=Read1302(DS1302_WEEK); //读取星期数
temp=temp-1; //星期数减1
down_flag=1;
if(temp==0)
temp=0x7;;
break;
case 5:
temp=Read1302(DS1302_DAY); //读取日数
temp=temp-1; //日数减1
down_flag=1;
if(temp==0)
temp=31;
break;
case 6:
temp=Read1302(DS1302_MONTH); //读取月数
temp=temp-1; //月数减1
down_flag=1;
if(temp==0)
temp=12;
break;
case 7:
temp=Read1302(DS1302_YEAR); //读取年数
temp=temp-1; //年数减1
down_flag=1;
if(temp==-1)
temp=0x85;
break;
default:break;}while(Down==0); }}
void Setkey()//模式选择按键
{Set=1;if(Set==0)
{ mdelay(8); count=count+1; //Setkey按一次,count就加1
done=1; //进入调整模式
while(Set==0); }}
void keydone()//按键功能执行
{ uchar Second;
if(flag==0) //关闭时钟,停止计时
{ Write1302(0x8e,0x00); //写入允许
temp=Read1302(0x80);
Write1302(0x80,temp|0x80);
Write1302(0x8e,0x80); //禁止写入
flag=1; }
Setkey(); //扫描模式切换按键
switch(count)
{case 1:do //count=1,调整秒
{ outkey(); //扫描跳出按钮
Upkey(); //扫描加按钮
Downkey(); //扫描减按钮
if(up_flag==1||down_flag==1) //数据更新,重新写入新的数据
{
Write1302(0x8e,0x00); //写入允许
Write1302(0x80,temp|0x80); //写入新的秒数
Write1302(0x8e,0x80); //禁止写入
up_flag=0;
down_flag=0; }
hide_sec++; //位闪计数
if(hide_sec>3)
hide_sec=0;
show_time(); //液晶显示数据
}while(count==2);break;
case 2:do //count=2,调整分{ hide_sec=0;
outkey();
Upkey();
Downkey();
if(temp>0x60)
temp=0;
if(up_flag==1||down_flag==1)
{ Write1302(0x8e,0x00); //写入允许
Write1302(0x82,temp); //写入新的分数
Write1302(0x8e,0x80); //禁止写入
up_flag=0;
down_flag=0; }
hide_min++;
if(hide_min>3)
hide_min=0;
show_time();
}while(count==3);break;
case 3:do //count=3,调整小时{ hide_min=0;
outkey();
Upkey();
Downkey();
if(up_flag==1||down_flag==1)
{ Write1302(0x8e,0x00); //写入允许
Write1302(0x84,temp); //写入新的小时数
Write1302(0x8e,0x80); //禁止写入
up_flag=0;
down_flag=0; }
hide_hour++;
if(hide_hour>3)
hide_hour=0;
show_time();
}while(count==4);break;
case 4:do //count=4,调整星期{ hide_hour=0;
outkey();
Upkey();
Downkey();
if(up_flag==1||down_flag==1)
{ Write1302(0x8e,0x00); //写入允许
Write1302(0x8a,temp); //写入新的星期数
Write1302(0x8e,0x80); //禁止写入
up_flag=0;
down_flag=0;}
hide_week++;
if(hide_week>3)
hide_week=0;
show_time();
}while(count==5);break;
case 5:do //count=5,调整日{hide_week=0;
outkey();
Upkey();
Downkey();
if(up_flag==1||down_flag==1)
{Write1302(0x8e,0x00); //写入允许
Write1302(0x86,temp); //写入新的日数
Write1302(0x8e,0x80); //禁止写入
up_flag=0;
down_flag=0; }
hide_day++;
if(hide_day>3)
hide_day=0;
show_time();
}while(count==6);break;
case 6:do //count=6,调整月{ hide_day=0;
outkey();
Upkey();
Downkey();
if(up_flag==1||down_flag==1)
{ Write1302(0x8e,0x00); //写入允许
Write1302(0x88,temp); //写入新的月数
Write1302(0x8e,0x80); //禁止写入
up_flag=0;
down_flag=0; }
hide_month++;
if(hide_month>3)
hide_month=0;
show_time();
}while(count==7);break;
case 7:do //count=7,调整年
{ hide_month=0;
outkey();
Upkey();
Downkey();
if(up_flag==1||down_flag==1)
{ Write1302(0x8e,0x00); //写入允许
Write1302(0x8c,temp); //写入新的年数
Write1302(0x8e,0x80); //禁止写入
up_flag=0;
down_flag=0; }
hide_year++;
if(hide_year>3)
hide_year=0;
show_time();
}while(count==8);break;
case 8: count=0;hide_year=0; //count8, 跳出调整模式,返回默认显示状态 Second=Read1302(DS1302_SECOND);
Write1302(0x8e,0x00); //写入允许
Write1302(0x80,Second&0x7f);
Write1302(0x8E,0x80); //禁止写入
done=0;
break; //count=7,开启中断,标志位置0并退出
default:break; }}
void show_time() //液晶显示程序
{ DS1302_GetTime(&CurrentTime); //获取时钟芯片的时间数据
TimeToStr(&CurrentTime); //时间数据转换液晶字符
DateToStr(&CurrentTime); //日期数据转换液晶字符
GotoXY(0,1);
Print(); //显示时间
GotoXY(0,0);
Print(); //显示日期
GotoXY(15,0);
Print(week_value); //显示星期
GotoXY(11,0);
Print("Week"); //在液晶上显示字母 week
Delay1ms(400); //扫描延时}
main()
{ flag=1; //时钟停止标志
LCD_Initial(); //液晶初始化
Initial_DS1302(); //时钟芯片初始化
up_flag=0;
down_flag=0;
done=0; //进入默认液晶显示
while(1)
{ while(done==1) keydone(); //进入调整模式
while(done==0)
{ show_time(); //液晶显示数据
flag=0; Setkey(); //扫描各功能键}
}
}。