自制单片机之十三……时钟IC_DS1302

发布者:BlissfulSpirit最新更新时间:2016-12-15 来源: eefocus关键字:自制单片机  时钟IC  DS1302 手机看文章 扫描二维码
随时随地手机看文章

  在网上看了很久,发现初学者最有兴趣的就是DS1302时钟电路,也很自然,它是个做出来就让你觉得最实用的电路了,但实际上制做上并不简单,首先你要让你的显示部分(不管是数码管还是LCD)调试通过。然后把DS1302接好,调试正确了才能在成功显示时间和日期。下面我们就来说说DS1302的用法。
  DS1302的图如下:

  DS1302是美国DALLAS公司推出的一种高性能、低功耗的实时时钟芯片,附加31字节静态RAM,采用SPI三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。实时时钟可提供秒、分、时、日、星期、月和年,一个月小与31天时可以自动调整,且具有闰年补偿功能。工作电压宽达2.5~5.5V。采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后背电源进行涓细电流充电的能力。
  下面是标准的接线电路图:

    各引脚功能如下:
引脚号            名称                     功能
①                     Vcc2                   主电源
②、③               X1,X2       接32768Hz晶振
④                     GND                    地线
⑤                     RST                    复位
⑥                       I/0               数据输入输出
⑦                     SCLK             串行时钟
⑧                      Vccl                 后备电源

  DS1302有关日历、时间的寄存器共有12个,其中有7个寄存器(读时81h~8Dh,写时80h~8Ch)是存放秒、分,小时、日、月、年、周数据的,存放的数据格式为BCD码形式
  它的内部时间寄存器如下:

这张表呢是DS1302内部的7个与时间、日期有关的寄存器图和一个写保护寄存器,我们要做的就是将初始设置的时间、日期数据写入这几个寄存器,然后再不断地读取这几个寄存器来获取实时时间和日期。这几个寄存器的说明如下:
1、秒寄存器(81h、80h)的位7定义为时钟暂停标志(CH)。当初始上电时该位置为1,时钟振荡器停止,DS1302处于低功耗状态;只有将秒寄存器的该位置改写为0时,时钟才能开始运行。
2、小时寄存器(85h、84h)的位7用于定义DS1302是运行于12小时模式还是24小时模式。当为高时,选择12小时模式。在12小时模式时,位5是 ,当为1时,表示PM。在24小时模式时,位5是第二个10小时位
3、控制寄存器(8Fh、8Eh)的位7是写保护位(WP),其它7位均置为0。在任何的对时钟和RAM的写操作之前,WP位必须为0。当WP位为1时,写保护位防止对任一寄存器的写操作。也就是说在电路上电的初始态WP是1,这时是不能改写上面任何一个时间寄存器的,只有首先将WP改写为0,才能进行其它寄存器的写操作。

下面来说说如果对DS1302进行读写:
  上面的电路图可以看出,除了电源和接地,DS1302只有三根线和单片机连接,SCLK、I/O和RST(有的也写成CE),先看时序图:

  DS1302的数据读写是通过I/O串行进行的。当进行一次读写操作时最少得读写两个字节,第一个字节是控制字节,就是一个命令,告诉DS1302是读还是写操作,是对RAM还是对CLOK寄存器操作,以及操作的地址。第二个字节就是要读或写的数据了。
  我们先看单字节写:在进行操作之前先得将CE(也可说是RST)置高电平,然后单片机将控制字的位0放到I/O上,当I/O的数据稳定后,将SCLK置高电平,DS1302检测到SCLK的上升沿后就将I/O上的数据读取,然后单片机将SCLK置为低电平,再将控制字的位1放到I/O上,如此反复,将一个字节控制字的8个位传给DS1302。接下来就是传一个字节的数据给DS1302,当传完数据后,单片机将CE置为低电平,操作结束。
  单字节读操作的一开始写控制字的过程和上面的单字节写操作是一样,但是单字节读操作在写控制字的最后一个位,SCLK还在高电平时,DS1302就将数据放到I/O上,单片机将SCLK置为低电平后数据锁存,单机机就可以读取I/O上的数据。如此反复,将一个字节的数据读入单片机。读与写操作的不同就在于,写操作是在SCLK低电平时单片机将数据放到IO上,当SCLK上升沿时,DS1302读取。而读操作是在SCLK高电平时DS1302放数据到IO上,将SCLK置为低电平后,单片机就可从IO上读取数据。
  现在我们来看看控制字的内容:


