1.简介
DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。
RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片
下面是DS1302的典型工作电路图与A2开发板上的实际对应图:
DS1302时钟/1-1.png)
DS1302时钟/1-2.png)
对应的引脚名和作用如下图所示:
引脚名 | 作用 | 引脚名 | 作用 |
---|---|---|---|
VCC2 | 主电源 | CE | 芯片使能 |
VCC1 | 备用电池 | IO | 数据输入/输出 |
GND | 电源地 | SCLK | 串行时钟 |
X1、X2 | 32.768KHz晶振 |
VCC1可以连接外部电池,做到在VCC2断电的情况下依然可以独立运行。
CE负责芯片使能,当其处于高电平时才能进行读写操作。
IO负责数据输入/输出,用于读取命令字与数据。
SCLK为串行时钟,在时钟上升沿写入数据,在时钟的下降沿读取数据。
CE,IO,SCLK是我们在配置时钟时主要需要操作的三个寄存器。数据传输时序如下图所示:
DS1302时钟/1-3.png)
命令字格式与读写内容如下图所示:
DS1302时钟/1-4.png)
地址命令字的最高位恒为1,次高位置1表示操作RAM,置0表示操作时钟。第五位到第一位表示需要操作的寄存器的地址。最后一位置1表示写入,置0表示读出。
举例说明:对秒寄存器进行读操作,地址命令字应为0b 1000 0001,即0x81。
其他操作在上表中均有给出。
DS1302的初始化代码如下:
1 | void DS1302_Init(){ |
2.写入数据
先给出代码:
1 | void DS1302_WriteByte(unsigned char Command,Data){ |
根据时序图可知,想要进行读写操作,首先需要对CE置1,在读写完成后,需要对CE置0。
先读入命令字,通过执行八次DS1302_IO=Command&(0x01<<i);
完成对命令字的读取,每次读取需要对SCLK的电平进行一次变化。命令字写入完成以后,在以同样的方式将数据写入IO,从而完成写入操作。
整个过程总共经历了16个上升沿,对应8次命令字的写入与8次数据的写入,符合时序图。
下面是写入数据时脉冲和有效上升沿下降沿的对应关系:
8个脉冲(8个上升沿)+8个脉冲(8个上升沿)=16个脉冲(8个上升沿+8个上升沿)
3.读出数据
先给出代码:
1 | unsigned char DS1302_ReadByte(unsigned char Command){ |
与写入数据相同,想要进行读写操作,首先需要对CE置1,在读写完成后,需要对CE置0。
先读入命令字,通过执行八次DS1302_IO=Command&(0x01<<i);
完成对命令字的读取,每次读取需要对SCLK的电平进行一次变化。
然后通过执行八次if(DS1302_IO){Data|=(0x01<<i);}
完成对数据的读取,需要注意的是,只有当DS1302_IO=1时才需要将数据写入Data对应的位上,如果删掉判断会导致Data的每一位均置1,结果为0xFF。
可以看到两次循环DS1302_SCLK置0与置1的顺序不一样,这是因为读模式下时序只需要15个脉冲。在经过8个上升沿后,命令字部分被全部写入完成,接下来开始读取时间数据。由于下一个下降沿马上到来,因此相写入数据会少用一个脉冲。(注意上图SCLK各脉冲有效上升沿或下降沿的方向)
下面是读出数据时脉冲和有效上升沿下降沿的对应关系:
7个脉冲(7个上升沿)+1个脉冲(1个上升沿+1个下降沿)+7个脉冲(7个下降沿)=15个脉冲(8个上升沿+8个下降沿)
在读取完成后,还需要将IO口置0,否则会导致显示混乱。
4.BCD码与十进制码转换
在DS1302中,时间是以BCD码的形式存储的,例如:0x35=0011 0101,在十六进制转为十进制情况下为 3*16+5=53,但在BCD码中0x35用高位和低位分别存储8位数据,在BCD码转为十进制的情况下为35。
需要注意BCD码的四位最多支持到1001,即十进制下的9,类似0x1A这样的数据不合法。
如果不对BCD码进行转换,那么在时钟运行的时候,会出现显示9秒过后立马显示16秒的情况。这是因为9=0x 0000 1001,16=0x 0001 0000,按照BCD的进位规则是正确的,但给我们读数带来了困难。
BCD码与十进制码的转换公式如下:
BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)
十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD)
5.一次性写入与读取时间
前面我们介绍了如何将时间信息写入DS1302或如何从DS1302中读出时间信息,但这样只能对其中的某个寄存器进行操作,例如:DS1302_WriteByte(0x8D,24)
可以将年份设置为24年。我们还需要一个函数完成对整体的年月日时分秒的一次性设置。
首先,设置一个数组,用于存放年月日时分秒的信息:
1 | char DS1302_Time[]={24,7,18,23,59,55,4}; |
这里不使用unsigned char是因为之后在设置时间合法性的时候需要判断当前时间信息是否小于0,因此可能会出现负数的情况,如使用unsigned char定义数组则在数据从0减去1的时候会直接变成255,无法触发小于0的判定条件。
然后,对各地址命令字的写操作进行宏定义:
1 |
如果要进行读操作,只需要对传进来的Command或等于0x01即可,有效减少了宏定义的数量。
再然后就是对年月日时分秒分别进行赋值了。需要注意的是,0x8E为写保护开关,只有在最高位置0时才可以对寄存器进行写入,置1时无法写入。
写入时,需要把十进制转化为BCD码。
具体的写入代码如下:
1 | void DS1302_SetTime(){ |
读出时,需要将读到的BCD码转为十进制存回数组中。
具体的读出代码如下:
1 | void DS1302_ReadTime(){ |
这样,就实现了一次性写入与读取时间,用户只需要调用DS1302_SetTime()
与 DS1302_ReadTime()
即可完成需求。