当前位置:文档之家› 电子万年历的设计

电子万年历的设计

?《单片机电子万年历的设计》论文
一?项目背景
随着社会的发展?时间的推移?从以前的观察太阳、摆钟?到现
在的单片机电子钟?人类在不断地研究?不断地突破?不断地创造新
纪录?单片机电子钟也已成为当今社会人类准确、快速获取信息的重
要工具之一。
现代社会生活节奏逐渐加快?很多人没有时间观念?也相应导致
了一些社会问题的发生。而市场上一些其他的钟表?不是价格昂贵?
就是质量问题?对许多普通家庭并不适用。
我们设计的电子万年历以单片机 AT89S52 为控制核心?加以时钟
构成时钟电路?能够实现时间和日期的显示。以下详细介绍分析设计
原理和制作过程。
关键词 ? 单片机?实时时钟?显示电路?按键扫描?温度传感器?
二?设计任务与要求
基本要求?
1.能够显示年、月、日、时、分、秒和星期、温度?
2.可以人为校正年、月、日、时、分、秒和星期、温度?
3.掉电情况下原有的信息不会丢失?
三?方案设计与论证
方案一?
按照系统的设计的功能要求?初步确定系统由主控模块?时钟模
块、显示模块、各键盘接口模块共四个模块组成?电路系统框图如图
1 所示。主控芯片采用 AT89S52 单片机?时钟芯片使用 DS1302。采用
DS1302 作为计时芯片?可以做到计时准确。更重要的是?DS1302 可
以在很小电流的后备电源?2.5V-5.5V 电源?在 2.5V 时耗电<300nA?
下继续计时?而且 DS1302 可以编程选择多种充电电流来对后备电源
进行慢速充电?可以保证后备电源基本功不耗电。显示模块采用普通
的共阳 LED 数码管?键输入采用查询法实现功能调整。
图 1 电子万年历电路系统构成框图
方案二?
按照系统的设计的功能要求?将系统分为主控模块、时钟电路模
块、按键扫描模块、LCD 显示模块、电源电路、复位电路、晶振电路
几个模块?系统框图如图 2 所示。主控模块采用 AT89S52 单片机?按
键模块有四个按键?用于调整时间?显示模块采用 LCD1602?时钟电
路模块采用 DS1302 实现对时间、日期的操作。
时钟电路
?DS1302?
主控模块
AT89S52
键 盘 扫 描
LED 显示电路

图 2 基于 AT89S52 单片机的电子万年历电路系统框图
方案三?
按照系统设计的要求和功能?将系统分为主控制器模块、显示模
块、按键开关模块、蜂鸣器电路模块。系统框图如图 3 所示。主控模
块采用 AT89S52 单片机位控制中心? 显示模块采用液晶 LCD1602 显
示?计时器使用 AT89S52 自带的定时器功能?实现对日期、时间的操
作?通过按键对日期、时间进行调整。
图 3 基于单片机 AT89S52 的电子万年历电路系统框图

