单片机计时的缺陷:
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号引脚没有接备用电源。