DS3231时钟芯片全解析——概况,性能,MCU连接,样例代码

news2024/10/28 13:34:43

DS3231概述:

在这里插入图片描述

数据:
DS3231是一个超高精度I2C实时时钟芯片,带有集成的温度补偿晶振。误差范围:温度范围为0摄氏度到40摄氏度(±2PPM),温度范围为-40摄氏度到85摄氏度(±3.5PPM,每天±0.432秒)。包含时钟(24小时格式或12小时格式)、日历(年,月,日,星期)、两个可编程时间报警和一个可编程方波输出。

供电:
DS3231采用两组供电,一组主电,由外部供电(2.3V~5.5V),一组备电,一般由纽扣电池供电。当主电存在时,由主电供电,当主电不存在时由备电供电。

功能:
DS3231,通过内部带有温补的晶振自动对时间进行计数,对月末日期进行调整,以及闰年矫正。MCU可对其时间日期进行设置及读取,显示正确的当前时间。

连接:
与MCU通过标准I2C总线进行连接(注意,非标准I2C设备会占用I2C总线全部设备地址,导致标准设备不可用。),多个标准I2C设备可连接到同一条I2C总线上。

DS3231型号说明:

DS3231主要有两种型号,一种是16pin的DS3231SN,一种是8pin的DS3231MZ。主要是提醒,8pin的DS3231MZ,守时精度为±5PPM。

DS3231引脚说明:

在这里插入图片描述

引脚名称功能
132kHz32.768kHz输出。开漏输出,需要外接上拉电阻。可在任何电源上工作,不使用需保持开路。
2VCC初级供电引脚。需使用0.1uF~1uF电容解耦,不使用请接地。
3INT/SQW主动下拉中断或方波输出。开漏输出,需要外接5.5V以下的上拉电阻。由多功能引脚控制寄存器的INTCN位决定。
4RST低电平复位引脚。同时可指示VCC和VPF的大小。VCC<VPF,引脚低电平;VCC>VPF,引脚高电平。不使用悬空。
5~12N.C.无连接,必须接地。
13GND电源接地。
14VBAT备用电源输入。需使用0.1uF~1uF电容解耦,不使用请接地。不会对VCC反向充电。
15SDAI2C串行数据输入输出引脚。
16SCLI2C串行时钟输入引脚。

DS3231电气性能:

在这里插入图片描述
VPF断电电流,VCC小于该电流时且小于VBAT时,由VBAT供电。
在这里插入图片描述
保持电流1uA,读取写入电流150uA,温度转换(64s周期)电流650uA,数据保持(关闭晶振)电流100nA。

DS3231电源控制:

DS3231对存储设备的电池消耗进行了优化,在第一次安装电池时,内部晶振不会启动 ,直到VCC超过上述VFP数值时,或将有效的I2C地址写入时,在1s内,晶振启动。大约2秒,设备进行测温,并应用于补偿,测温周期为64秒。晶振在激活后一直有效。

DS3231 MCU连接:

在这里插入图片描述
如图所示:
左侧为MCU接口,SCL和SDA接上拉电阻RPU,RPU取值可为10k,RST引脚可接MCU,如不使用,可悬空。
右侧VCC和VBAT各需要一个去耦电容,==其中VBAT的去耦电容如果无外部供电时不会进行读写操作,则可以取消。==去耦电容大小0.1uF1uF。INT~/SQW和32kHz接上拉电阻,如不使用,均可悬空,其中32kHz引脚输出32.768kHz方波可作为单片机时钟源进行精确延时。

DS3231寄存器定义:

DS3231设备地址:0xD0
时间寄存器:
在这里插入图片描述

  • 寄存器读取长度超过12h,则超过部分从00h开始。读取指针指向00h时,外部寄存器数值自动与内部寄存器同步。
  • 在数据读写时复位,则数据会丢失。
  • 时间数据和其他实时时钟芯片一样使用二进制编码的十进制(BCD)格式。
  • 小时寄存器的第6位为12/24小时格式选择位,第5位为AM/PM标志位,0为AM,1为PM。
  • 月份寄存器的第七位为世纪位。