复位电路
晶振电路
时钟电路
?DS1302?
主控模块
AT89S52
按键扫描模块
LCD1602 显示模块
复位电路
晶振电路
单片机
AT89S52
按键电路
LCD1602 显示电路
蜂鸣器电路
方案论证?
以上提到的三个方案中?在电路原理方面大致相同?都能够达到
设计任务与要求。在方案一和方案二中?使用时钟芯片 DS1302 实现
对日期、时间的操作?方案三中则利用单片机自带的定时器功能。另
外? 在方案二和方案三的显示模块中? 都采用的是液晶显示屏LCD1602
作为显示?方案一中采用则 LED 数码管进行显示?采用 LED 数码管进
行动态扫描?数码管价格适中?对于显示数字较好?而且使用单片机
的端口也较少?而采用 LCD1602 液晶显示屏?液晶显示屏显示功能强
大?可以显示大量文字图形?显示多样性?显示清晰可见?但价格上
相对于 LED 数码管昂贵很多。但基于显示的东西很多这一点考虑?如
果数码管显示?则需要数量较多?价格也会相应提高?而且也不利于
控制?因此选择 LCD1602 液晶显示屏作为显示模块。DS1302 是一款
高性能实时时钟芯片?以计时准确、接口简单、使用方便、工作电压
范围宽和低功耗等优点?得到广泛的应用?同时可以对秒、时、分、
日、月、年以及闰年补偿年进行计数?而且在掉电时能够在外部纽扣
电池的供电下继续工作?不会因为掉电而引起时间的重置。但方案三
中采用的定时器会因为掉电使时间和日期置于初始值? 虽然采用单片
机自带的定时器功能成本较低?但产生的时间误差较大?所以不采用
此种方案。
通过对以上方案的论证分析? 本次设计选择方案二? 采用AT89S52
单片机作为主控系统?DS1302 提供时钟?LCD1602 液晶显示屏作为显
示模块。
四?硬件单元电路设计与参数计算
1.主控制系统
单片机中央处理系统的方案设计?我们选用具有 ATMEL 公司的
AT89C52 单片机作为中央处理器?如图 4 所示。该单片机除了拥有
MCS-51 系列单片机的所有优点外?内部还具有 8K 的在系统可编程
FLASH 存储器? 低功耗的空闲和掉电模式? 极大的降低了电路的功耗?
还包含了定时器、程序存储器、数据存储器等硬件?其硬件能符合整
个控制系统的要求?不需要外接其他存储器芯片和定时器件?方便地
构成一个最小系统。整个系统结构紧凑?抗干扰能力强?性价比高?
是比较合适的方案。
图 4 AT89S52 主控制系统
2.时钟振荡电路
时钟振荡电路图 5 所示? 时钟振荡电路用于产生单片机正常工作
时所需要的时钟信号?电路由两个 30pF 的瓷片电容和一个 12MHz 的
晶振组成?并接入到单片机的 XTAL1 和 XTAL2 引脚处?

使单片机工作
于内部振荡模式。此电路在加电后延迟大约 10ms 振荡器起振?在
XTAL2 引脚产生幅度为 3V 左右的正弦波时钟信号?其振荡频率主要
由石英晶振的频率决定。电路中两个电容 C1、C2 的作用使电路快速
起振? 提高电路的运行速度? 对于 AT89S52 其工作频率为 0 至 33MHz,
在这个范围内单片机能够正常的工作。
图 5 AT89S52 时钟振荡电路
3.复位电路
复位电路由电阻和极性电容组成?如图 6 所示。通过高电平使单
片机复位? 在时钟电路开始工作后? 当高电平的时间超过大约 2us 时?
即可实现复位。此复位电路同时具备了上电复位和手动复位的功能?
上电复位发生在开机加电时?由系统自动完成?手动复位通过一个按
键来实现?在程序运行时?若遇到死机?死循环或程序“跑飞”等情
况?通过手动复位就可以实现重新启动的操作。手动按钮复位需要人
为在复位输入端 RST 上加入高电平。 一般采用的办法是在 RST 端和正
电源 Vcc 之间接一个按钮和一个电阻? 如图所示。 当人为按下按钮时?
则 Vcc 的+5V 电平就会直接加到 RST 端? 由于人的动作再快也会使按
钮保持接通达数十毫秒?所以?完全能够满足复位的时间要求。上电
复位的工作过程是在加电时? 复位电路通过电容加给 RST 端一个短暂
的高电平信号?此高电平信号随着 Vcc 对电容的充电过程而逐渐回
落?即 RST 端的高电平持续时间取决于电容的充电时间?由图可知充
电时间为?T=2.3RC=2.3*10*10
-6 *5.1*10 3 =0.1173s ?保证系统能够
可靠地复位。
图 6 AT89S52 复位电路
4.DS1302 时钟电路
时钟电路主要由时钟芯片 DS1302、备用电池、晶振等几部分组
成?如图 7 所示。DS1302 采用 3 线串行接口?占用引脚少?内部集
成了可编程日历时钟? 用户可以根据需要通过单片机的控制来自行设
置?支持双电源供电?可以使用外部主电源和备用电源?备份电源能
够使时钟芯片继续工作。
图 7 DS1302 时钟电路
5.按键电路
按键电路由四个轻触开关组成?如图 8 所示。按键用来调整时间
和设定闹钟?其一端直接接到单片机的端口?另一端接地?当按下按
键时?相应的端口变为低电平?通过检测这一低电平就可以判断是哪
个键按下?从而作相应的操作。
图 8 按键电路
6.显示电路
显示电路采用 LCD1602 液晶显示?图中只画出了其相应的接口?
如图 9 所示。3 脚用于调节 LCD1602 的背光,4、5、6 为 LCD1602 的控
制口?用于控制其写入或是读出指令?7 至 14 脚为 LCD1602 的数据
口?将数传送到 LCD1602 中。
图 9 LCD1602 接口电路
7.蜂鸣器电路
蜂鸣器电路由一个 220 欧的电阻?三极管 8550?及

