一、模块简介
芯片结构原理-toc" style="margin-left:40px;">DS3231时钟芯片结构原理
引脚图
内部结构图
DS3231典型应用电路
DS芯片结构3231时钟
二、与stm32通信
1、配置IO
2.初始化和函数
3.调用主函数
4、使用
源码下载 一、模块简介
DS3231高精度I2C具有集成温度补偿晶体振荡器的实时钟器件。该装置包含电池输入端,断开主电源时仍能保持精确计时。集成晶体振荡器可以提高装置的长期精度。DS3231寄存器可保存秒、分、时、周、日、月、年、闹钟设置等信息。月末日期可自动调整,包括闰年补偿。时钟的工作格式是24小时或带AM/PM指示12小时格式。DS3231提供两个可编程日历闹钟和一路可编程方波输出。DS单片机通过3231I2C传输地址和数据的双向串行总线。
DS3231高精度I2C实时时钟(RTC)集成温度补偿晶体振荡器(TCXO)。该装置包含电池输入端,断开主电源时仍能保持精确计时。集成晶体振荡器可以提高装置的长期精度。DS3231寄存器可保存秒、分、时、周、日、月、年、闹钟设置等信息。月末日期可自动调整,包括闰年补偿。时钟的工作格式是24小时或带AM/PM指示12小时格式。DS3231提供两个可编程日历闹钟和一路可编程方波输出。DS单片机通过3231I2C传输地址和数据的双向串行总线。
VCC为电源引脚;
INT/SQW低电平有效中断或方波输出:低电平有效复位引脚;
N.C.表示无连接,外部必须接地;
GND为地;
VBAT输入备用电源;
SDA输入输出串行数据;
SCL输入串行时钟。
下图为DS图中可以看到3231典型应用电路,DS3231几乎不需要外部元件。
如下图1所示,DS3231的主要组成部分有8个模块,分为4个功能组:TCXO、电源控制、按钮复位和RTC。
TCXO包括温度传感器、振荡器和控制逻辑。控制器读取片上的温度传感器输出,并使用查表法确定所需的电容AGE寄存器的老化修正。然后设置电容器,选择寄存器。只有在温度变化或用户启动的温度转换完成后,才能加载包括AGE新的寄存器变化值。VCC第一次上电时读取温度值,然后每隔64次 s读取一次。
DS3231寄存器地址00h~12h,用于存储秒、分、时、周、日、闹钟设置信息。如果地址在多字节访问期间到达RAM空间的结尾12h处,将发生卷绕,此时定位到开始位置即00h单元。DS通过读取相应的寄存器来设置和初始化3231的时间和日历信息。用户辅助缓冲区用于防止更新内部寄存器时可能出现的错误。读取时间和日历寄存器时,用户缓冲区在任何区域START当寄存器指针返回零时,与内部寄存器同步。这些辅助寄存器读取时间信息,此时钟继续运行。主寄存器在读取操作过程中更新时,可避免重读寄存器。控制寄存器(地址为0EH)例如,实时时钟、闹钟和方波输出可以控制。其各bit定义如下表所示。
BIT七位:使能振荡器(EOEC)。当设置为逻辑0时,启动振荡器。如果设置为逻辑1,则在DS3231电源切换至VBAT当振荡器停止时。第一次上电时,该位置被清除。 (逻辑0) 。当DS3231由VCC振荡器和EOSC位置状态无关,始终保持工作状态。
BIT6位:电池备份的方波使能(BBSOW)。温度转换不影响内部64 s更新周期。用户启动的温度转换约为2 ms内不会影响BSY位。CONV从写入到转换完成,位始终保持在1,转换完成后,CONV和BSY均变为0。用户启动转换状态时应使用CONV位。
BIT4和BIT三位:频率选择(RS2和RS第一次上电时,BIT
并将其设置为逻辑1DS3231由VBAT引脚供电时,未加载VCC在这种情况下,使能方波输出。BB-SQW若设置为逻辑0VCC如果低于电源故障门的限值,INT/SQW引脚变成高阻抗。第一次上电时,清零(逻辑0)。
BIT五位:转换温度(CONV)。当位置为1时,强制温度传感器将温度转换为数字并执行TCXO算法更新振荡器的电容阵列。只有在空闲时间才有效。BSY=1.禁止设置转换温度位。用户在强制控制器中开始新的TCXO操作前。检查状态位置BSY。用户启动的
4和BIT3设置为逻辑1。方波使能时用于控制方波输出的频率。RS1、RS逻辑值与方波输出频率的关系如表2所列。
BIT二:中断控制(INTCN)。该位控制INT/SQW信号。INTCN置为0时,INT/SQW引脚输出方波;INTCN如果计时寄存器与任何闹钟寄存器匹配,它将被触发INT/SQW信号(如果也能闹钟)。匹配时,相应的闹钟标志总是位置,与INTCN位置状态无关。第一次上电时,INTCN位置为逻辑1。
BIT1位闹钟2中断使能(A2IE)。该位置为逻辑1时,允许状态寄存器中的闹钟2标志位(A2F)触发INT/SQW信号(当INTCN=1时)。当A2IE位置为0或者INTCN置为0时,A2F位不启动中断信号。初次上电时,A2IE位清零(逻辑0)。
BIT0位:闹钟1中断使能(A1IE)。该位置为逻辑1时,允许状态寄存器中的闹钟1标志位(A1F)触发INT/SQW信号(当INTCN=1时)。当A1IE位置为0或者INTCN置为0时,A1F位不启动INT/SQW信号。初次上电时,A1IE位清零(逻辑0)。
电源控制功能由温度补偿电压基准(VPF)和监视VCC电平的比较器电路提供。当VCC高于VPF时,DS3231由VCC供电,当VCC低于VPF但高于VBAT时,DS3231由VCC供电;当VCC低于VPF并低于VBAT时,DS3231由VBAT供电。为保护电池,VBAT首次加到器件时振荡器并不启动,除非加载VCC,或者向器件写入一个有效的I2C地址。典型的振荡器启动时间在1 s以内。在VCC加电后或者有效的I2C地址写入后大约2 s,器件会测量一次温度,并使用计算的修正值校准振荡器。一旦振荡器运行,只要电源(VCC或者VBAT)有效就会一直保持工作状态。器件每隔64 s进行一次温度测量并校准振荡器频率。
可以通过读取适当的寄存器字节获得时钟和日历信息。通过写入适当的寄存器字节设定或者初始化时钟和日历数据。时钟和日历寄存器的内容采用二-十进制编码(BCD)格式。DS3231运行于12小时或者24小时模式。小时寄存器的第6位定义为12或24小时模式选择位。该位为高时,选择12小时模式。在12小时模式下,第5位为AM/PM指示位,逻辑高时为PM。
DS3231具有连接至RST输出引脚的按钮开关功能。若DS3231不在复位周期,会持续监视RST信号的下降沿。如果检测到一个边沿转换,DS3231通过拉低RST完成开关去抖。内部定时器定时结束后,DS3231继续监视RST信号。如果信号依旧保持低电平,DS3231持续监视信号线以检测上升沿。一旦检测到按钮释放,DS3231强制RST为低电平并保持tRST。RST还可用于指示电源故障报警情况。当VCC低于VPF时,产生内部电源故障报警信号,并强制拉低RST引脚。当VCC返回至超过VPF电平时。RST保持低电平大约250 ms(tREC),使供电电源达到稳定。如果在VCC加载时,振荡器不工作,将跳过tREC,RST立刻变为高电平。
二、与stm32通信
模块通过stm32模拟I2C信号通道,接在串口3或者4,下面上源码(串口3为例)
1、配置IO
/**
******************************************************************************
* @file bsp_i2c_ds3231.h
* @author 兲涳
* @version V1.0
* @date 2020-11-16
* @brief i2c RTC(DS3231)应用函数bsp
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __I2C_DS3231_H
#define __I2C_DS3231_H
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "stdio.h"
/* Exported types ------------------------------------------------------------*/
typedef struct
{
int hour;
int min;
int sec;
u32 year;
int month;
int date;
int week;
int temperature;
}_calendar_obj;
extern _calendar_obj calendar; //日历结构体
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/**************************I2C参数定义,I2C1或I2C2*****************************/
#define _I2C1 1
#define _I2C2 0
#if _I2C1
#define DS3231_I2Cx I2C1
#define DS3231_I2C_APBxClock_FUN RCC_APB1PeriphClockCmd
#define DS3231_I2C_CLK RCC_APB1Periph_I2C1
#define DS3231_I2C_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define DS3231_I2C_GPIO_CLK RCC_APB2Periph_GPIOC
#define DS3231_I2C_SCL_PORT GPIOC
#define DS3231_I2C_SCL_PIN GPIO_Pin_10
#define DS3231_I2C_SDA_PORT GPIOC
#define DS3231_I2C_SDA_PIN GPIO_Pin_11
#elif _I2C2
#define DS3231_I2Cx I2C2
#define DS3231_I2C_APBxClock_FUN RCC_APB1PeriphClockCmd
#define DS3231_I2C_CLK RCC_APB1Periph_I2C2
#define DS3231_I2C_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define DS3231_I2C_GPIO_CLK RCC_APB2Periph_GPIOB
#define DS3231_I2C_SCL_PORT GPIOB
#define DS3231_I2C_SCL_PIN GPIO_Pin_10
#define DS3231_I2C_SDA_PORT GPIOB
#define DS3231_I2C_SDA_PIN GPIO_Pin_11
#endif
/* STM32 I2C 快速模式 */
#define I2C_Speed 400000
/* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */
#define I2Cx_OWN_ADDRESS7 0X0A
/* DS3231 地址定义 */
#define DS3231_ADDRESS 0xD0
/*等待超时时间*/
#define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))
/*信息输出*/
#define DS3231_DEBUG_ON 0
#define DS3231_INFO(fmt,arg...) printf("<<-DS3231-INFO->> "fmt"\n",##arg)
#define DS3231_ERROR(fmt,arg...) printf("<<-DS3231-ERROR->> "fmt"\n",##arg)
#define DS3231_DEBUG(fmt,arg...) do{\
if(DS3231_DEBUG_ON)\
printf("<<-DS3231-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
}while(0)
/* DS3231寄存器地址 */
#define DS3231_SECOND 0x00 //秒
#define DS3231_MINUTE 0x01 //分
#define DS3231_HOUR 0x02 //时
#define DS3231_WEEK 0x03 //星期
#define DS3231_DAY 0x04 //日
#define DS3231_MONTH 0x05 //月
#define DS3231_YEAR 0x06 //年
/* 闹铃1 */
#define DS3231_SALARM1ECOND 0x07 //秒
#define DS3231_ALARM1MINUTE 0x08 //分
#define DS3231_ALARM1HOUR 0x09 //时
#define DS3231_ALARM1WEEK 0x0A //星期/日
/* 闹铃2 */
#define DS3231_ALARM2MINUTE 0x0b //分
#define DS3231_ALARM2HOUR 0x0c //时
#define DS3231_ALARM2WEEK 0x0d //星期/日
#define DS3231_CONTROL 0x0e //控制寄存器
#define DS3231_STATUS 0x0f //状态寄存器
#define BSY 2 //忙
#define OSF 7 //振荡器停止标志
#define DS3231_XTAL 0x10 //晶体老化寄存器
#define DS3231_TEMPERATUREH 0x11 //温度寄存器高字节(8位)
#define DS3231_TEMPERATUREL 0x12 //温度寄存器低字节(高2位)
/* Exported functions ------------------------------------------------------- */
void I2C_DS3231_Init(void);
uint32_t I2C_DS3231_ByteWrite(u8 WriteAddr, u8 data);
uint8_t I2C_DS3231_DataRead(u8 ReadAddr);
void I2C_WaitDs3231StandbyState(void);
uint8_t BCD_DEC(u8 val);
uint8_t DEC_BCD(u8 val);
void I2C_DS3231_SetTime(u8 yea,u8 mon,u8 da,u8 we,u8 hou,u8 min,u8 sec);
void Time_Regulate_Get(_calendar_obj *tm);
void I2C_DS3231_getTime(void);
void I2C_DS3231_getTemperature(void);
void GregorianDay(_calendar_obj * tm);
#endif /* __I2C_DS3231_H */
/*********************************************END OF FILE**********************/
2、初始化与函数
/**
******************************************************************************
* @file bsp_i2c_ds3231.c
* @author 兲涳
* @version V1.0
* @date 2020-11-16
* @brief i2c RTC(DS3231)应用函数bsp
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "bsp_i2c_ds3231.h"
/* Private typedef -----------------------------------------------------------*/
_calendar_obj calendar;
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static __IO uint32_t I2CTimeout = I2CT_LONG_TIMEOUT;
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief 转换成BCD码
* @param 无
* @retval 无
*/
u16 B_BCD(u8 val)
{
u8 i,j,k;
i=val/10;
j=val%10;
k=j+(i<<4);
return k;
}
/**
* @brief I2C I/O配置
* @param 无
* @retval 无
*/
static void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能与 I2C 有关的时钟 */
DS3231_I2C_APBxClock_FUN ( DS3231_I2C_CLK, ENABLE );
DS3231_I2C_GPIO_APBxClock_FUN ( DS3231_I2C_GPIO_CLK, ENABLE );
/* I2C_SCL、I2C_SDA*/
GPIO_InitStructure.GPIO_Pin = DS3231_I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏复用输出
GPIO_Init(DS3231_I2C_SCL_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DS3231_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏复用输出
GPIO_Init(DS3231_I2C_SDA_PORT, &GPIO_InitStructure);
}
/**
* @brief I2C 工作模式配置
* @param 无
* @retval 无
*/
static void I2C_Mode_Configu(void)
{
I2C_InitTypeDef I2C_InitStructure;
/* 通信速率 */
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
/* I2C 配置 */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
/* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
/* 地址设置 */
I2C_InitStructure.I2C_OwnAddress1 = I2Cx_OWN_ADDRESS7;
/* 使能应答 */
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
/* I2C的寻址模式 */
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
/* I2C 初始化 */
I2C_Init(DS3231_I2Cx, &I2C_InitStructure);
/* 使能 I2C */
I2C_Cmd(DS3231_I2Cx, ENABLE);
}
/**
* @brief I2C 外设(DS3231)初始化
* @param 无
* @retval 无
*/
void I2C_DS3231_Init(void)
{
I2C_GPIO_Config();
I2C_Mode_Configu();
}
/**
* @brief 写一个字节到I2C DS3231中
* @param
* @arg data:要写入的字节
* @arg WriteAddr:写地址
* @retval 返回1,表示写入成功.
*/
uint32_t I2C_DS3231_ByteWrite(u8 WriteAddr, u8 data)
{
/* 产生 I2C 起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV5 事件并清除标志*/
while (!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 发送 DS3231 设备地址 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Transmitter);
/* 检测 EV6 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
}
/* 发送要写入的 DS3231 内部地址(即 DS3231 内部存储器的地址) */
I2C_SendData(DS3231_I2Cx, WriteAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV8 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
}
/* 发送一字节要写入的数据 */
I2C_SendData(DS3231_I2Cx, data);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV8 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
/* 发送停止信号 */
I2C_GenerateSTOP(DS3231_I2Cx, ENABLE);
return 1;
}
/**
* @brief 从DS3231里面读取一个字节数据
* @param
* @arg data:存放从DS3231读取的数据
* @arg ReadAddr:读取数据的DS3231的地址
* @retval data:返回数据.
*/
uint8_t I2C_DS3231_DataRead(u8 ReadAddr)
{
uint8_t data;
I2CTimeout = I2CT_LONG_TIMEOUT;
/* 等待空闲 */
while(I2C_GetFlagStatus(DS3231_I2Cx, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
}
/* 产生 I2C 起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV5 事件并清除标志*/
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
}
/* 发送 DS3231 设备地址,写 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Transmitter);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV6 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
}
/* 通过重新设置 PE 位清除 EV6 事件 */
I2C_Cmd(DS3231_I2Cx, ENABLE);
/* 发送要读取的 DS3231 内部地址(即 DS3231 内部存储器的地址) */
I2C_SendData(DS3231_I2Cx, ReadAddr);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV8 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
}
/* 产生第二次 I2C 起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV5 事件并清除标志*/
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
}
/* 发送 DS3231 设备地址,读 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Receiver);
/* 设置超时等待时间 */
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* 检测 EV6 事件并清除标志 */
while(!I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
}
/* 发送非应答信号 */
I2C_AcknowledgeConfig(DS3231_I2Cx, DISABLE);
/* 发送停止信号 */
I2C_GenerateSTOP(DS3231_I2Cx, ENABLE);
/* 检测 EV7 事件并清除标志 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_CheckEvent(DS3231_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
/* 通过 I2C,从设备中读取一个字节的数据 */
data = I2C_ReceiveData(DS3231_I2Cx);
/* 使能应答,方便下一次 I2C 传输 */
I2C_AcknowledgeConfig(DS3231_I2Cx, ENABLE);
return data;
}
/**
* @brief 等待 DS3231 到准备状态
* @param 无
* @retval 无
*/
void I2C_WaitDs3231StandbyState(void)
{
vu16 SR1_Tmp = 0;
do
{
/* 发送起始信号 */
I2C_GenerateSTART(DS3231_I2Cx, ENABLE);
/* 读 I2C1 SR1 寄存器 */
SR1_Tmp = I2C_ReadRegister(DS3231_I2Cx, I2C_Register_SR1);
/* 发送 DS3231 地址 + 方向 */
I2C_Send7bitAddress(DS3231_I2Cx, DS3231_ADDRESS, I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(DS3231_I2Cx, I2C_Register_SR1) & 0x0002));
/* 清除 AF 位 */
I2C_ClearFlag(DS3231_I2Cx, I2C_FLAG_AF);
/* 发送停止信号 */
I2C_GenerateSTOP(DS3231_I2Cx, ENABLE);
}
/**
* @brief BCD(8421)转DEC.
* @param val:BCD码.
* @retval i:DEC码.
*/
uint8_t BCD_DEC(u8 val)
{
u8 i;
i= val&0x0f;
val >>= 4;
val &= 0x0f;
val *= 10;
i += val;
return i;
}
/**
* @brief BCD(8421)转DEC.
* @param val:DEC码.
* @retval k:BCD码.
*/
uint8_t DEC_BCD(u8 val)
{
u8 i,j,k;
i=val/10;
j=val%10;
k=j+(i<<4);
return k;
}
/**
* @brief 超时报警处理.
* @param errorCode:错误代码,可以用来定位是哪个环节出错.
* @retval 返回0,表示IIC读取失败.
*/
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* 使用串口 printf 输出错误信息,方便调试 */
DS3231_ERROR("I2C 等待超时!errorCode = %d",errorCode);
return 0;
}
/**
* @brief 时间设置
* @param
* @arg 分别输入 年 月 日 星期 时 分 秒
* @retval 无
*/
void I2C_DS3231_SetTime(u8 yea,u8 mon,u8 da,u8 we,u8 hou,u8 min,u8 sec)
{
u8 temp=0;
temp=DEC_BCD(yea);
I2C_DS3231_ByteWrite(0x06,temp);
temp=DEC_BCD(mon);
I2C_DS3231_ByteWrite(0x05,temp);
temp=DEC_BCD(da);
I2C_DS3231_ByteWrite(0x04,temp);
// temp=DEC_BCD(we);
// I2C_DS3231_ByteWrite(0x03,temp);
temp=DEC_BCD(hou);
I2C_DS3231_ByteWrite(0x02,temp);
temp=DEC_BCD(min);
I2C_DS3231_ByteWrite(0x01,temp);
temp=DEC_BCD(sec);
I2C_DS3231_ByteWrite(0x00,temp);
}
/**
* @brief 获取时间
* @param
* @arg pBuffer:存放从DS3231读取的数据的缓冲区指针
* @arg ReadAddr:读取数据的DS3231的地址
* @arg NumByteToWrite:要从DS3231读取的字节数
* @retval 返回1,表示读取成功.
*/
void I2C_DS3231_getTime(void)
{
calendar.year=I2C_DS3231_DataRead(0x06);
calendar.year=BCD_DEC(calendar.year);
calendar.month=I2C_DS3231_DataRead(0x05);
calendar.month=BCD_DEC(calendar.month);
calendar.date=I2C_DS3231_DataRead(0x04);
calendar.date=BCD_DEC(calendar.date);
calendar.week=I2C_DS3231_DataRead(0x03);
calendar.week=BCD_DEC(calendar.week);
calendar.hour=I2C_DS3231_DataRead(0x02);
calendar.hour&=0x3f;
calendar.hour=BCD_DEC(calendar.hour);
calendar.min=I2C_DS3231_DataRead(0x01);
calendar.min=BCD_DEC(calendar.min);
calendar.sec=I2C_DS3231_DataRead(0x00);
calendar.sec=BCD_DEC(calendar.sec);
}
/**
* @brief 保存用户使用串口设置的时间
* @param
* @arg tm:用于设置RTC时间的结构体指针
* @retval
*/
void Time_Regulate_Get(_calendar_obj *tm)
{
uint32_t temp_num = 0;
uint8_t day_max=0 ;
printf("\r\n=========================设置时间==================");
do
{
printf("\r\n 请输入年份(Please Set Years),范围[2000~2255],输入字符后请加回车:");
scanf("%d",&temp_num);
if(temp_num <2000 || temp_num >65535)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 年份被设置为: %d\n\r", temp_num);
temp_num-=2000;
DEC_BCD(temp_num);
printf("\n\r 年份被设置为11: %d\n\r", temp_num);
tm->year = temp_num;
printf("year = %d %d\n",calendar.year,temp_num);
break;
}
}while(1);
do
{
printf("\r\n 请输入月份(Please Set Months):范围[1~12],输入字符后请加回车:");
scanf("%d",&temp_num);
if(temp_num <1 || temp_num >12)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 月份被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->month = temp_num;
break;
}
}while(1);
/*根据月份计算最大日期*/
switch(tm->month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
day_max = 31;
break;
case 4:
case 6:
case 9:
case 11:
day_max = 30;
break;
case 2:
/*计算闰年*/
if(((tm->year+2000)%4==0) &&
(((tm->year+2000)%100!=0) || ((tm->year+2000)%400==0)))
{
day_max = 29;
} else
{
day_max = 28;
}
break;
}
do
{
printf("\r\n 请输入日期(Please Set Months),范围[1~%d],输入字符后请加回车:",day_max);
scanf("%d",&temp_num);
if(temp_num <1 || temp_num >day_max)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 日期被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->date = temp_num;
break;
}
}while(1);
GregorianDay( tm );
do
{
printf("\r\n 请输入时钟(Please Set Hours),范围[0~23],输入字符后请加回车:");
scanf("%d",&temp_num);
if( temp_num >23)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 时钟被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->hour = temp_num;
break;
}
}while(1);
do
{
printf("\r\n 请输入分钟(Please Set Minutes),范围[0~59],输入字符后请加回车:");
scanf("%d",&temp_num);
if( temp_num >59)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 分钟被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->min = temp_num;
break;
}
}while(1);
do
{
printf("\r\n 请输入秒钟(Please Set Seconds),范围[0~59],输入字符后请加回车:");
scanf("%d",&temp_num);
if( temp_num >59)
{
printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
}
else
{
printf("\n\r 秒钟被设置为: %d\n\r", temp_num);
DEC_BCD(temp_num);
tm->sec = temp_num;
break;
}
}while(1);
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
}
/**
* @brief 获取温度
* @param 无
* @retval 无
*/
void I2C_DS3231_getTemperature(void)
{
I2C_DS3231_ByteWrite(DS3231_CONTROL, 0x20|0x05);
calendar.temperature=I2C_DS3231_DataRead(DS3231_TEMPERATUREH);
}
/*计算公历天数得出星期*/
void GregorianDay(_calendar_obj * tm)
{
int leapsToDate;
int lastYear;
int day;
int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
lastYear=tm->year-1;
/*计算从公元元年到计数的前一年之中一共经历了多少个闰年*/
leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
/*如若计数的这一年为闰年,且计数的月份在2月之后,则日数加1,否则不加1*/
if((tm->year%4==0) &&
((tm->year%100!=0) || (tm->year%400==0)) &&
(tm->month>2)) {
/*
* We are past Feb. 29 in a leap year
*/
day=1;
} else {
day=0;
}
day += lastYear*365 + leapsToDate + MonthOffset[tm->month-1] + tm->date; /*计算从公元元年元旦到计数日期一共有多少天*/
tm->week=day%7; //算出星期
}
/*********************************************END OF FILE**********************/
3、主函数调用
/**
******************************************************************************
* @file main.c
* @author 兲涳
* @version V1.0
* @date 2020-11-16
* @brief
******************************************************************************
* @attention
* I2C_2 RTC(DS3231)测试,测试信息通过USART1打印在电脑调试助手上,通过串口设置时间
* !!!串口输入 t 回车 ,进行设置时间。
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_i2c_ds3231.h"
#include <string.h>
/* Private typedef -----------------------------------------------------------*/
uint8_t i=0;
extern _calendar_obj calendar; //日历结构体
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
#define SOFT_DELAY Delay(0x4FFFFF);
/* Private variables ---------------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void Delay(__IO u32 nCount);
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/* 初始化USART 配置模式为 115200 8-N-1 */
USART_Config();
printf("\nDS3231 RTC时钟\n");
/* I2C 外设初(DS3231)始化 */
I2C_DS3231_Init();
// I2C_DS3231_SetTime(20,8,24,1,13,43,20);
while(1)
{
SOFT_DELAY;
if(i==1)
{
Time_Regulate_Get( &calendar );
I2C_DS3231_SetTime(calendar.year, calendar.month, calendar.date, calendar.week, calendar.hour, calendar.min, calendar.sec);
i=0;
}
I2C_DS3231_getTime(); //获取时间
I2C_DS3231_getTemperature(); //获取温度
printf("%d年%d月%d日%d时%d分%d秒 星期%d 温度%d\n",calendar.year+2000,calendar.month,calendar.date,\
calendar.hour,calendar.min,calendar.sec,calendar.week,calendar.temperature);//打印到串口屏不能有printf("\n");换行!!!
}
}
/**
* @brief 延时函数
* @param 无
* @retval 无
*/
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
/*********************************************END OF FILE**********************/
4、使用
在串口工具上,输入t,即可进入设置时钟。该程序自动在串口上打印时间及温度