基于DS3231时钟模块的stm32 程序,源码

目录

一、模块简介

DS3231时钟芯片结构原理

引脚图

内部结构图

DS3231典型应用电路

DS3231时钟芯片结构

二、与stm32通信

1、配置IO

2、初始化与函数

3、主函数调用

4、使用


源码下载
一、模块简介

DS3231是一款高精度I2C实时时钟器件,具有集成的温度补偿晶体振荡器。该器件包含电池输入端,断开主电源时仍可保持精确计时。集成的晶体振荡器可提高器件的长期精确度。DS3231的寄存器能保存秒、分、时、星期、日期、月、年和闹钟设置等信息。少于31天的月份,可自动调整月末日期,包括闰年补偿。时钟的工作格式为24小时或带AM/PM指示的12小时格式。DS3231提供两个可编程日历闹钟和一路可编程方波输出。DS3231与单片机通过I2C双向串行总线传输地址与数据。

DS3231时钟芯片结构原理

DS3231是一款高精度I2C实时时钟(RTC)器件,具有集成的温度补偿晶体振荡器(TCXO)。该器件包含电池输入端,断开主电源时仍可保持精确计时。集成的晶体振荡器可提高器件的长期精确度。DS3231的寄存器能保存秒、分、时、星期、日期、月、年和闹钟设置等信息。少于31天的月份,可自动调整月末日期,包括闰年补偿。时钟的工作格式为24小时或带AM/PM指示的12小时格式。DS3231提供两个可编程日历闹钟和一路可编程方波输出。DS3231与单片机通过I2C双向串行总线传输地址与数据。

引脚图

 

ds3231时钟模块怎么用_DS3231的操作与使用

ds3231时钟模块怎么用_DS3231的操作与使用

VCC为电源引脚;

INT/SQW为低电平有效中断或方波输出:是低电平有效复位引脚;

N.C.表示无连接,外部必须接地;

GND为地;

VBAT为备用电源输入;

SDA为串行数据输入输出;

SCL为串行时钟输入。

内部结构图

ds3231时钟模块怎么用_DS3231的操作与使用

DS3231典型应用电路

下图为DS3231典型应用电路,图中可看出,DS3231几乎不需要外部元件。

ds3231时钟模块怎么用_DS3231的操作与使用

 

 

DS3231时钟芯片结构

如下图1所示,DS3231的主要组成部分有8个模块,划分为4个功能组:TCXO、电源控制、按钮复位和RTC。

1. 32 kHz的TCXO

TCXO包括温度传感器、振荡器和控制逻辑。控制器读取片上温度传感器输出,使用查表法确定所需的电容,加上AGE寄存器的老化修正。然后设置电容选择寄存器。仅在温度变化或者用户启动的温度转换完成时,才加载包括AGE寄存器变化的新值。VCC初次上电时就会读取温度值,然后每隔64 s读取一次。

2. DS3231的内部寄存器及功能

DS3231寄存器地址为00h~12h,分别用于存放秒、分、时、星期、日期及闹钟设置信息。在多字节访问期间,如果地址达到RAM空间的结尾12h处,将发生卷绕,此时定位到开始位置即00h单元。DS3231的时间和日历信息通过读取相应的寄存器来设置和初始化。用户辅助缓冲区用于防止内部寄存器更新时可能出现的错误。读取时间和日历寄存器时,用户缓冲区在任何START条件下或者寄存器指针返回到零时与内部寄存器同步。时间信息从这些辅助寄存器读取,此时时钟继续保持运行状态。这样在读操作期间发生主寄存器更新时可以避免重新读取寄存器。以控制寄存器(地址为0EH)为例,可以控制实时时钟、闹钟和方波输出。其各bit定义如下表。

ds3231时钟模块怎么用_DS3231的操作与使用

BIT7位:使能振荡器(EOEC)。设定为逻辑0时,启动振荡器。如果设定为逻辑1,在DS3231电源切换至VBAT时,振荡器停止。初次上电时该位清零 (逻辑0) 。当DS3231由VCC供电时,振荡器与EOSC位的状态无关,始终保持工作状态。

BIT6位:电池备份的方波使能(BBSOW)。温度转换不影响内部64 s更新周期。用户启动的温度转换在大约2 ms内不会影响BSY位。CONV位从写入开始直到转换完成一直保持为1,转换完后,CONV和BSY均变为0。在监视用户启动转换状态时,应使用CONV位。

BIT4和BIT3位:频率选择(RS2和RS1),初次上电时,BIT

当设定为逻辑1并且DS3231由VBAT引脚供电时,在没有加载VCC的情况下,该位使能方波输出。当BB-SQW设定为逻辑0时,若VCC降至低于电源故障门限值,则INT/SQW引脚变为高阻抗。初次上电时,该位清零(逻辑0)。

BIT5位:转换温度(CONV)。该位置为1时,强制温度传感器将温度转换成数字,并执行TCXO算法更新振荡器的电容阵列。只在空闲期间有效。状态位BSY=1时,禁止设定转换温度位。用户在强制控制器开始新的TCXO操作之前。应检查状态位BSY。用户启动的

4和BIT3设置为逻辑1。方波使能时用于控制方波输出的频率。RS1、RS2的逻辑值与方波输出频率的关系如表2所列。

ds3231时钟模块怎么用_DS3231的操作与使用

BIT2位:中断控制(INTCN)。该位控制INT/SQW信号。INTCN置为0时,INT/SQW引脚输出方波;INTCN置为1时,若计时寄存器与任一个闹钟寄存器相匹配,则会触发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)。

3. DS3231的电源控制

电源控制功能由温度补偿电压基准(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进行一次温度测量并校准振荡器频率。

4. DS3231的时钟和日历RTC

可以通过读取适当的寄存器字节获得时钟和日历信息。通过写入适当的寄存器字节设定或者初始化时钟和日历数据。时钟和日历寄存器的内容采用二-十进制编码(BCD)格式。DS3231运行于12小时或者24小时模式。小时寄存器的第6位定义为12或24小时模式选择位。该位为高时,选择12小时模式。在12小时模式下,第5位为AM/PM指示位,逻辑高时为PM。

5. DS3231的复位按钮

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,即可进入设置时钟。该程序自动在串口上打印时间及温度

 

 

  • 14
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值