蜂鸣器组成?
如图 10 所示。通过控制三极管的导通和截止来实现蜂鸣器的响与不
响。

图 10 蜂鸣器电路
8.温度传感器 S18B20 电路
S18B20 是美国 DALLAS 公司生产的数字温度传感器?采用单总线
的接口方式与微处理器连接时仅需要一条口线即可实现微处理器
与 DS18B20 的双向通讯。
DS18B20 与单片机的接口电路?
五?软件设计与流程图
1.程序流程图
主程序首先初始化定时器、LCD1602 及 DS1302?然后就开始查询
按键?有键按下则开始调整时间和设置闹钟?若没有按下?则执行下
面的时间、日期及闹钟时间的显示?最后依次循环这些相同的操作?
相应流程图如图 11 所示?
图 11 程序主流程
开始
初始化
按键是否按

显示时间、日期及闹钟时间
时间、日期、闹钟设定
结束


按键的检测主要是通过查询的办法来实现? 利用按键进行间调整及闹
钟设置?首先检测 K1 键是否按下?当 K1 键按下时?并且 K2 键按下
时?则设置初始的默认时间?当 K1 按下?并且 K4 按下时?则是开启
闹钟功能?若只是 K3 按下则开始设置时间及日期?同时被选择的时
间和日期开始闪烁?第一次按下 K3 时?设置年份?若按下 K1?则是
减 1 操作?按下 K2 是加 1 操作?设置好年后?第二次按下 K3 时?则
是设置月份?按 K1 减?按 K2 则加 1?依次循环下去?则可以将时间
和日期设置完毕?而当按下 K4 时?则是设置闹钟时间?第一次按下
K4?设置时?按 K1 时减 1?按 K2 时加 1?第二次按下时?设置分?
同样的操作?按 K1 分减 1?按 K2 分加 1?程序流程图 12 所示?
图 12 时间调整及闹钟设置程序流程图
2.软件设计
软件总设计:
主程序首先对系统环境初始化? 设置定时器 T0 工作模式为 16 位
定时/计数器模式?置位总中断允许位 EA?并对键盘端口置位?再对
LCD1602 初始化?DS1302 初始化。接着扫描键盘?在键盘程序里面是
对时间、日期及闹钟的调整?最下面是时间的显示及闹钟比较时间。
子程序设计:
读写 DS1302 子程序?
六?总原电路及元器件清单
1.总原理图
基于 AT89S52 单片机的电子万年历硬件电路图如图 13 所示?系
统由 AT89S52 单片机?按键扫描电路、显示电路、时钟电路、晶振电
路、复位电路、电源指示电路及蜂鸣器输出电路。
写 DS1302 子程序?
uchar read_ds1302(uchar addr)
{
uchar backdata;
sclk=0;
reset=1;
write_byte(addr); //先写地址
backdata=read_byte();// 然 后
读数据
sclk=0;
reset=0;
return (backdata);
}
读写 LCD1602 子程序?
写指令数据到 LCD 子程序:
void lcd_wcmd(uchar cmd)
{
while(lcd_busy());
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 0;
_nop_();
_

nop_();
P0 = cmd;
delayNOP();
LCD_EN = 1;
delayNOP();
LCD_EN = 0;
}

图 13 基于 AT89S52 的电子万年历电路原理图
2.PCB 制板图
基于 AT89S52 单片机的电子万年历制板如图 14 所示?
图 14 基于 AT89S52 的电子万年历 PCB 制板图
3.整体电路仿真图以及仿真结果分析
整体电路仿真图如图 15 所示所示。