报警寄存器:
在这里插入图片描述
报警间隔设置寄存器:
在这里插入图片描述
注意报警1秒钟匹配时警报,报警2分钟匹配时警报。

  • DS3231包含两个时间/日期报警。
  • 07h到0Ah为报警1时间;0Bh到0Dh为报警2时间。
  • 通过INTCN和A1IE和A2IE控制INT/SQW引脚输出。
  • 报警可编程为每隔1秒、1分钟、1小时、1天、或日期重复。
  • DY/DT用于设置是日期还是星期。
  • 当RTC寄存器的值与报警寄存器设置匹配时,相应的警报标志“A1F”或“A2F”位被设置为1。如果相应的警报中断使能“A1IE”或“A2IE”也被设置为1,并且INTCN位设置为1,则激活INT/SQW信号。时间和日期寄存器的每秒匹配一次。

控制寄存器:
报警寄存器:

  • BIT 7:启动晶振控制位。为0时启动晶振;为1时当VCC供电时晶振启动,当VBAT供电时关闭晶振。默认为1,首次通电时清0。
  • BIT 6:电池供电方波使能。条件苛刻,用处不大。
  • BIT 5: 转换温度控制位。设为1,强制更新一次温补。使用前先检查BSY位,避免与64s周期转换冲突。温补转换完成,BIT5和BSY位自动复位为0。
  • BIT 4 和 BIT 3:控制方波输出频率。方波启用时可用。
    在这里插入图片描述
  • BIT 2:方波报警控制位。当RTC寄存器的值与报警寄存器设置匹配时,相应的警报标志“A1F”或“A2F”位被设置为1。如果相应的警报中断使能“A1IE”或“A2IE”也被设置为1,并且INTCN位设置为1,则激活INT/SQW信号。时间和日期寄存器的每秒匹配一次。
  • BIT 1:报警2中断使能,VCC上电清0。
  • BIT 0:报警1中断使能,VCC上电清0。

状态寄存器:
在这里插入图片描述

  • BIT 7:晶振停止标志位。为1时表示晶振已停止。
  • BIT 3:启用32.768kHz控制位。
  • BIT 2:温补操作状态位,为1时表示正在温度转换。
  • BIT 1:报警2报警标志位。该位为0时,同时A2IE为1,INTCN为1,此时当Alarm2时间匹配时才会触发 INT/SQW引脚的主动下拉中断(如果中断触发后,需要重新将其清0,才可以再次触发中断。)
  • BIT 0:报警1报警标志位。该位为0时,同时A1IE为1,INTCN为1,此时当Alarm1时间匹配时才会触发 INT/SQW引脚的主动下拉中断(如果中断触发后,需要重新将其清0,才可以再次触发中断。)
  • BIT 1和BIT 0有1,则INT/SQW引脚为低电平,都为零则为低电平。

老化偏置寄存器:
在这里插入图片描述

  • 二进制补码,调整电容值以进一步校准时钟。
  • 一般,25℃时,最小调整量为0.1ppm。

温度寄存器:
在这里插入图片描述

  • 温度寄存器为10bit,分辨率为0.25℃。使用二进制补码形式。上8位为整数部分在11h,下2位为小数部分在12h。例如00011001 01b=+25.25摄氏度,即25+0.25*1。

DS3231寄存器读写:

DS3231可在标准模式100kHz和快速模式400kHz下工作。
在这里插入图片描述
写时序:(写入的寄存器地址,后跟数据)
在这里插入图片描述
读时序:(写入的寄存器地址,再重新开启I2C通讯,再跟设备读取地址)
在这里插入图片描述

