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 HighSDA_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 HighSDA_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 readingSET_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;
}