mirror of
https://github.com/adafruit/RTClib.git
synced 2026-07-28 04:05:31 +00:00
Since we use Unix text format, all lines, including the last one, should be terminated by ASCII LF.
293 lines
12 KiB
C++
293 lines
12 KiB
C++
#include "RTClib.h"
|
|
|
|
#define PCF8523_ADDRESS 0x68 ///< I2C address for PCF8523
|
|
#define PCF8523_CLKOUTCONTROL 0x0F ///< Timer and CLKOUT control register
|
|
#define PCF8523_CONTROL_1 0x00 ///< Control and status register 1
|
|
#define PCF8523_CONTROL_2 0x01 ///< Control and status register 2
|
|
#define PCF8523_CONTROL_3 0x02 ///< Control and status register 3
|
|
#define PCF8523_TIMER_B_FRCTL 0x12 ///< Timer B source clock frequency control
|
|
#define PCF8523_TIMER_B_VALUE 0x13 ///< Timer B value (number clock periods)
|
|
#define PCF8523_OFFSET 0x0E ///< Offset register
|
|
#define PCF8523_STATUSREG 0x03 ///< Status register
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Start I2C for the PCF8523 and test succesful connection
|
|
@param wireInstance pointer to the I2C bus
|
|
@return True if Wire can find PCF8523 or false otherwise.
|
|
*/
|
|
/**************************************************************************/
|
|
boolean RTC_PCF8523::begin(TwoWire *wireInstance) {
|
|
if (i2c_dev)
|
|
delete i2c_dev;
|
|
i2c_dev = new Adafruit_I2CDevice(PCF8523_ADDRESS, wireInstance);
|
|
if (!i2c_dev->begin())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Check the status register Oscillator Stop flag to see if the PCF8523
|
|
stopped due to power loss
|
|
@details When battery or external power is first applied, the PCF8523's
|
|
crystal oscillator takes up to 2s to stabilize. During this time adjust()
|
|
cannot clear the 'OS' flag. See datasheet OS flag section for details.
|
|
@return True if the bit is set (oscillator is or has stopped) and false only
|
|
after the bit is cleared, for instance with adjust()
|
|
*/
|
|
/**************************************************************************/
|
|
boolean RTC_PCF8523::lostPower(void) {
|
|
return read_register(PCF8523_STATUSREG) >> 7;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Check control register 3 to see if we've run adjust() yet (setting
|
|
the date/time and battery switchover mode)
|
|
@return True if the PCF8523 has been set up, false if not
|
|
*/
|
|
/**************************************************************************/
|
|
boolean RTC_PCF8523::initialized(void) {
|
|
return (read_register(PCF8523_CONTROL_3) & 0xE0) != 0xE0;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Set the date and time, set battery switchover mode
|
|
@param dt DateTime to set
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::adjust(const DateTime &dt) {
|
|
uint8_t buffer[8] = {3, // start at location 3
|
|
bin2bcd(dt.second()),
|
|
bin2bcd(dt.minute()),
|
|
bin2bcd(dt.hour()),
|
|
bin2bcd(dt.day()),
|
|
bin2bcd(0), // skip weekdays
|
|
bin2bcd(dt.month()),
|
|
bin2bcd(dt.year() - 2000U)};
|
|
i2c_dev->write(buffer, 8);
|
|
|
|
// set to battery switchover mode
|
|
write_register(PCF8523_CONTROL_3, 0x00);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Get the current date/time
|
|
@return DateTime object containing the current date/time
|
|
*/
|
|
/**************************************************************************/
|
|
DateTime RTC_PCF8523::now() {
|
|
uint8_t buffer[7];
|
|
buffer[0] = 3;
|
|
i2c_dev->write_then_read(buffer, 1, buffer, 7);
|
|
|
|
return DateTime(bcd2bin(buffer[6]) + 2000U, bcd2bin(buffer[5]),
|
|
bcd2bin(buffer[3]), bcd2bin(buffer[2]), bcd2bin(buffer[1]),
|
|
bcd2bin(buffer[0] & 0x7F));
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Resets the STOP bit in register Control_1
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::start(void) {
|
|
uint8_t ctlreg = read_register(PCF8523_CONTROL_1);
|
|
if (ctlreg & (1 << 5))
|
|
write_register(PCF8523_CONTROL_1, ctlreg & ~(1 << 5));
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Sets the STOP bit in register Control_1
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::stop(void) {
|
|
write_register(PCF8523_CONTROL_1,
|
|
read_register(PCF8523_CONTROL_1) | (1 << 5));
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Is the PCF8523 running? Check the STOP bit in register Control_1
|
|
@return 1 if the RTC is running, 0 if not
|
|
*/
|
|
/**************************************************************************/
|
|
uint8_t RTC_PCF8523::isrunning() {
|
|
return !((read_register(PCF8523_CONTROL_1) >> 5) & 1);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Read the mode of the INT/SQW pin on the PCF8523
|
|
@return SQW pin mode as a #Pcf8523SqwPinMode enum
|
|
*/
|
|
/**************************************************************************/
|
|
Pcf8523SqwPinMode RTC_PCF8523::readSqwPinMode() {
|
|
int mode = read_register(PCF8523_CLKOUTCONTROL);
|
|
mode >>= 3;
|
|
mode &= 0x7;
|
|
return static_cast<Pcf8523SqwPinMode>(mode);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Set the INT/SQW pin mode on the PCF8523
|
|
@param mode The mode to set, see the #Pcf8523SqwPinMode enum for options
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::writeSqwPinMode(Pcf8523SqwPinMode mode) {
|
|
write_register(PCF8523_CLKOUTCONTROL, mode << 3);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Enable the Second Timer (1Hz) Interrupt on the PCF8523.
|
|
@details The INT/SQW pin will pull low for a brief pulse once per second.
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::enableSecondTimer() {
|
|
uint8_t ctlreg = read_register(PCF8523_CONTROL_1);
|
|
uint8_t clkreg = read_register(PCF8523_CLKOUTCONTROL);
|
|
// TAM pulse int. mode (shared with Timer A), CLKOUT (aka SQW) disabled
|
|
write_register(PCF8523_CLKOUTCONTROL, clkreg | 0xB8);
|
|
// SIE Second timer int. enable
|
|
write_register(PCF8523_CONTROL_1, ctlreg | (1 << 2));
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Disable the Second Timer (1Hz) Interrupt on the PCF8523.
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::disableSecondTimer() {
|
|
write_register(PCF8523_CONTROL_1,
|
|
read_register(PCF8523_CONTROL_1) & ~(1 << 2));
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Enable the Countdown Timer Interrupt on the PCF8523.
|
|
@details The INT/SQW pin will be pulled low at the end of a specified
|
|
countdown period ranging from 244 microseconds to 10.625 days.
|
|
Uses PCF8523 Timer B. Any existing CLKOUT square wave, configured with
|
|
writeSqwPinMode(), will halt. The interrupt low pulse width is adjustable
|
|
from 3/64ths (default) to 14/64ths of a second.
|
|
@param clkFreq One of the PCF8523's Timer Source Clock Frequencies.
|
|
See the #PCF8523TimerClockFreq enum for options and associated time ranges.
|
|
@param numPeriods The number of clkFreq periods (1-255) to count down.
|
|
@param lowPulseWidth Optional: the length of time for the interrupt pin
|
|
low pulse. See the #PCF8523TimerIntPulse enum for options.
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::enableCountdownTimer(PCF8523TimerClockFreq clkFreq,
|
|
uint8_t numPeriods,
|
|
uint8_t lowPulseWidth) {
|
|
// Datasheet cautions against updating countdown value while it's running,
|
|
// so disabling allows repeated calls with new values to set new countdowns
|
|
disableCountdownTimer();
|
|
|
|
// Leave compatible settings intact
|
|
uint8_t ctlreg = read_register(PCF8523_CONTROL_2);
|
|
uint8_t clkreg = read_register(PCF8523_CLKOUTCONTROL);
|
|
|
|
// CTBIE Countdown Timer B Interrupt Enabled
|
|
write_register(PCF8523_CONTROL_2, ctlreg |= 0x01);
|
|
|
|
// Timer B source clock frequency, optionally int. low pulse width
|
|
write_register(PCF8523_TIMER_B_FRCTL, lowPulseWidth << 4 | clkFreq);
|
|
|
|
// Timer B value (number of source clock periods)
|
|
write_register(PCF8523_TIMER_B_VALUE, numPeriods);
|
|
|
|
// TBM Timer B pulse int. mode, CLKOUT (aka SQW) disabled, TBC start Timer B
|
|
write_register(PCF8523_CLKOUTCONTROL, clkreg | 0x79);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@overload
|
|
@brief Enable Countdown Timer using default interrupt low pulse width.
|
|
@param clkFreq One of the PCF8523's Timer Source Clock Frequencies.
|
|
See the #PCF8523TimerClockFreq enum for options and associated time ranges.
|
|
@param numPeriods The number of clkFreq periods (1-255) to count down.
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::enableCountdownTimer(PCF8523TimerClockFreq clkFreq,
|
|
uint8_t numPeriods) {
|
|
enableCountdownTimer(clkFreq, numPeriods, 0);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Disable the Countdown Timer Interrupt on the PCF8523.
|
|
@details For simplicity, this function strictly disables Timer B by setting
|
|
TBC to 0. The datasheet describes TBC as the Timer B on/off switch.
|
|
Timer B is the only countdown timer implemented at this time.
|
|
The following flags have no effect while TBC is off, they are *not* cleared:
|
|
- TBM: Timer B will still be set to pulsed mode.
|
|
- CTBIE: Timer B interrupt would be triggered if TBC were on.
|
|
- CTBF: Timer B flag indicates that interrupt was triggered. Though
|
|
typically used for non-pulsed mode, user may wish to query this later.
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::disableCountdownTimer() {
|
|
// TBC disable to stop Timer B clock
|
|
write_register(PCF8523_CLKOUTCONTROL,
|
|
~1 & read_register(PCF8523_CLKOUTCONTROL));
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Stop all timers, clear their flags and settings on the PCF8523.
|
|
@details This includes the Countdown Timer, Second Timer, and any CLKOUT
|
|
square wave configured with writeSqwPinMode().
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::deconfigureAllTimers() {
|
|
disableSecondTimer(); // Surgically clears CONTROL_1
|
|
write_register(PCF8523_CONTROL_2, 0);
|
|
write_register(PCF8523_CLKOUTCONTROL, 0);
|
|
write_register(PCF8523_TIMER_B_FRCTL, 0);
|
|
write_register(PCF8523_TIMER_B_VALUE, 0);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Compensate the drift of the RTC.
|
|
@details This method sets the "offset" register of the PCF8523,
|
|
which can be used to correct a previously measured drift rate.
|
|
Two correction modes are available:
|
|
|
|
- **PCF8523\_TwoHours**: Clock adjustments are performed on
|
|
`offset` consecutive minutes every two hours. This is the most
|
|
energy-efficient mode.
|
|
|
|
- **PCF8523\_OneMinute**: Clock adjustments are performed on
|
|
`offset` consecutive seconds every minute. Extra adjustments are
|
|
performed on the last second of the minute is `abs(offset)>60`.
|
|
|
|
The `offset` parameter sets the correction amount in units of
|
|
roughly 4 ppm. The exact unit depends on the selected mode:
|
|
|
|
| mode | offset unit |
|
|
|---------------------|----------------------------------------|
|
|
| `PCF8523_TwoHours` | 4.340 ppm = 0.375 s/day = 2.625 s/week |
|
|
| `PCF8523_OneMinute` | 4.069 ppm = 0.352 s/day = 2.461 s/week |
|
|
|
|
See the accompanying sketch pcf8523.ino for an example on how to
|
|
use this method.
|
|
|
|
@param mode Correction mode, either `PCF8523_TwoHours` or
|
|
`PCF8523_OneMinute`.
|
|
@param offset Correction amount, from -64 to +63. A positive offset
|
|
makes the clock slower.
|
|
*/
|
|
/**************************************************************************/
|
|
void RTC_PCF8523::calibrate(Pcf8523OffsetMode mode, int8_t offset) {
|
|
write_register(PCF8523_OFFSET, ((uint8_t)offset & 0x7F) | mode);
|
|
}
|