DS1302实时时钟


DS1302是一款低功耗实时时钟芯片,主要由时钟发生器、时钟计数器、RAM、控制逻辑等组成,它的工作原理如下:

  • 1.时钟发生器:DS1302内部的时钟发生器负责产生高精度的时钟信号,一般为32.768kHz的晶振信号。
  • 2.时钟计数器:DS1302内部的时钟计数器用来计算时间,可以计算出年、月、日、星期、时、分、秒等,并且可以进行时间的设置和校正。
  • 3.RAM:DS1302内部带有31个字节的RAM,可以用来存储一些额外的信息,比如温度、电量等。
  • 4.控制逻辑:DS1302内部的控制逻辑负责控制芯片的各项功能,包括时钟频率的选择、数据的读写、时钟计数器的校正等。

DS1302在工作时,需要一个外部晶振将时间基准提供给芯片内部的时钟发生器,芯片内部的时钟计数器根据时钟信号进行计数,并将计数结果反馈给控制逻辑,控制逻辑则进行相应的处理,包括时间的读写、校正等。通过这些功能,DS1302可以实现精确的时间计数和管理。


典型工作电路

DS1302内部结构框图

 

引脚描述

 DS1302 使用一个外部 32.768kHz晶体.振荡电路工作时不需要任何外接的电阻或者电容

命令字


命令字由主机发出,DS1302接收(低位到高位依次传入)。第七位是是否对DS1302进行写入(1为允许,0为禁止);第六位在逻辑0时规定为时钟/日历数据,逻辑1时为RAM数据;位1至位5表示了输入输出的指定寄存器;位0在为逻辑0时为Write模式,逻辑1时为Read模式。

时序图

 写与读都是地址/命令字节在前,数据在后。
读写每次CE与SCLK都从0开始,所以需要一个初始化。

        读数据:先把CE置为高电平,开始读/写;先把命令字节通过I/O口传入,读最低位为1,传入的过程中先I/O口放上数据,再将SCLK置为高电平,从机读取后,SCLK置0,重复。
但因为读取完命令字节后,进入读出模式时SCLK要马上置0(注意时序图),等价于要保持高电平的状态出写入地址,,再由读出置0。所以为保证时序的对称,读数据时的写入地址先将SCLK置0再置1进行写入(看下面代码结合时序图理解),保证结束写入地址后SCLK为高电平;从机读取完后,直接进入读出状态,先将SCLK保持1,再置0,再开始读出,结束后将CE置为0。

        写数据:先将CE置为1,再将数据放到I/O口,再将SCLK置1,从机读取后,再将SCLK置0。重复俩个字节,分别写入命令字节和Data,结束后将CE置为0

 各部分响应时间(对应要Delay的时间,防止程序执行过快导致错误)

BCD码

 存放进DS1302的数据是以BCD码的形式来存入,所以要先将十进制数据转为BCD码形式后写入;读出时将BCD码转为十进制后读出。

具体实现代码(单纯的实现时钟功能)

/*
    使用普中51A2开发板,CPU选为AT89C52
*/

/*
    以下为DS1302.h
*/

#ifndef __DS1302_H__
#define __DS1302_H__

extern unsigned char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command, Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_ReadTime(void);
void DS1302_SetTime(void);


#endif


/*
    以下为DS1302.c
*/

#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



//初始化时间存放在该数组,左到右依次为年月日时分秒
unsigned char DS1302_Time[] = {23, 6, 3, 23, 13, 55};

/**
  * @brief 初始化DS1302
  * @param 无
  * @retval 无
  */
void DS1302_Init(void)
{
	DS1302_CE = 0;
	DS1302_SCLK = 0;
}

/**
  * @brief 写入
  * @param Command 地址/命令字节  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 读出
  * @param Command 地址/命令字节
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
	unsigned char i, Data = 0x00;
	DS1302_CE = 1;
	Command |= 0x01;
	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 = Data | (0x01<<i);}	
	}
	DS1302_CE = 0;
	DS1302_IO = 0;
	return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
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);
}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
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;
}


/*
    main.c
*/
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"


unsigned char KeyNum;


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);
		LCD_ShowNum(2,10,DS1302_Time[6],1);

	}
}

可调时钟代码

/*
    main.c
*/

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

//TimeSetSelect选择要修改的时间(年月日时分秒)
//TimeSetFlashFlag实现选中后的闪烁
//MODE选择不同的模式
unsigned char KeyNum, MODE, TimeSetSelect, TimeSetFlashFlag;

/**
  * @brief 显示时间
  * @param 无
  * @retval 无
  */
void TimeShow(void)
{
		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);

}

/**
  * @brief 修改时间
  * @param 无
  * @retval 无
  */
