DS1302时钟芯片介绍

DS1302简介

  DS1302 是 DALLAS 公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和 31 字节静态 RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。时钟操作可通过 AM/PM 指示决定采用 24 或 12 小时格式。DS1302 与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三根通信线:

  1. RES复位
  2. I/O数据线
  3. SCLK串行时钟。时钟/RAM 的读/写数据以一个字节或多达31 个字节的字符组方式通信。DS1302 工作时功耗很低保持数据和时钟信息时功率小于1mW。

  DS1302 由 DS1202 改进而来增加了以下的特性:双电源管脚用于主电源和备份电源供应,Vcc1 为可编程涓流充电电源,附加七个字节存储器。它广泛应用于电话、传真、便携式仪器以及电池供电的仪器仪表等产品领域下面。

  主要的性能指标:

  • 实时时钟具有能计算 2100 年之前的秒、分、时、日、星期、月、年的能力,还有闰年调整的能力;
  • 31个8位暂存数据存储RAM;
  • 串行I/O口方式使得管脚数量最少;
  • 宽范围工作电压2.0~5.5V
  • 工作在2.0V时,电流小于300nA;
  • 读/写时钟或RAM数据时有两种传送方式单字节传送和多字节传送字符组方式;
  • 8脚DIP封装或可选的8脚SOIC封装根据表面装配;
  • 简单3线接口;
  • 与TTL兼容Vcc=5V;
  • 可选工业级温度范围-40~+85;
DS1302芯片的管脚
  • VCC2 : 主电源引脚
  • X1、X2 : DS1302 外部晶振引脚,通常需外接32.768K晶振
  • GND : 电源地
  • CE : 使能引脚,也是复位引脚(新版本功能变)。
  • I/O : 串行数据引脚,数据输出或者输入都从这个引脚
  • SCLK : 串行时钟引脚
  • VCC1 : 备用电源

DS1302使用

  操作 DS1302 的大致过程,就是将各种数据写入 DS1302 的寄存器,以设置它当前的时间的格式。然后使 DS1302 开始运作,DS1302 时钟会按照设置情况运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是我们常说的简易电子钟。所以总的来说 DS1302 的操作分2步(显示部分属于液晶显示的内容,不属于DS1302 本身的内容),但是在讲述操作时序之前,我们要先看看寄存器,DS1302 有一个控制寄存器、12个日历、时钟寄存器和31个RAM。

控制寄存器

  控制寄存器用于存放DS1302的控制命令字,DS1302的RST引脚回到高电平后写入的第一个字节就为控制命令。它用于对DS1302读写过程进行控制,格式如下:

  1. 第7位永远都是1
  2. 第6位,1表示RAM,寻址内部存储器地址;0表示CK,寻址内部寄存器
  3. 第5到第1位,为RAM或者寄存器的地址
  4. 最低位,高电平表示RD,即下一步操作将要“读”;低电平表示W,即下一步操作将要“写”。(与AT24C02寄存器类似,这点要理解好)。

  比如要读秒寄存器则命令为 1000 0001,反之写为 1000 0000,要注意其含义。

日历/时钟寄存器

  DS1302 共有12个寄存器,其中有7个与日历、时钟相关,存放的数据为BCD码形式。格式如下:

  • 秒寄存器 : 低四位为秒的个位,高的次三位为秒的十位。最高位 CH 为DS1302 的运行标志,当 CH=0 时,DS1302 内部时钟运行,反之 CH=1 时停止;
  • 小时寄存器 :时寄存器。最高位为 12/24 小时的格式选择位,该位为 1 时表示 12 小时格式。当设置为 12 小时显示格式时,第 5 位的高电平表示下午(PM);而当设置为 24 小时格式时,第5 位位具体的时间数据。
  • 写保护寄存器 : 当该寄存器最高位 WP 为 1 时,DS1302 只读不写,所以要在往 DS1302 写数据之前确保 WP 为 0。
  • 慢充电寄存器(涓细电流充电)寄存器 : 当 DS1302 掉电时,可以马上调用外部电源保护时间数据。该寄存器就是配置备用电源的充电选项的。其中高四位(4 个 TCS)只有在 1010 的情况下才能使用充电选项;低四位的情况与 DS1302 内部电路有关

  BCD码是通过4位二进制码来表示1位十进制中的0~9这10个数码。如下所示:

