智能时钟课程设计说明书

更新时间:2024-04-02 12:44:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

课程设计说明书

课程名称: 2011级机电专业课程设计 题 目: 智能电子钟(LCD显示)

学 院: 机电工程学院 系: 机电 专 业: 机械设计制造及其自动化 班 级: 机制xxx班 学 号: xxxxxxxxx 学生姓名: xxxxx 起讫日期: — 指导教师: xxxxxx 职称: 系分管主任: 审核日期:

摘 要

电子时钟主要是利用电子技术将时钟电子化、数字化,拥有时钟精确、体积小、界面友好、可扩展性能强等特点,被广泛应用于生活和工作当中。另外,在生活和工农业生产中,也常常需要温度,这就需要电子时钟具有多功能性。本设计主要为实现一款可正常显示时钟/日历、带有定时闹铃的多功能电子时钟。

本文对当前电子钟开发手段进行了比较和分析,最终确定了采用单片机技术实现多功能电子时钟。本设计应用AT89C51芯片作为核心,使用DS1302实时时钟日历芯片完成时钟/日历的基本功能。这种实现方法的优点是电路简单,性能可靠,实时性好,时间精确,操作简单,编程容易。

该电子时钟可以应用于一般的生活和工作中,也可通过改装,提高性能,增加新功能,从而给人们的生活和工作带来更多的方便。

关键词:电子时钟;多功能;AT89C51;DS1302

第1章

1.1 方案设计

总体设计方案

实时时钟芯片具备年、月、日、时、分、秒计时功能和多点计时功能,计时数据的更新每秒自动进行一次,不需程序干预。计算机可通过中断或查询方式读取计时数据进行显示,因此计时功能的实现无需占用CPU的时间,程序简单。此外,实时时钟芯片多数带有锂电池做后备电源,具备永不停止的计时功能;具有可编程方波输出功能,可用做实时测控系统的采样信号等;有的实时时钟芯片内部还带有非易失性RAM,可用来存放需长期保存但有时也需变更的数据,由于功能完善,精度高,软件程序设计相对简单,且计时不占用CPU时间,因此,在工业实时测控系统中多采用这一类专用芯片来实现实时时钟功能。

利用单片机内部的定时/计数器进行中断定时,配合软件延时实现时、分、秒的计时及秒表计时。该方案节省硬件成本,且能使设计者对单片机的指令系统能有更深入的了解,从而掌握单片机应用技术MCS-51汇编语言程序设计方法,因此,本系统设计采用此种软件控制方法来实现计时。而由于Atmel公司的AT89C51是一种自带4KB Flash存储器的低电压、高性能的CMOS 8位微处理器。该器件采用Atmel高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出引脚相兼容。它的功能强大,而且也比较容易购买,故本设计中所选的单片机为AT89C51单片机。 1.2 系统框图

按照系统设计功能的要求,初步确定系统由主控模块、时控模块、及显示模块和键盘

接口模块共4个模块组成。主控芯片使用51系列AT89C51单片机,时钟芯片使用美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟DS1302。采用DS1302作为计时芯片,可以做到计时准确。更重要的是,DS1302可以在很小电流的后备电源(2.5~5.5V电源,再2.5V时耗电小于300nA),而且DS1302可以编程选择多种充电电流来为后备电源进行慢速充电,可以保证后备电源基本不耗电。显示模块采用普通的液晶显示屏(LCD),设计框图如图1所示

图1 设计方案框图

第2章 核心芯片简介

2.1 DS1302简介

DS1302是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟芯片,它可以对年、月、日、周日、时、分、秒进行计时,且具有闰年补偿功能,工作电压宽达2.5~5.5V。时钟可工作在24小时格式或12小时(AM/PM)格式。 DS1302与单片机的接口使用同步串行通信,仅用3条线与之相连接。可采用一次传送一个字节或突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后背电源双电源引脚,同时提供了对后背电源进行涓细电流充电的能力。 2.1.1 DS1302引脚功能与内部结构

DS1302的引脚功能如表1所示,外形及内部结构如图2所示:

表1 DS1302引脚功能表

引脚号 1 2、3 4 5 6 7 8 引脚名称 VCC2 X1、X2 GND RST I/O SCLK VCC1

功能 主电源 振荡源,外接32768Hz晶振 地线 复位/片选线 串行数据输入/输出端(双向) 串行时钟输入端 后备电源

Vc

Vc

G

