概述
目录
Ds1302实时时钟芯片简介
一、Ds1302寄存器介绍
二、SPI总线通讯时序与Ds1302时序
1.SPI总线时序
2.Ds1302时序解析以及控制代码
总结
Ds1302实时时钟芯片简介
DS1302 实时时钟芯片广泛应用于电话、传真、便携式仪器等产品领域,它的主要性能 指标如下:
1、DS1302 是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且 还有软件自动调整的能力,可以通过配置 AM/PM 来决定采用 24 小时格式还是 12 小时格式。
2、拥有 31 字节数据存储 RAM。
3、串行 I/O 通信方式,相对并行来说比较节省 IO 口的使用。
4、DS1302 的工作电压比较宽,在 2.0~5.5V 的范围内都可以正常工作。
5、DS1302 这种时钟芯片功耗一般都很低,它在工作电压 2.0V 的时候,工作电流小于
300nA。
6、DS1302 共有 8 个引脚,有两种封装形式,一种是 DIP-8 封装,芯片宽度(不含引脚) 是 300mil,一种是 SOP-8 封装,有两种宽度,一种是 150mil,一种是 208mil
Ds1302原理图(有电容作为备用电源,不会断电就直接停止工作,但备用电源口Vcc1将他悬空也不影响)与单片机IO口相连时加上拉电阻,提高 IO 口的驱动能力,这样信号比较稳定
各功能管脚
一、Ds1302寄存器介绍
寄存器命令字节
DS1302 的 数据手册里的地址,直接把第 7 位、第 6 位和第 0 位值给出来了,所以指令就成了 0x80、0x81,最低位是 1,表示读,最低位是 0 ,表示写
Ds1302_writebyte((addr<<1)|0x80);//设置成写入数据模式,下一步就是写入data
Ds1302_writebyte(data_);
Ds1302_writebyte((addr<<1)|0x81);//设置成读数据模式,读取模式
data_ = Ds1302_readbyte();
接下来配置寄存器应该是这个芯片最重要的一步,先看懂如下时钟寄存器
寄存器 0:前面对应的是他们的存储地址,往这些地址上写入或读出数据。bit7是一位始终停止标志位,从名字都看得出来,我们上电检测到高电平(1)的时候,意思该芯片没有接入备用电源,断电以后直接停止工作;相反的检测到(0),我们就是检测到掉电以后芯片仍在工作。说明一下,ds1302内部存储的是BCD码,秒的个位能从0~9,bit0~bit3表示,十位显示0~5,bit4~bit6三位就ok
(为DS1302芯片的时钟数据的读和写的地址,也就是说要实现对芯片控制,就是往这些地址上写入或读出数据)
//将它们写成一个数组
uchar code Ds1302_Read_addr[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; //读
uchar code Ds1302_Write_addr[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; //写
寄存器 1:最高位未使用,剩下的 7 位中高 3 位是分钟的十位,低 4 位是分钟的个位
寄存器 2:bit7 是配置0/1,控制24h/12h方式显示;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_writedata(0x8E,0x00); //撤销写保护,允许写入数据
for(i = 0;i<7;i++){
Ds1302_writedata(i,init_time[i]);
}
二、SPI总线通讯时序与Ds1302时序
Ds1302时序跟SPI总线时序相似,(SPI变异物种),先来了解一下SPI总线
1.SPI总线时序
标准的 SPI 是 4根线,分别是 SSEL(片选,也写作 SCS)、SCLK(时钟,也写作 SCK)、MOSI(主机输出从机输入 Master Output/Slave Input)和 MISO(主机输入从机输出 Master Input/Slave Output)
SPI 是一 种高速的、全双工、同步通信总线,标准的 SPI 也仅仅使用 4 个引脚
SSEL:从设备片选使能信号。如果从设备是低电平使能的话,当拉低这个引脚后,从设 备就会被选中,主机和这个被选中的从机进行通信。
SCLK:时钟信号,由主机产生,和 I 2C 通信的 SCL 有点类似。
MOSI:主机给从机发送指令或者数据的通道。
MISO:主机读取从机的状态或者数据的通道。
(某些情况下,我们也可以用 3 根线的 SPI 或者 2 根线的 SPI 进行通信。比如主机只给 从机发送命令,从机不需要回复数据的时候,那么 MISO 就可以不要;而在主机只读取从机 的数据,不需要给从机发送指令的时候,那 MOSI 就可以不要;当一个主机一个从机的时候, 从机的片选有时可以固定为有效电平而一直处于使能状态,那么 SSEL 就可以不要;此时如 果再加上主机只给从机发送数据,那么 SSEL 和 MISO 都可以不要;如果主机只读取从机送 来的数据,SSEL 和 MOSI 都可以不要)
SPI 通信的主机也是我们的单片机,在读写数据时序的过程中,有四种模式
CPOL: Clock Polarity,时钟的极性(0/1)。如果 SCLK 在数据发送之前和之后的空闲状态是高电平,那么就是CPOL=1,如果空闲状态 SCLK 是低电平,那么就是 CPOL=0。
CPHA: Clock Phase,时钟的相位(0/1)。
CPHA=1,就表示数据的输出是在一个时钟周期的第一个沿上,至于这个沿是上升沿(此 时 CPOL=1 )还 是下降沿(CPOL=0 ),这要视 CPOL 的值而定。数据的 采集在第二个沿上
CPHA=0,就表示数据的采样是在一个时钟周期的第一个沿上,由上升沿(此 时 CPOL=1 )还 是下降沿(CPOL=0 )决定
SPI四种时序
例如CPOL=1/CPHA=1(当数据未发送时以及发送完毕后,SCK 都是高电平,因此 CPOL=1。 可以看出,在 SCK 第一个沿的时候,MOSI 和 MISO 会发生变化,SCK 第二个沿的时 候,数据是稳定的,此刻采样数据是合适的,也就是上升沿即一个时钟周期的后沿锁存读取 数据,即 CPHA=1。注意最后最隐蔽的 SSEL 片选,这个引脚通常用来决定是哪个从机和主 机进行通信)
UART 和 SPI 在通信的时候,只负责通信,不管是否通信成功,而 I 2C 却要通过应答信息来获取通信成功失败 的信息
2.Ds1302时序解析以及控制代码
总的控制程序也就分读操作,写操作以及初始化1302
Ds1302写字节时序
这是单字节写入的时序图,可见,先拉高使能端,进行使能选择,然后在时钟上升沿写入一个字节。
DS1302在进行读写操作时最少读写两个字节,第一个是控制字节,就是一个命令,说明是读还是写操作,第二个时需要读写的数据。
对于单字节写,只有在SCLK为低电平时才能将 CE 置高电平,所以刚开始将SCLK 置低,CE置高,然后把需要写入的字节送入 IO口,然后跳变SCLK,在SCLK下降沿时,写入数据
写入代码如下
void Ds1302_writebyte(uchar byte)
{
uint i;
uint t = 0x01;
for(i=0;i<8;i++)
{
SCIO = byte & t;
t<<=1;
DOWN(); //下降沿完成一个位的操作
}
SCIO = 1; //确保释放io引脚,没有特殊
}
void Ds1302_writedata(uchar addr,uchar data_)
{
CE = 0; nop(); //_nop_();
SCLK = 0; nop();
CE = 1; nop(); //使能片选信号
Ds1302_writebyte((addr<<1)|0x80); //方便后面写入BCD16位
Ds1302_writebyte(data_);
CE = 0; nop(); //传送数据结束
}
Ds1302读字节时序
读操作有两处需要注意的地方
第一,DS1302 的时序图上的箭头都是针对 DS1302来说的,因此读操作的时候,先写第一个字节指令,上升沿的时候 DS1302 来锁存数据,下 降沿我们用单片机发送数据。到了第二个字数据,由于我们这个时序过程相当于CPOL=0/CPHA=0,前沿发送数据,后沿读取数据,第二个字节是 DS1302 下降沿输出数据,单片机上升沿来读取,因此箭头从 DS1302 角度来说,出现在了下降沿。
第二,51单片机没有标准的 SPI 接口,和 I 2C 一样需要用 IO口来模拟通信过程。在读 DS1302 的时候,理论上 SPI 是上升沿读取,但是程序是用 IO 口模 拟的,所以数据的读取和时钟沿的变化不可能同时了,必然就有一个先后顺序。通过实验发 现,如果先读取 IO 线上的数据,再拉高 SCLK 产生上升沿,那么读到的数据一定是正确的, 而颠倒顺序后数据就有可能出错。这个问题产生的原因还是在于 DS1302 的通信协议与标准SPI 协议存在的差异造成的,如果是标准 SPI 的数据线,数据会一直保持到下一个周期的下降沿才会变化,所以读取数据和上升沿的先后顺序就无所谓了;但 DS1302 的 IO 线会在时钟 上升沿后被 DS1302 释放,也就是撤销强推挽输出变为弱下拉状态,而此时在 51 单片机引脚 内部上拉的作用下,IO 线上的实际电平会慢慢上升,从而导致在上升沿产生后再读取 IO 数 据的话就可能会出错。因此这里的程序我们按照先读取 IO 数据,再拉高 SCLK 产生上升沿 的顺序。
读取代码如下
uchar Ds1302_readbyte()
{
uint i;
uchar data_ = 0;
uint t = 0x01;
for(i=0;i<7;i++)
{
if(SCIO)
{
data_ = data_ | t; //低位在前,逐位读取
}
t<<=1;
DOWN();
}
return data_;
}
uchar Ds1302_readdata(uchar addr)
{
uchar data_ = 0;
CE = 0; nop();
SCLK = 0; nop();
CE = 1; nop();
Ds1302_writebyte((addr<<1)|0x81);
data_ = Ds1302_readbyte();
CE = 0; nop();
SCLK = 1; nop();
SCIO = 0; nop();
SCIO = 1; nop();
return data_;
}
Ds1302初始化函数
void Ds1302Init()
{
uchar i;
CE = 0; //初始化引脚
SCLK = 0;
i = Ds1302_readdata(0x00); //读取秒寄存器,秒在最低位
if((i & 0x80 != 0)) //最高位为0,即存在保护不允许写入
{
Ds1302_writedata(7,0x00); //撤销写保护,允许写入数据
for(i = 0;i<7;i++)
{
Ds1302_writedata(i,init_time[i]);
}
}
}
结合上一篇文章的LCD1602显示程序,试着将他们显示出来
初始化显示2022-11-21 22:30:00
#include "DS1302.h"
#include"lcd1602.h"
uchar init_time[7]={0x00,0x30,0x22,0x21,0x11,0x07,0x22}; //秒,分,时,日,月,星期,年
void main()
{
uchar str[15];
Ds1302Init(); //初始化 DS1302
LcdInit1602(); //初始化1602
while(1){
Ds1302_readtime();// 将他读取的值存储在init_time[7]里面,只要进行循环就来读取数值显示
str[0] = '2';
str[1] = '0';
str[2] = (init_time[6] >>4)+'0';//提取高四位年分十位
str[3] = (init_time[6]&0x0f)+'0';//高四位清空,低四位提取
str[4] = '-';
str[5] = (init_time[4] >>4)+'0';
str[6] = (init_time[4]&0x0f)+'0';
str[7] = '-';
str[8] = (init_time[3] >>4)+'0';
str[9] = (init_time[3]&0x0f)+'0';
str[10] = '