0 0000 5 0101
1 0001 6 0110
2 0010 7 0111
3 0011 8 1000
4 0100 9 1001

DS1302的读写时序

  在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位(位0)开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。其时序图如下所示

  上图就是DS1302的三个时序:复位时序,单字节写时序,单字节读时序;

  • CE(RST) : 复位时序,即在RST引脚产生一个正脉冲,在整个读写器件,RST 要保持高电平,一次字节读写完毕之后,要注意把RST返回低电平准备下次读写周期;
  • 单字节读时序 : 注意读之前还是要先对寄存器写命令,从最低位开始写;可以看到,写数据是在SCLK的上升沿实现,而读数据在SCLK的下降沿实现。所以,在单字节读时序中,写命令的第八个上升沿结束后紧接着的第八个下降沿就将要读寄存器的第一位数据读到数据线上了!这个就是DS1302操作中最特别的地方。当然读出来的数据也是最低位开始。
  • 单字节写时序 :两个字节的数据配合16个上升沿将数据写入即可。

程序注意事项

  • 要记得在操作DS1302之前关闭写保护
  • 注意用延时来降低单片机的速度以配合器件时序;
  • DS1302 读出来的数据是BCD码形式,要转换成我们习惯的10进制
  • 读取字节之前,将IO设置为输入口,读取完之后,要将其改回输出口;
  • 在写程序的时候,建议实现开辟数组(内存空间)来集中放置DS1302的一系列数据,方便以后扩展键盘输入

硬件设计

  本实验使用到的硬件资源如下:

  1. 动态数码管
  2. DS1302
DS1302时钟模块电路

软件设计

  本实验要实现的功能时:数码管商显示电子时钟时分秒,格式为“XX-XX-XX”

  程序框架如下:

  1. 编写数码管显示功能
  2. 编写DS1302时钟读写功能
  3. 主函数
实物接线图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**************************************************************************************
实验名称:DS1302时钟实验
接线说明:
实验现象:下载程序后,数码管上显示电子时钟时分秒,格式为“XX-XX-XX”
注意事项:
***************************************************************************************/
#include "public.h"
#include "smg.h"
#include "ds1302.h"

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 time_buf[8];

ds1302_init();//初始化DS1302

while(1)
{
ds1302_read_time();
time_buf[0]=gsmg_code[gDS1302_TIME[2]/16];
time_buf[1]=gsmg_code[gDS1302_TIME[2]&0x0f];
time_buf[2]=0x40;
time_buf[3]=gsmg_code[gDS1302_TIME[1]/16];
time_buf[4]=gsmg_code[gDS1302_TIME[1]&0x0f];
time_buf[5]=0x40;
time_buf[6]=gsmg_code[gDS1302_TIME[0]/16];
time_buf[7]=gsmg_code[gDS1302_TIME[0]&0x0f];
smg_display(time_buf,1);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "ds1302.h"
#include "intrins.h"

//---DS1302写入和读取时分秒的地址命令---//
//---秒分时日月周年 最低位读写位;-------//
u8 gREAD_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
u8 gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};

//---DS1302时钟初始化2021年5月20日星期四13点51分47秒。---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
u8 gDS1302_TIME[7] = {0x47, 0x51, 0x13, 0x20, 0x04, 0x05, 0x21};


/*******************************************************************************
* 函 数 名 : ds1302_write_byte
* 函数功能 : DS1302写单字节
* 输 入 : addr:地址/命令
dat:数据
* 输 出 : 无
*******************************************************************************/
void ds1302_write_byte(u8 addr,u8 dat)
{
u8 i=0;

DS1302_RST=0;
_nop_();
DS1302_CLK=0;//CLK低电平
_nop_();
DS1302_RST=1;//RST由低到高变化
_nop_();

for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
{
DS1302_IO=addr&0x01;
addr>>=1;
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;//CLK由低到高产生一个上升沿,从而写入数据
_nop_();
}
for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
{
DS1302_IO=dat&0x01;
dat>>=1;
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;
_nop_();
}
DS1302_RST=0;//RST拉低
_nop_();
}