DS3231注意事项:

  • DS3231封装包含石英音叉晶体。可以使用拾取-贴装设备但是必须谨慎小心,以确保避免过度冲击。避免使用超声波清理,以免损坏晶体。
  • 除非封装与信号线之间有地层隔开,否则避免在器件下面走信号线。所有N.C.(无连接)引脚必须接地。
  • 潮湿敏感封装出厂时采用防潮包装。必须遵循封装标签上列出的操作说明,以防止回流焊过程中损坏器件。
  • 潮湿敏感器件(MSD)的分类和回流焊温度曲线请参考IPC/JEDEC J-STD-020标准。允许的回流焊次数最多2次。

DS3231样例代码: (基于HC32L130)

ds3231.h

#ifndef __DS3231_H__
#define __DS3231_H__


/* Includes ------------------------------------------------------------------*/

#include "gpio.h"


/* Exported types ------------------------------------------------------------*/

typedef struct
{
		uint8_t yearH;			//年千百位
		uint8_t yearL;			//年 
		uint8_t month;			//月 			
		uint8_t date;		  //日 
		uint8_t hour;		  //时 
		uint8_t minute;		//分
		uint8_t second;		//秒
		uint8_t week;		  //周 
}_calendar_obj;

extern _calendar_obj calendar;	//日历结构体

/* DS3231 地址定义 */
/* ADDR Pin Conect to VSS */
#define 	 DS3231_ADDR    		  0xD0
#define    DS3231_ADDR_WRITE    0xD0
#define    DS3231_ADDR_READ     0xD1

/* DS3231寄存器地址 */
typedef enum
{
    DS3231_SECOND = 0x00,    //秒
		DS3231_MINUTE = 0x01,    //分
		DS3231_HOUR   = 0x02,    //时
		DS3231_WEEK   = 0x03,    //星期
		DS3231_DAY    = 0x04,    //日
		DS3231_MONTH  = 0x05,    //月
		DS3231_YEAR   = 0x06,    //年
		/* 闹铃1 */          
		DS3231_ALARM1SECOND   = 0x07,    //秒
		DS3231_ALARM1MINUTE   = 0x08,    //分
		DS3231_ALARM1HOUR     = 0x09,    //时
		DS3231_ALARM1DATE  		= 0x0A,    //星期/日
		/* 闹铃2 */
		DS3231_ALARM2MINUTE 	= 0x0b,    //分
		DS3231_ALARM2HOUR     = 0x0c,    //时
		DS3231_ALARM2DAY     = 0x0d,    //星期/日
		
		DS3231_CONTROL        = 0x0e,    //控制寄存器
		DS3231_STATUS         = 0x0f,    //状态寄存器
		BSY                 	= 2,       //忙
		OSF                		= 7,       //振荡器停止标志
		DS3231_XTAL         	= 0x10,    //晶体老化寄存器
		DS3231_TEMPERATUREH 	= 0x11,    //温度寄存器高字节(8位)
		DS3231_TEMPERATUREL 	= 0x12,    //温度寄存器低字节(高2位) 
} DS3231_CMD;																				
																																								
/* Exported functions ------------------------------------------------------- */

void DS3231_Init(void);		

en_result_t I2C_DS3231_WriteCmd(M0P_I2C_TypeDef* I2CX,DS3231_CMD pu8Addr, uint8_t pu8Data);
en_result_t I2C_MasterRead_DS3231Data(M0P_I2C_TypeDef* I2CX,DS3231_CMD pu8Addr,uint8_t*pu8Data,uint32_t u32Len);

void I2C_DS3231_SetTime(uint8_t year,uint8_t month,uint8_t day,uint8_t week,uint8_t hour,uint8_t minute,uint8_t second);
void I2C_DS3231_ReadTime(void);
void I2C_DS3231_SetAlarm_1(boolean_t en,uint8_t date,uint8_t hour,uint8_t minute,uint8_t second);
void I2C_DS3231_SetAlarm_2(boolean_t en,uint8_t hour,uint8_t minute);
uint8_t I2C_DS3231_ReadTime_Hour(void);
uint8_t I2C_DS3231_ReadTime_Minute(void);