电源 控制 VccXX2 G1 2 3 4 8 7 6 5 VcSCI/R 32 768KHz X2 振荡器与分频器 X1 实时时钟 I/输入移位寄存器 SC命令与控 制逻辑 AD 31x8RAM RS图2 DS1302管脚图及内部结构图

2.1.2 DS1302控制字

DS1302的控制字节如表2所示:

表2 DS1302控制字节的含义

7 1 6 5 RAM A4 4 A3 3 A2 2 A1 1 A0 0 RAM K CK

控制字节的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入到DS1302中。位6如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出。 2.1.3 DS1302复位引脚

通过把RST输入驱动置高电平来启动所有的数据传送。 RST输入有两种功能:首先,

RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供了终止单字节

或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中置RST为低电平,则会终止此次数据传送,并且I/O引脚变为高阻态。上电运行时,在Vcc≥2.5V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。 2.1.4 DS1302数据的输入与输出

在控制指令字输入后的下一个SCLK时钟的上升沿时数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位至高位7,数据读写时序如图3所示:

SCLKRST0I/O1234567014567R/WA0A1A2A3A4R/C1DATA I/O BYTE1DATA I/O BYTE2图3 数据读写时序

2.1.5 DS1302寄存器

DS1302共有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD

码形式。其日历、时间寄存器及其控制字见表3。

此外,DS1302还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器的内容。 DS1302与RAM相关的寄存器分为两类,一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H--FDH,其中奇数为读操作,偶数为写操作;再一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。

表3 DS1302的日历、时钟寄存器及其控制字

寄存器名 分寄存器 时寄存器 日寄存器 月寄存器 周寄存器 年寄存器 命令字 写操作 读操作 81H 83H 85H 87H 89H 8BH 8DH 82H 84H 86H 88H 8AH 8CH 取值范围 00--59 00--59 01-12或00-23 01-28,29,30,31 01--12 01--07 00--99 7 CH 0 12/24 0 0 0 0 0 0 0 6 5 10SEC 10MIN 10 HR 各位内容 4 3 2 1 0 SEC MIN HR DATE MONTH 0 DAY YEAR 秒寄存器 80H 10DATE 0 0 10M 0 10YEAR 2.2 AT89C51简介

AT89C51是一种带4K字节FLASH存储器(FPEROM—Flash Programmable and Erasable Read Only Memory)的低电压、高性能CMOS 8位微处理器,俗称单片机。AT89C2051是一种带2K字节闪存可编程可擦除只读存储器的单片机。单片机的可擦除只读存储器可以反复擦除1000次。该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。由于将多功能8位CPU和闪烁存储器组合在单个芯片中,ATMEL的AT89C51是一种高效微控制器,AT89C2051是它的一种精简版本。AT89C51单片机为很多嵌入式控制系统提供了一种灵活性高且价廉的方案。

2.2.1 AT89C51芯片引脚及特点

AT89S51芯片的引脚结构如图4所示:

图4 AT89C51芯片的引脚结构图

(1)功能特性概括:

AT89C51提供以下标准功能:40个引脚、4K Bytes Flash片内程序存储器、128 Bytes的随机存取数据存储器(RAM)、32个外部双向输入/输出(I/O)口、5个中断优先级2层中断嵌套中断、2个数据指针、2个16位可编程定时/计数器、2个全双工串行通信口、看门狗(WDT)电路、片内振荡器及时钟电路。此外,AT89C51可降至0Hz的静态逻辑操作,并支持两种软件可选的节电工作模式。空闲模式下,CPU暂停工作,而RAM、定时/计数器、串行通信口、外中断系统可继续工作。掉电模式冻结振荡器而保存RAM的数据,停止芯片其它功能直至外中断激活或硬件复位。同时该芯片还具有PDIP、TQFP和PLCC等三种封装形式,以适应不同产品的需求。 (2)管脚说明: VCC:供电电压。 GND:接地。

P0口:P0口为一个8位漏级开路双向I/O口,也即地址/数据总线复用口。作为输出

口用时,能驱动8个TTL逻辑门电路。对端口写“1”时,被定义为高阻输入。 在访问外部数据存储器或程序存储器时,这组口线分时转换地址(低8位)和数据总线复用,在访问期间激活内部上拉电阻。

在Flash编程时,P0口接收指令字节,而在程序校验时,输出指令字节,校验时,要求外接上拉电阻。

P1口:P1口是一个带内部上拉电阻的8位双向I/O口,P1口的输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对端口写“1”,通过内部的上拉电阻把端口拉到高电平,此时可作输入口。作输入口使用时,因为内部存在上拉电阻,某个引脚被外部信号拉低时会输出一个电流(I)。

在Flash编程和程序校验期间,P1接收低8位地址。部分端口还有第二功能,如表4所示

表4 P1口部分引脚第二功能

端口引脚 P1.5 P1.6 P1.7

第二功能 MOSI(用于ISP编程) MISO(用于ISP编程) SCK (用于ISP编程) P2口:P2口是一个带有内部上拉电阻的8位双向I/O口, P2口的输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对端口写“1”,通过内部的上拉电阻把端口拉到高电平,此时可作输入口。作输入口使用时,因为内部存在上拉电阻,某个引脚被外部信号拉低时会输出一个电流(IIL)。

在访问外部程序存储器或16位地址的外部数据存储器(例如执行MOVX@DPTR指令)时,P2口送出高8位地址数据。在访问8位地址的外部数据寄存器(例如执行MOVX@Ri指令)时,P2口线上的内容(也即特殊功能寄存器(SFR)区中P2寄存器的内容),在整个访问期间不改变。

在Flash编程或校验时,P2亦接收高位地址和其它控制信号。

P3口: P3口是一个带有内部上拉电阻的双向8位I/O口, P3口的输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对P3口写“1”时,它们被内部的上拉电阻拉高并可作为输入端口。作输入口使用时,被外部信号拉低的P3口将用上拉电阻输出电流(IIL)。

P3口除了作为一般的I/O口线外,更重要的用途是它的第二功能,如表5所示: P3口还接收一些用于Flash闪速存储器编程和程序校验的控制信号。

表5 P3口引脚第二功能

端口引脚 P3.0 P3.1 P3.2 P3.3 P3.4 P3.5 P3.6 P3.7 第二功能 RXD (串行输入口) TXD (串行输出口) INT0 (外中断0) INT1 (外中断1) T0 (定时/计数器0) T1 (定时/计数器1) WR (外部数据存储器写选通) RD (外部数据存储器读选通)

RST:复位输入。当振荡器工作时,RST引脚出现两个机器周期以上的高电平时间将使单片机复位。WDT溢出将使该引脚输出高电平,设置SFR AUXR的DISRTO位(地址8EH)可打开或关闭该功能。 DISRTO位缺省为RESET输出高电平打开状态。

ALE/PROG:当访问外部存储器或数据存储器时,ALE(地址锁存允许)输出脉冲用于锁存地址的低8位字节。即使不访问外部寄存器,ALE仍以时钟振荡频率的1/6输出固定的正脉冲信号,因此它可对外输出时钟或用于定时目的。值得注意的是:每当访问外部数据存储器时将跳过一个ALE脉冲。

对Flash存储器编程期间,该引脚还用于输入编程脉冲(PROG)。

如有必要,可通过对特殊功能寄存器(SFR)区中的8EH单元的D0位置位,可禁止ALE操作。该位置位后,只要一条MOVX和MOVC指令才会激活ALE。此外,该引脚会被微弱拉高,单片机执行外部程序时,应设置ALE无效。

PSEN:程序存储允许(PSEN)输出是外部程序存储器的读选通信号,当AT89S51由外部程序存储器取指令(或数据)时,每个机器周期两次PSEN有效,即输出两个脉冲。当访问外部数据存储器时,没有两次有效的PSEN信号。

EA/VPP:外部访问允许。欲使CPU仅访问外部程序存储器(地址为0000H-FFFFH),EA端必须保持低电平(接地)。需要注意的是:如果加密位LB1被编程,复位时内部会锁存EA端状态。

如EA端保持高电平(接VCC端),CPU则执行内部程序存储器中的指令。

Flash存储器编程期间,该引脚用于施加+12V编程电压(VPP)。 XTAL1:反向振荡放大器的输入及内部时钟工作电路的输入端。 XTAL2:反向振荡放大器器的输出端。 (3)晶体振荡器特性:

AT89C51中有一个用于构成内部振荡器的高增益反相放大器,引脚XTAL1和XTAL2分别为该反向放大器的输入端和输出端。这个反向放大器与作为反馈元件的片外石英晶体或陶瓷谐振器一起构成自激振荡器。

外接石英晶体(或陶瓷谐振器)及电容C1、C2接在放大器的反馈回路中构成并联振荡电路。对外接电容C1、C2虽然没有十分严格的要求,但电容容量的大小会轻微影响振荡频率的高低、振荡器工作的稳定性、起振的难易程度及温度稳定性。如果使用石英晶体,电容应该使用30pF?10pF。

