接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇咱们重点讲解DS3231时钟模块。没有看过上一篇的同窗,麻烦先去阅读一下,由于不少理论基础已经在上一篇作了详细讲解,这里再也不重复。编程
经常使用的DS1302须要使用外置晶振,且没有温度补偿,偏差较大。这就促使了更高精度的时钟芯片 —— DS3231。
DS3231内置晶振且有内部温度补偿,偏差可作到1分钟每一年。说白了,精度更高。less
跟DS1302同样,对于DS3231的操做就是操做对应的寄存器,其寄存器对应关系以下:ide
老规矩,先看看源码,博主在源码中加入了部分代码注释:函数
#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__
函数说明:oop
/** * 初始化,会把三个引脚设置为输入状态 */ void Begin()
函数说明:测试
/** * 获取上次错误编码 * @return 返回错误编码 */ uint8_t LastError()
注意:ui
函数说明:this
/** * 判断时间是否有效 * @return false 一般意味着电池没电或日期和时间从未设置 * true 意味时间有效 */ bool IsDateTimeValid()
函数说明:编码
/** * 判断时钟是否正在运行 * @return bool * true 时钟运行 * false 时钟停振,进入低功耗态 */ bool GetIsRunning()
源码说明:3d
/** * 判断时钟是否正在运行 * @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)
源码说明:
/** * 设置时钟是否运行 * @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)
源码说明:
/** * 设置日期时间 * @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()
源码说明:
/** * 获取日期时间 * @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); }
函数说明:
/** * 使能32kHz引脚输出 * @param enable true 使能 * false 禁止 */ void Enable32kHzPin(bool enable)
函数说明:
/** * 设置方波输出 * @param DS3231SquareWavePinMode 方波引脚模式 */ void SetSquareWavePin(DS3231SquareWavePinMode pinMode)
DS3231SquareWavePinMode 参数说明:
源码说明:
/** * 设置方波输出 */ 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); }
函数说明:
/** * 设置方波时钟频率 * @param DS3231SquareWaveClock 方波时钟频率 */ void SetSquareWavePinClockFrequency(DS3231SquareWaveClock freq)
DS3231SquareWaveClock 参数说明:
函数说明:
/** * 设置闹钟1 * @param DS3231AlarmOne 闹钟1 */ void SetAlarmOne(const DS3231AlarmOne& alarm)
注意点:
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)
函数说明:
/** * 获取闹钟1 * @return DS3231AlarmOne 闹钟1 */ DS3231AlarmOne GetAlarmOne()
函数说明:
/** * 设置闹钟2 * @param DS3231AlarmTwo 闹钟2 */ void SetAlarmTwo(const DS3231AlarmTwo& alarm)
注意点:
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 * @return DS3231AlarmTwo 闹钟2 */ DS3231AlarmTwo GetAlarmTwo()
函数说明:
/** * 处理闹钟触发 * @return DS3231AlarmFlag * --- DS3231AlarmFlag_Alarm1 闹钟1触发 * --- DS3231AlarmFlag_Alarm2 闹钟2触发 * --- DS3231AlarmFlag_AlarmBoth 闹钟一、2触发 */ DS3231AlarmFlag LatchAlarmsTriggeredFlags()
注意点:
前面说到了,DS3231时钟模块集成了AT24c32 eeprom存储芯片,若是咱们须要用到存储数据功能,就得引入 EepromAt24c32库。那么,咱们来看看该库有什么方法。
函数说明:
/** * 初始化引脚 */ void Begin()
函数说明:
/** * 获取上次错误编码 * @return 返回错误编码 */ uint8_t LastError()
注意:
函数说明:
/*** * 写入数据 * @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)
函数说明:
/*** * 读取数据 * @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)
DS3231采用I2C总线方式,SCLK、SDA。
测试用例分为三个:
实验内容
实验器材
引脚链接
模块引脚 | Mega2560引脚 |
---|---|
VCC | VCC5V |
GND | GND |
SDA | SDA(20) |
SCL | SCL(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); }
实验结果:
实验内容
实验器材
引脚链接
模块引脚 | Mega2560引脚 |
---|---|
VCC | VCC5V |
GND | GND |
SDA | SDA(20) |
SCL | SCL(21) |
SQW | 19 |
实验代码
// 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); }
实验结果:
实验内容
实验器材
引脚链接
模块引脚 | Mega2560引脚 |
---|---|
VCC | VCC5V |
GND | GND |
SDA | SDA(20) |
SCL | SCL(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); }
实验结果:
本篇主要针对DS3231进行讲解RTC库,相对比较简单,基本上看完例子都能熟练使用,读者能够继续自行研究DS3234库,思想很是类似。