uint8_t I2C_DS3231_getTemperature(void);

#endif /* __DS3231_H__ */

ds3231.c

#include "ds3231.h"
#include "i2c.h"
#include "bsp_delay.h"


// BCD(8421)转DEC
uint8_t BCD_DEC(uint8_t val)
{
	uint8_t i;
	i= val&0x0f;
	val >>= 4;
	val &= 0x0f;
	val *= 10;
	i += val;    
	return i;
}

// DEC转BCD(8421)
uint8_t DEC_BCD(uint8_t val)
{
  uint8_t i,j,k;
  i=val/10;
  j=val%10;
  k=j+(i<<4);
  return k;
}

//DS3231初始化
void DS3231_Init(void)
{
    I2C_DS3231_WriteCmd(M0P_I2C0,DS3231_CONTROL,0x04);    	//闹钟中断允许,初始化禁用闹钟1闹钟2
    I2C_DS3231_WriteCmd(M0P_I2C0,DS3231_STATUS,0x00);     	//32KHZ输出禁止,闹钟标志位清零
}

// DS3231写命令函数,只进行写命令操作
en_result_t I2C_DS3231_WriteCmd(M0P_I2C_TypeDef* I2CX,DS3231_CMD pu8Addr, uint8_t pu8Data)
{
		en_result_t enRet = Error;
		//超时重启值
		uint32_t u32TimeOut = 0x00FFFFu;
    uint8_t sendCount = 0, u8State = 0;
		I2C_SetFunc(I2CX,I2cStop_En);          ///发送停止条件
		I2C_ClearIrq(I2CX);
    I2C_SetFunc(I2CX,I2cStart_En);										 ///发送起始条件
		while(1)
		{
				while(0 == I2C_GetIrq(I2CX))
				{
						while(0 == I2C_GetIrq(I2CX))
						{		
								//超时I2C还未启动
								if(0 == u32TimeOut--)
								{
										//软重启
										NVIC_SystemReset();
								}
						}
				}
				u8State = I2C_GetState(I2CX);
				switch(u8State)
				{
						case 0x08:                                 ///已发送起始条件
								I2C_ClearFunc(I2CX, I2cStart_En);
								I2C_WriteByte(I2CX, DS3231_ADDR_WRITE);  ///发送设备地址+W写标志0
								break;
						case 0x18:                                 ///已发送SLW+W,已接收ACK
						case 0x28:                                 ///已发送I2Cx_DATA中的数据,已接收ACK
								switch(sendCount)
								{
										case 0:
												I2C_WriteByte(I2CX,pu8Addr);		 ///发送地址
												break;
										case 1:
												I2C_WriteByte(I2CX,pu8Data);		 ///发送数据
												break;
								}
								sendCount++;
								break;
						case 0x20:                                 ///已发送SLW+W,已接收非ACK
								break;
						case 0x30:                                 ///已发送I2Cx_DATA中的数据,已接收非ACK,将传输一个STOP条件
								I2C_SetFunc(I2CX,I2cStop_En);          ///发送停止条件
								break;
						case 0x58:                                 ///< 已接收到最后一个数据,NACK已返回
                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件
                break;
            case 0x38:                                 ///< 在发送地址或数据时,仲裁丢失
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 当总线空闲时发起起始条件
                break;
            case 0x48:                                 ///< 发送SLA+R后,收到一个NACK
                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 发送起始条件
                break;
						default:
								break;
				}            
				if(sendCount>2)
				{
						I2C_SetFunc(I2CX,I2cStop_En);              ///此顺序不能调换,出停止条件
						I2C_ClearIrq(I2CX);
						break;
				}
				I2C_ClearIrq(I2CX);                            ///清除中断状态标志位
		}
		enRet = Ok;
    return enRet;
}
 