还可以使用外部时钟。这种情况下,外部时钟脉冲接XTAL1端,即内部时钟发生器的输入端, XTAL2应悬空。

由于外部时钟信号是通过一个2分频触发器后作为内部时钟信号的,所以外部时钟信号的占空比没有特殊要求,但最小高电平持续时间和最大低电平持续时间应符合产品技术条件的要求。

2.2.2 AT89C51的主要性能参数: (1)与MCS-51产品指令系统完全兼容。

(2)4K字节在系统可编程(ISP)Flash闪速存储器。 (3)1000次擦写周期。

(4)4.0—5.5V的工作电压范围。 (5)全静态工作模式:0Hz--33Hz。 (6)三级程序加密。

(7)128x8字节的内部RAM。 (8)32个双向可编程I/O口线。 (9)2个16位可编程定时/计数器。 (10)6个中断源。

(11)全双工UART串行通道。 (12)低功耗空闲和掉电模式。 (13)中断可从空闲模式唤醒系统。 (14)看门狗(WDT)及双数据指针。

(15)掉电标识和快速编程特性。

(16)灵活的在系统编程(ISP-字节或页写模式)。

第3章 智能电子钟软硬件电路的设计

3.1 时钟电路

使用串行接口时钟芯片DS1302设计时钟电路。该设计方案以单片机AT89S51为主控芯片,以串行时钟芯片DS1302为核心计时芯片,组成数字时钟电路。该电路不但能准确地计时、附加其它功能,而且,其三线接口可以节省接口资源,在断电后不丢失时间和数据信息。该设计方案的接口电路如图5所示:

图5 DS1302接口电路

3.2 硬件设计

该设计的硬件电路由主控部分(单片机AT89C51)、计时部分(实时时钟芯片DS1302)、D1602LCD、电源部分(三端稳压器7805)4个部分组成。各部分之间相互协作,构成一个统一的有机整体,实现数字时钟的功能。各部分的硬件电路设计如下。设计总电路图见附录一。

3.2.1 单片机AT89C51外围电路设计

单片机AT89S51作为主控芯片,控制整个电路的运行。单片机外围需要一个复位电路,复位电路的功能是:系统上电时提供复位信号,直至系统电源稳定后,撤消复位信号。为

可靠起见,电源稳定后还要经一定的延时才撤销复位信号,以防电源开关或电源插头分-合过程中引起的抖动而影响复位。该设计采用含有二极管的复位电路,复位电路可以有效的解决电源毛刺和电源缓慢下降(电池电压不足)等引起的问题,在电源电压瞬间下降时可以使电容迅速放电,一定宽度的电源毛刺也可令系统可靠复位。复位电路的设计图如图6所示:

图6 单片机复位电路图

AT89S51具有在系统可编程功能,可以很方便的改写单片机存储器内的程序不需要把芯片中从工作环境中剥离,把AT89S ISP下载口接入电路,可使电路实现该功能。AT89S51需要接入一个普通12MHz晶振,为其提供稳定的时钟脉冲。该设计中有6个八段数码显示管LED,所以,在单片机AT89S51外围需要接入6个三极管来驱动数码显示管。此外,单片机外围需要接入3个开关,用来调整时钟。单片机外围电路的设计图如图7示: 3.2.2 DS1302与单片机的接口设计

时钟芯片DS1302与单片机AT89S51的接口是由3条线来完成的,单片机AT89S51的P1.0与时钟芯片的数据传输端相连,P1.1用来作为DS1302输入时钟SCLK控制端,P1.2控制DS1302的复位输入端。DS1302接标准32.768KHz石英晶振。DS1302与单片机的接口电路如图8所示:

图8 DS1302与AT89C51连接图

3.2.4 电源设计

时钟芯片DS1302有很宽的工作电压范围,其工作电压为2.5~5.5V。单片机AT89S51的工作电压范围相对较窄,为4.0~5.5V,所以本设计中,给电路接入三端稳压器7805,利用它的稳压功能给电路提供稳定的+5V电压,使电路的工作保持很高的可靠性。在电路中接入一个发光二极管作为指示灯,可以很方便地指示电源与电路是否接通。该设计的电源部分如图10所示:

图10 电源电路图

3.2.5 按键开关去抖设计