/*******************************************************************************
* 函 数 名 : ds1302_read_byte
* 函数功能 : DS1302读单字节
* 输 入 : addr:地址/命令
* 输 出 : 读取的数据
*******************************************************************************/
u8 ds1302_read_byte(u8 addr)
{
u8 i=0;
u8 temp=0;
u8 value=0;

DS1302_RST=0;
_nop_();
DS1302_CLK=0;//CLK低电平
_nop_();
DS1302_RST=1;//RST由低到高变化
_nop_();

for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
{
DS1302_IO=addr&0x01;
addr>>=1;
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;//CLK由低到高产生一个上升沿,从而写入数据
_nop_();
}
for(i=0;i<8;i++)//循环8次,每次读1位,先读低位再读高位
{
temp=DS1302_IO;
value=(temp<<7)|(value>>1);//先将value右移1位,然后temp左移7位,最后或运算
DS1302_CLK=1;
_nop_();
DS1302_CLK=0;
_nop_();
}
DS1302_RST=0;//RST拉低
_nop_();
DS1302_CLK=1;//对于实物中,P3.4口没有外接上拉电阻的,此处代码需要添加,使数据口有一个上升沿脉冲。
_nop_();
DS1302_IO = 0;
_nop_();
DS1302_IO = 1;
_nop_();
return value;
}

/*******************************************************************************
* 函 数 名 : ds1302_init
* 函数功能 : DS1302初始化时间
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void ds1302_init(void)
{
u8 i=0;
ds1302_write_byte(0x8E,0X00);
for(i=0;i<7;i++)
{
ds1302_write_byte(gWRITE_RTC_ADDR[i],gDS1302_TIME[i]);
}
ds1302_write_byte(0x8E,0X80);
}

/*******************************************************************************
* 函 数 名 : ds1302_read_time
* 函数功能 : DS1302读取时间
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void ds1302_read_time(void)
{
u8 i=0;
for(i=0;i<7;i++)
{
gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "smg.h"

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

/*******************************************************************************
* 函 数 名 : smg_display
* 函数功能 : 动态数码管显示
* 输 入 : dat:要显示的数据
pos:从左开始第几个位置开始显示,范围1-8
* 输 出 : 无
*******************************************************************************/
void smg_display(u8 dat[],u8 pos)
{
u8 i=0;
u8 pos_temp=pos-1;

for(i=pos_temp;i<8;i++)
{
switch(7-i)//位选
{
case 0: LSC=1;LSB=1;LSA=1;break;
case 1: LSC=1;LSB=1;LSA=0;break;
case 2: LSC=1;LSB=0;LSA=1;break;
case 3: LSC=1;LSB=0;LSA=0;break;
case 4: LSC=0;LSB=1;LSA=1;break;
case 5: LSC=0;LSB=1;LSA=0;break;
case 6: LSC=0;LSB=0;LSA=1;break;
case 7: LSC=0;LSB=0;LSA=0;break;
}
SMG_A_DP_PORT=dat[i-pos_temp];//传送段选数据
delay_10us(100);//延时一段时间,等待显示稳定
SMG_A_DP_PORT=0x00;//消音
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "public.h"

/*******************************************************************************
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1时,大约延时10us
* 输 入 : ten_us
* 输 出 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
while(ten_us--);
}

/*******************************************************************************
* 函 数 名 : delay_ms
* 函数功能 : ms延时函数,ms=1时,大约延时1ms
* 输 入 : ms:ms延时时间
* 输 出 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}

  相关头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef _ds1302_H
#define _ds1302_H

#include "public.h"

//管脚定义
sbit DS1302_RST=P3^5;//复位管脚
sbit DS1302_CLK=P3^6;//时钟管脚
sbit DS1302_IO=P3^4;//数据管脚

//变量声明
extern u8 gDS1302_TIME[7];//存储时间

//函数声明
void ds1302_init(void);
void ds1302_read_time(void);


#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef _smg_H
#define _smg_H

#include "public.h"


#define SMG_A_DP_PORT P0 //使用宏定义数码管段码口

//定义数码管位选信号控制脚
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

extern u8 gsmg_code[17];

void smg_display(u8 dat[],u8 pos);

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;

void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif