主要特性
DS1307是Maxim的串行、I2C实时时钟芯片。主要特性有:
工作电压:主电源电压4.5~5.5V,电池电压2.0~3.5V
功耗:电池供电、备份模式时<500nA
接口:I2C,最大速率100kbps
可编程方波输出
电源自动切换、失效检测
内置56字节大小、支持电池备份的RAM
封装:8-Pin SO/PDIP
管脚定义
X1、X2: 接32.768kHz晶体,要求晶体负载电容12.5pF
Vcc:主电源,范围4.5~5.5V。当需要对DS1307读写时,需要接Vcc。
VBAT:接电池,范围2.0~3.5V。
GND:地
SDA、SCL:I2C接口数据线、时钟线。
SQW/OUT:方波输出脚、通过写寄存器来使能。
注意事项
DS1307的工作电压为4.5V-5.5V供电。
I2C总线控制DS1307,用GPIO模拟I2C时序即可。
时间配置:直接把时间值写入0x00~0x06的时间寄存器。
时间读取:直接读取0x00~0x06的时间寄存器即可。
读取时间时发现未走时可能是00h 地址的寄存器第7 位为1,此位为1则芯片未开始工作,即时钟未走时,记得首次上电时把该位清零。
首次上电设置时间后不必每次上电都再次初始化一下时间,停电后有备用电池,此时不必重写时间单元。
#include "global.h"
#include "drv_ds1307.h"
#define I2C_CLK_PORT GPIOB
#define I2C_CLK_PIN GPIO_PIN_4
#define I2C_SDA_PORT GPIOB
#define I2C_SDA_PIN GPIO_PIN_5
#define SCL_High() GPIO_Init(I2C_CLK_PORT, I2C_CLK_PIN, GPIO_MODE_OUT_PP_HIGH_SLOW)
#define SCL_Low() GPIO_Init(I2C_CLK_PORT, I2C_CLK_PIN, GPIO_MODE_OUT_PP_LOW_SLOW)
#define SDA_High() GPIO_Init(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_MODE_OUT_PP_HIGH_SLOW)
#define SDA_Low() GPIO_Init(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_MODE_OUT_PP_LOW_SLOW)
#define SDAM() (GPIO_ReadInputData(I2C_SDA_PORT) & 0x20) ?1:0
#define SET_SCL_OUT() GPIO_Init(I2C_CLK_PORT, I2C_CLK_PIN, GPIO_MODE_OUT_PP_HIGH_SLOW)
#define SET_SDA_OUT() GPIO_Init(I2C_SDA_PORT, I2C_SDA_PIN, GPIO_MODE_OUT_PP_HIGH_SLOW)
#define SET_SDA_IN() GPIO_Init(GPIOA, GPIO_PIN_0, GPIO_MODE_IN_PU_NO_IT)
extern uint8_t Ds1307_WriteByte(uint8_t WriteAddr,uint8_t WriteData);
extern uint8_t Ds1307_ReadByte(uint8_t ReadAddr);
extern void Ds1307_WriteData();
extern void Ds1307_ReadData();
extern void Init_Timer();
extern void Write_Time();
uint8_t g_u8ReadData[8];
/*******************************************************************************
// Function: I2C_Int
// Description: 模拟I2C 与ds1307端口初始化
// Param:
// Return:
*******************************************************************************/
static void I2C_Int(void)
{
SET_SDA_OUT();
SET_SCL_OUT();
}
/*******************************************************************************
// Function: Delay_5us
// Description: 微妙级延时函数 延时时间约为16us
// Param:
// Return: fcpu 16MHz 时
*******************************************************************************/
static void Delay_5us(void)
{
uint8_t i;
for (i=5; i>0; i--);
}
/*******************************************************************************
// Function: I2C_Start
// Description: I2C 开始传输信号 当SCL 为高时 SDA由高变低
// Param:
// Return:
*******************************************************************************/
static void I2C_Start(void)
{
// SDA 1->0 while SCL High
SDA_High();
SCL_High();
Delay_5us();
SDA_Low();
Delay_5us();
SCL_Low();
}
/*******************************************************************************
// Function: I2C_Stop
// Description: I2C 停止传输信号 当SCL 为高时 SDA由低变高
// Param:
// Return:
*******************************************************************************/
static void I2C_Stop(void)
{
// SDA 0->1 while SCL High
SDA_Low();
SCL_High();
Delay_5us();
SDA_High();
Delay_5us();
}
/*******************************************************************************
// Function: I2C_SendACK
// Description: 主机向从机发送应答信号
// Param: 应答信号 1:表示SDA高电平(无应答) 0:SDA低电平(有应答)
// Return:
*******************************************************************************/
static void I2C_SendACK(uint8_t ack)
{
if(ack == 0)
{
SDA_Low();
}
else
{
SDA_High();
}
SCL_High();
Delay_5us();
SCL_Low();
Delay_5us();
}
/*******************************************************************************
// Function: I2C_SendByte
// Description: 模拟I2C通信 发送8位数据
// Param: 发送的8为数据值
// Return: 返回应答信号 0表示有应答 1表示无应答
*******************************************************************************/
static uint8_t I2C_SendByte(uint8_t SendByte)
{
static uint8_t i,RevAck;
SDA_Low();
for (i= 0 ; i< 8; i++)
{
SCL_Low();
if (SendByte & 0x80) // write data
{
SDA_High();
}
else
{
SDA_Low();
}
Delay_5us();
SendByte <<= 1;
SCL_High();
Delay_5us();
}
SCL_Low();
SDA_High();
Delay_5us();
SET_SDA_IN();
SCL_High();
asm("nop");
asm("nop");
RevAck = (uint8_t)SDAM();
Delay_5us();
SCL_Low();
SET_SDA_OUT();
Delay_5us();
return RevAck;
}
/*******************************************************************************
// Function: I2C_RecvByte
// Description: 模拟I2C通信 从从机读取8位数据
// Param:
// Return: 返回读取的8为数据值
*******************************************************************************/
static uint8_t I2C_RecvByte()
{
uint8_t i;
uint8_t RecvData = 0;
SDA_High(); // latch the Data port befor reading
SET_SDA_IN();
for (i=0; i<8; i++)
{
RecvData <<= 1;
SCL_High();
asm("nop");
asm("nop");
if (SDAM())
{
RecvData |= 0x01;
}
else
{
RecvData &= 0xfe;
}
Delay_5us();
SCL_Low();
Delay_5us();
}
SET_SDA_OUT();
return RecvData;
}
/*******************************************************************************
// Function: Ds1307_WriteByte
// Description: 模拟I2C通信 写入1字节数据到指定地址
// Param: WriteAddr:待写入数据 WriteData;写入的地址
// Return: 1: 成功写入 0: 写入出错
*******************************************************************************/
uint8_t Ds1307_WriteByte(uint8_t WriteAddr,uint8_t WriteData)
{
I2C_Start();
if(I2C_SendByte(0xD0)) // Device Addr + Write (operation)
{
return 0;
}
if(I2C_SendByte(WriteAddr))
{
return 0;
}
if(I2C_SendByte(WriteData))
{
return 0;
}
I2C_Stop();
return 1;
}
/*******************************************************************************
// Function: Ds1307_ReadByte
// Description: 模拟I2C通信 从指定地址读取1字节数据
// Param: ReadAddr:读取的地址
// Return: RevData:读取的8位数据
*******************************************************************************/
uint8_t Ds1307_ReadByte(uint8_t ReadAddr)
{
uint8_t RevData;
I2C_Start();
I2C_SendByte(0xD0); // Device Addr + Write (operation)
I2C_SendByte(ReadAddr);
I2C_Start();
I2C_SendByte(0xD1); // Device Addr + Write (operation)
RevData = I2C_RecvByte();
I2C_SendACK(1);
I2C_Stop();
return RevData;
}
/*******************************************************************************
// Function: Ds1307_WriteData
// Description: 模拟I2C通信 写入8字节数据 从0x00~0x07
// Param: pWriteData: 指针指向待写入的数组的地址
// Return:
*******************************************************************************/
void Ds1307_WriteData()
{
uint8_t i;
uint8_t *pWriteData;
pWriteData = (uint8_t *)&ICTimerBuf;
I2C_Start();
I2C_SendByte(0xD0); // Device Addr + Write (operation)
I2C_SendByte(0x00);
for(i=0; i<8; i++)
{
I2C_SendByte(*pWriteData++);
}
I2C_Stop();
}
/*******************************************************************************
// Function: Ds1307_ReadData
// Description: 模拟I2C通信 读取8字节数据 从0x00~0x07
// Param: pReadData: 指针指向保存数据的数组
// Return:
*******************************************************************************/
void Ds1307_ReadData()
{
uint8_t i;
uint8_t *pReadData;
pReadData = (uint8_t *)&ICTimerBuf;
I2C_Start();
I2C_SendByte(0xD0); // Device Addr + Write (operation)
I2C_SendByte(0x00);
I2C_Start();
I2C_SendByte(0xD1); // Device Addr + Write (operation)
for(i=0; i<7; i++)
{
// *pReadData++ = I2C_RecvByte();
*pReadData = I2C_RecvByte();
pReadData++;
if(i < 6)
I2C_SendACK(0); //DIO低电平 表示ACK 应答
else
I2C_SendACK(1);
}
I2C_Stop();
}
/*******************************************************************************
// Function: Init_Timer
// Description: 上电初始化时钟以及读时钟
// Param:
// Return: 判断0x00 地址bit7 是否为1 为1表示时钟芯片掉电
*******************************************************************************/
void Init_Timer()
{
I2C_Int();
Ds1307_ReadData();
if(ICTimerBuf.TimerSec & 0x80)
{
Ds1307_ReadData();
if(ICTimerBuf.TimerSec & 0x80)
{
ICTimerBuf.TimerSec =0x00;
ICTimerBuf.TimerMin =0x00;
ICTimerBuf.TimerHour =0x12;
ICTimerBuf.TimerWeek =0x02;
ICTimerBuf.TimerDay =0x15;
ICTimerBuf.TimerMonth =0x04;
ICTimerBuf.TimerYear =0x14;
Ds1307_WriteData();
}
}
ICTimer.TimerSec =(ICTimerBuf.TimerSec/16) * 10 + (ICTimerBuf.TimerSec%16);
ICTimer.TimerMin =(ICTimerBuf.TimerMin/16) * 10 + (ICTimerBuf.TimerMin%16);
ICTimer.TimerHour =((ICTimerBuf.TimerHour&0x1f)/16) * 10 + ((ICTimerBuf.TimerHour&0x1f)%16);
ICTimer.TimerWeek =(ICTimerBuf.TimerWeek/16) * 10 + (ICTimerBuf.TimerWeek%16);
ICTimer.TimerDay =(ICTimerBuf.TimerDay/16) * 10 + (ICTimerBuf.TimerDay%16);
ICTimer.TimerMonth =(ICTimerBuf.TimerMonth/16) * 10 + (ICTimerBuf.TimerMonth%16);
ICTimer.TimerYear =(ICTimerBuf.TimerYear/16) * 10 + (ICTimerBuf.TimerYear%16);
//更新系统时间参数
g_u8TimeSettingHourValue = ICTimer.TimerHour;
g_u8TimeSettingMinutesValue = ICTimer.TimerMin;
g_u8TimeSettingAmPmValue = (ICTimerBuf.TimerHour &0x20)?1:0;
}
/*******************************************************************************
// Function: Write_Time
// Description: 如果设置了时钟则写入时钟
// Param:
// Return:
*******************************************************************************/
void Write_Time()
{
if(g_u8TimeChangeFlag) //如果设置了时钟
{
g_u8TimeChangeFlag = 0;
ICTimerBuf.TimerSec = (ICTimer.TimerSec/10)*16 + (ICTimer.TimerSec%10);
ICTimerBuf.TimerMin = (ICTimer.TimerMin/10)*16 + (ICTimer.TimerMin%10);
ICTimerBuf.TimerHour = (ICTimer.TimerHour/10)*16 + (ICTimer.TimerHour%10);
ICTimerBuf.TimerWeek = (ICTimer.TimerWeek/10)*16 + (ICTimer.TimerWeek%10);
ICTimerBuf.TimerDay = (ICTimer.TimerDay/10)*16 + (ICTimer.TimerDay%10);
ICTimerBuf.TimerMonth = (ICTimer.TimerMonth/10)*16 + (ICTimer.TimerMonth%10);
ICTimerBuf.TimerYear = (ICTimer.TimerYear/10)*16 + (ICTimer.TimerYear%10);
//转换为12时制时间
if(g_u8TimeSettingAmPmValue %2) //为1 表示 pM
{
ICTimerBuf.TimerHour |= 0x60;
}
else //为0表示AM
{
ICTimerBuf.TimerHour |= 0x40;
}
//转换好之后把时间写入芯片
Ds1307_WriteData();
}
}
/*
*********************************************************************************************************
* 函 数 名: uint8_t DS1307_Read(uint8_t address)
* 功能说明: 向DS1307寄存器读取数据
* 形 参:寄存器地址
* 返 回 值: 读出的数据
*********************************************************************************************************
*/
uint8_t DS1307_Read(uint8_t address)
{
uint8_t dat;
i2c_Start();
i2c_SendByte(0xD0);//发送设备地址,写指令
i2c_WaitAck();
i2c_SendByte(address);//发送寄存器地址
i2c_WaitAck();
i2c_Stop();
i2c_Start();//重新启动IIC总线
i2c_SendByte(0xD1); //发送设备地址,读指令
i2c_WaitAck();
dat=i2c_ReadByte(); //读一个字节数据
i2c_NAck();
i2c_Stop();
return dat;
}
/*
*********************************************************************************************************
* 函 数 名: DS1307_Wirte(uint8_t address , uint8_t dat)
* 功能说明: 向DS1307寄存器写数据
* 形 参:address:寄存器地址,dat:要写入的数据
* 返 回 值: 无
*********************************************************************************************************
*/
void DS1307_Wirte(uint8_t address , uint8_t dat)
{
i2c_Start();
i2c_SendByte(0xD0);//发送设备地址,写指令
i2c_WaitAck();
i2c_SendByte(address);//发送寄存器地址
i2c_WaitAck();
i2c_SendByte(dat);//写一个字节数据
i2c_WaitAck();
i2c_Stop();
}
/*
*********************************************************************************************************
* 函 数 名: void DS1307_Init_Time(void)
* 功能说明: 第一次上电时,需要初始化时间,初始化一次后就不必重复初始化
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void DS1307_Init_Time(void)
{
if(DS1307_Read(FLAG_ADDR)!=FLAG_VAL)//如果未初始化,执行初始化时间
{
DS1307_Set_Time(2023,2,16,19,02,00,4);//2023年2月16日 17:50:00 星期四
printf("时间初始化完成,flag=%d\r\n",DS1307_Read(FLAG_ADDR));
} else{
printf("时间已初始化过,flag=%d\r\n",DS1307_Read(FLAG_ADDR));
}
}
//时间初始化设置
void DS1307_Set_Time(uint16_t year,uint8_t mon,uint8_t day,uint8_t hour,uint8_t min,uint8_t sec,uint8_t week)
{
year-=2000;//年默认2000年开始
if(year > 100) year = 0;
//十进制转为BCD码
year = ((year /10) << 4) + year %10;
mon = ((mon /10) << 4) + mon %10;
day = ((day /10) << 4) + day %10;
week = ((week /10) << 4) + week %10;
hour = ((hour /10) << 4) + hour %10;
min = ((min /10) << 4) + min %10;
sec = ((sec /10) << 4) + sec %10;
//写入寄存器,同时标记一个地址
DS1307_Wirte(FLAG_ADDR,FLAG_VAL); //写入已经设置时间标志
DS1307_Wirte(0x00,sec); //设置秒
DS1307_Wirte(0x01,min); //设置分
DS1307_Wirte(0x02,hour); //设置时
DS1307_Wirte(0x03,week); //设置周
DS1307_Wirte(0x04,day); //设置日
DS1307_Wirte(0x05,mon); //设置月
DS1307_Wirte(0x06,year); //设置年
}
//获取当前时间
void Get_Now_Time(void)
{
uint8_t sec,min,hour,week,day,mon,year;
uint8_t i;
sec = DS1307_Read(0x00);
min = DS1307_Read(0x01);
hour = DS1307_Read(0x02);
week = DS1307_Read(0x03);
day = DS1307_Read(0x04);
mon = DS1307_Read(0x05);
year = DS1307_Read(0x06);
//BCD码转十进制
date_time.sec = (sec/16)*10 + sec%16;
date_time.min = (min/16)*10 + min%16;
date_time.hour = (hour/16)*10 + hour%16;
date_time.day = (day/16)*10 + day%16;
date_time.mon = (mon/16)*10 + mon%16;
date_time.year = (year/16)*10 + year%16 + 2000;
date_time.week = (week/16)*10 + week%16;
}