// 主机读取数据函数,只进行读数据操作
en_result_t I2C_MasterRead_DS3231Data(M0P_I2C_TypeDef* I2CX,DS3231_CMD pu8Addr,uint8_t *pu8Data,uint32_t u32Len)
{
		en_result_t enRet = Error;
		//超时重启值
		uint32_t u32TimeOut = 0x00FFFFu;
    uint8_t u8State=0;
    uint8_t receiveCount=0;
    uint8_t sendAddrCount=0;
		I2C_SetFunc(I2CX,I2cStop_En);          ///发送停止条件
		I2C_ClearIrq(I2CX);
    I2C_SetFunc(I2CX,I2cStart_En);										 ///发送起始条件

    while(1)
    {
        while(0 == I2C_GetIrq(I2CX))
        {		
						//超时I2C还未启动
						if(0 == u32TimeOut--)
						{
								//软重启
								NVIC_SystemReset();
						}
				}
        u8State = I2C_GetState(I2CX);
        switch(u8State)
        {
            case 0x08:                                 ///< 已发送起始条件,将发送SLA+W
								sendAddrCount++;
								I2C_ClearFunc(I2CX,I2cStart_En);
								I2C_WriteByte(I2CX,DS3231_ADDR_WRITE); 
                break;
            case 0x18:                                 ///< 已发送SLA+W,并接收到ACK
                I2C_WriteByte(I2CX, pu8Addr); 					 ///< 读取寄存器位置
                break;
            case 0x28:                                 ///< 已发送数据,接收到ACK, 此处是已发送从机内存地址u8Addr并接收到ACK
								I2C_SetFunc(I2CX,I2cStart_En);
                break;
            case 0x10:                                 ///< 已发送重复起始条件
                I2C_ClearFunc(I2CX,I2cStart_En);
								I2C_WriteByte(I2CX,DS3231_ADDR_READ);///< 发送SLA+R,开始从从机读取数据	
                break;
            case 0x40:                                 ///< 已发送SLA+R,并接收到ACK
                if(u32Len>=1)
                {
                    I2C_SetFunc(I2CX,I2cAck_En);       ///< 使能主机应答功能
                }
                break;
            case 0x50:                                 ///< 已接收数据字节,并已返回ACK信号
                pu8Data[receiveCount] = I2C_ReadByte(I2CX);
								receiveCount++;
                if(receiveCount==u32Len)
                {
                    I2C_ClearFunc(I2CX,I2cAck_En);     ///< 已接收到倒数第二个字节,关闭ACK应答功能
                }
                break;
            case 0x58:                                 ///< 已接收到最后一个数据,NACK已返回
								I2C_ClearFunc(I2CX,I2cStart_En);
                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件
								I2C_SetFunc(I2CX,I2cStart_En);
                break;
            case 0x38:                                 ///< 在发送地址或数据时,仲裁丢失
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 当总线空闲时发起起始条件
                break;
            case 0x48:                                 ///< 发送SLA+R后,收到一个NACK
                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 发送起始条件
                break;
            default:
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 其他错误状态,重新发送起始条件
                break;
        }
        I2C_ClearIrq(I2CX);                            ///< 清除中断状态标志位
        if(receiveCount==u32Len)                                ///< 数据全部读取完成,跳出while循环
        {
                break;
        }
    }
    enRet = Ok;
    return enRet;
}
 
// DS3231设置时间日期
void I2C_DS3231_SetTime(uint8_t year,uint8_t month,uint8_t day,uint8_t week,uint8_t hour,uint8_t minute,uint8_t second)
{
		uint8_t readData = 0x04;
		// DS3231忙则等待
		while((readData&0x04) > 0)
		{
			I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_STATUS,&readData,1);
		}
	
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_SECOND,DEC_BCD(second));
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_MINUTE,DEC_BCD(minute));
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_HOUR,DEC_BCD(hour));
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_WEEK,DEC_BCD(week));
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_DAY,DEC_BCD(day));
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_MONTH,DEC_BCD(month));
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_YEAR,DEC_BCD(year));
}	