位0就是读写位,当位0为1时,就是告诉DS1302,下面是进行读出操作,而当位0为0时就是写入操作。
位0-位5是要进行操作的DS1302寄存器地址。
位6就是告诉DS1302,是要对RAM进行操作还是对CLK寄存器进行操作,0就是对时间寄存器操作,一般我们都是对时间寄存器进行操作。
位7就是固定的1。为什么是1呢。还记得上面说的单字节读操作吗?在写控制字的最后一个位也就是位7时,DS1302已将它的寄存器数据位0放到IO上了,要是控制字的位7是0的话,DS1302就无法将它的随后的数据放到IO上了。
  这样你现在就知道为什么控制字80H是写秒寄存器,而80H是读秒寄存器了吧!80H换成二进制就是10000000。而81H的二进制就是10000001,一个是写操作,另一个是读操作嘛!

好!我们现在来总结一下,如何对DS1302进行操作。
①首先要通过8eH将写保护去掉,这样我们才能将日期,时间的初值写时各个寄存器。
②然后就可以对80H、82H、84H、86H、88H、8AH、8CH进行初值的写入。同时也通过秒寄存器将位7的CH值改成0,这样DS1302就开始走时运行了。
③将写保护寄存器再写为80H,防止误改写寄存器的值。
④不断读取80H-8CH的值,将它们格式化后显示到LCD或数码管上。

 这个电路本身还是很简单的,见下图:


元件就是DS1302、一个晶振,一个104的瓷片电源滤波电容。和98ATS52连接的线有电源5V(Vcc)、接地(GND)、SCLK、I/O、CE(RST)。共5根线。

  按上面的电路图焊好后别忘了检测一直是否有短路,我检测了我的电路,电源和地之间的静态电流为50uA。
  接上单片机后如图:

软件部分的编写,首先得把显示部分调试好,我用的是LCD12864,是已经调试好的可调用子程序12863put.c

下面DS1302程序部分,是根据网上用的非常多的一个DS1302子程序修改的:

/**************************/
/* ds1302实时时钟C程序    */
/**************************/

#include < reg52.h>
#define uchar unsigned char

sbit T_CLK = P1^0; /*实时时钟时钟线引脚 */
sbit T_IO = P1^1; /*实时时钟数据线引脚 */
sbit T_RST = P1^2; /*实时时钟复位线引脚 */

sbit ACC0=ACC^0;
sbit ACC7=ACC^7;
void Init1302(void);
void v_W1302(uchar ucAddr, uchar ucDa);
uchar uc_R1302(uchar ucAddr);
void v_Set1302(uchar *pSecDa);
void v_Get1302(uchar ucCurtime[]);

/****************************************** 
* 名称: v_RTInputByte
* 说明: 
* 功能: 往DS1302写入1Byte数据
* 调用:
* 输入: ucDa 写入的数据 
* 返回值: 无
******************************************/
void v_WTInputByte(uchar ucDa) 

uchar i;
ACC= ucDa;
for(i=8; i>0; i--)
{
T_IO = ACC0; //相当于汇编中的 RRC 
T_CLK = 1; 
T_CLK = 0;
ACC =ACC>> 1; 
}
}

/***************************************** 
* 名称: uchar uc_RTOutputByte
* 说明: 
* 功能: 从DS1302读取1Byte数据
* 调用: 
* 输入: 
* 返回值: ACC
******************************************/
uchar uc_RTOutputByte(void) 

uchar i;
for(i=8; i>0; i--)
{
ACC = ACC>>1; //相当于汇编中的 RRC
ACC7 = T_IO;
T_CLK = 1; 
T_CLK = 0;

return(ACC); 
}

