用DS1302与12864LCD设计的可调式中文电子日历

用DS1302与12864LCD设计的可调式中文电子日历

  • 一、需求分析
    • 1.1设计背景
    • 1.2设计目的及意义
    • 1.3系统的总体概述
      • 1.3.1目标
    • 1.4系统模型
      • 1.4.1主程序模块
    • 1.5功能需求
    • 1.6精度
      • 1.6.1时间特性
      • 1.6.2灵活性
    • 1.7环境
  • 二、系统设计
    • 2.1系统结构框架图
    • 2.2 AT89C51性能简介
    • 2.3 AT89C51引脚功能说明
    • 2.4 DS1302
    • 2.5 12864LCD
    • 2.6系统方案设计
    • 2.7主要参数设计部分
  • 三、硬件设计
    • 3.1电路设计图
    • 3.2主要单元电路设计
      • 3.2.1 LCD12864技术资料
      • 3.2.1.1 12864(KS0108)字符型LCD简介
      • 3.2.1.2 引脚说明
      • 3.2.1.3 12864LCD的指令说明
      • 3.2.2复位电路
      • 3.2.3实时时钟芯片DS1302 简介
  • 四、软件设计
    • 4.1主程序设计
    • 4.2 初始化及按键识别
    • 4.3按键调节时间模块
    • 4.4 LCD12864初始化模块
    • 4.5 DS1302时间模块
  • 五、软件测试
    • 5.1系统功能实现如下
      • 5.1.1初始界面图
      • 5.1.2按键设置时间功能
  • 七、附录
    • LCD_12864.c
    • Main.c
  • 个人设计一
  • 个人设计二

一、需求分析

1.1设计背景

随着人们生活水平的提高和生产力的不断发展,人们越来越注重时间管理和生活质量。电子日历作为一种时间管理工具,可以为人们提供便利和实用性。

传统的电子日历大多数以数字和英文的形式显示时间和日期,对于一些使用习惯不同的人群来说,使用不太方便。同时,传统电子日历的功能也相对单一,无法满足人们对于时间管理的多样化需求。

因此,为了满足人们对于时间管理的需求,可调式中文电子日历应运而生。相比传统电子日历,可调式中文电子日历不仅具有数字和英文的时间显示,还可以显示中文,更符合国人的使用习惯。同时,可调式中文电子日历还具有多种功能,如日期和时间的调节、闹钟提醒等,满足了人们对于时间管理的多样化需求。

可调式中文电子日历是为了更好地满足人们对于时间管理的需求,为人们提供方便、实用的时间管理工具,帮助人们更好地安排时间和提高生活质量。

1.2设计目的及意义

(1)设计目的

①掌握单片机控制知识,学习扩展外部设备的基本技能。

②通过设计学以致用,加强单片机的实践操作能力。

(2)设计意义

可调式中文电子日历的设计意义在于为人们提供一种实用、便捷的时间管理工具,帮助人们更好地安排时间和提高生活质量。

1. 提供多种功能:可调式中文电子日历不仅可以显示时间和日期,还可以调节日期和时间,设置闹钟提醒等多种功能,满足了人们对于时间管理的多样化需求。

2. 中文显示更符合国人使用习惯:传统电子日历大多数以数字和英文的形式显示时间和日期,对于一些使用习惯不同的人群来说,使用不太方便。可调式中文电子日历的中文显示更符合国人使用习惯,提高了使用的方便性。

3. 便于操作和调节:可调式中文电子日历操作简单,通过按键就可以实现日期和时间的调节,便于用户使用。

4. 美观实用:可调式中文电子日历的设计美观实用,不仅可以作为日常时间管理工具,还可以作为办公室、家庭等场所的装饰品,提高了空间的美观性。

5. 创新和发展:可调式中文电子日历的设计是对传统电子日历的创新和发展,不断满足人们对于时间管理工具的需求,促进了时间管理领域的发展。

1.3系统的总体概述

1.3.1目标

用DS1302与12864LCD设计的可调式中文电子日历的设计目标是为人们提供一种实用、便捷、美观的时间管理工具,满足人们对于时间管理的多样化需求。

1. 显示当前时间和日期:可调式中文电子日历需要能够准确地显示当前的时间和日期,包括年、月、日、时、分、秒等信息。

2. 中文显示:可调式中文电子日历需要支持中文显示,方便国人使用。

3. 可调节日期和时间:可调式中文电子日历需要能够通过按键等方式调节日期和时间,方便用户进行时间管理。

4. 设置闹钟提醒:可调式中文电子日历需要支持闹钟设置,方便用户设置提醒事件。

5. 美观实用:可调式中文电子日历的设计需要美观实用,不仅能够作为日常时间管理工具,还能够作为办公室、家庭等场所的装饰品。

6. 简单易用:可调式中文电子日历的设计需要简单易用,方便用户操作。

7. 低功耗:可调式中文电子日历需要具备低功耗的特性,以便长时间使用。

1.4系统模型

1.4.1主程序模块

用DS1302与12864LCD设计的可调式中文电子日历_第1张图片

图1-1主程序模块图

1.5功能需求

本系统包含基本功能和扩展功能:

基本功能:

本系统实现了日历时间信息的中文及数字显示功能,运行时会以PC时间为默认时间开始,运行过程中可以通过K1选择调节对象,所选中的调节对象会反相显示,K2,K3用于增减当前选中的被调节对象,K4用于保存所调节的值.

本系统自动将日期时间调节控制在合法范围内,星期调节会在调整,年月日时自动完成,闰年问题也能自动处理.

2.扩展功能:

(1) 通过Zimo软件提取笔段式数字点阵。在液晶屏上仿真数码管数字显示日期时间。

(2) 当前调节过程中无操作时间已达十秒且未”确认保存”, 系统自动恢复原时间继续运行。

(3) 为系统添加闹铃功能。

(4) 为系统添加农历。节气信息显示,必要时可更换更大尺寸的LCD。

1.6精度

1.6.1时间特性

DS1302是一款低功耗时钟芯片,具有高精度的时钟、日历和定时器功能,能够提供准确的时间和日期信息。

DS1302具有低功耗的特性,使得可调式中文电子日历能够长时间使用,不需要频繁更换电池。

1.6.2灵活性

用DS1302与12864LCD设计的可调式中文电子日历具有一定的灵活性,主要表现在以下几个方面:

1. 可调节性:可调式中文电子日历可以通过按键等方式调节日期和时间,方便用户进行时间管理,并可以满足不同用户的需求。

2. 多功能性:除了显示时间和日期外,可调式中文电子日历还具有多种功能,如设置闹钟提醒等,可以满足人们对于时间管理的多样化需求。

3. 显示屏幕大:12864LCD是一款128x64点阵液晶显示屏,能够显示文字、数字、图形等多种信息,显示效果清晰,方便用户查看时间和日期。

4. 中文显示:可调式中文电子日历支持中文显示,方便国人使用,提高了使用的便利性。

5. 美观实用:可调式中文电子日历的设计美观实用,不仅可以作为日常时间管理工具,还可以作为办公室、家庭等场所的装饰品,提高了空间的美观性。

6. 可定制化:可调式中文电子日历的设计可以根据用户的需求进行定制,如可以根据不同场合进行外观设计,或者增加其他功能模块等,提高了可调式中文电子日历的灵活性。

1.7环境

Keil uVision5:进行程序的编写及hex文件的生成;

proteus:进行硬件系统的搭建。

二、系统设计

2.1系统结构框架图

用DS1302与12864LCD设计的可调式中文电子日历_第2张图片

图2-1 系统架构框图

2.2 AT89C51性能简介

AT89C51具有如下特点:40个引脚,4K字节可编程FLASH片内程序存储器,128×8位随即存取数据存储器(RAM) ,32个外部双向输入/输出(I/O)口,5个中断源,两个16位可编程定时计数器,可编程串行通道,低功耗的闲置和掉电模式,片内振荡器和时钟电路。

用DS1302与12864LCD设计的可调式中文电子日历_第3张图片

图2-2 AT89C51芯片引脚图

2.3 AT89C51引脚功能说明

VCC:电源电压

GND:地

P0口:P0口是一组8位漏极开路型双向I/O口,也即地址/数据总线复用口,作为输出口用时,每位能驱动8个TTL逻辑门电路,对端口写“1”可作为高阻抗输入端口。在访问外部数据存储器或程序存储器时,这组口线分时转换地址(低8位)和数据总线复用,在访问期间激活内部上拉电阻。在Flash编程时,P0口接收指令字节,而在程序校验时,输出指令字节,校验时,要求外接上拉电阻。

P3口:P3口是一组带有内部上拉电阻的8位双向I/O口。P3口输出缓冲级可驱动(吸收或输出电流)4个TTL逻辑门电路。对P3口写入“1”时,它们被内部上拉电阻拉高并可作为输入端口。作输入端口时,被外部拉低的P3口将用上拉电阻输出电流I。P3口除了作为一般的I/O口线外,更重要的用途是它的第二功能,P3口的第二功能如下图2-3。

用DS1302与12864LCD设计的可调式中文电子日历_第4张图片

图2-3 p3口第二功能图

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

