精华内容
下载资源
问答
  • 资源为一个完整的基于STM32F103驱动DS3231工程,使用了硬件IIC与DS3231通信,工程已经通过了串口二测试,直接使用Keil5 MDK打开即可,方便移植。
  • 使用stm32 i2c读取ds3231例程
  • STM32 DS3231驱动.zip

    2020-07-16 17:26:43
    资源内有基于STM32DS3231驱动,分别使用STM32模拟IIC与硬件IIC与DS3231通信,工程可使用Keil5 MDK直接打开,亲测可用。
  • DS3231是低成本,高精度I2C实时时钟(RTC),具有集成的温补晶体振荡器(TCXO)和1个32.768kHz的晶体。该晶体包含电池输入端,断开主电源仍可保持精确计时。
  • ds3231时钟stm32文件包

    2020-10-16 18:10:16
    ds3231时钟stm32文件包,包括iic,ds3231读写,简单读取和设置时间,可以通过串口读取和设置时间
  • STM32ds3231实时时钟模块的I2C通讯,学习I2C和实时时钟的好资料
  • STM32F1串口打印DS3231时间输出,测试.直接可以使用的,使用的原子STM32的工程模版通俗易懂 //ALIENTEK战舰STM32开发板实验22 //IIC 实验 //技术支持:www.openedv.com //广州市星翼电子科技有限公司
  • 关于DS3231模块我在我的博客基于DS3231时钟模块的stm32 程序,源码中有介绍 ,大家可以自己看下。我这篇直接讲HAL的代码。 一、配置IO /** /** *************************************************************...

    关于DS3231模块介绍我在我的博客基于DS3231时钟模块的stm32 程序,源码中有介绍 ,大家可以自己看下。我这篇直接讲HAL的代码。

    一、配置IO

    /**
      /**
    	******************************************************************************
    	* @file    bsp_i2c_ds3231.h  
    	* @author  兲涳
    	* @version V1.0
    	* @date    2020-11-18
    	* @brief   模拟i2c RTC(DS3231)应用函数bsp
    	******************************************************************************
    	* @attention
    	* 
    	******************************************************************************
    	*/ 
    	  
    
      /******************************************************************************
      * @attention
      * 
      ******************************************************************************
      */ 
    	
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __I2C_DS3231_H
    #define	__I2C_DS3231_H
    
    /* Includes ------------------------------------------------------------------*/
    
    
    #ifdef _I2C_DS3231_PUBLIC_
    #define PEXT 
    #else
    #define PEXT extern
    #endif
    
    
    /* Exported types ------------------------------------------------------------*/
    typedef struct 
    {
    	u8  hour;
    	u8  min;
    	u8  sec;			
    	u32 year;
    	u8  month;
    	u8  day;
    	u8  week;
    	u16 temperature;
    }_calendar_obj;	
    
    
    /* Exported constants --------------------------------------------------------*/
    /* Exported macro ------------------------------------------------------------*/
    /**************************I2C参数定义,I2C1或I2C2*****************************/
    
    
    
    
    //#define	DS3231_I2C_GPIO_APBxClock_FUN   RCC_AHB1PeriphClockCmd
    //#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
    
    
    											 
    #define 	I2C_WR		0		
    #define 	I2C_RD		1
    
    
    #define		I2C_SCL_1()  			HAL_GPIO_WritePin(DS3231_I2C_SCL_PORT, DS3231_I2C_SCL_PIN,GPIO_PIN_SET)		
    #define 	I2C_SCL_0()  			HAL_GPIO_WritePin(DS3231_I2C_SCL_PORT, DS3231_I2C_SCL_PIN,GPIO_PIN_RESET)		
    #define 	I2C_SDA_1()           	HAL_GPIO_WritePin(DS3231_I2C_SDA_PORT, DS3231_I2C_SDA_PIN,GPIO_PIN_SET)		
    #define 	I2C_SDA_0()           	HAL_GPIO_WritePin(DS3231_I2C_SDA_PORT, DS3231_I2C_SDA_PIN,GPIO_PIN_RESET)		
    #define 	I2C_SDA_READ()        	HAL_GPIO_ReadPin(DS3231_I2C_SDA_PORT, DS3231_I2C_SDA_PIN)	
    
    
    /* STM32 I2C 快速模式 */
    #define             I2C_Speed             										 400000  
    /* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */
    #define 						I2Cx_OWN_ADDRESS7     										 0X0A   
    /* DS3231 地址定义 */
    #define 						DS3231_ADDRESS 														 0xD0   
    
    /* 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    //年 											 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位) 																				
    
    
    
    PEXT _calendar_obj calendar;	//日历结构体
    
    /* Exported functions ------------------------------------------------------- */																				
    PEXT void I2C_DS3231_Init(void);	
    PEXT void i2c_Stop(void);
    PEXT uint32_t I2C_DS3231_ByteWrite(u8 WriteAddr, u8 data); 	
    PEXT uint8_t I2C_DS3231_DataRead(u8 ReadAddr);																			
    PEXT uint8_t BCD_DEC(u8 val);		
    PEXT uint8_t DEC_BCD(u8 val);
    PEXT void i2c_SendByte(uint8_t _ucByte);
    PEXT void I2C_DS3231_SetTime(u8 yea,u8 mon,u8 da,u8 we,u8 hou,u8 min,u8 sec);
    PEXT void Time_Regulate_Get(_calendar_obj *tm);
    
    PEXT void I2C_DS3231_getTime(void);
    PEXT void I2C_DS3231_getTemperature(void);
    
    PEXT void GregorianDay(_calendar_obj * tm);
    
    #undef PEXT
    #endif
    
    /*********************************************END OF FILE**********************/
    

    二、初始化与函数

    #define		_I2C_DS3231_PUBLIC_
    #include	"common.h"
    
    
    /**
      ******************************************************************************
      * @file    bsp_i2c_ds3231.c 
      * @author  兲涳
      * @version V1.0
      * @date    2020-11-16
      * @brief   i2c RTC(DS3231)应用函数bsp
      ******************************************************************************
      * @attention
      * 
      ******************************************************************************
      */
    
    
    /**
      * @brief  转换成BCD码
      * @param  None
      * @retval 返回BCD码
      */
    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  None
      * @retval None
      */
    static void I2C_GPIO_Config(void)
    {
    	GPIO_InitTypeDef GPIO_Initure;
    	
    	/* 使能与 I2C 有关的时钟 */
      	__HAL_RCC_GPIOC_CLK_ENABLE();           	// 使能GPIOC端口时钟
    	
    	/* I2C_SCL、I2C_SDA*/
      	GPIO_Initure.Mode=GPIO_MODE_OUTPUT_OD;  	//开漏输出
    	GPIO_Initure.Pull=GPIO_PULLUP;          	//上拉
    	GPIO_Initure.Speed=GPIO_SPEED_HIGH;    	 	//高速	
    	GPIO_Initure.Pin=GPIO_PIN_10|GPIO_PIN_11; 						//PC3
        HAL_GPIO_Init(GPIOC,&GPIO_Initure);
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10|GPIO_PIN_11,GPIO_PIN_SET);	//初始化后置1
    }
    
    /**
      * @brief  I2C 外设(DS3231)初始化
      * @param  None
      * @retval None
      */
    void I2C_DS3231_Init(void)
    {
      I2C_GPIO_Config(); 
    	
    	/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
    	i2c_Stop();
    }
    
    /**
      * @brief  I2C总线位延迟,最快400KHz
      * @param  None
      * @retval None
      */
    static void i2c_Delay(void)
    {
    	uint8_t i;
    
    	/* 
    	 	下面的时间是通过逻辑分析仪测试得到的。
        工作条件:CPU主频72MHz ,MDK编译环境,1级优化
      
    		循环次数为10时,SCL频率 = 205KHz 
    		循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
    	 	循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
    	*/
    	for (i = 0; i < 10; i++);
    }
    
    /**
      * @brief  I2C总线启动信号
      * @param  None
      * @retval None
      */
    void i2c_Start(void)
    {
    	/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
    	I2C_SDA_1();
    	I2C_SCL_1();
    	i2c_Delay();
    	I2C_SDA_0();
    	i2c_Delay();
    	I2C_SCL_0();
    	i2c_Delay();
    }
    
    /**
      * @brief  I2C总线停止信号
      * @param  None
      * @retval None
      */
    void i2c_Stop(void)
    {
    	/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
    	I2C_SDA_0();
    	I2C_SCL_1();
    	i2c_Delay();
    	I2C_SDA_1();
    }
    
    /**
      * @brief  MCU向I2C总线设备发送8bit数据
      * @param   
      *		@arg 	_ucByte:发送的字节
    	* @retval None
      */
    void i2c_SendByte(uint8_t _ucByte)
    {
    	uint8_t i;
    
    	/* 先发送字节的高位bit7 */
    	for (i = 0; i < 8; i++)
    	{		
    		if (_ucByte & 0x80)
    		{
    			I2C_SDA_1();
    		}
    		else
    		{
    			I2C_SDA_0();
    		}
    		i2c_Delay();
    		I2C_SCL_1();
    		i2c_Delay();	
    		I2C_SCL_0();
    		if (i == 7)
    		{
    			 I2C_SDA_1(); // 释放总线
    		}
    		_ucByte <<= 1;	/* 左移一个bit */
    		i2c_Delay();
    	}
    }
    
    /**
      * @brief  MCU从I2C总线设备读取8bit数据
      * @param  None
    	* @retval 读到的数据
      */
    uint8_t i2c_ReadByte(void)
    {
    	uint8_t i;
    	uint8_t value;
    
    	/* 读到第1个bit为数据的bit7 */
    	value = 0;
    	for (i = 0; i < 8; i++)
    	{
    		value <<= 1;
    		I2C_SCL_1();
    		i2c_Delay();
    		if (I2C_SDA_READ())
    		{
    			value++;
    		}
    		I2C_SCL_0();
    		i2c_Delay();
    	}
    	return value;
    }
    
    /**
      * @brief  MCU产生一个时钟,并读取器件的ACK应答信号
      * @param  None
    	* @retval 返回0表示正确应答,1表示无器件响应
      */
    uint8_t i2c_WaitAck(void)
    {
    	uint8_t re;
    
    	I2C_SDA_1();	/* CPU释放SDA总线 */
    	i2c_Delay();
    	I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    	i2c_Delay();
    	if (I2C_SDA_READ())	/* CPU读取SDA口线状态 */
    	{
    		re = 1;
    	}
    	else
    	{
    		re = 0;
    	}
    	I2C_SCL_0();
    	i2c_Delay();
    	return re;
    }
    
    /**
      * @brief  MCU产生一个ACK信号
      * @param  None
    	* @retval None
      */
    void i2c_Ack(void)
    {
    	I2C_SDA_0();	/* CPU驱动SDA = 0 */
    	i2c_Delay();
    	I2C_SCL_1();	/* CPU产生1个时钟 */
    	i2c_Delay();
    	I2C_SCL_0();
    	i2c_Delay();
    	I2C_SDA_1();	/* CPU释放SDA总线 */
    }
    
    /**
      * @brief  MCU产生1个NACK信号
      * @param  None
    	* @retval None
      */
    void i2c_NAck(void)
    {
    	I2C_SDA_1();	/* CPU驱动SDA = 1 */
    	i2c_Delay();
    	I2C_SCL_1();	/* CPU产生1个时钟 */
    	i2c_Delay();
    	I2C_SCL_0();
    	i2c_Delay();	
    }
    
    /**
      * @brief  检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
      * @param   
      *		@arg  _Address:设备的I2C总线地址
    	* @retval 返回值 0 表示正确, 返回1表示未探测到
      */
    uint8_t i2c_CheckDevice(uint8_t _Address)
    {
    	uint8_t ucAck;
    
    	I2C_DS3231_Init();		/* 配置GPIO */
    
    	
    	i2c_Start();		/* 发送启动信号 */
    
    	/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
    	i2c_SendByte(_Address | I2C_WR);
    	ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */
    
    	i2c_Stop();			/* 发送停止信号 */
    
    	return ucAck;
    }
    
    //
    /**
      * @brief	写一个字节到I2C DS3231中
      * @param   
      *		@arg data:要写入的字节
      *		@arg WriteAddr:写地址 
    	* @retval 返回1,表示写入成功.
      */
    uint32_t I2C_DS3231_ByteWrite(u8 WriteAddr, u8 data) 
    {
    	/* 第1步:发起I2C总线启动信号 */
    	i2c_Start();
    	
    	/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    	i2c_SendByte(DS3231_ADDRESS| I2C_WR);	/* 此处是写指令 */
    	
    	/* 第3步:等待ACK */
    	if (i2c_WaitAck() != 0)
    	{
     		goto cmd_fail;	/* EEPROM器件无应答 */
    	}
    	
    	/* 第4步:发送寄存器地址 */
    	i2c_SendByte((uint8_t)WriteAddr);
    	
      /* 第5步:等待ACK */
    	if (i2c_WaitAck() != 0)
    	{
    	  goto cmd_fail;	/* EEPROM器件无应答 */
    	}
    	
    	/* 第6步:开始写入数据 */
    	i2c_SendByte(data);
    	
    	/* 第7步:等待ACK */
    	if (i2c_WaitAck() != 0)
    	{
    		goto cmd_fail;	/* EEPROM器件无应答 */
    	}
    	
      /* 命令执行成功,发送I2C总线停止信号 */
    	i2c_Stop();
    	return 1;
    cmd_fail: /* 命令执行失败后,发送停止信号 */
    	/* 发送I2C总线停止信号 */
    	i2c_Stop();
    	return 0;
    }
    
    /**
      * @brief	从DS3231里面读取一个字节数据 
      * @param   
      *		@arg data:存放从DS3231读取的数据
      *		@arg ReadAddr:读取数据的DS3231的地址
    	* @retval data:返回数据.
      */
    uint8_t I2C_DS3231_DataRead(u8 ReadAddr)
    {
    	uint8_t data;
    
    	/* 第1步:发起I2C总线启动信号 */
    	i2c_Start();
    
    	/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    	i2c_SendByte(DS3231_ADDRESS|I2C_WR);	/* 此处是写指令 */
      
    	/* 第3步:等待ACK */
    	if (i2c_WaitAck() != 0)
    	{
    		goto cmd_fail;	/* EEPROM器件无应答 */
    	}
    	
    	/* 第4步:发送DS3231寄存器地址 */
    	i2c_SendByte((uint8_t)ReadAddr);
    	
    	/* 第5步:等待ACK */
    	if (i2c_WaitAck() != 0)
    	{
    		goto cmd_fail;	/* EEPROM器件无应答 */
    	}
    	
    	/* 第6步:产生第二次 I2C 起始信号 */  
      	i2c_Start();
    	
    	/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    	//i2c_SendByte(DS3231_ADDRESS | I2C_RD);	/* 此处是读指令 */
    	i2c_SendByte(DS3231_ADDRESS | I2C_RD);	/* 此处是读指令 */
    	
    	/* 第8步:发送ACK */
    	if (i2c_WaitAck() != 0)
    	{
    		goto cmd_fail;	/* EEPROM器件无应答 */
    	}	
       
      	/* 第9步:读取数据 */
    	data = i2c_ReadByte(); 
     
      	i2c_NAck();	
    	i2c_Stop();
    	return data;
    	
      	cmd_fail: /* 命令执行失败后,发送停止信号 */
    	/* 发送I2C总线停止信号 */
    	i2c_Stop();
    	return 0;
    }
    
    /**
      * @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   
      *		@arg 	分别输入 年 月 日 星期 时 分 秒
      * @retval 无
      */
    void I2C_DS3231_SetTime(u8 yea,u8 mon,u8 da,u8 we,u8 hou,u8 min,u8 sec)
    {
      u8 temp=0;
      yea-=2000;
      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)+2000;
    
    	calendar.month=I2C_DS3231_DataRead(0x05); 
    	calendar.month=BCD_DEC(calendar.month);
    
    	calendar.day=I2C_DS3231_DataRead(0x04);  
    	calendar.day=BCD_DEC(calendar.day);
    
    	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 >2255)
    			{
    				printf("\r\n 您输入的数字是:%d,不符合要求",temp_num);
    					  
    			}
    			else
    			{	  
    				printf("\n\r  年份被设置为: %d\n\r", temp_num);
            temp_num-=2000;
    				DEC_BCD(temp_num);
    				tm->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->day = 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);
    		__HAL_UART_ENABLE_IT(&UART1_Handler,UART_IT_RXNE);	//开启接收中断
    }
    
    
    /**
      * @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->day; /*计算从公元元年元旦到计数日期一共有多少天*/
    
    	tm->week=day%7; //算出星期
    }
    
    /*********************************************END OF FILE**********************/
    

    三、函数调用(我用标准库的代码,如果在hal库有问题自己改下。调用方式是一样的)

    /**
      ******************************************************************************
      * @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**********************/
    

     

     

     

    展开全文
  • 采用DS3231实时时钟模块该芯片与单片机通过IIC串行通信,传递速度比较快。自带有电池,不需要外部晶振,完整的时钟日历功能包括秒、分、时、星期、日期、月和年计时,并提供有效期到2100年的闰年补偿,两个日历闹钟...
  • DS3231程序 stm32f051

    2018-09-06 20:33:18
    stm32f0 ds3231程序 亲自测试 没有问题 移植非常方便 欢迎下载
  • CUBEMX构建的STM32F030F4的HAL库例子。采用硬件IIC读写DS3231时钟芯片。封装了DS3231功能的函数,直接调用。附带中英文资料文档。
  • 使用STM32c8作为主控,并口驱动的0.96寸oled,DS3231做时钟芯片,W25q做存储的pcb和原理图 有备用电池 因为用于频谱显示,所以使用并口方式驱动oled 剩下12个io,不是很多,但是都通过FPC引出来, 整个pcb 3cm x 3cm 大小...
  • 基于DS3231实时时钟模块的stm32简易闹钟

    千次阅读 热门讨论 2020-07-06 16:00:49
    stm32简易闹钟 设计、制作一个简易的闹钟。要求如下: (1)作品需设有电源指示灯(绿),上电后,指示灯亮; (2)该闹钟能够显示当前的日期和时间,显示界面如下: Alarm Clock Date:年-月-日 (日期) Time:时:分:秒 ...

    stm32简易闹钟

    设计、制作一个简易的闹钟。要求如下:
    (1)作品需设有电源指示灯(绿),上电后,指示灯亮;
    (2)该闹钟能够显示当前的日期和时间,显示界面如下:
    Alarm Clock
    Date:年-月-日 (日期)
    Time:时:分:秒 (时间)
    Alm: 时:分:秒 (报警时间)
    (3)报警时间可调。设4个按键,分别为调时、调分、调秒、确定
    (4)当报警时间到达时,采用蜂鸣器报警,蜂鸣器要求响5S钟。
    系统系统在这里插入图片描述

    时钟模块设计与论证

    方案一:直接采用STM32单片机自身集成的RTC实时时钟,使用程序实现年、月、日、星期、时、分、秒计数。采用此种方案虽然减少芯片的使用,节约成本,但是单片机掉电会丢失数据,需要外部32.768KHz晶振和纽扣电池供电。 
    方案二:采用DS1302为计时时钟芯片 
    该芯片是串行电路,与单片机接口简单,但需另备电池和32.768kHz晶振,因焊接工艺和晶振质量等原因会导致精度降低。
    方案三:采用DS3231实时时钟模块
    该芯片与单片机通过IIC串行通信,传递速度比较快。自带有电池,不需要外部晶振,完整的时钟日历功能包括秒、分、时、星期、日期、月和年计时,并提供有效期到2100年的闰年补偿,两个日历闹钟。还有集成了温度传感器,成本低。
    由于DS3231时钟芯片模块精度高,而且具有闰年补偿功能且价格经济实惠等优点,故采用方案三。

    本设计采用模块

    采用STM32F103C8T6作为控制器,采用独立按键输入,0.96寸OLED显示屏spi四线通信作为显示模块,DS3231M模块作为实时时钟模块,发声模块采用有源蜂鸣器模块。
    原理图如下:
    在这里插入图片描述
    主程序:

    
    #include "system.h"
    #include "SysTick.h"
    #include "led.h"
    #include "usart.h"
    #include "oled.h"
    #include "picture.h"
    #include "ds3231.h"
    #include "key.h"
    
    _calendar_part calendar;//声明时钟结构体
    
    _Alarmclock_part Alarmclock;//声明闹钟结构体
    
    typedef struct
    {
    	u8 sure_flag;
    	u8 stop_flag;
    }_Alarm_flag;	//标志结构体
    
    _Alarm_flag flag;
    
    void display(void);
    void Key_Control(void);
    
    int main()
    {
    	SysTick_Init(72);//系统时钟初始化
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
    	LED_Init();				//led初始化
    	OLED_Init();			//OLED初始化
    	DS3231_Init();		//DS3231初始化
    	KEY_Init();				//按键初始化
    	Alarmclock.hour = 8;//闹钟初始值
    	Alarmclock.min = 50;
    	Alarmclock.sec = 0;
    	Alarmclock1_close();//初始先将闹钟关闭并清除闹钟标志位
    	Alarmclock1_cmd(0);
    	beep = 1;
    	while(1)
    	{
    		get_time();
    		display();	//oled显示
    		Key_Control();	//按键控制
    		if(flag.sure_flag)
    		{
    			Alarmclock1_cmd(1);	//闹钟使能
    			SetAlarmclock(Alarmclock.hour,Alarmclock.min,Alarmclock.sec);//设置闹钟
    			if(Alarmclock1state()==1&&!flag.stop_flag)//检测闹钟标志位
    			{
    				beep = 0;					//蜂鸣器打开
    			Alarmclock1_cmd(0); //闹钟失能
    			Alarmclock1_close();	//关闭闹钟并清理标志位
    			flag.stop_flag = 1;	
    			}
    		}
    		else
    		{
    			beep = 1;	//蜂鸣器关闭
    		}
    		if(flag.stop_flag)		//使闹钟5秒钟后停止
    		{
    			Alarmclock1_cmd(1);		
    			SetAlarmclock(Alarmclock.hour,Alarmclock.min,Alarmclock.sec+5);	
    			if(Alarmclock1state()==1)
    			{
    					beep = 1;
    				Alarmclock1_cmd(0);
    				Alarmclock1_close();
    				flag.stop_flag = 0;
    				flag.sure_flag = 0;
    			}
    		}
    		
    	}
    }
    
    void display(void)//OLED显示
    {
    	/*****日期显示****/
    	OLED_ShowNum(0,0,2000+calendar.year,4,16);
    	OLED_ShowFontHZ(16*2,0,0,16,1);  //年
    	OLED_ShowNum(16*3,0,calendar.month,2,16);
    	OLED_ShowFontHZ(16*4,0,1,16,1);  //月
    	OLED_ShowNum(16*5,0,calendar.day,2,16);
    	OLED_ShowFontHZ(16*6,0,2,16,1);  //日
    	/*****时间显示*****/
    	OLED_ShowChar(24*0,16,calendar.hour/10+'0',24,1);	//时
    	OLED_ShowChar(12*1,16,calendar.hour%10+'0',24,1);	//时
    	OLED_ShowChar(6*5,16,':',24,1);
    	OLED_ShowChar(24*2,16,calendar.min/10+'0',24,1);	//分
    	OLED_ShowChar(12*5,16,calendar.min%10+'0',24,1);	//分
    	OLED_ShowChar(6*13,16,':',24,1);
    	OLED_ShowChar(24*4,16,calendar.sec/10+'0',24,1);	//秒
    	OLED_ShowChar(12*9,16,calendar.sec%10+'0',24,1);	//秒
    	
    	/******闹钟******/
    	if(flag.sure_flag)OLED_ShowString(16*0,48,"A",16);
    	else OLED_ShowString(16*0,48," ",16);
    	OLED_ShowChar(16*1,48,Alarmclock.hour/10+'0',16,1);	//时
    	OLED_ShowChar(8*3,48,Alarmclock.hour%10+'0',16,1);	//时
    	OLED_ShowChar(4*9,48,':',16,1);
    	OLED_ShowChar(16*3,48,Alarmclock.min/10+'0',16,1);	//分
    	OLED_ShowChar(8*7,48,Alarmclock.min%10+'0',16,1);	//分
    	OLED_ShowChar(4*17,48,':',16,1);
    	OLED_ShowChar(16*5,48,Alarmclock.sec/10+'0',16,1);	//秒
    	OLED_ShowChar(8*11,48,Alarmclock.sec%10+'0',16,1);	//秒
    	
    	OLED_Refresh_Gram();  //刷新GRAM数组
    
    }
    
    void Key_Control(void)//按键控制
    {
    	u8 temp = 0;
    	temp =	KEY_Scan(0);
    	switch(temp)
    	{
    		case 1:	Alarmclock.hour+=1;
    						if(Alarmclock.hour>=24) Alarmclock.hour=0;
    						break;
    		case 2: Alarmclock.min+=1;
    						if(Alarmclock.min>=60) Alarmclock.min=0;
    						break;
    		case 3: Alarmclock.sec+=1;
    						if(Alarmclock.sec>=60) Alarmclock.sec=0;
    						break;
    		case 4: flag.sure_flag =~ flag.sure_flag;
    						break;
    	}
    
    }
    
    
    

    源程序这里https://download.csdn.net/download/qq_44702247/12581386

    展开全文
  • stm32f411 freertos 驱动oled屏幕读取ds3231显示时间温度, 主要是freertos的简单使用,欢迎下载
  • DS3231时钟模块使用,IIC协议实践。(基于STM32

    千次阅读 多人点赞 2021-03-20 10:32:05
    写在前面 因为毕业设计,需要用到记录时间的功能,对时钟模块一直...那么现在在刚回顾和复习完IIC通讯协议下,在这里我将IIC协议实践在DS3231时钟模块上。 文件结构,方便扩展 和 代码管理。 这里建立两个库: software

    写在前面

    因为毕业设计,需要用到记录时间的功能,对时钟模块一直只闻其名,却从未用过。之前的在使用其他模块的时候,总是一个劲的拿来主义,经常是在别人现成的代码上改改宏定义就直接进行使用,一旦项目需要用到的模块多了起来,每个模块别人写好一个库,在不清楚其基本原理的情况下整个项目就变得臃肿不堪了。(据我所知很多同学本科阶段使用模块都是这样一个模式)。
    那么现在在刚回顾和复习完IIC通讯协议下,在这里我将IIC协议实践在DS3231时钟模块上。

    文件结构,方便扩展 和 代码管理。

    这里建立两个库: softwareIIC.hDS3231.h
    DS3231.h 即存储DS3231的寄存器定义,时间变量结构体定义,等等。
    softwareIIC.h即存储相关IIC的函数定义,软件 IIC 占用的 IO口定义,等等。
    在使用上,只需要在DS3231.c中 #include "softwareIIC.h"即可。
    同理,因为我们将模块和 IIC 协议进行了分离, 在项目中需要加到其他使用IIC协议的模块时,也只需要在对应模块的文件中 调用软件IIC的头文件 即可。
    那么在之后其他项目也可以使用这个时候写好的 软件IIC 库, 只需要改改softwareIIC.h 里对于占用端口的宏定义即可, 十分方便代码的移植😉👍。
    若你是初学者(虽然我也是),希望你能养成这样的一个模式、习惯,这对之后的帮助异常的大。

    关于DS3231

    DS3231是低成本、高精度的I2C实时时钟(RTC)。该器件包括电池输入端,断开主电源时,仍可保持精准计时。
    RTC保存秒、分、时、星期、月和年信息。少于31天的月份,将自动调整月末的日期,包括闰年修正。时钟格式可以时24小时或带AM/PM的12小时格式。提供两个可设置的日历闹钟和1Hz输出。

    DS3231特性
    在这里我用的是在某宝上购买的模块,已经预留出了VCC、GND、SDA、SCL。

    DS3231 寄存器设置


    第一眼看上去可能有点不明所以,但其实很简单。DS3231采用8421BCD码用来存储时间、日期等数据。(什么是8421BCD码? 你可以简单理解为,一般用四位二进制表示一位数字,比如十进制数字58,对应的8421BCD码即:0101 1000 , 这里1000代表个位数的8 , 0101代表十位数的 5 。)
    比如Seconds(秒),寄存器地址是0x00,我们需要读取和设置 秒 ,也只需要对这个寄存器进行读写即可。
    秒范围是 00 - 59,故第四位用来存储个位数0 - 9,高三位用来存储十位数 0 - 5 ( 000 - 101)。
    其他寄存器同理。
    另外关于采用 24小时制 还是 12小时制度,取决于Hours寄存器(0x02)中的第6位。

    代码

    欧克,关于IIC 借助我上一篇博客相信已经有所掌握,现在我们直接将其使用在和DS3231的通讯和设置中。

    softwareIIC.h

    #ifndef		___SOFTWARE_IIC
    #define		___SOFTWARE_IIC
    
    #include "stm32f10x.h"
    #include "./sys/sys.h"
    
    
    #define 		IIC_SDA_GPIOx									GPIOA
    #define			IIC_SDA_GPIO_Pin							GPIO_Pin_11
    #define			IIC_SDA_GPIO_RCC							RCC_APB2Periph_GPIOA
    #define			IIC_SDA_RCCPeriphClockcmd			RCC_APB2PeriphClockCmd
    
    
    #define 		IIC_SCL_GPIOx									GPIOA
    #define			IIC_SCL_GPIO_Pin							GPIO_Pin_12
    #define			IIC_SCL_GPIO_RCC							RCC_APB2Periph_GPIOA		//上面已经定义过了
    #define			IIC_SCL_RCCPeriphClockcmd			RCC_APB2PeriphClockCmd
    
    #define			IIC_SCLSDA_GPIO_RCC						RCC_APB2Periph_GPIOA		
    	
    	
    //使用4线串行接口时使用 
    #define  		IIC_SDA		 PAout(11)
    #define  		IIC_SCL		 PAout(12)
    
    #define			READ_SDA   GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)
    
    
    
    void IIC_Init(void);
    void IIC_Start(void);  //产生起始条件(符)
    void IIC_Stop(void);			//产生结束条件(符)
    uint8_t IIC_Wait_Ask(void);
    void IIC_Ack(void);
    void IIC_NAck(void);
    void IIC_WriteByte(u8 data);
    u8 IIC_Read_Byte(unsigned char ack);
    #endif  // ___SOFTWARE_IIC
    

    software.c

    #include  "./softwareIIC/softwareIIC.h"
    #include	"./systick/bsp_systick.h"
    
    void IIC_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStructer;
    		//开启时钟
        IIC_SDA_RCCPeriphClockcmd(IIC_SDA_GPIO_RCC, ENABLE);  //or IIC_SCL_GPIOx   ,因为 sda scl 使用的是同一个gpiox
    		//定义pin
        GPIO_InitStructer.GPIO_Pin=IIC_SDA_GPIO_Pin | IIC_SCL_GPIO_Pin; //10--SCL   11--SDA			//PB10 PB11
    		//定义频率
        GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
    		//定义IO模式
        GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
    		//初始化
        GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer);
    }
    
    
    static void SDA_OUT(void)
    {
        GPIO_InitTypeDef GPIO_InitStructer;
        GPIO_InitStructer.GPIO_Pin= IIC_SDA_GPIO_Pin;
        GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;	//推挽输出模式
        GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer);
    }
    
    static void SDA_IN(void)
    {
        GPIO_InitTypeDef GPIO_InitStructer;
        GPIO_InitStructer.GPIO_Pin= IIC_SDA_GPIO_Pin;
        GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IPU;		//上拉输入模式
        GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer);
    }
    
     void IIC_Start(void)  //产生起始条件(符)
    {
        SDA_OUT();
        IIC_SDA=1;   					//先确保 SDA为高
        IIC_SCL=1;   					//拉高SCL
        SysTick_Delay_us(4);
        IIC_SDA=0;						//产生下降沿
        SysTick_Delay_us(4);
        IIC_SCL=0; 						//将时钟线拉低,只有时钟线拉低才运行SDA数据变化有效
        SysTick_Delay_us(4);
    }
    
     void IIC_Stop(void)			//产生结束条件(符)
    {
        SDA_OUT();
        IIC_SCL = 0;
        IIC_SDA=0;						//先让SDA 为低电平
        IIC_SCL=1;						//将SCL拉高
        SysTick_Delay_us(4);	// 延时,保证时长
        IIC_SDA=1;						//SDA拉高 产生上升沿
        SysTick_Delay_us(4);  // 结束后释放SDA , SDA变高
    }
    
    // uint8_t IIC_Wait_Ask(void)
    //{
    //    SDA_IN();
    //    IIC_SCL=1;
    //    SysTick_Delay_us(4);
    //    IIC_SCL=0;
    //    SysTick_Delay_us(4);
    //    return 0;
    //}
    uint8_t IIC_Wait_Ask(void)  
    {
        uint16_t tempTime = 0;
        SDA_IN();
        IIC_SDA = 1;   //释放数据总线,交由从机控制
        SysTick_Delay_us(4);
        IIC_SCL = 1;
        SysTick_Delay_us(1);
        while (GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)) //读到 0 ,即接收到ACK,循环跳出
        {
            tempTime++;
            if(tempTime > 300)
            {
                IIC_Stop();
                return 1;			//超时返回1
            }
        }
        IIC_SCL = 0;
        return 0;							//接收到 ACK 返回0
    }
    
    
    void IIC_Ack(void)
    {
        IIC_SCL = 0 ;
        SDA_OUT();
        IIC_SDA = 0;
        SysTick_Delay_us(2);
        IIC_SCL = 1;
        SysTick_Delay_us(5);
        IIC_SCL = 0;
    }
    //主机不产生应答信号NACK
    void IIC_NAck(void)
    {
        IIC_SCL = 0;
        SDA_OUT();
        IIC_SDA = 1;
        SysTick_Delay_us(2);
        IIC_SCL = 1;
        SysTick_Delay_us(5);
        IIC_SCL = 0;
    }
    
     void IIC_WriteByte(u8 data)
    {
        u8 i;
        SDA_OUT();
        for(i=0;i<8;i++)
        {
            IIC_SCL=0;
            SysTick_Delay_us(4);
            if(data & 0x80)
                IIC_SDA=1;
            else
                IIC_SDA=0;
            IIC_SCL=1;
            SysTick_Delay_us(4);
            IIC_SCL=0;
            data<<=1;
    
        }
    }
    
    //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
    u8 IIC_Read_Byte(unsigned char ack)
    {
    		unsigned char i,receive=0;
    		SDA_IN();//SDA设置为输入
        for(i=0;i<8;i++ )
    		{
            IIC_SCL=0; 
            SysTick_Delay_us(2);
    				IIC_SCL=1;
            receive<<=1;
            if(READ_SDA)
    					receive++;   
    				SysTick_Delay_us(2); 
        }					 
        if (!ack)
            IIC_NAck();//发送nACK
        else
            IIC_Ack(); //发送ACK   
        return receive;
    }
    
    

    上面是软件IIC的代码,是对于IIC总线的操作,不具体针对某一个模块,可移植性高。

    DS3231.h

    #ifndef DS3231_H
    #define DS3231_H
    
    #include <stm32f10x.h>
    
    
    #define DS3231_ADDRESS	      0x68 //I2C Slave address
    #define	DS3231_ADDRESS_Write	0xD0
    #define	DS3231_ADDRESS_Read		0xD1
    
    /* DS3231 Registers. Refer Sec 8.2 of application manual */
    #define DS3231_SEC_REG        0x00    // 秒
    #define DS3231_MIN_REG        0x01    //
    #define DS3231_HOUR_REG       0x02
    #define DS3231_WDAY_REG       0x03
    #define DS3231_MDAY_REG       0x04
    #define DS3231_MONTH_REG      0x05
    #define DS3231_YEAR_REG       0x06
    
    #define DS3231_AL1SEC_REG     0x07
    #define DS3231_AL1MIN_REG     0x08
    #define DS3231_AL1HOUR_REG    0x09
    #define DS3231_AL1WDAY_REG    0x0A
    
    #define DS3231_AL2MIN_REG     0x0B
    #define DS3231_AL2HOUR_REG    0x0C
    #define DS3231_AL2WDAY_REG    0x0D
    
    #define DS3231_CONTROL_REG          0x0E
    #define DS3231_STATUS_REG           0x0F
    #define DS3231_AGING_OFFSET_REG     0x0F
    #define DS3231_TMP_UP_REG           0x11
    #define DS3231_TMP_LOW_REG          0x12
    
    #define EverySecond     0x01
    #define EveryMinute     0x02
    #define EveryHour       0x03
    
    
    typedef struct DateTImeStruct{
    	uint8_t second;
    	uint8_t minute;
    	uint8_t hour;
    	uint8_t dayofmonth;
    	uint8_t month;
    	uint16_t year;
    	uint8_t dayOfWeek;  /*Su=0 Mo=1 Tu=3 We=4 Th=5 Fr=6 Sa=7 */
    }DateTime;
    
    uint8_t DS3231_setDate(uint8_t year,uint8_t mon,uint8_t day);
    uint8_t DS3231_setTime(uint8_t hour , uint8_t min , uint8_t sec);
    uint8_t DS3231_getdate(DateTime* ans);
    uint8_t DS3231_gettime(DateTime* ans);
    
    #endif 
    
    

    DS3231.c

    #include "./DS3231/DS3231.h"
    #include "./softwareIIC/softwareIIC.h"
    #include "./usart/bsp_usart.h"
    
    void DS3231_Init(void){
    	IIC_Init();
    }
    
    uint8_t IIC_DS3231_ByteWrite(uint8_t WriteAddr , uint8_t date)
    {
    	IIC_Start();
    	IIC_WriteByte(DS3231_ADDRESS_Write);
    	if(IIC_Wait_Ask())
    		return 1;
    	IIC_WriteByte(WriteAddr);
    	if(IIC_Wait_Ask())
    		return 2;
    	IIC_WriteByte(date);
    	if(IIC_Wait_Ask())
    		return 3;
    	IIC_Stop();
    	return 0;
    }
    
    uint8_t IIC_DS3231_ByteRead(uint8_t ReadAddr,uint8_t* Receive)
    {
    	uint8_t data = 0;
    	
    	IIC_Start();													//产生起始位
    	IIC_WriteByte(DS3231_ADDRESS_Write); 	//发送从机地址(写模式)
    	if(IIC_Wait_Ask())										//等待响应
    		return 1;
    	IIC_WriteByte(ReadAddr);							//发送寄存器地址
    	if(IIC_Wait_Ask())										//等待响应
    		return 2;
    	IIC_Start();													//重复起始位
    	IIC_WriteByte(DS3231_ADDRESS_Read);		//发送从机地址(读模式)
    	if(IIC_Wait_Ask())										//等待响应
    		return 3;
    	data = IIC_Read_Byte(0);							//读取数据,参数设为0 --- NACK
    	*Receive = data;											//将结果赋值给接收位
    	IIC_Stop();
    	return 0;
    }
    
    uint8_t DS3231_setDate(uint8_t year,uint8_t mon,uint8_t day)
    {
    	uint8_t temp_H , temp_L;
    	temp_L = year%10;
    	temp_H = year/10;
    	year = (temp_H << 4) + temp_L;
    	if(IIC_DS3231_ByteWrite(DS3231_YEAR_REG,year)) //set year
    	{
    			printf("set year error\r\n");
    			return 1;
    	}	
    	temp_L = mon%10;
    	temp_H = mon/10;
    	mon = (temp_H << 4) + temp_L;	
    	if(IIC_DS3231_ByteWrite(DS3231_MONTH_REG,mon)) //set mon
    	{
    		printf("set month error\r\n");
    		return 2;
    	}
    	temp_L = day%10;
    	temp_H = day/10;
    	day = (temp_H << 4) + temp_L;		
    	if(IIC_DS3231_ByteWrite(DS3231_MDAY_REG,day)) //set day
    	{
    		printf("set day error\r\n");
    		return 3;
    	}
    	return 0;
    }
    
    
    uint8_t DS3231_setTime(uint8_t hour , uint8_t min , uint8_t sec)
    {
    	uint8_t temp_H , temp_L;
    	temp_L = hour%10;
    	temp_H = hour/10;
    	hour = (temp_H << 4) + temp_L;
    	if(IIC_DS3231_ByteWrite(DS3231_HOUR_REG,hour)) //set hour
    		return 1;
    	temp_L = min%10;
    	temp_H = min/10;
    	min = (temp_H << 4) + temp_L;
    	if(IIC_DS3231_ByteWrite(DS3231_MIN_REG,min)) //SET min
    		return 2;	
    	temp_L = sec%10;
    	temp_H = sec/10;
    	sec = (temp_H << 4) + temp_L;	
    	if(IIC_DS3231_ByteWrite(DS3231_SEC_REG,sec))		//SET sec
    		return 3;
    	return 0;
    }
    
    static uint8_t bcdToDec(uint8_t byte)
    {
    	uint8_t temp_H , temp_L;
    	temp_L = byte & 0x0f;
    	temp_H = (byte & 0xf0) >> 4;
    	return ( temp_H * 10 )+ temp_L;
    }
    
    uint8_t DS3231_gettime(DateTime* ans)
    {
    	uint8_t receive = 0;
    	if(IIC_DS3231_ByteRead(DS3231_HOUR_REG,&receive))
    		return 1;
    	ans->hour = bcdToDec(receive);
    	if(IIC_DS3231_ByteRead(DS3231_MIN_REG,&receive))
    		return 2;
    	ans->minute = bcdToDec(receive);
    	if(IIC_DS3231_ByteRead(DS3231_SEC_REG,&receive))
    		return 3;
    	ans->second = bcdToDec(receive);
    	return 0;
    }
    
    uint8_t DS3231_getdate(DateTime* ans)
    {
    	uint8_t receive = 0;
    	if(IIC_DS3231_ByteRead(DS3231_YEAR_REG,&receive))
    		return 1;
    	ans->year = bcdToDec(receive) + 2000;
    	if(IIC_DS3231_ByteRead(DS3231_MONTH_REG,&receive))
    		return 2;
    	ans->month = bcdToDec(receive);
    	if(IIC_DS3231_ByteRead(DS3231_MDAY_REG,&receive))
    		return 3;
    	ans->dayofmonth = bcdToDec(receive);
    	return 0;
    }
    
    

    总结不易,若对你有帮助,希望点赞收藏是😉👍

    展开全文
  • 二、与stm32通信 1、配置IO 2、初始化与函数 3、主函数调用 4、使用 源码 一、模块简介 DS3231是一款高精度I2C实时时钟器件,具有集成的温度补偿晶体振荡器。该器件包含电池输入端,断开主电源时仍可保持精确...

    目录

    一、模块简介

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

     

     

    展开全文
  • 硬件IIC读取DS3231-基于STM32F407

    千次阅读 2018-06-13 16:40:29
    硬件平台:STM32F407ZGT6软件平台:Keil4本文主要记录调试STM32硬件IIC实现读取DS3231的代码,新手上路请多多包涵。DS3231初始化函数:void DS3231_init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_...
  • STM32F103C8T6例程-DEMO.zip

    2020-09-03 10:41:51
    STM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMOSTM32F103C8T6例程-DEMO
  • 参赛作品《STM32桌面万年历》-STM32F103C8T6万年历(第1版).rar
  • DS3231时钟串口打印工程包,使用的时钟模块为DS3221, 采用模拟IIC驱动。 时钟模块引脚接线 SCL-> PB10 ,SDA-> PB11,32K和SQW引脚悬空,什么都不接。
  • 视频演示地址: https://v.youku.com/v_show/id_XMzc2NjIxNTk4MA==.html?spm=a2h1n.8251843.playList.5!4~5~A&f=51844923&o=1
  • DS3231.rar

    2019-12-17 10:24:11
    DS3231时钟模块的库文件,网上常见的有两种库,一种是Eric Ayars的,下载后的库文件夹是:DS3231_TEST一种是Henning Karlsen的,下载后的库文件夹是DS3231.这个库是第二种
  • 给老妈做了个电子表,用DS3231+STM32做的,结果DS3231资源非常少,憋了好几天才整出来,目前运行良好,据说DS3231精度超级高啊。
  • ds1302和stm32f103配合显示时间,并用串口1显示,后续会加入原理图和引脚图
  • 基于STM32ds2431读写C代码,IO口用的是PA4,可自行修改,都是经过硬件开发板验证过的代码,包括1WIRE命令,读ID,读数据,写数据,写配置等
  • STM32 STM32F103C8T6 读取DS18B20温度数据例程 将读出的温度数据发送到调试串口 温度数据单位是0.1摄氏度
  • STM32F030的CUBEMX完整Keil5代码。 LCD2004+PCF8574两线IIC驱动液晶屏作为显示(淘宝上11-13元,20字*4行),含PCF8574驱动LCD2004的函数库。 DS3231高精度时钟模块+24C32存储的IIC驱动,三者连在一个IIC上。只需要...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,803
精华内容 2,321
关键字:

ds3231stm32