// DS3231读取时间日期
void I2C_DS3231_ReadTime()
{
		uint8_t readTimeList[7]= {0};
    
		uint8_t readData = 0x04;
		// DS3231忙则等待
		while((readData&0x04) > 0)
		{
			I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_STATUS,&readData,1);
		}

		I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_SECOND,readTimeList,7);
		
		calendar.yearH  = 20;			//年千百位
		calendar.yearL  = BCD_DEC(readTimeList[6]);			//年 
		calendar.month  = BCD_DEC(readTimeList[5]);			//月 			
		calendar.date   = BCD_DEC(readTimeList[4]);		  //日 
		calendar.hour   = BCD_DEC(readTimeList[2]);		  //时 
		calendar.minute = BCD_DEC(readTimeList[1]);			//分
		calendar.second = BCD_DEC(readTimeList[0]);			//秒
		calendar.week   = BCD_DEC(readTimeList[3]);		  //周 
}	

//DS3231设置Alarm_1
void I2C_DS3231_SetAlarm_1(boolean_t en,uint8_t date,uint8_t hour,uint8_t minute,uint8_t second)
{
		uint8_t readData = 0x04;
		// DS3231忙则等待
		while((readData&0x04) > 0)
		{
			I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_STATUS,&readData,1);
		}

		if(en)
		{
				I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_ALARM1SECOND,DEC_BCD(second));
				I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_ALARM1MINUTE,DEC_BCD(minute));
				I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_ALARM1HOUR,DEC_BCD(hour));
				I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_ALARM1DATE,DEC_BCD(date));

				I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_CONTROL,&readData,1);
				readData |= 0x01;
				I2C_DS3231_WriteCmd(M0P_I2C0,DS3231_CONTROL,readData);    	//复用引脚设置闹钟中断
		}
		else
		{
				I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_CONTROL,&readData,1);
				readData &= 0xFE;
				I2C_DS3231_WriteCmd(M0P_I2C0,DS3231_CONTROL,readData);    	//复用引脚禁用闹钟中断
		}
}	

//DS3231设置Alarm_2
void I2C_DS3231_SetAlarm_2(boolean_t en, uint8_t hour, uint8_t minute)
{
		uint8_t readData = 0x04;
		// DS3231忙则等待
		while((readData&0x04) > 0)
		{
			I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_STATUS,&readData,1);
		}
		
		//设置日期匹配寄存器A2M4位为1,即每日进行时分匹配
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_ALARM2DATE,0x80);
		//写入时分匹配数据
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_ALARM2MINUTE,DEC_BCD(minute));
		I2C_DS3231_WriteCmd(M0P_I2C0, DS3231_ALARM2HOUR,DEC_BCD(hour));

		if(en)
		{
				I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_CONTROL,&readData,1);
				readData |= 0x02;
				I2C_DS3231_WriteCmd(M0P_I2C0,DS3231_CONTROL,readData);    	//复用引脚设置闹钟中断
		}
		else
		{
				I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_CONTROL,&readData,1);
				readData &= 0xFD;
				I2C_DS3231_WriteCmd(M0P_I2C0,DS3231_CONTROL,readData);    	//复用引脚禁用闹钟中断
		}
}	

// DS3231读取小时数据
uint8_t I2C_DS3231_ReadTime_Hour()
{
		uint8_t readData = 0x04;
		// DS3231忙则等待
		while((readData&0x04) > 0)
		{
			I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_STATUS,&readData,1);
		}

		I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_HOUR,&readData,1);
		
		return readData;
}	