图 15 电子万年历仿真图
仿真结果分析
仿真正确显示了时间?在 LCD1602 中正确显示了当前日期、时间
并可以显示闹钟时间?通过按按键 K3,就可以开始设置时间,依次按
K3 依次在年、月、日、时、分之间切换?并且相应的调整的位会闪
烁?按 K2 键用于加 1 操作,K1 键用于减 1 操作。按 K4 键则可以设置
闹钟时间?依次按 K4 可以在时和分之间切换?按 K2 键,可以增加值,
按 K1 键,可以减小值。闹钟开启设置?先按住 K1?然后再按 K4 就可
以开启闹钟功能?当设置好闹钟后并开启闹钟功能后?就可以有闹钟
功能?闹钟时间为 1 分钟。仿真正确显示了我们需要达到的要求?符
合了我们设计的要求。
4.元件清单
基于 AT89S52 单片机的电子万年历元件清单如表 1 所示。
表 1 基于 AT89S52 单片机的电子万年历元件清单
元件名称 型号 数量/个 用途
单片机 AT89S52 1 控制核心
时钟芯片 DS1302 1 实时时钟
晶振 12MHz 1 晶振电路
晶振 32.768kHz 1 时钟电路
电容 30pF 2 晶振电路
电解电容 10uF/25V 1 复位电路
按键开关 6*6*6 5 按键/复位电路
电阻 1K 1 电源指示电路
电阻 5.1K 1 复位电路
电阻 220 2 限流/复位电路
滑动变阻器 1M 1 LCD1602 背光调节
三极管 S8550 1 蜂鸣器电路
发光二极管 绿色 1 电源指示电路
蜂鸣器 有源蜂鸣器 1 蜂鸣器电路
排针 间距 2.54mm 2 电源接口
排座 间距 2.54mm 16 显示电路
LCD LCD1602 1 显示电路
独石电容 104 1 电源接口
电源 Vcc +5V 1 提供+5V 电源
七?安装与调试
1.电路安装
用 Protel99se 画好 PCB 后,并做出相应的电路板,测试过电路板
没有短路和断路后,对照Protel99se中的PCB板图将相应的元件插到
电路板上,首先将矮的电子元件先插上,然后将其焊在电路板上,依照
高矮,将剩余的元件安装在电路板上,并依次焊接好.当焊接完成后.
剪去引脚,并测试电路焊接后是否有短路.
2.电路调试
在测试中遇到 LCD1602 不能够显示出时间和日期? 经过检查才发
现是 LCD1602 的背光没有调好? 通过调节接在 LCD1602 上 3 脚上的滑
动变阻器?改变所给的电压?可以清晰地看见了 LCD 能够显示。
对于 DS1302 在测试过程中发其上电不久就发热?在检查电路过
程中?不是因为发现芯片短路?最终检查发现原来是 DS1302 的引脚
接法不正确?给+5V 的

应该是芯片的脚?给后备电池的是 1 脚?当换
过来之后?芯片就不发热了?而且可以正常显示出我们要显示的时间
和日期,显示效果如图 16 所示?

图 16 调试显示效果图
3.软件调试
在软件调试过程中?当按下按键调节时间和日期后?时间不能继
续在加?后来分析了程序才发现?是在设置好时间和日期时我们暂停
了时钟? 在设置完后没有启动时钟? 所以时间和日期就不能够继续走?
在那里停止了?发现这个原因后?我在设置完时间后就开启时钟?时
间和日期就能够正确显示了。
八?性能测试与分析
上电测试?LCD1602 能够正确显示时间和日期?第一次 K3 按钮?
开始设置年?再按 K1?年减 1?按 K2?年加 1?按二次 K3?设置月?
按 K1?月减 1?按 K2?月加 1?按三次 K3?设置日?按 K1?日减 1?
按 K2?日加 1?按四次 K3?设置时?按 K1?时减 1?按 K2?时加 1?
按五次 K3?设置分?按 K1?分减 1?按 K2?分加 1?按第五次?设
置时间完成。闹钟设置?按下 K4?开始设置闹钟?按第一次?设置
分?按 K1?时减 1?按 K2?时加 1?按第二次设置分?按 K1?分减 1?
按 K2?分加 1?设置好闹钟后?按组合键 K1+K4 开启闹钟功能?当到
闹钟时间后?蜂鸣器响?闹钟时间为 1 分钟?在对电路测试后?电路
达到了所需的功能。
九?程序清单
程序清单如下?
#include
#include
//#include "LCD1602.h"
//#include "DS1302.h"
#define uint unsigned int
#define uchar unsigned char
sbit DS1302_CLK = P1^7; //实时时钟时钟线引脚
sbit DS1302_IO = P1^6; //实时时钟数据线引脚
sbit DS1302_RST = P1^5; //实时时钟复位线引脚
sbit wireless_1 = P3^0;
sbit wireless_2 = P3^1;
sbit wireless_3 = P3^2;
sbit wireless_4 = P3^3;
sbit ACC0 = ACC^0;
sbit ACC7 = ACC^7;
char
hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year;
//秒,分,时到日,月,年位闪的计数
sbit Set = P2^0; //模式切换键
sbit Up = P2^1; //加法按钮
sbit Down = P2^2; //减法按钮
sbit out = P2^3; //立刻跳出调整模式按钮
sbit DQ = P1^0; //温度传送数据 IO 口
char done,count,temp,flag,up_flag,down_flag;
uchar temp_value; //温度值
uchar TempBuffer[5],week_value[2];

