资源描述
课 程 设 计 说 明 书
题 目: 基于单片机的多功能电子钟
课 程: 单片机原理及应用B课程设计
山东建筑大学信息与电气工程学院课程设计说明书
目 录
摘 要 II
1 设计目的 1
2 设计要求 2
3 设计内容 3
3.1电子时钟的工作原理 3
3.2 系统硬件电路设计及元件 4
3.2.1 AT89C51芯片 4
3.2.2 DS1302芯片 8
3.2.3 LCD1602液晶显示 12
3.3系统软件电路设计 15
3.3.1 系统流程图及源代码设计 15
总结与致谢 18
参考文献 19
附录一 20
摘 要
单片机, 是集 CPU ,RAM ,ROM , 定时器,计数器和多种接口于一体的微控制器。自20 世纪 70 年代问世以来,以其极高的性能价格比,受到人们的重视和关注。它体积小、重量轻、抗干扰能力强、环境要求不高、价格低廉、可靠性高、灵活性好、开发较为容易,广泛应用于智能生产和工业自动化上。
本系统为基于DS1302的多功能电子钟 ,以AT89C51单片机 作为主控芯, 采用实时时钟芯片DS1302,使用1602液晶作为显示输出。该系统走时精确,具有闹钟设置,时间模式切换,秒表以及可同时显示时间、日期等多种功能。本文将详细介绍AT89C51单片机和DS1302 时钟芯片的基本原理,从软件和硬件电路的实现两大方面进行分析。
关键词:AT89C51;单片机;液晶屏;时钟芯片;蜂鸣器
34
1 设计目的
基于51单片机设计一个能够支持年、月、日、星期、时、分、秒的电子时钟。并且支持闹钟功能,及整点报时功能。在设计过程中,学习利用DXP软件进行硬件电路设计或者利用仿真软件进行仿真,进行单片机的软件编程。学习芯片的选择及方案选择,熟悉单片机的I/O口原理;掌握LCD显示的原理;掌握定时计数器的使用;要求设计出程序流程图和程序。
2 设计要求
1.设计能支持年、月、日、星期、时、分、秒的时钟,时钟有时间调整功能及闹钟功能;
2.时钟具有装卸电池时掉电保护功能,保护时间大于5分钟;
3.时钟功耗小于0.5MA/5V。
4.能够提供生日提醒指示;能够每天提供3个时间点的闹钟报时功能;
5.非易失定时闹铃
6.整点报时
3 设计内容
3.1电子时钟的工作原理
本次设计时钟电路,使用了AT89C51单片机芯片控制电路,单片机控制电路简单且省去了很多复杂的线路,使得电路简明易懂,用C语言程序来控制整个时钟的显示,使得编程变得更容易,这样通过五个模块:芯片、显示屏、看门狗、电源、时钟即可满足设计要求。
此设计原理图框图如图3.1所示,此电路包括以下五个部分:单片机、时钟电路、看门狗、液晶屏、电源模块、时钟振荡电路。
图3.1 主电路设计框图
本设计采用C语言程序设计,使单片机控制液晶显示年、月、日、时、分、秒,当秒计数满60时就向分进位,分计数器计满60后向时计数器进位,小时计数器按“23翻0”规律计数。时、分、秒的计数结果经过数据处理可直接送显示器显示。当计时发生误差的时候可以用校时电路进行校正。设计采用的是时、分、秒显示,单片机对数据进行处理同时在LCD上显示。
3.2 系统硬件电路设计及元件
3.2.1 AT89C51芯片
AT89C51 是美国ATMEL 公司生产的8 位Flash ROM 单片机。其最突出的优点是片内ROM 为Flash ROM,可擦写1000 次以上,应用并不复杂的通用ROM 写入器就能方便的擦写,读取也很方便,价格低廉,具有片程序ROM 二级保密系统。因此可灵活应用于各种控制领域。
AT89C51 包含以下一些功能部件:
(1)一个8 位CPU ;
(2)一个片内振荡器和时钟电路;
(3)4KB Flash ROM ;
(4)128B 内RAM;
(5)可寻址64KB 的外ROM 和外RAM 控制电路;
(6)两个16 位定时/计数器;
(7)21 个特殊功能寄存器;
(8)4 个8 位并行I/O 口;
(9)一个可编程全双工串行口;
(10)5 个中断源,可设置成2 个优先级。
AT89C51 单片机一般采用双列直插DIP 封装,共40 个引脚,图3.2 为其引脚排列图。40 个引脚大致可分为4 类:电源、时钟、控制各I/O 引脚。
图3.2 AT89C51引脚图
(1)控制线
控制线共有4 根,其中3 根是复用线。所谓复用线是指具有两种功能,正常使用时是一种功能,在某种条件下是另一种功能。
1、ALE/ PROG ——地址锁存允许/片内EPROM 编程脉冲。
(1)ALE 功能:用来锁存P0 口送出的低八位地址。
AT89C51 在并行扩展外存储器时,P0 口用于分时传送低8 位地址和数据信号,且均为二进制数。当ALE 信号有效时,P0 口传送的是低8 位地址信号;ALE 信号无效时,P0 口传送的是低8 位地址信号。在ALE 信号的下降沿,锁定P0 口传送的内容,即低8 位地址信号。
需要指出的是,当CPU 不执行访问外RAM 指令,ALE 以时钟振荡频率1/6 的固定频率输出,因此ALE 信号也可作为外部芯片CLK 时钟或其他需要。但是,当CPU 执行MOVX 指令时,ALE 将跳过一个ALE 脉冲。
(2) PROG 功能:片内EPROM 的芯片,在EPROM 编程期间,此引脚输入编程脉冲。
2、 PSEN ——外ROM 读选通信号。
89C51 读外ROM 时,每个机器周期内PSEN 两次有效输出。PSEN 可作为外ROM芯片输出允许OE 的选通信号。在读内ROM 或读外RAM 时, PSEN 无效。
3、RST/VPD——复位/备用电源。
(1)正常工作时,RST 端为复位信号输入端,只要在该引脚上连续保持两个机器周期以上高电平,AT89C51 芯片即实现复位操作,复位后一切从头开始,CPU 从0000H 开始执行指令。
(2)VPD 功能:在VCC 掉电情况下,该引脚可接上备用电源,由VPD 向片内RAM供电,以保持片内RAM 中的数据不丢失。
4、EA /VPP——内外ROM 选择/片内EPROM 编程电源。
(1) EA 功能:正常工作时, EA 为内外ROM 选择端。AT89C51 单片机ROM 寻址范围为64KB,其中4KB 在片内,60KB 在片外。当EA 保持高电平时,先访问内ROM,但当PC 值超过4KB 时,将自动转向执行外ROM 中的程序。当EA 保持低电平时,则只访问外ROM,不管芯片内有否内ROM。
(2)VPP 功能:片内有EPROM 的芯片,在EPROM 编程期间,此引脚用于施加编程电源。
(2) I/O引脚
AT89C51 有P0、P1、P2、P3 4 个8 位并行I/O 端口,共32 个引脚。
P0 口是一组8 位漏级开路型双向I/O 口,也即地址/数据总线复用口。作为输出口用时,每位能以吸收电流的方式驱动8 个TTL 逻辑门电路,对端口写1 时,又可作高阻抗输入端用。在访问外部程序和数据存储器时,它是分时多路转换的地址(低8 位)/数据总线,在访问期间激活了内部的上拉电阻。在Flash 编程时,P0 端口接收指令字节;而在验证程序时,则输出指令字节。验证时,要求外接上拉电阻。
P1口是带内部上拉电阻的双向I/O口,向P1口写入1时P1口被内部上拉为高电平,可用作输入口。当作为输入脚时被外部信号拉低的P1口会因为内部上拉而输出一个电流。Flash编程和程序校验期间,P1接收低8位地址。
P2口是带内部上拉电阻的双向I/O口,向P2口写入1时P2口被内部上拉为高电平可用作输入口,当作为输入脚时被外部拉低的P2口会因为内部上拉而输出电流。在访问外部程序存储器或16位地址的外部数据存储器(例如执行MOVX @DPTR指令)时,P2口送出高8位地址数据,当使用8位寻址方式(MOVX@RI)访问外部数据存储器时,P2口发送P2特殊功能寄存器的内容,在整个访问期间不改变。Flash编程和程序校验时,P2也接收高位地址和一些控制信号。
P3口是带内部上拉电阻的双向I/O口,向P3口写入1时P3口被内部上拉为高电平可用作输入口,当作为输入脚时被外部拉低的P3口会因为内部上拉而输出电流。P3口除了作为一般的I/O口线外,更重要的是它的第二功能,如表3-1所示:
表3-1 P3端口的特殊功能
端口引脚
兼 用 功 能
P3.0——RXD
串行输入口
P3.1——TXD
串行输出口
P3.2——/INT0
外部中断0
P3.3——/INT1
外部中断1
P3.4——T0
定时器0的外部输入
P3.5——T1
定时器1的外部输入
P3.6——/WR
外部数据存储器写选通
P3.7——/RD
外部数据存储器读选通
3.2.2 DS1302芯片
DS1302是美国DALLAS公司推出的一种高性能、低功耗的实时时钟日历芯片,附加31字节静态RAM,采用SPI三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。实时时钟可提供秒、分、时、日、星期、月和年,一个月小于31天时可以自动调整,且具有闰年补偿功能。工作电压宽达2.5~5.5V。采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后备电源进行涓细电流充电的能力。有主电源和备份电源双引脚,而且备份电源可由大容量电容(>1F)来替代。需要强调的是,DS1302需要使用32.768KHz的晶振。
DS1302 包含以下一些功能部件:
(1)实时时钟,可对秒、分、时、日、周、月以及带闰年补偿的年进行计数;
(2)用于高速数据暂存的31×8位RAM;
(3)最少引脚的串行I/O;
(4)2.5~5.5V 电压工作范围;
(5)2.5V时耗电小于300nA;
(6)用于时钟或RAM数据读/写的单字节或多字节(脉冲方式)数据传送方式;
(7)简单的3线接口;
(8)可选的慢速充电(至VCC1)的能力。
(1) DS1302数据操作原理
DS1302在任何数据传送时必须先初始化,把RST脚置为高电平,然后把8位地址和命令字装入移位寄存器,数据在SCLK的上升沿被输入。无论是读周期还是写周期,开始8位指定40个寄存器中哪个被访问到。在开始8个时钟周期,把命令字节装入移位寄存器之后,另外的时钟周期在读操作时输出数据,在写操作时写入数据。时钟脉冲的个数在单字节方式下为8加8,在多字节方式下为8加字节数,最大可达248字节数。DS1302的内部结构图如图3.3所示。
图3.3 DS1302内部结构图
(2) DS1302 的引脚图及功能
DS1302 的引脚图如图3.4所示。
图3.4 DS1302引脚图
DS1302 各引脚功能:
Vcc1:主电源;
Vcc2:备份电源。当Vcc2>Vcc1+0.2V时,由Vcc2向DS1302供电,当Vcc2< Vcc1时,由Vcc1向DS1302供电。
SCLK:串行时钟,输入,控制数据的输入与输出;
I/O:三线接口时的双向数据线;
CE:输入信号,在读、写数据期间,必须为高。该引脚有两个功能:第一,CE开始控制字访问移位寄存器的控制逻辑;其次CE提供结束单字节或多字节数据传输的方法。引脚功能如表3-2所示:
表3-2 DS1302引脚功能说明
引脚号
名称
功能
1
VCC1
备份电源输入
2
X1
32.768KHz晶振输入
3
X2
32.768KHz晶振输出
4
GND
地
5
RST
控制移位寄存器/复位
6
I/O
数据输入/输出
7
SCLK
串行时钟
8
VCC2
主电源输入
(3) DS1302的控制字
DS1302的控制字如表3-3所示:
表3-3控制字(即地址及命令字节)
7
6
5
4
3
2
1
0
1
RAM
A4
A3
A2
A1
A0
RD
控制字的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入到DS1302中。
位6:如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;
位5至位1(A4~A0):指示操作单元的地址;
位0(最低有效位):如为0,表示要进行写操作,为1表示进行读操作。
控制字总是从最低位开始输出。在控制字指令输入后的下一个 SCLK时钟的上升沿时,数据被写入 DS1302,数据输入从最低位(0位)开始。同样,在紧跟8位的控制字指令后的下一个 SCLK脉冲的下降沿,读出 DS1302的数据,读出的数据也是从最低位到最高位。
(4) DS1302时序
CE 输入驱动高启动所有的数据传输。CE 输入有两个功能。首先,CE打开控制逻辑,允许访问的移位寄存器的地址/命令序列。其次,CE 提供了一个终止单字节或多字节数据传输方法。
一个时钟周期是由一个下降沿之后的上升沿序列。对于数据传输而言,数据必须在有效的时钟的上升沿输入,在时钟的下降沿输出。如果CE 为低,所有的I/O 引脚变为高阻抗状态,数据传输终止。
对于数据输入:开始的8个SCLK 周期,输入写命令字节,数据字节在后8个SCLK 周期的上升沿输入。数据输入位0 开始。
对于数据输出:开始的8个SCLK 周期,输入一个读命令字节,数据字节在后8个SCLK 周期的下降沿输出。注意,第一个数据字节的第一个下降沿发生后,命令字的最后一位被写入,命令字节的最后一位被写入。当CE 仍为高时。如果还有额外的SCLK 周期,DS1302 将重新发送数据字节,这使DS1302 具有连续突发读取的能力。DS1302数据读写时序如图3.5所示:
图3.5 数据读写时序
3.2.3 LCD1602液晶显示
LCD1602是一种用5x7点阵图形来显示字符的液晶显示器模块,它显示的容量为2行16个字共32个字符。它由若干个5×7或者5×11点阵字符位组成,每个点阵字符位都可以显示一个字符。模块采用数字式接口,能够方便地与单片机等控制类芯片进行通信。由于其具有体积小、重量轻、显示质量高、功耗低等诸多优点而被广泛用于智能化仪器仪表的显示器件。
(1)LCD1602引脚及功能
LCD1602的引脚如图3.6所示:
①VSS:电源地。
②VDD:接电源正极。
③VL:液晶显示偏压信号。
④RS:数据/命令选择端(H/L)。
⑤:读/写选择端(H/L)。
⑥E:使能端。当E端由高电平跳变成低电平时,液晶模块执行命令。
⑦D1~D8:8位双向数据线。
⑧BLA:背光源正极;
⑨BLK:背光源负极。
图3.6 LCD1602引脚图
LCD1602引脚功能表如下表3-4所示:
表3-4 LCD1602引脚功能表
编号
符号
引脚说明
编号
符号
引脚说明
1
VSS
电源地
9
D2
数据
2
VDD
电源正极
10
D3
数据
3
VL
液晶显示偏压
11
D4
数据
4
RS
数据/命令选择
12
D5
数据
5
R/W
读/写选择
13
D6
数据
6
E
使能信号
14
D7
数据
7
D0
数据
15
BLA
背光源正极
8
D1
数据
16
BLK
背光源负极
液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志位是不是为低电平,是低电平则表示不忙,否则此指令失效。要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符。表3-5为 LCD1602的内部显示地址。
表3-5 LCD1602的内部显示地址
1
2
3
·····
14
15
16
第一行
80H
81H
82H
·····
8DH
8EH
8FH
第二行
0C0H
0C1H
0C2H
·····
0CDH
0CEH
0CFH
3.3系统软件电路设计
3.3.1 系统流程图及源代码设计
(1) 主程序流程图设计
3.7 主程序流程图
(2)子程序流程图设计
1、 DS1302 定时器中断子程序流程图
图3.8 DS1302中断流程图
程序主要实现对DS1302写保护、充电,对年、月、日、时、分、秒等寄存器的读写操作。在读写操作子程序中都执行了关中断指令,因为在串行通信时对时序要求比较高,而且在此是用I/O口软件模拟串行时钟脉冲,所以在通信过程中最好保证传输的连续性,不要允许中断,如图3.8所示。
2、 LCD1602 流程图
程序主要实现对信息的显示,包括年、月、日、时、分、秒等信息,在这里要注意它的读写控制指令。其流程图如图3.9所示。
图3.9 LCD1602初始化流程图
总结与致谢
通过这次对《基于单片机的电子时钟设计》的设计与制作过程,加强了我们动手、思考和解决问题的能力。
在设计过程中我们利用Altium软件进行原理图及PCB图的绘制,最后再利仿真软件进行检查,看程序是否能显示其功能。在设计过程中让我进一步深刻的学习各种软件的利用以及各种软件给我们带来的便利。
通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重重,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固。
这次课程设计终于顺利完成了,在设计中遇到了很多专业知识问题,最后在老师的辛勤指导下,终于游逆而解。同时,在老师的身上我们学也到很多实用的知识,在次我们表示感谢!同时,对给过我帮助的所有同学和各位指导老师再次表示忠心的感谢!
参考文献
[1] 马潮. AVR单片机嵌入式系统原理与应用实践 [M].北京: 北京航空航天大学出版社,2007.
[2] 杨欣,王玉凤,刘湘黔. 51单片机应用从零开始 [M].北京: 清华大学出版社,2008.
[3] 陈在平. 现场总线及工业控制网络技术[M].北京: 电子工业出版社,2008.
[4] 沈建华,杨艳琴. MSP430系列16位超低功耗单片机原理与实践[M]. 北北京航空航天大学出版社,2008.
[5] 张成伟. 基于嵌入式Linix的GSM/GPRS无线通信模块的设计与实现[D]. 华中科技大学
[6] 赵亮GPRS无线网络在远程数据采集中的应用计算机工程与设计.2005. 26(9).
[7] 马忠梅.单片机的C语言应用程序设计[M].北京:北京航空航天大学出版社,2003.
[8] 胡汉才.单片机原理及其接口技术[M].北京:清华大学出版社,2004.
[9] 雷霖. 现场总线及工业控制网络技术[M].北京: 电子工业出版社,2004.
[10] 许洪华. 现场总线与工业以太网技术[M]. 北京: 电子工业出版社,2007.
附录一
#include <reg52.h>
#include<INTRINS.H>
/************************************************************/
#define uchar unsigned char
#define uint unsigned int
#define TIME (0X10000-50000)
#define FLAG 0XEF //闹钟标志
/************************************************************/
//引脚连接图
sbit CLK=P1^2;
sbit DAT=P1^3;
sbit RST=P1^4;
sbit RS=P1^5;
sbit RW=P1^6;
sbit E=P1^7;
sbit P33=P3^3;
sbit KEY1 = P2^0;
sbit KEY2 = P2^1;
sbit KEY3 = P2^2;
sbit KEY4 = P2^3;
/************************************************************/
//全局变量及常量定义
uchar i=20,j;
uchar DataBuf[16] = {};//日期
uchar TimeBuf[16] = {};//时间
uchar alarm[2],time[3];
uchar code Day[]={31,28,31,30,31,30,31,31,30,31,30,31};//12个月的最大日期(非闰年)
//闰年月星期表
const unsigned char WeekTab[] = {
(3 << 5) + 31,///1月
(6 << 5) + 29,///2月
(0 << 5) + 31,///3月
(3 << 5) + 30,///4月
(5 << 5) + 31,//5月
(1 << 5) + 30,//6月
(3 << 5) + 31,//7月
(6 << 5) + 31,//8月
(1 << 5) + 30,//9月
(4 << 5) + 31,//10月
(0 << 5) + 30,//11月
(2 << 5) + 31 //12月
};
//音律表
uint code table1[]={64260,64400,64524,64580,64684,64777,
64820,64898,64968,65030,65058,65110,65157,65178,65217};
//发声部分的延时时间
uchar code table2[]={0x82,1,0x81,0xf4,0xd4,0xb4,0xa4,
0x94,0xe2,1,0xe1,0xd4,0xb4,0xc4,0xb4,4,0};
//闹钟中用的全局变量
uchar th1,tl1;
/************************************************************/
//延时1ms函数
delay1ms(uchar time)
{
uchar i,j;
for(i=0;i<time;i++)
{
for(j=0;j<250;j++);
}
}
/************************************************************/
//LCD控制函数
Enable()
{
RS=0;
RW=0;
E=0;
delay1ms(3);
E=1;
}
/************************************************************/
//LCD1602写入字符函数
LCD1602_WriteSChr(uchar i)
{
P0=i;
RS=1;
RW=0;
E=0;
delay1ms(2);
E=1;
}
/************************************************************/
//LCD1602写入字符串函数
//入口函数
//uchar data *address : 写入数据首地址
//ucharm:写入字节数
LCD1602_WriteStr(uchar *address,uchar m)
{
uchar i,j;
for(i=0;i<m;i++,address++)
{
j=*address;
LCD1602_WriteSChr(j);
}
}
/************************************************************/
//LCD显示
void LCDShow(void)
{
P0=0XC; //显示器开、光标关
Enable();
//P0=0x80; //写入显示起始地址
//Enable();
//LCD1602_WriteStr(DataBuf,16); //写入日期显示缓存
P0=0xc1; //写入显示起始地址
Enable();
LCD1602_WriteStr(TimeBuf,16); //写入时间显示缓存
}
/************************************************************/
//DS1302写入子程序
void DS1302_Write(uchar temp)
{
uchar i;
CLK=0; //将DS1320时钟脉冲拉低
_nop_();//延时一指令周期
RST=1; //RST置高电平
_nop_();//延时一指令周期
for(i=0;i<8;i++) //循环8次
{
DAT=temp&0x01; //向DS1302写入一字节数据
_nop_(); //延时一指令周期
CLK=1; //拉高时钟脉冲
temp>>=1; //右移一位
CLK=0; //拉低时钟脉冲
}
}
/************************************************************/
//DS1302读取子程序
uchar DS1302_Read()
{
uchar i,j=0;
for(i=0;i<8;i++)//循环8次
{
j>>=1; //右移一位
_nop_(); //延时一指令周期
CLK=0; //拉低时钟脉冲
_nop_(); //延时一指令周期
if(DAT) //判断接收该位数据是否为1
j|=0x80;//该位置1
_nop_(); //延时一指令周期
CLK=1; //拉高时钟脉冲
}
return(j); //返回数值
}
/************************************************************/
//部分显示数据初始化
TimeStart()
{
TimeBuf[0]=TimeBuf[8]=TimeBuf[9]=TimeBuf[10]=0x20; //不显示字符
TimeBuf[2]=TimeBuf[5]=':'; //时间分隔显示
DS1302_Write(0xc1);
alarm[0]=DS1302_Read();
RST=0;
DS1302_Write(0xc3);
alarm[1]=DS1302_Read();
RST=0;
DS1302_Write(0xc5);
DataBuf[0]=DS1302_Read();
RST=0;
}
/************************************************************/
//读取时间
ReadTime()
{
uchar i,m,n;
for(m=0,i=0,n=0x85;i<7;i+=3,n-=2,m++) //连续读取时,分,秒
{
DS1302_Write(n); //写入读取寄存器指令
time[m]=DS1302_Read(); //读取数据
RST=0; //将RST电平拉低,停止传输
TimeBuf[i]=time[m]/16+0x30; //将两位数据的十位转为字符格式
TimeBuf[i+1]=time[m]%16+0x30;//将两位数据的个位转为字符格式
}
}
/************************************************************/
//功能选择超时定时器
time0() interrupt 1 using 1
{
i--;
if(i==0)
{
if(j!=0)
j--;
i=20;
}
TH0=TIME/256,TL0=TIME%256;
}
/************************************************************/
//产生闹铃音调
intime1() interrupt 3
{
TH1=th1,TL1=tl1;
P32=!P32;
}
/************************************************************/
//闹钟显示
void AlarmShow(void)
{
uchar i,j,a,b,n;
ET1=1;
for(j=0;j<6;j++)
{
i=0;
while(1)
{
a=table2[i];
if(a==0)
break;
b=a&0xf;
a>>=4;
if(a==0)
{
TR1=0;
goto D1;
}
a=((--a)<<1)/2;
TH1=th1=table1[a]/256,TL1=tl1=table1[a]%256;
TR1=1;
D1: do
{
b--;
for(n=0;n<3;n++)
{
ReadTime();
LCDShow();
P2=0xff;
if(KEY4 == 1)
{
delay1ms(100);
if(KEY4 == 1)
{
TR1=0;
ET1=0;
P32 = 1;
return;
}
}
}
}while(b!=0);
i++;
}
TR1=0;
}
ET1=0;
}
/************************************************************/
//设置日期、时间
void SetTime(void)
{
uchar i=0xc2,year,month,day,n;
TimeBuf[6]=TimeBuf[7]=0x30;
DataBuf[14]=DataBuf[15]=0x20;
LCDShow();
while(1)
{
P0=0xe; //显示器开、光标开
Enable();
P0=i; //定光标
Enable();
P2=0xff;
if((KEY1 == 1)||(KEY2 == 1)||(KEY3 == 1)||(KEY4 == 1))
{
delay1ms(100); //延时0.1s去抖动
if((KEY1 == 1)||(KEY2 == 1)||(KEY3 == 1)||(KEY4 == 1))
{
j=7;
if(KEY1 == 1)
{
i+=3; //更改设置项目
if(i==0x8e)
i=0xc2;
else if(i>0xc5)
i=0xc2;
}
else if(KEY2 == 1)
{
year=(DataBuf[4]&0xf)*10+(DataBuf[5]&0xf); //将字符格式的年份转换为数值格式
month=(DataBuf[7]&0xf)*10+(DataBuf[8]&0xf); //将字符格式的月份转换为数值格式
day=(DataBuf[10]&0xf)*10+(DataBuf[11]&0xf); //将字符格式的日数转换为数值格式
if(i==0x85) //设置年份
{
year++;
if(year>99)
year=0;
if((year%4)!=0)
if(month==2&&day==29)
day=28;
}
else if(i==0x88) //设置月份
{
month++;
if(month>12)
month=1;
if(day>Day[month-1])
{
day=Day[month-1];
if(month==2&&(year%4)==0) //计算是否闰年
day=29;
}
}
else if(i==0x8b) //设置日期
{
day++;
if(day>Day[month-1])
{
if(month==2&&(year%4)==0) //计算是否闰年
{
if(day>29)
day=1;
}
if(month!=2)
day=1;
}
}
else if(i==0xc2) //设置小时
{
n=(TimeBuf[0]&0xf)*10+(TimeBuf[1]&0xf);
n++;
if(n>23)
n=0;
TimeBuf[0]=n/10+0x30;
TimeBuf[1]=n%10+0x30;
}
else //设置分钟
{
n=(TimeBuf[3]&0xf)*10+(TimeBuf[4]&0xf);
n++;
if(n>59)
n=0;
TimeBuf[3]=n/10+0x30;
TimeBuf[4]=n%10+0x30;
}
DataBuf[4
展开阅读全文