最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

数字钟课程设计论文

来源:动视网 责编:小OO 时间:2025-09-24 16:45:36
文档

数字钟课程设计论文

《基于单片机的数字时钟》设计任务书一、设计题目基于单片机的数字时钟二、设计任务以51单片机、实时时钟芯片DS1307(或DS1302)和液晶显示LCD1602为主体,设计一款简易数字时钟,主要功能如下:基本功能1.能够准确显示时间(显示格式为时时:分分:秒秒,12//24小时制切换);2.可进行时间调整;3.具有闹钟时间设置、闹钟开/关。扩展功能1.可设置多个闹钟。2.其它功能(如音乐闹钟、温度显示灯)。三、设计要求电路设计1.画出电路框图;2.画出整机电路工作原理图。软件设计1.根据功能和电
推荐度:
导读《基于单片机的数字时钟》设计任务书一、设计题目基于单片机的数字时钟二、设计任务以51单片机、实时时钟芯片DS1307(或DS1302)和液晶显示LCD1602为主体,设计一款简易数字时钟,主要功能如下:基本功能1.能够准确显示时间(显示格式为时时:分分:秒秒,12//24小时制切换);2.可进行时间调整;3.具有闹钟时间设置、闹钟开/关。扩展功能1.可设置多个闹钟。2.其它功能(如音乐闹钟、温度显示灯)。三、设计要求电路设计1.画出电路框图;2.画出整机电路工作原理图。软件设计1.根据功能和电
《基于单片机的数字时钟》设计任务书

一、设计题目

基于单片机的数字时钟

二、设计任务

以51单片机、实时时钟芯片DS1307(或DS1302)和液晶显示LCD1602为主体,设计一款简易数字时钟,主要功能如下:

基本功能1. 能够准确显示时间(显示格式为时时:分分:秒秒,12//24小时制切换);

2. 可进行时间调整;

3. 具有闹钟时间设置、闹钟开/关。

扩展功能1. 可设置多个闹钟。

2. 其它功能(如音乐闹钟、温度显示灯)。

三、设计要求

电路设计1. 画出电路框图;

2. 画出整机电路工作原理图。

软件设计1. 根据功能和电路编写程序流程图;

2. 编写配套的子程序和主程序,实现任务要求的功能。

软硬件调试1. 调试和测试项目完整,有记录、有统计和分析结果;

2. 能根据结果提出改进措施。

                                                                                                                                                                               

附件3  软件设计变……………………………………………………………………………………24

附件4  程序模块及参数…………………………………………………………………………....25

绪论

本次课程设计的目的是为我们更好的掌握单片机的应用知识,有助于提高我们的职业素质,同时加深对知识的了解,本次设计也是为我们的毕业设计打基础,加强知识的综合应用。

本设计以ATC51单片机为核心,以实时时钟芯片DS1307和液晶显示LCD1602为主体设计了一款简易数字时钟。该时钟系统主要由单片机最小系统、时钟模块、闹钟模块、液晶显示模块、键盘控制模块组成。系统具有简单清晰的操作界面,能够准确显示时间(显示格式为时时:分分:秒秒,24小时制),可随时进行时间调整,具有闹钟时间设置、闹钟开/关。设计以硬件软件化为指导思想,充分发挥单片机功能,大部分功能通过软件编程来实现,电路简单明了,系统稳定性高。同时,该时钟系统还具有功耗小、成本低的特点,具有很强的实用性。由于系统所用元器件较少,单片机所被占用的I/O口不多,因此系统具有一定的可扩展性。软件程序均采用C语言编写,便于移植与升级。报告详细介绍了整个系统的硬件组成结构、工作原理和系统的软件程序设计。

    通过一个阶段的对知识点的了解更加明确,同时也了解了做项目不是件容易的事,需要多方面的知识,还需要大量的查阅资料,再把自己的所学知识综合应用,这样才有可能做出项目来。

第1章 设计方案

1.1 方案论证与比较

方案一:基于FPGA的系统总体设计方法。为了实现:(1)显示年、月、日、时、分、秒、星期,并且可以进行调整时间;(2)可以设定闹钟和整点报时的功能,数字时钟在总体上主要分为三大部分:输入人机界面部分、FPGA核心功能部分和输出界面部分,其系统设计框图如图1-1所示。

图1-1 基于FPGA的系统设计方框图

方案二:采用单片机技术来实现数字钟的功能。系统以ATC51单片机为核心控制器件,它除了具备微机CPU的数值计算功能外,还具有灵活强大的控制功能,以便实时检测系统的输入量、控制系统的输出量,实现自动控制。与传统机械表相比,它具有走时精确,显示直观等特点。它的计时周期为24小时,显满刻度为“23时59分59秒”,另外具有校时功能,断电后有记忆功能,恢复供电时可实现计时同步等特点。外围主要有串行通信器件实时时钟芯片DS1307等,使得系统线路简单可靠性高。系统结构框图如图1-2所示。

图1-2 基于单片机实现的数字时钟系统结构框图

单片机最小系统:其作用是和外围的时钟芯片通信,并控制数据传输的过程,采集时间信息并予以处理。

键盘模块:键盘模块可以设置时间信息,通过单片机写入时钟芯片,以更新时间;也可以设置闹钟,由单片机存入其内部RAM中。

时钟模块:此模块由专用的实时时钟芯片构成,由它提供实时的日历时钟信息。

液晶显示模块:单片机读取时钟芯片DS1307中的信息,通过液晶显示器实时显示。采用LCD作为显示器,具有界面友好、功耗低的优点。

闹钟模块:单片机主控模块读取日历芯片中的时间信息,与所设置的闹钟时间相比较,若相同时,闹钟模块工作闹钟模块。

电源模块:用220V市电经整流、滤波、稳压后,输出稳定的+5V的直直流电为其供电。

1.2 方案确定

综合考虑以上两种方案的优缺点以及题目的基本要求和发挥要求,在本设计中,我采了第二种方案,即采用单片机来实现数字时钟的功能。

第2章 硬件设计

2.1 单片机最小系统

单片机最小系统以ATC51单片机为核心,由单片机、时钟电路、复位电路等组成如图2-1所示。主要负责各个模块的初始化工作;读取并处理时间;处理按键响应;控制液晶实时显示等。

图2-1 单片机最小系统

主控制器ATC51单片机与MCS51系列单片机产品兼容,内部自带有4KB的Flash存储器及256KB RAM单元,不需另外扩展EEPROM及静态RAM,可以在线下载程序,易于日后的升级。

图中,P2.0、P2.1、P2.2及P1口为单片机与液晶显示器连接的控制和通信的数据端口;P2.6和P2.7为单片机与时钟芯片DS1307通信的端口;P2.3为闹钟的控制端口;P0.0、P0.1、P0.2、P0.3为按键模块的接口。

时钟电路是由XTAL1和XTAL2之间跨接的晶体振荡器和微调电容构成。时钟电路中晶体振荡器的频率高则系统的时钟频率就高,所以该系统采用12M晶振;

复位电路有两种形式:手动按键复位和上电复位,在本系统中采用的是手动按键复位。如图2-1所示,R1、R2、C3和SW组成系统手动按键复位电路。

2.2 时钟模块

系统采用DS1307时钟芯片。DS1307是美国DALLAS公司推出的一种高性能、低功耗的时钟芯片,它是一款I2C总线接口的时钟芯片,采用两线与CPU进行通信,片内含有8个特殊功能寄存器和56bit的SRAM。

DS1307的主要技术指标:具有秒、分、时、日、星期、月、年的计数功能;12小时制和24小时制两种计数模式;可自动调整每月的天数,具有闰年自动修正、掉电保护和上电复位功能。

2.2.1 DS1307的引脚功能

DS1307采用8引脚双列直插DIP封装形式,引脚分布如图2-2所示。相应功能如下:

Vcc:主电源;

Vbat:备用电源。当Vbat>Vcc+0.2V时,由Vcc向DS1307供电,当VbatGND:逻辑地;

SCL:I2C总线时钟线;

SDA:I2C总线数据线;

SQW/OUT:方波/输出驱动器。

图2-2 DS1307引脚分布图

2.2.2 DS1307的内部结构

1.内部结构

DS1307芯片由晶体振荡器、电源控制器、I2C总线控制、分频处理、逻辑控制、RAM存储、多路选择器、时钟/日历寄存器、缓冲器组成,内部结构如图2-3所示。

1Hz

图2-3 DS1307的内部结构

2寄存器

DS1307有关日历、时间的寄存器共有12个,其中有7个寄存器(读时81H~8DH,写时80H~8CH),存放的数据格式为BCD码形式,如表2-1所示。

表2-1 DS1307的日历、时间寄存器

地址D7D6D5D4D3D2D1D0功能取值范围
81HCH十位个位00-59
83H0十位个位00-59
85H012十位十位个位AM:1-12

PM:00-23

24
8B00000星期星期01-07
87H00十位个位01-31
H000十位个位01-12
8DH十位Year00-99
8FHOUT00SOWE00RS1RS0控制字-
91HRAM

56×8

00H-FFH
(1)小时寄存器(85H、84H)的位7用于定义DS1307是运行于12小时模式还是24小时模式。当为高电平时,选择12小时模式。在12小时模式时,位D5是,当为1时,表示PM。在24小时模式时,位5是第二个10小时位。

(2)秒寄存器(81H、80H)的位7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1307处于低功耗状态;当该位置为0时,时钟开始运行。

(3)控制寄存器(8FH、8EH)的位7是写保护位(WP),其它7位均置为0。在任何的对时钟和RAM的写操作之前,WP位必须为0。当WP位为1时,写保护位防止对任一寄存器的写操作。

2.2.3 DS1307的读写操作

DS1307是基于I2C总线接口的时钟芯片,软件上与I2C总线完全兼容。

1.I2C总线的驱动

I2C总线在传送数据时,必须确认传送数据的开始和结束。而且每传送一个字节,要发送一个应答位(0);在一个周期发送结束后,要发送一个应答位(1)。具体如图2-4所示,三种信号的格式如下:

(1)启动信号:当时钟总线SCL为高电平时,数据线SDA由高电平跳变为低电平定义为“启动”信号。

(2)停止信号:当时钟总线SCL为高电平时,数据线SDA由低电平跳变为高电平定义为“结束”信号。

(3)应答位:当主器件发送完一字节的数据后,后面必须跟一个应答位(ACK)。在时钟高电平期间,如果数据线SDA为低电平代表一个字节的传送结束,并准备下一个要传送的字节;在时钟高电平期间,如果数据线SDA为低电平代表一个传送周期结束,准备下一个传送周期。

图2-4 I2C总线的数据传送格式

2.DS1307的写控制

图2-5是DS1307的写控制格式,首先发送启动信号,然后发送的第一个字节是用来控制芯片的地址以及读写控制位(D0:0–写),之后是应答位,然后发送其它字节数据,在最后发送一个结束标志的应答位,紧跟着是停止信号。

S11010000AXXXX

XXXX

AXXXX

XXXX

AXXXX

XXXX

AXXXX

XXXX

AP
图2-5 CPU写数据模式

3.DS1307的读控制

图2-6是DS1307的读控制格式,首先发送启动信号,然后发送的第一个字节是用来控制芯片的地址以及读写控制为(D0:1–读),之后是应答位,然后发送其它字节数据,在最后发送一个结束标志的应答位,紧跟着是停止信号。

S11010001AXXXX

XXXX

AXXXX

XXXX

AXXXX

XXXX

AXXXX

XXXX

AP
图2-6 CPU读数据模式

2.2.4 DS1307硬件电路设计

DS1307采用与CPU进行通信,电路连接简单。DS1307的内部振荡电路结构如图2-7所示,在芯片内部连接有两个电容,目的是为了使晶振起振,所以在电路设计

中就不需要另外再加电容了。

图2-7 DS1307的内部振荡电路

时钟模块电路如图2-8所示,其中晶振采用的是32.768kHz,经内部电路分频后可获得一个标准的秒脉冲信号;电阻R3、R4是I2C总线的上拉电阻。

图2-8 DS1307的电路连接

2.3 闹铃模块

系统采用蜂鸣器作为闹铃输出,电路连接如图2-9所示。电路中采用PNP管Q1来控制蜂鸣器的开关,由图可以看出:当P2.3引脚为高电平时,PNP管截止,蜂鸣器不工作;当P2.3引脚为低电平时,PNP管导通,蜂鸣器工作。其中R9为限流电阻。

图2-9 闹铃电路

2.4 键盘模块

键盘模块设置了四个按键:KEY1、KEY2、KEY3、KEY4。其中KEY1为模式切换键,KEY2为设定值上升键,用KEY3为设定值减小键,KEY4是返回键。

电路连接如图2-10所示。4个上拉电阻可以保证在没有按键输入时,进入单片机四个I/O口的按键状态均为高电平,防止干扰产生;当有按键按下时,相应的端口线状态转为低电平。

图2-10 键盘电路

2.5.1 LCD1602的引脚功能

LCD1602模块的引脚如图2-11所示。

图2-11 LCD1602模块

其引脚功能如下:

RS:数据和指令选择控制端,RS=0命令状态;RS=1数据;

R/W:读写控制线,R/W=0写操作;R/W=1读操作;

A:背光控制正电源;

K:背光控制地;

E:数据读写操作控制位,E线向LCD模块发送一个脉冲,LCD模块与单片机间将进行一次数据交换;

DB0~DB7:数据线,可用8位连接,也可只用高4位连接,节约单片机资源;

VDD:电源端;

VEE:亮度控制端(1-5V);

VSS:接地端。

2.5.2 LCD1602的显示操作

1.四种基本操作

LCD有四种基本操作,具体如表2-2所示。

表2-2 LCD与单片机之间有四种基本操作

RSR/W操作
00写命令操作(初始化,光标定位等)

01读状态操作(读忙标志位)

10写数据操作(要显示内容)

11读数据操作(可以把显示存储区中的数据反读出来)

(1)读状态操作

执行读状态字操作,如表2-2所示须满足RS=0、R/W=1。根据管脚功能,当为有效电平时,状态命令字可从LCD模块传输到数据总线。同时可以保持一段时间,从而实现读状态字的功能。如结束

图2-12所示为读入状态字流程图。

图2-12 读入状态字流程图

(2)写命令操作

由表2-2可知当RS=0,R/W=0时,才可以通过单片机或用户指令把数据即命令,写到LCD模块,此时就对LCD进行调制。可采用查询方式:先读入状态字,再判断忙标志,最后写命令字。

1)命令字

表2-3所示为命令字,其主要介绍了指令名称、控制信号及控制代码。其指令名称是指要实现的功能,控制代号是采用的十六进制的数值表示的。

a.清零操作是指输入某命令字后即能将整个屏幕显示的内容全部清除;

b.归home位:将光标送到初始位;其中的*号为任意,高低电平均可;

c.输入方式:设光标移动方向并指定整体显示,是否移动。I/D=0:减量方式,S=1:移位,S=0:不移位;

d.显示状态:D指设置整体显示开关;C指设置光标显示开关;B指设置光标的字符闪耀;

e.光标画面滚动:R/L指右移或左移;S/C指移动总体或光标;

f.功能设置:DL接口数位,L指显示行数,F显示字型;

g.CGRAM地址设制:相当于一个数据库,可以在其中选择所需要的符号;

h.DDRAM地址设制:显示定位;

i.读BF和AC:B为最高位忙的标志,F为标志位;

j.写数据:将数据按要求写入到对应的单元;

k.读数据:读相应单元内的数据。

表2-3 命令字

指令名称控制信号控制代码
RSRWD7D6D5D4D3D2D1D0
清屏0000000001
归HOME位

000000001*
输入方式设制00000001I/DS
显示状态设制0000001DCB
无标画面滚动000001S/CRL**
功能设置00001DLNF**
CGRAM地址设制

0001A5A4A3A2A1A0
DDRAM地址设制

001A6A5A4A3A2A1A0
读BF和AC

01BFAC6AC5AC4AC3AC2AC1AC0
写数据10数 据

读数据11数 据

2)如图2-13为写命令字的流程图。

返回

图2-13 写命令字流程图

3)定义光标位置

