硬件I2C实战(DS3231)

硬件I2C实战(DS3231)驱动 DS3231 芯片 可输出时间及对应的温度 ds3231

大家好,欢迎来到IT知识分享网。

 

 

一、模块简介

DS3231时钟芯片结构原理

680010f532cb4495b0513b60229d6b61.png

VCC为电源引脚;

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

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

GND为地;

VBAT为备用电源输入;

SDA为串行数据输入输出;

SCL为串行时钟输入。

内部结构图

505e7e303b1f4c8c89058aecad8192f7.png

5af1caade341483fb12f6507ce7d7b32.png

 

DS3231时钟寄存器介绍

1c63b9fb236b467ba904bc013a991137.png

1. 32 kHz的TCXO

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

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

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

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所列。

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.c

#include "DS3231.h" #include "Delay.h" _calendar_obj calendar; void DS3231_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE); //I2C2是APB1的外设,所以用APB1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //GPIO是APB2的外设,所以用APB2 GPIO_InitTypeDef GPIO_InitStructure;//初始化GPIO GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //复用开漏模式 ,复用:控制权交给外设。开漏:协议要求 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_InitTypeDef I2C_InitStructure;//初始化I2C2 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//是否给应答 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//响应7位地址 I2C_InitStructure.I2C_ClockSpeed = ;//时钟频率:100KHZ I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //占空比 低:高 = 2:1; //因为I2C总线采用上拉电阻,是弱上拉,弹回高电平速度比拉低电平速度慢,所以要给低电平更多资源 //让数据有足够的时间放到SDA总线上 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_OwnAddress1 = 0x0A ; I2C_Init(I2C2,&I2C_InitStructure); I2C_Cmd(I2C2,ENABLE);//使能I2C2; } void DS3231_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) { uint32_t Timeout; Timeout = 10000; while(I2C_CheckEvent(I2Cx,I2C_EVENT) != SUCCESS) { Timeout--; if(Timeout == 0) { break; } } } void DS3231_WriteReg(uint8_t RegAddress, uint8_t Data) {//指定地址写寄存器 (写一个字节) I2C_GenerateSTART(I2C2,ENABLE);//开始时序 //等待EV5事件(可参考上文图245);等不到(SUCCESS)就一直空循环,等到就跳出循环,执行下一步; DS3231_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); I2C_Send7bitAddress(I2C2,DS3231_ADDRESS,I2C_Direction_Transmitter);//发送从机地址(点名从机) //I2C2,从机地址,读写位置0(发送); DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待EV6事件; I2C_SendData(I2C2,RegAddress);//发送寄存器的地址(点名从机的寄存器) DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);//等待EV8事件; I2C_SendData(I2C2,Data); DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); //连续发送字节时,等待EV8事件,当发送最后一个字节后,应等待EV8_2事件; I2C_GenerateSTOP(I2C2,ENABLE);//停止时序 } uint8_t DS3231_ReadReg(uint8_t RegAddress) {//指定地址读寄存器 (读一个字节) uint8_t Data; while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)) { uint32_t Timeout; Timeout = 10000; if(Timeout == 0) { break; } Timeout--; } I2C_GenerateSTART(I2C2,ENABLE);//开始时序 DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //等待EV5事件(可参考上文图245);等不到(SUCCESS)就一直空循环,等到就跳出循环,执行下一步; I2C_Send7bitAddress(I2C2,DS3231_ADDRESS,I2C_Direction_Transmitter);//发送从机地址(点名从机) //I2C2,从机地址,读写位置0(发送); DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待EV6事件; I2C_SendData(I2C2,RegAddress);//发送寄存器的地址(点名从机的寄存器) DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待EV8_2事件(等待发送完成再产生重复起始条件); I2C_GenerateSTART(I2C2,ENABLE);//开始时序 DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件 I2C_Send7bitAddress(I2C2,DS3231_ADDRESS,I2C_Direction_Receiver);//接收,读写位置1(接收) DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//等待EV6事件; I2C_AcknowledgeConfig(I2C2,DISABLE);//不给应答,ACK置0; I2C_GenerateSTOP(I2C2,ENABLE);//停止时序 DS3231_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//等待EV7事件; Data = I2C_ReceiveData(I2C2); I2C_AcknowledgeConfig(I2C2,ENABLE);//ACK置1,方便产生下一个开始时序; return Data; } / * @brief 等待 DS3231 到准备状态 * @param 无 * @retval 无 */ void I2C_WaitDs3231StandbyState(void) { vu16 SR1_Tmp = 0; do { /* 发送起始信号 */ I2C_GenerateSTART(I2C2, ENABLE); /* 读 I2C1 SR1 寄存器 */ SR1_Tmp = I2C_ReadRegister(I2C2, I2C_Register_SR1); /* 发送 DS3231 地址 + 方向 */ I2C_Send7bitAddress(I2C2, DS3231_ADDRESS, I2C_Direction_Transmitter); }while(!(I2C_ReadRegister(I2C2, I2C_Register_SR1) & 0x0002)); /* 清除 AF 位 */ I2C_ClearFlag(I2C2, I2C_FLAG_AF); /* 发送停止信号 */ I2C_GenerateSTOP(I2C2, 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 * @arg 分别输入 年 月 日 星期 时 分 秒 * @retval 无 */ void I2C_DS3231_SetTime(u8 yea,u8 mon,u8 da,u8 we,u8 hou,u8 min,u8 sec) { // u8 yea = 0x23;//23年 0010 0011 //u8 mon = 0x10;//10月 0001 0000 //u8 da = 0x13;//13日 0001 0011 //u8 we = 0x06;//周6 0000 0110 //u8 hou = 0x08; //8时 0000 1000 //u8 min = 0x08;//8分 //u8 sec = 0x08;//8秒 while((DS3231_ReadReg(DS3231_STATUS)&0x04) == 0x04) { } DS3231_WriteReg(DS3231_CONTROL,0x25);//001 DS3231_WriteReg(0x06,DEC_BCD(yea)); DS3231_WriteReg(0x05,DEC_BCD(mon)); DS3231_WriteReg(0x04,DEC_BCD(da)); DS3231_WriteReg(0x03,DEC_BCD(we)); DS3231_WriteReg(0x02,DEC_BCD(hou)); DS3231_WriteReg(0x01,DEC_BCD(min)); DS3231_WriteReg(0x00,DEC_BCD(sec)); } / * @brief 获取时间 * @param * @arg pBuffer:存放从DS3231读取的数据的缓冲区指针 * @arg ReadAddr:读取数据的DS3231的地址 * @arg NumByteToWrite:要从DS3231读取的字节数 * @retval 返回1,表示读取成功. */ void I2C_DS3231_getTime(void) { calendar.year= DS3231_ReadReg(0x06); calendar.month= DS3231_ReadReg(0x05); calendar.date= DS3231_ReadReg(0x04); calendar.week= DS3231_ReadReg(0x03); calendar.hour= DS3231_ReadReg(0x02); calendar.hour&=0x3f; calendar.min= DS3231_ReadReg(0x01); calendar.sec= DS3231_ReadReg(0x00); } / * @brief 获取温度 * @param 无 * @retval 无 */ void I2C_DS3231_getTemperature(void) { while((DS3231_ReadReg(DS3231_STATUS)&0x04) == 0x04) { } DS3231_WriteReg(DS3231_CONTROL, 0x20|0x05);//0010 0000 //0000 0101 0010 0101 calendar.temperature=DS3231_ReadReg(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; //算出星期 } 

