0%

51单片机 —— DS1302简要记录

DS1302是DALLAS公司推出的一款具有涓流充电功能的实时时钟芯片,带有存储容量为31字节的RAM,可对秒、分、时、日、月、年进行计时,能对日期进行自动调整(包括闰年的校正),可通过配置AM/PM来选择使用24小时制还是12小时制,通过串行通信的方式与MCU进行数据交换。

DS1302引脚定义

  • VCC2:主电源引脚
  • X1&X2:连接一个32.768K的晶振,为DS1302提供一个时钟基准
  • GND:接地
  • RST':复位/片选引脚,也相当于使能引脚,读写DS1302时,该引脚必须为高电平
  • I/O:双向通信引脚,DS1302通过该引脚进行数据的读写
  • SCLK:时钟信号输入引脚
  • VCC1:备用电源引脚

DS1302命令字

  • bit7:最高有效位,必须为1,才能将数据写入DS1302
  • bit6:若为0,表示存取日历时钟数据;若为1,表示存取RAM数据
  • bit5~bit1:对将要进行操作的寄存器的地址
  • bit0:读写选择位;若为0,表示进行写操作;若为1,表示进行读操作

DS1302读写时序

读时序

写时序

DS1302数据地址和传输格式

如图所示,时钟日历被写入7个寄存器,数值以BCD码的形式被存储于这7个寄存器中。

  • 寄存器1寄存器;最高位CH为时钟停止标志位,该位为0表示时钟持续运行,该位为1表示时钟停止运行;剩下的7位中,高3位为的十位,低4位为的个位
  • 寄存器2寄存器;最高位无意义;剩下的7位中,高3位为的十位,低4位为的个位
  • 寄存器3寄存器;bit71表示12小时制,为0则表示24小时制;bit6固定为0bit5在12小时制下若为0则表示AM,若为1则表示PM,在24小时制下和bit4一起表示的十位;低4位则表示的个位
  • 寄存器4寄存器;高2位固定为0bit5bit4表示的十位;低4位表示的个位
  • 寄存器5寄存器;高3位固定为0bit4表示的十位;低4位表示的个位
  • 寄存器6星期寄存器;高5位固定为0;低3位表示星期
  • 寄存器7寄存器;高4位表示的十位;低4位表示的个位;范围为从2000年到2099年
  • 寄存器8:写保护寄存器;最高位WP为保护位,若该位为1,则禁止向DS1302的RAM或其他寄存器写入数据
  • 寄存器9:涓流充电功能所用到的寄存器

举个栗子

从2021年5月11日0时0分0秒开始计时并在LCD1602上显示。

#include<reg52.h>
#include<intrins.h>

sbit RST=P2^4;
sbit CLK=P2^1;
sbit IO=P2^0;
sbit RS=P1^0;
sbit RW=P1^1;
sbit E=P2^5;
sbit DU=P2^6;

unsigned char num[]={"0123456789"}; //LCD1602显示的数字字符

void X() //关闭数码管
{
DU=1;
P0=0x00;
DU=0;
}

void Delay()
{
_nop_();
}

void Write_Bit_DS1302(unsigned char DAT) //向DS1302写入一字节的数据
{
unsigned char i;
CLK=0;
Delay();
for(i=0;i<8;i++)
{
IO=DAT&0x01; //低位在前,高位在后
Delay();
CLK=1; //时钟信号上升沿,写入数据
Delay();
CLK=0; //重新拉低CLK,形成脉冲
DAT>>=1; //将DAT的各数据位右移1位,准备写入下一数据位
}
}

void Write_DS1302(unsigned char CMD,unsigned char DAT) //向DS1302写入命令和数据
{
RST=0; //禁止数据传输
CLK=0; //在写入数据前确保CLK置低电平
RST=1; //开始数据传输
Delay();
Write_Bit_DS1302(CMD); //写入命令
Write_Bit_DS1302(DAT); //写入数据
CLK=1;
RST=0;
}

unsigned char Read_Bit_DS1302() //从DS1302读出一字节的数据
{
unsigned char i,DAT;
Delay();
for(i=0;i<8;i++)
{
DAT>>=1;
if(IO==1)
{
DAT|=0x80;
}
CLK=1;
Delay();
CLK=0; //时钟信号下降沿,读出数据
Delay();
}
return DAT;
}

unsigned char Read_DS1302(unsigned char CMD) //向DS1302写入命令后再从DS1302读出数据
{
unsigned char DAT;
RST=0;
CLK=0;
RST=1;
Write_Bit_DS1302(CMD); //写入命令
DAT=Read_Bit_DS1302(); //读出数据
CLK=1;
RST=0;
return DAT;
}

void Init_DS1302() //DS1302初始化
{
unsigned char X;
X=Read_DS1302(0x81);
if(X&0x80) //判断DS1302是否处于运行状态
{
Write_DS1302(0x8e,0x00); //允许将数据写入DS1302的寄存器
Write_DS1302(0x80,((00/10)<<4|(00%10))); //写入“秒”的初始值,需要将LCD1602显示的数字的ASCII值转换成BCD码
Write_DS1302(0x82,((00/10)<<4|(00%10))); //写入“分”的初始值
Write_DS1302(0x84,((00/10)<<4|(00%10))); //写入“时”的初始值
Write_DS1302(0x86,((11/10)<<4|(11%10))); //写入“日”的初始值
Write_DS1302(0x88,((5/10)<<4|(5%10))); //写入“月”的初始值
Write_DS1302(0x8c,((21/10)<<4|(21%10))); //写入“年”的初始值
Write_DS1302(0x8e,0x80); //禁止将数据写入DS1302的寄存器
}
}