void show_time(); //液晶显示程序
/***********1602 液晶显示部分子程序****************/
//Port
Definitions**********************************************************
sbit LcdRs = P2^5;
sbit LcdRw = P2^6;
sbit LcdEn = P2^7;
sfr DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=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++;
}
}
/***********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%10 + '0';
}
else
{
Time->DateString[0] = ' ';
Time->DateString[1] = ' ';
Time->DateString[2] = ' ';
Time->DateString[3] = ' ';
}
Time->DateString[4] = '/';
if(hide_month<2)
{
Time->DateString[5] = Time->Month/10 + '0';
Time->DateString[6] = Time->Month%10 + '0';
}
else
{
Time->DateString[5] = ' ';
Time->DateString[6] = ' ';
}
Time->DateString[7] = '/';
if(hide_day<2)
{
Time->DateString[8] = Time->Day/10 + '0';
Time->DateString[9] = Time->Day%10 + '0';
}
else
{
Time->DateString[8] = ' ';
Time->DateString[9] = ' ';
}
if(hide_week<2)
{
week_value[0] = Time->Week%10 + '0'; //星期的数据另外放到
week_value[]数组里,跟年,月,日的分开存放,因为等一下要在最后显示
}
else
{
week_value[0] = ' ';
}
week_value[1] = '\0';
Time->DateString[10] = '\0'; //字符串末尾加 '\0' ,判断结束字符
}
void TimeToStr(SYSTEMTIME *Time) //将时,分,秒数据转换成液晶显示字符放
到数组 TimeString[];
{ if(hide_hour<2)
{
Time->TimeString[0] = Time->Hour/10 + '0';
Time->TimeString[1] = Time->Hour%10 + '0';
}
else
{
Time->TimeString[0] = ' ';
Time

->TimeString[1] = ' ';
}
Time->TimeString[2] = ':';
if(hide_min<2)
{
Time->TimeString[3] = Time->Minute/10 + '0';
Time->TimeString[4] = Time->Minute%10 + '0';
}
else
{
Time->TimeString[3] = ' ';
Time->TimeString[4] = ' ';
}
Time->TimeString[5] = ':';
if(hide_sec<2)
{
Time->TimeString[6] = Time->Second/10 + '0';
Time->TimeString[7] = Time->Second%10 + '0';
}
else
{
Time->TimeString[6] = ' ';
Time->TimeString[7] = ' ';
}
Time->DateString[8] = '\0';
}
void Initial_DS1302(void) //时钟芯片初始化
{
unsigned char Second=Read1302(DS1302_SECOND);
if(Second&0x80) //判断时钟芯片是否关闭
{
Write1302(0x8e,0x00); //写入允许
Write1302(0x8c,0x07); //以下写入初始化时间 日期:07/07/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); //禁止写入
}
}
/***********ds18b20 子程序*************************/
/***********ds18b20 延迟子函数?晶振 12MHz ?*******/
void delay_18B20(unsigned int i)
{
while(i--);
}
/**********ds18b20 初始化函数**********************/
void Init_DS18B20(void)
{
unsigned char x=0;
DQ = 1; //DQ 复位
delay_18B20(8); //稍做延时
DQ = 0; //单片机将 DQ 拉低
delay_18B20(80); //精确延时 大于 480us
DQ = 1; //拉高总线
delay_18B20(14);
x=DQ; //稍做延时后 如果 x=0 则初始化成功 x=1 则初始化失

delay_18B20(20);
}
/***********ds18b20 读一个字节**************/
unsigned char ReadOneChar(void)
{
uchar i=0;
uchar dat = 0;
for (i=8;i>0;i--)
{
DQ = 0; // 给脉冲信号
dat>>=1;
DQ = 1; // 给脉冲信号
if(DQ)
dat|=0x80;
delay_18B20(4);
}
return(dat);
}
/*************ds18b20 写一个字节****************/
void WriteOneChar(uchar dat)
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat&0x01;
delay_18B20(5);
DQ = 1;
dat>>=1;
}
}
/**************读取 ds18b20 当前温度************/
void ReadTemp(void)
{
unsigned char a=0;
unsigned char b=0;
unsigned char t=0;
Init_DS18B20();
WriteOneChar(0xCC); // 跳过读序号列号的操作
WriteOneChar(0x44); // 启动温度转换
delay_18B20(100); // this message is wery important
Init_DS18B20();
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0xBE); //读取温度寄存器等?共可读 9 个寄存器? 前两个
就是温度

delay_18B20(100);
a=ReadOneChar(); //读取温度值低位
b=ReadOneChar(); //读取温度值高位
temp_value=b<<4;
temp_value+=(a&0xf0)>>4;
}
void temp_to_str() //温度数据转换成液晶字符显示
{
TempBuffer[0]=temp_value/10+'0'; //十位
TempBuffer[1]=temp_value%10+'0'; //个位
TempBuffer[2]=0xdf; //温度符号
TempBuffer[3]='C';
TempBuffer[4]='\0';
}
void Delay1ms(unsigned int count)
{
unsigned int i,j;
for(i=0;ifor(j=0;j<120;j++);
}
/*延时子程序*/
void mdelay(uint delay

)
{ uint i;
for(;delay>0;delay--)
{for(i=0;i<62;i++) //1ms 延时.
{;}
}
}
void outkey() //跳出调整模式,返回默认显示
{ uchar Second;
if(out==0||wireless_1==1)
{ mdelay(8);
count=0;
hide_sec=0,hide_min=0,hide_hour=0,hide_day=0,hide_week=0,hide_mon
th=0,hide_year=0;
Second=Read1302(DS1302_SECOND);
Write1302(0x8e,0x00); //写入允许
Write1302(0x80,Second&0x7f);
Write1302(0x8E,0x80); //禁止写入
done=0;
while(out==0);
while(wireless_1==1);
}
}
/////////////////////////////////////////////////////////////////////
///////////////////////////////////////
void Upkey()//升序按键
{
Up=1;
if(Up==0||wireless_2==1)
{
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);
while(wireless_2==1);
}
}
/////////////////////////////////////////////////////////////////////
///////////////////////////////////////
void Downkey()//降序按键
{
Down=1;
if(Down==0||wireless_3==1)
{
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;
bre

ak;
default:break;
}
while(Down==0);
while(wireless_3==1);
}
}
void Setkey()//模式选择按键
{
Set=1;
if(Set==0||wireless_4==1)
{
mdelay(8);
count=count+1; //Setkey 按一次,count 就加 1
done=1; //进入调整模式
while(Set==0);
while(wireless_4==1);
}
}
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); //日期数据转换液晶字符
ReadTemp(); //开启温度采集程序
temp_to_str(); //温度数据转换成液晶字符
GotoXY(12,1); //液晶字符显示位置
Print(TempBuffer); //显示温度
GotoXY(0,1);
Print(CurrentTime.TimeString); //显示时间
GotoXY(0,0);
Print(CurrentTime.DateString); //显示日期
GotoXY(15,0);
Print(week_value); //显示星期
GotoXY(11,0);
Print("Week"); //在液晶上显示 字母 week
Delay1ms(400); //扫描延时
}
main()
{
flag=1; //时钟停止标志
LCD_Initial(); //液晶初始化
Init_DS18B20( ) ; //DS18B20 初始化
Initial_DS1302(); //时钟芯片初始化
up_flag=0;
down_flag=0;
done=0; //进入默认液晶显示
wireless_1=0;
wireless_2=0;
wireless_3=0;
wireless_4=0;
while(1)
{
while(done==1)
keydone(); //进入调整模式
while(done==0)
{
show_time(); //液晶显示数据
flag=0;
Setkey(); //扫描各功能键
}
}
}

相关主题
文本预览
相关文档 最新文档