51单片机——DS1302实时时钟(学习记录)

1、DS1302介绍

DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片工作电压为2.0V~5.5V,采用普通32.768kHz晶振。内部有一个用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后备电源双电源引脚,同时提供了对后备电源进行涓细电流充电的能力。主要特点是采用串行数据传输,可为掉电保护电源提供可编程的充电功能,并且可以关闭充电功能。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。

单片机定时器精度不高,占用cpu,不能掉电运行

RTC实时时钟芯片(DS1302为其中一种)

封装方式:直插、贴片

VCC2与主电源VCC连一起

开发板上VCC1的没接备用电源

WP写保护置1写入无效,可读出

CH时钟静止

2、结构

3、寄存器

内部与时钟有关的寄存器,内部不止这些

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

4、命令字与时序

命令字8位,最高有效位(第7位)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果为0,则表示存取数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,命令字总是从最低位开始输出。在命令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。

CE操作使能,写之前给1,写完清0,整个写入才有效

SCLK给一个固定时钟

I/O给数据

在时钟上升沿数据被写入,下降沿读出数据

左边字节命令字(在哪写or读),右边字节是具体数据

5、BCD码

RTC内数据不是以正常的8进制储存的,而是以BCD码存储

6、代码实现

代码实现主要思路:在何处、写入/读出、具体数据

通过CE、I/O、SCLK三个引脚读取、写入时间

main.c

#include <at89c51RC2.h>
#include "LCD1602.h"
#include "DS1302.h"
#include "key.h"
#include "timer.h"
#include "delay.h"
unsigned char keynum,mode,timesetselect,timesetflash;//按键,模式,时间选择位,时间设置选中闪烁
 
 void timeshow()//LCD显示时钟初始值
{
		read_time();
		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);
}
void timeset()//通过按键控制设置时间
{
	if(keynum==2)//按键2实现位置选择
	{
		timesetselect++;
		timesetselect%=6;//越界清0
	}
	if(keynum==3)//实现数字加
	{
		DS1302_time[timesetselect]++;//选中第几位就让第几位自加
		if(DS1302_time[0]>99){DS1302_time[0]=0;}//年越界判断
		if(DS1302_time[1]>12){DS1302_time[1]=1;}//月越界判断
		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]>31){DS1302_time[2]=1;}
		}
		else if(DS1302_time[1]==4||DS1302_time[1]==6||DS1302_time[1]==9||DS1302_time[1]==11)
			{
				if(DS1302_time[2]>30){DS1302_time[2]=1;}
			}
		else if(DS1302_time[1]==2)
		{
			if(DS1302_time[0]%4==0)//判断闰年
			{
				if(DS1302_time[2]>29){DS1302_time[2]=1;}//闰年天数大于29,天数回到1
			}
			else 
			{
				if(DS1302_time[2]>28){DS1302_time[2]=1;}//平年天数大于28,天数回到1
			}
		}
		if(DS1302_time[3]>23){DS1302_time[3]=0;}//小时超过23,回到
		if(DS1302_time[4]>59){DS1302_time[4]=0;}
		if(DS1302_time[5]>59){DS1302_time[5]=0;}
	}
	if(keynum==4)//实现数字减
	{
		DS1302_time[timesetselect]--;
		if(DS1302_time[0]<0){DS1302_time[0]=99;}//年小于0回到99
		if(DS1302_time[1]<1){DS1302_time[1]=12;}//月小于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;}//大月的日小于1回到31
			if(DS1302_time[2]>31){DS1302_time[2]=1;}
		}
		}
		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;}//小月的日小于1回到30
				if(DS1302_time[2]>30){DS1302_time[2]=1;}
			}
		else if(DS1302_time[1]==2)//二月份单独判断
		{
			if(DS1302_time[0]%4==0)//判断闰年
			{
				if(DS1302_time[2]<1){DS1302_time[2]=29;}//闰年天数小于1,天数回到29
				if(DS1302_time[2]>29){DS1302_time[2]=1;}//闰年天数大于29,天数回到1
			}
			else 
			{
				if(DS1302_time[2]<1){DS1302_time[2]=28;}//平年天数小于1,天数回到28
				if(DS1302_time[2]>28){DS1302_time[2]=1;}//平年天数大于28,天数回到1
			}
		}
		if(DS1302_time[3]<0){DS1302_time[3]=23;}//小时小于1,回到23
		if(DS1302_time[4]<0){DS1302_time[4]=59;}//分钟小于1,回到59
		if(DS1302_time[5]<0){DS1302_time[5]=59;}//秒小于1,回到59
		
		if(timesetselect==0&&timesetflash==1){LCD_ShowString(1,1,"  ");}//选择位为年,该位闪烁
		else {LCD_ShowNum(1,1,DS1302_time[0],2);}
    if(timesetselect==1&&timesetflash==1){LCD_ShowString(1,4,"  ");}
		else {LCD_ShowNum(1,4,DS1302_time[1],2);}
		if(timesetselect==2&&timesetflash==1){LCD_ShowString(1,7,"  ");}
		else {LCD_ShowNum(1,7,DS1302_time[2],2);}
		if(timesetselect==3&&timesetflash==1){LCD_ShowString(2,1,"  ");}
		else {LCD_ShowNum(2,1,DS1302_time[3],2);}
		if(timesetselect==4&&timesetflash==1){LCD_ShowString(2,4,"  ");}
		else {LCD_ShowNum(2,4,DS1302_time[4],2);}
		if(timesetselect==5&&timesetflash==1){LCD_ShowString(2,7,"  ");}
		else {LCD_ShowNum(2,7,DS1302_time[5],2);}
} 
void main()
{
	LCD_Init();
	DS1302_init();
	Timer0_Init();
	LCD_ShowString(1,1,"  -  -  ");
	LCD_ShowString(2,1,"  :  :  ");
	set_time();
	
	while(1)
	{
		keynum=key();
		if(keynum==1)  
		{
			if(mode==0){mode=1;timesetselect=0;}//进入设置时间后确保从年开始设置时间
			else if(mode==1){mode=0;set_time();}//每一次模式变换就将数据写入DS1302
		
		}
		switch(mode)
		{
			case 0:timeshow();break;//模式0显示时间
			case 1:timeset();break;//模式1,LCD停止显示时间,设置时间
		}
	}
}
void timer0_routine() interrupt 1
{
	static unsigned int t0count;
	TL0=0x18;//设置定时初值
	TH0=0xFC;//设置定时初值
	t0count++;
	if(t0count>=4000)
	{
		t0count=0;
		timesetflash=!timesetflash;//逻辑取反,以实现定时闪烁
	}
}

