我是靠谱客的博主 精明高山,最近开发中收集的这篇文章主要介绍【无标题】Ds1302驱动代码编写并在Lcd1602液晶显示 Ds1302实时时钟芯片简介一、Ds1302寄存器介绍二、SPI总线通讯时序与Ds1302时序总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

 

目录

 

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

  

af5a8f284542497d939276205ac48fc3.png

 

4a3cd2e5cb3849ed8e8d1dac8d39dd72.png

 

 Ds1302原理图(有电容作为备用电源,不会断电就直接停止工作,但备用电源口Vcc1将他悬空也不影响)与单片机IO口相连时加上拉电阻,提高 IO 口的驱动能力,这样信号比较稳定

d2adf2c914974ac6bc60adef797d2dcb.png

各功能管脚  c01723d3f7384a7598d670343069dc3f.png 


 

一、Ds1302寄存器介绍

寄存器命令字节

915adccf24994e678caf6bca3edf9c15.png

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();

接下来配置寄存器应该是这个芯片最重要的一步,先看懂如下时钟寄存器

abbf6c9722944da5aba0f65b8ae2345f.png 寄存器 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四种时序

5049eb9618994d069a6b5b35af25ee0b.png

7e86e4ef7a9c4944b6a862f724bf20dc.png 

43af796de76b43528fa8e8001929d813.png 

66ac6e086c514feda64dc29f3176cbab.png 

 例如CPOL=1/CPHA=1(当数据未发送时以及发送完毕后,SCK 都是高电平,因此 CPOL=1。 可以看出,在 SCK 第一个沿的时候,MOSI 和 MISO 会发生变化,SCK 第二个沿的时 候,数据是稳定的,此刻采样数据是合适的,也就是上升沿即一个时钟周期的后沿锁存读取 数据,即 CPHA=1。注意最后最隐蔽的 SSEL 片选,这个引脚通常用来决定是哪个从机和主 机进行通信)

UART 和 SPI 在通信的时候,只负责通信,不管是否通信成功,而 I 2C 却要通过应答信息来获取通信成功失败 的信息

2.Ds1302时序解析以及控制代码

总的控制程序也就分读操作,写操作以及初始化1302

Ds1302写字节时序

9d9eeb5ef22e480584da9ea6091eb0ca.png

 

这是单字节写入的时序图,可见,先拉高使能端,进行使能选择,然后在时钟上升沿写入一个字节。

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读字节时序

08fee5376ad74933a0967ef4134f8071.png

读操作有两处需要注意的地方

第一,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] = '';
			 LcdShowStr(1,0,str);

			 str[0] = (init_time[5]&0x0f)+'0';
			 str[1] = '';
			 LcdShowStr(1,11,"week");
			 LcdShowStr(1,15,str);

			 str[0]	= (init_time[2] >>4)+'0';
			 str[1]	= (init_time[2]&0x0f)+'0';
			 str[2] = ':';
             str[3]	= (init_time[1] >>4)+'0';
			 str[4]	= (init_time[1]&0x0f)+'0';
			 str[5] = ':';
             str[6]	= (init_time[0] >>4)+'0';
			 str[7]	= (init_time[0]&0x0f)+'0';
			 str[8] = '';
			 LcdShowStr(2,1,str);
	}
}

305396f445e94db5afbe935ad685a7e3.png 


 

总结

结合前面的Lcd1602液晶显示,熟悉Ds1302芯片使用方法,学习笔记+1,不过这里面有挺多文字性的说明在别的文章借鉴过来,如介意可以私信,勿喷

 

最后

以上就是精明高山为你收集整理的【无标题】Ds1302驱动代码编写并在Lcd1602液晶显示 Ds1302实时时钟芯片简介一、Ds1302寄存器介绍二、SPI总线通讯时序与Ds1302时序总结的全部内容,希望文章能够帮你解决【无标题】Ds1302驱动代码编写并在Lcd1602液晶显示 Ds1302实时时钟芯片简介一、Ds1302寄存器介绍二、SPI总线通讯时序与Ds1302时序总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部