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、寄存器
内部与时钟有关的寄存器,内部不止这些
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&×etflash==1){LCD_ShowString(1,1," ");}//选择位为年,该位闪烁
else {LCD_ShowNum(1,1,DS1302_time[0],2);}
if(timesetselect==1&×etflash==1){LCD_ShowString(1,4," ");}
else {LCD_ShowNum(1,4,DS1302_time[1],2);}
if(timesetselect==2&×etflash==1){LCD_ShowString(1,7," ");}
else {LCD_ShowNum(1,7,DS1302_time[2],2);}
if(timesetselect==3&×etflash==1){LCD_ShowString(2,1," ");}
else {LCD_ShowNum(2,1,DS1302_time[3],2);}
if(timesetselect==4&×etflash==1){LCD_ShowString(2,4," ");}
else {LCD_ShowNum(2,4,DS1302_time[4],2);}
if(timesetselect==5&×etflash==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