DS1302.c

#include <at89c51RC2.h>

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

int DS1302_time[]={24,10,23,21,50,2,3};//数组存储初始时间
#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//写保护位
void DS1302_init()//将使能位,SCLK时钟初始化为0
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

void  DS1302_writebyte(unsigned char command,Data)//写入函数
{
			unsigned char i;
			DS1302_CE=1;//在写之前,将使能位置高电平
			for(i=0;i<8;i++)//控制寄存器数据需要通过IO线一个一个写入控制寄存器;低位先写入
		{
				DS1302_IO=command&(0x01<<i);//相当于把第1~7位置0,只留第0位,如果第0位是0,则为0;反之则为1
			  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;//写入完成后将使能位置0
}
unsigned char   DS1302_readbyte(unsigned char command)//读取函数
{
	  unsigned char i,Data=0X00;//data变量用于存取从DS1302读取的数据
	  command|=0x01;//让命令字或上1,得到读的地址
		DS1302_CE=1;//使能位置1
		for(i=0;i<8;i++)//控制寄存器数据需要通过IO线一个一个写入控制寄存器;低位先写入
		{
			DS1302_IO=command&(0x01<<i);//读取命令字第i位
				DS1302_SCLK=0;	
				DS1302_SCLK=1;
		}

			for(i=0;i<8;i++)//数据读出
		{
					DS1302_SCLK=1;
					DS1302_SCLK=0;			
			   if(DS1302_IO)//检测IO口是否为1
					{Data |=(0X01<<i);}//记录下每一位
		}
		DS1302_CE=0;//使能位置0
		DS1302_IO=0;
		return Data;
}
void set_time()//设置时间函数
{
	DS1302_writebyte(DS1302_wp,0x00);//关闭写保护
	DS1302_writebyte(DS1302_year,DS1302_time[0]/10*16+DS1302_time[0]%10);//将时间写入DS1302,将十进制转化为DS1302内数据的存储方式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 read_time()//读取时间函数
{
	unsigned char temp;
	temp=DS1302_readbyte(DS1302_year);
	DS1302_time[0]=temp/16*10+temp%16;//将从DS1302传来的数据(BCD码)转化为10进制
	
	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;

}

 DS1302.h

#ifndef _DS1302_H_
#define _DS1302_H_
extern int DS1302_time[];
void  DS1302_writebyte(unsigned char command,Data);
unsigned char   DS1302_readbyte(unsigned char command);
void DS1302_init();
void set_time();
void read_time();
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值