基于STC89C52的多功能实时时钟

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

课程设计
课程名称__ ___单片机_________________ 题目名称_基于STC89C52的多功能实时时钟学生学院_____材料与能源学院___________ 专业班级_________*********____________ 学号_________*********____________ 学生姓名_______ 刘旭波 _______________ 指导教师_________刘俊 _____________
2012 年01 月04 日
- 摘要 -
随着社会发展需求的改变,万年历是一个应用非常广泛的实用日常计时工具,带有显示年、月、日、星期、时、分、秒、秒表和按键可调时间及其按键设置闹钟的功能,同时具有月末自动更新,闰年补偿功能等多种功能。

此系统是基于STC89C52单片机设计的,包含液晶显示模块,DS1302实时时钟模块,DS18B20温度采集模块,键盘扫描模块,发声模块。

STC89C52作为控制核心,具有功耗低,功能强等特点,电压可选3到5V电源供电。

显示模块采用1602液晶动态显示,相对数码管而言经济实用,占用空间小,对于显示数字、字母最为合适,而且与单片机连线简单,占用IO口相对较少。

实时时钟芯片DS1302提供RTC/日历、定时闹钟等功能,如果检测到主电源故障,该器件可自动切换到备用电源供电,DS1302将石英晶体与电池集成在一起,在断电后仍可精确走1年。

温度检测模块采用数字式温度传感器DS18B20,该芯片具有精度高,测量范围广等优点,易与单片机连接,模块电路组成简单。

钟表的数字化给人们生产生活带来了极大的方便,在此基础上完成的万年历精度高,功能易于扩展。

可扩展成为诸如定时自动报警、按时自动打铃、时间程序自动控制、定时广播、自动起闭路灯、定时开关烘箱、通断动力设备、甚至各种定时电气的自动启用等电路。

关键词:STC89C52、DS1302、DS18B20、1602液晶显示、实时时钟、温度测量
- 目录 -
第一章设计要求与方案论证.......................................................... - 1 -
1.1 基本要求.................................................................. - 1 -
1.2 扩展功能............................................................ - 1 -
1.3 系统方案选择和论证........................................................ - 1 -
1.3.1 总系统方案.......................................................... - 1 -
1.3.2 芯片方案选择和确定.................................................. - 1 -
1.3.
2.1 STC89C52简介 ................................................. - 1 -
1.3.
2.2 DS1302时钟芯片 ............................................... - 2 -
1.3.
2.3 温度传感器DS18B20............................................. - 3 -
1.3.
2.4 LCD1602 ....................................................... - 3 - 第二章硬件设计和实现.............................................................. - 3 -
2.1 系统设计.................................................................. - 3 -
2.1.1系统设计框图 ......................................................... - 3 -
2.1.2 系统硬件需求介绍..................................................... - 4 -
2.2 系统硬件各模块作用........................................................ - 4 -
2.2.1 单片机模块........................................................... - 4 -
2.2.2 实时时钟模块......................................................... - 5 -
2.2.3 温度传感模块......................................................... - 6 -
2.2.4 液晶显示模块......................................................... - 8 -
2.2.5 矩阵键盘模块........................................................ - 10 -
2.2.6 发声模块............................................................ - 11 -
2.2.7 电源模块............................................................ - 11 -
2.3 系统电路图设计........................................................... - 11 -
2.3.1 系统总原理图及连线.................................................. - 11 - 第三章软件设计与分析........................................................... - 12 -
3.1 主程序流程图............................................................. - 12 - 第四章系统测试................................................................. - 13 -
4.1 功能测试................................................................ - 13 - 第五章设计总结和心得........................................................... - 13 -
5.1 设计总结与心得.......................................................... - 13 -
5.1.1 本设计的创新点与未来功能扩展........................................ - 13 -
5.2 设计心得................................................................ - 14 - 第六章参考文献.................................................................. - 14 -
6.1 参考文献................................................................ - 14 - 附录一:程序清单.................................................................. - 15 - 附录二:实物图片.................................................................. - 33 -
第一章设计要求与方案论证
1.1 基本要求:
(1):设计能支持年、月、日、星期、时、分、秒的时钟,有时间调整功能及闹钟功能。

(2):时钟附带有一个温度计功能,温度检测精度高于2度,显示精度为1度。

(3):时钟具有装卸电池时掉电保护功能,保护时间大于5分钟。