void Delay5ms()
{
unsigned char i,j;
_nop_();
i=9;
j=244;
do
{
while(--j);
}
while(--i);
}

int ReadBusy() //LCD1602“读忙”操作
{
int temp;
RS=0;
RW=1;
_nop_();
P0=0xff;
_nop_();
E=1;
_nop_();
temp=P0;
_nop_();
E=0;
return(temp&0x80);
}

void Write_Com(char com) //LCD1602“写命令”操作
{
while(ReadBusy());
RS=0;
RW=0;
E=0;
_nop_();
P0=com;
_nop_();
E=1;
Delay5ms();
E=0;
Delay5ms();
}

void Write_Dat(char dat) //LCD1602“写数据”操作
{
while(ReadBusy());
RS=1;
RW=0;
E=0;
_nop_();
P0=dat;
_nop_();
E=1;
Delay5ms();
E=0;
Delay5ms();
}

void LCD1602_Init() //LCD1602初始化
{
Delay5ms(); //延时15ms,首次写入LCD1602时应给LCD1602一段较长的响应时间
Delay5ms();
Delay5ms();
Write_Com(0x38); //显示模式设置:16*2显示、5*7点阵,连续写入3次,确保LCD1602初始化成功
Delay5ms();
Write_Com(0x38);
Delay5ms();
Write_Com(0x38);
Delay5ms();
Write_Com(0x0c); //显示模式设置:开显示、光标不显示、光标不闪烁
Delay5ms();
Write_Com(0x06); //显示模式设置:光标右移,字符不右移
Delay5ms();
Write_Com(0x01); //清除屏幕
Delay5ms();
}

void Display_Second(unsigned char x) //LCD1602显示“秒”的数值
{
unsigned char i,j;
i=x/10; //取数值的十位
j=x%10; //取数值的个位
Write_Com(0x80+0x49); //写入在LCD1602上显示的位置
Write_Dat(num[i]);
Write_Dat(num[j]);
Delay5ms();
}

void Display_Minute(unsigned char x) //LCD1602显示“分”的数值
{
unsigned char i,j;
i=x/10;
j=x%10;
Write_Com(0x80+0x46);
Write_Dat(num[i]);
Write_Dat(num[j]);
Delay5ms();
}

void Display_Hour(unsigned char x) //LCD1602显示“时”的数值
{
unsigned char i,j;
i=x/10;
j=x%10;
Write_Com(0x80+0x43);
Write_Dat(num[i]);
Write_Dat(num[j]);
Delay5ms();
}

void Display_Day(unsigned char x) //LCD1602显示“日”的数值
{
unsigned char i,j;
i=x/10;
j=x%10;
Write_Com(0x80+0x0c);
Write_Dat(num[i]);
Write_Dat(num[j]);
Delay5ms();
}

void Display_Month(unsigned char x) //LCD1602显示“月”的数值
{
unsigned char i,j;
i=x/10;
j=x%10;
Write_Com(0x80+0x09);
Write_Dat(num[i]);
Write_Dat(num[j]);
Delay5ms();
}

void Display_Year(unsigned char x) //LCD1602显示“年”的数值
{
unsigned char i,j;
i=x/10;
j=x%10;
Write_Com(0x80+0x06);
Write_Dat(num[i]);
Write_Dat(num[j]);
Delay5ms();
}

void main()
{
unsigned char second,minute,hour,day,month,year;
unsigned char temp; //暂存从DS1302读出的数据
X();
LCD1602_Init();
Write_Com(0x80+0x01);
Write_Dat('D');
Write_Dat('A');
Write_Dat('T');
Write_Dat('E');
Write_Dat(':');
Delay5ms();
Write_Com(0x80+0x08);
Write_Dat('-');
Delay5ms();
Write_Com(0x80+0x0b);
Write_Dat('-');
Delay5ms();
Write_Com(0x80+0x45);
Write_Dat(':');
Delay5ms();
Write_Com(0x80+0x48);
Write_Dat(':');
Delay5ms();
Init_DS1302();

while(1)
{
temp=Read_DS1302(0x81);
second=((temp&0x70)>>4)*10+(temp&0x0f); //将“秒”的BCD码转换成对应的ASCII值
Display_Second(second);
temp=Read_DS1302(0x83);
minute=((temp&0x70)>>4)*10+(temp&0x0f); //将“分”的BCD码转换成对应的ASCII值
Display_Minute(minute);
temp=Read_DS1302(0x85);
hour=((temp&0x70)>>4)*10+(temp&0x0f); //将“时”的BCD码转换成对应的ASCII值
Display_Hour(hour);
temp=Read_DS1302(0x87);
day=((temp&0x70)>>4)*10+(temp&0x0f); //将“日”的BCD码转换成对应的ASCII值
Display_Day(day);
temp=Read_DS1302(0x89);
month=((temp&0x70)>>4)*10+(temp&0x0f); //将“月”的BCD码转换成对应的ASCII值
Display_Month(month);
temp=Read_DS1302(0x8d);
year=((temp&0x70)>>4)*10+(temp&0x0f); //将“年”的BCD码转换成对应的ASCII值
Display_Year(year);
}
}


参考资料:

DS1302中文资料

关于实时时钟模块DS1302使用心得

时钟芯片DS1302的原理及使用

DS1302的使用方式 及 51单片机连接DS1302的驱动程序

实时时钟DS1302详细介绍