精华内容
下载资源
问答
  • ds3231时钟模块资料源码,包括模块说明和基于arduino的示例程序。
  • 存储器LR24C32D(C411182)主要...区别在于,只有基本电路部分才能实现时钟功能。 物理尺寸很小,约为2cm * 2cm。 该电路相对简单,主要是因为背面的电池座占据较大的空间,否则可以进一步减小尺寸。 小型1220纽扣电池
  • DS3231时钟模块

    2018-12-05 21:24:18
    这是ds3231的描述,用户手册,淘宝卖家给我的,上面详细描述了how to 使用
  • 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;
    }
    
    

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

    展开全文
  • Arduino--DS3231实时时钟模块

    千次阅读 2020-06-05 15:52:19
    DS3231模块如下图所示,包含RTC芯片和一个32K的EEPROM 自身带有3V CR2032锂电池,掉电时间不变 包括年、月、日、时、分、秒等具体时间信息 (2)连线 主要是IIC接口,本实验室是基于Arduino UNO使用,其他开发板的...

    (1)简介

    DS3231是一种实时的RCT IC芯片,通过IIC接口进行通信,默认IIC通信地址为(0X68,应该是在头文件中有体现,大概看了下也没找见),其内部还集成了晶体振荡器和温度传感器,无需连接外部晶体。

    DS3231模块如下图所示,包含RTC芯片和一个32K的EEPROM
    自身带有3V CR2032锂电池,掉电时间不变

    包括年、月、日、时、分、秒等具体时间信息
    在这里插入图片描述

    (2)连线

    主要是IIC接口,本实验室是基于Arduino UNO使用,其他开发板的IIC管脚参考以下链接:
    https://blog.csdn.net/u011816009/article/details/106566177

    UNODS3231
    5VVCC
    GNDGND
    A4SDA
    A5SCL

    (3)程序

    DS3231的程序分为两个部分,第一部分是设置起始日历信息(年、月、日、时、分、秒),第二部分是具体显示部分,分别阐述如下:

    设置日历

    // DS3231_Serial_Easy
    // Copyright (C)2015 Rinky-Dink Electronics, Henning Karlsen. All right reserved
    // web: http://www.RinkyDinkElectronics.com/
    //
    // A quick demo of how to use my DS3231-library to 
    // quickly send time and date information over a serial link
    //
    // To use the hardware I2C (TWI) interface of the Arduino you must connect
    // the pins as follows:
    //
    // Arduino Uno/2009:
    // ----------------------
    // DS3231:  SDA pin   -> Arduino Analog 4 or the dedicated SDA pin
    //          SCL pin   -> Arduino Analog 5 or the dedicated SCL pin
    //
    // Arduino Leonardo:
    // ----------------------
    // DS3231:  SDA pin   -> Arduino Digital 2 or the dedicated SDA pin
    //          SCL pin   -> Arduino Digital 3 or the dedicated SCL pin
    //
    // Arduino Mega:
    // ----------------------
    // DS3231:  SDA pin   -> Arduino Digital 20 (SDA) or the dedicated SDA pin
    //          SCL pin   -> Arduino Digital 21 (SCL) or the dedicated SCL pin
    //
    // Arduino Due:
    // ----------------------
    // DS3231:  SDA pin   -> Arduino Digital 20 (SDA) or the dedicated SDA1 (Digital 70) pin
    //          SCL pin   -> Arduino Digital 21 (SCL) or the dedicated SCL1 (Digital 71) pin
    //
    // The internal pull-up resistors will be activated when using the 
    // hardware I2C interfaces.
    //
    // You can connect the DS3231 to any available pin but if you use any
    // other than what is described above the library will fall back to
    // a software-based, TWI-like protocol which will require exclusive access 
    // to the pins used, and you will also have to use appropriate, external
    // pull-up resistors on the data and clock signals.
    //
     
    #include <DS3231.h>
     
    // Init the DS3231 using the hardware interface
    DS3231  rtc(SDA, SCL);
     
    void setup()
    {
      // Setup Serial connection
      Serial.begin(115200);
      // Uncomment the next line if you are using an Arduino Leonardo
      //while (!Serial) {}
      
      // Initialize the rtc object
      rtc.begin();
      
      // The following lines can be uncommented to set the date and time
      rtc.setDOW(FRIDAY);     // Set Day-of-Week to SUNDAY
      rtc.setTime(14, 26, 50);     // Set the time to 12:00:00 (24hr format)
      rtc.setDate(3, 8, 2019);   // Set the date to January 1st, 2014
    }
     
    void loop()
    {
      // Send Day-of-Week
      Serial.print(rtc.getDOWStr());
      Serial.print(" ");
      
      // Send date
      Serial.print(rtc.getDateStr());
      Serial.print(" -- ");
     
      // Send time
      Serial.println(rtc.getTimeStr());
      
      // Wait one second before repeating :)
      delay (1000);
    }
    

    时钟显示

    #include <DS3231.h>
     
    // Init the DS3231 using the hardware interface
    DS3231  rtc(SDA, SCL);
     
    void setup()
    {
      // Setup Serial connection
      Serial.begin(115200);
      // Uncomment the next line if you are using an Arduino Leonardo
      //while (!Serial) {}
      
      // Initialize the rtc object
      rtc.begin();
      
      // The following lines can be uncommented to set the date and time
      //rtc.setDOW(WEDNESDAY);     // Set Day-of-Week to SUNDAY
      //rtc.setTime(12, 0, 0);     // Set the time to 12:00:00 (24hr format)
      //rtc.setDate(1, 1, 2014);   // Set the date to January 1st, 2014
    }
     
    void loop()
    {
      // Send Day-of-Week
      Serial.print(rtc.getDOWStr());
      Serial.print(" ");
      
      // Send date
      Serial.print(rtc.getDateStr());
      Serial.print(" -- ");
     
      // Send time
      Serial.println(rtc.getTimeStr());
      
      // Wait one second before repeating :)
      delay (1000);
    }
    
    展开全文
  • DS3231模块使用

    2019-03-12 10:55:15
    1.该模块是最近流行的时钟模块,采取IIC和单片机进行通讯,使用方便,且目前已经找到可以使用的程序。 2.注意:该模块的工作电压是3.3V不是5V,昨天在排除所有硬件错误之后,采取3.3V供电,模块才能正常工作,因此...

    1.该模块是最近流行的时钟模块,采取IIC和单片机进行通讯,使用方便,且目前已经找到可以使用的程序。
    2.注意:该模块的工作电压是3.3V不是5V,昨天在排除所有硬件错误之后,采取3.3V供电,模块才能正常工作,因此使用任何电子元器件之前或者模块之前,一定要先看电气手册
    已经成功调通的程序代码如下:
    DS3231.c :
    #include “DS3231.h”
    #include “myiic.h”
    #include “delay.h”
    #include “sys.h”
    _calendar_obj calendar;

    #define DS3231_WriteAddress 0xD0
    #define DS3231_ReadAddress 0xD1

    u8 BCD2HEX(u8 val)
    {
    u8 i;
    i= val&0x0f;
    val >>= 4;
    val &= 0x0f;
    val *= 10;
    i += val;

    return i;
    

    }

    u16 B_BCD(u8 val)
    {
    u8 i,j,k;
    i=val/10;
    j=val%10;
    k=j+(i<<4);
    return k;
    }

    void I2cByteWrite(u8 addr,u8 bytedata)
    {
    IIC_Start();
    delay_us(5);
    IIC_Send_Byte(DS3231_WriteAddress);
    IIC_Wait_Ack();
    delay_us(5);
    IIC_Send_Byte(addr);
    IIC_Wait_Ack();
    delay_us(5);
    IIC_Send_Byte(bytedata);
    IIC_Wait_Ack();
    delay_us(5);
    IIC_Stop();
    }

    u8 I2cByteRead(u8 addr)
    {
    u8 Dat=0;

    IIC_Start();
    IIC_Send_Byte(DS3231_WriteAddress);
    IIC_Wait_Ack();
    delay_us(5);
    IIC_Send_Byte(addr);
    IIC_Wait_Ack();
    delay_us(5);
    IIC_Start();
    IIC_Send_Byte(DS3231_ReadAddress);
    IIC_Wait_Ack();
    delay_us(5);
    Dat=IIC_Read_Byte(1);
    IIC_Stop();
    return Dat;
    }

    void DS3231_Init(void)
    {
    IIC_Init();
    I2cByteWrite(0x0e,0);
    delay_ms(2);
    I2cByteWrite(0x0f,0x0);
    delay_ms(2);
    }
    void DS3231_Get(void)
    {
    calendar.w_year=I2cByteRead(0x06);
    calendar.w_month=I2cByteRead(0x05);
    calendar.w_date=I2cByteRead(0x04);
    calendar.hour=I2cByteRead(0x02);
    calendar.min=I2cByteRead(0x01);
    calendar.sec=I2cByteRead(0x00);
    }

    void DS3231_Set(u8 yea,u8 mon,u8 da,u8 hou,u8 min,u8 sec)
    {
    u8 temp=0;

    temp=B_BCD(yea);
    I2cByteWrite(0x06,temp);

    temp=B_BCD(mon);
    I2cByteWrite(0x05,temp);

    temp=B_BCD(da);
    I2cByteWrite(0x04,temp);

    temp=B_BCD(hou);
    I2cByteWrite(0x02,temp);

    temp=B_BCD(min);
    I2cByteWrite(0x01,temp);

    temp=B_BCD(sec);
    I2cByteWrite(0x00,temp);
    }

    void get_show_time(void)
    {

    calendar.w_year=I2cByteRead(0x06);
    calendar.w_year=BCD2HEX(calendar.w_year);

    calendar.w_month=I2cByteRead(0x05);
    calendar.w_month=BCD2HEX(calendar.w_month);

    calendar.w_date=I2cByteRead(0x04);
    calendar.w_date=BCD2HEX(calendar.w_date);

    calendar.hour=I2cByteRead(0x02);
    calendar.hour&=0x3f;
    calendar.hour=BCD2HEX(calendar.hour);

    calendar.min=I2cByteRead(0x01);
    calendar.min=BCD2HEX(calendar.min);

    calendar.sec=I2cByteRead(0x00);
    calendar.sec=BCD2HEX(calendar.sec);
    }
    DS3231.h :
    #include “sys.h”
    #ifndef DS3231_H
    #define DS3231_H

    typedef struct
    {
    u8 hour;
    u8 min;
    u8 sec;
    u32 w_year;
    u8 w_month;
    u8 w_date;
    u8 week;
    }_calendar_obj;
    extern _calendar_obj calendar; //日历结构体

    extern u8 const mon_table[12]; //月份日期数据表

    void DS3231_Init(void);
    void get_show_time(void);
    //u8 RTC_Get_Week(u16 year,u8 month,u8 day);
    void DS3231_Set(u8 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//设置时间

    #endif

    由于该模块使用的IIC通讯,因此特将正点原子的官方IIC程序复制:
    myiic.c

    #include “myiic.h”
    #include “delay.h”
    //
    //本程序只供学习使用,未经作者许可,不得用于其它任何用途
    //ALIENTEK战舰STM32开发板
    //IIC驱动 代码
    //正点原子@ALIENTEK
    //技术论坛:www.openedv.com
    //修改日期:2012/9/9
    //版本:V1.0
    //版权所有,盗版必究。
    //Copyright© 广州市星翼电子科技有限公司 2009-2019
    //All rights reserved
    //

    //初始化IIC
    void IIC_Init(void)
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_SetBits(GPIOC,GPIO_Pin_12|GPIO_Pin_11); 	//PB10,PB11 输出高
    

    }
    //产生IIC起始信号
    void IIC_Start(void)
    {
    SDA_OUT(); //sda线输出
    IIC_SDA=1;
    delay_us(1);
    IIC_SCL=1;
    delay_us(5);
    IIC_SDA=0;//START:when CLK is high,DATA change form high to low
    delay_us(5);
    IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
    delay_us(2);
    }
    //产生IIC停止信号
    void IIC_Stop(void)
    {
    SDA_OUT();//sda线输出
    IIC_SCL=0;
    IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
    delay_us(4);
    IIC_SCL=1;
    delay_us(5);
    IIC_SDA=1;//发送I2C总线结束信号
    delay_us(4);
    }
    //等待应答信号到来
    //返回值:1,接收应答失败
    // 0,接收应答成功
    u8 IIC_Wait_Ack(void)
    {
    u8 ucErrTime=0;
    SDA_IN(); //SDA设置为输入
    IIC_SDA=1;delay_us(1);
    IIC_SCL=1;delay_us(1);
    while(READ_SDA)
    {
    ucErrTime++;
    if(ucErrTime>250)
    {
    IIC_Stop();
    return 1;
    }
    }
    IIC_SCL=0;//时钟输出0
    return 0;
    }
    //产生ACK应答
    void IIC_Ack(void)
    {
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=0;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
    }
    //不产生ACK应答
    void IIC_NAck(void)
    {
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
    }
    //IIC发送一个字节
    //返回从机有无应答
    //1,有应答
    //0,无应答
    void IIC_Send_Byte(u8 txd)
    {
    u8 t;
    SDA_OUT();
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {
    //IIC_SDA=(txd&0x80)>>7;
    if((txd&0x80)>>7)
    IIC_SDA=1;
    else
    IIC_SDA=0;
    txd<<=1;
    delay_us(2); //对TEA5767这三个延时都是必须的
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
    delay_us(2);
    }
    }
    //读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;
    delay_us(2);
    IIC_SCL=1;
    receive<<=1;
    if(READ_SDA)receive++;
    delay_us(1);
    }
    if (!ack)
    IIC_NAck();//发送nACK
    else
    IIC_Ack(); //发送ACK
    return receive;
    }

    myiic.h:

    #ifndef __MYIIC_H
    #define __MYIIC_H
    #include “sys.h”
    //
    //本程序只供学习使用,未经作者许可,不得用于其它任何用途
    //ALIENTEK战舰STM32开发板
    //IIC驱动 代码
    //正点原子@ALIENTEK
    //技术论坛:www.openedv.com
    //修改日期:2012/9/9
    //版本:V1.0
    //版权所有,盗版必究。
    //Copyright© 广州市星翼电子科技有限公司 2009-2019
    //All rights reserved
    //

    //IO方向设置
    #define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}
    #define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}

    //IO操作函数
    #define IIC_SCL PCout(12) //SCL
    #define IIC_SDA PCout(11) //SDA
    #define READ_SDA PCin(11) //输入SDA

    //IIC所有操作函数
    void IIC_Init(void); //初始化IIC的IO口
    void IIC_Start(void); //发送IIC开始信号
    void IIC_Stop(void); //发送IIC停止信号
    void IIC_Send_Byte(u8 txd); //IIC发送一个字节
    u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
    u8 IIC_Wait_Ack(void); //IIC等待ACK信号
    void IIC_Ack(void); //IIC发送ACK信号
    void IIC_NAck(void); //IIC不发送ACK信号

    void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
    u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
    #endif

    展开全文
  • 深入学习 RTC时钟DS3231

    万次阅读 多人点赞 2019-06-20 09:05:53
        接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块。没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里不再重复。 DS3231 2.DS...

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

    快速导航
    单片机菜鸟的博客快速索引(快速找到你要的)

    如果觉得有用,麻烦点赞收藏,您的支持是博主创作的动力。

    1.前言

        接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块。没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里不再重复。

    • DS3231

    2.DS3231介绍

    2.1 为什么使用DS3231

        常用的DS1302需要使用外置晶振,且没有温度补偿,误差较大。这就促使了更高精度的时钟芯片 —— DS3231。
        DS3231内置晶振且有内部温度补偿,误差可做到1分钟每年。说白了,精度更高。

    2.2 DS3231概述

    • DS3231是一款高精度I2C实时时钟器件,具有集成的温度补偿晶体振荡器,集成的晶体振荡器可提高器件的长期精确度;
    • 该器件包含电池输入端(也就是普通纽扣电池),断开主电源时仍可保持精确计时;
    • DS3231的寄存器能保存秒、分、时、星期、日期、月、年和闹钟设置等信息(除了和DS1302有一样的寄存器之外,还额外闹钟寄存器,这是可以做闹钟应用的一个关键点);
    • 少于31天的月份,可自动调整月末日期,包括闰年补偿。时钟的工作格式为24小时或带AM/PM指示的12小时格式;
    • DS3231提供两个可编程日历闹钟和一路可编程方波输出;
    • DS3231与单片机通过I2C双向串行总线传输地址与数据;
    • 自带存储芯片:AT24C32 EEPROM芯片(存储容量32K,可以存不少东西);

    2.3 DS3231电路图&引脚关系

    • 电路图

    image

    • 引脚关系

    image

    2.4 DS3231寄存器

        跟DS1302一样,对于DS3231的操作就是操作对应的寄存器,其寄存器对应关系如下:

    image

    2.5 RTCDS3231库

        老规矩,先看看源码,博主在源码中加入了部分代码注释:

    
    
    #ifndef __RTCDS3231_H__
    #define __RTCDS3231_H__
    
    #include <Arduino.h>
    
    #include "RtcDateTime.h"
    #include "RtcTemperature.h"
    #include "RtcUtility.h"
    
    
    //I2C Slave Address  
    const uint8_t DS3231_ADDRESS = 0x68;
    
    //DS3231 Register Addresses
    const uint8_t DS3231_REG_TIMEDATE  = 0x00;//日期时间相关寄存器的第一个地址
    const uint8_t DS3231_REG_ALARMONE  = 0x07;//闹钟1寄存器
    const uint8_t DS3231_REG_ALARMTWO  = 0x0B;//闹钟2寄存器
                                             
    const uint8_t DS3231_REG_CONTROL   = 0x0E;//控制寄存器
    const uint8_t DS3231_REG_STATUS    = 0x0F;//状态寄存器
    const uint8_t DS3231_REG_AGING     = 0x10;
                                             
    const uint8_t DS3231_REG_TEMP      = 0x11;
    
    //DS3231 Register Data Size if not just 1
    const uint8_t DS3231_REG_TIMEDATE_SIZE = 7;//日期时间相关寄存器的数量
    const uint8_t DS3231_REG_ALARMONE_SIZE = 4;//闹钟1寄存器占用空间大小 4字节
    const uint8_t DS3231_REG_ALARMTWO_SIZE = 3;//闹钟2寄存器占用空间大小 3字节
    
    const uint8_t DS3231_REG_TEMP_SIZE = 2;
    
    // DS3231 Control Register Bits
    const uint8_t DS3231_A1IE  = 0;
    const uint8_t DS3231_A2IE  = 1;
    const uint8_t DS3231_INTCN = 2;
    const uint8_t DS3231_RS1   = 3;
    const uint8_t DS3231_RS2   = 4;
    const uint8_t DS3231_CONV  = 5;
    const uint8_t DS3231_BBSQW = 6;
    const uint8_t DS3231_EOSC  = 7;
    const uint8_t DS3231_AIEMASK = (_BV(DS3231_A1IE) | _BV(DS3231_A2IE));
    const uint8_t DS3231_RSMASK = (_BV(DS3231_RS1) | _BV(DS3231_RS2));
    
    // DS3231 Status Register Bits
    const uint8_t DS3231_A1F      = 0;
    const uint8_t DS3231_A2F      = 1;
    const uint8_t DS3231_BSY      = 2;
    const uint8_t DS3231_EN32KHZ  = 3;
    const uint8_t DS3231_OSF      = 7;
    const uint8_t DS3231_AIFMASK = (_BV(DS3231_A1F) | _BV(DS3231_A2F));
    
    
    // seconds accuracy
    enum DS3231AlarmOneControl
    {
        // bit order:  A1M4  DY/DT  A1M3  A1M2  A1M1
        DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch = 0x00,
        DS3231AlarmOneControl_OncePerSecond = 0x17,
        DS3231AlarmOneControl_SecondsMatch = 0x16,
        DS3231AlarmOneControl_MinutesSecondsMatch = 0x14,
        DS3231AlarmOneControl_HoursMinutesSecondsMatch = 0x10,
        DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch = 0x08,
    };
    
    class DS3231AlarmOne
    {
    public:
        DS3231AlarmOne( uint8_t dayOf,
                uint8_t hour,
                uint8_t minute,
                uint8_t second,
                DS3231AlarmOneControl controlFlags) :
            _flags(controlFlags),
            _dayOf(dayOf),
            _hour(hour),
            _minute(minute),
            _second(second)
        {
        }
    
        uint8_t DayOf() const
        {
            return _dayOf;
        }
    
        uint8_t Hour() const
        {
            return _hour;
        }
    
        uint8_t Minute() const
        {
            return _minute;
        }
    
        uint8_t Second() const
        {
            return _second;
        }
    
        DS3231AlarmOneControl ControlFlags() const
        {
            return _flags;
        }
    
        bool operator == (const DS3231AlarmOne& other) const
        {
            return (_dayOf == other._dayOf &&
                    _hour == other._hour &&
                    _minute == other._minute &&
                    _second == other._second &&
                    _flags == other._flags);
        }
    
        bool operator != (const DS3231AlarmOne& other) const
        {
            return !(*this == other);
        }
    
    protected:
        DS3231AlarmOneControl _flags;
    
        uint8_t _dayOf;
        uint8_t _hour;
        uint8_t _minute;
        uint8_t _second;  
    };
    
    // minutes accuracy
    enum DS3231AlarmTwoControl
    {
        // bit order:  A2M4  DY/DT  A2M3  A2M2
        DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch = 0x00,
        DS3231AlarmTwoControl_OncePerMinute = 0x0b,
        DS3231AlarmTwoControl_MinutesMatch = 0x0a,
        DS3231AlarmTwoControl_HoursMinutesMatch = 0x08,
        DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch = 0x04,
    };
    
    class DS3231AlarmTwo
    {
    public:
        DS3231AlarmTwo( uint8_t dayOf,
                uint8_t hour,
                uint8_t minute,
                DS3231AlarmTwoControl controlFlags) :
            _flags(controlFlags),
            _dayOf(dayOf),
            _hour(hour),
            _minute(minute)
        {
        }
    
        uint8_t DayOf() const
        {
            return _dayOf;
        }
    
        uint8_t Hour() const
        {
            return _hour;
        }
    
        uint8_t Minute() const
        {
            return _minute;
        }
    
        DS3231AlarmTwoControl ControlFlags() const
        {
            return _flags;
        }
    
        bool operator == (const DS3231AlarmTwo& other) const
        {
            return (_dayOf == other._dayOf &&
                    _hour == other._hour &&
                    _minute == other._minute &&
                    _flags == other._flags);
        }
    
        bool operator != (const DS3231AlarmTwo& other) const
        {
            return !(*this == other);
        }
    
    protected:
        DS3231AlarmTwoControl _flags;
    
        uint8_t _dayOf;
        uint8_t _hour;
        uint8_t _minute;
    };
    
    
    enum DS3231SquareWaveClock
    {
        DS3231SquareWaveClock_1Hz  = 0b00000000,
        DS3231SquareWaveClock_1kHz = 0b00001000,
        DS3231SquareWaveClock_4kHz = 0b00010000,
        DS3231SquareWaveClock_8kHz = 0b00011000,
    };
    
    enum DS3231SquareWavePinMode
    {
        DS3231SquareWavePin_ModeNone,
        DS3231SquareWavePin_ModeBatteryBackup,
        DS3231SquareWavePin_ModeClock,
        DS3231SquareWavePin_ModeAlarmOne,
        DS3231SquareWavePin_ModeAlarmTwo,
        DS3231SquareWavePin_ModeAlarmBoth
    };
    
    enum DS3231AlarmFlag
    {
        DS3231AlarmFlag_Alarm1 = 0x01,
        DS3231AlarmFlag_Alarm2 = 0x02,
        DS3231AlarmFlag_AlarmBoth = 0x03,
    };
    
    template<class T_WIRE_METHOD> class RtcDS3231
    {
    public:
        RtcDS3231(T_WIRE_METHOD& wire) :
            _wire(wire),
            _lastError(0)
        {
        }
    
        void Begin()
        {
            //会把三个引脚设置为输入状态
            _wire.begin();
        }
    
        uint8_t LastError()
        {
            return _lastError;
        }
    
        bool IsDateTimeValid()
        {
            uint8_t status = getReg(DS3231_REG_STATUS);
            return !(status & _BV(DS3231_OSF));
        }
    
        /**
         * 判断时钟是否正在运行
         * @return bool
         *        true 时钟运行
         *        false 时钟停振,进入低功耗态
         */
        bool GetIsRunning()
        {
            //判断控制寄存器 DS3231_EOSC bit位置
            uint8_t creg = getReg(DS3231_REG_CONTROL);
            return !(creg & _BV(DS3231_EOSC));
        }
    
        /**
         * 设置时钟是否运行
         * @param isRunning
         *        true 时钟运行
         *        false 时钟停振,进入低功耗态
         */
        void SetIsRunning(bool isRunning)
        {
            uint8_t creg = getReg(DS3231_REG_CONTROL);
            if (isRunning)
            {
                creg &= ~_BV(DS3231_EOSC);
            }
            else
            {
                creg |= _BV(DS3231_EOSC);
            }
            setReg(DS3231_REG_CONTROL, creg);
        }
    
        /**
         * 设置日期时间
         * @param RtcDateTime 日期时间对象
         */
        void SetDateTime(const RtcDateTime& dt)
        {
            // clear the invalid flag
            uint8_t status = getReg(DS3231_REG_STATUS);
            status &= ~_BV(DS3231_OSF); // clear the flag
            setReg(DS3231_REG_STATUS, status);
    
            // set the date time  批量设置时间
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_TIMEDATE);
    
            _wire.write(Uint8ToBcd(dt.Second()));//秒数
            _wire.write(Uint8ToBcd(dt.Minute()));//分钟
            _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
    
            uint8_t year = dt.Year() - 2000;
            uint8_t centuryFlag = 0;
    
            if (year >= 100)
            {
                year -= 100;
                centuryFlag = _BV(7);
            }
    
            // RTC Hardware Day of Week is 1-7, 1 = Monday
            // convert our Day of Week to Rtc Day of Week
            uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
    
            _wire.write(Uint8ToBcd(rtcDow));
    
            _wire.write(Uint8ToBcd(dt.Day()));//天数
            _wire.write(Uint8ToBcd(dt.Month()) | centuryFlag);//月份
            _wire.write(Uint8ToBcd(year));//年份
    
            _lastError = _wire.endTransmission();
        }
    
        /**
         * 获取日期时间
         * @return RtcDateTime 日期时间对象
         */
        RtcDateTime GetDateTime()
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_TIMEDATE);
            _lastError = _wire.endTransmission();
            if (_lastError != 0)
            {
                return RtcDateTime(0);
            }
    
            _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TIMEDATE_SIZE);
            uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数
            uint8_t minute = BcdToUint8(_wire.read());//分钟
            uint8_t hour = BcdToBin24Hour(_wire.read());//小时
    
            _wire.read();  // throwing away day of week as we calculate it
    
            uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数
            uint8_t monthRaw = _wire.read();//月份
            uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份
    
            if (monthRaw & _BV(7)) // century wrap flag
            {
                year += 100;
            }
            uint8_t month = BcdToUint8(monthRaw & 0x7f);
    
    
            return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
        }
    
        RtcTemperature GetTemperature()
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_TEMP);
            _lastError = _wire.endTransmission();
            if (_lastError != 0)
            {
                return RtcTemperature(0);
            }
    
            // Temperature is represented as a 10-bit code with a resolution
            // of 1/4th �C and is accessable as a signed 16-bit integer at
            // locations 11h and 12h.
            //
            //       |         r11h          | DP |         r12h         |
            // Bit:   15 14 13 12 11 10  9  8   .  7  6  5  4  3  2  1  0  -1 -2
            //         s  i  i  i  i  i  i  i   .  f  f  0  0  0  0  0  0
            //
            // As it takes (8) right-shifts to register the decimal point (DP) to
            // the right of the 0th bit, the overall word scaling equals 256.
            //
            // For example, at +/- 25.25�C, concatenated registers <r11h:r12h> =
            // 256 * (+/- 25+(1/4)) = +/- 6464, or 1940h / E6C0h.
    
            _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TEMP_SIZE);
            int8_t  r11h = _wire.read();                  // MS byte, signed temperature
            return RtcTemperature( r11h, _wire.read() );  // LS byte is r12h
        }
    
        void Enable32kHzPin(bool enable)
        {
            uint8_t sreg = getReg(DS3231_REG_STATUS);
    
            if (enable == true)
            {
                sreg |= _BV(DS3231_EN32KHZ);
            }
            else
            {
                sreg &= ~_BV(DS3231_EN32KHZ);
            }
    
            setReg(DS3231_REG_STATUS, sreg);
        }
    
        /**
         * 设置方波输出
         */
        void SetSquareWavePin(DS3231SquareWavePinMode pinMode)
        {
            uint8_t creg = getReg(DS3231_REG_CONTROL);
    
            // clear all relevant bits to a known "off" state
            creg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW));
            creg |= _BV(DS3231_INTCN);  // set INTCN to disables SQW
    
            switch (pinMode)
            {
            case DS3231SquareWavePin_ModeNone:
                break;
    
            case DS3231SquareWavePin_ModeBatteryBackup:
                creg |= _BV(DS3231_BBSQW); // set battery backup flag
                creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW 
                break;
    
            case DS3231SquareWavePin_ModeClock:
                creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW 
                break;
    
            case DS3231SquareWavePin_ModeAlarmOne:
                creg |= _BV(DS3231_A1IE);
                break;
    
            case DS3231SquareWavePin_ModeAlarmTwo:
                creg |= _BV(DS3231_A2IE);
                break;
    
            case DS3231SquareWavePin_ModeAlarmBoth:
                creg |= _BV(DS3231_A1IE) | _BV(DS3231_A2IE);
                break;
            }
    
            setReg(DS3231_REG_CONTROL, creg);
        }
    
        void SetSquareWavePinClockFrequency(DS3231SquareWaveClock freq)
        {
            uint8_t creg = getReg(DS3231_REG_CONTROL);
    
            creg &= ~DS3231_RSMASK; // Set to 0
            creg |= (freq & DS3231_RSMASK); // Set freq bits
    
            setReg(DS3231_REG_CONTROL, creg);
        }
    
        /**
         * 设置闹钟1
         */
        void SetAlarmOne(const DS3231AlarmOne& alarm)
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_ALARMONE);
    
            _wire.write(Uint8ToBcd(alarm.Second()) | ((alarm.ControlFlags() & 0x01) << 7));
            _wire.write(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x02) << 6));
            _wire.write(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x04) << 5)); // 24 hour mode only
    
            uint8_t rtcDow = alarm.DayOf();
            if (alarm.ControlFlags() == DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
            {
                rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
            }
    
            _wire.write(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x18) << 3));
    
            _lastError = _wire.endTransmission();
        }
    
        /**
         * 设置闹钟2
         */
        void SetAlarmTwo(const DS3231AlarmTwo& alarm)
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_ALARMTWO);
    
            _wire.write(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x01) << 7));
            _wire.write(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x02) << 6)); // 24 hour mode only
    
            // convert our Day of Week to Rtc Day of Week if needed
            uint8_t rtcDow = alarm.DayOf();
            if (alarm.ControlFlags() == DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch)
            {
                rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
            }
            
            _wire.write(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x0c) << 4));
    
            _lastError = _wire.endTransmission();
        }
    
        /**
         * 获取闹钟1
         */
        DS3231AlarmOne GetAlarmOne()
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_ALARMONE);
            _lastError = _wire.endTransmission();
            if (_lastError != 0)
            {
                return DS3231AlarmOne(0, 0, 0, 0, DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch);
            }
    
            _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMONE_SIZE);
    
            uint8_t raw = _wire.read();
            uint8_t flags = (raw & 0x80) >> 7;
            uint8_t second = BcdToUint8(raw & 0x7F);
    
            raw = _wire.read();
            flags |= (raw & 0x80) >> 6;
            uint8_t minute = BcdToUint8(raw & 0x7F);
    
            raw = _wire.read();
            flags |= (raw & 0x80) >> 5;
            uint8_t hour = BcdToBin24Hour(raw & 0x7f);
    
            raw = _wire.read();
            flags |= (raw & 0xc0) >> 3;
            uint8_t dayOf = BcdToUint8(raw & 0x3f);
    
            if (flags == DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
            {
                dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
            }
    
            return DS3231AlarmOne(dayOf, hour, minute, second, (DS3231AlarmOneControl)flags);
        }
    
        /**
         * 获取闹钟2
         */
        DS3231AlarmTwo GetAlarmTwo()
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_ALARMTWO);
            _lastError = _wire.endTransmission();
            if (_lastError != 0)
            {
                return DS3231AlarmTwo(0, 0, 0, DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch);
            }
    
            _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMTWO_SIZE);
    
            uint8_t raw = _wire.read();
            uint8_t flags = (raw & 0x80) >> 7;
            uint8_t minute = BcdToUint8(raw & 0x7F);
    
            raw = _wire.read();
            flags |= (raw & 0x80) >> 6;
            uint8_t hour = BcdToBin24Hour(raw & 0x7f);
    
            raw = _wire.read();
            flags |= (raw & 0xc0) >> 4;
            uint8_t dayOf = BcdToUint8(raw & 0x3f);
    
            if (flags == DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch)
            {
                dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
            }
    
            return DS3231AlarmTwo(dayOf, hour, minute, (DS3231AlarmTwoControl)flags);
        }
    
        // Latch must be called after an alarm otherwise it will not
        // trigger again
        DS3231AlarmFlag LatchAlarmsTriggeredFlags()
        {
            uint8_t sreg = getReg(DS3231_REG_STATUS);
            uint8_t alarmFlags = (sreg & DS3231_AIFMASK);
            sreg &= ~DS3231_AIFMASK; // clear the flags
            setReg(DS3231_REG_STATUS, sreg);
            return (DS3231AlarmFlag)alarmFlags;
        }
    
        void ForceTemperatureCompensationUpdate(bool block)
        {
            uint8_t creg = getReg(DS3231_REG_CONTROL);
            creg |= _BV(DS3231_CONV); // Write CONV bit
            setReg(DS3231_REG_CONTROL, creg);
    
            while (block && (creg & _BV(DS3231_CONV)) != 0)
            {
                // Block until CONV is 0
                creg = getReg(DS3231_REG_CONTROL);
            }
        }
    
        int8_t GetAgingOffset()
        {
            return getReg(DS3231_REG_AGING);
        }
    
        void SetAgingOffset(int8_t value)
        {
            setReg(DS3231_REG_AGING, value);
        }
    
    private:
        T_WIRE_METHOD& _wire;
        uint8_t _lastError;
    
        uint8_t getReg(uint8_t regAddress)
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(regAddress);
            _lastError = _wire.endTransmission();
            if (_lastError != 0)
            {
                return 0;
            }
    
            // control register
            _wire.requestFrom(DS3231_ADDRESS, (uint8_t)1);
    
            uint8_t regValue = _wire.read();
            return regValue;
        }
    
        void setReg(uint8_t regAddress, uint8_t regValue)
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(regAddress);
            _wire.write(regValue);
            _lastError = _wire.endTransmission();
        }
    
    };
    
    #endif // __RTCDS3231_H__
    
    

    2.5.1 Begin() —— 初始化

    函数说明:

    /**
     * 初始化,会把三个引脚设置为输入状态
     */
    void Begin()
    

    2.5.2 LastError() —— 获取上次错误编码

    函数说明:

    /**
     * 获取上次错误编码
     * @return 返回错误编码
     */
    uint8_t LastError()
    

    注意:

    • 错误编码请参考 https://www.arduino.cc/en/Reference/WireEndTransmission

    2.5.3 IsDateTimeValid() —— 判断时间是否有效

    函数说明:

    /**
     * 判断时间是否有效
     * @return false 通常意味着电池没电或日期和时间从未设置
     *         true  意味时间有效
     */
    bool IsDateTimeValid()
    

    2.5.4 GetIsRunning() —— 判断时钟是否正在运行

    函数说明:

    /**
     * 判断时钟是否正在运行
     * @return bool
     *        true 时钟运行
     *        false 时钟停振,进入低功耗态
     */
    bool GetIsRunning()
    

    源码说明:

        /**
         * 判断时钟是否正在运行
         * @return bool
         *        true 时钟运行
         *        false 时钟停振,进入低功耗态
         */
        bool GetIsRunning()
        {
            //判断控制寄存器 DS3231_EOSC bit位置
            uint8_t creg = getReg(DS3231_REG_CONTROL);
            return !(creg & _BV(DS3231_EOSC));
        }
    

    2.5.5 SetIsRunning() —— 设置时钟是否运行

    函数说明:

    /**
     * 设置时钟是否运行
     * @param isRunning
     *        true 时钟运行
     *        false 时钟停振,进入低功耗态
     */
    void SetIsRunning(bool isRunning)
    

    源码说明:

        /**
         * 设置时钟是否运行
         * @param isRunning
         *        true 时钟运行
         *        false 时钟停振,进入低功耗态
         */
        void SetIsRunning(bool isRunning)
        {
            uint8_t creg = getReg(DS3231_REG_CONTROL);
            if (isRunning)
            {
                creg &= ~_BV(DS3231_EOSC);
            }
            else
            {
                creg |= _BV(DS3231_EOSC);
            }
            setReg(DS3231_REG_CONTROL, creg);
        }
    

    2.5.6 SetDateTime() —— 设置日期时间

    函数说明:

    /**
     * 设置日期时间
     * @param RtcDateTime 日期时间对象
     */
    void SetDateTime(const RtcDateTime& dt)
    

    源码说明:

    /**
         * 设置日期时间
         * @param RtcDateTime 日期时间对象
         */
        void SetDateTime(const RtcDateTime& dt)
        {
            // clear the invalid flag
            uint8_t status = getReg(DS3231_REG_STATUS);
            status &= ~_BV(DS3231_OSF); // clear the flag
            setReg(DS3231_REG_STATUS, status);
    
            // set the date time  批量设置时间
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_TIMEDATE);
    
            _wire.write(Uint8ToBcd(dt.Second()));//秒数
            _wire.write(Uint8ToBcd(dt.Minute()));//分钟
            _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
    
            uint8_t year = dt.Year() - 2000;
            uint8_t centuryFlag = 0;
    
            if (year >= 100)
            {
                year -= 100;
                centuryFlag = _BV(7);
            }
    
            // RTC Hardware Day of Week is 1-7, 1 = Monday
            // convert our Day of Week to Rtc Day of Week
            uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
    
            _wire.write(Uint8ToBcd(rtcDow));
    
            _wire.write(Uint8ToBcd(dt.Day()));//天数
            _wire.write(Uint8ToBcd(dt.Month()) | centuryFlag);//月份
            _wire.write(Uint8ToBcd(year));//年份
    
            _lastError = _wire.endTransmission();
        }
    

    2.5.7 GetDateTime() —— 获取日期时间

    函数说明:

    /**
     * 获取日期时间
     * @return RtcDateTime 日期时间对象
     */
    RtcDateTime GetDateTime()
    

    源码说明:

    /**
         * 获取日期时间
         * @return RtcDateTime 日期时间对象
         */
        RtcDateTime GetDateTime()
        {
            _wire.beginTransmission(DS3231_ADDRESS);
            _wire.write(DS3231_REG_TIMEDATE);
            _lastError = _wire.endTransmission();
            if (_lastError != 0)
            {
                return RtcDateTime(0);
            }
    
            _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TIMEDATE_SIZE);
            uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数
            uint8_t minute = BcdToUint8(_wire.read());//分钟
            uint8_t hour = BcdToBin24Hour(_wire.read());//小时
    
            _wire.read();  // throwing away day of week as we calculate it
    
            uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数
            uint8_t monthRaw = _wire.read();//月份
            uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份
    
            if (monthRaw & _BV(7)) // century wrap flag
            {
                year += 100;
            }
            uint8_t month = BcdToUint8(monthRaw & 0x7f);
    
    
            return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
        }
    

    2.5.8 Enable32kHzPin() —— 使能32kHz引脚输出

    函数说明:

    /**
     * 使能32kHz引脚输出
     * @param enable true 使能
     *                false 禁止
     */
    void Enable32kHzPin(bool enable)
    

    2.5.10 SetSquareWavePin() —— 设置方波输出

    函数说明:

    /**
     * 设置方波输出
     * @param DS3231SquareWavePinMode 方波引脚模式
     */
    void SetSquareWavePin(DS3231SquareWavePinMode pinMode)
    

    DS3231SquareWavePinMode 参数说明:

    • DS3231SquareWavePin_ModeNone 禁止引脚输出
    • DS3231SquareWavePin_ModeBatteryBackup 如果外部电源电压低于电池电压,该引脚会触发中断
    • DS3231SquareWavePin_ModeClock 该引脚触发频率由SetSquareWavePinClockFrequency方法定义
    • DS3231SquareWavePin_ModeAlarmOne 闹钟1会触发
    • DS3231SquareWavePin_ModeAlarmTwo 闹钟2会触发
    • DS3231SquareWavePin_ModeAlarmBoth 闹钟1或者闹钟2都会触发

    源码说明:

     /**
         * 设置方波输出
         */
        void SetSquareWavePin(DS3231SquareWavePinMode pinMode)
        {
            uint8_t creg = getReg(DS3231_REG_CONTROL);
    
            // clear all relevant bits to a known "off" state
            creg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW));
            creg |= _BV(DS3231_INTCN);  // set INTCN to disables SQW
    
            switch (pinMode)
            {
            case DS3231SquareWavePin_ModeNone:
                break;
    
            case DS3231SquareWavePin_ModeBatteryBackup:
                creg |= _BV(DS3231_BBSQW); // set battery backup flag
                creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW 
                break;
    
            case DS3231SquareWavePin_ModeClock:
                creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable SQW 
                break;
    
            case DS3231SquareWavePin_ModeAlarmOne:
                creg |= _BV(DS3231_A1IE);
                break;
    
            case DS3231SquareWavePin_ModeAlarmTwo:
                creg |= _BV(DS3231_A2IE);
                break;
    
            case DS3231SquareWavePin_ModeAlarmBoth:
                creg |= _BV(DS3231_A1IE) | _BV(DS3231_A2IE);
                break;
            }
    
            setReg(DS3231_REG_CONTROL, creg);
        }
    

    2.5.11 SetSquareWavePinClockFrequency() —— 设置方波时钟频率

    函数说明:

    /**
     * 设置方波时钟频率
     * @param DS3231SquareWaveClock 方波时钟频率
     */
    void SetSquareWavePinClockFrequency(DS3231SquareWaveClock freq)
    

    DS3231SquareWaveClock 参数说明:

    • DS3231SquareWaveClock_1Hz
    • DS3231SquareWaveClock_1kHz
    • DS3231SquareWaveClock_4kHz
    • DS3231SquareWaveClock_8kHz

    2.5.12 SetAlarmOne() —— 设置闹钟1

    函数说明:

    /**
     * 设置闹钟1
     * @param DS3231AlarmOne 闹钟1
     */
    void SetAlarmOne(const DS3231AlarmOne& alarm)
    

    注意点:

    • 当达到闹钟定义的条件,就会触发中断,INT/SQW输出低电平信号;

    DS3231AlarmOne源码解析:

    class DS3231AlarmOne
    {
    public:
        DS3231AlarmOne( uint8_t dayOf,
                uint8_t hour,
                uint8_t minute,
                uint8_t second,
                DS3231AlarmOneControl controlFlags) :
            _flags(controlFlags),
            _dayOf(dayOf),
            _hour(hour),
            _minute(minute),
            _second(second)
        {
        }
    
        /**
         * 返回一周的一天或者一个月中的一天
         */
        uint8_t DayOf() const
        {
            return _dayOf;
        }
    
        /**
         * 返回一天的小时 24h制
         */
        uint8_t Hour() const
        {
            return _hour;
        }
    
        /**
         * 返回分钟
         */
        uint8_t Minute() const
        {
            return _minute;
        }
    
        /**
         * 返回秒数
         */
        uint8_t Second() const
        {
            return _second;
        }
    
        DS3231AlarmOneControl ControlFlags() const
        {
            return _flags;
        }
    
        bool operator == (const DS3231AlarmOne& other) const
        {
            return (_dayOf == other._dayOf &&
                    _hour == other._hour &&
                    _minute == other._minute &&
                    _second == other._second &&
                    _flags == other._flags);
        }
    
        bool operator != (const DS3231AlarmOne& other) const
        {
            return !(*this == other);
        }
    
    protected:
        DS3231AlarmOneControl _flags;
    
        uint8_t _dayOf;
        uint8_t _hour;
        uint8_t _minute;
        uint8_t _second;  
    };
    

    重点看构造函数:

    /**
     * 建立闹钟1对象
     * @param dayOf - (0-6) (1-31) day of the week or the day of the month , see flags below
     * @param hour - (0-23) the hour of the day
     * @param minute - (0-59) the minute of the hour
     * @param second - (0-59) the second of the minute
     * @param controlFlags 
     *  -- DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch  月天时分秒都匹配才会触发中断
     *  -- DS3231AlarmOneControl_OncePerSecond 每一秒都触发
     *  -- DS3231AlarmOneControl_SecondsMatch  每一分钟的秒数匹配才触发
     *  -- DS3231AlarmOneControl_MinutesSecondsMatch 每小时里面的分秒都匹配才触发
     *  -- DS3231AlarmOneControl_HoursMinutesSecondsMatch  一天中时分秒都匹配才触发
     *  -- DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch 一个星期中天时分秒都匹配才触发
     */
    DS3231AlarmOne( uint8_t dayOf, uint8_t hour, uint8_t minute, uint8_t second, DS3231AlarmOneControl controlFlags)
    

    2.5.13 GetAlarmOne() —— 获取闹钟1

    函数说明:

    /**
     * 获取闹钟1
     * @return DS3231AlarmOne 闹钟1
     */
    DS3231AlarmOne GetAlarmOne()
    

    2.5.14 SetAlarmTwo() —— 设置闹钟2

    函数说明:

    /**
     * 设置闹钟2
     * @param DS3231AlarmTwo 闹钟2
     */
    void SetAlarmTwo(const DS3231AlarmTwo& alarm)
    

    注意点:

    • 当达到闹钟定义的条件,就会触发中断,INT/SQW输出低电平信号;

    DS3231AlarmTwo源码解析:

    class DS3231AlarmTwo
    {
    public:
        DS3231AlarmTwo( uint8_t dayOf,
                uint8_t hour,
                uint8_t minute,
                DS3231AlarmTwoControl controlFlags) :
            _flags(controlFlags),
            _dayOf(dayOf),
            _hour(hour),
            _minute(minute)
        {
        }
    
        /**
         * 返回一周的一天或者一个月中的一天
         */
        uint8_t DayOf() const
        {
            return _dayOf;
        }
    
        /**
         * 返回一天的小时 24h制
         */
        uint8_t Hour() const
        {
            return _hour;
        }
    
    
        /**
         * 返回分钟
         */
        uint8_t Minute() const
        {
            return _minute;
        }
    
        DS3231AlarmTwoControl ControlFlags() const
        {
            return _flags;
        }
    
        bool operator == (const DS3231AlarmTwo& other) const
        {
            return (_dayOf == other._dayOf &&
                    _hour == other._hour &&
                    _minute == other._minute &&
                    _flags == other._flags);
        }
    
        bool operator != (const DS3231AlarmTwo& other) const
        {
            return !(*this == other);
        }
    
    protected:
        DS3231AlarmTwoControl _flags;
    
        uint8_t _dayOf;
        uint8_t _hour;
        uint8_t _minute;
    };
    

    重点看构造函数:

    /**
     * 建立闹钟2对象
     * @param dayOf - (0-6) (1-31) day of the week or the day of the month , see flags below
     * @param hour - (0-23) the hour of the day
     * @param minute - (0-59) the minute of the hour
     * @param controlFlags 
     *  -- DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch  每月天时分都匹配才会触发中断
     *  -- DS3231AlarmTwoControl_OncePerMinute  每一分钟都触发
     *  -- DS3231AlarmTwoControl_MinutesMatch   每一小时的分钟匹配才触发
     *  -- DS3231AlarmTwoControl_HoursMinutesMatch  每天里面的时分都匹配才触发
     *  -- DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch   每星期的天时分匹配才触发
     */
    DS3231AlarmTwo( uint8_t dayOf, uint8_t hour, uint8_t minute, DS3231AlarmTwoControl controlFlags)
    

    2.5.15 GetAlarmTwo() —— 获取闹钟2

    函数说明:

    /**
     * 获取闹钟2
     * @return DS3231AlarmTwo 闹钟2
     */
    DS3231AlarmTwo GetAlarmTwo()
    

    2.5.16 LatchAlarmsTriggeredFlags() —— 处理闹钟触发

    函数说明:

    /**
     * 处理闹钟触发
     * @return DS3231AlarmFlag
     *        --- DS3231AlarmFlag_Alarm1  闹钟1触发
     *        --- DS3231AlarmFlag_Alarm2   闹钟2触发
     *        --- DS3231AlarmFlag_AlarmBoth    闹钟1、2触发
     */
    DS3231AlarmFlag LatchAlarmsTriggeredFlags()
    

    注意点:

    • 当闹钟触发之后必须要调用该方法,不然不会再次触发,用来确保我们处理了闹钟事件;

    2.6 EepromAt24c32库

        前面说到了,DS3231时钟模块集成了AT24c32 eeprom存储芯片,如果我们需要用到存储数据功能,就得引入 EepromAt24c32库。那么,我们来看看该库有什么方法。

    2.6.1 Begin() —— 初始化

    函数说明:

    /**
     * 初始化引脚
     */
    void Begin()
    

    2.6.2 LastError() —— 获取上次错误编码

    函数说明:

    /**
     * 获取上次错误编码
     * @return 返回错误编码
     */
    uint8_t LastError()
    

    注意:

    • 错误编码请参考 https://www.arduino.cc/en/Reference/WireEndTransmission

    2.6.3 SetMemory() —— 存储数据

    函数说明:

    /***
     * 写入数据
     * @param  memoryAddress 地址偏移量
     * @param  value 数据
     */
    void SetMemory(uint16_t memoryAddress, uint8_t value)
    
    /**
     * 批量写入数据
     * @param pValue 批量数据
     * @param countBytes 数据字节数
     */
    uint8_t SetMemory(uint16_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
    
    

    2.6.4 GetMemory() —— 读取数据

    函数说明:

    /***
     * 读取数据
     * @param  memoryAddress 地址偏移量
     * @return  数据
     */
    uint8_t GetMemory(uint16_t memoryAddress)
    
    /***
     * 批量读取数据
     * @param  memoryAddress 地址偏移量
     * @param pValue 存储空间
     * @param countBytes 数据字节数
     */
    uint8_t SetMemory(uint16_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
    
    

    2.7 DS3231接线

    DS3231采用I2C总线方式,SCLK、SDA。

    3.测试用例

    测试用例分为三个:

    • 测试时间
    • 测试闹钟
    • 测试存储

    3.1 测试时间

    实验内容

    • 设置时间并在串口上打印时间

    实验器材

    • Mega2560 + DS3231

    引脚连接

    模块引脚Mega2560引脚
    VCCVCC5V
    GNDGND
    SDASDA(20)
    SCLSCL(21)

    实验代码

    
    // CONNECTIONS:
    // DS3231 SDA --> SDA
    // DS3231 SCL --> SCL
    // DS3231 VCC --> 3.3v or 5v
    // DS3231 GND --> GND
    
    /* for software wire use below
    #include <SoftwareWire.h>  // must be included here so that Arduino library object file references work
    #include <RtcDS3231.h>
    
    SoftwareWire myWire(SDA, SCL);
    RtcDS3231<SoftwareWire> Rtc(myWire);
     for software wire use above */
    
    /* for normal hardware wire use below */
    #include <Wire.h> // must be included here so that Arduino library object file references work
    #include <RtcDS3231.h>
    RtcDS3231<TwoWire> Rtc(Wire);
    /* for normal hardware wire use above */
    
    
    void setup () 
    {
        Serial.begin(57600);
    
        Serial.print("compiled: ");
        Serial.print(__DATE__);
        Serial.println(__TIME__);
    
        //--------RTC SETUP ------------
        // if you are using ESP-01 then uncomment the line below to reset the pins to
        // the available pins for SDA, SCL
        // Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
        
        Rtc.Begin();
    
        RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
        printDateTime(compiled);
        Serial.println();
    
        if (!Rtc.IsDateTimeValid()) 
        {
            if (Rtc.LastError() != 0)
            {
                // we have a communications error
                // see https://www.arduino.cc/en/Reference/WireEndTransmission for 
                // what the number means
                Serial.print("RTC communications error = ");
                Serial.println(Rtc.LastError());
            }
            else
            {
                // Common Cuases:
                //    1) first time you ran and the device wasn't running yet
                //    2) the battery on the device is low or even missing
    
                Serial.println("RTC lost confidence in the DateTime!");
    
                // following line sets the RTC to the date & time this sketch was compiled
                // it will also reset the valid flag internally unless the Rtc device is
                // having an issue
    
                Rtc.SetDateTime(compiled);
            }
        }
    
        if (!Rtc.GetIsRunning())
        {
            Serial.println("RTC was not actively running, starting now");
            Rtc.SetIsRunning(true);
        }
    
        RtcDateTime now = Rtc.GetDateTime();
        if (now < compiled) 
        {
            Serial.println("RTC is older than compile time!  (Updating DateTime)");
            Rtc.SetDateTime(compiled);
        }
        else if (now > compiled) 
        {
            Serial.println("RTC is newer than compile time. (this is expected)");
        }
        else if (now == compiled) 
        {
            Serial.println("RTC is the same as compile time! (not expected but all is fine)");
        }
    
        // never assume the Rtc was last configured by you, so
        // just clear them to your needed state
        Rtc.Enable32kHzPin(false);
        Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); 
    }
    
    void loop () 
    {
        if (!Rtc.IsDateTimeValid()) 
        {
            if (Rtc.LastError() != 0)
            {
                // we have a communications error
                // see https://www.arduino.cc/en/Reference/WireEndTransmission for 
                // what the number means
                Serial.print("RTC communications error = ");
                Serial.println(Rtc.LastError());
            }
            else
            {
                // Common Cuases:
                //    1) the battery on the device is low or even missing and the power line was disconnected
                Serial.println("RTC lost confidence in the DateTime!");
            }
        }
    
        RtcDateTime now = Rtc.GetDateTime();
        printDateTime(now);
        Serial.println();
    
    	RtcTemperature temp = Rtc.GetTemperature();
    	temp.Print(Serial);
    	// you may also get the temperature as a float and print it
        // Serial.print(temp.AsFloatDegC());
        Serial.println("C");
    
        delay(10000); // ten seconds
    }
    
    #define countof(a) (sizeof(a) / sizeof(a[0]))
    
    void printDateTime(const RtcDateTime& dt)
    {
        char datestring[20];
    
        snprintf_P(datestring, 
                countof(datestring),
                PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
                dt.Month(),
                dt.Day(),
                dt.Year(),
                dt.Hour(),
                dt.Minute(),
                dt.Second() );
        Serial.print(datestring);
    }
    
    

    实验结果:

    在这里插入图片描述

    3.2 测试闹钟

    实验内容

    • 设置时间并设置闹钟

    实验器材

    • Mega2560 + DS3231

    引脚连接

    模块引脚Mega2560引脚
    VCCVCC5V
    GNDGND
    SDASDA(20)
    SCLSCL(21)
    SQW19

    实验代码

    
    // CONNECTIONS:
    // DS3231 SDA --> SDA
    // DS3231 SCL --> SCL
    // DS3231 VCC --> 3.3v or 5v
    // DS3231 GND --> GND
    // SQW --->  (Pin19) Don't forget to pullup (4.7k to 10k to VCC)
    
    /* for software wire use below
    #include <SoftwareWire.h>  // must be included here so that Arduino library object file references work
    #include <RtcDS3231.h>
    
    SoftwareWire myWire(SDA, SCL);
    RtcDS3231<SoftwareWire> Rtc(myWire);
     for software wire use above */
    
    /* for normal hardware wire use below */
    #include <Wire.h> // must be included here so that Arduino library object file references work
    #include <RtcDS3231.h>
    RtcDS3231<TwoWire> Rtc(Wire);
    /* for normal hardware wire use above */
    
    
    // Interrupt Pin Lookup Table
    // (copied from Arduino Docs)
    //
    // CAUTION:  The interrupts are Arduino numbers NOT Atmel numbers
    //   and may not match (example, Mega2560 int.4 is actually Atmel Int2)
    //   this is only an issue if you plan to use the lower level interupt features
    //
    // Board           int.0    int.1   int.2   int.3   int.4   int.5
    // ---------------------------------------------------------------
    // Uno, Ethernet    2       3
    // Mega2560         2       3       21      20     [19]      18 
    // Leonardo         3       2       0       1       7
    
    #define RtcSquareWavePin 19 // Mega2560
    #define RtcSquareWaveInterrupt 4 // Mega2560
    
    // marked volatile so interrupt can safely modify them and
    // other code can safely read and modify them
    volatile uint16_t interuptCount = 0;
    volatile bool interuptFlag = false;
    
    void InteruptServiceRoutine()
    {
        // since this interupted any other running code,
        // don't do anything that takes long and especially avoid
        // any communications calls within this routine
        interuptCount++;
        interuptFlag = true;
    }
    
    void setup () 
    {
        Serial.begin(57600);
    
        // set the interupt pin to input mode
        pinMode(RtcSquareWavePin, INPUT);
    
        //--------RTC SETUP ------------
        // if you are using ESP-01 then uncomment the line below to reset the pins to
        // the available pins for SDA, SCL
        // Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
        
        Rtc.Begin();
    
        RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    
        if (!Rtc.IsDateTimeValid()) 
        {
            if (Rtc.LastError() != 0)
            {
                // we have a communications error
                // see https://www.arduino.cc/en/Reference/WireEndTransmission for 
                // what the number means
                Serial.print("RTC communications error = ");
                Serial.println(Rtc.LastError());
            }
            else
            {
                Serial.println("RTC lost confidence in the DateTime!");
                Rtc.SetDateTime(compiled);
            }
        }
    
        if (!Rtc.GetIsRunning())
        {
            Serial.println("RTC was not actively running, starting now");
            Rtc.SetIsRunning(true);
        }
    
        RtcDateTime now = Rtc.GetDateTime();
        if (now < compiled) 
        {
            Serial.println("RTC is older than compile time!  (Updating DateTime)");
            Rtc.SetDateTime(compiled);
        }
        
        Rtc.Enable32kHzPin(false);
        Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeAlarmBoth); 
    
        // Alarm 1 set to trigger every day when 
        // the hours, minutes, and seconds match
        RtcDateTime alarmTime = now + 88; // into the future
        DS3231AlarmOne alarm1(
                alarmTime.Day(),
                alarmTime.Hour(),
                alarmTime.Minute(), 
                alarmTime.Second(),
                DS3231AlarmOneControl_HoursMinutesSecondsMatch);
        Rtc.SetAlarmOne(alarm1);
    
        // Alarm 2 set to trigger at the top of the minute
        DS3231AlarmTwo alarm2(
                0,
                0,
                0, 
                DS3231AlarmTwoControl_OncePerMinute);
        Rtc.SetAlarmTwo(alarm2);
    
        // throw away any old alarm state before we ran
        Rtc.LatchAlarmsTriggeredFlags();
    
        // setup external interupt 
        attachInterrupt(RtcSquareWaveInterrupt, InteruptServiceRoutine, FALLING);
    }
    
    void loop () 
    {
        if (!Rtc.IsDateTimeValid()) 
        {
            if (Rtc.LastError() != 0)
            {
                // we have a communications error
                // see https://www.arduino.cc/en/Reference/WireEndTransmission for 
                // what the number means
                Serial.print("RTC communications error = ");
                Serial.println(Rtc.LastError());
            }
            else
            {
                Serial.println("RTC lost confidence in the DateTime!");
            }
        }
    
        RtcDateTime now = Rtc.GetDateTime();
    
        printDateTime(now);
        Serial.println();
    
        // we only want to show time every 10 seconds
        // but we want to show responce to the interupt firing
        for (int timeCount = 0; timeCount < 20; timeCount++)
        {
            if (Alarmed())
            {
                Serial.print(">>Interupt Count: ");
                Serial.print(interuptCount);
                Serial.println("<<");
            }
            delay(500);
        }
    }
    
    bool Alarmed()
    {
        bool wasAlarmed = false;
        if (interuptFlag)  // check our flag that gets sets in the interupt
        {
            wasAlarmed = true;
            interuptFlag = false; // reset the flag
            
            // this gives us which alarms triggered and
            // then allows for others to trigger again
            DS3231AlarmFlag flag = Rtc.LatchAlarmsTriggeredFlags();
    
            if (flag & DS3231AlarmFlag_Alarm1)
            {
                Serial.println("alarm one triggered");
            }
            if (flag & DS3231AlarmFlag_Alarm2)
            {
                Serial.println("alarm two triggered");
            }
        }
        return wasAlarmed;
    }
    
    #define countof(a) (sizeof(a) / sizeof(a[0]))
    
    void printDateTime(const RtcDateTime& dt)
    {
    	char datestring[20];
    
    	snprintf_P(datestring, 
    			countof(datestring),
    			PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
    			dt.Month(),
    			dt.Day(),
    			dt.Year(),
    			dt.Hour(),
    			dt.Minute(),
    			dt.Second() );
        Serial.print(datestring);
    }
    
    

    实验结果:

    在这里插入图片描述

    3.3 测试存储

    实验内容

    • 设置时间并在串口上打印时间,同时存储“What time is it in Greenwich?”字符串进EEPROM

    实验器材

    • Mega2560 + DS3231

    引脚连接

    模块引脚Mega2560引脚
    VCCVCC5V
    GNDGND
    SDASDA(20)
    SCLSCL(21)

    实验代码

    
    // CONNECTIONS:
    // DS1307 SDA --> SDA
    // DS1307 SCL --> SCL
    // DS1307 VCC --> 5v
    // DS1307 GND --> GND
    
    #define countof(a) (sizeof(a) / sizeof(a[0]))
    
    /* for software wire use below
    #include <SoftwareWire.h>  // must be included here so that Arduino library object file references work
    #include <RtcDS3231.h>
    #include <EepromAt24C32.h>
    
    SoftwareWire myWire(SDA, SCL);
    RtcDS1307<SoftwareWire> Rtc(myWire);
    /* for software wire use above */
    
    /* for normal hardware wire use below */
    #include <Wire.h> // must be included here so that Arduino library object file references work
    #include <RtcDS3231.h>
    #include <EepromAt24C32.h>
    
    RtcDS3231<TwoWire> Rtc(Wire);
    EepromAt24c32<TwoWire> RtcEeprom(Wire);
    
    // if you have any of the address pins on the RTC soldered together
    // then you need to provide the state of those pins, normally they
    // are connected to vcc with a reading of 1, if soldered they are 
    // grounded with a reading of 0.  The bits are in the order A2 A1 A0
    // thus the following would have the A2 soldered together
    // EepromAt24c32<TwoWire> RtcEeprom(Wire, 0b011);
    
    /* for normal hardware wire use above */
    
    // nothing longer than 32 bytes
    // rtc eeprom memory is 32 byte pages
    // writing is limited to each page, so it will wrap at page
    // boundaries. 
    // But reading is only limited by the buffer in Wire class which
    // by default is 32
    const char data[] = "What time is it in Greenwich?";
    const uint16_t stringAddr = 64; // stored on page boundary
    
    void setup () 
    {
        Serial.begin(57600);
    
        Serial.print("compiled: ");
        Serial.print(__DATE__);
        Serial.println(__TIME__);
    
        //--------RTC SETUP ------------
        // if you are using ESP-01 then uncomment the line below to reset the pins to
        // the available pins for SDA, SCL
        // Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
        
        Rtc.Begin();
        RtcEeprom.Begin();
        
        RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
        printDateTime(compiled);
        Serial.println();
    
        if (!Rtc.IsDateTimeValid()) 
        {
            if (Rtc.LastError() != 0)
            {
                // we have a communications error
                // see https://www.arduino.cc/en/Reference/WireEndTransmission for 
                // what the number means
                Serial.print("RTC communications error = ");
                Serial.println(Rtc.LastError());
            }
            else
            {
                Serial.println("RTC lost confidence in the DateTime!");
                Rtc.SetDateTime(compiled);
            }
        }
    
        if (!Rtc.GetIsRunning())
        {
            Serial.println("RTC was not actively running, starting now");
            Rtc.SetIsRunning(true);
        }
    
        RtcDateTime now = Rtc.GetDateTime();
        if (now < compiled) 
        {
            Serial.println("RTC is older than compile time!  (Updating DateTime)");
            Rtc.SetDateTime(compiled);
        }
    
        // never assume the Rtc was last configured by you, so
        // just clear them to your needed state
        Rtc.Enable32kHzPin(false);
        Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); 
    
    /* comment out on a second run to see that the info is stored long term */
        // Store something in memory on the Eeprom
    
        // store starting address of string
        RtcEeprom.SetMemory(0, stringAddr); 
        // store the string, nothing longer than 32 bytes due to paging
        uint8_t written = RtcEeprom.SetMemory(stringAddr, (const uint8_t*)data, sizeof(data) - 1); // remove the null terminator strings add
        // store the length of the string
        RtcEeprom.SetMemory(1, written); // store the 
    /* end of comment out section */
    }
    
    void loop () 
    {
        if (!Rtc.IsDateTimeValid()) 
        {
            if (Rtc.LastError() != 0)
            {
                // we have a communications error
                // see https://www.arduino.cc/en/Reference/WireEndTransmission for 
                // what the number means
                Serial.print("RTC communications error = ");
                Serial.println(Rtc.LastError());
            }
            else
            {
                // Common Cuases:
                //    1) the battery on the device is low or even missing and the power line was disconnected
                Serial.println("RTC lost confidence in the DateTime!");
            }
        }
    
        RtcDateTime now = Rtc.GetDateTime();
    
        printDateTime(now);
        Serial.println();
    
        delay(5000);
    
        // read data
    
        // get the offset we stored our data from address zero
        uint8_t address = RtcEeprom.GetMemory(0);
        if (address != stringAddr)
        {
            Serial.print("address didn't match ");
            Serial.println(address);
        }
        
        {
            // get the size of the data from address 1
            uint8_t count = RtcEeprom.GetMemory(1);
            uint8_t buff[64];
    
            // get our data from the address with the given size
            uint8_t gotten = RtcEeprom.GetMemory(address, buff, count);
    
            if (gotten != count ||
                count != sizeof(data) - 1) // remove the extra null terminator strings add
            {
                Serial.print("something didn't match, count = ");
                Serial.print(count, DEC);
                Serial.print(", gotten = ");
                Serial.print(gotten, DEC);
                Serial.println();
            }
            Serial.print("data read (");
            Serial.print(gotten);
            Serial.print(") = \"");
            for (uint8_t ch = 0; ch < gotten; ch++)
            {
                Serial.print((char)buff[ch]);
            }
            Serial.println("\"");
        }
    
        
        delay(5000);
    }
    
    
    
    void printDateTime(const RtcDateTime& dt)
    {
        char datestring[20];
    
        snprintf_P(datestring, 
                countof(datestring),
                PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
                dt.Month(),
                dt.Day(),
                dt.Year(),
                dt.Hour(),
                dt.Minute(),
                dt.Second() );
        Serial.print(datestring);
    }
    
    

    实验结果:

    在这里插入图片描述

    4.总结

    本篇主要针对DS3231进行讲解RTC库,相对比较简单,基本上看完例子都能熟练使用,读者可以继续自行研究DS3234库,思想非常相似。

    展开全文
  • 玩转 RTC时钟DS3231

    千次阅读 2019-06-18 23:42:00
        接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块。没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里不再重复。 DS3231 2.DS3231...
  • AT24C02时钟模块使用附带完整程序DS1302引脚说明DS1302相关寄存器时序说明代码讲解DS1302初始化读取当前时间参考程序 DS1302引脚说明 引脚 说明 Vcc2 主电源 Vcc1 后备电源(断电后保证时钟正常运行) ...
  •  本次的实验传感为:DS3231时钟模块),对于时钟模块的具体信息我也就不多介绍,大家可以自行度娘,具体功能无非就是让单片机中能够起到获取时间的作用。该模块是可以由IIC协议去驱动的,再加上所要的操作也是...
  • Arduino UNO DS3231高精度RTC芯片 制作时钟

    万次阅读 多人点赞 2017-07-28 00:13:17
    是一个时钟模块,上面包含一个纽扣电池位置,可以在主机断电的情况下还可以继续计算时间,以便以后记录使用。 模块参数:  1.尺寸:38mm(长)*22mm(宽)*14mm(高)  2.重量:8g  3.工作电压:3.3--5.5V  4.时钟芯片:...
  • 这个时钟系列的前的四篇分别是: 《8266+DS3231时钟之开发个时钟遇到的N个坑【一】》 《8266+ds3231时钟之arduino官网发布的DS3231库的分析【二...前面几篇,完成了8266NodeMCU、DS3231时钟模块、TM1638及数码管显示、
  • ds1302时钟程序详解

    2010-08-04 20:29:33
    控制字节的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1...
  • 深入学习RTC时钟DS1302

    万次阅读 多人点赞 2019-06-20 09:05:27
        为了避免资源浪费以及重复编写代码,博主还是抱着尝试的心态去寻找能够同时兼容 DS1302、DS3231甚至其他的时钟模块的第三方库。终于,还是被我找到了 —— Rtc 时钟库。 2.RTC 2.1 简介 Arduino Real ...
  • 本系列为全网最详细的拓展模块使用心得和教程,欢迎各位看官发表高见,对您有所帮助将是我最大的荣幸,创作不易,点赞评论,拒绝白嫖,从我做起。 作者希望系本列只起到快速开发和辅助学习的作用,允许的话应该从...
  • 搜了一下DS12C887的资料,最详细的是郭天祥关于这个模块的视频教程,教程中讲解了这个模块的使用手册和程序的编写,但是这个教程是在51单片机上讲的,由于51与Arduino的不同,把程序直接移植到Arduino上比较困难。...
  • 硬件资源:指示灯DS0、OLED模块 STM32控制程序 设置STM32与OLED模块相连接的IO(设置与OLED相连的IO口设置为输出); 初始化OLED模块(硬复位SSD1306、驱动IC初始化程序、开启显示、清零显存、开始显示); ...
  • * 读写DS1302 时钟芯片 * @author Yangtf * 很棒的文档 http://www.21ic.com/jichuzhishi/datasheet/DS1302/data/185858.html * 时序图 ...
  • 玩转 RTC时钟DS1302

    千次阅读 2019-06-18 23:41:00
        为了避免资源浪费以及重复编写代码,博主还是抱着尝试的心态去寻找能够同时兼容 DS1302、DS3231甚至其他的时钟模块的第三方库。终于,还是被我找到了 —— Rtc 时钟库。 2.RTC 2.1 简介 Arduino Real Time ...
  • 51单片机 ,TM1638芯片+DS1302驱动共阴数码管时钟,最后包括按键检测程序(是分开的。) 有原理图 和 源代码 word 格式可以直接拷贝
  • 2、DS3231完整的通讯及操作模块(包括时间日期读取、设定,闹钟读取、设定); 3、74HC595完整的通讯函数; 4、RGB LED全彩控制IC(LPD6803)的完整通讯程序; 5、三种荧光管数字呼吸切换效果的完整实现程序; 6、DS...
  •  这次课程设计要完成的是制作一个基于51单片机的电子时钟的万年历(protues仿真),需要用到1602液晶屏+DS1302+DS18B20+按键等模块。各个的模块就不在一一介绍,直接讲解这个系统的功能,首先是四个按键,第一个...
  • 蓝桥杯嵌入式开发经验分享(8.实时时钟讲解

    千次阅读 多人点赞 2018-07-23 17:07:58
    RTC实际上就是实时时钟,如果接触过单片机的同学也许你听过DS1302实时时钟模块,实际上就是拿来获取时间的,STM32把RTC集成在了芯片内部,它内部具有专门的实时时钟单元,不需要外接其他模块。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 870
精华内容 348
关键字:

ds3231时钟模块详细讲解