接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块。没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里不再重复。
常用的DS1302需要使用外置晶振,且没有温度补偿,误差较大。这就促使了更高精度的时钟芯片 —— DS3231。
DS3231内置晶振且有内部温度补偿,误差可做到1分钟每年。说白了,精度更高。
跟DS1302一样,对于DS3231的操作就是操作对应的寄存器,其寄存器对应关系如下:
老规矩,先看看源码,博主在源码中加入了部分代码注释:
#ifndef __RTCDS3231_H__
#define __RTCDS3231_H__
#include
#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 =
// 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__
函数说明:
/**
* 初始化,会把三个引脚设置为输入状态
*/
void Begin()
函数说明:
/**
* 获取上次错误编码
* @return 返回错误编码
*/
uint8_t LastError()
注意:
函数说明:
/**
* 判断时间是否有效
* @return false 通常意味着电池没电或日期和时间从未设置
* true 意味时间有效
*/
bool IsDateTimeValid()
函数说明:
/**
* 判断时钟是否正在运行
* @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));
}
函数说明:
/**
* 设置时钟是否运行
* @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 闹钟1、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 // must be included here so that Arduino library object file references work
#include
SoftwareWire myWire(SDA, SCL);
RtcDS3231 Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include // must be included here so that Arduino library object file references work
#include
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 // must be included here so that Arduino library object file references work
#include
SoftwareWire myWire(SDA, SCL);
RtcDS3231 Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include // must be included here so that Arduino library object file references work
#include
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 // must be included here so that Arduino library object file references work
#include
#include
SoftwareWire myWire(SDA, SCL);
RtcDS1307 Rtc(myWire);
/* for software wire use above */
/* for normal hardware wire use below */
#include // must be included here so that Arduino library object file references work
#include
#include
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 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库,思想非常相似。