按键开关在电路中的连接如图所示。按键未按下时,A点电位为高电平5V;按键按下时,A点电位为低电平。A点电位就用于向CPU传递按键的开关状态。但是由于按键的结构为机械弹性开关,在按键按下和断开时,触点在闭合和断开瞬间还会接触不稳定,引起A点电平不稳定,如图11所示,键盘的抖动时间一般为5~10ms,抖动现象会引起CPU对一次键操作进行多次处理,从而可能产生错误。因此必须设法消除抖动的不良后果。

图11 键操作和键抖动

消除抖动的不良后果的方法有硬、软件两种。

为了节省硬件,通常在单片机系统中,一般不采用硬件方法消除键的抖动,而是用软件消除抖动的方法。根据抖动特性,在第一次检测到按键按下后,执行一段延时5~10ms让前延抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认真正有键按下。当检测到按键释放后,也要给5~10ms的延时,待后延抖动消失后才转入该键处理程序。 3.3 软件设计

电子时钟的程序主要包括3个方面的内容:一是DS1302从单片机中读取数据进行计数,二是利用按键进行时间的调整,三是单片机中读取DS1302中的数据驱动LED数码管显示时间。STC89C52RC单片机主要I/O口的分配:P10、P11、P12分别接时钟芯片的SCLK、I/O、RST引脚,P13、P14接两个独立按键。

开 始 控制键有效,进入月调整程序 控制键有效,进去年调整程序 等待按键程序 等待按键程序 加键有效 加键有效 减键有效 月加1 年加1 年减1 减键有效 月减1 控制键有效,进星期调整程序 控制键有效,进入日期调整程序 等待按键程序 加键有效 星期加1 日加1 日减1 控制键有效,进分钟调整程序 控制键有效,进入小时调整程序 等待按键程序 等待按键程序 加键有效 分钟加1 小时加1 小时减1 按键有效,跳出时间调整程序,进入主循环程序 图12 时间调整程序流程图

减键有效 分钟减1 星期减1 减键有效 等待按键程序 加键有效 减键有效 加键有效 减键有效

第4章 protues仿真与调试

4.1总原理图

利用keil软件编写源程序。在protues中画好其电路图如下图14所示:

图13 电路板的原理图

4.2电路板的仿真

源 程 序

#include #include #include \#include \

#define uint unsigned int #define uchar unsigned char

sbit Set = P3^0; //模式切换键 sbit Up = P3^3; //加法按钮 sbit Down = P3^6; //减法按钮

sbit out = P3^1; //立刻跳出调整模式按钮 char done,count,temp,flag,up_flag,down_flag; void show_time(); //液晶显示程序 void Delay1ms(unsigned int count) {

unsigned int i,j; for(i=0;i

/*延时子程序*/

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(CurrentTime.TimeString); //显示时间 GotoXY(0,0);

Print(CurrentTime.DateString); //显示日期 GotoXY(15,0);

Print(week_value); //显示星期 GotoXY(11,0);

Print(\在液晶上显示 字母 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(); //扫描各功能键 } } }

LCD头文件:

#ifndef LCD_CHAR_1602_2005_4_9 #define LCD_CHAR_1602_2005_4_9

#include

/***********1602液晶显示部分子程序****************/

//Port Definitions********************************************************** sbit LcdRs = P2^0; sbit LcdRw = P2^1; sbit LcdEn = P2^2;

sfr DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口

//内部等待函数************************************************************************** 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++; } }

#endif

DS1302头文件:

#ifndef _REAL_TIMER_DS1302_2003_7_21_ #define _REAL_TIMER_DS1302_2003_7_21_ #define uchar unsigned char

sbit DS1302_CLK = P1^6; //实时时钟时钟线引脚 sbit DS1302_IO = P1^7; //实时时钟数据线引脚 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; 计数

uchar week_value[2];

//秒,分,时到日,月,年位闪的

/***********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 ReadValue;

ReadValue = Read1302(DS1302_SECOND);

Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_MINUTE);

Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_HOUR);

Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_DAY);

Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_WEEK);

Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_MONTH);

Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_YEAR);

Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&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 + '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 + '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 + '0'; }

else {

Time->DateString[8] = ' ';

Time->DateString[9] = ' '; }

if(hide_week<2)

{

week_value[0] = Time->Week + '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 + '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 + '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 + '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/25.星期: 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); //禁止写入 } }

#endif

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/25.星期: 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); //禁止写入 } }

#endif

本文来源:https://www.bwwdw.com/article/2ghr.html

Top