51单片机编程应用(C语言):DS1302实时时钟

本文介绍了单片机计时的局限性,特别是DS1302芯片的优点和使用方法,包括其内部结构、寄存器操作、时序控制以及BCD码转换。文章还提到了如何通过控制寄存器进行读写操作和解决写保护问题,以及51单片机的掉电计时限制。
摘要由CSDN通过智能技术生成

单片机计时的缺陷:

1.他的精度不高,没有时钟芯片精度高,

2.会占用单片机CPU的时间, 

3.单片机的时钟无法掉电继续运行,(最大的缺点)

DS1302芯片内部有备用电池,可以掉电继续计时。

 

1,VCC2:主电源引脚

2,X1、X2:DS1302 外部晶振引脚,通常需外接 32.768K 晶振(给芯片提供一个稳定的脉冲,用来计数的脉冲,由石英震荡而成)(石英钟) 

3,GND:电源地

4,CE:使能引脚,也是复位引脚(新版本功能变)。

5,I/O:串行数据引脚,数据输出或者输入都从这个引脚

6,SCLK:串行时钟引脚

7,VCC1:备用电源

CE,I/O,SCLK,是我们主要操作的引脚,我们操作这三个引脚把芯片内部的时钟读出来。写入时间也是用这三个引脚,把我们想要设置的时间写进去。它规定了一套协议,我们要熟悉这些协议。这些时间都是存在芯片内部的寄存器中。我们要通过总线对寄存器进行读写,就可以读取和写入了

看内部结构可知,时钟芯片内部有31个8位寄存器。

 

当然,上面只是RTC的寄存器,还有很多其他的寄存器,只是我们只要操作时钟,这些寄存器就够了。 

ds1302控制寄存器 

 用于存放控制DS1302命令,可以控制该寄存器以控制选择读或写等操作。

我们操作单片机,是不是要考虑:在哪  写入  什么   在哪  读出   (什么)

在哪就是我们要操作哪一个寄存器,这么多寄存器,每个寄存器是有地址的,类似于房间的门牌号。写入数据还是读出数据,所以就有了上面的控制寄存器。

1.第7位,固定为1

2.第6位,1为寻址内部存储器地址RAM,0为寻址内部寄存器,即年月日所在存储器,一般设置为0

3.第5到1位,第6位存储器的地址

4.第0位,1位读操作,0为写操作

比如:

就是读秒这个寄存器:对应16进制的0x80 h

 

有了命令字,这些命令字与数据是如何对应起来的呢。

 

CE:复位时序

SCLK:时钟时序

IO:数据传输时序

注:DS1302与DS18B20都是从低位到高位传输(读/写)数据,编程时要注意。

上面是单字节写,单字节读。在写和读的时候,CE都是给高电平的。

SCLK就是单片机给固定的时钟。

I/O口的数据怎么写进去的,我们怎么通过I/O口读出数据呢?

这个协议就规定,在时钟上升沿的时候,I/O的数据将会被写入,在时钟下降沿,DS1302就会把自己的数据输出。(输不输出看我们给的命令)(方便我们要读)

简而言之就是: 上升沿单片机向时钟芯片写入,下降沿单片机对时钟芯片读出。跟SPI通信协议很像。

上升沿是地址,下降沿是数据,就是你首先给出访问的地址,这个地址包括命令,

比如:每来一个上升沿单片机写入了地址,这时候,芯片收到命令,如果是读,在下一个时钟周期

的下降沿就把数据发出来,如果是写,就在每个时钟的上升沿把数据写进去,

下面是代码环节:

比如我们先写入一个数据再读出:

首先按照芯片协议写入相关的函数,比如字节写,字节读等一系列操作

首先引脚定义一下:

//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

初始化:因为单片机刚上电都是默认为1的。

void DS1302_Init(void)
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

写的操作(按照时序图一步一步完成的) 

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i;
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	for(i=0;i<8;i++)
	{
		DS1302_IO=Data&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;
}

 读的操作(按照时序图一步一步完成的) 

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
	Command|=0x01;	//将指令转换为读指令
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0x01<<i);}
	}
	DS1302_CE=0;
	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
	return Data;
}

下面是是主函数实现写入一位数据后然后读出来:(都是模块化编程)自己把.h文件写一下

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"



unsigned char Second;
void main()
{
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"RTC");
    DS1302_WriteByte(0x80,0x03)
	
	while(1)
	{
		Second=DS1302_ReadByte(0x81);
        LCD_ShowNum(2,1,Second,3);
	}
}

但是读出来的数仍然有问题:(看视频28分钟后)为啥加了两个代码不是很理解。

如果读出时间为一个大于59并且不动的数,这芯片有可能是处于写保护状态,在   DS1302_WriteByte(0x80,0x03)后面加上

  DS1302_WriteByte(0x8E,0x00)即可解除芯片保护状态。

但是仍然会出现计时不准确的现象,因为寄存器的存储选着的是BCD码,不是正常的二进制

什么是BCD码:

所以我们要把BCD码转化为10进制。

下面是DS1302显示时间的main函数的代码:其他的在模块化编程中

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"

void main()
{
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
	LCD_ShowString(2,1,"  :  :  ");
	
	DS1302_SetTime();//设置时间
	
	while(1)
	{
		DS1302_ReadTime();//读取时间
		LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
		LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
		LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
		LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
		LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
		LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
	}
}

 

 注意:

但是51单片机内部的DS1302 没有备用电源,所以掉电后也无法继续计时。

8号引脚没有接备用电源。 

  • 25
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值