void TimeSet(void)
{
	if(KeyNum == 2) //按键2选择要修改的时间
	{
		TimeSetSelect++;
		TimeSetSelect %= 6;
	}
	if(KeyNum == 3) //按键3选中的时间自增
	{
		DS1302_Time[TimeSetSelect]++;
		if(DS1302_Time[0] > 99){DS1302_Time[0] = 0;}
		if(DS1302_Time[1] > 12){DS1302_Time[1] = 0;}
		
		if(DS1302_Time[1] == 1 || DS1302_Time[1] == 3 || DS1302_Time[1] == 5 || 
           DS1302_Time[1] == 7 || DS1302_Time[1] == 8 || 
           DS1302_Time[1] == 10 || DS1302_Time[1] == 12) //31天的月份
		{
			if(DS1302_Time[2]>31){DS1302_Time[2] = 0;}
		}
		else if(DS1302_Time[1] == 4 || DS1302_Time[1] == 6 || DS1302_Time[1] == 9 || 
                DS1302_Time[1] == 11)//30天的月份
		{
			if(DS1302_Time[2]>30){DS1302_Time[2] = 0;}
		}
		else if(DS1302_Time[1] == 2)//特殊的2月
		{
			//判断是否为闰年
			if(DS1302_Time[0] % 4 == 0)
			{
				if(DS1302_Time[2]>29){DS1302_Time[2]=0;}
			}
			else
			{
				if(DS1302_Time[2]>28){DS1302_Time[2]=0;}
			}
		}
		
		if(DS1302_Time[3] > 24){DS1302_Time[3] = 0;}
		if(DS1302_Time[4] > 59){DS1302_Time[4] = 0;}
		if(DS1302_Time[5] > 59){DS1302_Time[5] = 0;}

	}
	if(KeyNum == 4) //按键4选中的时间自减
	{
		DS1302_Time[TimeSetSelect]--;
		if(DS1302_Time[0] < 1){DS1302_Time[0] = 99;}
		if(DS1302_Time[1] < 1){DS1302_Time[1] = 12;}
		if(DS1302_Time[1] == 1 || DS1302_Time[1] == 3 || DS1302_Time[1] == 5 ||                                     
           DS1302_Time[1] == 7 || DS1302_Time[1] == 8 || 
           DS1302_Time[1] == 10 || DS1302_Time[1] == 12)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2] = 31;}
			if(DS1302_Time[2]>31){DS1302_Time[2] = 0;}
		}
		else if(DS1302_Time[1] == 4 || DS1302_Time[1] == 6 || DS1302_Time[1] == 9 || 
                DS1302_Time[1] == 11)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2] = 30;}
			if(DS1302_Time[2]>30){DS1302_Time[2] = 0;}
		}
		else if(DS1302_Time[1] == 2)
		{
			if(DS1302_Time[0] % 4 == 0)
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
				if(DS1302_Time[2]>29){DS1302_Time[2]=0;}
			}
			else
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
				if(DS1302_Time[2]>28){DS1302_Time[2]=0;}
			}
		}
		if(DS1302_Time[3] < 1){DS1302_Time[3] = 24;}
		if(DS1302_Time[4] < 1){DS1302_Time[4] = 59;}
		if(DS1302_Time[5] < 1){DS1302_Time[5] = 59;}		
	}
	
	//TimeSetFlashFlag因为定时器而以一定频率在1和0之间变换,进而结合下面代码实现选中闪烁
	if(TimeSetSelect == 0 && TimeSetFlashFlag == 1) {LCD_ShowString(1,1,"  ");}
	else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
	if(TimeSetSelect == 1 && TimeSetFlashFlag == 1) {LCD_ShowString(1,4,"  ");}
	else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
	if(TimeSetSelect == 2 && TimeSetFlashFlag == 1) {LCD_ShowString(1,7,"  ");}
	else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
	if(TimeSetSelect == 3 && TimeSetFlashFlag == 1) {LCD_ShowString(2,1,"  ");}
	else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
	if(TimeSetSelect == 4 && TimeSetFlashFlag == 1) {LCD_ShowString(2,4,"  ");}
	else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
	if(TimeSetSelect == 5 && TimeSetFlashFlag == 1) {LCD_ShowString(2,7,"  ");}
	else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
	
}


void main()
{
	LCD_Init();
	DS1302_Init();
	Timer0Init();
	LCD_ShowString(1,1,"  -  -  ");
	LCD_ShowString(2,1,"  :  :  ");

	DS1302_SetTime();
	
	while(1)
	{
		KeyNum = Key();
		if(KeyNum == 1) //按键1切换模式
		{
			//切换为模式1,TimeSetSelect从0开始选择
			if(MODE == 0){MODE = 1;TimeSetSelect = 0;}
			//切换为模式0,DS1302_SetTime重新更新数据
			else if(MODE == 1){MODE = 0;DS1302_SetTime();}
		}
		switch(MODE)
		{
			case 0:TimeShow();break; //MODE为0正常显示
			case 1:TimeSet();break; //MODE为1进入可调模式
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;
	TH0 = 0xFC;
	T0Count++;
	if(T0Count > 500)
	{
		T0Count = 0;
		TimeSetFlashFlag = !TimeSetFlashFlag;
	}
	
}

Key模块

//Key.h

#ifndef __KEY_Y__
#define __KEY_Y__

unsigned char Key();


#endif

//Key.c

#include <REGX52.H>
#include "Delay.h"

unsigned char Key()
{
	unsigned char KeyNumber = 0;
	
	if(P3_1 == 0){Delay(20); while(P3_1 == 0); Delay(20); KeyNumber = 1;}
	if(P3_0 == 0){Delay(20); while(P3_0 == 0); Delay(20); KeyNumber = 2;}
	if(P3_2 == 0){Delay(20); while(P3_2 == 0); Delay(20); KeyNumber = 3;}
	if(P3_3 == 0){Delay(20); while(P3_3 == 0); Delay(20); KeyNumber = 4;}
	
	return KeyNumber;

}

Timer0模块

//Timer.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init(void);

#endif

//Timer0.c

#include <REGX52.H>


void Timer0Init(void)		
{
	TMOD &= 0xF0;		
	TMOD |= 0x01;		
	TL0 = 0x66;		
	TH0 = 0xFC;		
	TF0 = 0;		
	TR0 = 1;		
	
	ET0 = 1;
	EA = 1;
	PT0 = 0;
}

DS1302.c与上面普通时钟中相同

  • 18
    点赞
  • 127
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值