(4):功耗小于0.5mA/5V。

1.2 扩展功能
(1):提高温度转换精度,在0~99度时显示度。

(2):实现双电源供电(220V及电池供电)。

(3):能够提供生日提醒指示;能够每天提供3个时间点的闹钟报时功能。

(4):非接触止闹功能
1.3 系统方案选择和论证
1.3.1 总系统方案
方案一:直接利用单片机系统的定时器进行定时计数来实现电子时钟和万年历,通过温度传感器来实现温度的测量,数据全部存储在单片机RAM中,通过单片机I/O口上设置按键,来调整时间。

方案二:利用单片机作为控制核心,配以实时时钟芯片,由单片机读取实时时钟芯片内的时间寄存器,经过格式转换和其他的相关处理后,送到显示部分;配以数字温度传感器,直接读取转换过的数字温度值,根据精度要求进行相关运算后,也送到显示部分;配以EEPROM,保存闹钟数据,系统掉电重启后原来设置的闹钟不会丢失。

在单片机的I/O口上设置按键,用来调整时间、闹钟等。

方案比较及确定:都是利用单片机作为控制核心,方案一的数据保存在RAM中,系统掉电后,将会丢失数据;方案二则是时钟芯片中,具有掉电数据保存功能,而且可以在显示时间的同时准确外部事件的发生时间。

综上所述,我们采用方案二实现系统总体要求。

1.3.2 芯片方案选择和确定
1.3.
2.1 STC89C52简介
STC89C52单片机作为核心控制体,该单片机具有高可靠,超低价,低功耗,无法解密等优点。

该单片机属于双列直插式封装的PDI40口管脚。

具有4个输入输出端口,分别为PORT0、PROT1、PROT2、PROT3,其中P0口是一组8位漏极开路型双向IO口,校验时,要求接上拉电阻。

其他三个内部有30K的电阻,所以不用再外接电阻。

此单片机具有6个中断,其中包括三个定时器中断,二个外部中断,一个串口中断,为全双工通信口。

内部有静态非易失EEPROM和看门狗。

片内含8Kbbytes的可反复檫写的只读程序存储器(PEROM)和256bytes的随机存取数据存储器(RAM),功能强大,适合许多较为复杂的控制应用场合。

相比较其他芯片来说比较适合学生试验所用,故采用此单片机作为核心控制芯片。

1.3.
2.2 DS1302时钟芯片
DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。

采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。

DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。

DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后备电源双电源引脚,同时提供了对后备电源进行涓细电流充电的能力。

DS1302可以用于数据记录,特别是对某些具有特殊意义的数据点的记录,能实现数据与出现该数据的时间同时记录。

这种记录对长时间的连续测控系统结果的分析及对异常数据出现的原因的查找具有重要意义。

传统的数据记录方式是隔时采样或定时采样,没有具体的时间记录,因此,只能记录数据而无法准确记录其出现的时间;若采用单片机计时,一方面需要采用计数器,占用硬件资源,另一方面需要设置中断、查询等,同样耗费单片机的资源,而且,某些测控系统可能不允许。

但是,如果在系统中采用时钟芯片DS1302,则能很好地解决这个问题。

DS1302可以用于数据记录,特别是对某些具有特殊意义的数据点的记录,能实现数据与出现该数据的时间同时记录。

这种记录对长时间的连续测控系统结果的分析及对异常数据出现的原因的查找具有重要意义。

传统的数据记录方式是隔时采样或定时采样,没有具体的时间记录,因此,只能记录数据而无法准确记录其出现的时间;若采用单片机计时,一方面需要采用计数器,占用硬件资源,另一方面需要设置中断、查询等,同样耗费单片机的资源,而且,某些测控系统可能不允许。

但是,如果在系统中采用时钟芯片DS1302,则能很好地解决这个问题。

1.3.
2.3 温度传感器DS18B20
DS18B20是数字式温度传感器,采用单总线通信协议。

DS18B20具有体积小,硬件开销低,抗干扰能力强,精度高附加功能强,封装形式多样等特点。

适合各种狭小空间内设备的数字测温和控制。

同时单线可挂接多个元件,因为每个元件都有唯一的一个64位光刻ROM编码,家族码为28H,可以多个也可单个操作。

电压测量范围是3.0V 到5.5V。

内部含有EEPROM,其报警上、下限温度值和设定的分辨率倍数在芯片掉电的情况不丢失。

