目录
前言
DS1302是一种RTC(Real Time Clock)实时时钟芯片,由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。
一、工作原理
下面是其在普中51单片机开发板上的原理图:
简单介绍一下,右边是一个晶振电路,虽然是它时钟芯片,但依然需要独立外接晶振电路,晶体振荡器Y1可以产生晶振频率为其提供时钟信号,C2和C3为起振电容,这是晶振电路的典型画法,VCC2为主电源,VCC1为备用电源,接电池后可以在主电源断开后保持计时状态。SCLK为串行时钟引脚接单片机P3^6,I/O为数据输入输出引脚接单片机P3^5,CE为片选引脚接单片机P3^4,这里的通信使用的是该芯片专用的协议。
上图是DS1302的内部结构框图,还是简单介绍下,左上角是电源控制模块,对输入的电源进行分配,为整个芯片供电。右上角为时钟分频模块,对输入的32.768KHz的时钟信号进行处理后得到1Hz的标准时钟信号,左边片选引脚CE在芯片内部下拉,,也就是说只能检测到高电平,也就是高电平触发,当CE输入高电平,芯片内部命令控制逻辑区才能接通并开始工作,类似于总开关的作用。I/O数据引脚收发数据会受串行时钟信号的控制,数据引脚和时钟引脚的变化要严格遵循它的通信时序。
如下图所示是寄存器定义表:
从上到下分别是秒、分、时、日、月、周、年最后两个寄存器是写保护和备用电源控制,左边两列寄存器的值代表读和写,例如,81为读“秒”寄存器,82为写“分”寄存器。
上图为读写寄存器的时序图,它的时序也很简单哈,串行时钟信号的上升沿为写数据,下降沿为读数据,当检测到时钟信号的上升沿时对I/O引脚的电平采样,当检测到时钟信号的下降沿时向I/O引脚输出电平信息。前8位为固定写,写入的这个字节为命令字,对某个寄存器进行读或写的操作,也就是上面的81h、82h...,后8位为写入或读出的数据,通过移位寄存器进行传递(低位先行),如此这般反复执行这一过程,通信也就随之产生了,数据存入到寄存器中上电后便开始走时。
二、代码
先对引脚和寄存器地址及命令字进行定义
#include <REGX52.H>
//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
//寄存器写入地址/指令定义
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
DS1302读写数据函数
void DS1302_WriteByte(unsigned char Command,Data)
{
unsigned char i;
DS1302_CE=1;
for(i=0;i<8;i++) //先写入8位命令字
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
for(i=0;i<8;i++) //写8位数据
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
Command|=0x01; //最低为置1,将指令转换为读指令
DS1302_CE=1;
for(i=0;i<8;i++) //写8位命令字
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;
}
for(i=0;i<8;i++) //读8位数据
{
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0; //读取后将IO设置为0,否则读出的数据会出错
return Data;
}
其实读写函数可以合并,大致思路就是判断命令字是奇数还是偶数,奇数就是读偶数就是写
根据上面写的通信层代码实现功能,对秒、分、时...进行解析,注意这里涉及到BCD码与十进制的转化。
//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
unsigned char DS1302_Time[]={19,11,16,12,59,55,6};
void DS1302_SetTime(void)
{
DS1302_WriteByte(DS1302_WP,0x00);
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//转BCD码后写入
DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
DS1302_WriteByte(DS1302_WP,0x80);
}
void DS1302_ReadTime(void)
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16; //BCD码转十进制后读取
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_Time[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATE);
DS1302_Time[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_Time[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_Time[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_Time[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_Time[6]=Temp/16*10+Temp%16;
}
总结
数字信号可以分配格式地输出显示到LCD1602上