EA/VPP:外部访问允许。欲使CPU公访问外部程序存储器(地址0000H-FFFFH),EA端必须保持低电平(接地)。需注意的是:如果加密位LB1被编程,复位时内部会锁存EA端状态。如EA端为高电平(接VCC端),CPU则执行内部程序存储器中的指令。Flash存储器编程时,该引脚加上+12V的编程电压VPP。

XTAL1:振荡器反相放大器及内部时钟发生器的输入端。

XTAL2:振荡器反相放大器的输出端。

2.4 DS1302

DS1302是一款数字时钟芯片,由美国达拉斯半导体公司(Dallas Semiconductor)生产制造。它可以用于构建各种类型的电子时钟、计时器和日历等应用。DS1302内置了一个实时时钟(RTC)和一个电池备份电源,可以在主电源掉电的情况下继续运行。

DS1302具有低功耗、高精度、简单易用等特点,它采用串行接口进行通信,可以通过三根线(数据、时钟和控制)与微控制器进行连接。DS1302还具有多种时钟格式、闹钟功能、温度补偿等特性,非常适合用于各种电子产品的时钟和定时控制功能。

除了DS1302,达拉斯半导体公司还生产了许多其他型号的数字时钟芯片,如DS3231、DS1338等。这些芯片具有更高的精度、更多的特性和更强的抗干扰能力,可以满足更高要求的应用场景。

2.5 12864LCD

12864LCD是一种常见的图形液晶显示器,它可以显示128x64个像素的图形和文本信息。它通常用于各种嵌入式系统中,例如单片机、开发板等,作为用户界面的输出设备。

12864LCD的主要特点包括:

1. 显示效果好:12864LCD采用STN液晶技术,显示效果清晰、亮度高、可视角度广。

2. 支持多种显示模式:12864LCD支持多种显示模式,包括文本模式、图形模式、反白模式等。

3. 显示速度快:12864LCD采用并行接口和高速显示控制器,可以快速地刷新屏幕。

4. 硬件控制简单:12864LCD的硬件控制非常简单,只需要使用几个GPIO口即可完成数据传输和控制。

5. 低功耗:12864LCD的功耗很低,可以通过软件控制背光亮度和休眠模式来进一步降低功耗。

需要注意的是,12864LCD通常需要配合特定的驱动芯片使用,例如KS0108、ST7920等。这些驱动芯片负责控制屏幕的显示和刷新,通常通过并行或串行接口与12864LCD连接。因此,在使用12864LCD时需要注意选择合适的驱动芯片,并按照其相关的接口和控制信号进行连接和编程。

2.6系统方案设计

根据设计的基本要求,经过多方面的查证与对比,以充分发挥资源和提高系统性价比为原则,本系统采用AT89C51单片机为控制电路,使用LCD液晶显示屏来实现功能。采用以单片机为核心的控制方案,选用单片机作为系统的核心部件,实现控制与处理的功能。单片机具有资源丰富、速度快、编程容易等优点。利用单片机内部的随机存储器(RAM)和只读存储器(ROM)及其引脚资源,外接液晶显示(LCD),键盘输入等实现数据的处理传输和显示功能,具有较好的灵活性,基本上能实现设计指标。

2.7主要参数设计部分

本设计涉及的主要计算为键盘输入后系统解析为数字的计算。首先扫描按下按键在矩阵的位置然后通过程序计算将其转换为相应数字。

键盘扫描应用了反转法,通过两次更改AT89C51的I/O口电平,然后与按下按键后的电平相异或得到按键在矩阵的地址。通过switch语句将其变为相应数字。计算程序如下:

列扫描:switch(P1)

{

case 0x0E: Key = 0; break;

case 0x0D: Key = 1; break;

case 0x0B: Key = 2; break;

case 0x07: Key = 3; break;

default : Key = 0xFF; return -1;

}

行扫描: switch(P1)

{

case 0xE0: Key += 0; break;

case 0xD0: Key += 4; break;

case 0xB0: Key += 8; break;

case 0x70: Key += 12; break;

default : Key = 0xFF; return -1;

}

三、硬件设计

3.1电路设计图

用DS1302与12864LCD设计的可调式中文电子日历_第5张图片

图3-1 电路设计图

3.2主要单元电路设计

3.2.1 LCD12864技术资料

12864D使用KS0108(或其兼容芯片)作为控制器,适配M6800系列时序,具有8位标准数据总线。6条控制线及电源线可显示各种字符及图形。每个KS0108拥有64×64位(512字节)的显示RAM,12864D显示屏上的64×64点,显示RAM中的数据直接作为显示驱动信号。具有操作指令简单,低功耗的特点。

3.2.1.1 12864(KS0108)字符型LCD简介

12864D使用KS0108(或其兼容芯片)作为控制器,适配M6800系列时序,具有8位标准数据总线。6条控制线及电源线可显示各种字符及图形。每个KS0108拥有64×64位(512字节)的显示RAM,12864D显示屏上的64×64点,显示RAM中的数据直接作为显示驱动信号。具有操作指令简单,低功耗的特点。

3.2.1.2 引脚说明

表3-2-1 引脚说明

管脚号 管脚 电平 说明
1 CSA H/L 片选择信号,低电平时选择前64列。
2 CSB H 片选择信号,低电平时选择后64列。
3 GND 0V 逻辑电源地。
4 VCC 5V 逻辑电源。
5 VEE -10V LCD驱动电源。
6 D/I H/L 数据\指令选择,高电平:数据D0-D7将送入显示RAM; 低电平:数据D0-D7将送入指令寄存器执行。
7 R/W H/L 读\写选择,高电平:读数据;低电平:写数据。
8 E H.H/L 读写使能,高电平有效,下降沿锁定数据。
9-16 DB0 H/L 数据输入输出引脚。

3.2.1.3 12864LCD的指令说明

1、指令列表

表3-2-2 指令列表

用DS1302与12864LCD设计的可调式中文电子日历_第6张图片

2、指令功能详解

(1)读状态字(read status)

格式

BUSY 0 ON/OFF RESET 0 0 0 0

★ BUSY=1,表示KS0108正在处理计算机发来的指令或数据。此时接口电路被封锁,不能接受除读状态字以外的任何操作。BUSY=0表示KS0108接口控制电路已处于“准备好”状态,等待计算机的访问。

★ ON/OFF:表示当前的显示状态。ON/OFF=1表示关显示状态;ON/OFF=0表示开显示状态。

★ RESET表示当前KS0108的工作状态,即反映RST端的电平状态。当RST为低电平状态时KS0108处于复位工作状态,RESET=1。当RST为高电平状态时,KS0108为正常工作状态,RESET=0。

★ 在占领设置和数据读写时要注意状态字中的BUSY标志。只有在BUSY=0时,计算机对KS0108的操作才能有效。因此计算机在每次对KS0108操作之前,都要读出状态字判断BUSY是否为“0”。若不为“0”,则计算机需要等待,直至BUSY=0为止。

(2)显示开关(display on/off)

格式

0 0 1 1 1 1 1 D

该指令设置显示开关/触发器的状态,由此控制显示数据锁存器的工作方式,从而控制显示上的显示状态。

D位为显示开/关的控制位。当D=1为显示设置,显示数据锁存器正常工作,显示屏上呈现所许的效果。此时在状态字中ON/OFF=0。当D=0为关显示设置,显示数据锁存器被置零,显示屏呈不显示状态,但显示存储器并没有被破坏,在状态组中ON/OFF=1。

(3)显示起始行设置(Display start line)

格式

1 1 L5 L4 L3 L2 L1 L0

该指令设置了显示起始行寄存器的内容。KS0108有64行显示的管理能力,该指令中L5~L0为显示起始行的地址,取值在00~3FH(1~64)范围内,它规定了显示屏上最顶一行所对应的显示存储器的行地址。如果定时间隔地,等间距地修改(如加一或减一)显示起始行寄存器的内容,则显示屏将呈现显示内容向上或向下平滑滚动的显示效果。

(4)页面地址设置[Set page(X address)]

格式

1 0 1 1 1 P2 P1 P0

该指令设置了页面地址-X地址寄存器的内容。KS0108将显示存储器分成了8页,指令代码中P2~P0就是要确定当前所要选择的页面地址,取值范围为00~07H,代表第1~8页。该指令规定了以后的读/写操作将在哪一个页面上进行。执行本指令后,下面的读写操作将在指定页内,直到重新设置。页地址就是DDRAM 的行地址,页地址存储在X地址计数器中,P2-P0可表示8页,读写数据对页地址没有影响,除本指令可改变页地址外,复位信号(RST)可把页地址计数器内容清零。

表3-2-3 DDRAM地址映像表

Y1 Y2 Y3 Y4 ………… Y62 Y63 Y64
X=0 Line 0 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB0
Line 1 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB1
Line 2 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB2
Line 3 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB3
Line 4 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB4
Line 5 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB5
Line 6 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB6
Line 7 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB7
………… ………… …………
X=7 Line60 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB4
Line61 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB5
Line62 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB6
Line63 1/0 1/0 1/0 1/0 ………… 1/0 1/0 1/0 DB7

(5)列地址设置(Set Y address)

格式