//DS3231读取分钟数据
uint8_t I2C_DS3231_ReadTime_Minute()
{   
		uint8_t readData = 0x04;
		// DS3231忙则等待
		while((readData&0x04) > 0)
		{
			I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_STATUS,&readData,1);
		}

		I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_MINUTE,&readData,1);
		
		return readData;
}	

// 获取温度整数部分
uint8_t I2C_DS3231_getTemperature(void)
{
		uint8_t readData = 0x04;
		// DS3231忙则等待
		while((readData&0x04) > 0)
		{
			I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_STATUS,&readData,1);
		}

		I2C_MasterRead_DS3231Data(M0P_I2C0,DS3231_TEMPERATUREH,&readData,1);

		return readData;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2225457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

栈和队列-栈的练习题

1. 逆波兰表达式 题目: 给出一个算数式的后缀表达式,我们来求他最后算数值. 在解题之前我们来认识一下中缀表达式,和后缀表达式(逆波兰表达式 我们在写数学遇到的那种形式的算数表达式就是中缀表达式,我们要从中缀表达式变为后缀表达式(逆波兰式),计算机时不知道式子的计算…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-25

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-25 0. 前言 大语言模型在很多领域都有成功的应用&#xff0c;在本期计算机前沿技术进展研究介绍中&#xff0c;我们将带来一篇用大语言模型进行诺贝尔文学作品分析的论文。虽然有一定趁最近诺贝尔奖热潮的意味&…

现代数字信号处理I-极大似然估计 学习笔记

目录 1. 极大似然估计的模型介绍 2. 极大似然估计可以达到CRLB的说明 2.1 前期准备&#xff1a;符号定义及说明 2.2 中心极限定理 2.3 大数定理 2.4 说明思路 2.5 具体过程 说明&#xff1a;此部分内容在2024版本的课程中没有提供&#xff0c;需要参考2023之前的课程&…

R语言笔记(五):Apply函数

文章目录 一、Apply Family二、apply(): rows or columns of a matrix or data frame三、Applying a custom function四、Applying a custom function "on-the-fly"五、Applying a function that takes extra arguments六、Whats the return argument?七、Optimized…

基于贝叶斯优化的K折交叉验证BP回归模型(可预测未来数据)

基于贝叶斯优化的K折交叉验证BP回归模型(可预测未来数据) 目录 基于贝叶斯优化的K折交叉验证BP回归模型(可预测未来数据)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于贝叶斯优化的K折交叉验证BP回归模型的多输入单一输出回归模型 Matlab版本&#xff1a;2020a及以…

深度学习_循环神经网络_预测平安中国股价(文末附带数据集下载链接, 长期有效, 如果有大佬愿意帮忙, 我先在这磕一个,感谢)

简介: 使用循环神经网络RNN对股价进行预测, 也就是搭建循环神经网络对中国平安的收盘股价进行预测 深度学习训练流程 1.数据集导入 2.数据预处理 3.模型训练 模型结构要求: 单层简单R…

U盘恢复数据,这四款软件你必须知道!

不管是哪个行业哪个职位&#xff0c;数据安全都是很重要的。比如说我认识的财务姐姐&#xff0c;每天处理的财务报表、客户信息、合同文件等&#xff0c;都必须确保万无一失&#xff0c;尤其是各种U盘数据。为了防止数据丢失后找不到数据的情况&#xff0c;今天来和大家分享四款…

智能管线巡检系统:强化巡检质量,确保安全高效运维

线路巡检质量的监控是确保线路安全、稳定运行的重要环节。为了有效监控巡检质量&#xff0c;采用管线巡检系统是一种高效、科学的手段。以下是对如何通过管线巡检系统实现线路巡检质量监控的详细分析&#xff1a; 一、巡检速度监控 管线巡检系统能够实时监控巡检人员的巡检速度…

算力引领 智慧安防| Gooxi助力安防行业智慧化转型

安防行业作为AI最为合适生长的天然场域&#xff0c;早在国内AI市场爆发之前就提前进行了“预演”&#xff0c;诞生了AI四小龙。当AIGC赋能安防技术革新&#xff0c;安防再次与AI浪潮撞了个满怀。在这一次大模型的浪潮中&#xff0c;安防场上的老玩家纷纷抢滩大模型。而今&#…

Word中Normal.dotm样式模板文件

Normal.dotm文档 首先将自己电脑中C:\Users\自己电脑用户名\AppData\Roaming\Microsoft\Templates路径下的Normal.dotm文件做备份&#xff0c;在下载本文中的Normal.dotm文件&#xff0c;进行替换&#xff0c;重新打开word即可使用。 字体样式如下&#xff08;可自行修改&#…

Redis 发布订阅 总结

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 发布订阅 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 发布订阅 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis &a…

git的学习之远程进行操作

1.代码托管GitHub&#xff1a;充当中央服务器仓库的角色 2.git远程进行操作 3.配置本地服务器的公钥 4.推送 5.git远程操作 pull .gitignore 6.给命令配置别名 git config --global alias.st status 7.标签管理 git tag -a [name] -m "XXX" [commit_id] 操作标签…

ICMP Redirect Attack Lab

本实验的网络拓扑图如下所示: Task 1: Launching ICMP Redirect Attack 在Victim主机上查看路由表&#xff0c;结果如下: 2.在Victim上Ping 192.168.60.60.5&#xff0c;结果如下: 3.在Attkaker主机上创建redirect_attack.py文件&#xff0c; 内容如下: 4.接下来查看tracerou…

纯GO语言开发RTSP流媒体服务器-RTSP推流直播、本地保存录像、录像回放、http-flv及hls协议分发

温馨提示&#xff1a;我们分享的文章是给需要的人&#xff0c;不需要的人请绕过&#xff0c;文明浏览&#xff0c;误恶语伤人&#xff01; 前言 在软件开发中遇到使用流媒体音视频的行业比较多&#xff0c;如安防监控系统、无人机巡逻视频上云处理、直播平台、教育与企业培训…

C++红黑树插入操作的模拟实现

1.红黑树概念 1.1什么是红黑树 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩…

Linux 重启命令全解析:深入理解与应用指南

Linux 重启命令全解析&#xff1a;深入理解与应用指南 在 Linux 系统中&#xff0c;掌握正确的重启命令是确保系统稳定运行和进行必要维护的关键技能。本文将深入解析 Linux 中常见的重启命令&#xff0c;包括功能、用法、适用场景及注意事项。 一、reboot 命令 功能简介 re…

Flutter图片控件(七)

1、加载图片 import package:flutter/material.dart;void main() {runApp(const MaterialApp(home: MyHomePage(),)); }class MyHomePage extends StatelessWidget {const MyHomePage({super.key});overrideWidget build(BuildContext context) {return Scaffold(appBar: AppB…

Python:背景知识及环境安装

一、计算机的基础概念 1.1 什么是计算机&#xff1f; 最早我们有计算器&#xff0c;但是他只能完成算数运算的功能 而计算机能完成的工作有&#xff1a; &#xff08;1&#xff09;算术运算 &#xff08;2&#xff09;逻辑判断 &#xff08;3&#xff09;数据存储 &#xff08…

k8s 二进制部署安装(一)

目录 环境准备 初始化操作系统 部署docker 引擎 部署 etcd 集群 准备签发证书环境 部署 Master01 服务器相关组件 apiserver scheduler controller-manager.sh admin etcd 存储了 Kubernetes 集群的所有配置数据和状态信息&#xff0c;包括资源对象、集群配置、元数据…

基于SSM+小程序的旅游社交登录管理系统(旅游4)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 ​ 本旅游社交小程序功能有管理员和用户。管理员有个人中心&#xff0c;用户管理&#xff0c;每日签到管理&#xff0c;景点推荐管理&#xff0c;景点分类管理&#xff0c;防疫查询管理&a…