读者可先根据项目实现功能自行尝试编写程序,当遇到困难时再参考本例中的源代码, 认真反省为何自己没有找到合适的解决办法。如果可能, 建议读者详细阅读DS12C887 的数据手册, 进 一步挖掘它的其他特性, 设计出更有意义的功能。
在许多电子设备中,通常会进行一些与时间有关的控制,如果用系统的定时器来设计时钟的话,偶然的掉电或晶振的误差都会造成时间的错乱,更糟糕的是,若完全用程序设计时钟还会占用大量的系统资源,从而严重影响系统的其他功能。为此,很多芯片制造公司都设计出了各种各样的实时时钟芯片。
常见的时钟芯片有两种。
一种是体积非常小的表贴式元件,通常用在高端小型手持式仪器或设备中,如手机、PDA、MP4播放器、GPS导航仪等。这种芯片在使用时需要外接备份电池和外部晶振,电池用来保持主系统在意外掉电时为时钟芯片供电,外部晶振用来提供时钟芯片所必须的振荡来源,标准频率为32.768kHz,这种芯片体积小、所以引脚也很少,操作起来非常方便,比如,DALLAS公司生产的串行实时时钟芯片DS1302, DS1337, DS1338, DS1390和并行的DS1558等,大家可上美芯官方网站查看更多型号,其中我们日常用的较多的是DS1302。
另一种体积相对较大,一般为直插式,它的内部集成有可充电锥电池,同时内部还集成了32.768kHz的标准晶振,一旦设定好时间,即使系统的主电源掉电,该时钟芯片仍然可以靠它内部集成的悝电池走数年,当系统重新上电时,又可为锤电池重新充电,这样一来可以非常有效地保持时间的连续性,使用时非常方便。这类芯片如DALLAS公司生产DS12887,DS12887A,DS12B887,DS12C887等。本例讲解的芯片是具有代表性且综合性能较高的DS12C887时钟芯片。
DS12C887时钟芯片能够自动产生世纪、年、月、日、时、分、秒等时间信息,其内部有世纪寄存器,从而利用硬件电路解决“千年”问题。DS12C887 中自带锥电池,外部掉电时,其内部时间信息还能够保持10年之久。对千一天内的时间记录,有 12小时制和24小时制两种模式。在12小时制模式中,用 AM和PM区分上午和下午;芯片内部时间的存储方式也有两种,一种用二进制数表示,另一种是用BCD码表示。DS12C887时钟芯片中带有128BRAM,其中11BRAM用来存储时间信息;4BRAM用来存储DS12C887的控制信息,称为控制寄存器;113B 通用RAM供用户使用。此外,用户还可对DS12C887进行编程以实现多种方波输出,并可对其内部的三路中断通过软件进行屏蔽。该芯片内部有一个精密的温度补偿电路用来监视Vee的状态,如果检测到主电源故障,该器件可以自动切换到备用电源供电。VBAeKUP引脚用于支持可充电电池或超级电容,内部包括一个始终有效的涓流充电器。DS12C887可以通过一个多路复用的单字节接口访问,该接口支持Intel和Motorola模式。DS12C887将它自已与石英晶体和电池集成在一起。
DS12C887特性描述如下:
DS12C887时钟芯片的引脚排列如下图所示,与单片机典型接口如下下图所示。各引脚功能说明如下:
1(MOT)—总线操作时序选择端。它有两种总线工作模式,即Motorola和Intel模式。当MOT接Vcc时,选用Motorola模式;当MOT接GND或悬空时,选用Intel模式。本文主要讨论Intel模式。
2,3,16,20,22(NC)—空引脚。
4~11(AD0~AD7)—复用地址数据总线。该总线采用时分复用技术,在总线周期的前半部分,出现在AD0~AD7上的是地址信息,可用以选通DS12C887内的RAM,总线周期的后半部分出现在AD0~AD7上的是数据信息。
12,24(GND,Vcc)一系统电源接入端。其中Vcc接+5V输入,GND接地,当Vcc输入为+5V时,用户可以访问DS12C887内RAM中的数据,并可对其进行读/写操作;当Vee的输入小于+4.25V时,禁止用户对内部RAM进行读/写操作,此时用户不能正确获取芯片内的时间信息;当Vcc的输入小于+3V时,DS12C887会自动将电源切换到内部自带的梩电池上,以保证内部的电路能够正常工作。
13(c/s/)—芯片片选端。低电平有效。
14(AS)—地址选通输入端。在进行读/写操作时,AS的上升沿将AD0~AD7上出现的地址信息锁存到DS12C887上,而下一个下降沿清除AD0~AD7上的地址信息,不论CS是否有效,DS12C887都将执行该操作。
15(R/W)—读/写输入端。该引脚也有两种工作模式,当MOT接Vcc时,R/W_工作在Motorola模式。此时,该引脚的作用是区分读操作还是写操作,R/W_高电平时为读操作,R/W_低电平时为写操作;当MOT接GND时,该引脚工作在Intel模式,此时该引脚作为写允许输入,即WriteEnable,此信号的上升沿锁存数据。
17(DS)—数据选择或读输入脚。该引脚有两种工作模式,当MOT接Vcc时,选用Motorola工作模式,此时,每个总线周期的后一部分的DS为高电平,称为数据选通。在读操作中,DS的上升沿使DS12C887将内部数据送往总线AD0~AD7上,以供外部读取。在写操作中,DS的下降沿将使总线AD0~AD7上的数据锁存在DS12C887中。当MOT接GND时,选用Intel工作模式,此时该引脚是读允许输入引脚,即ReadEnable。
18(RESET)—芯片复位引脚。低电平有效,通常将该引脚接Vcc即可。
19(I/R/Q/)一中断请求输出。低电平有效,用做处理器的中断申请输入。只要引起中断的状态位置位,并且相应中断使能位也置位,I/R/Q/将一直保持低电平,处理器程序通常读取C寄存器来清除I/R/Q/引脚输出,RESET引脚也会清除未处理的中断。没有中断发生时,I/R/Q/为高阻状态,可将多个中断器件接到一条I/R/Q/总线上,只要它们均为漏极开路输出即可。I/R/Q/引脚为漏极开路输出,需要使用一个外接上拉电阻与Vcc相连。
23(SQW)一方波输出引脚。当供电电压Vcc大于4.25V时,SQW引脚可输出方波,
此时,用户可以通过对控制寄存器编程来得到13种方波信号的输出。
DS12C887时钟芯片各寄存器定义如下表所示。
UIP—更新(UIP)位,用来标志芯片是否即将进行更新。当UIP=1时,更新即将开始;当UIP=0时,表示至少在244µs内芯片不会更新,此时,时钟、日历和闹钟信息可以通过读/写相应的字节获得并设置。UIP位为只读位且不受复位信号(RESET)的影响。通过把寄存器B中的SET位设置为1,可以禁止更新并将UIP位清0。
DV2,DV1, DV0—这三位用来开/关晶体振荡器和复位分频器。
当[DV0,DV1,DV2]=[010]时,晶体振荡器开启且保持时钟运行。
当[DV0,DV1,DV2]=[11X]时,晶体振荡器开启,但分频保持复位状态。
RS3,RS2,RS1,RS0—速率选择位。这4个速率选择位用来选择15级分频器的13种分频之一,或禁止分频器输出。按照所选择的频率产生方波输出(SWQ引脚)和/或一个周期性中断。用户可进行如下操作:
(1) 设置周期中断允许位(PIE);
(2) 设置方波输出允许位(SQWE);
(3) 两位同时设置为有效,并且设置频率;
(4) 两者都禁止。
下表为周期性中断率和方波中断频率表,该表列出了可通过RS寄存器选择的周期中断的频率和方波的频率。这 4 个可读/写位不受复位信号的影响。
SET—SET=0,芯片更新正常进行;SET=1,芯片更新被禁止。SET位可读/写,并不受复位信号的影响。
PIE—PIE=0,禁止周期中断输出到IRQ;PIE=1,允许周期中断输出到IRQ。
AIE—AIE=0,禁止闹钟中断输出到IRQ;AIE=1,允许闹钟中断输出到蔽5。
UIE—UIE=0,禁止更新结束中断输出到IRQ;UIE=1,允许更新结束中断输出到IRQ。此位在复位或设置SET位为高时清0。
SQWE—SQWE=0,SQW引脚为低电平;SQWE=1,SQW输出设定频率的方波。
DM—DM=0,设置寄存器存储数据格式为BCD码格式;DM=1,设置寄存器存储数据格式为二进制数格式,此位不受复位信号影响。
24/12—为1,24小时制;为0,12小时制。
DSE一夏令时允许标志。在四月的第一个星期日的1:59:59AM,时钟调到3:00:00AM;在十月的最后一个星期日的1:59:59AM, 时钟调到1:00: 00AM。
IRQF—中断请求标志。
当以下4种情况中有一种或几种发生时,IRQF置高:
•PF=PIE=1•AF=AIE=1
•UF=UIE=1 `
•IRQF=PF•PIE+AF•AIE+UF•UIE
IRQF—旦为高,IRQ脚输出低电平。
所有标志位在读寄存器C或复位后清0。
PF—周期中断标志。
AF—闹钟中断标志。
UF—更新中断标志。
VRT—VRT=0,表示内置电池能量耗尽,,此时RAM中数据的正确性就不能保证了。
Mototola和Intel模式总线读/写时序图下面三个图。
(1) 本章仅介绍了DS12C887时钟芯片的主要内容,具体操作时要求读者务必到其官方网站下载完整资料仔细阅读。
(2) 有一点要特别申明,大家在自己连接电路时,首先要确认自己连接好的电路是绝对正确的,否则无论如何修改程序也难以得出预期的结果。
(3) 按键可按如下方式设置:功能键为S2、数值增大键为S13、数值减小键为S17、闹钟查看键为S4。
(4) 存储器数据格式有两种模式:BCD码和二进制数。为方便操作,我们设置为二进制数模式,通过设置寄存器B中的DM=1来选择二进制数模式。
(5) DS12C887时钟芯片在出厂时内部振荡器均为关闭状态,这是为了避免在开始使用前消耗捚电池能量。寄存器A的第4~6位为010时,打开振荡器并使计时链可用。为11X组合时,(DV2=1,DV1=1,DV0=X)打开振荡器,但振荡器的计时链保持为复位状态。这三位的其他组合方式均使振荡器关闭。因此,在首次操作DS12C887芯片时,必须首先设置这三位的状态。
#include
#include "delay_Sum.h"
#include "1602_Sum.h"
#include "ds12c887_Sum.h"
#include "74hc595_Sum.h"
#include "delay_Sum.h"
#include "boardinit_Sum.h"
uchar num,time,moshi,moshi1,flag,kaiqi,c;
char shi,fen,miao,shiji,nian,yue,ri,xingqi,ashi,afen,amiao,kaiz;
void didi()
{
write_byte_74hc595(0x02); //蜂鸣器关
delay(500);
write_byte_74hc595(0x06); //蜂鸣器响
delay(500);
}
void main()
{
boardinit(); //XK-2板卡初始化
lcdinit_1602(); //1602液晶初始化
init_ds12c887();//ds12c887 初始化
EA=1;
IT0=1;
EX0=1;
c=read_ds_ds12c887(0x0c);
//write_ds_ds12c887(0x0b,0x26);//如果开启设置 控制器B,数据为BCD码,时间为24小时制,产生闹钟中断
num=0;time=0;moshi=0;moshi1=0;flag=0;kaiqi=0;
//**************************************************************************
/* 该部分是调试程序时候使用,当出现时间日期乱码时候,可以尝试重新写入给定数据
判断是否是内部保存数据长时间未用导致出现乱码。
write_ds_ds12c887(4,9); //小时寄存器 写 9点
write_ds_ds12c887(2,27); //分钟寄存器 写 27分
write_ds_ds12c887(0,25); //秒钟寄存器 写 25秒
write_ds_ds12c887(6,7); //星期寄存器写 7 星期天
write_ds_ds12c887(9,11); //年寄存器 写 11年
write_ds_ds12c887(8,8); //月份寄存器 写 8月
write_ds_ds12c887(7,7); //日期寄存器 写 7号
*/
//***************************************************************************
//在1602上写时间的框架
Disp_1602(1,1," 20 - - ",16);
Disp_1602(1,2," 00 - - ",16);
while(1)
{
keyscan();
if(flag==1)
{
didi();
}
if(kaiqi==0)
{
//****************************************************************************************
//读出DS12C887芯片里的 时间,日期 数据
miao = read_ds_ds12c887(0x00); //读 秒
fen = read_ds_ds12c887(0x02); //读 分
shi = read_ds_ds12c887(0x04); //读 时
ri = read_ds_ds12c887(0x07); //读 日
yue = read_ds_ds12c887(0x08); //读 月
nian = read_ds_ds12c887(0x09); //读 年
xingqi= read_ds_ds12c887(0x06); //读 星期
shiji = read_ds_ds12c887(0x10); //读 实际 保存实际数据,内部不自加,只是当做存储器用
kaiz = read_ds_ds12c887(0x0e); //读 闹钟 标志位
ashi = read_ds_ds12c887(0x05); //读 闹钟 时
afen = read_ds_ds12c887(0x03); //读 闹钟 分
amiao = read_ds_ds12c887(0x01); //读 闹钟 秒
//****************************************************************************************
//把读出的时间,日期数据, 写到1602液晶显示
write_twoline_ds12c887_1602(5,shi); //写 时
write_twoline_ds12c887_1602(8,fen); //写 分
write_twoline_ds12c887_1602(11,miao); //写 秒
write_oneline_ds12c887_1602(10,ri); //写 日
write_oneline_ds12c887_1602(7,yue); //写 月
write_oneline_ds12c887_1602(4,nian); //写 年
write_oneline_ds12c887_1602(14,xingqi);//写 星期
write_oneline_ds12c887_1602(2,shiji); //写 世纪
write_twoline_ds12c887_1602(2,kaiz); //写 闹钟开启位
//*****************************************************************************************
}
}
}
void exter() interrupt 0
{
flag=1; //开外部中断 0
}
/*
*文 件 名: delay_Sum.c
* 晶 振 : 11.0592MHz
*创 建 者: Sumjess
*创建时间: 2019.4.18
*/
#include "delay_Sum.h"
void delay(uint z) //毫秒
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void delay_smg(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=50;y>0;y--);
}
void delay_ds18b20(uint time) //液晶专用
{
uint i,j;
for(i=0;i
/*
* 文 件 名:1602led.c.c
* 芯 片:1602液晶
* 晶 振:11.0592MHz
* 创 建 者:XK
* 创建日期:2011.8.6
* 修 改 者:Sumjess
* 修改日期:2019.5.29
* 功能描述:1602,写数据函数
*/
#include
#include "1602_Sum.h"
#include "delay_Sum.h"
#include "74hc595_Sum.h"
sbit rs_1602=P2^5; //1602rs信号
sbit rw_1602=P2^6; //1602rw信号
sbit e=P2^4;
/*
void write_zl(uchar zl)
{
rs=0;
P0=zl;
delay(5);
e=1;
delay(5);
e=0;
}
void write_sj(uchar sj)
{
rs=1;
P0=sj;
delay(5);
e=1;
delay(5);
e=0;
} */
void write_order_1602(uchar order_data)
{
e=0;
rs_1602=0;
P0=order_data;
e=1;
delay(1);
e=0;
}
void write_data_1602(uchar data_1602)
{
e=0;
rs_1602=1;
P0=data_1602;
e=1;
delay(1);
e=0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//功能:按指定位置显示一串字符
///////////////////////////////////////////////////////////////
//输入: ////////
//列显示地址x_1602(取值范围1-16) ////////
//行显示地址y_1602(取值范围1-2), ////////
//指定字符串指针*p_1602, ////////
//要显示的字符个数count_1602 (取值范围1-16) ////////
///////////////////////////////////////////////////////////////
// 子函数使用例子: Disp_1602(1,1," TEMP: . ",16); //在1602第一行第一列写16个字符,既字符串数据
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Disp_1602(unsigned char x_1602,unsigned char y_1602,unsigned char *p_1602,unsigned char count_1602)
{
unsigned char i;
for(i=0;i
#include
#include "ds12c887_Sum.h"
#include "74hc595_Sum.h"
#include "delay_Sum.h"
sbit dsds_ds12c887=P1^0;
sbit dsrw_ds12c887=P1^1;
sbit dsas_ds12c887=P1^2;
//sbit dscs_ds12c887=P1^4; //该板卡的dscs 信号是通过74hc595提供 该使能信号需在下面子函数处理
sbit dsirq_ds12c887=P3^2;
void init_ds12c887() //初始化函数
{
write_ds_ds12c887(0x0b,0x26);//如果开启设置 控制器B,数据为BCD码,时间为24小时制,产生闹钟中断
write_ds_ds12c887(0x0a,0x20); //打开时钟震荡
amiao=read_ds_ds12c887(1);
afen=read_ds_ds12c887(3);
ashi=read_ds_ds12c887(5); //读DS12C887 闹钟 时,分,秒 即当进入闹钟设置状态下,显示当前设定闹钟的时间
}
void set_alarm_ds12c887(uchar ashi,uchar afen,uchar amiao)
{
write_ds_ds12c887(1,amiao);
write_ds_ds12c887(3,afen);
write_ds_ds12c887(5,ashi); //在DS12C887闹钟寄存器中写入 闹钟时间 函数
}
void write_ds_ds12c887(uchar add_ds12c887,uchar data_ds12c887)
{
delay(1);
write_byte_74hc595(0X02);// ds12c887de 使能端 cs=0 1602背光打开
delay(1);
dsas_ds12c887=1;
dsds_ds12c887=1;
dsrw_ds12c887=1;
delay(1);
P0=add_ds12c887;
delay(1);
dsas_ds12c887=0;
dsrw_ds12c887=0;
P0=data_ds12c887;
dsrw_ds12c887=1;
dsas_ds12c887=1;
delay(1);
write_byte_74hc595(0X12);// ds12c887de 使能端 cs=1 1602背光打开
delay(1);
}
//ds12c887 写数据子函数
//------------------------------------------------------------------------------
uchar read_ds_ds12c887(uchar add_ds12c887)
{
uchar ds_date;
dsas_ds12c887=1;
dsds_ds12c887=1;
dsrw_ds12c887=1;
delay(1);
write_byte_74hc595(0X02);// ds12c887de 使能端 cs=0 1602背光打开
delay(1);
P0=add_ds12c887;
delay(1);
dsas_ds12c887=0;
dsds_ds12c887=0;
P0=0XFF;
ds_date=P0;
dsds_ds12c887=1;
dsas_ds12c887=1;
write_byte_74hc595(0X12); // ds12c887de 使能端 cs=1 1602背光打开
return ds_date;
}
/*////////////////////////////////////////////////////////////////////////////////////////
* 文 件 名:74hc595_Sum.c
* 芯 片:74hc595
* 晶 振:11.0592MHz
* 创 建 者:XK
* 创建日期:2011.8.6
* 修 改 者:Sumjess
* 修改日期:2019.4.18
////////////////////////////////////////////////////////////////////////////////////////
* 功能描述:给74hc595芯片写一字节数据
芯片使用说明:SI 是串行数据输入口,当SRCK来一个上升沿时,写入此刻SI上的数据,到移位寄存器,同时左移一位。
当八位数据发送完时,给RCK一个上升沿,移位寄存器的八位数据输出到QA-Q7
芯片比较;74hc164和74hc595功能,基本相同。其根本的区别在RCK上,只有当RCK来上升沿输出QA-Q7,才会更新。
其次,595的驱动能力更强。
芯片使用;
第一步:目的:将要准备输入的位数据移入74HC595数据输入端上。方法:送位数据到_595。
第二步:目的:将位数据逐位移入74HC595,即数据串入方法:SH_CP产生一上升沿,将DS上的数据移入74HC595移位寄存器中,先送低位,后送高位。
第三步:目的:并行输出数据。即数据并出方法:ST_CP产生一上升沿,将由DS上已移入数据寄存器中的数据送入到输出锁存器。
说明: 从上可分析:从SH_CP产生一上升沿(移入数据)和ST_CP产生一上升沿(输出数据)是二个独立过程,实际应用时互不干扰。即可输出数据的 同时移入数据。 [2]
////////////////////////////////////////////////////////////////////////////////////////
//芯片作用;74hc573使能端送高电平,为的是让LED所连接的74hc573芯片使能,才可能驱动LED。//
////////////////////////////////////////////////////////////////////////////////////////*/
#include
#include "74hc595_Sum.h"
#include "delay_Sum.h"
sbit data_595si =P1^0; //595数据输入口
sbit clk_595srck =P1^4; //595数据写进移位寄存器时钟输入口
sbit e_595rck =P2^3; //595移位寄存器,数据送输出的时钟
void write_byte_74hc595(uchar dat) //74hc164芯片写一字节数据函数
{
uchar i;
e_595rck=0; //给595RCK低电平
for(i=0;i<8;i++)
{
clk_595srck=0; //给595SRCK低电平
data_595si=(dat<
/*
* 文 件 名:boardinit_Sum.c
* 芯 片:
* 晶 振:11.0592MHz
* 创 建 者:XK
* 创建日期:2011.9.17
* 修 改 者:Sumjess
* 修改日期:2019.4.18
* 功能描述:EASYBOARD xk-2 v2.0开发板把卡硬件初始化。
*/
#include
#include "74hc595_Sum.h"
#include "boardinit_Sum.h"
sbit duanxuan=P2^5; // 数码管,的段选使能,1 通 0关
sbit weixuan=P2^6; // 数码管,的位选使能,1 通 0关
void boardinit()
{
duanxuan=1; //数码管,的段选使能,1 通 0关
weixuan=1; //数码管,的位选使能,1 通 0关
P0=0x00; //P0口送清零,关LED数码管的必要数据准备。
duanxuan=0; //数码管,的段选使能,1 通 0关
weixuan=0; //数码管,的位选使能,1 通 0关
write_byte_74hc595(0x40); //选通8位发光二极管控制芯片u6(74HC573)锁存器的使能端
P1=0xff; //P1口送全1,关8位发光二极管的必要数据准备。
//写完上句理论上8个发光二级管都会熄灭,因74HC595数据口复用了P1.0故P1.0的二极管可能不能被熄灭。
write_byte_74hc595(0x00); //关闭8位发光二极管控制芯片u6(74HC573)锁存器的使能端,把P1口数据保存(既关发光二极管)。
}