并且内部带有AD转换电路,技术较为成熟,所以采用此芯片最为合适。

1.3.
2.4 LCD1602
1602液晶应用非常广泛,操作简单,功能强大,采用1602液晶显示各种数字信息最为合适,通过对单片机的编程来控制DS1302和DS18B20芯片的读写操作来获取相应的信息,再通过对液晶的编程控制将获取到的信息通过一系列转换从而显示到1602液晶上。

最后达到有实时时钟、万年历、温度测量、秒表、闹钟等功能。

系统论证时通过在单片机学习板上的试验操作,能够达到预期的效果!
第二章硬件设计和实现
2.1 系统设计
2.1.1系统设计框图
根据系统总体要求,将系统分为若干模块,完成多项功能。

按照系统设计功能的要求,以单片机为核心,共六个模块组成,即:单片机模块、实时时钟模块、显示模块、矩阵键盘模块、温度传感模块和发声模块,电路系统构成框图如图1所示。

图2 单片机模块
图2为单片机最小电路,其中晶振频率可以根据自己需要进行选择,范围在0-24MHZ,常用12MHZ。

复位电路得电容一般用10uF,但并不唯一,只要RC所得时间大于两个机器周期即可。

还有其P0内部无上拉电阻,所以在执行输出功能时,外部必须接上拉电阻(一般10K即可)。

2.2.2 实时时钟模块
使用DALLAS公司的实时时钟芯片DS1302。

DS1302是DALLAS公司推出的涓流充电时钟芯片,Vcc1引脚为可编程涓流充电电源,芯片内含有一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。

实时时钟/日历电路提供秒、分、时、日、星期、月、年的信息,每月的天数和闰年的天数可自动调整,时钟操作可通过AM/PM 指示决定采用24 或12 小时格式。

DS1302 与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:RES(复位)、 I/O (数据线)、 SCLK(串行时钟)。

时钟/SRAM 的读/写数据以一个字节或多达31个字节的字符组方式通信。

DS1302 工作时功耗很低,保持数据和时钟信息时功率小于1M,时钟电路如图3所示。

图3 实时时钟模块
2.2.3 温度传感模块
数字温度传感器选用DS18B20,采用单总线通信协议。

DS18B20主要特性有:
1、适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电。

2、独特的单线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯。

3、DS18B20支持多点组网功能,多个DS18B20可以并联在唯一的三线上,实现组网多点测温
4、DS18B20在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只三极管的集成电路内。

5、温范围-55℃~+125℃,在-10~+85℃时精度为0.5℃。

6、可编程的分辨率为9~12位,对应的可分辨温度分别为0.5℃、0.25℃、0.125℃和0.0625℃,可实现高精度测温。

7、在9位分辨率时最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms 内把温度值转换为数字,速度更快。

8、测量结果直接输出数字温度信号,以"一线总线"串行传送给CPU,同时可传送CRC校验码,具有极强的抗干扰纠错能力。

DS1820的操作指令分为ROM操作命令和存储器操作命令:
(1)、ROM操作命令及其含义
Skip ROM指令代码(CCh):此命令执行后的存储器操作将针对在线的所有DS1820。

Alarm Search指令代码(ECh):当温度值高于TH或低于TL中的数值时,此命令可以读出报警的DS1820。

(2)、存储器操作指令代码及其含义
Read Scratchpad指令代码(BEh):读取温度寄存器的温度值。

Copy Scratchpad指令代码(48h):将温度寄存器的数值拷贝到EERAM中,保证温度值不丢失。

Convert T指令代码(44h):启动在线DS1280做温度A/D转换。

Recall E2指令代码(B8h):将EERAM中的数值拷贝到温度寄存器中。

温度测量步骤如下:
(1).Read ROM(33 h),每次对DS1820进行操作之前都要对它进行初始化,主要目的在于确定传感器已经连接到单总线上。

(2).Search ROM(F0h),这条指令使处理器用排除的方法去辨别总线上的DS1820。

(3).Match ROM(55h),只有准确的符合64位ROM序列的DS1820才能响应其后的指令,当然,单点测温时可以使用Skip ROM(CCh)指令来跳过这一步。

(4).Convert T(44h),发完指令后应查询总线上的电平,当电平位高时温度转换完成。

(5).Read Scratchpad(BEh),将读指令发出后,就可从总线上读得表示温度的2字节二进制数。

由于采用单总线数据传输方式,DS18B20的数据I/O均由同一条线完成,因此,对读写的操作时序要求严格。

温度传感电路模块如图4所示。

图4 温度传感模块
2.2.4 液晶显示模块
字符型液晶显示模块是一种专门用于显示字母、数字、符号等点阵式LCD,本设计采用16列*2行的字符型LCD1602带背光的液晶显示屏。

液晶显示模块电路如图9。

各引脚接口说明如表2-1所示:
表2-1 LCD1602引脚接口说明
编号符号引脚说明编号符号引脚说明
1 VSS 电源地9 D
2 数据
2 VDD 电源正极10 D
3 数据
3 VL 液晶显示偏压11 D
4 数据
4 RS 数据/命令选择12 D
5 数据
5 R/W 读/写选择13 D
6 数据
6 E 使能信号14 D
7 数据
7 D0 数据15 BLA 背光源正极
8 D1 数据16 BLK 背光源负极
引脚接口说明:
第1脚:VSS为地电源。

第2脚:VDD接5V正电源。

第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。

第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。

第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。

当RS和R/W 共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。

第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。

第7~14脚:D0~D7为8位双向数据线。

第15脚:背光源正极。

第16脚:背光源负极。

1602LCD的指令说明及时序:
1602液晶模块内部的控制器共有11条控制指令,如表2-2所示:
表2-2 1602LCD的指令说明
序号指令
RS
R/
W
D7 D6 D5 D4 D3 D2 D1 D0
1 清显示0 0 0 0 0 0 0 0 0 1
2 光标返回0 0 0 0 0 0 0 0 1 *
3 置输入模式
0 0 0 0 0 0 0 1
I/
D
S
4 显示开/关控制0 0 0 0 0 0 1 D C B
5 光标或字符移位
0 0 0 0 0 1
S/
C
R/
L
* *
6 置功能0 0 0 0 1 DL N F * *
7 置字符发生存贮器地

0 0 0 1
字符发生存贮器地址
8 置数据存贮器地址0 0 1 显示数据存贮器地址
9 读忙标志或地址0 1 BF 计数器地址
10 写数到CGRAM或
DDRAM)
1 0
要写的数据内容
11 从CGRAM或DDRAM读数 1 1 读出的数据内容
字符控制命令说明:
1602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。

指令1:清显示,指令码01H,光标复位到地址00H位置。

指令2:光标复位,光标返回到地址00H。

指令3:光标和显示模式设置 I/D:光标移动方向,高电平右移,低电平左移 S:屏幕上所有文字是否左移或者右移。

高电平表示有效,低电平则无效。

指令4:显示开关控制。

D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示 C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。

指令5:光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标。

指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时双行显示 F: 低电平时显示5x7的点阵字符,高电平时显示5x10
的点阵字符。

指令7:字符发生器RAM地址设置。

指令8:DDRAM地址设置。

指令9:读忙信号和光标地址 BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。

指令10:写数据。

指令11:读数据。

基本操作时序表,读写操作时序如图所示:
图7 读操作时序
图8 写操作时序
图9 显示模块
2.2.5 矩阵键盘模块
采用4*4矩阵键盘,有8个按键,当扩展功能后对按键需求的增加时,有预留的未定义按键,只需在程序中加入相关的按键处理程序,就可实现新加按键的功能,不需重新制作电路,如图10所示。

图10 矩阵键盘模块图11 发声模块
2.2.6 发声模块
采用三极管推动蜂鸣器发声,利用一个按键来控制闹钟的启动与关闭,并在显示屏上显示N(开)或F(关闭),同时,每个按键都加了按键声音,该模块电路如图11所示。

2.2.7 电源模块
如图12所示,220V交流电经变压器降到9V、桥式整流、电容滤波后由7805三端集成稳压管分别得到+5V电压给整个电路供电,有发光二极管做电源指示灯,方便操作,如图12所示。

图12 电源模块
2.3 系统电路图设计
2.3.1 系统总原理图及连线
系统总原理图及连线如图13所示。