DS3231.h

​ #ifndef __I2C_DS3231_H #define __I2C_DS3231_H /* Includes ------------------------------------------------------------------*/ #include "stm32f10x.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; //日历结构体 /* 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 DS3231_Init(void); void DS3231_WriteReg(uint8_t RegAddress, uint8_t Data); uint8_t DS3231_ReadReg(uint8_t RegAddress); 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 I2C_DS3231_getTime(void); void I2C_DS3231_getTemperature(void); void GregorianDay(_calendar_obj * tm); #endif /* __I2C_DS3231_H */ ​

main.c

#include "stm32f10x.h" #include "Delay.h" #include "DS3231.h" #include "Serial.h" #include <string.h> /* Private typedef -----------------------------------------------------------*/ uint8_t i=0; extern _calendar_obj calendar; //日历结构体 int main(void) { Serial_Init(); DS3231_Init(); I2C_DS3231_SetTime(23, 11, 11, 6, 20, 54, 00); //Serial_SendByte(calendar.temperature); while(1) { // Delay_ms(20); // if(i==1) // { // //I2C_DS3231_SetTime(calendar.year, calendar.month, calendar.date, calendar.week, calendar.hour, calendar.min, calendar.sec); // I2C_DS3231_SetTime(23, 11, 11, 6, 20, 54, 00); // 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");换行!!! Delay_ms(5000); Serial_SendByte(calendar.year); Serial_SendByte(calendar.month); Serial_SendByte(calendar.date); Serial_SendByte(calendar.week); Serial_SendByte( calendar.hour); Serial_SendByte(calendar.min); Serial_SendByte(calendar.sec); Serial_SendByte(calendar.temperature); } } 

 

 

 

 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/119517.html

(0)
上一篇 2025-11-04 21:26
下一篇 2025-11-04 21:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信