目录
- 1.DS1302时钟芯片介绍
-
- [1.1 DS1302简介](#1.1 DS1302简介)
- [1.2 DS1302使用](#1.2 DS1302使用)
-
- [1.2.1 控制存储器](#1.2.1 控制存储器)
- [1.2.2 日历/时钟寄存器](#1.2.2 日历/时钟寄存器)
- [1.2.3 BCD码](#1.2.3 BCD码)
- [1.2.4 DS1302读写时序](#1.2.4 DS1302读写时序)
- 2.硬件设计
- 3.软件设计
-
- [3.1 DS1302写功能](#3.1 DS1302写功能)
- [3.2 DS1302读功能](#3.2 DS1302读功能)
- [3.3 读写时序时间地址定义](#3.3 读写时序时间地址定义)
- [3.4 时间定义](#3.4 时间定义)
- [3.5 初始化](#3.5 初始化)
- [3.6 读取时间](#3.6 读取时间)
- [3.7 转换成BCD码的十进制](#3.7 转换成BCD码的十进制)
- [3.8 static理解](#3.8 static理解)
- 4.整合
- 5.实验现象
1.DS1302时钟芯片介绍

DS1302是美国DALLAS公司生产的,具有涓流充电时钟芯片,涓流充电是指的是:芯片充电完成之后,它会由于内部电源又会继续放电,放电会导致没有达到充满状态,使用涓流充电这种技术,它可以以非常微小电流,慢慢的进行充电,直至达到充电完成状态,而不会说内部放电导致充电未完成。
它是涓流时钟芯片,内部含有一个时钟日历,还有31字节的静态RAM,通过简单的串行接口,跟单片机进行通信,它是一个三线制的SPI的接口。实时时钟电路提供了基本的一些时间信息,比如说,秒、分、时、日、周、年月,这些都可以从芯片获取数据,每个月的天数和闰年的天数可以自动调整,时钟操作可通过AM/PM来设置,我们是使用24小时制时间格式还是12小时制时间格式,我们可以通过寄存器来设置。
DS1302它是一个三线制的API的串行接口,因此它跟单片机连线也需要三根线,复位信号线(CE)也叫使能线,I/O数据线,时钟线(SCLK)这是简易的SPI,而标准的SPI线通常都要有四根,复位也叫使能线,MISO主收从发,MOSI主发从收,时钟线(SCLK),M表示主机,S表示从机,MO主机输出,SO从机输出,MI主机输入,SI从机输入。其中MOSI和MISO可以合成简易的一条线I/O,这个是简易的三线制SPI接口。
那我们DS1302就是这种,所以我们单片机就需要三个IO口与芯片进行连接,DS1302时钟工作时的功耗比较低,功率小于1毫瓦,可以从功率来看,可以利用芯片制作电子时钟,DS1302是DS1202改进版本,它增加了一些其他的功能,比如说DS1302具有双电源管脚,从图中可以看到1号和8号都是电源引脚,有两个电源管脚,1号管脚VCC2这个是主电源,我们整个芯片用这个管脚供电,8号管脚是一个备用的电源,这个电源是当我们主电源没有电的时候,依靠备用电源给这个芯片工作,这样一来我们的时钟依然可以继续保持时钟,等到下一次主电源开电,我们时钟数据不会丢失的,这就像我们的电脑,我们开机后,里面的时钟依然可以继续走时的,一般在主板上有一个纽扣电池,这个纽扣电池相当于给这个备用电源供电的。DS1302的应用非常广泛,它可以在电话传真,电子供电的仪器仪表当中会经常使用到。
1.1 DS1302简介
DS1302特点:
① DS1302实时时钟具有计算2100年之前的,秒、时、分、年、月、日,;例如现在是2026,现在还有74年,时间足够了,还具备闰年调整功能。
② 它还有31个8位暂存存储器RAM,这些数据都可以通过暂存存储器存储,然后读取暂存存储器的数据。
③ 它是以串行通信IO方式,它是简易是三线制的SPI接口,因此与单片机连线的时候管脚比较少,只需要三根。
④ 它的工作电压比较宽广,它能支持2.0 ~ 5.5V的电压范围工作。如果工作电压是2.0V那么它的工作电流小于300NA,功耗低。
⑤ 它还具备读写时钟,有两种传输方式,单字节传送和多字节传送
⑥ 它还具备两种封装,直插式封装DIP,还有一种贴片封装SOIC,体积小
⑦ 它是三线制简单接口,它还兼容TTL电平,而单片机电平也是TTL电平,所以可以直接跟我们单片机IO口直接连接。
⑧ 它的工作温度范围,-40 ~ 85℃

一共有8个管脚。
1:主电源引脚,当1号引脚断电的时候,依靠8号引脚
8:备用电源引脚,当1号引脚断电的时候,可以在8号引脚加一块纽扣电池给8号引脚供电
2、3:外部晶振引脚,通常接32.768K的晶振给时钟提供晶振源
4:接地
5:使能管脚,当该引脚设置高电平的时候,芯片会工作。
6:串行数据输入输出操作引脚
7:时钟引脚
1.2 DS1302使用
操作DS1302过程是将各种数据写入到DS1302寄存器当中,设置当前的时间格式,然后使DS1302开始运作,DS1302时钟会按照我们设置的情况进行运转,在用单片机将寄存器内部数据读取出来,因为DS1302的时间这些数据都是保存在对应的寄存器当中的,那么我们需要读取它的一个时间,那么我们就会从寄存器中读取出来,读出来的数据进行显示就可以通过数码管,液晶屏进行显示,就是我们常常的简易电子时钟。
总的来说,使用DS1302分为两步,1.从DS1302读取时间,2.将读取到的时间进行转换放到数码管或者液晶屏显示。
DS1302内部寄存器, 当我们需要读取DS1302的时钟或者写入对应的命令,我们需要知道对应寄存器的格式和功能,DS1302有一个控制寄存器和一个12个日历时钟寄存器,因为它的时钟数据都会保存在时钟寄存器当中,还有一个31个的RAM相对应的存储空间。
1.2.1 控制存储器
控制寄存器用来存放DS1302控制命令的,DS1302的RST引脚也就是复位引脚如果回到高电平的时候,可以开始工作,第一个要写入的就是控制命令,命令DS1302进行什么样的操作,它用于对于DS1302进行读写控制。

最高位7:永远都是1
6:有一个RAM和一个CK。
RAM:DS1302要对它进行内部存储器的地址操作,因为我们知道DS1302内部有31个RAM存储空间,你是要对 寄存器操作还是RAM进行操作通过这位来进行设置,如果这一位是1的话作为RAM功能的使用,如果这一位为0的话作为CK操作,也就是内部寄存器,我们时钟数据那些都是保存内部寄存器当中的。
所以通常我们这一位设置0
5 ~ 1:如果是作为RAM功能使用的话,就会设置RAM功能的地址,如果是作为寄存器CK功能的话,就会设置寄存器的地址
0:RD和WR表示读写方向位,如果这位设置1的话,就是作为RD的功能,RD就是下一步进行读的操作,如果为0下一步进行写的操作。

这里的D7 ~ D0就是控制寄存器的0 ~ 7位,首先D7始终是设置1的,D6是用来设置RAM和CK,如果设置0表示寄存器,所以秒、分、时、日、月、星期、年,通通都在对应的寄存器当中的,RAM是设置1的,总共有31个RAM,从RAM0 ~ RAM30,D5~D1是对应的地址,比如说我们要对秒操作的话,那么对应的地址是0000,最后一位是读或写,如果是要读秒寄存器这位设置1,如果是要将秒写入到寄存器当中就设置0,所以通过D5 ~ D1这几位分析对应秒、分、时、日、月、星期、年中对应的地址, 通通都知道对应的寄存器地址,知道这些不同时间地址,方面后续从DS1302中去读取时间用的,或者是写入时间进去。
1.2.2 日历/时钟寄存器

DS1302总共有12个寄存器,其中有7个是日历时钟相关的,他们存放的数据是以BCD编码格式进行存入的。
秒寄存器:低四位是秒的个位,它的取值范围是0 ~ 59,比如59,这个9是放在低四位存储的,它是以BCD编码格式存储的,BCD编码是用四位二进制位来表示一个10进制的0 ~ 9,因此我们这个十进制的9用二进制表示就是1001,而前面的三位用来存放秒的十位,比如说这个5,不可能大于5,所以用三位表示十位了,然后D7最高位CH,CH是DS1302运行的标志位,当CH设置0的时候,DS1302是工作的状态,如果这个CH设置1时候,DS1302就会停止工作。
分寄存器:低四位保存分的个位,高三位保存分的十位,最高位是0,取值范围是0 ~ 59。
小时寄存器:D7用来区分是12小时格式还是24小时格式,如果D7位为1的话表示12小时格式,如果为0的话表示24小时格式。当如果D7设置1的时候,就是12小时格式,那么D5位A/P,A表示上午,P表示下午,如果D7设置0的时候,是24小时格式,那么D5就是保存具体时间数据。
写保护寄存器:D0~D6七位全部固定设置0,能够更改的就是D7的WP,这个WP就是写保护位,如果这一位设置1的话那么DS1302是进行写保护了,只能读,不能写,如果我们要将DS1302进行写操作的时候,我们就需要将WP设置1,也就是关闭写保护功能。
慢充电寄存器:当DS1302掉电的时候可以马上调用电源保护时间数据,这个寄存器是用来配置备用电源充电选项,其中高四位是TCS,当TCS高四位设置1010才是充电选项,低四位是与DS1302内部电路有关系,很少用到。
1.2.3 BCD码
BCD码是用四位二进制,表示1位十进制数的0 ~ 9的,例如
0000--------0
0001--------1
0010--------2
0011--------3
0100--------4
0101--------5
0110--------6
0111--------7
1000--------8
1001--------9
例如
数字5->BCD:0101
数字9->BCD:1001
数字12->拆成1和2:0001 0010
1.2.4 DS1302读写时序

写或者读都是从低位开始的。读的时候,是在控制命令发送完成之后的下降沿,继续读取,而写的时候,是在控制命令发送完成之后的上升沿,继续写入数据。
根据前面介绍的它是有三根线的接口,分别是CE、SCLK、IO,那么IO用来数据的传输,SCLK用来产生时钟,CE是使能管脚,当我们要对DS1302时候CE处于高电平的,当对DS1302读写操作完成之后需要对CE拉低电平,等待下一次的读写操作。
在DS1302传输过程中当SCLK处于上升沿时候,我们的数据就会被写进去,它是先写低位在写高位。
读操作
需要将CE设置高电平的时候就可以开始进行读的操作。
第一个字节是控制命令字节,第一个发送控制命令字节给DS1302然后才能进行读数据,最高位是D7,最低位是D0。传输是从最低位到最高位,它是在时钟信号线处于上升沿时候写入进去的,来一个上升沿就写入一位数据。
我们是单字节读所以R/W位设置1,然后时钟信号线来了一个上升沿时候,此时1就会被写入进去了,然后后面的五位,也是每次时钟信号线来一个上升沿就写入进去,从低位开始写,一位一位的写入,然后R/C位是用来设置读寄存器还是读RAM的数据,如果我们是需要读取时钟寄存器所以这一位设置0,如果设置1就是RAM的一个功能,然后最后最高位固定设置1,然后SCLK来一个上升沿此时最高位的1就传入进去了。
当发送完成一个控制命令字节之后,下一个下降沿就开始进行数据读取,它也是从低位开始读,来一个下降沿就开始读。
写操作
需要将CE设置高电平的时候就可以开始进行写的操作。
然后SCLK也会产生时钟周期,这个IO口用来写数据,同样也是从低位,从D0开始写最后到D7,进行写操作,最低位设置0的,进行写操作,然后后面是通过SCLK每次产生一个上升沿的时候进行将IO口数据写进去。
第一个字节写入进去之后,然后后面一个字节是按照SCLK的上升沿写入数据,与读取不同的是,读操作,开始第二个字节的时候,是在SCLK下降沿时候读取数据。
需要区分读写时候,读操作时候,写入控制命令后,第二个字节是数据读取出来,是在时钟信号线在下降沿的时候,而写的操作时候,在第二个字节写入时候是时钟信号线处于上升沿时候,主要区分时钟信号线上升沿和下降沿。
总结:
读时序:先写入命令字节->再在SCLK下降沿的时候读取数据
写时序:先写命令字节->再在SCLK上升沿写入数据。
DS1302具备写保护的功能的,我们在DS1302进行写操作时候,要吧对应的寄存器WP为0,将它的写保护功能关闭。当我们写时序操作完成后,不需要更改数据的时候,我们就可以将写保护功能打开,WP设置1,只能读取,不能修改,不能写。
读写数据时候,是以BCD码格式进行存放的,比如我们要对时钟的小时,写入11小时时候,我们不能以10进制11进行写入,要以BCD码格式写入进去,而BCD码类似于16进制,我们可以将0X11,每一位就是用四位表示,左边1用四位二进制表示 0001 右边就是0001,所以对应的八位二进制是0001 0001,读取出来的数据也是按照16进制进行转换成10进制进行显示。
2.硬件设计


3.软件设计
实现功能:在数码管上显示时分秒,格式为:"XX-XX-XX"
3.1 DS1302写功能
写入数据

写操作时候,刚开始让使能线CE拉低电平,延时时间是纳秒级别,所以通过_nop_()函数延时时间足够了,如果晶振频率是12M那么,一个机器周期延时时间,1微秒。
拉低时钟线SCL,延时,拉高使能线CE,延时,开始写入控制寄存器一字节IO,每次时钟线处于上升沿的时候就写入数据,接着写入第二字节数据IO,也是每次时钟线处于上升沿的时候写入。
从最低位到最高位一位一位的写入。
c
//写字节
void DS1302_write_byte(u8 addr, u8 dat) {
u8 i = 0;
DS1302_RST = 0;
_nop_();
DS1302_CLK = 0;
_nop_();
DS1302_RST = 1; //可以开始写入
_nop_();
//写入第一个字节,控制寄存器
for (i = 0; i < 8; i++) {
DS1302_IO = addr & 0x01;
addr >>= 1;
DS1302_CLK = 1; //上升沿写入
_nop_();
DS1302_CLK = 0; //为下一个上升沿做准备
_nop_();
}
//写入第二个字节,数据
for (i = 0; i < 8; i++) {
DS1302_IO = dat & 0x01;
dat >>= 1;
DS1302_CLK = 1; //上升沿写入
_nop_();
DS1302_CLK = 0; //为下一个上升沿做准备
_nop_();
}
DS1302_RST = 0;
_nop_();
}
3.2 DS1302读功能

读操作时候,第一个字节跟写操作一致,第一个字节是控制寄存器八位数据,先执行写的操作,不同的是第二字节的八位读取是当SCK时钟线处于下降沿的时候才能读取,从最低位到最高位一位一位的读
c
//读字节
u8 DS1302_read_byte(u8 addr) {
u8 i = 0;
u8 temp = 0;
u8 value = 0;
DS1302_RST = 0;
_nop_();
DS1302_CLK = 0;
_nop_();
DS1302_RST = 1; //可以开始写入
_nop_();
//写入第一个字节,控制寄存器
for (i = 0; i < 8; i++) {
DS1302_IO = addr & 0x01;
addr >>= 1;
DS1302_CLK = 1; //上升沿写入
_nop_();
DS1302_CLK = 0; //为下一个上升沿做准备
_nop_();
}
//读取数据
for (i = 0; i < 8; i++) {
temp = DS1302_IO;
value = (temp << 7) | (value >> 1);
DS1302_CLK = 1; //为下降沿做准备
_nop_();
DS1302_CLK = 0; //下降沿
_nop_();
}
DS1302_RST = 0;
_nop_();
return value;
}
3.3 读写时序时间地址定义

写时序,根据秒、分、时、日、月、周、年,地址数组定义,如果是写的话,最低位就是0,例如秒的地址是1000 0000就对应的0X80,例如年的地址是1000 1100对应的16进制是0X8C

而读时序最低位D0是1,其他D1 ~ D7跟写时序地址一样,所以在原本写时序的秒、分、时、日、月、周、年,对应的地址+1
3.4 时间定义


时间定义,定义当前的时间,2026年2月22日17时34分25秒,第7周,用来给DS1302传入当前的时间,当DS1302主电源关闭,备用电源用纽扣电池供电的时候,内部会继续以当前传入的时间继续走时,当我开启主电源的时候,就是走时后的结果。
3.5 初始化
初始化写功能
开始时候需要对写保护设置,需要修改,写入数据,这样数据可以修改数据了
然后按照先写入控制寄存器地址,然后写入控制寄存器对应的时间设置
例如:秒寄存器设置地址,设置秒时间写入...
最后设置打开写保护,数据只允许读,不允许修改
c
//初始化DS1302
void ds1302_init(void) {
u8 i = 0;
DS1302_write_byte(0X8E, 0X00); //关闭写保护-----可以修改可以写入数据
for (i = 0; i < DS1302_TIME_REG_NUM; i++) {
DS1302_write_byte(gWRITE_RTC_ADDR[i], gDS1302_TIME[i]);
}
DS1302_write_byte(0X8E, 0X80); //打开写保护-----不能修改不可以写入数据
}
3.6 读取时间
按照格式:写入对应的时间地址,读取对应时间
c
//读取时间
void ds1302_read_time(u8* display_time) {
u8 i = 0;
if (display_time == ((void*)0)) {
return;
}
for (i = 0; i < DS1302_TIME_REG_NUM; i++) {
display_time[i] = DS1302_read_byte(gREAD_RTC_ADDR[i]);
}
}
3.7 转换成BCD码的十进制
c
#define BCD_TO_DEC(x) (((x >> 4) * 10) + (x & 0x0F)) //转换成10进制
例如:
- 原始值: 0x17
- 十六进制 0x17 对应的二进制是: 0001 0111
- 它是一个 BCD 码,高4位 0001 代表十位 1 ,低4位 0111 代表个位 7 ,合起来就是十进制的 17。
-
第一步:提取十位 x >> 4
-
对 0001 0111 执行右移4位操作:
右移4位后,原来的高4位 0001 被移到了低4位的位置,而高4位补0。
- 结果 0000 0001 对应的十进制是 1 。
-
乘以10: 1 * 10 = 10 。
-
第二步:提取个位 x & 0x0F
-
先看 0x0F 的二进制: 0000 1111 。
-
对 0001 0111 和 0000 1111 执行按位与( & )操作:
按位与的规则是:只有对应位都为1时,结果才为1,否则为0。这样就只保留了原数的低4位。
-
结果 0000 0111 对应的十进制是 7 。
-
第三步:合并结果
- 十位贡献的值: 10
- 个位贡献的值: 7
- 合并: 10 + 7 = 17
这样,我们就把 BCD 码 0x17 成功转换成了十进制数 17 。
再举一个例子: x = 0x59
- 原始值: 0x59 → 二进制 0101 1001 (BCD码,代表59)
- 提取十位: 0101 1001 >> 4 = 0000 0101 → 十进制 5 → 5 * 10 = 50
- 提取个位: 0101 1001 & 0000 1111 = 0000 1001 → 十进制 9
- 合并: 50 + 9 = 59
反过来:十进制转 BCD( x = 23 )
宏: #define DEC_TO_BCD(x) (((x / 10) << 4) | (x % 10))
- 处理十位:
- 23 / 10 = 2 → 二进制 0000 0010
- 左移4位: 0000 0010 << 4 = 0010 0000 → 十六进制 0x20
- 处理个位:
- 23 % 10 = 3 → 二进制 0000 0011 → 十六进制 0x03
- 合并:
-
对 0x20 ( 0010 0000 ) 和 0x03 ( 0000 0011 ) 执行按位或( | ):
-
结果 0010 0011 就是十六进制 0x23 ,即 DS1302 需要的 BCD 码。
3.8 static理解
c
void DS1302_write_byte
u8 DS1302_read_byte
如上,如果这两个两函数,只在ds1302源文件中被别的函数使用,被如下两函数使用:
c
void ds1302_init(void)
void ds1302_read_time(u8* display_time)
如上,这两个函数使用,并不被其他源文件函数调用,所以需要修改成如下:
c
static void DS1302_write_byte(u8 addr, u8 dat)
static u8 DS1302_read_byte(u8 addr)
在原来的基础上加上static,用来修饰它的访问权限,只能被当前自己的ds1302源文件其他函数使用,不能被别的源文件使用或者不能被别的源文件其他函数里面去调用,强调了访问权限,让内部数据不被外部任意修改,提高代码的可维护性和安全性。
除此之外,还有数组
c
static u8 gWRITE_RTC_ADDR[DS1302_TIME_REG_NUM]
static u8 gREAD_RTC_ADDR[DS1302_TIME_REG_NUM]
static u8 gDS1302_TIME[DS1302_TIME_REG_NUM]
这三个数组定义也只能在ds1302源文件使用,设置了访问权限,所以也加了static
然后在.h头文件中,表示ds1302这个文件外设代码,只提供了以下两个函数的使用。
c
void ds1302_init(void)
void ds1302_read_time(u8* display_time)
而下面两个函数没有加在头文件声明中,表示不对外访问。只能在自己的ds1302.c源文件使用,或者被ds1302.c源文件其他函数调用,不能被别的源文件使用。
c
static void DS1302_write_byte(u8 addr, u8 dat)
static u8 DS1302_read_byte(u8 addr)
c
#ifndef _ds1302_H
#define _ds1302_H
#include "public.h"
#define DS1302_TIME_REG_NUM 7
sbit DS1302_IO = P3^4;
sbit DS1302_RST = P3^5;
sbit DS1302_CLK = P3^6;
//提供对外访问的接口函数
//初始化DS1302
void ds1302_init(void);
//读取时间
void ds1302_read_time(u8* display_time);
#endif
4.整合
main.c
c
#include "public.h"
#include "smg.h"
#include "ds1302.h"
#define BCD_TO_DEC(x) (((x >> 4) * 10) + (x & 0x0F)) //转换成10进制
void main() {
//时间数组大小,定义于ds1302.h
u8 display_time[DS1302_TIME_REG_NUM];
u8 smg_time[8];
u8 cnt = 0;
ds1302_init();
while(1) {
//display_time时间存储顺序秒分时日月周年
ds1302_read_time(display_time);
smg_time[0] = BCD_TO_DEC(display_time[2]) / 10; //小时十位
smg_time[1] = BCD_TO_DEC(display_time[2]) % 10; //小时个位
smg_time[2] = 0x40;
smg_time[3] = BCD_TO_DEC(display_time[1]) / 10; //分十位
smg_time[4] = BCD_TO_DEC(display_time[1]) % 10; //分个位
smg_time[5] = 0x40;
smg_time[6] = BCD_TO_DEC(display_time[0]) / 10; //秒十位
smg_time[7] = BCD_TO_DEC(display_time[0]) % 10; //秒个位
smg_display(smg_time);
}
}
smg.c
c
#include "smg.h"
u8 gsmg_code[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //共阴数码管0-F
void smg_display(u8 dat[]) {
u8 i = 0;
for (i = 0; i < 8; i++)
{
switch(i) {
case 0: LSA = 0; LSB = 0; LSC = 0; break;
case 1: LSA = 1; LSB = 0; LSC = 0; break;
case 2: LSA = 0; LSB = 1; LSC = 0; break;
case 3: LSA = 1; LSB = 1; LSC = 0; break;
case 4: LSA = 0; LSB = 0; LSC = 1; break;
case 5: LSA = 1; LSB = 0; LSC = 1; break;
case 6: LSA = 0; LSB = 1; LSC = 1; break;
case 7: LSA = 1; LSB = 1; LSC = 1; break;
}
if (dat[i] == 0x40) {
SMG_A_DP_POST = 0x40; //断码 '-'
}
else {
SMG_A_DP_POST = gsmg_code[dat[i]]; //数字
}
delay_10us(100);
SMG_A_DP_POST = 0X00;//消影
}
}
smg.h
c
#ifndef _smg_H
#define _smg_H
#include "public.h"
#define SMG_A_DP_POST P0
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
extern u8 gsmg_code[17];
void smg_display(u8 dat[]);
#endif
ds1302.c
c
#include "ds1302.h"
#include "intrins.h"
//写寄存器地址
static u8 gWRITE_RTC_ADDR[DS1302_TIME_REG_NUM] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C}; //秒分时日月周年
//读寄存器地址
static u8 gREAD_RTC_ADDR[DS1302_TIME_REG_NUM] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8B, 0x8D}; //秒分时日月周年
//当前时间
static u8 gDS1302_TIME[DS1302_TIME_REG_NUM] = {0X25, 0X34, 0X17, 0X23, 0X02, 0X07, 0X26}; //26年2月22日17时34分25秒 第7个星期
//写字节
static void DS1302_write_byte(u8 addr, u8 dat) {
u8 i = 0;
DS1302_RST = 0;
_nop_();
DS1302_CLK = 0;
_nop_();
DS1302_RST = 1; //可以开始写入
_nop_();
//写入第一个字节,控制寄存器
for (i = 0; i < 8; i++) {
DS1302_IO = addr & 0x01;
addr >>= 1;
DS1302_CLK = 1; //上升沿写入
_nop_();
DS1302_CLK = 0; //为下一个上升沿做准备
_nop_();
}
//写入第二个字节,数据
for (i = 0; i < 8; i++) {
DS1302_IO = dat & 0x01;
dat >>= 1;
DS1302_CLK = 1; //上升沿写入
_nop_();
DS1302_CLK = 0; //为下一个上升沿做准备
_nop_();
}
DS1302_RST = 0;
_nop_();
}
//读字节
static u8 DS1302_read_byte(u8 addr) {
u8 i = 0;
u8 temp = 0;
u8 value = 0;
DS1302_RST = 0;
_nop_();
DS1302_CLK = 0;
_nop_();
DS1302_RST = 1; //可以开始写入
_nop_();
//写入第一个字节,控制寄存器
for (i = 0; i < 8; i++) {
DS1302_IO = addr & 0x01;
addr >>= 1;
DS1302_CLK = 1; //上升沿写入
_nop_();
DS1302_CLK = 0; //为下一个上升沿做准备
_nop_();
}
//读取数据
for (i = 0; i < 8; i++) {
temp = DS1302_IO;
value = (temp << 7) | (value >> 1);
DS1302_CLK = 1; //为下降沿做准备
_nop_();
DS1302_CLK = 0; //下降沿
_nop_();
}
DS1302_RST = 0;
_nop_();
return value;
}
//初始化DS1302
void ds1302_init(void) {
u8 i = 0;
DS1302_write_byte(0X8E, 0X00); //关闭写保护-----可以修改可以写入数据
for (i = 0; i < DS1302_TIME_REG_NUM; i++) {
DS1302_write_byte(gWRITE_RTC_ADDR[i], gDS1302_TIME[i]);
}
DS1302_write_byte(0X8E, 0X80); //打开写保护-----不能修改不可以写入数据
}
//读取时间
void ds1302_read_time(u8* display_time) {
u8 i = 0;
if (display_time == ((void*)0)) {
return;
}
for (i = 0; i < DS1302_TIME_REG_NUM; i++) {
display_time[i] = DS1302_read_byte(gREAD_RTC_ADDR[i]);
}
}
ds1302.h
c
#ifndef _ds1302_H
#define _ds1302_H
#include "public.h"
#define DS1302_TIME_REG_NUM 7
sbit DS1302_IO = P3^4;
sbit DS1302_RST = P3^5;
sbit DS1302_CLK = P3^6;
//提供对外访问的接口函数
//初始化DS1302
void ds1302_init(void);
//读取时间
void ds1302_read_time(u8* display_time);
#endif
public.c
c
#include "public.h"
void delay_10us(u16 us) {
while(us--);
}
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
public.h
c
#ifndef _public_H
#define _public_H
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
void delay_10us(u16 us);
void delay_ms(u16 ms);
#endif
5.实验现象
ds1302运行结果