0 1 C5 C4 C3 C2 C1 C0

该指令设置了Y地址计数器的内容,C5~C0=0~3FH(1~64)代表某一页面上的某一单元地址,随后的一次读或写数据将在这个单元上进行。Y地址计数器具有自动加一功能,在每一次读/写数据后它将自动加一,所以在连续进行读/写数据时,Y地址计数器不必每次都设置一次。页面地址的设置和列地址的设置将显示存储器单元唯一地确定下来,为后来的显示数据的读/写作了地址的选通。

(6)写显示数据(Write display data)

格式

数据

该操作将8位数据写入先前已确定的显示存储器的单元内,操作完成后列地址计数器自动加1。

(7)读显示数据(Read display data)

格式

数据

该操作将KS0108接口部的输出寄存器内容读出,然后列地址计数器自动加1。

3.2.2复位电路

由电容串联电阻构成,由图并结合“电容电压不能突变”的性质,可知当系统一上电,RST引脚将会出现高电平,且这个高电平持续时间由电路的RC值决定,典型的51单片机当RST引脚的高电平持续两个机器周期以上就复位。

用DS1302与12864LCD设计的可调式中文电子日历_第7张图片

图3-2 复位电路

3.2.3实时时钟芯片DS1302 简介

DS1302实时时钟/日历片带点滴式充电器及31字节RAM。DS1302的双电源引脚Vcc1Vcc2分别用于备用电源和主电源供电,保持数据和时钟信息时功耗小于 1mW。它可以对月、日、周、时、分、秒进行计时,具有补偿功能,最大有效份到2100年。

图3-3给出了DS1302 的内部结构与外部引脚图,DS1302与单片机通信时仅需要三条引线即复位线RST(Reset)串行时钟线 SCLK (Serial Clock)输入输出线I/0(数据线)。通过三条引线串行访问 DS1302的日期/时钟/配置/RAM等寄存器地址即可实现DS1302的所有操作。

用DS1302与12864LCD设计的可调式中文电子日历_第8张图片

图3-3 24C04存储器

四、软件设计

4.1主程序设计

本系统软件设计采用C51,使用keil C51编译生成.HEX文件,Proteus设计原理图,并将.HEX文件加入运行实现功能。

本系统是针对DS1302芯片和LCD12864进行程序的设计,而DS1302的驱动程序在上个项目中我们已经作了详细介绍,在此不再赘述。我们主要编制LCD12864的驱动程序。根据对LCD12864资料的分析,程序编制主要有:读取LCD的状态,忙检查,向LCD写入命令,向LCD写入数据,LCD初始化,设置LCD液晶的显示位置,显示字符等。

本系统包含基本功能和扩展功能:

  1. 基本功能:

实现了日历时间信息的中文及数字显示功能,在运行过程中,按下K1按键可选择调节对象,被选中的调节对象将反相显示,K2、K3键用于增减前选中的被节对象,K4 用于保存所调节的值。

2.扩展功能:

(1) 通过Zimo软件提取笔段式数字点阵。在液晶屏上仿真数码管数字显示日期时间。

(2) 为系统添加闹铃功能。

4.2 初始化及按键识别

系统初始化包括LCD的初始化、显示初始化以及中断初始化工作。有2个中断服务子程序:一个用来刷新显示,一个用来处理4个按键。

  1. LCD12864显示驱动程序

(1)检查LCD是否忙

bit LCD_Check_Busy()
{
LCD_DB_PORT=0xFF;
RW=1;
\_nop_();
DI=0;
E=1;
\_nop_();
E=0;
return (bit)(P0&0x80);
}

(2)向LCD写入一个字节(一般用于发送命令)

void LCD_Write_Command( INT8U c)
{
while(LCD_Check_Busy());
LCD_DB_PORT=0xFF;
RW=0;
\_nop_();
DI=0;
LCD_DB_PORT=c;
E=1;
\_nop_();
E=0;
}

(3)向LCD写入数据

void LCD_Write_Data(INT8U d)
{
while(LCD_Check_Busy());
LCD_DB_PORT=0xFF;
RW=0;
\_nop_();
DI=1;
if(!Reverse_Display) //??Reverse_Display????????
LCD_DB_PORT=d;
else
LCD_DB_PORT=\~d;
E=1;
\_nop_();
E=0;
}

(4)初始化LCD

void LCD_Initialize()
{
CS1=1;
CS2=1;
LCD_Write_Command(0x38);
LCD_Write_Command(0x0F);
LCD_Write_Command(0x01);
LCD_Write_Command(0x06);
LCD_Write_Command(LCD_START_ROW);
}

(5)通用显示函数 从第P页第L列显示W个字节数据,具体显示的数据在r所指的数组中

