前言
此时钟芯片具有RTC功能,实时北京时间,还有年月日记录的功能。能够做万能闹钟,万年历等产品。此芯片只需一颗纽扣电池作备用电源就可实现断电继续工作,也可以接DC电源工作。本篇文章加上数据手册就可以熟练使用DS1302。
内部结构介绍
- 图片左边字节是保护位(1位) + 固定的地址(6位)+ 读写位(1位)称为命令字等具体看下面讲解
- 右边字节是存放的时钟数据也称为数据字,具体看下面讲解
- 左边的地址寄存器与右边的数据寄存器 一 一 对应
- 通讯方法:单片机先发送地址给芯片,再读 / 写相对应的数据寄存器
- 左边地址寄存器分为两部分:时钟地址和RAM地址
– A时钟部分就是实时时钟的地址,也是这个芯片最主要的功能,常用时、分、秒地址
– B部分则是31个字节的RAM的地址,想存放什么就存放什么,只要有纽扣电池并且有电就不会丢失
通讯过程
MCU先发送一个命令字也就是地址给DS1302,再发送一个数据字或接收一个数据字。则写/读数据成功。
命令字
- 位7为写保护位,逻辑1表示可以写,为0表示禁止写
- 表示地址的有6位:
– 位6为逻辑0表示操作 A时钟 / 日历数据,为逻辑1表示操作 RAM 数据
– A4到A0也是地址位,A部分时钟的地址,看上面的图5,还有B部分31个字节的RAM的地址也在图5下方,不过最后的全1是连续写入的方式,一般不使用 - 位0是读写位,逻辑0表示写操作,逻辑1表示读操作
- 命令字就是以上这些信息,下面的表3帮我们总结出了命令字的数据,也就是读/写两种操作
数据字
- 左边READ和WRITE是读和写命令字节,先发送命令字再发送数据字就可以操作芯片了
- BIT0 - BIT7是数据字,存储格式是BCD码
- 右边是数据字的数据范围,也就是秒,分,时等等对应的范围
- 图中 0 表示不使用此位
- 图中寄存器分别为秒、分、时、号、月、星期、年
- 小时寄存器的BIT7表示12小时制(1)或24小时制(0)
– 12小时制时的BIT5表示AM(0)或PM(1)
– 24 小时制模式下, 由于BCD码的存储方式,BIT3-0只能表示个位数0点-9点,那么BIT5-4就表示十位数1或2,也就是十几点,或二十几点。
– 一旦 12/24 改变,小时数据必须被重新初始化. - CH为时钟暂停位,1表示时钟停止,0表示时钟开始。上电时状态未定义
- 写保护寄存器:WP为写保护位,置0表示可以写操作,置1表示不能写操作。上电时状态未定义
复位和时钟控制
- 选择32.768kHz 的晶振可通过引脚 2 和 3(X1 和 X2)直接连接至 DS1302。所选晶振规定的负载电容量(CL)应当为 6pF。
- 前面的命令字和数据字都是通过IO引脚发送的,SCK和RST引脚也有作用
- 通过单片机将 RST拉至高电平才能开始数据传送。其次,RST 为低电平时可以中止数据传送,且 I/O引脚变为高阻。
- 数据输入时,在时钟的上升沿数据必须有效,而数据位在时钟的下降沿输出。也就是说,在SCK为0时,单片机输出一个电平信号,再将SCK置1,置0,此时数据发送完成
- 上电初始化时,RST保持低电平复位,SCK保持低电平;通信时,将RST置1停止复位,再按照图3通信即可
- 数据传输,低位在前高位在后
- 读时序:跟随在输入读命令字节的 8 个 SCLK 周期之后,在随后的 8 个 SCLK 周期的下降沿输出数据字节。注意,被传送的每一个数据位发生在读命令字节的最后一位之后的第一个下降沿。要衔接好命令字与数据字。
- 写时序:跟随在输入写命令字节的 8 个 SCLK 周期之后,在下 8 个 SCLK 周期的上升沿输入数据。如果有额外的 SCLK 周期,它们将被忽略。输入从位 0 开始。另外,在 SCLK 的每一个上升沿,I/O 引脚为三态。(高阻态,高电平,低电平)
代码
//---DS1302写入和读取时分秒的地址命令---//
//---秒分时日月周年 最低位读写位;-------//
u8 code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
u8 code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
//---DS1302时钟初始化2013年1月1日星期二8点00分00秒。---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
//u8 TIME[7] = {0x00, 0x09, 0x14, 0x01, 0x01, 0x02, 0x13};
/*******************************************************************************
* 函 数 名 : IO_Init_PP
* 函数功能 : 初始化IO口推挽输出
* 输 入 :
* 输 出 : 无
*******************************************************************************/
void IO_Init_PP()
{
P3M0 |= 0x0b; P3M1 &= ~0x0b; //推挽输出P3.013
}
/*******************************************************************************
* 函 数 名 : IO_Init_Input
* 函数功能 : 初始化IO口为输入
* 输 入 :
* 输 出 : 无
*******************************************************************************/
void IO_Init_Input()
{
P3M0 &= ~0x02; P3M1 &= ~0x02;P31 = 1;//准双向口P3.1
}
/*******************************************************************************
* 函 数 名 : IO_Init_Input
* 函数功能 : BCD转十进制
* 输 入 : BCD码
* 输 出 : 十进制
*******************************************************************************/
u8 BCDToInt(u8 bcd)
{
return (0x0f & (bcd >> 4))*10 + (0x0f & bcd);
}
/*******************************************************************************
* 函 数 名 : IO_Init_Input
* 函数功能 : 十进制转BCD
* 输 入 : 十进制数
* 输 出 : BCD码
*******************************************************************************/
u8 IntToBCD(u8 Int)
{
return ((Int / 10) << 4) + ((Int % 10) & 0x0f);
}
/*******************************************************************************
* 函 数 名 : Ds1302_SendAddress
* 函数功能 : 向DS1302发送命令
* 输 入 : addr
* 输 出 : 无
*******************************************************************************/
void Ds1302_SendAddress(u8 addr)
{
u8 n;
//IO初始化
IO_Init_PP();
RST = 0;//使能复位 一切操作无效
_nop_();_nop_();_nop_();_nop_();
SCLK = 0;//先将SCLK置低电平。由低到高是写
_nop_();_nop_();_nop_();_nop_();
RST = 1; 禁止复位
_nop_();_nop_();_nop_();_nop_();
for (n=0; n<8; n++)//开始传送八位地址命令
{
SCLK = 0;//先将SCLK置低电平。由低到高是写
_nop_();_nop_();_nop_();_nop_();
DSIO = addr & 0x01;//数据从低位开始传送
_nop_();_nop_();
addr >>= 1;
SCLK = 1;//写地址完成 此时SCLK为高电平
_nop_();_nop_();_nop_();_nop_();
}
}
/*******************************************************************************
* 函 数 名 : Ds1302Write
* 函数功能 : 向DS1302命令(地址+数据)
* 输 入 : addr,dat
* 输 出 : 无
*******************************************************************************/
void Ds1302Write(u8 addr, u8 dat)
{
u8 n;
dat = IntToBCD(dat);
Ds1302_SendAddress(addr);
for (n=0; n<8; n++)//写入8位数据。由低到高是写
{
SCLK = 0;//写数据低电平写高电平发送
_nop_();_nop_();_nop_();_nop_();
DSIO = dat & 0x01;
_nop_();_nop_();
dat >>= 1;
SCLK = 1;//写数据完成SCLK为高
_nop_();_nop_();_nop_();_nop_();
}
RST = 0;//传送数据结束
_nop_();
}
/*******************************************************************************
* 函 数 名 : Ds1302Read
* 函数功能 : 读取一个地址的数据
* 输 入 : addr
* 输 出 : dat
*******************************************************************************/
u8 Ds1302Read(u8 addr)
{
u8 n,dat;
Ds1302_SendAddress(addr);
IO_Init_Input();
for(n=0; n<8; n++)//读取8位数据。由高到低是读
{
SCLK = 0;//拉低电平接收数据高电平等待
_nop_();_nop_();_nop_();_nop_();
dat >>= 1;
if(DSIO)
dat |= 0x80;
SCLK = 1;//读数据取结束时SCLK为高
_nop_();_nop_();_nop_();_nop_();
}
DSIO = 0;
RST = 0;
_nop_(); //以下为DS1302复位的稳定时间,必须的。
SCLK = 1;
_nop_();
DSIO = 0;
_nop_();
DSIO = 1;
_nop_();
dat = BCDToInt(dat);
return dat;
}
/*******************************************************************************
* 函 数 名 : Ds1302Init
* 函数功能 : 初始化DS1302.
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Ds1302Init()
{
u8 n;
RST = 0;
Ds1302Write(0x8E,0X00); //WP取消写保护,就是可以写
// for(n=0; n<7; n++)//写入7个字节的时钟信号:分秒时日月周年
// {
// Ds1302Write(WRITE_RTC_ADDR[n],TIME[n]);
// }
// Ds1302Write(0x8E,0x80); //开启写保护功能
}
/*******************************************************************************
* 函 数 名 : Ds1302ReadTime
* 函数功能 : 读取时钟信息
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Ds1302ReadTime()
{
u8 n;
u8 TIME[7] = {0x00, 0x09, 0x14, 0x01, 0x01, 0x02, 0x13};
for (n=0; n<7; n++)//读取7个字节的时钟信号:分秒时日月周年
{
TIME[n] = Ds1302Read(READ_RTC_ADDR[n]);
}
}