From 77a773337ebc30254ce235811990aee29029eec8 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:19:23 +0100 Subject: [PATCH 01/12] Add files via upload Method to calibrate PCF8523 RTC including method to read the offset register- --- examples/pcf8523_calibrate/README.txt | 40 ++++++ .../pcf8523_calibrate/pcf8523_calibrate.ino | 125 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 examples/pcf8523_calibrate/README.txt create mode 100644 examples/pcf8523_calibrate/pcf8523_calibrate.ino diff --git a/examples/pcf8523_calibrate/README.txt b/examples/pcf8523_calibrate/README.txt new file mode 100644 index 00000000..148dadbd --- /dev/null +++ b/examples/pcf8523_calibrate/README.txt @@ -0,0 +1,40 @@ + The PCF8523 can be calibrated for: + - Aging adjustment + - Temperature compensation + - Accuracy tuning + The offset mode to use, once every two hours or once every minute. + The offset Offset value from -64 to +63. A positive offset makes the clock slower. + See the Application Note for calculation of offset values. + https://www.nxp.com/docs/en/application-note/AN11247.pdf + The deviation in parts per million can be calculated over a period of observation. Both the drift (which can be negative) + and the observation period must be in seconds. For accuracy the variation should be observed over about 1 week. + Note: any previous calibration should cancelled prior to any new observation period. + Recommendation: + Syncronise host PC time. + run this sketch cancelling any previous calibration, + record the output including timestamp, + after several days again syncronise host PC time, + run sketch again record the output including timestamp, + calculate period of observation in seconds, and drift in seconds. + Run sketch with the calculated figures and uncomment rtc.calibrate line as required. + Example - RTC gaining 43 seconds in 1 week + float drift = 43; // seconds plus or minus over oservation period - set to 0 to cancel previous calibration. + float period_sec = (7 * 86400); // total obsevation period in seconds (86400 = seconds in 1 day: 7 days = (7 * 86400) seconds ) + float deviation_ppm = (drift / period_sec * 1000000); // deviation in parts per million (?s) + float drift_unit = 4.34; // use with offset mode PCF8523_TwoHours + // float drift_unit = 4.069; //For corrections every min the drift_unit is 4.069 ppm (use with offset mode PCF8523_OneMinute) + int8_t offset = round(deviation_ppm / drift_unit); + rtc.calibrate(PCF8523_TwoHours, offset); // Un-comment to perform calibration once drift (seconds) and observation period (seconds) are correct + // rtc.calibrate(PCF8523_OneMinute, offset); // // Un-comment to perform calibration with offset mode PCF8523_OneMinute + // rtc.calibrate(PCF8523_TwoHours, 0); // Un-comment to cancel previous calibration + +In order to provide a method of reading the offset register, which may contain an previous calibration +two methods are provided; 1. rtc.readOffsetReg(), or 2. rtc.getOffsetMode() and rtc.getOffset() +See the example sketch: pcf8523_calibrate.ino + +Hint: + Once the calibration Offset mode and Offset are known a line can be entered in the setup of the operating project sketch + to re-establish the offset register after a battery replacement or clock reset. Note that your sketch will still require a method + to insert the actual date and time. + In the case of the above sample the line to insert in setup() would be: + rtc.calibrate(PCF8523_TwoHours, 16); // re-insert previously calculated calibration after clock reset. \ No newline at end of file diff --git a/examples/pcf8523_calibrate/pcf8523_calibrate.ino b/examples/pcf8523_calibrate/pcf8523_calibrate.ino new file mode 100644 index 00000000..ff4b1a97 --- /dev/null +++ b/examples/pcf8523_calibrate/pcf8523_calibrate.ino @@ -0,0 +1,125 @@ +// Date and time functions using a PCF8523 RTC connected via I2C and Wire lib +#include "RTClib.h" + +RTC_PCF8523 rtc; + +char daysOfTheWeek[7][12] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + +void setup() { + Serial.begin(57600); + delay(500); + while (!Serial) + ; // wait for serial port to connect. Needed for native USB + + if (!rtc.begin()) { + Serial.println("Couldn't find RTC"); + Serial.flush(); + while (1) delay(10); + } + + if (!rtc.initialized() || rtc.lostPower()) { + Serial.println("RTC is NOT initialized, let's set the time!"); + // When time needs to be set on a new device, or after a power loss, the + // following line sets the RTC to the date & time this sketch was compiled + rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + // This line sets the RTC with an explicit date & time, for example to set + // January 21, 2014 at 3am you would call: + // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); + // + // Note: allow 2 seconds after inserting battery or applying external power + // without battery before calling adjust(). This gives the PCF8523's + // crystal oscillator time to stabilize. If you call adjust() very quickly + // after the RTC is powered, lostPower() may still return true. + } + + // When time needs to be re-set on a previously configured device, the + // following line sets the RTC to the date & time this sketch was compiled + // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + // This line sets the RTC with an explicit date & time, for example to set + // January 21, 2014 at 3am you would call: + // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); + + // When the RTC was stopped and stays connected to the battery, it has + // to be restarted by clearing the STOP bit. Let's do this to ensure + // the RTC is running. + rtc.start(); + + // The PCF8523 can be calibrated for: + // - Aging adjustment + // - Temperature compensation + // - Accuracy tuning + // The offset mode to use, once every two hours or once every minute. + // The offset Offset value from -64 to +63. See the Application Note for calculation of offset values. + // https://www.nxp.com/docs/en/application-note/AN11247.pdf + // The deviation in parts per million can be calculated over a period of observation. Both the drift (which can be negative) + // and the observation period must be in seconds. For accuracy the variation should be observed over about 1 week. + // Note: any previous calibration should cancelled prior to any new observation period. + // Recommendation: Syncronise host PC time. + // run this sketch cancelling any previous calibration, + // record the output including timestamp, + // run sketch again after several days, + // calculate period of observation in seconds, and drift in seconds. + // Run sketch with the calculated figures and uncomment rtc.calibrate line as required. + // Example - RTC gaining 43 seconds in 1 week + float drift = 43; // seconds plus or minus over oservation period - set to 0 to cancel previous calibration. + float period_sec = (7 * 86400); // total obsevation period in seconds (86400 = seconds in 1 day: 7 days = (7 * 86400) seconds ) + float deviation_ppm = (drift / period_sec * 1000000); // deviation in parts per million (μs) + float drift_unit = 4.34; // use with offset mode PCF8523_TwoHours + // float drift_unit = 4.069; //For corrections every min the drift_unit is 4.069 ppm (use with offset mode PCF8523_OneMinute) + int8_t offset = round(deviation_ppm / drift_unit); + // rtc.calibrate(PCF8523_TwoHours, offset); // Un-comment to perform calibration once drift (seconds) and observation period (seconds) are correct + // rtc.calibrate(PCF8523_OneMinute, offset); // // Un-comment to perform calibration with offset mode PCF8523_OneMinute + // rtc.calibrate(PCF8523_TwoHours, 0); // Un-comment to cancel previous calibration + + Serial.println(); + Serial.print("Calculated Offset for calibration is: "); + Serial.println(offset); // Print to control calculated offset + + // read offset register ******************************* + Serial.println("Read RTC PCF8523 Offset Register"); // Print to control offset + + // Method 1 **************************** + int8_t OffsetReg = rtc.readOffsetReg(); + Serial.print("Offset mode is: "); + if bitRead (OffsetReg, 7) { + Serial.println("PCF8523_OneMinute"); + } else { + Serial.println("PCF8523_TwoHours "); + } + offset = OffsetReg; + bitWrite(offset, 7, bitRead(OffsetReg, 6)); + Serial.print("Offset is: "); + Serial.println(offset); // Print to control offset + + // Method 2 **************************** + String OffsetMode = String(rtc.getOffsetMode()); + Serial.print("Offset mode is: "); + Serial.println(OffsetMode); + + offset = rtc.getOffset(); + Serial.print("Offset is: "); + Serial.println(offset); // Print to control offset + // End read offset register ******************************* + + DateTime now = rtc.now(); + + Serial.print(now.year(), DEC); + Serial.print('/'); + Serial.print(now.month(), DEC); + Serial.print('/'); + Serial.print(now.day(), DEC); + Serial.print(" ("); + Serial.print(daysOfTheWeek[now.dayOfTheWeek()]); + Serial.print(") "); + Serial.print(now.hour(), DEC); + Serial.print(':'); + Serial.print(now.minute(), DEC); + Serial.print(':'); + Serial.print(now.second(), DEC); + Serial.println(); +} + +void loop() { + + // do nothing +} From 79f96db86308c403171e778666133ca96d09ac1b Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:27:04 +0100 Subject: [PATCH 02/12] Add files via upload Added method to read the offset register --- src/RTC_PCF8523.cpp | 626 +++++++++++++++++++++++--------------------- src/RTClib.h | 4 + 2 files changed, 338 insertions(+), 292 deletions(-) diff --git a/src/RTC_PCF8523.cpp b/src/RTC_PCF8523.cpp index 17debef7..d2af3f93 100644 --- a/src/RTC_PCF8523.cpp +++ b/src/RTC_PCF8523.cpp @@ -1,292 +1,334 @@ -#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. -*/ -/**************************************************************************/ -bool 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() -*/ -/**************************************************************************/ -bool 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 -*/ -/**************************************************************************/ -bool 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(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); -} +#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. +*/ +/**************************************************************************/ +bool 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() +*/ +/**************************************************************************/ +bool 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 +*/ +/**************************************************************************/ +bool 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(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); +} + +/**************************************************************************/ +/*! + @brief read the offset register +*/ +/**************************************************************************/ + +int8_t RTC_PCF8523::readOffsetReg() { + int8_t OffsetReg = read_register(PCF8523_OFFSET); + return OffsetReg; +} + +/**************************************************************************/ +/*! + @brief read the offset register and return OffsetMode +*/ +/**************************************************************************/ +String RTC_PCF8523::getOffsetMode() { + String OffsetMode; +// int8_t OffsetReg = readOffsetReg(); + if bitRead (readOffsetReg(), 7) { + OffsetMode = String("PCF8523_OneMinute"); + } else { + OffsetMode = String("PCF8523_TwoHours "); + } + return OffsetMode; +} + +/**************************************************************************/ +/*! + @brief read the offset register and return offset + The `offset` parameter is held in bits 0 to 6 as a signed 7bit interger + bit 6 needs to be copied to bit 7 to convert to a signed 8bit interger + +*/ +/**************************************************************************/ + +int8_t RTC_PCF8523::getOffset() { + int8_t offset = readOffsetReg(); + bitWrite(offset, 7, bitRead(offset, 6)); + return offset; +} diff --git a/src/RTClib.h b/src/RTClib.h index 879bb3fa..464e7561 100644 --- a/src/RTClib.h +++ b/src/RTClib.h @@ -423,6 +423,10 @@ class RTC_PCF8523 : RTC_I2C { void disableCountdownTimer(void); void deconfigureAllTimers(void); void calibrate(Pcf8523OffsetMode mode, int8_t offset); + int8_t readOffsetReg(); + String getOffsetMode(); + int8_t getOffset(); + }; /**************************************************************************/ From 82d1f2d4dd550c95cadcab4a944d07edd2d90801 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Sun, 14 Jan 2024 12:06:57 +0100 Subject: [PATCH 03/12] Delete examples/pcf8523_calibrate/README.txt Content moved to comments in example sketch. --- examples/pcf8523_calibrate/README.txt | 40 --------------------------- 1 file changed, 40 deletions(-) delete mode 100644 examples/pcf8523_calibrate/README.txt diff --git a/examples/pcf8523_calibrate/README.txt b/examples/pcf8523_calibrate/README.txt deleted file mode 100644 index 148dadbd..00000000 --- a/examples/pcf8523_calibrate/README.txt +++ /dev/null @@ -1,40 +0,0 @@ - The PCF8523 can be calibrated for: - - Aging adjustment - - Temperature compensation - - Accuracy tuning - The offset mode to use, once every two hours or once every minute. - The offset Offset value from -64 to +63. A positive offset makes the clock slower. - See the Application Note for calculation of offset values. - https://www.nxp.com/docs/en/application-note/AN11247.pdf - The deviation in parts per million can be calculated over a period of observation. Both the drift (which can be negative) - and the observation period must be in seconds. For accuracy the variation should be observed over about 1 week. - Note: any previous calibration should cancelled prior to any new observation period. - Recommendation: - Syncronise host PC time. - run this sketch cancelling any previous calibration, - record the output including timestamp, - after several days again syncronise host PC time, - run sketch again record the output including timestamp, - calculate period of observation in seconds, and drift in seconds. - Run sketch with the calculated figures and uncomment rtc.calibrate line as required. - Example - RTC gaining 43 seconds in 1 week - float drift = 43; // seconds plus or minus over oservation period - set to 0 to cancel previous calibration. - float period_sec = (7 * 86400); // total obsevation period in seconds (86400 = seconds in 1 day: 7 days = (7 * 86400) seconds ) - float deviation_ppm = (drift / period_sec * 1000000); // deviation in parts per million (?s) - float drift_unit = 4.34; // use with offset mode PCF8523_TwoHours - // float drift_unit = 4.069; //For corrections every min the drift_unit is 4.069 ppm (use with offset mode PCF8523_OneMinute) - int8_t offset = round(deviation_ppm / drift_unit); - rtc.calibrate(PCF8523_TwoHours, offset); // Un-comment to perform calibration once drift (seconds) and observation period (seconds) are correct - // rtc.calibrate(PCF8523_OneMinute, offset); // // Un-comment to perform calibration with offset mode PCF8523_OneMinute - // rtc.calibrate(PCF8523_TwoHours, 0); // Un-comment to cancel previous calibration - -In order to provide a method of reading the offset register, which may contain an previous calibration -two methods are provided; 1. rtc.readOffsetReg(), or 2. rtc.getOffsetMode() and rtc.getOffset() -See the example sketch: pcf8523_calibrate.ino - -Hint: - Once the calibration Offset mode and Offset are known a line can be entered in the setup of the operating project sketch - to re-establish the offset register after a battery replacement or clock reset. Note that your sketch will still require a method - to insert the actual date and time. - In the case of the above sample the line to insert in setup() would be: - rtc.calibrate(PCF8523_TwoHours, 16); // re-insert previously calculated calibration after clock reset. \ No newline at end of file From 7ec46456e257508f95dff05f8382206cde9d41b9 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Sun, 14 Jan 2024 12:25:49 +0100 Subject: [PATCH 04/12] Update pcf8523_calibrate.ino Updated comments --- examples/pcf8523_calibrate/pcf8523_calibrate.ino | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/pcf8523_calibrate/pcf8523_calibrate.ino b/examples/pcf8523_calibrate/pcf8523_calibrate.ino index ff4b1a97..52f43a23 100644 --- a/examples/pcf8523_calibrate/pcf8523_calibrate.ino +++ b/examples/pcf8523_calibrate/pcf8523_calibrate.ino @@ -75,6 +75,15 @@ void setup() { Serial.print("Calculated Offset for calibration is: "); Serial.println(offset); // Print to control calculated offset + // In order to provide a method of reading the offset register, which may contain an previous calibration + // two methods are provided; 1. rtc.readOffsetReg(), or 2. rtc.getOffsetMode() and rtc.getOffset() + // Hint: + // Once the calibration Offset mode and Offset are known a line can be entered in the setup of the operating project sketch + // to re-establish the offset register after a battery replacement or clock reset. Note that your sketch will still require a method + // to insert the actual date and time. + // In the case of the above sample the line to insert in setup() would be: + // rtc.calibrate(PCF8523_TwoHours, 16); // re-insert previously calculated calibration after clock reset. + // read offset register ******************************* Serial.println("Read RTC PCF8523 Offset Register"); // Print to control offset From 41b7bea85cbbc8e51932f2648938e953ec774213 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Sun, 14 Jan 2024 14:49:14 +0100 Subject: [PATCH 05/12] Update RTC_PCF8523.cpp Added methods to obtain data from the offset register: readOffsetReg(); getOffsetMode(); getOffset(); --- src/RTC_PCF8523.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/RTC_PCF8523.cpp b/src/RTC_PCF8523.cpp index d2af3f93..6984e3f7 100644 --- a/src/RTC_PCF8523.cpp +++ b/src/RTC_PCF8523.cpp @@ -290,15 +290,14 @@ void RTC_PCF8523::deconfigureAllTimers() { void RTC_PCF8523::calibrate(Pcf8523OffsetMode mode, int8_t offset) { write_register(PCF8523_OFFSET, ((uint8_t)offset & 0x7F) | mode); } - /**************************************************************************/ /*! - @brief read the offset register + @brief read the offset register */ /**************************************************************************/ -int8_t RTC_PCF8523::readOffsetReg() { - int8_t OffsetReg = read_register(PCF8523_OFFSET); +uint8_t RTC_PCF8523::readOffsetReg() { + uint8_t OffsetReg = read_register(PCF8523_OFFSET); return OffsetReg; } @@ -307,9 +306,9 @@ int8_t RTC_PCF8523::readOffsetReg() { @brief read the offset register and return OffsetMode */ /**************************************************************************/ + String RTC_PCF8523::getOffsetMode() { String OffsetMode; -// int8_t OffsetReg = readOffsetReg(); if bitRead (readOffsetReg(), 7) { OffsetMode = String("PCF8523_OneMinute"); } else { @@ -321,9 +320,8 @@ String RTC_PCF8523::getOffsetMode() { /**************************************************************************/ /*! @brief read the offset register and return offset - The `offset` parameter is held in bits 0 to 6 as a signed 7bit interger - bit 6 needs to be copied to bit 7 to convert to a signed 8bit interger - + The offset parameter is held in bits 0 to 6 as a signed 7bit integer + bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer */ /**************************************************************************/ From a6d1ba2d7844b3426822b279836b7e6a81cb8fb9 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Sun, 14 Jan 2024 14:53:25 +0100 Subject: [PATCH 06/12] Update RTClib.h --- src/RTClib.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/RTClib.h b/src/RTClib.h index 464e7561..84a612fb 100644 --- a/src/RTClib.h +++ b/src/RTClib.h @@ -423,10 +423,9 @@ class RTC_PCF8523 : RTC_I2C { void disableCountdownTimer(void); void deconfigureAllTimers(void); void calibrate(Pcf8523OffsetMode mode, int8_t offset); - int8_t readOffsetReg(); - String getOffsetMode(); + uint8_t readOffsetReg(); + String getOffsetMode(); int8_t getOffset(); - }; /**************************************************************************/ From 0144018df415a5b009cd6a89695b0c05966029c4 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Sun, 14 Jan 2024 14:56:42 +0100 Subject: [PATCH 07/12] Update pcf8523_calibrate.ino --- examples/pcf8523_calibrate/pcf8523_calibrate.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pcf8523_calibrate/pcf8523_calibrate.ino b/examples/pcf8523_calibrate/pcf8523_calibrate.ino index 52f43a23..9b051086 100644 --- a/examples/pcf8523_calibrate/pcf8523_calibrate.ino +++ b/examples/pcf8523_calibrate/pcf8523_calibrate.ino @@ -88,7 +88,7 @@ void setup() { Serial.println("Read RTC PCF8523 Offset Register"); // Print to control offset // Method 1 **************************** - int8_t OffsetReg = rtc.readOffsetReg(); + uint8_t OffsetReg = rtc.readOffsetReg(); Serial.print("Offset mode is: "); if bitRead (OffsetReg, 7) { Serial.println("PCF8523_OneMinute"); From 3dc423b56d8d53fc3c6a363cbf1ed292d8ccacae Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:45:39 +0100 Subject: [PATCH 08/12] Update RTC_PCF8523.cpp Updated '@return' --- src/RTC_PCF8523.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/RTC_PCF8523.cpp b/src/RTC_PCF8523.cpp index 6984e3f7..9e74aa23 100644 --- a/src/RTC_PCF8523.cpp +++ b/src/RTC_PCF8523.cpp @@ -293,6 +293,7 @@ void RTC_PCF8523::calibrate(Pcf8523OffsetMode mode, int8_t offset) { /**************************************************************************/ /*! @brief read the offset register + @return OffsetReg the raw value of the register */ /**************************************************************************/ @@ -303,13 +304,14 @@ uint8_t RTC_PCF8523::readOffsetReg() { /**************************************************************************/ /*! - @brief read the offset register and return OffsetMode + @brief read the offset register + @return OffsetMode */ /**************************************************************************/ String RTC_PCF8523::getOffsetMode() { String OffsetMode; - if bitRead (readOffsetReg(), 7) { + if (bitRead (readOffsetReg(), 7)) { OffsetMode = String("PCF8523_OneMinute"); } else { OffsetMode = String("PCF8523_TwoHours "); @@ -319,9 +321,10 @@ String RTC_PCF8523::getOffsetMode() { /**************************************************************************/ /*! - @brief read the offset register and return offset - The offset parameter is held in bits 0 to 6 as a signed 7bit integer + @brief read the offset register + @details The offset parameter is held in bits 0 to 6 as a signed 7bit integer bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer + @return offset */ /**************************************************************************/ From c1875a41dd669799336d7f5c220b5e1aa6345aea Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:49:04 +0100 Subject: [PATCH 09/12] Update pcf8523_calibrate.ino Various minor changes and updated comments. --- .../pcf8523_calibrate/pcf8523_calibrate.ino | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/examples/pcf8523_calibrate/pcf8523_calibrate.ino b/examples/pcf8523_calibrate/pcf8523_calibrate.ino index 9b051086..4c83319c 100644 --- a/examples/pcf8523_calibrate/pcf8523_calibrate.ino +++ b/examples/pcf8523_calibrate/pcf8523_calibrate.ino @@ -1,4 +1,6 @@ -// Date and time functions using a PCF8523 RTC connected via I2C and Wire lib +// Functions to calibrate a PCF8523 RTC connected via I2C and Wire lib +// Functions to check the calibration once written or +// to check an unknown possible previous calibration #include "RTClib.h" RTC_PCF8523 rtc; @@ -88,27 +90,35 @@ void setup() { Serial.println("Read RTC PCF8523 Offset Register"); // Print to control offset // Method 1 **************************** - uint8_t OffsetReg = rtc.readOffsetReg(); + // Read offset register and interpret the result + Serial.println("Method 1"); + uint8_t OffsetReg = rtc.readOffsetReg(); // get raw data Serial.print("Offset mode is: "); - if bitRead (OffsetReg, 7) { + if (bitRead(OffsetReg, 7)) { // if bit 7 is 0b1 Serial.println("PCF8523_OneMinute"); - } else { + } else { // bit 7 is 0b0 Serial.println("PCF8523_TwoHours "); } offset = OffsetReg; + // The offset parameter is held in bits 0 to 6 as a signed 7bit integer + // bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer bitWrite(offset, 7, bitRead(OffsetReg, 6)); Serial.print("Offset is: "); Serial.println(offset); // Print to control offset + Serial.println(); // Method 2 **************************** - String OffsetMode = String(rtc.getOffsetMode()); + // Obtain and output Offset Mode + Serial.println("Method 2"); Serial.print("Offset mode is: "); - Serial.println(OffsetMode); + Serial.println(rtc.getOffsetMode()); // Print to control Offset Mode - offset = rtc.getOffset(); + // offset = rtc.getOffset(); + // Obtain and output Offset value -64 to +63 Serial.print("Offset is: "); - Serial.println(offset); // Print to control offset - // End read offset register ******************************* + Serial.println(rtc.getOffset()); // Print to control offset + Serial.println(); + // End read offset register ******************************* DateTime now = rtc.now(); From fd4c4f73dbbe513857c46ac80c5d11c7aa406110 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:34:32 +0100 Subject: [PATCH 10/12] Update RTC_PCF8523.cpp --- src/RTC_PCF8523.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RTC_PCF8523.cpp b/src/RTC_PCF8523.cpp index 9e74aa23..e2c41f6b 100644 --- a/src/RTC_PCF8523.cpp +++ b/src/RTC_PCF8523.cpp @@ -323,7 +323,7 @@ String RTC_PCF8523::getOffsetMode() { /*! @brief read the offset register @details The offset parameter is held in bits 0 to 6 as a signed 7bit integer - bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer + bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer @return offset */ /**************************************************************************/ From a9894c9c4d338c4d779b2c1bc144fc9db18db679 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Tue, 16 Jan 2024 17:38:50 +0100 Subject: [PATCH 11/12] Add files via upload Uploaded following clang format --- src/RTC_PCF8523.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/RTC_PCF8523.cpp b/src/RTC_PCF8523.cpp index e2c41f6b..7fd0eb4d 100644 --- a/src/RTC_PCF8523.cpp +++ b/src/RTC_PCF8523.cpp @@ -311,7 +311,7 @@ uint8_t RTC_PCF8523::readOffsetReg() { String RTC_PCF8523::getOffsetMode() { String OffsetMode; - if (bitRead (readOffsetReg(), 7)) { + if (bitRead(readOffsetReg(), 7)) { OffsetMode = String("PCF8523_OneMinute"); } else { OffsetMode = String("PCF8523_TwoHours "); @@ -322,8 +322,8 @@ String RTC_PCF8523::getOffsetMode() { /**************************************************************************/ /*! @brief read the offset register - @details The offset parameter is held in bits 0 to 6 as a signed 7bit integer - bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer + @details The offset parameter is held in bits 0 to 6 as a signed 7bit + integer bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer @return offset */ /**************************************************************************/ From ef17692548ef00eb8babc32f68add2bdc04518e3 Mon Sep 17 00:00:00 2001 From: Peter Bradley <46190700+Gambalunga@users.noreply.github.com> Date: Wed, 17 Jan 2024 23:45:03 +0100 Subject: [PATCH 12/12] Add files via upload Upfated following possible corruption by clang-format tool --- src/RTC_PCF8523.cpp | 670 ++++++++++++++++++++++---------------------- 1 file changed, 335 insertions(+), 335 deletions(-) diff --git a/src/RTC_PCF8523.cpp b/src/RTC_PCF8523.cpp index 7fd0eb4d..8c15843b 100644 --- a/src/RTC_PCF8523.cpp +++ b/src/RTC_PCF8523.cpp @@ -1,335 +1,335 @@ -#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. -*/ -/**************************************************************************/ -bool 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() -*/ -/**************************************************************************/ -bool 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 -*/ -/**************************************************************************/ -bool 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(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); -} -/**************************************************************************/ -/*! - @brief read the offset register - @return OffsetReg the raw value of the register -*/ -/**************************************************************************/ - -uint8_t RTC_PCF8523::readOffsetReg() { - uint8_t OffsetReg = read_register(PCF8523_OFFSET); - return OffsetReg; -} - -/**************************************************************************/ -/*! - @brief read the offset register - @return OffsetMode -*/ -/**************************************************************************/ - -String RTC_PCF8523::getOffsetMode() { - String OffsetMode; - if (bitRead(readOffsetReg(), 7)) { - OffsetMode = String("PCF8523_OneMinute"); - } else { - OffsetMode = String("PCF8523_TwoHours "); - } - return OffsetMode; -} - -/**************************************************************************/ -/*! - @brief read the offset register - @details The offset parameter is held in bits 0 to 6 as a signed 7bit - integer bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer - @return offset -*/ -/**************************************************************************/ - -int8_t RTC_PCF8523::getOffset() { - int8_t offset = readOffsetReg(); - bitWrite(offset, 7, bitRead(offset, 6)); - return offset; -} +#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. +*/ +/**************************************************************************/ +bool 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() +*/ +/**************************************************************************/ +bool 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 +*/ +/**************************************************************************/ +bool 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(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); +} +/**************************************************************************/ +/*! + @brief read the offset register + @return OffsetReg the raw value of the register +*/ +/**************************************************************************/ + +uint8_t RTC_PCF8523::readOffsetReg() { + uint8_t OffsetReg = read_register(PCF8523_OFFSET); + return OffsetReg; +} + +/**************************************************************************/ +/*! + @brief read the offset register + @return OffsetMode +*/ +/**************************************************************************/ + +String RTC_PCF8523::getOffsetMode() { + String OffsetMode; + if (bitRead(readOffsetReg(), 7)) { + OffsetMode = String("PCF8523_OneMinute"); + } else { + OffsetMode = String("PCF8523_TwoHours "); + } + return OffsetMode; +} + +/**************************************************************************/ +/*! + @brief read the offset register + @details The offset parameter is held in bits 0 to 6 as a signed 7bit + integer bit 6 needs to be copied to bit 7 to convert to a signed 8bit integer + @return offset +*/ +/**************************************************************************/ + +int8_t RTC_PCF8523::getOffset() { + int8_t offset = readOffsetReg(); + bitWrite(offset, 7, bitRead(offset, 6)); + return offset; +}