void Common_Show(INT8U P,INT8U L,INT8U W,INT8U \*r) reentrant
{
INT8U i;
if(L\<64)
{
CS1=1;
CS2=0;
LCD_Write_Command(LCD_PAGE+P);
LCD_Write_Command(LCD_COL+L);
if(L+W\<64)
{
for(i=0;i\

(6) 显示一个8×16点阵字符

void Disp_A_Char(INT8U P,INT8U L,INT8U \*M) reentrant
{
Common_Show(P,L,8,M);
Common_Show(P+1,L,8,M+8);
}

(7) 显示一个16×16点阵字符 (汉字上半部分与下半部分分别处在相邻两页中)

void Disp_A_Word(INT8U P,INT8U L,INT8U \*M) reentrant
{
Common_Show(P,L,16,M);
Common_Show(P+1,L,16,M+16);
}
  1. 主程序

主程序较简单,有初始化子程序LCD_Initialize(),其中包括LCD的初始化、显示初始化以及中断初始化工作,DS1302时钟程序、字符汉字显示程序、2个中断服务子程序:一个用来刷新显示,一个用来处理4个按键。

其中字符汉字显示程序我们主要定义了3个数组,分别为:

(1)年、月、日、星期、时、分、秒等汉字点阵(16×16) 数组INT8U code DATETIME_WORDS[];

(2)一、二、三、四、五、六和天等汉字点阵(16×16) INT8U code WEEK_WORDS[];

(3)0-9等数字点阵(8×16) INT8U code DIGITS[];

各个汉字数字的具体字模可用专门的取字模软件得到,在文后的完整程序代码中有它们的具体字模数组。

用DS1302与12864LCD设计的可调式中文电子日历_第9张图片

图4-1 初始化及按键识别

按键识别程序如下:

//-----------------------------------------------------------------
// 键盘中断(INT0)
//-----------------------------------------------------------------
void EX_INT0() interrupt 0
{
if (K1 == 0)
{
if (Adjust_Index == -1 \|\| Adjust_Index == 0) Adjust_Index = 7;
if (--Adjust_Index == 5) Adjust_Index = 4;
}
else if (K2 == 0) DateTime_Adjust(1);
else if (K3 == 0) DateTime_Adjust(-1);
else if (K4 == 0)
{
SET_DS1302();
Adjust_Index = -1;
}
}

4.3按键调节时间模块

首先LCD初始化,首次点击K1键选择年份后两位,第二次选择月份的两位,第三次选择日期的天数,同理依次选择时,分,秒。K2、K3按下后先检查条件是否小于上限、大于上限,之后再分别进行加减操作,在此时间内,若有K4确认键按下,则立刻将此时LCD显示的时间设置为当前的时间。

用DS1302与12864LCD设计的可调式中文电子日历_第10张图片

图4-2 按键模块

用DS1302与12864LCD设计的可调式中文电子日历_第11张图片

4-3 按键子程序模块

按键模块代码如下:

//-----------------------------------------------------------------
// 年月日时分秒递增/减调节(星期自动计算,不允许直接调节)
//-----------------------------------------------------------------
void DateTime_Adjust(char x)
{
INT8U d;
switch (Adjust_Index)
{
case 6:
if (x == 1 && DateTime[6] \< 99)
DateTime[6]++;
if (x == -1 && DateTime[6] \> 0)
DateTime[6]--;
if (DateTime[4] == 2 && DateTime[3] == 29)DateTime[3] = 28;
RefreshWeekDay();
break;
case 4:
if (x == 1 && DateTime[4] \< 12)
DateTime[4]++;
if (x == -1 && DateTime[4] \> 1)
DateTime[4]--;
if (DateTime[4] == 2 && DateTime[3] == 29)
DateTime[3] = 28;
RefreshWeekDay();
break;
case 3:
d = M_Days[DateTime[4]];
if (DateTime[4] == 2 && isLeapYear(2000 + DateTime[6]))
d = 29;
if (x == 1 && DateTime[3] \< d)
DateTime[3]++;
if (x == -1 && DateTime[3] \> 1)
DateTime[3]--;
RefreshWeekDay();
break;
case 2:
if (x == 1 && DateTime[2] \< 23)
DateTime[2]++;
if (x == -1 && DateTime[2] \> 0)
DateTime[2]--;
break;
case 1:
if (x == 1 && DateTime[1] \< 59)
DateTime[1]++;
if (x == -1 && DateTime[1] \> 0)
DateTime[1]--;
break;
case 0:
if (x == 1 && DateTime[1] \< 59)
DateTime[0]++;
if (x == -1 && DateTime[1] \> 0)
DateTime[0]--;
break;
}
}

4.4 LCD12864初始化模块

LCD12864初始化模块在每次更新显示内容时都会被调用。流程图如下:

用DS1302与12864LCD设计的可调式中文电子日历_第12张图片

图4-4 LCD12864初始化模块

LCD初始化:
//-----------------------------------------------------------------
// 初始化LCD
//-----------------------------------------------------------------
void LCD_Initialize()
{
  CS1=1;
  CS2=1;
  LCD_Write_Command(0x38);
  LCD_Write_Command(0x0F);
  LCD_Write_Command(0x01);
  LCD_Write_Command(0x06);
  LCD_Write_Command(LCD_START_ROW);
}

4.5 DS1302时间模块

用DS1302与12864LCD设计的可调式中文电子日历_第13张图片

图4-5 DS1302时间模块

(1)// 向DS1302写入一字节

//-----------------------------------------------------------------
void Write_one_byte(INT8U x)
{ 
	INT8U i;
	for (i = 0; i < 8; i++) {
		SDA = x & 0x01;
		CLK = 1;
		CLK = 0;
		x >>= 1;
	}
}

(2)// 从DS1302读取一字节

//-----------------------------------------------------------------
INT8U Get_one_byte()
{ 
	INT8U i, b, t;
	for (i = 0; i < 8; i++) {
		b >>= 1;
		t = SDA;
		b |= t << 7;
		CLK = 1;
		CLK = 0;
	}
	return b / 16 * 10 + b % 16;  //??????BCD?
}

(3)// 从DS1302指定位置读数据

//-----------------------------------------------------------------
INT8U Read_Data(INT8U addr)
{
	INT8U dat; RST = 0;
	CLK = 0; RST = 1;
	Write_one_byte(addr);
	dat = Get_one_byte();
	CLK = 1; RST = 0;
	return dat;
}

(4)// 向DS1302某地址写入数据

//-----------------------------------------------------------------
void Write_DS1302(INT8U addr,INT8U dat)
{
	CLK = 0; RST = 1;
	Write_one_byte(addr);
	Write_one_byte(dat);
	CLK = 0; RST = 0;
}

(5)// 设置时间

//-----------------------------------------------------------------
void SET_DS1302() 
{
	INT8U i;
	Write_DS1302(0x8E, 0x00);  
	for (i = 0; i < 7; i++) {
		Write_DS1302(0x80 + 2 * i, (DateTime[i] / 10 << 4) | (DateTime[i] % 10));
	}
	Write_DS1302(0x8E, 0x80);  
}

(6)// 读取当前日期时间

//-----------------------------------------------------------------
void GetTime() 
{
	INT8U i;
	for (i = 0; i < 7; i++)DateTime[i] = Read_Data(0x81 + 2 * i);

}

五、软件测试

5.1系统功能实现如下

5.1.1初始界面图

如下图5-1

用DS1302与12864LCD设计的可调式中文电子日历_第14张图片

图5-1 系统初始界面图

5.1.2按键设置时间功能

点击K1键如图5-2所示;按下K1后,液晶屏上首先反相显示年份后两位表示选中该数字,可以进行加减操作,点击K2进行加操作,2023变成了2024,如图5-3所示;点击K3进行减操作,2024变回了2023,如图5-4所示;再次点击K1,月份数字反相显示,表示已选中,如同5-5所示;点击K4键表示确认,即将该时间写入DS1302,如图5-6所示。

用DS1302与12864LCD设计的可调式中文电子日历_第15张图片

图5-2 首次点击K1

用DS1302与12864LCD设计的可调式中文电子日历_第16张图片

图5-3 点击K2加操作

用DS1302与12864LCD设计的可调式中文电子日历_第17张图片

图5-4 点击K3减操作

用DS1302与12864LCD设计的可调式中文电子日历_第18张图片

图5-5 再次点击K1切换调节对象

用DS1302与12864LCD设计的可调式中文电子日历_第19张图片

图5-5 点击K4确认

六、实验心得

我们成功地使用C51单片机实现了基于DS1302和12864LCD的可调式中文电子日历。在这个过程中遇到了一些挑战,但通过学习、实践和不断改进,我们已经尽力完成了这个项目。

1. C51单片机的选择:C51系列是一款功能强大、性价比高的8位单片机。通过使用C51单片机,可以更轻松地控制DS1302实时时钟和12864LCD显示器。

2. DS1302实时时钟的应用:DS1302是一款常用的实时时钟芯片,它可以提供精确的时间和日期信息。在这个项目中,学会了如何通过SPI接口与C51单片机通信,以及如何设置和读取DS1302的时间和日期数据。

3. 12864LCD显示器的驱动:12864LCD是一款图形点阵液晶显示器,拥有128x64个点阵,可以显示中文字符和图形。学会了如何通过并行接口驱动12864LCD,以及如何使用字库显示中文字符。

4. 中文字符显示的处理:在这个项目中,我们遇到了显示中文字符的挑战。通过使用GB2312编码的字库,可以把汉字转换为点阵数据,然后在12864LCD上显示出来。这个过程涉及到编码转换、字库查找和点阵数据的显示。

5. 可调式日历的实现:为了让用户能够方便地调整时间和日期,我们设计了一个简单的按键操作系统。通过监听按键事件,可以实现对DS1302的时间和日期数据进行设置,从而实现可调式日历功能。

6. 硬件与软件的协同:在这个项目中,不仅需要学习并掌握C51单片机、DS1302和12864LCD的硬件知识,还需要编写相应的软件代码。这要求你具备较强的硬件和软件设计能力,以实现各个模块之间的协同工作。

7.调试与优化:在整个项目过程中,我们遇到了各种问题。通过分析问题、查找资料和请教他人,我们逐步解决了这些问题,使得电子日历系统能够正常运行。在这个过程中,我们对项目进行了多次调试和优化,提高了系统的稳定性和可靠性。

总之,完成这个基于C51单片机的可调式中文电子日历项目,对我们两个人的硬件设计、软件编程和问题解决能力都有很大的提升。希望我们之后可以继续探索更多的项目,进一步提高自己的技能。

其中,在解决扩展功能时,在扩展1中,学会了使用字模软件来设置笔段式字体,字模的宽度必须是8的倍数;在扩展2中,在实现“按下K1后,10s无操作后时间恢复”这一功能时,利用定时器中断实现,但是中断了后,一开始出现面板锁定后无法按键的情况,经调整问题解决,但是又出现了一旦进行按键就无法再次计数的情况,通过设置清空计数标志,一旦按键,之前的计数清空,才使得“10s无操作恢复时间”后,可以进行别的操作;在扩展3中,碰到了字模大小不一样的情况,需要重新写一个函数,可以显示小一号字模,并且需要在LCD屏上显示到合适的位置,进行了很多次调试才合适,闹钟的实现用到了蜂鸣器,进行了硬件的连接和代码的编写;扩展4由于时间原因未能实现。

七、附录

LCD_12864.c

//-----------------------------------------------------------------
//  名称:12864LCD显示驱动程序 (不带字库)
//-----------------------------------------------------------------
#include 
#include 
#define INT8U	unsigned char
#define INT16U	unsigned int
#define	LCD_DB_PORT		P0		//液晶DB0-DB7
#define LCD_START_ROW	0xC0	//起始行
#define LCD_PAGE		0xB8	//页指令
#define LCD_COL			0x40	//列指令
//液晶引脚定义
sbit	DI  = P2^0 ;
sbit	RW  = P2^1 ;
sbit	E   = P2^2 ;
sbit	CS1 = P2^3 ;
sbit	CS2	= P2^4 ;
sbit	RST = P2^5 ;
//是否反相显示(白底黑字/黑底白字)
bit Reverse_Display = 0;
//-----------------------------------------------------------------
// 检查LCD是否忙
//-----------------------------------------------------------------
bit LCD_Check_Busy()
{
  LCD_DB_PORT=0xFF;
	RW=1;
	_nop_();
	DI=0;
	E=1;
	_nop_();
	E=0;
	return (bit)(P0&0x80);
}

//-----------------------------------------------------------------
// 向LCD发送命令
//-----------------------------------------------------------------
void LCD_Write_Command( INT8U c)
{
	while(LCD_Check_Busy());  
  LCD_DB_PORT=0xFF;
  RW=0;
  _nop_();
  DI=0;
  LCD_DB_PORT=c;
  E=1;
  _nop_();
  E=0;
}
//-----------------------------------------------------------------
// 向LCD发送数据
//-----------------------------------------------------------------
void LCD_Write_Data(INT8U d)
{
   while(LCD_Check_Busy());  
   LCD_DB_PORT=0xFF;
   RW=0;
   _nop_();
   DI=1;
   if(!Reverse_Display)	   //??Reverse_Display????????
		LCD_DB_PORT=d;
   else 
		LCD_DB_PORT=~d;
   E=1;
   _nop_();
   E=0;
}

//-----------------------------------------------------------------
// 初始化LCD
//-----------------------------------------------------------------
void LCD_Initialize()
{
  CS1=1;
  CS2=1;
  LCD_Write_Command(0x38);
  LCD_Write_Command(0x0F);
  LCD_Write_Command(0x01);
  LCD_Write_Command(0x06);
  LCD_Write_Command(LCD_START_ROW);
}
//-----------------------------------------------------------------
//
// 通用显示函数
// 
// 从第P页第L列开始显示W个字节数据,数据在r所指向的缓冲
// 每字节8位是垂直显示的,高位在下,低位在上
// 每个8*128的矩形区域为一页
// 整个LCD又由64x64的左半屏和64x64的右半屏构成
//-----------------------------------------------------------------
void Common_Show(INT8U P,INT8U L,INT8U W,INT8U *r) reentrant
{
  INT8U i;
	if(L<64)
		{
			CS1=1;
	    CS2=0;
			LCD_Write_Command(LCD_PAGE+P);
			LCD_Write_Command(LCD_COL+L);
			if(L+W<64)
				{
					for(i=0;i

Main.c

//-----------------------------------------------------------------
//名称: 用DS1302与12864LCD设计的可调式电子日历与时钟
//-----------------------------------------------------------------
//说明: 本例运行时会以PC时间为默认时间开始,运行过程中可以通过K1
//		选择调节对象,所选中的调节对象会反色显示,K2,K3进行加减调节,
//		K4保存.
//		本例自动将日期时间调节控制在合法范围内,星期调节会在调整
//		年月日时自动完成,闰年问题也能自动处理. 
//								
//-----------------------------------------------------------------
#include 
#include 
#include 
#define INT8U	unsigned char
#define INT16U	unsigned int
extern void LCD_Initialize();
extern void Disp_A_Char(INT8U P,INT8U L,INT8U *M) reentrant;
extern void Disp_A_Word(INT8U P,INT8U L,INT8U *M) reentrant;
//在调节日期时间时,用该位决定是否反白显示
extern bit Reverse_Display ;
sbit SDA = P1^0;		//DS1302数据线
sbit CLK = P1^1;		//DS1302时钟线
sbit RST = P1^2;		//DS1302复位线
sbit K1 = P3^4;			//选择 
sbit K2 = P3^5;			//加
sbit K3 = P3^6;			//减
sbit K4 = P3^7;			//确定
sbit beep = P1^3;

INT8U tCount = 0;
//一年中每个月的天数,2月的天数是否再加1由年份决定
code INT8U M_Days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
INT8U DateTime[7];			//所读取的日期时间
//当前调节的时间对象:秒,分,时,日,月,年(0,1,2,3,4,6)
//5对应星期,星期调节由年月日调节自动完成
char Adjust_Index = -1;
INT8U H_Offset = 10, V_P_Offset = 0;	//水平页偏移与垂直像素偏移
//"年、月、日、星、期、时、分、秒"的汉字点阵(16x16)-----------------------
INT8U code DATETIME_WORDS[] = 
{
/*---------------年----------------*/
0x40,0x20,0x10,0x0C,0xE3,0x22,0x22,0x22,0xFE,0x22,0x22,0x22,0x22,0x02,0x00,0x00,
0x04,0x04,0x04,0x04,0x07,0x04,0x04,0x04,0xFF,0x04,0x04,0x04,0x04,0x04,0x04,0x00,
/*---------------月----------------*/
0x00,0x00,0x00,0x00,0x00,0xFF,0x11,0x11,0x11,0x11,0x11,0xFF,0x00,0x00,0x00,0x00,
0x00,0x40,0x20,0x10,0x0C,0x03,0x01,0x01,0x01,0x21,0x41,0x3F,0x00,0x00,0x00,0x00,
/*---------------日----------------*/
0x00,0x00,0x00,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0xFE,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x3F,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x3F,0x00,0x00,0x00,0x00,
/*---------------星----------------*/
0x00,0x00,0x00,0xBE,0x2A,0x2A,0x2A,0xEA,0x2A,0x2A,0x2A,0x2A,0x3E,0x00,0x00,0x00,
0x00,0x48,0x46,0x41,0x49,0x49,0x49,0x7F,0x49,0x49,0x49,0x49,0x49,0x41,0x40,0x00,
/*---------------期----------------*/
0x00,0x04,0xFF,0x54,0x54,0x54,0xFF,0x04,0x00,0xFE,0x22,0x22,0x22,0xFE,0x00,0x00,
0x42,0x22,0x1B,0x02,0x02,0x0A,0x33,0x62,0x18,0x07,0x02,0x22,0x42,0x3F,0x00,0x00,
/*---------------时----------------*/
0x00,0xFC,0x44,0x44,0x44,0xFC,0x10,0x90,0x10,0x10,0x10,0xFF,0x10,0x10,0x10,0x00,
0x00,0x07,0x04,0x04,0x04,0x07,0x00,0x00,0x03,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,
/*---------------分----------------*/
0x80,0x40,0x20,0x98,0x87,0x82,0x80,0x80,0x83,0x84,0x98,0x30,0x60,0xC0,0x40,0x00,
0x00,0x80,0x40,0x20,0x10,0x0F,0x00,0x00,0x20,0x40,0x3F,0x00,0x00,0x00,0x00,0x00,
/*---------------秒----------------*/
0x12,0x12,0xD2,0xFE,0x91,0x11,0xC0,0x38,0x10,0x00,0xFF,0x00,0x08,0x10,0x60,0x00,
0x04,0x03,0x00,0xFF,0x00,0x83,0x80,0x40,0x40,0x20,0x23,0x10,0x08,0x04,0x03,0x00
};
//星期"日、一、二、三、四、五、六"的汉字点阵(16x16)-----------------------
INT8U code WEEK_WORDS[] = 
{
/*---------------日----------------*/
0x00,0x00,0x00,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0xFE,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x3F,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x3F,0x00,0x00,0x00,0x00,
/*---------------一----------------*/
0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xC0,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*---------------二----------------*/
0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x06,0x04,0x00,0x00,0x00,
0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x18,0x10,0x00,
/*---------------三----------------*/
0x00,0x04,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x04,0x00,0x00,
0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
/*---------------四----------------*/
0x00,0xFE,0x02,0x02,0x02,0xFE,0x02,0x02,0xFE,0x02,0x02,0x02,0x02,0xFE,0x00,0x00,
0x00,0x7F,0x28,0x24,0x23,0x20,0x20,0x20,0x21,0x22,0x22,0x22,0x22,0x7F,0x00,0x00,
/*---------------五----------------*/
0x00,0x02,0x82,0x82,0x82,0x82,0xFE,0x82,0x82,0x82,0xC2,0x82,0x02,0x00,0x00,0x00,
0x20,0x20,0x20,0x20,0x20,0x3F,0x20,0x20,0x20,0x20,0x3F,0x20,0x20,0x30,0x20,0x00,
/*---------------六----------------*/
0x10,0x10,0x10,0x10,0x10,0x91,0x12,0x1E,0x94,0x10,0x10,0x10,0x10,0x10,0x10,0x00,
0x00,0x40,0x20,0x10,0x0C,0x03,0x01,0x00,0x00,0x01,0x02,0x0C,0x78,0x30,0x00,0x00
};

//半角数字"0~9"的点阵(8x16)------------------------------------------
INT8U code DIGITS[] = 
{
/*---------------0----------------*/
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,
/*---------------1----------------*/
0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,
/*---------------2----------------*/
0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,
/*---------------3----------------*/
0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,
/*---------------4----------------*/
0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,
/*---------------5----------------*/
0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,
/*---------------6----------------*/
0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,
/*---------------7----------------*/
0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,
/*---------------8----------------*/
0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,
/*---------------9----------------*/
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00
};

//-----------------------------------------------------------------
// 向DS1302写入一字节
//-----------------------------------------------------------------
void Write_one_byte(INT8U x)
{ 
	INT8U i;
	for (i = 0; i < 8; i++) {
		SDA = x & 0x01;
		CLK = 1;
		CLK = 0;
		x >>= 1;
	}
}

//-----------------------------------------------------------------
// 从DS1302读取一字节
//-----------------------------------------------------------------
INT8U Get_one_byte()
{ 
	INT8U i, b, t;
	for (i = 0; i < 8; i++) {
		b >>= 1;
		t = SDA;
		b |= t << 7;
		CLK = 1;
		CLK = 0;
	}
	return b / 16 * 10 + b % 16; 
}

//-----------------------------------------------------------------
// 从DS1302指定位置读数据
//-----------------------------------------------------------------
INT8U Read_Data(INT8U addr)
{
	INT8U dat; RST = 0;
	CLK = 0; RST = 1;
	Write_one_byte(addr);
	dat = Get_one_byte();
	CLK = 1; RST = 0;
	return dat;
}

//-----------------------------------------------------------------
// 向DS1302某地址写入数据
//-----------------------------------------------------------------
void Write_DS1302(INT8U addr,INT8U dat)
{
	CLK = 0; RST = 1;
	Write_one_byte(addr);
	Write_one_byte(dat);
	CLK = 0; RST = 0;
}

//-----------------------------------------------------------------
// 设置时间
//-----------------------------------------------------------------
void SET_DS1302() 
{
	INT8U i;
	Write_DS1302(0x8E, 0x00); 
	for (i = 0; i < 7; i++) {
		Write_DS1302(0x80 + 2 * i, (DateTime[i] / 10 << 4) | (DateTime[i] % 10));
	}
	Write_DS1302(0x8E, 0x80); 

}

//-----------------------------------------------------------------
// 读取当前日期时间
//-----------------------------------------------------------------
void GetTime() 
{
	INT8U i;
	for (i = 0; i < 7; i++)DateTime[i] = Read_Data(0x81 + 2 * i);
}
//-----------------------------------------------------------------
// 判断是否为闰年
//-----------------------------------------------------------------
INT8U isLeapYear(INT16U y)
{
	return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
//-----------------------------------------------------------------
// 求自2000.1.1开始的任何一天是星期几
// 函数没有通过求出总天数后再求星期几,
// 是因为求总天数可能会越出INT16U的范围
//-----------------------------------------------------------------
void RefreshWeekDay()
{ 
	INT16U y, i, d, w = 5; 
	y = 2000 + DateTime[6];
	for (i = 2000; i < y; i++) {
		d = isLeapYear(i) ? 366 : 365;
		w = (w + d) % 7;
	}
	d = 0;
	for (i = 1; i < DateTime[4]; i++)
	{
		d += M_Days[3];
		if (i == 2 && isLeapYear(y)) d++; 
	}
	d += DateTime[3];
	DateTime[5] = (w + d) % 7 + 1; 
}
//-----------------------------------------------------------------
// 年月日时分秒递增/减调节(星期自动计算,不允许直接调节)
//-----------------------------------------------------------------
void DateTime_Adjust(char x)
{ 
	INT8U d;
	switch (Adjust_Index)
	{
	case 6:
		if (x == 1 && DateTime[6] < 99)
			DateTime[6]++;
		if (x == -1 && DateTime[6] > 0)
			DateTime[6]--;
		if (DateTime[4] == 2 && DateTime[3] == 29)DateTime[3] = 28;
		RefreshWeekDay(); 
		break;
	case 4:
		if (x == 1 && DateTime[4] < 12)
			DateTime[4]++;
		if (x == -1 && DateTime[4] > 1)
			DateTime[4]--;
		if (DateTime[4] == 2 && DateTime[3] == 29)
			DateTime[3] = 28;
		RefreshWeekDay();
		break;
	case 3:
		d = M_Days[DateTime[4]]; 
		if (DateTime[4] == 2 && isLeapYear(2000 + DateTime[6])) 
			d = 29;
		if (x == 1 && DateTime[3] < d)
			DateTime[3]++;
		if (x == -1 && DateTime[3] > 1)
			DateTime[3]--;
		RefreshWeekDay();
		break;
	case 2:
		if (x == 1 && DateTime[2] < 23)
			DateTime[2]++;
		if (x == -1 && DateTime[2] > 0)
			DateTime[2]--;
		break;
	case 1:
		if (x == 1 && DateTime[1] < 59)
			DateTime[1]++;
		if (x == -1 && DateTime[1] > 0)
			DateTime[1]--;
		break;
	case 0:
		if (x == 1 && DateTime[1] < 59)
			DateTime[0]++;
		if (x == -1 && DateTime[1] > 0)
			DateTime[0]--;
		break;
	}	
}
//-----------------------------------------------------------------	
// 定时器0每秒刷新LCD显示	 
//-----------------------------------------------------------------
void T0_INT() interrupt 1 
{
	TH0 = -50000 >> 8; TL0 = -50000 & 0xFF; if (++tCount != 2) return;
	tCount = 0;
	Reverse_Display = Adjust_Index == 6;
	Disp_A_Char(V_P_Offset, 16 + H_Offset, DIGITS + DateTime[6] / 10 * 16);
	Disp_A_Char(V_P_Offset, 24 + H_Offset, DIGITS + DateTime[6] % 10 * 16);
	Reverse_Display = Adjust_Index == 4;
	Disp_A_Char(V_P_Offset, 48 + H_Offset, DIGITS + DateTime[4] / 10 * 16);
	Disp_A_Char(V_P_Offset, 56 + H_Offset, DIGITS + DateTime[4] % 10 * 16);
	Reverse_Display = Adjust_Index == 3;
	Disp_A_Char(V_P_Offset, 80 + H_Offset, DIGITS + DateTime[3] / 10 * 16);
	Disp_A_Char(V_P_Offset, 88 + H_Offset, DIGITS + DateTime[3] % 10 * 16);
	Reverse_Display = 0;
	Disp_A_Word(V_P_Offset+2, 96 + H_Offset, WEEK_WORDS+(DateTime[5]-1)*32);
	//时
	Reverse_Display = Adjust_Index == 2;
	Disp_A_Char(V_P_Offset+5,16+ H_Offset, DIGITS + DateTime[2] / 10 * 16);
	Disp_A_Char(V_P_Offset+5, 24 + H_Offset, DIGITS + DateTime[2] % 10 * 16);
	//分
	Reverse_Display = Adjust_Index == 1;
	Disp_A_Char(V_P_Offset+5, 48 + H_Offset, DIGITS + DateTime[1] / 10 * 16);
	Disp_A_Char(V_P_Offset+5, 56 + H_Offset, DIGITS + DateTime[1] % 10 * 16);
	//秒
	Reverse_Display = Adjust_Index == 0;
	Disp_A_Char(V_P_Offset+5, 80 + H_Offset, DIGITS + DateTime[0] / 10 * 16);
	Disp_A_Char(V_P_Offset+5, 88 + H_Offset, DIGITS + DateTime[0] % 10 * 16);
}

//-----------------------------------------------------------------
// 键盘中断(INT0)
//-----------------------------------------------------------------
void EX_INT0() interrupt 0 
{
	if (K1 == 0)
	{
		if (Adjust_Index == -1 || Adjust_Index == 0) Adjust_Index = 7;
		if (--Adjust_Index == 5) Adjust_Index = 4;
	}
	else if (K2 == 0) DateTime_Adjust(1); 
	else if (K3 == 0) DateTime_Adjust(-1); 
	else if (K4 == 0)
	{
		SET_DS1302(); 
		Adjust_Index = -1;
	}
}
//-----------------------------------------------------------------
// 主程序	 
//-----------------------------------------------------------------
void main()
{
	beep = 0;
	LCD_Initialize(); //?????
	//?????????
	Disp_A_Char(V_P_Offset, 0 + H_Offset, DIGITS + 2 * 16);
	Disp_A_Char(V_P_Offset, 8 + H_Offset, DIGITS);
	//--------------------------------------------------------------------
	//--------------------------------------------------------------------
	Disp_A_Word(V_P_Offset, 32 + H_Offset, DATETIME_WORDS + 0 * 32);
	Disp_A_Word(V_P_Offset, 64 + H_Offset, DATETIME_WORDS + 1 * 32);
	Disp_A_Word(V_P_Offset, 96 + H_Offset, DATETIME_WORDS + 2 * 32);
	Disp_A_Word(V_P_Offset + 2, 64 + H_Offset, DATETIME_WORDS + 3 * 32);
	Disp_A_Word(V_P_Offset + 2, 80 + H_Offset, DATETIME_WORDS + 4 * 32);
	Disp_A_Word(V_P_Offset + 5, 32 + H_Offset, DATETIME_WORDS + 5 * 32);
	Disp_A_Word(V_P_Offset + 5, 64 + H_Offset, DATETIME_WORDS + 6 * 32);
	Disp_A_Word(V_P_Offset + 5, 96 + H_Offset, DATETIME_WORDS + 7 * 32);
	IE = 0x83; IP = 0x01; IT0 = 0x01;
	TH0 = -50000 >> 8; TL0 = -50000 & 0xFF; TR0 = 1;
	while (1)
	{
		if (Adjust_Index == -1) GetTime();
	}
}          

个人设计一

  1. 扩展功能总体说明
    1.1 总体说明
    我完成的扩展功能是为系统添加闹铃功能,以下是对于该项扩展功能的总体说明:
    该系统启动后页面有添加显示set clock小字显示内容,其后紧跟闹铃的时分数值,set clock小字的大小为16x16,时分数值大小为6x8,通过点击K1,便可以对闹铃进行设置,其余加减、确定功能同基本功能一样,为了闹铃到了时间进行响铃,在硬件方面添加了一个蜂鸣器,连接P1_3口,当时间到了之后,蜂鸣器响延续一小段时间。
    1.2 整体改进
    如下图是我对整个电路图即显示屏显示的整体改进:

用DS1302与12864LCD设计的可调式中文电子日历_第20张图片
图1-1 改进前的电路图即LCD显示
用DS1302与12864LCD设计的可调式中文电子日历_第21张图片

图1-2 改进后的电路图及LCD显示

二、硬件设计
2.1 蜂鸣器模块
2.1.1硬件连接图
为系统添加蜂鸣器进行到时响铃
用DS1302与12864LCD设计的可调式中文电子日历_第22张图片

图2-1蜂鸣器接线图
2.1.2 蜂鸣器工作原理
BUZZER,电磁式蜂鸣器: 由振荡器电磁线圈、磁铁、振动膜片及外壳等组成,详情如图。
通过在某一接脚接高电压使线圈产生电流从而产生电磁场。产生磁场后将振动膜片吸附到中间的铁柱上,然后在此接脚接低电压,电流断开,磁场消失,振动膜片回弹。有振荡器的蜂鸣器为有源蜂鸣器,它可以直接将输入的高电压转换为一定频率的方波。无源蜂鸣器则没有振荡器需自己设计频率。
用DS1302与12864LCD设计的可调式中文电子日历_第23张图片

图2-2 蜂鸣器原理图
2.1.3 蜂鸣器的用法
三极管做蜂鸣器的开关应用:
首先不接三级管是不可以的,虽然单片机能直接写1,在输出口输出5V的电压,但是因为P1的I/O口内接上拉电阻,上拉电阻的电压比较大的,所以单片机的输出电流是不足以驱动蜂鸣器的。
用DS1302与12864LCD设计的可调式中文电子日历_第24张图片

图2-3 三级管原理图
如图:当IB>1mA时,E到C接通。
用DS1302与12864LCD设计的可调式中文电子日历_第25张图片

图2-4 蜂鸣器应用图
三、软件设计
3.1 LCD12864 添加显示闹铃设置画面
用DS1302与12864LCD设计的可调式中文电子日历_第26张图片

图3-1 字模排列
3.2 取“set clock”字模
0x12,0x25,0x29,0x12,0x80,0x1E,0x25,0x25,0x25,0x12,0x00,0x02,0x1F,0x22,0x92,0x00,
0x1C,0x22,0x14,0x00,0x3F,0x00,0x1C,0x22,0x1C,0x00,0x1C,0x22,0x14,0x00,0x3F,0x14
用DS1302与12864LCD设计的可调式中文电子日历_第27张图片

图3-2 取“set clock”字模
3.3 取闹铃显示小字的字模
INT8U code CLOCK[] = {
0x00,0x36,0x41,0x41,0x36,0x00,//0
0x00,0x00,0x00,0x00,0x77,0x00,//1
0x00,0x31,0x49,0x49,0x46,0x00,//2
0x00,0x49,0x49,0x49,0x36,0x00,//3
0x00,0x07,0x08,0x08,0x77,0x00,//4
0x00,0x06,0x49,0x49,0x30,0x00,//5
0x00,0x36,0x49,0x49,0x30,0x00,//6
0x00,0x01,0x01,0x01,0x76,0x00,//7
0x00,0x36,0x49,0x49,0x36,0x00,//8
0x00,0x06,0x49,0x49,0x36,0x00//9
};
3.4 代码改进部分
为了显示闹铃时间和设置闹铃,在显示文字和调节对象代码部分作了相应改变流程图大致如下:
用DS1302与12864LCD设计的可调式中文电子日历_第28张图片

图3-4 调节对象选择流程图
1、修改INT8U DateTime[9];数组大小从7改成9
当前调节的时间对象:秒,分,时,日,月,年,闹钟的分、时(0,1,2,3,4,6,7,8)
2、DateTime_Adjust(char x)函数添加以下内容,扩展闹铃的时、分选项
//fengqian:Adjust_Index=8/7:闹钟的时/分
case 8:
if (x == 1 && DateTime[8] < 23)
DateTime[8]++;
if (x == -1 && DateTime[8] > 0)
DateTime[8]–;
break;
case 7:
if (x == 1 && DateTime[7] < 59)
DateTime[7]++;
if (x == -1 && DateTime[7] > 0)
DateTime[7]–;
break;
3、在LCD_12864文件添加函数Disp_A_Char_2()显示6x8的闹钟时间点阵字符
void Disp_A_Char_2(INT8U P,INT8U L,INT8U *M) reentrant
{
Common_Show(P,L,6,M);
}
4、T0_INT() interrupt 1中断函数中添加如下内容,在LCD屏显示闹钟时间
//fengqian:设置闹钟:时
Reverse_Display = Adjust_Index == 8;
Disp_A_Char_2(V_P_Offset+3, 10 + H_Offset, CLOCK + DateTime[8] / 10 * 6);
Disp_A_Char_2(V_P_Offset+3, 16 + H_Offset, CLOCK + DateTime[8] % 10 * 6);
//fengqian:设置闹钟:分
Reverse_Display = Adjust_Index == 7;
Disp_A_Char_2(V_P_Offset+3, 24 + H_Offset, CLOCK + DateTime[7] / 10 * 6);
Disp_A_Char_2(V_P_Offset+3, 30 + H_Offset, CLOCK + DateTime[7] % 10 * 6);
5、添加延时程序让延长蜂鸣器响铃时间
void delay(INT16U n)
{
INT16U i;
for(;n>0;n–)
for(i=200;i>0;i–);
}
6、在主函数while循环中加闹铃时间判断,决策是否进行响铃
while (1){
//fengqian:控制蜂鸣器响
if(((DateTime[1] / 10)(DateTime[7] / 10))&&
((DateTime[1] % 10)
(DateTime[7] % 10))&&
((DateTime[2] / 10)(DateTime[8] / 10))&&
((DateTime[2] % 10)
(DateTime[8] & 10)))//判断当前显示的时间的时分数和闹钟的是否一样,如果一样就响铃
{
beep=1;
delay(100);
}
else{
beep=0;
}
3.5 蜂鸣器软件设计
在主程序的获取时间循环中,新添闹铃时间与当前时间一致性判断,以进行响铃的决策代码,其判断;流程如下
用DS1302与12864LCD设计的可调式中文电子日历_第29张图片

图3-5 蜂鸣器响铃流程图
四、功能测试
4.1 闹钟设置
如图K1键按下后首次显示闹钟时的设定,其次是闹钟分钟,然后才是年、月、日……,均能实现加减操作
用DS1302与12864LCD设计的可调式中文电子日历_第30张图片

图4-1 K1按下首次显示闹钟时、分设置
用DS1302与12864LCD设计的可调式中文电子日历_第31张图片

图4-2 K2按下进行加操作
用DS1302与12864LCD设计的可调式中文电子日历_第32张图片

图4-3 K3按下进行减操作
用DS1302与12864LCD设计的可调式中文电子日历_第33张图片

图4-4 再次按下K1切换到分的设置
4.2 闹钟响状态
此处记录了当闹铃时间到,蜂鸣器鸣响功能实现成功
用DS1302与12864LCD设计的可调式中文电子日历_第34张图片

图4-5 时间和闹钟不一致闹铃未响状态
用DS1302与12864LCD设计的可调式中文电子日历_第35张图片

图4-6 时间和闹钟一致闹铃响状态
五、附录(主要源码)
5.1 main.c文件

// 声明外部函数,这个函数是在其他源文件LCD_12864.c中定义的
extern void Disp_A_Char_2(INT8U P,INT8U L,INT8U *M) reentrant;
sbit beep = P1^3;//定义蜂鸣器引脚
//当前调节的时间对象:秒,分,时,日,月,年,闹钟的分、时(0,1,2,3,4,6,7,8)
INT8U DateTime[9]; //所读取的日期时间
//5对应星期,星期调节由年月日调节自动完成
char Adjust_Index = -1;
//"年、月、日、星、期、时、分、秒"的汉字点阵(16x16)-----------------------
INT8U code DATETIME_WORDS[] ={……};
//星期"日、一、二、三、四、五、六"的汉字点阵(16x16)-----------------------
INT8U code WEEK_WORDS[] ={……};
//半角数字"0~9"的点阵(8x16)------------------------------------------
INT8U code DIGITS[] ={……};
//fengqian:闹钟小字字模
INT8U code CLOCK[]={……};
// 向DS1302写入一字节
//-----------------------------------------------------------------
void Write_one_byte(INT8U x){ }
//-----------------------------------------------------------------
// 从DS1302读取一字节
//-----------------------------------------------------------------
INT8U Get_one_byte(){ }
//-----------------------------------------------------------------
// 从DS1302指定位置读数据
//-----------------------------------------------------------------
INT8U Read_Data(INT8U addr){ }
//-----------------------------------------------------------------
// 向DS1302某地址写入数据
//-----------------------------------------------------------------
void Write_DS1302(INT8U addr,INT8U dat){ }
//-----------------------------------------------------------------
// 设置时间
//-----------------------------------------------------------------
void SET_DS1302() { }
//-----------------------------------------------------------------
// 读取当前日期时间
//-----------------------------------------------------------------
void GetTime() { }
//-----------------------------------------------------------------
// 判断是否为闰年
//-----------------------------------------------------------------
INT8U isLeapYear(INT16U y){ }
//-----------------------------------------------------------------
// 求自2000.1.1开始的任何一天是星期几
// 函数没有通过求出总天数后再求星期几,
// 是因为求总天数可能会越出INT16U的范围
//-----------------------------------------------------------------
void RefreshWeekDay(){ }
//-----------------------------------------------------------------
// 年月日时分秒递增/减调节(星期自动计算,不允许直接调节)
//-----------------------------------------------------------------
void DateTime_Adjust(char x)
{
INT8U d;
switch (Adjust_Index) //Adjust_Index为调节分类,x=1为递增,x=-1为递减
{
//fengqian:Adjust_Index,闹钟的时分
case 8:
if (x == 1 && DateTime[8] < 23)
DateTime[8]++;
if (x == -1 && DateTime[8] > 0)
DateTime[8]--;
break;
case 7:
if (x == 1 && DateTime[7] < 59)
DateTime[7]++;
if (x == -1 && DateTime[7] > 0)
DateTime[7]--;
break;
case 6://年00-99
if (x == 1 && DateTime[6] < 99)
DateTime[6]++;
if (x == -1 && DateTime[6] > 0)
DateTime[6]--;
//?????,???????2.29,??????2.28
if (DateTime[4] == 2 && DateTime[3] == 29)DateTime[3] = 28;
RefreshWeekDay(); //????
break;
case 4://年01-12
if (x == 1 && DateTime[4] < 12)
DateTime[4]++;
if (x == -1 && DateTime[4] > 1)
DateTime[4]--;
//年份变化后,如果此前刚好为2.29,则当前重设为2.28
if (DateTime[4] == 2 && DateTime[3] == 29)
DateTime[3] = 28;
RefreshWeekDay(); //刷新星期
break;
case 3://?00-28/29/30/31
d = M_Days[DateTime[4]]; //首先获取当前月的最大天数
//若为闰年的2月则最大天数改为29
if (DateTime[4] == 2 && isLeapYear(2000 + DateTime[6]))
d = 29;
if (x == 1 && DateTime[3] < d)
DateTime[3]++;
if (x == -1 && DateTime[3] > 1)
DateTime[3]--;
RefreshWeekDay();//刷新星期
break;
case 2://时
if (x == 1 && DateTime[2] < 23)
DateTime[2]++;
if (x == -1 && DateTime[2] > 0)
DateTime[2]--;
break;
case 1://分
if (x == 1 && DateTime[1] < 59)
DateTime[1]++;
if (x == -1 && DateTime[1] > 0)
DateTime[1]--;
break;
case 0://秒
if (x == 1 && DateTime[1] < 59)
DateTime[0]++;
if (x == -1 && DateTime[1] > 0)
DateTime[0]--;
break;
}
}
//-----------------------------------------------------------------
// 定时器0每秒刷新LCD显示
//-----------------------------------------------------------------
void T0_INT() interrupt 1
{
TH0 = -50000 >> 8; TL0 = -50000 & 0xFF; if (++tCount != 2) return;
tCount = 0;
//fengqian:设置闹钟:时
Reverse_Display = Adjust_Index == 8;
Disp_A_Char_2(V_P_Offset+3, 10 + H_Offset, CLOCK + DateTime[8] / 10 * 6);
Disp_A_Char_2(V_P_Offset+3, 16 + H_Offset, CLOCK + DateTime[8] % 10 * 6);
//fengqian:设置闹钟:分
Reverse_Display = Adjust_Index == 7;
Disp_A_Char_2(V_P_Offset+3, 24 + H_Offset, CLOCK + DateTime[7] / 10 * 6);
Disp_A_Char_2(V_P_Offset+3, 30 + H_Offset, CLOCK + DateTime[7] % 10 * 6);
//年(后两位)
Reverse_Display = Adjust_Index == 6;
Disp_A_Char(V_P_Offset, 16 + H_Offset, DIGITS + DateTime[6] / 10 * 16);
Disp_A_Char(V_P_Offset, 24 + H_Offset, DIGITS + DateTime[6] % 10 * 16);
//月
Reverse_Display = Adjust_Index == 4;
Disp_A_Char(V_P_Offset, 48 + H_Offset, DIGITS + DateTime[4] / 10 * 16);
Disp_A_Char(V_P_Offset, 56 + H_Offset, DIGITS + DateTime[4] % 10 * 16);
//日
Reverse_Display = Adjust_Index == 3;
Disp_A_Char(V_P_Offset, 80 + H_Offset, DIGITS + DateTime[3] / 10 * 16);
Disp_A_Char(V_P_Offset, 88 + H_Offset, DIGITS + DateTime[3] % 10 * 16);
//星期(1.星期日,2.星期一,....,7.星期六,故减1)
Reverse_Display = 0;
Disp_A_Word(V_P_Offset+2, 96 + H_Offset, WEEK_WORDS+(DateTime[5]-1)*32);
//时
Reverse_Display = Adjust_Index == 2;
Disp_A_Char(V_P_Offset+5,16+ H_Offset, DIGITS + DateTime[2] / 10 * 16);
Disp_A_Char(V_P_Offset+5, 24 + H_Offset, DIGITS + DateTime[2] % 10 * 16);
//分
Reverse_Display = Adjust_Index == 1;
Disp_A_Char(V_P_Offset+5, 48 + H_Offset, DIGITS + DateTime[1] / 10 * 16);
Disp_A_Char(V_P_Offset+5, 56 + H_Offset, DIGITS + DateTime[1] % 10 * 16);
//秒
Reverse_Display = Adjust_Index == 0;
Disp_A_Char(V_P_Offset+5, 80 + H_Offset, DIGITS + DateTime[0] / 10 * 16);
Disp_A_Char(V_P_Offset+5, 88 + H_Offset, DIGITS + DateTime[0] % 10 * 16);
}
//-----------------------------------------------------------------
// 键盘中断(INT0)
//-----------------------------------------------------------------
void EX_INT0() interrupt 0
{
if (K1 == 0)//选择调节对象
{
if (Adjust_Index == -1 || Adjust_Index == 0) Adjust_Index = 9;
if (--Adjust_Index == 5) Adjust_Index = 4;//跳过对星期的调节
}
else if (K2 == 0) DateTime_Adjust(1); //加
else if (K3 == 0) DateTime_Adjust(-1); //减
else if (K4 == 0)
{
SET_DS1302(); //将调节后的时间写入DS1302
Adjust_Index = -1; //操作索引重设为-1,时间继续写正常显示
}
}
//-----------------------------------------------------------------
// 主程序
//-----------------------------------------------------------------
//fengqian:延时程序
void delay(INT16U n)
{
INT16U i;
for(;n>0;n--)
for(i=200;i>0;i--);
}
void main()
{
beep = 0;
LCD_Initialize(); //液晶初始化
//显示年的固定前两位
Disp_A_Char(V_P_Offset, 0 + H_Offset, DIGITS + 2 * 16);
Disp_A_Char(V_P_Offset, 8 + H_Offset, DIGITS);
//--------------------------------------------------------------------
//显示固定汉字:年月日,星期,时分秒
//--------------------------------------------------------------------
Disp_A_Word(V_P_Offset, 32 + H_Offset, DATETIME_WORDS + 0 * 32);
Disp_A_Word(V_P_Offset, 64 + H_Offset, DATETIME_WORDS + 1 * 32);
Disp_A_Word(V_P_Offset, 96 + H_Offset, DATETIME_WORDS + 2 * 32);
Disp_A_Word(V_P_Offset + 2, 64 + H_Offset, DATETIME_WORDS + 3 * 32);
Disp_A_Word(V_P_Offset + 2, 80 + H_Offset, DATETIME_WORDS + 4 * 32);
Disp_A_Word(V_P_Offset + 5, 32 + H_Offset, DATETIME_WORDS + 5 * 32);
Disp_A_Word(V_P_Offset + 5, 64 + H_Offset, DATETIME_WORDS + 6 * 32);
Disp_A_Word(V_P_Offset + 5, 96 + H_Offset, DATETIME_WORDS + 7 * 32);
//fengqian :显示字set clock
Disp_A_Word(V_P_Offset + 2, H_Offset-10, DATETIME_WORDS + 8 * 32);
//允许INTO,T0中断,并配置相关初值
IE = 0x83; IP = 0x01; IT0 = 0x01;
TH0 = -50000 >> 8; TL0 = -50000 & 0xFF; TR0 = 1;
while (1)
{
//fengqian:控制蜂鸣器响
if(((DateTime[1] / 10)==(DateTime[7] / 10))&&
((DateTime[1] % 10)==(DateTime[7] % 10))&&
((DateTime[2] / 10)==(DateTime[8] / 10))&&
((DateTime[2] % 10)==(DateTime[8] & 10)))
{
beep=1;
delay(100);
}
else{
beep=0;
}
//如果未执行调节操作则正常读取当前时间
if (Adjust_Index == -1) GetTime();
}
}

5.2 LCD_12864.c

//显示一个6x8点阵字符
void Disp_A_Char_2(INT8U P,INT8U L,INT8U *M) reentrant
{
Common_Show(P,L,6,M);
}

个人设计二

一、扩展功能总体说明
1.1 总体说明
当前调节过程中无操作时间已达十秒且未”确认保存”, 系统自动恢复原时间继续运行
以下是对于该项扩展功能的总体说明:
该系统启动后lcd液晶屏上的数字将从普通数字变为笔段式数字显示,字的大小为16x16;按下K1按钮后进入调节过程,如果在接下来的10s后不进行操作(不点击K1、K2、K3、K4),系统将自动读取PC机上的时间即恢复原时间继续运行。

用DS1302与12864LCD设计的可调式中文电子日历_第36张图片
图4-2按下K1 选择调节对象 时间暂停

用DS1302与12864LCD设计的可调式中文电子日历_第37张图片

你可能感兴趣的:(本科实验报告,物联网)