深入学习 RTC时钟库 DS3231

article/2025/10/11 14:34:13

❤️博主介绍❤️
😬 作者:单片机菜鸟哥
👉火爆博客:ESP8266 Arduino教程

零基础从入门到熟悉Arduino平台下开发ESP8266,同时会涉及网络编程知识。专栏文章累计超过60篇,分为基础篇、网络篇、应用篇、高级篇,涵盖ESP8266大部分开发技巧。

👻最近更新:ESP32 MicroPython应用

讲解Python在esp32上的应用,包括网络请求、爬虫

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

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

文章目录

    • 1.前言
    • 2.DS3231介绍
      • 2.1 为什么使用DS3231
      • 2.2 DS3231概述
      • 2.3 DS3231电路图&引脚关系
      • 2.4 DS3231寄存器
      • 2.5 RTCDS3231库
        • 2.5.1 Begin() —— 初始化
        • 2.5.2 LastError() —— 获取上次错误编码
        • 2.5.3 IsDateTimeValid() —— 判断时间是否有效
        • 2.5.4 GetIsRunning() —— 判断时钟是否正在运行
        • 2.5.5 SetIsRunning() —— 设置时钟是否运行
        • 2.5.6 SetDateTime() —— 设置日期时间
        • 2.5.7 GetDateTime() —— 获取日期时间
        • 2.5.8 Enable32kHzPin() —— 使能32kHz引脚输出
        • 2.5.10 SetSquareWavePin() —— 设置方波输出
        • 2.5.11 SetSquareWavePinClockFrequency() —— 设置方波时钟频率
        • 2.5.12 SetAlarmOne() —— 设置闹钟1
        • 2.5.13 GetAlarmOne() —— 获取闹钟1
        • 2.5.14 SetAlarmTwo() —— 设置闹钟2
        • 2.5.15 GetAlarmTwo() —— 获取闹钟2
        • 2.5.16 LatchAlarmsTriggeredFlags() —— 处理闹钟触发
      • 2.6 EepromAt24c32库
        • 2.6.1 Begin() —— 初始化
        • 2.6.2 LastError() —— 获取上次错误编码
        • 2.6.3 SetMemory() —— 存储数据
        • 2.6.4 GetMemory() —— 读取数据
      • 2.7 DS3231接线
    • 3.测试用例
      • 3.1 测试时间
      • 3.2 测试闹钟
      • 3.3 测试存储
    • 4.总结

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  A1M1DS3231AlarmOneControl_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  A2M2DS3231AlarmTwoControl_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 flaguint8_t status = getReg(DS3231_REG_STATUS);status &= ~_BV(DS3231_OSF); // clear the flagsetReg(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 onlyuint8_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 Weekuint8_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 ituint8_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 temperaturereturn 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" statecreg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW));creg |= _BV(DS3231_INTCN);  // set INTCN to disables SQWswitch (pinMode){case DS3231SquareWavePin_ModeNone:break;case DS3231SquareWavePin_ModeBatteryBackup:creg |= _BV(DS3231_BBSQW); // set battery backup flagcreg &= ~_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 0creg |= (freq & DS3231_RSMASK); // Set freq bitssetReg(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 onlyuint8_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 neededuint8_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 againDS3231AlarmFlag LatchAlarmsTriggeredFlags(){uint8_t sreg = getReg(DS3231_REG_STATUS);uint8_t alarmFlags = (sreg & DS3231_AIFMASK);sreg &= ~DS3231_AIFMASK; // clear the flagssetReg(DS3231_REG_STATUS, sreg);return (DS3231AlarmFlag)alarmFlags;}void ForceTemperatureCompensationUpdate(bool block){uint8_t creg = getReg(DS3231_REG_CONTROL);creg |= _BV(DS3231_CONV); // Write CONV bitsetReg(DS3231_REG_CONTROL, creg);while (block && (creg & _BV(DS3231_CONV)) != 0){// Block until CONV is 0creg = 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 flaguint8_t status = getReg(DS3231_REG_STATUS);status &= ~_BV(DS3231_OSF); // clear the flagsetReg(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 onlyuint8_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 Weekuint8_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 ituint8_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" statecreg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW));creg |= _BV(DS3231_INTCN);  // set INTCN to disables SQWswitch (pinMode){case DS3231SquareWavePin_ModeNone:break;case DS3231SquareWavePin_ModeBatteryBackup:creg |= _BV(DS3231_BBSQW); // set battery backup flagcreg &= ~_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, SCLRtc.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 meansSerial.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 missingSerial.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 issueRtc.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 stateRtc.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 meansSerial.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 disconnectedSerial.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 routineinteruptCount++;interuptFlag = true;
}void setup () 
{Serial.begin(57600);// set the interupt pin to input modepinMode(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, SCLRtc.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 meansSerial.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 matchRtcDateTime alarmTime = now + 88; // into the futureDS3231AlarmOne alarm1(alarmTime.Day(),alarmTime.Hour(),alarmTime.Minute(), alarmTime.Second(),DS3231AlarmOneControl_HoursMinutesSecondsMatch);Rtc.SetAlarmOne(alarm1);// Alarm 2 set to trigger at the top of the minuteDS3231AlarmTwo alarm2(0,0,0, DS3231AlarmTwoControl_OncePerMinute);Rtc.SetAlarmTwo(alarm2);// throw away any old alarm state before we ranRtc.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 meansSerial.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 firingfor (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 againDS3231AlarmFlag 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 boundaryvoid 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, SCLRtc.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 meansSerial.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 stateRtc.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 stringRtcEeprom.SetMemory(0, stringAddr); // store the string, nothing longer than 32 bytes due to paginguint8_t written = RtcEeprom.SetMemory(stringAddr, (const uint8_t*)data, sizeof(data) - 1); // remove the null terminator strings add// store the length of the stringRtcEeprom.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 meansSerial.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 disconnectedSerial.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 zerouint8_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 1uint8_t count = RtcEeprom.GetMemory(1);uint8_t buff[64];// get our data from the address with the given sizeuint8_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库,思想非常相似。


http://chatgpt.dhexx.cn/article/Z8G0oHse.shtml

相关文章

rtc时钟

一、设计效果 配置RTC时钟&#xff0c;初始化为2000年1月1日00:00:00&#xff1b;通过RTC时钟秒中断串口每秒打印一次当前时刻&#xff0c;并切换一次流水灯状态&#xff1b;配置闹铃为10:00:05&#xff0c;在闹铃中断中每秒切换一次蜂鸣器状态闹铃&#xff0c;直到按KEY1关闭…

RTC时钟实现实时日历

文章目录 1、RTC时钟简介1.2、初识RTC1.2、相关寄存器 2、创建项目23、完善代码4、总结 1、RTC时钟简介 1.2、初识RTC 1、简介&#xff1a; RTC—real time clock&#xff0c;实时时钟&#xff0c;主要包含日历、闹钟和自动唤醒这三部分的功能&#xff0c;其中的日历功能我们使…

STM32之RTC时钟

前言 了解实时时钟RTC的原理。STM32芯片自带RTC&#xff0c;因此不须像其他MCU需外接RTC模块。请编程实现STM32的日历读取、设置和输出。要求&#xff1a; 1&#xff09;读取RTC初始时间&#xff0c;验证是否为 1970年1月1日零分零秒&#xff1b; 2&#xff09;将RTC时间调整为…

【STM32】HAL库 STM32CubeMX教程十三---RTC时钟

前言&#xff1a; 本系列教程将 对应外设原理&#xff0c;HAL库与STM32CubeMX结合在一起讲解&#xff0c;使您可以更快速的学会各个模块的使用 所用工具&#xff1a; 1、芯片&#xff1a; STM32F407ZET6/ STM32F103ZET6 2、STM32CubeMx软件 3、IDE&#xff1a; MDK-Keil软…

【STM32】详解RTC实时时钟的概念和配置示例代码

一、什么是RTC RTC(Real-time Clock)&#xff1a;实时时钟&#xff0c;本质上是一个支持BCD编码的定时器/计数器。主电源断电后能够由电池供电&#xff0c;使其时钟跳转依然正常。 二、STM32F4芯片内的RTC功能 ①日历时钟&#xff08;时分秒、年月日、星期&#xff09; ②两个闹…

android小错误:Failure retrieving text 0x7f050001 in package

1.在启动android应用程序的过程中&#xff0c;logcat打印如下的错误&#xff1a; 报错的原因&#xff1a;资源的id没有找到。资源在R.java文件中&#xff0c;找到0x7f050001 对应的定义 R.java public static final int AppTheme0x7f050001; AppTheme这个文件在AndroidMan…

Windows7系统蓝屏-解决办法, 错误代码:0x0000007F

1.电脑突然蓝屏提示stop:0x0000007F&#xff0c;如下图所示&#xff1a; 2.故障排除 重启电脑&#xff0c;一直按F8&#xff0c;进入“安全模式”&#xff0c;如能正常进入系统&#xff0c;查看系统错误日志。针对系统记录的错误日志&#xff0c;解决问题。如下图&#xff0c;…

java.lang.IllegalArgumentException No view found for id 0x7

java.lang.IllegalArgumentException No view found for id 0x7 布局上找不到这个View&#xff0c;看下ERROR的地方&#xff0c;可以看到是因为Container找不到因此报错了。 从布局入手&#xff0c;参考了很多文章主要分为以下两种原因 1 Fragment 嵌套了 Fragment 例如&…

Fragment报java.lang.IllegalArgumentException:No view found for id 0x7f070250的错误

我用的是Fragment嵌套Fragment做的&#xff0c;放fragment切换快的时候程序就会崩溃&#xff0c;解决这个办法就是在View生成前先将FragemnentManger创建好&#xff0c;就不会报错了

Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x7f解析

先看日志报错这个错误纠结我两天了&#xff0c;真的要崩溃了~_~ 关于android.content.res.Resources$NotFoundException: Resource ID的问题&#xff0c;那行代码是onCreate加载.xml布局&#xff0c;是加载的R.layout.xxxx&#xff0c;不是R.id.xxxx&#xff0c;布局文件存在&a…

关于No view found for id 0x7f080135 (com.xxx.xxx:id/viewpager) for fragment PopupWindow

最近在开发项目中&#xff0c;遇到了一个问题。PopupWindow中无法嵌套viewpagerFragment&#xff0c;现在进行总结一下。 问题描述&#xff1a;在Fragment中弹出一个PopupWindow,PopupWindow中用viewpager加载多个Fragment时出现上面问题。 网上搜的都是那些什么没有设置id之…

流水灯设计

流水灯设计 目的与要求 通过采用单片机控制8个LED发光二极管顺序点亮的流水灯系统设计与制作&#xff0c;让读者了解C语言的数据类型、常量与变量、运算符和表达式等基本概念及使用方法。 设计要求&#xff1a;首先点亮连接到P1.7引脚的发光二极管&#xff0c;延时一定时间后…

IT管理人才必备的十大能力

公众号回复&#xff1a;干货&#xff0c;领取价值58元/套IT管理体系文档 公众号回复&#xff1a;ITIL教材&#xff0c;领取最新ITIL4中文教材 正文 作为IT技术人员&#xff0c;相信没有一个人愿意永远在底层编写程序或做简单的系统维护。经过一段时间的技术和经验的积累&#x…

他,连续 3 年担任新星计划导师,这次的内容有点特别

一、新星计划 新星计划是一个发掘潜力新人、培养优质博主为目标的创作活动&#xff0c;通过为期两周的时间&#xff0c;明白为什么要写博客&#xff0c;写作的意义是什么&#xff1f;制定学习计划&#xff0c;完善Java知识体系。 二、学习计划 创作打卡阶段第1周&#xff08…

Python 彻底甩掉 Java,位居 48 种编程语言之首!

Python 彻底甩掉 Java&#xff0c;位居 48 种编程语言之首&#xff01; 昨日&#xff0c;IEEE Spectrum 杂志发布了一年一度的编程语言排行榜&#xff0c;这个排行榜已经连续发布了五年。对于每位开发者而言&#xff0c;想要衡量编程语言流行度则需要依赖相对流行的网站统计数据…

java并发问题概述

转自 https://www.jb51.net/article/131411.htm java并发问题概述 转载 更新时间&#xff1a;2017年12月25日 09:28:54 作者&#xff1a;人圭先生 我要评论 这篇文章主要介绍了java并发问题概述&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。 1什么是…

【Java基础系列教程】第一章 编程入门

一、计算机概述 1.1 计算机简介 计算机&#xff08;computer&#xff09;俗称电脑&#xff0c;是现代一种用于高速计算的电子计算机器&#xff0c;可以进行数值计算&#xff0c;又可以进行逻辑计算&#xff0c;还具有存储记忆功能。是能够按照程序运行&#xff0c;自动、高速处…

关于程序员这14条经典定律,我全中~

定律1:最难定位的问题要么是最疑难的问题,要么是最低级的问题,这两种问题都有一个共同特征,就是让你意想不到。 举一个例子,一次代码编译不过,报函数没有定义,开始怀疑是类没有“;”结束符,然后怀疑有没有匹配的“{”,折腾了好久,最后才发现是开头的“#ifndef”定义…

系统性能优化的十大策略(强烈推荐,建议收藏)

点击关注公众号&#xff0c;实用技术文章及时了解 上篇 提升系统性能&#xff0c;榨干计算机资源是程序员的极致追求&#xff0c;今天跟大家聊聊性能优化。分为上中下三篇&#xff0c;由浅及深的写了关于性能优化的方方面面&#xff0c;并不仅仅局限于代码层面&#xff0c;希望…

JAVA工程师的十大借口,你知道哪些?

他们是近几十年来出现的新物种。他们的着装可能经常会遭到别人的吐槽&#xff0c;他们的玩笑可能别人也经常get不到笑点&#xff0c;他们心照不宣的执着让外人觉得莫名其妙。但同时&#xff0c;他们也拥有无与伦比的耐力&#xff0c;超越时代的智商&#xff0c;和横穿社会的自虐…