/******************************************** 
* 名称: v_W1302
* 说明: 先写地址,后写数据
* 功能: 往DS1302写入数据
* 调用: v_RTInputByte() 
* 输入: ucAddr: 控制字, ucDa: 要写的数据
* 返回值: 无
*********************************************/
void v_W1302(uchar ucAddr, uchar ucDa)
{
//OE=0;
T_RST = 0;
T_CLK = 0;
T_RST = 1;
v_WTInputByte(ucAddr); /* 地址,命令 */
v_WTInputByte(ucDa); /* 写1Byte数据*/
T_CLK = 1;
T_RST =0;
//OE=1; 
}

/********************************************* 
* 名称: uc_R1302
* 说明: 先写地址,后读数据
* 功能: 读取DS1302某地址的数据
* 调用: v_RTInputByte() , uc_RTOutputByte()
* 输入: ucAddr: 控制字
* 返回值: ucDa :读取的数据
**********************************************/
uchar uc_R1302(uchar ucAddr)
{
uchar ucDa;
//OE=0;
T_RST = 0;
T_CLK = 0;
T_RST = 1;
v_WTInputByte(ucAddr); /* 地址,命令 */
ucDa = uc_RTOutputByte(); /* 读1Byte数据 */
T_CLK = 1;
T_RST =0;
// OE=1;
return(ucDa);
}

/*************************************** 
*
* 名称: v_Set1302
* 说明: 
* 功能: 设置初始时间
* 调用: v_W1302() 
* 输入: pSecDa: 初始时间数组首地址。 
* 返回值: 无
****************************************/
void v_Set1302(uchar *pSecDa) 
{
uchar i;
uchar ucAddr = 0x80; 
v_W1302(0x8e,0x00); // 控制命令,WP=0,允许写操作
for(i =7;i>0;i--)

v_W1302(ucAddr,*pSecDa); //秒 分 时 日 月 星期 年 
pSecDa++;
ucAddr +=2;
}
v_W1302(0x8e,0x80); // 控制命令,WP=1,写保护
}

/********************************************** 
* 名称: v_Get1302
* 说明: 
* 功能: 读取DS1302当前时间
* 调用: uc_R1302() 
* 输入: ucCurtime: 保存当前时间数据的数组地址 
* 返回值: 无
***********************************************/
void v_Get1302(uchar ucCurtime[]) 
{
uchar i;
uchar ucAddr = 0x81;
for (i=0;i<7;i++)
      {
         ucCurtime[i] = uc_R1302(ucAddr);//格式为: 秒 分 时 日 月 星期 年 
         ucAddr += 2;
      }
}

/*******************************************
* 名称: Init1302
* 说明: 
* 功能: 初始化DS1302
* 调用: 
* 输入: 
* 返回值: 无
*******************************************/
void Init1302(void)
{
v_W1302(0x8e,0x00); //控制写入WP=0
v_W1302(0x90,0xa5); //辅助电源充电命令
v_W1302(0x80,0x00); //写秒
v_W1302(0x82,0x59); //写分
v_W1302(0x84,0x10); //写时
v_W1302(0x86,0x07); //写日
v_W1302(0x88,0x05); //写月
v_W1302(0x8a,0x04); //写星期
v_W1302(0x8c,0x09); //写年 
v_W1302(0x8e,0x80); //写保护WP=1
}

下面是主程序部分DS1302main.c,将DS1302、LCD12864子程序整合在一起。

#include
#define uchar unsigned char
#define uint unsigned int
extern void LcmClear( void );       //清屏
extern void LcmInit( void );        //初始化
extern void LcmPutstr( uchar row,uchar y,uchar * str ); //在设定位置显示字符串
extern void Init1302(void);//初始化DS1302
extern void v_Get1302(uchar ucCurtime[]);//获取DS1302内的7个字节时间数据存入数组中
uchar getTimebuf[7];//用于存放获取的时间数据
uchar setTimebuf[7];//用于存放要设置的时间日期数据
uchar time[]={" : : "};//时间格式字符串
uchar date[]={"20 - - "};//日期格式字符串
uchar daylist[]={"SunMonTueWedThuFriSat"};//星期字符列表
uchar day1[]={"   "};//星期格式字符串


//****************
//    主函数
//****************