把显示数据要某个位置,就是把显示数据写在相应的DDRAM地址中,DDRAM地址占7位。Set DDRAM address命令如表2-3所示。光标定位,写入一个显示字符后,DDRAM地址会自动加1或减1,加或减由输入方式设置。

表2-4 SetDDRAMaddress命令

RSR/WDB7DB6DB5DB4DB3DB2DB1DB0
001AC6AC5AC4AC3AC2AC1AC0
第1行DDRAM地址与第2行DDRAM地址并不连续。如表2-4所示。

表2-5 DDRAM地址

row12345141516
line180H81H82H83H84H8dH8eH8fH
line20c0H

0c1H

0c2H

0c3H

0c4H

0cdH0ceH0cfH
4)LCD初始化

从通电开始通过延时,先经过判忙后再进行功能设置,过一段时间后可以设制显示状态(如设置行、位或阵列)再经过延时后清屏后再可以设置输入方式,具体如图2-14所示。

返回

图2-14 LCD初始化流程图

2.LCD显示程序设计

LCD显示程序的设计一般先要确定LCD的初始化、光标定位、确定显示字符后,LCD就可以按如图2-15显示。

返回

图2-15 LCD显示程序流程图

第3章 软件设计

系统的软件设计可以分为几个部分,首先编写各个模块的底层驱动程序,而后是系统联机调试,编写上层系统程序。本系统软件程序主要包括:液晶LCD1602的底层驱动模块、时钟芯片DS1307的底层驱动模块、键盘扫描及键值处理模块、闹钟模块等。

