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与上面普通时钟中相同