void Main( void )
{ uchar i,j,k;
Init1302();//初始化DS1302
LcmInit(); //初始化LCD12864
LcmClear();
LcmPutstr( 0,28,"DS1302 TEST" );
LcmPutstr( 3,24,"");
LcmPutstr( 5,0,"BLOG:http://" );
LcmPutstr( 6,18,"hi.baidu.com/txz01" );
LcmPutstr( 7,8,"Email:TXZ001@139.com" );
while(1)
    {
      v_Get1302(getTimebuf);//获取DS1302内7个时间日期数据存入数组getTimebuf[].
      time[6]=(getTimebuf[0])/16+48;//格式化时间秒
      time[7]=(getTimebuf[0])%16+48;
      time[3]=(getTimebuf[1])/16+48;//格式化时间分
      time[4]=(getTimebuf[1])%16+48;
      time[0]=(getTimebuf[2])/16+48;//格式化时间小时
      time[1]=(getTimebuf[2])%16+48;
      date[8]=getTimebuf[3]/16+48;//格式化日期日
      date[9]=getTimebuf[3]%16+48;
      date[5]=getTimebuf[4]/16+48;//格式化日期月
      date[6]=getTimebuf[4]%16+48;
      date[2]=getTimebuf[6]/16+48;//格式化日期年
      date[3]=getTimebuf[6]%16+48;
      day1[0]=daylist[(getTimebuf[5]%10)*3];//格式化星期
      day1[1]=daylist[(getTimebuf[5]%10)*3+1];
      day1[2]=daylist[(getTimebuf[5]%10)*3+2];
   LcmPutstr( 2,0,date);//显示日期
   LcmPutstr(2,96,day1);//显示星期
   LcmPutstr( 3,36,time);//显示时间
      for(i=0;i<5;i++)
for(j=0;j<255;j++) 
    for(k=0;k<255;k++);
    }
}

用Keil将程序编译:

生成HEX文件后下载到AT89S52板上,运行,如下图:

结果并不好,时间数据在乱跳,但我发现秒数基本是一秒一跳,分钟也是一分一跳的,就是说DS1302在正常工作,说明CH修改已正常,只是读取数据不正常。在分析了软件部分都正常后,决定将数据线缩短。因为原来的数据线大约有10多CM。再加上从接口到52芯片的转接线总共大约有30CM长了。因此得将DS1302的数据线剪短,再直接插在52芯片的接口P1上试试。如下图:

上面这张图是示意图,是晚上补做的图,没插电。其结果是基本上能看出是在我设定的时间初值上每秒跳一下,但还是很频繁地有乱跳现象。我于是就想那么短的线难道数据还不稳?莫非是阻抗太高了,使数据线容易受到干扰?我想起那个标准电路中三路数据线是有上拉10K的电阻的,当时我想反正常P1内部也有上拉电阻,就没外接电阻了。现在想起来是不是P1内部的上拉电阻值太大了,使读取数据受外界干扰。我想起我P0上外接的不就是10K的排阻吗?于是将DS1302的数据线插到了P0口的P0.0、P0.1、P0.2上,在软件里修改了数据线接口的定义,然后烧写运行。哈哈!现在就非常稳定了。

总结,其实DS1302的数据引线略长一点是没关系的,但那三根数据线上的上拉电阻一定得要,否则阻抗太高就很容易造成数据传输不稳定,受到外界干扰,造成显示数据乱跳的现象。
  DS1302的调试不是很容易,因为当你做好了电路,写好了软件烧写运行后,如果DS1302没有任何反应。你就不太好判断问题出在哪,是晶振不起振还是程序改写秒寄存器CH没成功?我就因此换过晶振,换过DS1302,反复修改过程序。因为DS1302在初始接上电源时晶振是不起振的,就无法检测晶振电路的好坏。这是这个电路的难点。


关键字:自制单片机  时钟IC  DS1302 引用地址:自制单片机之十三……时钟IC_DS1302

上一篇:自制单片机之十四……通俗的电子基础课
下一篇:自制单片机之十二……AT89C2051烧写器的制做与调试

推荐阅读最新更新时间:2024-03-16 15:25

