DS1302引脚图
DS1302典型电路
DS1302的一条指令一个字节共8位。
其中第7位(最高位)固定为1。这一位如果是0的话,那写进去也是无效的。
第6位是选择RAM还是CLOCK的。第6位是0就是选择CLOCK功能,第6位是1就是选择RAM。
第5到第1位,决定了寄存器的5位地址。
第0位是读写位。第0位是0就是写,第0位是1就是读。
DS1302时钟的寄存器,有8个与时钟相关的。
注意:DS1302内部是BCD码!!!
寄存器0:最高位CH是一个时钟停止标志位。如果时钟电路有备用电源,上电后,我们要先检测一下这一位,如果这一位是0,那说明时钟芯片在系统掉电后,由于备用电源的供给,时钟是持续正常运行的;如果这一位是1,那么说明时钟芯片在系统掉电之后,时钟部分不工作了。我们可以通过这一位判断时钟在单片机系统掉电后是否在正常运行。剩下的7位,高3位是秒的十位,低4位是秒的个位。
寄存器1:最高位未使用,剩下的7位中高3位是分钟的十位,低4位是分钟的个位。
寄存器2:bit7是1的话代表是12小时制,0代表是24小时制。bit6固定是0。bit5在12小时制下0代表是上午,1代表是下午,在24小时制下和bit4一起代表了小时的十位。低4位代表的是小时的个位。
寄存器3:高2位固定是0,bit5和bit4是日期的十位,低4位是日期的个位。
寄存器4:高3位固定是0,bit4是月的十位,低4位是月的个位。
寄存器5:高5位固定是0,低3位代表了星期。
寄存器6:高4位代表了年的十位,低4位代表了年的个位。注意:00~99指的是2000~2099年。
寄存器7:最高位一个写保护位,如果这一位是1,那么是禁止给任何其他寄存器或者那31个字节的RAM写数据的。因此在写数据之前,这一位必须先写成0。
DS1302的通信是SPI的变异种类,它用了SPI的通信时序,但是通信的时候没有完全按照SPI的规则来。
上升沿DS1302写入数据,下降沿DS1302读出数据! 上沿采样,下沿输出。
单字节读时序
单字节写时序
读操作有两处需要特别注意的地方。第一,DS1302的时序图上的箭头都是针对DS1302来说的,因此读操作的时候,先写第一个字节指令,上升沿的时 候DS1302来锁存数据,下降沿我们用单片机发送数据。到了第二个字数据,由于我们这个时序过程相当于CPOL=0/CPHA=0,前沿发送数据,后沿读取数据,第二个字节是DS1302下降沿输出数据,我们的单片机上升沿来读取,因此箭头从DS1302角度来说,出现在了下降沿。
第二个需要注意的地方就是,我们的单片机没有标准的SPI接口,和I2C一样需要用IO口来模拟通信过程。在读DS1302的时候,理论上SPI是上升沿读取,但是程序是用IO口模拟的,所以数据的读取和时钟沿的变化不可能同时了,必然就有一个先后顺序。通过实验发现,如果先读取IO线上的数据,再拉高SCLK产生上升沿,那么读到的数据一定是正确的,而颠倒顺序后数据就有可能出错。这个问题产生的原因还是在于DS1302的通信协议与标准SPI协议存在的差异造成的,如果是标准SPI的数据线,数据会一-直保持到下一个周期的下降沿才会变化,所以读取数据和上升沿的先后顺序也就无所谓了。但DS1302的IO线会在时钟上升沿后被DS1302释放,也就是撤销强推挽输出变为弱下拉状态,而此时在单片机引脚内部上拉的作用下,IO线上的实际电平会慢慢上升,从而导致在上升沿产生后再读取IO数据的话就可能会出错。因此这里的程序我们按照先读取IO数据,再拉高SCLK产生上升沿的顺序。
定时器时间到了 200ms 后,我们连续把 DS1302 的时间参数的 7 个字节读了出来。但是不管怎么读,都会有一个时间差,在极端的情况下就会出现这样一种情况:假如我们当前的时间是 00:00:59,我们先读秒,读到的秒是 59,然后再去读分钟,而就在读完秒到还未开始读分钟的这段时间内,刚好时间进位了,变成了 00:01:00 这个时间,我们读到的分钟就是 01,显示在液晶上就会出现一个 00:01:59,这个时间很明显是错误的。出现这个问题的概率极小,但却是实实在在可能存在的。
所以这个时候就有了BURST模式,BURST模式就是一次把7 个字节全部读或写到缓冲区,然后再来进行后续操作!
当写指令到DS1302的时候,只要将要写的5位地址全部写1,即读操作用0xBF,写操作用0xBE,这样的指令送给DS1302之后,它就会自动识别出来是burst模式,马上把所有的8个字节同时锁存到另外的8个字节的寄存器缓冲区中,这样时钟继续走,而我们读数据是从另外一个缓冲区内读取的。同样的道理,如果我们是用burst模式写数据,那么我们也是先写到这个缓冲区内,最终DS1302会把这个缓冲区内的数据一次性送到它的时钟寄存器内。
注意:不管是读还是写,只要使用时钟的burst模式,则必须一次性读写8个寄存器,要把时钟的寄存器完全读出来或者完全写进去。
#include "sys.h"
sbit DS1302_IO = P2^3;
sbit DS1302_CK = P1^7;
sbit DS1302_CE = P1^3;
struct sTime{
unsigned int year; //年
unsigned char mon; //月
unsigned char day; //日
unsigned char hour; //时
unsigned char min; //分
unsigned char sec; //秒
unsigned char week; //周
};
/*******************************************************************************
* 函数名 :DS1302ByteWrite
* 输入值 :unsigned char dat
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:发送一个字节到DS1302通信总线上
* 备注 :
*******************************************************************************/
void DS1302ByteWrite(unsigned char dat)
{
unsigned char mask;
DS1302_IO = 1; //拉低IO总线
for(mask = 0x01; mask != 0; mask <<= 1) //低位在前,逐位移出
{
if((dat&mask) != 0) //首先输出该位数据
DS1302_IO = 1;
else
DS1302_IO = 0;
DS1302_CK = 1; //拉高时钟线
DS1302_CK = 0; //拉低时钟线,完成一个位的操作
}
DS1302_IO = 1; //写完之后确保释放IO总线
}
/*******************************************************************************
* 函数名 :DS1302ByteRead
* 输入值 :none
* 返回值 :unsigned char dat
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:由DS1302通信总线上读取一个字节
* 备注 :返回读到的字节数据
*******************************************************************************/
unsigned char DS1302ByteRead(void)
{
unsigned char mask, dat = 0;
for(mask = 0x01; mask != 0; mask <<= 1) //低位在前,逐位读取
{
if(DS1302_IO) //首先读取此时的IO引脚,并设置dat中的对应位
dat |= mask;
DS1302_CK = 1; //拉高时钟
DS1302_CK = 0; //再拉低时钟,完成一个位的操作
}
return dat; //返回读到的字节数据
}
/*******************************************************************************
* 函数名 :DS1302SingleWrite
* 输入值 :unsigned char reg, unsigned char dat
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:用单次写操作向某一寄存器写入一个字节
* 备注 :reg为寄存器地址,dat为待写入字节
*******************************************************************************/
void DS1302SingleWrite(unsigned char reg, unsigned char dat)
{
DS1302_CE = 1; //使能片选信号
DS1302ByteWrite((reg << 1) | 0x80); //发送写寄存器指令
DS1302ByteWrite(dat); //写入字节数据
DS1302_CE = 0; //除能片选信号
}
/*******************************************************************************
* 函数名 :DS1302SingleRead
* 输入值 :unsigned char reg
* 返回值 :unsigned char dat
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:用单次读操作从某一寄存器读取一个字节
* 备注 :reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
unsigned char DS1302SingleRead(unsigned char reg)
{
unsigned char dat;
DS1302_CE = 1; //使能片选信号
DS1302ByteWrite((reg << 1) | 0x81); //发送读寄存器指令
dat = DS1302ByteRead(); //读取字节数据
DS1302_CE = 0; //除能片选信号
DS1302_IO = 0; //单字节读必须加的!
return dat;
}
/*******************************************************************************
* 函数名 :DS1302BurstWrite
* 输入值 :unsigned char *dat
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:用突发模式连续写入8个寄存器数据
* 备注 :reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
void DS1302BurstWrite(unsigned char *dat)
{
unsigned char i;
DS1302_CE = 1;
DS1302ByteWrite(0xBE); //发送突发写寄存器指令
for(i = 0; i < 7; i ++) //连续写入8字节数据
DS1302ByteWrite(*dat++);
DS1302_CE = 0;
}
/*******************************************************************************
* 函数名 :DS1302BurstRead
* 输入值 :unsigned char *dat
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:用突发模式连续读取8个寄存器的数据
* 备注 :dat为读到的字节
*******************************************************************************/
void DS1302BurstRead (unsigned char *dat)
{
unsigned char i;
DS1302_CE = 1;
DS1302ByteWrite(0xBF); //发送突发读寄存器指令
for(i = 0; i < 7; i++) //连续读取8个字节
dat[i] = DS1302ByteRead();
DS1302_CE = 0;
DS1302_IO = 0; //突发读必须加
}
/*******************************************************************************
* 函数名 :GetRealTime
* 输入值 :struct sTime *time
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:读取DS1302时间
* 备注 :
*******************************************************************************/
void GetRealTime(struct sTime *time)
{
unsigned char buf[8];
DS1302BurstRead(buf);
time -> year = buf[6] + 0x2000;
time -> mon = buf[4];
time -> day = buf[3];
time -> hour = buf[2];
time -> min = buf[1];
time -> sec = buf[0];
time -> week = buf[5];
}
/*******************************************************************************
* 函数名 :SetRealTime
* 输入值 :struct sTime *time
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:设置DS1302时间
* 备注 :
*******************************************************************************/
void SetRealTime(struct sTime *time)
{
unsigned char buf[8];
buf[7] = 0;
buf[6] = time -> year;
buf[4] = time -> mon;
buf[3] = time -> day;
buf[2] = time -> hour;
buf[1] = time -> min;
buf[0] = time -> sec;
buf[5] = time -> week;
DS1302BurstWrite(buf);
}
/*******************************************************************************
* 函数名 :InitDS1302
* 输入值 :none
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年3月1日
* 功能描述:DS1302初始化
* 备注 :
*******************************************************************************/
void InitDS1302()
{
struct sTime InitTime[] = { //2019年3月1日 星期五 21:50:00
0x19, 0x03, 0x01, 0x21, 0x50, 0x00, 0x05
};
DS1302_CE = 0; //初始化DS1302通信引脚
DS1302_CK = 0;
DS1302SingleWrite(7, 0x00); //撤销写保护以允许写入数据
SetRealTime(&InitTime); //设置DS1302为默认时间
}