DQ
IO
SCLK RST D0D1D2D3D4D5D6D7
D 0D 1D 2D 3D 4D 5D 6D 7
E N
R W R S SCLK IO RST DQ beef EN R W RS beep
P10P11
P12
P13
P15
P16P10P11P12P13P14P15
VCC
9V
XTAL2
18XTAL1
19
ALE 30EA
31
PSEN 29RST
9
P0.0/AD039P0.1/AD138P0.2/AD237P0.3/AD336P0.4/AD435P0.5/AD534P0.6/AD633P0.7/AD732
P1.0/T21P1.1/T2EX 2P1.23P1.34P1.45P1.56P1.67P1.78
P3.0/RXD 10P3.1/TXD 11P3.2/INT012P3.3/INT113P3.4/T014P3.7/RD
17
P3.6/WR 16P3.5/T115P2.7/A1528P2.0/A821P2.1/A922P2.2/A1023P2.3/A1124P2.4/A1225P2.5/A1326P2.6/A1427U1
AT89C52
RST 5SCLK 7I/O
6X12
X2
3
VCC1
8VCC2
1U3
DS1302
D 7
14
D 613D 512D 411D 310D 29D 18D 07
E 6R W 5R S 4V S S 1V D D 2V E E
3
LCD1
LM016L
28.0
DQ 2VCC
3GND 1
U2
DS18B20
R1
10k
X1
CRYSTAL
FREQ=11.0592MHz C1
22pF
C2
22pF
C3
10uF
R2
10k
X2
CRYSTAL
BAT1
3V
23456789
1
RP1
RESPACK-8
LS1
SOUNDER
Q1
2N5366
R3
10k
R4
10k
VI
1
VO
3G N D
2
U4
7805
C4
10uF
C5
1uF
的备份电源可以保存与时钟同样长的时间
开机滚动画面:每次开机时,都会有滚动出现的开机画面,内容是多功能时钟及
队员名称。

备份电源随时充电:当系统有外部电源供给时,自动对备份电源充电,保持备份电源电能饱满。

温度显示:实时观测当地温度。

2)未来功能扩展
多种开机画面任意选择功能;
增加EEPROM存储器,使闹钟的组数增多,保存时间不再受备份电源的限制;
增加温度报警功能;
增加无线数据传输功能,可对其他多部同型号数字钟同步统一时间、闹钟等设定。

5.2设计心得
本课程设计从软件设计到仿真到硬件制作和调试,我收获不菲。

特别是在仿真编程和硬件调试方面。

在编程过程中一直灌输给自己“编程是一种思想”,一定要用编程的思想去编程,如模块化思想,文件管理思想,头文件和接口函数的思想,设计程序时要考虑到程序的可扩充性,兼容性,可维护性以及重用性,并归纳和总结各种功能算法,各种调度和事件驱动机制等等。

在编程方面有了一定的进步。

在使用仿真软件时得到了一些启示:仿真只是提供一个实现的大概参考,真正的功能实现仍需在实际硬件调试中完善。

第六章参考文献
6.1 参考文献
[1].李朝青.单片机原理及接口技术(第3版).北京:北京航天航空大学出版社,2005
年10月
[2].惠仇.手把手教你51单片机.北京:电子工业出版社,2009年1月
[3].谭浩强.C语言程序设计(第二版)。

北京:清华大学出版社,1999年12月
[4].楼然苗等.单片机实验与课程设计(Proteus仿真版).浙江:浙江大学出版社.2010
年10月
[5].龙威林等.单片机应用入门(AT89S51和AVR).北京:化学工业出版社,2008年9

附录一:程序清单
#include <REG51.H>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit SCLK = P2^0;
sbit IO = P2^1;
sbit RST = P2^2;
sbit beep=P2^4;
sbit K1 = P1^4;
sbit K2 = 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,hide_min_ alarm,hide_hour_alarm; //秒、分、时、日、月、年位闪的计数
uchar stop_watch_value[]={' ','0','0',':','0','0',':','0','0',':','0','0','\0'};
uchar code weekture[7][3]={{"MON"},{"TUE"},{"WEN"},{"THU"},{"FRI"},{"SAT"},{"SUN"}};
char settime_flag,stopwatch_flag,stopwatch_count=0 ;
uchar alarm_TimeString[6]="12:12";
uchar alarm_hour,alarm_min;
uchar alarm_count;
uchar alarm_flag;
uchar min=0;
uchar sec=0;
uchar count_stop=0;
sbit DQ = P2^3; //温度传送数据IO口
char done,done2,count,temp,flag,up_flag,down_flag;
uchar temp_value; //温度值
uchar TempRoom[5],week_value[2];
void alarm_to_str();
void show_time(); //液晶显示程序
/***********1602液晶显示部分子程序****************/
sbit rs= P2^7;
sbit rw= P2^6;
sbit en = P2^5;
sfr DBPort= 0x80;//数据端口
unsigned char LCD_Wait(void) //内部等待函数
{
rs=0;
rw=1;_nop_();
en=1;_nop_();
en=0;
return DBPort;
}
//向LCD写入命令或数据
#define command1602 0 // Command
#define data1602 1 // Data
#define clear_screen1602 0x01
// 清屏
#define home1602 0x02 // 光标返回原点
void lcd_write(bit style, unsigned char input)
{ en=0;
rs=style;
rw=0;_nop_();
DBPort=input; _nop_();//注意顺序
en=1; _nop_();//注意顺序
en=0; _nop_();
LCD_Wait();
}
//设置显示模式
#define show1602 0x04 //显示开
#define hide1602 0x00 //显示关
#define cursor1602 0x02 //显示光标
#define no_cursor1602 0x00 //无光标
#define flash1602 0x01 //光标闪动
#define no_flash1602 0x00 //光标不闪动
void lcd_set_display(unsigned char displaymode)
{ lcd_write(command1602, 0x08|displaymode);
}
//设置输入模式
#define ac_up1602 0x02
#define ac_down1602 0x00 // default
#define move1602 0x01 // 画面可平移
#define no_move1602 0x00 //default
void lcd_set_input(unsigned char inputmode)
{
lcd_write(command1602, 0x04|inputmode);
}
void lcd_initial() //初始化LCD
{
en=0;
lcd_write(command1602,0x38); //8位数据端口,2行显示,5*7点阵
lcd_write(command1602,0x38);
lcd_set_display(show1602|no_cursor1602); //开启显示, 无光标 lcd_write(command1602,clear_screen1602); //清屏
lcd_set_input(ac_up1602|no_move1602); //AC递增, 画面不动}
/*液晶字符输入的位置*/
void GotoXY(unsigned char x, unsigned char y)
{
if(y==0)
lcd_write(command1602,0x80|x);
if(y==1)
lcd_write(command1602,0x80|(x-0x40));
}
void Print(unsigned char *str) //将字符输出到液晶显示
{
while(*str!='\0')
{ lcd_write(data1602,*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 datedata[9];
unsigned char timedata[9];
}SYSTEMTIME; //定义的时间类型
SYSTEMTIME NowTime;
#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 ds1302_input_byte(unsigned char d) //实时时钟写入一字节
{
unsigned char i;
ACC = d;
for(i=8; i>0; i--)
{
IO = ACC0;
SCLK = 1;
SCLK = 0;
ACC = ACC >> 1;
}
}
unsigned char ds1302_output_byte(void) //实时时钟读取一字节
{
unsigned char i;
for(i=8; i>0; i--)
{
ACC = ACC >>1;
ACC7 = IO;
SCLK = 1;
SCLK = 0;
}
return(ACC);
}
void write1302(unsigned char ucAddr, unsigned char ucDa) //ucAddr: DS1302地址, ucData: 要写的数据
{
RST = 0;
SCLK = 0;
RST = 1;
ds1302_input_byte(ucAddr); // 地址,命令
ds1302_input_byte(ucDa); // 写1Byte数据
SCLK = 1;
RST = 0;
}
unsigned char read1302(unsigned char ucAddr) //读取DS1302某地址的数据
{
unsigned char ucData;
RST = 0;
SCLK = 0;
RST = 1;
ds1302_input_byte(ucAddr|0x01); // 地址,命令
ucData = ds1302_output_byte(); // 读1Byte数据
SCLK = 1;
RST = 0;
return(ucData);
}
void ds1302_get_time(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 date_to_string(SYSTEMTIME *Time)
{ //将时间年,月,日,星期数据转换成液晶显示字符串,放到数组里datedata[]
if(hide_year<2) //这里的if,else语句都是判断位闪烁
{ Time->datedata[0] = Time->year/10 + '0';
Time->datedata[1] = Time->year%10 + '0';
}
else
{ Time->datedata[0] = ' ';
Time->datedata[1] = ' ';
}
Time->datedata[2] = '/';
if(hide_month<2)
{ Time->datedata[3] = Time->month/10 + '0';
Time->datedata[4] = Time->month%10 + '0';
}
else
{ Time->datedata[3] = ' ';
Time->datedata[4] = ' ';
}
Time->datedata[5] = '/';
if(hide_day<2)
{ Time->datedata[6] = Time->day/10 + '0';
Time->datedata[7] = Time->day%10 + '0';
}
else
{ Time->datedata[6] = ' ';
Time->datedata[7] = ' ';
}
if(hide_week<2)
{ week_value[0] = Time->week%10 + '0';
}
else
{ week_value[0] = ' ';
}
week_value[1] = '\0';
Time->datedata[8] = '\0'; //字符串末尾加 '\0' ,判断结束字符
}
void time_to_string(SYSTEMTIME *Time)
{ if(hide_hour<2) //将时,分,秒数据转换成液晶显示字符放到数组timedata[];
{ Time->timedata[0] = Time->hour/10 + '0';
Time->timedata[1] = Time->hour%10 + '0';
}
else
{ Time->timedata[0] = ' ';
Time->timedata[1] = ' ';
}
Time->timedata[2] = ':';
if(hide_min<2)
{ Time->timedata[3] = Time->minute/10 + '0';
Time->timedata[4] = Time->minute%10 + '0';
}
else
{ Time->timedata[3] = ' ';
Time->timedata[4] = ' ';
}
Time->timedata[5] = ':';
if(hide_sec<2)
{ Time->timedata[6] = Time->second/10 + '0';
Time->timedata[7] = Time->second%10 + '0';
}
else
{ Time->timedata[6] = ' ';
Time->timedata[7] = ' ';
}
Time->datedata[8] = '\0';
}
void Initial_DS1302(void) //时钟芯片初始化
{ unsigned char second=read1302(ds1302_second);
if(second&0x80) //判断时钟芯片是否关闭
{ write1302(0x8e,0x00); //写入允许
write1302(0x8c,0x11); //以下写入初始化时间
write1302(0x88,0x12); // 日期:11/12/31.星期:6.
write1302(0x86,0x31); // 时间: 23:59:50
write1302(0x8a,0x06);
write1302(0x84,0x23);
write1302(0x82,0x59);
write1302(0x80,0x50);
write1302(0x90,0xAA);//涓流充电,双二极管,4K电阻
write1302(0x8e,0x80); //禁止写入
}
}
/***********ds18b20子程序*************************/
void delay_18B20(unsigned int i) //ds18b20延迟子函数
{ while(i--);
}
void Init_DS18B20(void) //ds18b20初始化函数
{
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);
}
unsigned char read_one_char(void) //ds18b20读一个字节
{ 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);
}
void write_one_char(uchar dat) //ds18b20写一个字节
{ unsigned char i=0;
for (i=8; i>0; i--)
{ DQ = 0;
DQ = dat&0x01;
delay_18B20(5);
DQ = 1;
dat>>=1;
}
}
void read_temp(void) //读取ds18b20当前温度
{ unsigned char a=0;
unsigned char b=0;
unsigned char t=0;
Init_DS18B20();
write_one_char(0xCC); // 跳过读序号列号的操作
write_one_char(0x44); // 启动温度转换
delay_18B20(100); //这个延时很重要
Init_DS18B20();
write_one_char(0xCC); //跳过读序号列号的操作
write_one_char(0xBE); //读取温度寄存器等(共可读9个寄存器)前两个就是温度
delay_18B20(100);
a=read_one_char(); //读取温度值低位
b=read_one_char(); //读取温度值高位
temp_value=b<<4;
temp_value+=(a&0xf0)>>4;
}
void temp_to_string() //温度数据转换成液晶字符显示
{
TempRoom[0]=temp_value/10+'0'; //十位
TempRoom[1]=temp_value%10+'0'; //个位
TempRoom[2]=0xdf; //温度符号
TempRoom[3]='C';
TempRoom[4]='\0';
}
//***********键盘识别程序********************/
void delay_key(uint a)
{ uint b;
for(b=0;b<a;b++);
}
uchar key(void)
{ uchar i,j;
K1=0;
i=P1;
delay_key(200);
j=P1;
K1=1;
if(i==j)
{
switch(j)
{ case 0xee: return(j);break;
case 0xed: return(j);break;
case 0xeb: return(j);break;
case 0xe7: return(j);break;
}
}
K2=0;
i=P1;
delay_key(200);
j=P1;
K2=1;
if(i==j)
{
switch(j)
{ case 0xde: return(j);break;
case 0xdd: return(j);break;
case 0xdb: return(j);break;
case 0xd7: return(j);break;
}
}
}
uchar key_only(void)
{ uchar i,j;
K1=0;
i=P1;
delay_key(200);
j=P1;
K1=1;
if(i==j)
{
if(j==0xee)
{
while(j==0xee)//直到放手
{ delay_key(1000);
j=P1;
}
delay_key(1000);
return(0xee);
}
else
return(0);
}
else
return(0);
}
mode(void ) //模式判断
{
if(key_only()==0xee)
{ beep=0;
delay_key(1000);
beep=1;
++done;
if(done==2)
{done=0;}
while(key_only()==0xee);
}
}
void delay1ms(unsigned int count) //延时ms程序{ unsigned int i,j;
for(i=0;i<count;i++)
for(j=0;j<120;j++);
}
void mdelay(uint delay) //延时子程序
{ uint i;
for(;delay>0;delay--)
{
for(i=0;i<62;i++) //1ms延时.
{;}
}
}
void mode2 (void) //调时模式按键
{if(key()==0xed)
{ done2=1;
count=count+1;
beep=0;
delay_key(1000);
beep=1;
while(key()==0xed);
}
}
void outkey() //跳出调整模式,返回默认显示
{ uchar second;
if(key()==0xe7)
{
beep=0;
delay_key(1000);
beep=1;
count=0;
hide_sec=0,hide_min=0,hide_hour=0,hide_day=0,hide_week=0,hide_month=0,hide_ye ar=0,hide_min_alarm,hide_hour_alarm;
second=read1302(ds1302_second);
write1302(0x8e,0x00); //写入允许
write1302(0x80,second&0x7f);
write1302(0x8E,0x80); //禁止写入
done=0;
done2=0;
while(key()==0xe7);
}
}
///////////////////////////////////////////////////////////////////////////// ///////////////////////////////
void Upkey()//升序按键
{
if(key()==0xdd)
{ beep=0;
delay_key(1000);
beep=1;
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;
case 8:
temp=alarm_min; //读取闹钟分数 temp=temp+1; //闹钟分加1 数
up_flag=1;
if(temp>59) //超过59分,清零
temp=0;
break;
case 9:
temp=alarm_hour; //读取闹钟时数
temp=temp+1; //闹钟时加1
up_flag=1;
if(temp>23) //超过23分,清零
temp=0;
break;
default:break;
}
while(key()==0xdd);
}
}
void Downkey() //降序按键
{
if(key()==0xdb)
{ beep=0;
delay_key(1000);
beep=1;
switch(count)
{case 1:
temp=read1302(ds1302_second);
temp=temp-1;
down_flag=1;
if(temp==0x7f)
temp=0x59;
break;
case 2:
temp=read1302(ds1302_minute);
temp=temp-1;
down_flag=1;
if(temp==-1)
temp=0x59;
break;
case 3:
temp=read1302(ds1302_hour); temp=temp-1;
down_flag=1;
if(temp==-1)
temp=0x23;
break;
case 4:
temp=read1302(ds1302_week); temp=temp-1;
down_flag=1;
if(temp==0)
temp=0x7;;
break;
case 5:
temp=read1302(ds1302_day); temp=temp-1;
down_flag=1;
if(temp==0)
temp=31;
break;
case 6:
temp=read1302(ds1302_month); temp=temp-1;
down_flag=1;
if(temp==0)
temp=12;
break;
case 7:
temp=read1302(ds1302_year); temp=temp-1;
down_flag=1;
if(temp==-1)
temp=0x85;
break;
case 8:
temp=alarm_min;
temp=temp-1;
down_flag=1;
if(temp==-1)
temp=59;
break;
case 9:
temp=alarm_hour;
temp=temp-1;
down_flag=1;
if(temp==-1)
temp=23;
break;
default:break;
}
while(key()==0xdb);
}
}
void keyalarm() //闹钟开启关闭键
{ if (key()==0xeb)
{ alarm_flag=~alarm_flag;
while(key()==0xeb);
}
}
void keydone() //按键功能执行
{
uchar second;
if(flag==0) //关闭时钟,停止计时
{
write1302(0x8e,0x00); //写入允许
temp=read1302(0x80);
write1302(0x80,temp|0x80);
write1302(0x8e,0x80); //禁止写入
flag=1;
}
mode2(); //扫描模式切换按键
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>2)
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();。

相关文档
最新文档