SPI时钟芯片DS1302的应用
DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31 8的用于临时性存放数据的RAM寄存器。采用普通32.768kHz晶振!RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操
[单片机]
DS1302与PIC16F877接口程序
;================================================ ;DS1302控制子程序 ;================================================= ;程序包括:DS1302初始化,时间数据写入和读取程序 ;DS1302_init ;init ds1302 ;Set_DS1302 ;set time to ds1302 ;Get_DS1302 ;get time from ds1302 ;================================================= ;============== 1302定
[单片机]
AVR控制的DS1302 C程序
下面是DS1302的AVR 单片机 的C程序,这个程序结构很不错。但是站长没有调试这个AVR单片机的程序。 #define ds1302_rst PC0 #define ds1302_io PC1 #define ds1302_sclk PC2 #define set_ds1302_rst_ddr() DDRC|=1 ds1302_rst #define set_ds1302_rst() PORTC|=1 ds1302_rst #define clr_ds1302_rst() PORTC&=~(1 ds1302_rst) #define set_ds1302_io_ddr() DDRC|=1 ds1302_io #define
[单片机]
PIC16F877A DS1302芯片驱动
#include pic.h typedef unsigned char uchar; typedef unsigned int uint; #define rs_h PORTC|=0x01 #define rs_l PORTC&=0xfe #define rw_h PORTC|=0x02 #define rw_l PORTC&=0xfd #define en_h PORTC|=0x04 #define en_l PORTC&=0xfb #define rst_h PORTC|=0x08 #define rst_l PORTC&=0xf7 #define sck_h PORTC|=0
[单片机]
PIC16F877A <font color='red'>DS1302</font>芯片驱动
单片机万年历(LCD12864、DS18B20、DS1302
一、简介 此电路由AT89C51最小电路板和LCD12864显示模块、DS18B20温度模块、DS1302时钟模块组成。 主要的功能是:显示实时的时间和当前的温度。 二、运行效果 三、部分代码 /*想要更多项目私wo!!!*/ #include reg51.h #include DS18B20.H #include DS1302.H #include LCD12864.H unsigned char j ; sbit KEY1=P3^2; sbit KEY2=P3^3; sbit KEY3=P3^4; sbit LED1=P3^5; sbit LED2=P3^6; sbit LED3=P3^7; ucha
[单片机]
<font color='red'>单片机</font>万年历(LCD12864、DS18B20、<font color='red'>DS1302</font>)
PIC单片机DS1302时钟
大家好,通过前一期的学习,我们已经对ICD2 仿真烧写器和增强型PIC 实验板的使用方法及学习方式有所了解与熟悉,学会了如何用单片机来控制发光管、继电器、蜂鸣器、按键、数码管、RS232串口、步进电机、温度传感器、I2C 总线、SPI 总线等资源,体会到了学习板的易用性与易学性,这一期我们将介绍市面上常见的时钟芯片DS1302 的应用。 一、DS1302时钟芯片简介 DS1302 是DALLAS 公司推出的涓流充电时钟芯片,内含一个实时时钟/ 日历和31 字节静态RAM,可以通过串行接口与单片机进行通信。实时时钟/ 日历电路提供秒、分、时、日、星期、月、年的信息,每个月的天数和闰年的天数可自动调整,时钟操作可通过AM/PM
[单片机]
PIC<font color='red'>单片机</font>之<font color='red'>DS1302</font><font color='red'>时钟</font>
51单片机-时钟芯片DS1302
在许多电子设备中,通常会进行一些与时间有关的控制,如果用系统的定时器来设计时钟的话,偶然的掉电或晶振的误差都会造成时间的错乱,更糟糕的是,若完全用程序设计时钟还会占用大量的系统资源,从而严重影响系统的其他功能。为此,很多芯片制造公司都设计出了各种各样的实时时钟芯片。 常见的时钟芯片有两种。 一种是体积非常小的表贴是元件,通常用在高端小型手持式仪器或设备中。这种芯片在使用时需要外接备份电池和外部晶振,电池用来保持主系统在意外时为时钟芯片供电,外部晶振用来给时钟芯片所必须的震荡来源。 另一种体积相对较大,一般为直插式,它的内部有可充电锂电池,同时内部还集成了32.768KHZ的标准晶振。 DS18B20是由
[单片机]
51<font color='red'>单片机</font>-<font color='red'>时钟</font>芯片<font color='red'>DS1302</font>
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved