本文主要参考ds1302中文技术手册
引脚接入:
CLK P2^7
DAT P2^6
RST P2^5
原理图剖析
ds1302的原理图很简单很好理解,X1、X2接入晶振给模块提供振荡电路;VCC2作为供电电源、VCC1作为备用电源在VCC2不供电的时候保持续航;SCLK是时钟引脚、DAT(IO)是数据交流引脚、RST(CE)是复位引脚。
注意:RST置1才允许数据交流,但RST置1之前要先保证CLK为1。
时钟\控制寄存器
在代码中,我们实际需要用到的有:控制寄存器(control)、秒、分、时、日、月、年(sec、min、hr、day、month、year)。研究这些寄存器的字节数据可以发现,无非是7位实际地址+1位读写位的形式。控制寄存器的主要功能是是否禁用写保护,也就是在要进行写入数据的时候要先设置控制寄存器为0x00,写完后设置为0x80。
写地址{0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}
读地址{0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}
时序
可以看到,一个周期内,要先进行寄存器地址的写入,再进行读或者写。周期内读的话,就是RST为高电平,CLK下降沿读入一个数据;写的话,就是RST为高电平,CLK上升沿写入一个新数据。但在实际操作中,我读和写都是在下降沿才能成功,这就很无语。
代码
ds1302.c
#include "ds1302.h"
sbit CLK=P2^7;
sbit DAT=P2^6;
sbit RST=P2^5;
void ds1302_write(u8 addrs,u8 datas)
{
u8 i = 0;
RST = 0;
_nop_();
CLK = 0;//只有在SCLK为低电平时,才能将RST置为高电平
_nop_();
RST = 1;//RST为高,允许数据交换
_nop_();
for(i=0;i<=7;i++)
{
DAT = addrs & 0x01;
addrs = addrs>>1;
CLK = 1;
_nop_();
CLK = 0;
_nop_();
}
for(i=0;i<=7;i++)
{
DAT = datas & 0x01;
datas = datas>>1;
CLK = 1;
_nop_();
CLK = 0;
_nop_();
}
RST = 0;//RST为低,结束数据传输
_nop_();
}
u8 ds1302_read(u8 addrs)
{
u8 ret = 0,i =0,datas = 0;
RST = 0;
_nop_();
CLK = 0;//只有在SCLK为低电平时,才能将RST置为高电平
_nop_();
RST = 1;//RST为高,允许数据交换
_nop_();
for(i=0;i<=7;i++)
{
DAT = addrs & 0x01;
addrs = addrs>>1;
CLK = 1;
_nop_();
CLK = 0;
_nop_();
}
_nop_();
for(i=0;i<=7;i++)
{
datas = DAT;
ret = (ret>>1)|(datas<<7);
CLK = 1;
_nop_();
CLK = 0;
_nop_();
}
RST = 0;//RST为低,结束数据传输
_nop_();
return ret;
}
void ds1302_init(u8 *times)
{
//秒分时日月周年
u8 code addrs[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
u8 i =0;
ds1302_write(0x8E,0x00);//关闭写保护,这样才能写入数据
for(i=0;i<7;i++)
ds1302_write(addrs[i],times[i]);
ds1302_write(0x8E,0x80);//打开写保护
}
void time_read(u8 *times,u8 *display_data)
{
u8 n = 0;
u8 code addrs[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
for (n=0; n<7; n++)//读取7个字节的时钟信号:分秒时日月周年
{
times[n] = ds1302_read(addrs[n]);
}
datapros(times,display_data);
}
ds1302.h
#ifndef __DS1302__H
#define __DS1302__H
#include "reg52.h"
#include "delay.h"
#include "datapros.h"
void ds1302_init(u8 *times);
void time_read(u8 *times,u8 *display_data);
#endif