3.1 系统流程图

系统的软件主流程图如图3-1所示。

Y

图3-1 主程序流程图

3.2 键盘处理模块流程图

系统设制了四个按键,分别是功能设置、定值上升、定值下降和返回键。整个系统的软件设计均采用C语言开发,采用查询的方式对按键状态进行扫描,确保系统的实时性。因此这些器件的底层程序均可以移植到其它系统中,这就是采用C语言开发的最大的优点。键盘处理流程图分别如图3-2、3-3、3-4、3-5所示。

设置分状态:+、-不变

图3-2 时间设置模块

设置月态:+、-不变

图3-3日期设置流程图

设置星期状态:+、-不变

图3-4 星期设置流程图

N A\    Y                Y        Y                Y       Y

 N                       N                             N                N

     

  N                        N                           N                 N

  N                          N                          N               N

  N                       N                            N                N

               N                           N                      N

 Y                             Y                          Y   

图3-5 闹钟设置流程图

第4章 系统测试

本设计的核心采用是ATC51单片机,以时钟芯片DS1307和液晶LCD1602作为外围元件,构成了一个多功能的数字时钟系统。

在设计中出现了很多问题,有的问题解决了,而有的问题不能解决。

在设计中遇到了按键不能正常工作,有的案件不灵活,有时候不能实现调时间,经过多次试验,在不同的计算机上验证,最后证实是因为软件的版本问题,和设置问题,通过与同学的讨论找到了原因所在,并修改。

还有就是软件错误,这是个大麻烦,比较难找出错误,不过在大家的努力下最终把错误改正,使其正常工作。

这个设计的不足是不能实现12/24小时的切换,由于程序太过复杂,要修改时相当的困难,所以没有实现这一功能。

总结

本设计以功能齐全适用于大众为指导思想,选取ATC51单片机为控制核心。通过外围元件实时时钟芯片DS1307构成了一个具有实时时间与日历显示、按键调时、闹铃定时功能的数字时钟。系统采用液晶LCD1602作为显示器,软件程序采用均采用C语言编写,便于移植与升级。

系统经组装、调试后,可以稳定运行。同时可以对时间、日期、星期和闹钟进行设置。系统采用串行器件具有线路简单、体积小、价格低等优点。报告详细介绍了整个系统的硬件组成结构、工作原理和系统的软件程序设计。

在本次设计的全过程中,我对二年所学的知识有了一个比较系统的认识和理解。涉及了各方面的知识,大大扩展了我的知识面,同时使我学会了如何用所学的知识去解决一些实际的问题。

在设计中我深知自己掌握的知识还远远不够,掌握的一些理论知识应用到实践中去,总会出现这样或那样的问题,不是理论没有掌握好,而是光知道书本上的知识是远远不够的,一定要把理论知识和实践结合起来。把学到的知识应用到实践中去,多做多练,才可以把理论的精华发挥出来。知识不是知道、了解就好,一定要去应用它,发展它,让它在现实生活中得到充分的应用,从而解决一些问题,这才是学习的根本目的。在设计阶段,通过对课题的深入分析与研究,使我对技术有了一定的了解。在遇到问题时,得到了指导老师与同学的悉心帮助,使我感受到集体的力量是无穷的。

通过这次设计,我学会了和别人配合工作,因为一个人所学的知识不可能面面俱到的,

只有通过合作,发挥自己的优点,体现团队精神,才能使工作做得更为出色。通过这

次设计,我学到了许多书本上学不到的知识,增强了自己的动手能力。即将毕业的我十分珍惜这次锻炼的机会,我按部就班的完成了自己的设计任务,但由于自己的知识水平有限,仍然存在很多的不足之处,恳请老师多多指教!当今的社会是竞争的社会,而人才的竞争则是竞争的焦点,课程设计对于我们即将离校的同学来说,是离校前很好的一次锻炼,使我们各方面的能力都有了很大的提高,为我们踏出校门,走上社会增强了能力与自信。

参考文献

[1]  白驹衍, 《单片计算机及应用》北京:电子工业出版社, 1999.2

[2]李朝青.单片机原理及接口技术(修订版).北京:北京航空航天大学出版社,1998

[3] 李广弟.单片机基础.北京:北京航空航天大学出版社,1992

[4] 何立民.单片机应用技术大全.北京:北京航空航天大学出版社, 1994

[5]张毅刚. 单片机原理及接口技术.哈尔滨:哈尔滨工业大学出版社,1990

    [6] 谭浩强.单片机课程设计. 北京:清华大学出版社,19

[7] 谢自美,《电子线路设计、实验、测试 》武汉:华中理工大学出版2000

[8] 何书森、何华斌《实用数字电路原理与设计速成》福州:福建科学技术出版社,2000.6

附录1 系统仿真电路图

附录2 主要源程序

 /***************************定义头文件开始***************************/

#include

#include

#include

#include

/****************************义头文件结束****************************/

/*************************第一部分:预定义变量************************/

/*************************0.LCD1602模块开始**************************/

/***************************0.1 输入方式控制*************************/

#define LCD_AC_AUTO_INCREMENT       0x06           //数据读、写操作后,AC自动增一,不移位

#define LCD_AC_AUTO_DECREASE        0x04           //数据读、写操作后,AC自动减一,不移位

#define LCD_MOVE_ENABLE             0x05           //数据读、写操作,画面平移

#define LCD_MOVE_DISENABLE          0x04           //数据读、写操作,画面不动

/***************************0.2 光标画面归位*************************/

#define LCD_GO_HOME                 0x02            //AC=0,光标、画面回HOME位

/***************************0.3 显示状态设置*************************/

#define LCD_DISPLAY_ON              0x0C           //显示开:整体显示,光标显示关,光标位的字符无闪耀

#define LCD_DISPLAY_OFF             0x08           //显示关

#define LCD_CURSOR_ON               0x0A           //光标显示

#define LCD_CURSOR_OFF              0x08           //光标不显示

#define LCD_CURSOR_BLINK_ON         0x09           //光标闪烁

#define LCD_CURSOR_BLINK_OFF        0x08           //光标不闪烁

/***************************0.4 光标画面滚动*************************/

//光标、画面移动,不影响DDRAM

#define LCD_LEFT_MOVE               0x18           //LCD显示左移一位

#define LCD_RIGHT_MOVE              0x1C           //LCD显示右移一位

#define LCD_CURSOR_LEFT_MOVE        0x10           //光标左移一位

#define LCD_CURSOR_RIGHT_MOVE       0x14           //光标右移一位

/****************************0.5 功能设置****************************/

#define LCD_DISPLAY_DOUBLE_LINE     0x38           //两行显示

#define LCD_DISPLAY_SINGLE_LINE     0x30           //单行显示

/******************************0.6 清屏******************************/

#define LCD_CLEAR_SCREEN            0X01            //清屏

/***********************0.7 LCD1602地址相关**************************/

#define LINE1_HEAD                  0x80            // 第一行DDRAM起始地址 

#define LINE2_HEAD                  0xc0           // 第二行DDRAM起始地址

#define LINE_LENGTH                 16             //每行的最大字符长度

/********************0.8 LCD1602接线引脚定义*************************/

#define LCDIO                       P1             //定义P2口与LCD1602的数据口相接

sbit    LCD_RS=P2^0;       

sbit    LCD_RW=P2^1;         

sbit    LCD_EN=P2^2;

sbit    LCD_BUSY=LCDIO^7;

/*******************0.9函数申明及相关定义****************************/

#define uchar                          unsigned char

#define uint                         unsigned int

void    delay_ms(uint m,uint n);

/*******************0.10 显示字符串编码定义**************************/

unsigned char code str[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x03a,0};

/*************************0.LCD1602模块结束**************************/

/**************************1.DS1307模块开始**************************/

/*****************************1.1 引脚定义***************************/

sbit     SCLK=P2^6;                        //DS1307串行时钟信号输入端口 pin 6

sbit     SDA=P2^7 ;                       //DS1307串行数据输入端口     pin 5 

/*****************************1.2 变量定义***************************/

code unsigned char rtc_address[7]={0x00,0x01,0x02,0x03,0x04,0x05,0x06};

unsigned char read_rtc_code[7]={0,0,0,0,0,0,0};

/**************************1.DS1307模块结束**************************/

/**************************3.闹钟响应模块开始************************/

sbit    Alarm_On=P2^3;

unsigned int Alarm_On_Flag=0;

/**************************3.闹钟响应模块结束************************/

/**************************4.时钟设置模块开始************************/

unsigned int DD=90000000;

//unsigned char  Time_Init[]={0,0,12,2,4,12,7}; //设置时间时的初始值:2007-12-4,星期二,12:00:00

unsigned char  Set_Alarm_Init[]={0,0,0};          //闹钟初始时间

/*************************第一部分:预定义变量************************/

/****************************函数声明*******************************/

unsigned int  Key_Scan(void);                            //按键扫描程序

void Set_Time_Second(unsigned int Key_On_Number_Flag);  //设置时间秒函数

void Set_Time_Minute(unsigned int Key_On_Number_Flag);  //设置时间分函数

void Set_Time_Hour(unsigned int Key_On_Number_Flag);    //设置时间时函数

void Set_Time(void);                                    //设置时间函数

void Set_Time_Date(unsigned int x);                        //设置日期

void Set_Date_Day(unsigned int Key_On_Number_Flag,unsigned int Month);      //设置日期日函数

void Set_Date_Month(unsigned int Key_On_Number_Flag);   //设置日期月函数

void Set_Date_Year(unsigned int Key_On_Number_Flag);    //设置日期年函数

void Set_Date(void);                                    //设置时间函数

void Set_Week(unsigned int x);                            //设置星期函数

void Set_Alarm_Second(unsigned int Key_On_Number_Flag); //设置闹钟秒函数

void Set_Alarm_Minute(unsigned int Key_On_Number_Flag); //设置闹钟分函数

void Set_Alarm_Hour(unsigned int Key_On_Number_Flag);   //设置闹钟时函数

void Set_Alarm(void);                                    //设置闹钟函数

void Set_Time_Display(unsigned int x,unsigned int y);    //设置时间时的显示时间函数

void Alarm_Ring(void);                                    //闹钟铃声函数

unsigned int Alarm_Compare(void);                        //闹钟时间比较函数

void Alarm_Ack(void);                                    //闹钟响应函数

/****************************函数声明*******************************/

/**********************第二部分:各模块底层驱动程序*******************/

/*************************1:LCD1602显示模块开始**********************/

/*********************1.1 检测LCD忙闲状态函数:开始*******************/

void LCD_check_busy(void)

{

    do{

        LCD_EN=0;                    //使能端无效

         LCD_RS=0;                

         LCD_RW=1;                    //读命令字状态,与LCD_RS配合使用,LCD_RS=0、LCD_RW=1时读命令

         LCDIO=0xff;                    //设置LCDIO(单片机与LCD的数据接口)为输入状态

         LCD_EN=1;                    //使能端有效,读LCD状态入LCDIO

     }while(LCD_BUSY==1);            //当(LCDIO^7)=1,表示LCD为忙状态,可延时一段时间后再读

    LCD_EN=0;                        //LCD空闲时停止读状态,将使能端清0,置无效状态

}

/*********************检测LCD忙闲状态函数:结束*********************/

/**************1.2 单片机向LCD1602发送控制命令函数:开始***********/

/*********参数说明:command为单片机向LCD1602发送的控制命令*********/

void LCD_send_command(uchar command)

{

    LCD_check_busy();                //检查LCD忙闲状态

    LCD_RS=0;

    LCD_RW=0;                        //写命令字状态,与LCD_RS配合使用,二者同为低电平时写命令

    LCD_EN=1;                        //使能端有效,单片机向LCD发送控制命令

    LCDIO=command;

    LCD_EN=0;                        //写命令结束,恢复状态

}

/****************单片机向LCD1602发送控制命令函数:结束*************/

/***************1.3 单片机向LCD1602发送数据函数:开始**************/

/*******************参数说明:dat为待发送字符数据******************/

void LCD_send_data(uchar dat)

{

    LCD_check_busy();                //检查LCD忙闲状态

    LCD_RS=1;                

    LCD_RW=0;                        //写数据状态,与LCD_RS配合使用,LCD_RS=1、LCD_RW=0时写数据    

    LCD_EN=1;

    LCDIO=dat;                        //使能端有效,单片机向LCD发送数据,送LCDIO

    LCD_EN=0;                        //发送数据结束,恢复状态

}

/*****************单片机向LCD1602发送数据函数:结束****************/

/******************1.4 单片机读LCD1602数据函数:开始***************/

unsigned char LCD_read_data(void)

{

    unsigned char dat;

    LCD_check_busy();                //检查LCD忙闲状态

    LCD_RS=1;                

    LCD_RW=1;                        //读数据状态,与LCD_RS配合使用,LCD_RS=1、LCD_RW=1时读数据    

    LCD_EN=1;

    dat=LCDIO;                        //使能端有效,单片机读LCD数据,送dat

    LCD_EN=0;                        //读数据结束,恢复状态

    return(dat);

}

/********************单片机读LCD1602数据函数:结束*****************/

/**********************1.5 LCD1602初始化:开始*********************/

void LCD_init(void)

{

    LCD_send_command(LCD_DISPLAY_DOUBLE_LINE);            //功能设置

    delay_ms(2,300);                                    //延时37us

    LCD_send_command(LCD_DISPLAY_ON);                    //显示状态设置

    delay_ms(1,300);                                    //延时37us

    LCD_send_command(LCD_CLEAR_SCREEN);                    //清屏

    delay_ms(1,2000);                                    //延时1.5ms

    LCD_send_command(LCD_AC_AUTO_INCREMENT);            //输入方式设置

    delay_ms(1,300);

}

/*************************LCD1602初始化:结束**********************/

/*********************1.6 LCD1602显示字符串:开始******************/

/*******参数说明:x为列数,y为行数,Data为待显示的字符串指针*******/

void LCD_disp_string(uchar x,uchar y,uchar *Data)

{

    if(y==0)                                        //显示第一行字符串

        {

            if(x                {

                     LCD_send_command(LINE1_HEAD+x);         //DDRAM首行起始地址加x

                     for(;x                         {

                              LCD_send_data(*(Data++));    //连续发送数据

                         }

                     if(*Data!='\\0')                    //第一行显示完,若字符串未显示完,则转至第二行显示

                         {

                              x=0;                         //列地址归home位

                              y=1;                     //转至第二行显示

                         }

                }

        }

    if(y==1)                                          //显示第二行字符串

        {

            LCD_send_command(LINE2_HEAD+x);                 //DDRAM第二行起始地址加x

            for(;x                {

                     LCD_send_data(*(Data++));             //连续发送数据

                }

        }

}

/***********************LCD1602显示字符串:结束********************/

/**********************1.7 LCD1602显示字符:开始*******************/

/**********参数说明:x为列数,y为行数,Data为待显示的字符**********

void LCD_disp_string1(uchar x,uchar y,uchar Data)

{

    if(y==0&&x        {

               LCD_send_command(LINE1_HEAD+x);                 //DDRAM首行起始地址加x

             LCD_send_data(Data);                        //连续发送数据

        }

    if(y==1&&x        {

            LCD_send_command(LINE2_HEAD+x);                 //DDRAM第二行起始地址加x

            LCD_send_data(Data);                         //连续发送数据

           }

}

************************LCD1602显示字符:结束*********************/

/************************1.8 设置显示地址开始**********************/

void LCD_set_add(unsigned char row,unsigned char col)

{

    if(row==0&&col        LCD_send_command(LINE1_HEAD+col);                //DDRAM首行起始地址加col

    else if(row==1&&col        LCD_send_command(LINE2_HEAD+col);                 //DDRAM第二行起始地址加col

}

/************************1.8 设置显示地址结束**********************/

/*************************1.9 延时函数:开始***********************/

/***************参数说明:改变m,n的值可改变延时时间长短************/

void delay_ms(uint m,uint n)                                    

{

    uint i,j;

for(i=0;i        for(j=0;j              {}

}

/****************************延时函数:结束************************/ 

/************************1:LCD1602显示模块结束*********************/

/***************************2:I2C总线模块开始**********************/

/***********************2.1 I2C延时程序开始************************/

void I2C_delay(void)                        //I2C延时程序

{

      _nop_();                                //空操作指令

      _nop_();

      _nop_();

      _nop_();

}

/***********************2.1 I2C延时程序结束************************/

/***********************2.2 I2C启动程序开始************************/

void I2C_start(void)            //I2C启动函数

{

      SDA=1;                        //数据为1

      _nop_();                    //保持

      SCLK=1;                        //设置时钟为高电平

      _nop_();                    //保持

      SDA=0;                        //时钟为高电平时,让数据SDA由1→0,启动I2C总线

      _nop_();                    //保持

      SCLK=0;                        //设置时钟为低电平

      _nop_();                    //保持

}

/***********************2.2 I2C启动程序结束************************/

/***********************2.3 I2C停止程序开始************************/

void I2C_stop(void)                            //I2C停止函数

{

      SDA=0;                                    //数据为0

      _nop_();

      SCLK=1;                                    //设置时钟为高电平

       _nop_();

      SDA=1;            //时钟为高电平时,让数据SDA由0→,停止I2C总线

      _nop_();

      SCLK=0;

      _nop_();

}

/***********************2.3 I2C停止程序结束************************/

/***********************2.4 I2C应答程序开始************************/

void I2C_send_ack(bit k)                    //I2C总线应答函数

{

    SDA=k;    //数据为0或1,若为0,表示接下来发送下一字节数据;若为1,表示发送下一周期数据

    I2C_delay();                            //延时,准备数据

    SCLK=1;                    //设置时钟为高电平,发送响应信号

    I2C_delay();

    SCLK=0;                                    //设置时钟为低电平

}

/***********************2.4 I2C应答程序结束************************/

/*****************2.4 单片机向I2C发送数据dat程序开始***************/

void I2C_write_byte(unsigned char dat)        //单片机向I2C总线写数据dat

{

    unsigned char i;                        //设置每次写的二进制数的各位:8个,即每次发送一个字节数据

for (i=0;i<8;i++)

    {

        SCLK=0;                                //设置时钟为低电平    

         I2C_delay();

         SDA=(bit)(dat&0x80);                //将dat与二进制数1000 0000B相与,取最首位送SDA(数据线)

         dat<<=1;                            //数据dat左移一位,以便取下一位数

         I2C_delay();

         SCLK=1;                                //设置始终为高电平,发送数据SDA

         I2C_delay();

    }

    SCLK=0;                                   //设置时钟为低电平

}

/*****************2.4 单片机向I2C发送数据dat程序结束***************/

/*******************2.5 单片机读I2C数据dat程序开始*****************/

unsigned char I2C_read_byte(void)            //CPU读I2C总线数据

{

    unsigned char i,dat;

    dat=0;

    SDA=1;

for (i=0;i<8;i++)

    {

        SCLK=0;                                //首先设置时钟位低电平,准备读数据SDA

        dat=dat<<1;                            //dat左移1位

        SCLK=1;                                //设置时钟为高电平,取数据            

         I2C_delay();

         if(SDA==1)                            //如果SDA=1,则dat加1

        {

              dat++;

          }

        SCLK=0;  

    }  

   return (dat); 

/*******************2.5 单片机读I2C数据dat程序结束*****************/

/***************************2:I2C总线模块结束**********************/

/***************************3:DS1307模块开始***********************/

/****************3.1 单片机向DS1307发送数据程序开始****************/

unsigned char Write1307(unsigned char add,dat)//add为目标地址,dat为待发送数据

{

      unsigned char temp;        //定义变量temp

      temp=dat/10;             //将待输出的十进制数字转换为DS1307内部可识别和存放的BCD码

      temp<<=4;                //比如10应转换为00010000即10H存放,执行结果依次为:

      temp=dat%10+temp;        //temp=1;temp=00010000B;temp=10%10+00010000B=00010000B=10H

  

      I2C_start();

      I2C_write_byte(0xD0);             //CPU向I2C总线发写命令,设置为写模式

      I2C_send_ack(0);                //发应答信号,等待发下一个数据    

      I2C_write_byte(add);               //CPU向I2C总线发送地址信息

      I2C_send_ack(0);                //发应答信号,等待发下一个数据

      I2C_write_byte(temp);             //CPU向I2C总线发数据

      I2C_send_ack(1);                //发送完毕,等待下一周期

      I2C_stop();

       return (0);

}

/****************3.1 单片机向DS1307发送数据程序结束****************/

/****************3.2 单片机读DS1307发送数据程序开始****************/

unsigned char Read1307(unsigned char add)    //add为源地址,返回值为dat

{

      unsigned char temp,dat;

      I2C_start();

      I2C_write_byte(0xD0);

      I2C_send_ack(0);

      I2C_write_byte(add);

      I2C_send_ack(1);

      I2C_stop();

  

      I2C_start();

      I2C_write_byte(0xD1);

      I2C_send_ack(0);

      dat=I2C_read_byte(); 

      I2C_send_ack(1);

      I2C_stop();        

 

      temp=dat/16;

      dat=dat%16;

      dat=dat+temp*10;

 

      return (dat);

}

/****************3.2 单片机读DS1307发送数据程序结束****************/

/******************3.3 单片机读DS1307寄存器程序开始****************/

void Read_RTC(void)

{

     unsigned char i,*p;

     p=rtc_address;

     for(i=0;i<7;i++)

         {

              read_rtc_code[i]=Read1307(*p);

              p++;

         }

}

/******************3.3 单片机读DS1307寄存器程序结束****************/

/***********************3.4 DS1307初始化程序开始*******************/

void Set_RTC(void)

{

     unsigned char i,*p;

     p=rtc_address;

     for(i=0;i<7;i++)

         {

              Write1307(*p,read_rtc_code[i]);

              p++;  

         }

}  

/***********************3.4 DS1307初始化程序结束*******************/

/***************************3:DS1307模块结束***********************/

/***************************4: 显示模块开始************************/

/***********************4.1 选择星期程序开始***********************/

void chose(void)

{

     unsigned char x;

    x=read_rtc_code[3]-2;

    if(x>6)    x=x-6;

else if(x<0) x=x+6;

    switch (x) 

          { 

            case 0:        LCD_disp_string(0,0,"Mon.");        break;

            case 1:        LCD_disp_string(0,0,"Tue.");        break;

            case 2:        LCD_disp_string(0,0,"Wed.");        break;

            case 3:        LCD_disp_string(0,0,"Thu.");        break;

            case 4:        LCD_disp_string(0,0,"Fri.");        break;

            case 5:        LCD_disp_string(0,0,"Sat.");        break;

            case 6:        LCD_disp_string(0,0,"Sun.");        break;

           }

}

void chose_set(void)

{

     unsigned char x;

    x=read_rtc_code[3]-2;

    if(x>6)    x=x-6;

else if(x<0) x=x+6;

    switch (x) 

          { 

            case 0:        LCD_disp_string(0,1,"Mon.");        break;

            case 1:        LCD_disp_string(0,1,"Tue.");        break;

            case 2:        LCD_disp_string(0,1,"Wed.");        break;

            case 3:        LCD_disp_string(0,1,"Thu.");        break;

            case 4:        LCD_disp_string(0,1,"Fri.");        break;

            case 5:        LCD_disp_string(0,1,"Sat.");        break;

            case 6:        LCD_disp_string(0,1,"Sun.");        break;

           }

}

/***********************4.1 选择星期程序结束***********************/

/***************************4.2 显示程序开始***********************/

void Display(void)      

{

    LCD_set_add(1,8);                            //设置显示的初始位置

     LCD_send_data((read_rtc_code[6]/10)|0x30);    //显示年

     LCD_send_data((read_rtc_code[6]%10)|0x30);

     LCD_send_data('-');

     LCD_send_data((read_rtc_code[5]/10)|0x30);    //显示月

     LCD_send_data((read_rtc_code[5]%10)|0x30);

      LCD_send_data('-');

     LCD_send_data((read_rtc_code[4]/10)|0x30);    //显示日

     LCD_send_data((read_rtc_code[4]%10)|0x30);

    LCD_set_add(0,8);

     LCD_send_data((read_rtc_code[2]/10)|0x30);    //显示时

     LCD_send_data((read_rtc_code[2]%10)|0x30);

      LCD_send_data(':');

     LCD_send_data((read_rtc_code[1]/10)|0x30);    //显示分

     LCD_send_data((read_rtc_code[1]%10)|0x30);

     LCD_send_data(':');

     LCD_send_data((read_rtc_code[0]/10)|0x30);    //显示秒

     LCD_send_data((read_rtc_code[0]%10)|0x30);

    chose();                                    //显示星期

}

/***************************4.2 显示程序结束***********************/

/***************************4: 显示模块结束************************/

/****************************5 键盘模块开始***********************/

/*************************按键扫描函数******************************/

unsigned int Key_Scan(void)

{

    unsigned int Key_On_State=0;    //按键状态标记

    unsigned int Key_On_Number=0;    //按键号

    P0=0x0f;                        //设置P0低4位为输入状态

    Key_On_State=P0&0x0f;            //读P0口低4位/按键状态

    if(Key_On_State!=0x0f)            //如果有按键按下

        {

            Key_On_State=P0&0x0f;

            delay_ms(400,40);

            if(Key_On_State==0x0e)    

                Key_On_Number=1;

            else if(Key_On_State==0x0d)

                Key_On_Number=2;

            else if(Key_On_State==0x0b)

                Key_On_Number=3;

            else if(Key_On_State==0x07)

                Key_On_Number=4;

            else 

                Key_On_Number=0;

        }

    while(Key_On_State!=0x0f)

    {

        delay_ms(100,40);

        //Key_On_State=P0&0x0f;

        Key_On_State=P0;

    }

    return Key_On_Number;

}

/*************************按键扫描函数******************************/

/**************************设置秒函数*******************************/

void Set_Time_Second(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L1:    switch(Key_On_Number_Flag)                //判断秒是加1还是减1

    {

        case 0:    break;

        case 1: read_rtc_code[0]++;            //秒加1

                if(read_rtc_code[0]>60)

                    read_rtc_code[0]=read_rtc_code[0]-60;    //如果加到60以上,则减60

                break;

        case 2:    read_rtc_code[0]--;            //秒减1

                if(read_rtc_code[0]<0)

                    read_rtc_code[0]=read_rtc_code[0]+60;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(0,3);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    

            case 1:    LCD_send_command(LCD_CLEAR_SCREEN);

                    Set_Time();

                    return;    //按键4和按键1按下,返回

            case 2:    Key_On_Number_Flag=1;    //按键2和按键3按下,设置时间秒

                    y=DD;

                    goto L1;

            case 3:    Key_On_Number_Flag=2;

                    y=DD;

                    goto L1;

        }

    }

    

}

void Set_Time_Minute(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L2:    switch(Key_On_Number_Flag)                //判断秒是加1还是减1

    {

        case 0:    break;                    

        case 1: ++read_rtc_code[1];            //秒加1

                if(read_rtc_code[1]>59)

                    read_rtc_code[1]=read_rtc_code[1]-60;    //如果加到60以上,则减60

                break;

        case 2:    --read_rtc_code[1];            //秒减1

                if(read_rtc_code[1]<0)

                    read_rtc_code[1]=read_rtc_code[1]+60;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(0,2);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4按下,返回

            case 1:    Set_Time_Second(0);    //按键1按下,调用设置秒函数

                    y=DD;

                    break;

            case 2:    Key_On_Number_Flag=1;//按键2和按键3按下,设置时间分

                    y=DD;

                    goto L2;

            case 3:    Key_On_Number_Flag=2;

                    y=DD;

                    goto L2;

        }

    }

    

}

/**************************设置分函数*******************************/

/*************************设置小时函数******************************/

void Set_Time_Hour(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L3:    switch(Key_On_Number_Flag)                //判断秒是加1还是减1

    {

        case 0:    break;                    

        case 1: read_rtc_code[2]++;            //秒加1

                if(read_rtc_code[2]>23)

                    read_rtc_code[2]=read_rtc_code[2]-24;    //如果加到60以上,则减60

                break;

        case 2:    --read_rtc_code[2];            //秒减1

                if(read_rtc_code[2]<0)

                    read_rtc_code[2]=read_rtc_code[2]+24;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(0,1);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4按下,返回

            case 1:    Set_Time_Minute(0);    //按键1按下,调用设置分函数

                    y=DD;

                    break;

            case 2:    Key_On_Number_Flag=1;//按键2和按键3按下,设置时函数

                    y=DD;

                    goto L3;

            case 3:    Key_On_Number_Flag=2;

                    y=DD;

                    goto L3;

        }

    }

}

/*************************设置小时函数******************************/

/**************************设置日函数*******************************/

void Set_Date_Day(unsigned int Key_On_Number_Flag,unsigned int Month)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

    unsigned int Month_Days_Flag;

    switch(Month)

    {

        case 1:     

        case 3:

        case 5:

        case 7:

        case 8:

        case 10:

        case 12:    Month_Days_Flag=31;    break;

        case 4:

        case 6:

        case 9:

        case 11:    Month_Days_Flag=30;    break;

        case 2:        if(((read_rtc_code[0])%4==0)&&((read_rtc_code[0]%100)!=0))

                        Month_Days_Flag=29;    

                    else

                        Month_Days_Flag=28;

    }

L4:    switch(Key_On_Number_Flag)                //判断秒是加1还是减1

    {

        case 0:    break;

        case 1: read_rtc_code[4]++;            //秒加1

                if(read_rtc_code[4]>Month_Days_Flag)

                    read_rtc_code[4]=read_rtc_code[4]-Month_Days_Flag;    //如果加到60以上,则减60

                break;

        case 2:    read_rtc_code[4]--;            //秒减1

                if(read_rtc_code[4]<0)

                    read_rtc_code[4]=read_rtc_code[4]+Month_Days_Flag;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(1,5);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    

            case 1:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4和按键1按下,返回

            case 2:    Key_On_Number_Flag=1;//按键2和按键3按下,设置时间秒

                    y=DD;

                    goto L4;

            case 3:    Key_On_Number_Flag=2;

                    y=DD;

                    goto L4;

        }

    }

    

}

/**************************设置日函数*******************************/

/**************************设置月函数*******************************/

void Set_Date_Month(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L5:    switch(Key_On_Number_Flag)                //判断秒是加1还是减1

    {

        case 0:    break;                    

        case 1: read_rtc_code[5]++;            //秒加1

                if(read_rtc_code[5]>12)

                    read_rtc_code[5]=read_rtc_code[5]-12;    //如果加到60以上,则减60

                break;

        case 2:    read_rtc_code[5]--;            //秒减1

                if(read_rtc_code[5]<0)

                    read_rtc_code[5]=read_rtc_code[5]+12;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(1,6);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4按下,返回

            case 1:    Set_Date_Day(0,read_rtc_code[1]);    //按键1按下,调用设置秒函数

                    y=DD;

                    break;

            case 2:    Key_On_Number_Flag=1;//按键2和按键3按下,设置时间分

                    y=DD;

                    goto L5;

            case 3:    Key_On_Number_Flag=2;    

                    y=DD;

                    goto L5;

        }

    }

    

}

/**************************设置月函数*******************************/

/*************************设置年函数******************************/

void Set_Date_Year(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L6:    switch(Key_On_Number_Flag)                //判断秒是加1还是减1

    {

        case 0:    break;                    

        case 1: read_rtc_code[6]++;            //秒加1

                if(read_rtc_code[6]>2100)

                    read_rtc_code[6]=read_rtc_code[6]-2100;    //如果加到60以上,则减60

                break;

        case 2:    read_rtc_code[6]--;            //秒减1

                if(read_rtc_code[6]<0)

                    read_rtc_code[6]=read_rtc_code[6]+2100;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(1,7);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4按下,返回

            case 1:    Set_Date_Month(0);    //按键1按下,调用设置分函数

                    y=DD;

                    break;

            case 2:    Key_On_Number_Flag=1;//按键2和按键3按下,设置时函数

                    y=DD;

                    goto L6;

            case 3:    Key_On_Number_Flag=2;    

                    y=DD;

                    goto L6;

        }

    }

    

}

/*************************设置小时函数******************************/

/////////////////////////////////////////////////////////////////////

/************************设置闹钟秒函数*****************************/

void Set_Alarm_Second(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L7:    switch(Key_On_Number_Flag)                            //判断秒是加1还是减1

    {

        case 0:    break;

        case 1: Set_Alarm_Init[0]++;            //秒加1

                if(Set_Alarm_Init[0]>60)

                    Set_Alarm_Init[0]=Set_Alarm_Init[0]-60;    //如果加到60以上,则减60

                break;

        case 2:    Set_Alarm_Init[0]--;            //秒减1

                if(Set_Alarm_Init[0]<0)

                    Set_Alarm_Init[0]=Set_Alarm_Init[0]+60;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(3,12);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    

            case 1:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4和按键1按下,返回

            case 2:    Key_On_Number_Flag=1;//按键2和按键3按下,设置时间秒

                    y=DD;

                    goto L7;

            case 3:    Key_On_Number_Flag=2;

                    y=DD;

                    goto L7;

        }

    }

    

}

/************************设置闹钟秒函数*****************************/

/************************设置闹钟分函数*****************************/

void Set_Alarm_Minute(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L8:    switch(Key_On_Number_Flag)                            //判断秒是加1还是减1

    {

        case 0:    break;                    

        case 1: Set_Alarm_Init[1]++;            //秒加1

                if(Set_Alarm_Init[1]>60)

                    Set_Alarm_Init[1]=Set_Alarm_Init[1]-60;    //如果加到60以上,则减60

                break;

        case 2:    Set_Alarm_Init[1]--;            //秒减1

                if(Set_Alarm_Init[1]<0)

                    Set_Alarm_Init[1]=Set_Alarm_Init[1]+60;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(3,11);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4按下,返回

            case 1:    Set_Alarm_Second(0);    //按键1按下,调用设置秒函数

                        y=DD;

                        break;

            case 2:    Key_On_Number_Flag=1;    //按键2和按键3按下,设置时间分

                        y=DD;

                        goto L8;

            case 3:    Key_On_Number_Flag=2;

                        y=DD;

                        goto L8;    

        }

    }

    

}

/************************设置闹钟分函数*****************************/

/***********************设置闹钟小时函数****************************/

void Set_Alarm_Hour(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L9:    switch(Key_On_Number_Flag)                            //判断秒是加1还是减1

    {

        case 0:    break;                    

        case 1: Set_Alarm_Init[2]++;            //秒加1

                if(Set_Alarm_Init[2]>24)

                    Set_Alarm_Init[2]=Set_Alarm_Init[2]-24;    //如果加到60以上,则减60

                break;

        case 2:    Set_Alarm_Init[2]--;            //秒减1

                if(Set_Alarm_Init[2]<0)

                    Set_Alarm_Init[2]=Set_Alarm_Init[2]+24;    //如果减到0以下,则加60

                break;

    }

    while(y--)

    {

        Set_Time_Display(3,10);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    LCD_send_command(LCD_CLEAR_SCREEN);

                    return;    //按键4按下,返回

            case 1:    Set_Alarm_Minute(0);    //按键1按下,调用设置分函数

                        y=DD;

                        break;

            case 2:    Key_On_Number_Flag=1;    //按键2和按键3按下,设置时函数

                        y=DD;

                        goto L9;

            case 3:    Key_On_Number_Flag=2;    

                        y=DD;

                        goto L9;

        }

    }

    

}

/***********************设置闹钟小时函数****************************/

/*************************设置闹钟函数******************************/

void Set_Alarm(void)

{

    unsigned int x;

    unsigned int y=DD;

    LCD_send_command(LCD_CLEAR_SCREEN);

    while(y--)

    {

        Set_Time_Display(3,9);

        x=Key_Scan();

        switch(x)        

        {

            case 0:    break;    

            case 4: LCD_send_command(LCD_CLEAR_SCREEN);

                    y=DD;

                    return;            

            case 1: Set_Alarm();

                    y=DD;

                    break;

            case 2: Set_Alarm_Hour(1);

                    y=DD;

                    break;                                

            case 3: Set_Alarm_Hour(2);

                    y=DD;

                    break;

                }

            }

    LCD_send_command(LCD_CLEAR_SCREEN);

    return;

}

/*************************设置闹钟函数******************************/

/*************************设置星期函数******************************/

void Set_Week(unsigned int Key_On_Number_Flag)

{

    unsigned int Key_On_Number_Flag1;

    unsigned int y=DD;

L7:    switch(Key_On_Number_Flag)                            //判断秒是加1还是减1

    {

        case 0:    break;                    

        case 1: ++read_rtc_code[3];            //秒加1

                if(read_rtc_code[3]>7)

                    read_rtc_code[3]=read_rtc_code[3]-7;    //如果加到60以上,则减60

                break;

        case 2:    --read_rtc_code[3];            //秒减1

                if(read_rtc_code[3]<0)

                    read_rtc_code[3]=read_rtc_code[3]+7;    //如果减到0以下,则加60

                break;

    }

    LCD_send_command(LCD_CLEAR_SCREEN);

    while(y--)

    {

        Set_Time_Display(2,8);                    //显示刚设置更新的时间

        Key_On_Number_Flag1=Key_Scan();        //扫描按键状态,求键值

        switch(Key_On_Number_Flag1)

        {

            case 0:    break;    //无按键按下,则跳出循环,计数值减1,继续扫描

            case 4:    return;    //按键4按下,返回

            case 1:    Set_Alarm();    //按键1按下,调用设置分函数

                    y=DD;

                    break;

            case 2:    Key_On_Number_Flag=1;    //按键2和按键3按下,设置时函数

                    y=DD;

                    goto L7;

            case 3:    Key_On_Number_Flag=2;    

                    y=DD;

                    goto L7;

        }

    }

    LCD_send_command(LCD_CLEAR_SCREEN);

    return;

}

/*************************设置星期函数******************************/

/*************************设置日期函数******************************/

void Set_Date(void)

{

    unsigned int x;

    unsigned int y=DD;

    LCD_send_command(LCD_CLEAR_SCREEN);    

    while(y--)

    {

        Set_Time_Display(1,4);

        x=Key_Scan();

        switch(x)        

        {

            case 0:    break;    

            case 4: Set_RTC();

                    LCD_send_command(LCD_CLEAR_SCREEN);

                    y=DD;

                    return;            

            case 1: Set_Week(0);

                    y=DD;

                    break;

            case 2: Set_Date_Year(1);

                    y=DD;

                    break;

            case 3: Set_Date_Year(2);

                    y=DD;

                    break;

        }

    }

    LCD_send_command(LCD_CLEAR_SCREEN);

    return;

}

/*************************设置日期函数******************************/

/*************************设置时间函数******************************/

void Set_Time(void)

{

    unsigned int x;

    unsigned int y=DD;

    x=Key_Scan();

    if(x!=1)

        return;

    else

        {

                LCD_send_command(LCD_CLEAR_SCREEN);

                while(y--)

                {    

                    Set_Time_Display(0,0);

                    x=Key_Scan();

                    switch(x)        

                    {

                        case 0:    break;    

                        case 4: Set_RTC();

                                LCD_send_command(LCD_CLEAR_SCREEN);

                                y=DD;

                                return;            

                        case 1: Set_Date();            

                                y=DD;

                                break;

                        case 2: Set_Time_Hour(1);    

                                y=DD;

                                break;

                        case 3: Set_Time_Hour(2);    

                                y=DD;

                                break;

                    }

                }

        }

    LCD_send_command(LCD_CLEAR_SCREEN);

    return;

}

/*************************设置时间函数******************************/

/*************************闹钟响应函数******************************/

void Alarm_Ring(void)

{

    unsigned int x=50;

    switch(Alarm_On_Flag)

    {

        case 0:    break;

        case 1:    while(x--)

                {

                    Alarm_On=0;

                    delay_ms(5,5);

                    Alarm_On=1;

                    delay_ms(5,5);

                }

                break;

    }

    return;

}

/*************************闹钟响应函数******************************/

/*************************闹钟时间比较函数**************************/

unsigned int Alarm_Compare(void)

{

    unsigned int x=50;

    if((Set_Alarm_Init[1]==read_rtc_code[1])&&(Set_Alarm_Init[2]==read_rtc_code[2]))

        Alarm_On_Flag=1;

    else

        Alarm_On_Flag=0;

    return(Alarm_On_Flag);

}

/*************************闹钟时间比较函数**************************/

/***************************闹钟响应函数****************************/

void Alarm_Ack(void)

{

    unsigned x;

    x=Alarm_Compare();

    switch(x)

    {

        case 0:    break;

        case 1:    Alarm_Ring();    break;

    }

    return;

}

/***************************闹钟响应函数****************************/

/*************************显示时间函数******************************/

void Set_Time_Display(unsigned int x,unsigned int y)      

{

    unsigned char *Display;

    switch(y)

    {

        case 0:  Display="Set_Time";            break;

        case 1:  Display="Set_Time_Hour";        break;

        case 2:  Display="Set_Time_Min.";        break;

        case 3:  Display="Set_Time_Sec.";        break;

        case 4:  Display="Set_Date";            break;

        case 5:  Display="Set_Date_Day";        break;

        case 6:  Display="Set_Date_Mon.";        break;

           case 7:  Display="Set_Date_Year";        break;

        case 8:  Display="Set_Week";            break;

        case 9:  Display="Set_Alarm";            break;

        case 10: Display="Set_Alarm_Hour";        break;

        case 11: Display="Set_Alarm_Min.";        break;

        case 12: Display="Set_Alarm_Sec.";        break;

        case 13: Display="A_ON";                break;

        case 14: Display="A_OFF";                break;

    }

    switch(x)

    {

        case 0:        LCD_disp_string(0,0,Display);

                    LCD_set_add(1,0);            //设置显示的初始位置

                    LCD_send_data((read_rtc_code[2]/10)|0x30);    //显示时

                     LCD_send_data((read_rtc_code[2]%10)|0x30);

                      LCD_send_data(':');

                     LCD_send_data((read_rtc_code[1]/10)|0x30);    //显示分

                     LCD_send_data((read_rtc_code[1]%10)|0x30);

                     LCD_send_data(':');

                     LCD_send_data((read_rtc_code[0]/10)|0x30);    //显示秒

                     LCD_send_data((read_rtc_code[0]%10)|0x30);

                    break;

        case 1:        LCD_disp_string(0,0,Display);

                    LCD_set_add(1,0);

                    LCD_send_data((read_rtc_code[6]/10)|0x30);    //显示年

                     LCD_send_data((read_rtc_code[6]%10)|0x30);

                     LCD_send_data('-');

                     LCD_send_data((read_rtc_code[5]/10)|0x30);    //显示月

                     LCD_send_data((read_rtc_code[5]%10)|0x30);

                      LCD_send_data('-');

                     LCD_send_data((read_rtc_code[4]/10)|0x30);    //显示日

                     LCD_send_data((read_rtc_code[4]%10)|0x30);

                    break;

        case 2:        LCD_disp_string(0,0,Display);

                    chose_set();

                    break;

        case 3:        LCD_disp_string(0,0,Display);

                    LCD_set_add(1,0);            //设置显示的初始位置

                    LCD_send_data((Set_Alarm_Init[2]/10)|0x30);    //显示时

                     LCD_send_data((Set_Alarm_Init[2]%10)|0x30);

                      LCD_send_data(':');

                     LCD_send_data((Set_Alarm_Init[1]/10)|0x30);    //显示分

                     LCD_send_data((Set_Alarm_Init[1]%10)|0x30);

                     LCD_send_data(':');

                     LCD_send_data((Set_Alarm_Init[0]/10)|0x30);    //显示秒

                     LCD_send_data((Set_Alarm_Init[0]%10)|0x30);

                    break;

    }

}

/*************************显示时间函数******************************/

/****************************5 键盘模块结束***********************/

/***************************8.主函数:开始*************************/

void main(void)

{

    uint i;

    uchar *cp;                                            //定义字符串指针

    LCD_init();                                            //LCD初始化

    while(1)

        {

            delay_ms(20,100);                            //延时

            Read_RTC();                 //读时间信息

            delay_ms(100,100);

            Display();                    //液晶实时显示

            delay_ms(100,100);    

            Set_Time();                   //键盘处理

            Alarm_Ack();                 //闹钟处理

        }

}

/****************************主函数:结束**************************/

                   

附录3 软件设计变量表

序号变量名含义
1LCD_AC_AUTO_INCREMENT数据读、写操作后,AC自动增一,不移位

2LCD_AC_AUTO_DECREASE数据读、写操作后,AC自动减一,不移位

3LCD_MOVE_ENABLE数据读、写操作,画面平移

4LCD_MOVE_DISENAB光标画面归位
5LCD_GO_HOMEAC=0,光标、画面回HOME位

6LCD_DISPLAY_ON显示开:整体显示,光标显示关,光标位的字符无闪耀
7LCD_DISPLAY_OFF  LE数据读、写操作,画面不动

8LCD_CURSOR_ON光标显示
9LCD_CURSOR_OFF  光标不显示
10LCD_CURSOR_BLINK_ON光标闪烁
11LCD_CURSOR_BLINK_OFF  光标不闪烁
12LCD_LEFT_MOVE  LCD显示左移一位

13LCD_RIGHT_MOVELCD显示右移一位

14LCD_CURSOR_LEFT_MOVE光标左移一位
15LCD_CURSOR_RIGHT_MOVE光标右移一位
16LCD_DISPLAY_DOUBLE_LINE两行显示
17LCD_DISPLAY_SINGLE_LINE单行显示
18LCD_CLEAR_SCREEN清屏
19LINE1_HEAD第一行DDRAM起始地址

20LINE2_HEAD   第二行DDRAM起始地址

21LINE_LENGTH每行的最大字符长度
22LCDIO定义P1口与LCD1602的数据口相接

23unsigned char code str[]显示字符串编码定义
24SCLKP2^6DS1307串行时钟信号输入端口

25SDAP2^7 DS1307串行数据输入端口

26  LCD_RSP2^0
27LCD_RWP2^1
 28LCD_ENP2^2
 29LCD_BUSYLCDIO^7
 30Alarm_OnP2^3
附录4 程序模块及参数

序号子程序名入口参数出口参数功能
1LCD_check_busy检测LCD忙闲状态函数

2LCD_send_commandcommand单片机向LCD1602发送的控制命令

3LCD_send_datadat单片机向LCD1602发送数据函数

4LCD_read_datadat1.4 单片机读LCD1602数据函数

5LCD_init1.5 LCD1602初始化

6LCD_disp_stringuchar x,uchar y,uchar *DataLCD1602显示字符串

7LCD_disp_string1uchar x,uchar y,uchar DataLCD1602显示字符

8LCD_set_addchar row, char col

设置显示地址
9delay_msm,n

延时函数
10I2C_delay

I2C延时程序开始

11I2C_start

I2C启动程序

12I2C_stop

I2C停止程序

13I2C_send_ack

kI2C应答程序

14I2C_write_byte

char dat单片机向I2C发送数据dat程序

15I2C_read_byte

dat单片机读I2C数据dat程序

16Write1307char add,dat0单片机向DS1307发送数据程序

17Read1307adddat单片机读DS1307发送数据程序

18Read_RTC单片机读DS1307寄存器程序

19Set_RTCDS1307初始化程序

20chose选择星期程序
21Display显示程序
22Key_ScanKey_On_Number按键扫描函数
23Set_Time_Secondkey_On_Number_Flag设置秒函数
24Set_Time_MinuteKey_On_Number_Flag设置分函数
25Set_Time_HourKey_On_Number_Flag设置小时函数
26Set_Date_DayKey_On_Number_Flag, 

设置日函数
27Set_Date_MonthKey_On_Number_Flag设置月函数
28Set_Date_YearKey_On_Number_Flag设置年函数
29Set_Alarm_SecondKey_On_Number_Flag设置闹钟秒函数
30Set_Alarm_MinuteKey_On_Number_Flag设置闹钟分函数
31Set_Alarm_HourKey_On_Number_Flag设置闹钟小时函数
32Set_Alarm设置闹钟函数
33Set_WeekKey_On_Number_Flag)设置星期函数
34Set_Date设置日期函数
35Set_Time设置时间函数
36Alarm_Ring闹钟响应函数
37Alarm_Ack闹钟响应函数
38Alarm_CompareAlarm_On_Flag闹钟时间比较函数
39Set_Time_Displayint x int y

显示时间函数

文档

数字钟课程设计论文

《基于单片机的数字时钟》设计任务书一、设计题目基于单片机的数字时钟二、设计任务以51单片机、实时时钟芯片DS1307(或DS1302)和液晶显示LCD1602为主体,设计一款简易数字时钟,主要功能如下:基本功能1.能够准确显示时间(显示格式为时时:分分:秒秒,12//24小时制切换);2.可进行时间调整;3.具有闹钟时间设置、闹钟开/关。扩展功能1.可设置多个闹钟。2.其它功能(如音乐闹钟、温度显示灯)。三、设计要求电路设计1.画出电路框图;2.画出整机电路工作原理图。软件设计1.根据功能和电
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top