From 81624f6d5e97390406d6293db3889bb452e62db5 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Thu, 17 May 2012 14:48:21 +1000 Subject: [PATCH] Split simulated clock code out of DS1307RTC This change should make it easier to support other realtime clock chips in the future. --- libraries/BitBangI2C/DS1307RTC.cpp | 375 +++----------------------- libraries/BitBangI2C/DS1307RTC.h | 40 +-- libraries/BitBangI2C/RTC.cpp | 408 +++++++++++++++++++++++++++++ libraries/BitBangI2C/RTC.h | 89 +++++++ libraries/BitBangI2C/keywords.txt | 1 + 5 files changed, 533 insertions(+), 380 deletions(-) create mode 100644 libraries/BitBangI2C/RTC.cpp create mode 100644 libraries/BitBangI2C/RTC.h diff --git a/libraries/BitBangI2C/DS1307RTC.cpp b/libraries/BitBangI2C/DS1307RTC.cpp index 7b3e934c..d5e6b441 100644 --- a/libraries/BitBangI2C/DS1307RTC.cpp +++ b/libraries/BitBangI2C/DS1307RTC.cpp @@ -22,8 +22,6 @@ #include "DS1307RTC.h" #include -#include -#include /** * \class DS1307RTC DS1307RTC.h @@ -34,14 +32,18 @@ * provides support for reading and writing information about alarms * and other clock settings. * - * If there is no DS1307 chip on the I2C bus, this class will simulate the - * current time and date based on the value of millis(). At startup the - * simulated time and date is set to 9am on the 1st of January, 2012. + * If there is no DS1307 chip on the I2C bus, this class will fall back to + * the RTC class to simulate the current time and date based on the value + * of millis(). * * The DS1307 uses a 2-digit year so this class is limited to dates between * 2000 and 2099 inclusive. * - * \sa RTCTime, RTCDate, RTCAlarm + * Note: if this class has not been used with the DS1307 chip before, + * then the contents of NVRAM will be cleared. Any previous contents + * will be lost. + * + * \sa RTC */ // I2C address of the RTC chip (7-bit). @@ -59,36 +61,10 @@ #define DS1307_NVRAM 0x08 // Alarm storage at the end of the RTC's NVRAM. -#define DS1307_NUM_ALARMS 4 #define DS1307_ALARM_SIZE 3 -#define DS1307_ALARMS (64 - DS1307_NUM_ALARMS * DS1307_ALARM_SIZE - 1) +#define DS1307_ALARMS (64 - RTC::ALARM_COUNT * DS1307_ALARM_SIZE - 1) #define DS1307_ALARM_MAGIC 63 -#define MILLIS_PER_DAY 86400000UL -#define MILLIS_PER_SECOND 1000UL -#define MILLIS_PER_MINUTE 60000UL -#define MILLIS_PER_HOUR 3600000UL - -static uint8_t monthLengths[] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -inline bool isLeapYear(unsigned int year) -{ - if ((year % 100) == 0) - return (year % 400) == 0; - else - return (year % 4) == 0; -} - -inline uint8_t monthLength(const RTCDate *date) -{ - if (date->month != 2 || !isLeapYear(date->year)) - return monthLengths[date->month - 1]; - else - return 29; -} - /** * \brief Attaches to a realtime clock slave device on \a bus. * @@ -102,19 +78,9 @@ inline uint8_t monthLength(const RTCDate *date) DS1307RTC::DS1307RTC(BitBangI2C &bus, uint8_t oneHzPin) : _bus(&bus) , _oneHzPin(oneHzPin) - , _alarmCount(0) - , _alarmOffset(0) , prevOneHz(false) , _isRealTime(true) - , midnight(millis() - 9 * MILLIS_PER_HOUR) // Simulated clock starts at 9am - , alarms(0) - , nvram(0) { - // Start the simulated date at 1 Jan, 2012. - date.day = 1; - date.month = 1; - date.year = 2012; - // Make sure the CH bit in register 0 is off or the clock won't update. if (_bus->startWrite(DS1307_I2C_ADDRESS) == BitBangI2C::ACK) { _bus->write(DS1307_SECOND); @@ -142,14 +108,14 @@ DS1307RTC::DS1307RTC(BitBangI2C &bus, uint8_t oneHzPin) _bus->write(0x10); _bus->stop(); } + + // Initialize the alarms in the RTC chip's NVRAM. + if (_isRealTime) + initAlarms(); } DS1307RTC::~DS1307RTC() { - if (alarms) - free(alarms); - if (nvram) - free(nvram); } /** @@ -157,13 +123,6 @@ DS1307RTC::~DS1307RTC() * \brief Returns true if the realtime clock is on the I2C bus; false if the time and date are simulated. */ -/** - * \brief Returns true if the realtime clock has updated since the last call to this function. - * - * If the object is not using the 1 Hz pin, then this function will always - * return true. Otherwise it only returns true when the 1 Hz square wave - * output pin on the DS1307 changes from low to high. - */ bool DS1307RTC::hasUpdates() { // If not using a 1 Hz pin or there is no RTC chip available, @@ -203,11 +162,6 @@ inline uint8_t fromHourBCD(uint8_t value) } } -/** - * \brief Reads the current time from the realtime clock into \a value. - * - * \sa writeTime(), readDate() - */ void DS1307RTC::readTime(RTCTime *value) { if (_isRealTime) { @@ -219,31 +173,14 @@ void DS1307RTC::readTime(RTCTime *value) value->hour = fromHourBCD(_bus->read(BitBangI2C::NACK)); _bus->stop(); } else { - // Determine the number of seconds since the last midnight event. - unsigned long sinceMidnight = millis() - midnight; - if (sinceMidnight >= MILLIS_PER_DAY) { - // We have overflowed into the next day. Readjust midnight. - midnight += MILLIS_PER_DAY; - sinceMidnight -= MILLIS_PER_DAY; - - // Increment the simulated date. - adjustDays(&date, INCREMENT); - } - value->second = (uint8_t)(((sinceMidnight / MILLIS_PER_SECOND) % 60)); - value->minute = (uint8_t)(((sinceMidnight / MILLIS_PER_MINUTE) % 60)); - value->hour = (uint8_t)(sinceMidnight / MILLIS_PER_HOUR); + RTC::readTime(value); } } -/** - * \brief Reads the current date from the realtime clock into \a value. - * - * \sa writeDate(), readTime() - */ void DS1307RTC::readDate(RTCDate *value) { if (!_isRealTime) { - *value = date; + RTC::readDate(value); return; } _bus->startWrite(DS1307_I2C_ADDRESS); @@ -260,11 +197,6 @@ inline uint8_t toBCD(uint8_t value) return ((value / 10) << 4) + (value % 10); } -/** - * \brief Updates the time in the realtime clock to match \a value. - * - * \sa readTime(), writeDate() - */ void DS1307RTC::writeTime(const RTCTime *value) { if (_isRealTime) { @@ -275,20 +207,10 @@ void DS1307RTC::writeTime(const RTCTime *value) _bus->write(toBCD(value->hour)); // Changes mode to 24-hour clock. _bus->stop(); } else { - // Adjust the position of the last simulated midnight event. - unsigned long sinceMidnight = - value->second * MILLIS_PER_SECOND + - value->minute * MILLIS_PER_MINUTE + - value->hour * MILLIS_PER_HOUR; - midnight = millis() - sinceMidnight; + RTC::writeTime(value); } } -/** - * \brief Updates the date in the realtime clock to match \a value. - * - * \sa readDate(), writeTime() - */ void DS1307RTC::writeDate(const RTCDate *value) { if (_isRealTime) { @@ -299,34 +221,12 @@ void DS1307RTC::writeDate(const RTCDate *value) _bus->write(toBCD(value->year % 100)); _bus->stop(); } else { - date = *value; + RTC::writeDate(value); } } -/** - * \brief Returns the number of alarms that can be stored in the realtime clock's non-volatile memory. - * - * At least 4 alarms will be provided by this class. - * - * \sa readAlarm(), writeAlarm() - */ -uint8_t DS1307RTC::alarmCount() -{ - initAlarms(); - return _alarmCount; -} - -/** - * \brief Reads the details of the alarm with index \a alarmNum into \a value. - * - * Alarm details are stored at the end of the realtime clock's non-volatile - * memory. - * - * \sa writeAlarm(), alarmCount() - */ void DS1307RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value) { - initAlarms(); if (_isRealTime) { _bus->startWrite(DS1307_I2C_ADDRESS); _bus->write(DS1307_ALARMS + alarmNum * DS1307_ALARM_SIZE); @@ -335,27 +235,13 @@ void DS1307RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value) value->minute = fromBCD(_bus->read()); value->flags = _bus->read(BitBangI2C::NACK); _bus->stop(); - } else if (alarms) { - *value = alarms[alarmNum]; } else { - // Alarms default to 6am when simulated. - value->hour = 6; - value->minute = 0; - value->flags = 0; + RTC::readAlarm(alarmNum, value); } } -/** - * \brief Updates the details of the alarm with index \a alarmNum from \a value. - * - * Alarm details are stored at the end of the realtime clock's non-volatile - * memory. - * - * \sa readAlarm(), alarmCount() - */ void DS1307RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value) { - initAlarms(); if (_isRealTime) { _bus->startWrite(DS1307_I2C_ADDRESS); _bus->write(DS1307_ALARMS + alarmNum * DS1307_ALARM_SIZE); @@ -363,16 +249,11 @@ void DS1307RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value) _bus->write(toBCD(value->minute)); _bus->write(value->flags); _bus->stop(); - } else if (alarms) { - alarms[alarmNum] = *value; + } else { + RTC::writeAlarm(alarmNum, value); } } -/** - * \brief Reads the byte at \a offset within the realtime clock's non-volatile memory. - * - * \sa writeByte() - */ uint8_t DS1307RTC::readByte(uint8_t offset) { if (_isRealTime) { @@ -382,18 +263,11 @@ uint8_t DS1307RTC::readByte(uint8_t offset) uint8_t value = _bus->read(BitBangI2C::NACK); _bus->stop(); return value; - } else if (nvram) { - return nvram[offset]; } else { - return 0xFF; + return RTC::readByte(offset); } } -/** - * \brief Writes \a value to \a offset within the realtime clock's non-volatile memory. - * - * \sa readByte() - */ void DS1307RTC::writeByte(uint8_t offset, uint8_t value) { if (_isRealTime) { @@ -401,221 +275,38 @@ void DS1307RTC::writeByte(uint8_t offset, uint8_t value) _bus->write(DS1307_NVRAM + offset); _bus->write(value); _bus->stop(); - } else if (nvram) { - nvram[offset] = value; } else { - nvram = (uint8_t *)malloc(DS1307_ALARMS - DS1307_NVRAM); - if (nvram) { - memset(nvram, 0xFF, DS1307_ALARMS - DS1307_NVRAM); - nvram[offset] = value; - } + RTC::writeByte(offset, value); } } -/** - * \var DS1307RTC::INCREMENT - * \brief Increment the day, month, or year. - */ - -/** - * \var DS1307RTC::DECREMENT - * \brief Decrement the day, month, or year. - */ - -/** - * \var DS1307RTC::WRAP - * \brief Wrap around to the beginning of the current month/year rather than advance to the next one. - */ - -/** - * \brief Adjusts \a date up or down one day according to \a flags. - * - * \sa adjustMonths(), adjustYears() - */ -void DS1307RTC::adjustDays(RTCDate *date, uint8_t flags) -{ - if (flags & DECREMENT) { - --(date->day); - if (date->day == 0) { - if (!(flags & WRAP)) { - --(date->month); - if (date->month == 0) - date->month = 12; - } - date->day = monthLength(date); - } - } else { - ++(date->day); - if (date->day > monthLength(date)) { - if (!(flags & WRAP)) { - ++(date->month); - if (date->month == 13) - date->month = 1; - } - date->day = 1; - } - } -} - -/** - * \brief Adjusts \a date up or down one month according to \a flags. - * - * \sa adjustDays(), adjustYears() - */ -void DS1307RTC::adjustMonths(RTCDate *date, uint8_t flags) -{ - if (flags & DECREMENT) { - --(date->month); - if (date->month == 0) { - date->month = 12; - if (!(flags & WRAP) && date->year > 2000) - --(date->year); - } - } else { - ++(date->month); - if (date->month == 13) { - date->month = 1; - if (!(flags & WRAP) && date->year < 2099) - ++(date->year); - } - } - uint8_t len = monthLength(date); - if (date->day > len) - date->day = len; -} - -/** - * \brief Adjusts \a date up or down one year according to \a flags. - * - * \sa adjustDays(), adjustMonths() - */ -void DS1307RTC::adjustYears(RTCDate *date, uint8_t flags) -{ - if (flags & DECREMENT) { - --(date->year); - if (date->year < 2000) - date->year = 2000; - } else { - ++(date->year); - if (date->year > 2099) - date->year = 2099; - } - uint8_t len = monthLength(date); - if (date->day > len) - date->day = len; -} - void DS1307RTC::initAlarms() { - if (_alarmCount != 0) - return; - if (!_isRealTime) { - // No RTC clock available, so fake the alarms. - alarms = (RTCAlarm *)malloc(sizeof(RTCAlarm) * DS1307_NUM_ALARMS); - RTCAlarm alarm; - alarm.hour = 6; // Default to 6am for alarms. - alarm.minute = 0; - alarm.flags = 0; - for (uint8_t index = 0; index < _alarmCount; ++index) - writeAlarm(index, &alarm); - _alarmCount = DS1307_NUM_ALARMS; - return; - } _bus->startWrite(DS1307_I2C_ADDRESS); _bus->write(DS1307_ALARM_MAGIC); _bus->startRead(DS1307_I2C_ADDRESS); uint8_t value = _bus->read(BitBangI2C::NACK); _bus->stop(); - if ((value & 0xF0) == 0xB0) { - // RTC chip was previously initialized with alarm information. - _alarmCount = value & 0x0F; - } else { + if (value != (0xB0 + ALARM_COUNT)) { // This is the first time we have used this clock chip, // so initialize all alarms to their default state. - // Note: if the number of alarms is changed in the future, - // then the high nibble will still be 0xB, with the low - // nibble the actual number of alarms that are stored. - _alarmCount = DS1307_NUM_ALARMS; RTCAlarm alarm; alarm.hour = 6; // Default to 6am for alarms. alarm.minute = 0; alarm.flags = 0; - for (uint8_t index = 0; index < _alarmCount; ++index) + for (uint8_t index = 0; index < ALARM_COUNT; ++index) writeAlarm(index, &alarm); _bus->startWrite(DS1307_I2C_ADDRESS); _bus->write(DS1307_ALARM_MAGIC); - _bus->write(0xB0 + _alarmCount); + _bus->write(0xB0 + ALARM_COUNT); + _bus->stop(); + + // Also clear the rest of NVRAM so that it is in a known state. + // Otherwise we'll have whatever garbage was present at power-on. + _bus->startWrite(DS1307_I2C_ADDRESS); + _bus->write(DS1307_NVRAM); + for (uint8_t index = DS1307_NVRAM; index < DS1307_ALARMS; ++index) + _bus->write(0); _bus->stop(); } - _alarmOffset = DS1307_ALARM_MAGIC - _alarmCount * DS1307_ALARM_SIZE; } - -/** - * \class RTCTime DS1307RTC.h - * \brief Stores time information from a realtime clock chip. - * - * \sa RTCDate, RTCAlarm, DS1307RTC - */ - -/** - * \var RTCTime::hour - * \brief Hour of the day (0-23) - */ - -/** - * \var RTCTime::minute - * \brief Minute within the hour (0-59) - */ - -/** - * \var RTCTime::second - * \brief Second within the minute (0-59) - */ - -/** - * \class RTCDate DS1307RTC.h - * \brief Stores date information from a realtime clock chip. - * - * \sa RTCTime, RTCAlarm, DS1307RTC - */ - -/** - * \var RTCDate::year - * \brief Year (4-digit) - */ - -/** - * \var RTCDate::month - * \brief Month of the year (1-12) - */ - -/** - * \var RTCDate::day - * \brief Day of the month (1-31) - */ - -/** - * \class RTCAlarm DS1307RTC.h - * \brief Stores alarm information from a realtime clock chip. - * - * \sa RTCTime, RTCDate, DS1307RTC - */ - -/** - * \var RTCAlarm::hour - * \brief Hour of the day for the alarm (0-23). - */ - -/** - * \var RTCAlarm::minute - * \brief Minute of the hour for the alarm (0-59). - */ - -/** - * \var RTCAlarm::flags - * \brief Additional flags for the alarm. - * - * The least significant bit will be 0 if the alarm is disabled or - * 1 if the alarm is enabled. Other bits can be used by the application - * for any purpose. - */ diff --git a/libraries/BitBangI2C/DS1307RTC.h b/libraries/BitBangI2C/DS1307RTC.h index b0b75783..767eda5c 100644 --- a/libraries/BitBangI2C/DS1307RTC.h +++ b/libraries/BitBangI2C/DS1307RTC.h @@ -23,30 +23,10 @@ #ifndef DS1307RTC_h #define DS1307RTC_h +#include "RTC.h" #include "BitBangI2C.h" -struct RTCTime -{ - uint8_t hour; - uint8_t minute; - uint8_t second; -}; - -struct RTCDate -{ - unsigned int year; - uint8_t month; - uint8_t day; -}; - -struct RTCAlarm -{ - uint8_t hour; - uint8_t minute; - uint8_t flags; -}; - -class DS1307RTC { +class DS1307RTC : public RTC { public: DS1307RTC(BitBangI2C &bus, uint8_t oneHzPin = 255); ~DS1307RTC(); @@ -61,33 +41,17 @@ public: void writeTime(const RTCTime *value); void writeDate(const RTCDate *value); - uint8_t alarmCount(); void readAlarm(uint8_t alarmNum, RTCAlarm *value); void writeAlarm(uint8_t alarmNum, const RTCAlarm *value); uint8_t readByte(uint8_t offset); void writeByte(uint8_t offset, uint8_t value); - // Flags for adjustDays(), adjustMonths(), and adjustYears(). - static const uint8_t INCREMENT = 0x0000; - static const uint8_t DECREMENT = 0x0001; - static const uint8_t WRAP = 0x0002; - - static void adjustDays(RTCDate *date, uint8_t flags); - static void adjustMonths(RTCDate *date, uint8_t flags); - static void adjustYears(RTCDate *date, uint8_t flags); - private: BitBangI2C *_bus; uint8_t _oneHzPin; - uint8_t _alarmCount; - uint8_t _alarmOffset; bool prevOneHz; bool _isRealTime; - unsigned long midnight; - RTCDate date; - RTCAlarm *alarms; - uint8_t *nvram; void initAlarms(); }; diff --git a/libraries/BitBangI2C/RTC.cpp b/libraries/BitBangI2C/RTC.cpp new file mode 100644 index 00000000..1c24cf9c --- /dev/null +++ b/libraries/BitBangI2C/RTC.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "RTC.h" +#include +#include +#include + +/** + * \class RTC RTC.h + * \brief Base class for realtime clock handlers. + * + * This class simplifies the process of reading and writing the time and + * date information in a realtime clock chip. The class also provides + * support for reading and writing information about alarms and other + * clock settings. + * + * It is intended that the application will instantiate a subclass of this + * class to handle the specific realtime clock chip in the system. The default + * implementation in RTC simulates a clock based on the value of millis(), + * with alarms and clock settings stored in main memory. + * + * Because the common DS1307 realtime clock chip uses a 2-digit year, + * this class is also limited to dates between 2000 and 2099 inclusive. + * + * \sa RTCTime, RTCDate, RTCAlarm, DS1307RTC + */ + +/** + * \var RTC::ALARM_COUNT + * \brief Number of alarms that are supported by RTC::readAlarm() and RTC::writeAlarm(). + */ + +/** + * \var RTC::BYTE_COUNT + * \brief Number of bytes that are supported by RTC::readByte() and RTC::writeByte(). + */ + +#define MILLIS_PER_DAY 86400000UL +#define MILLIS_PER_SECOND 1000UL +#define MILLIS_PER_MINUTE 60000UL +#define MILLIS_PER_HOUR 3600000UL + +static uint8_t monthLengths[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +inline bool isLeapYear(unsigned int year) +{ + if ((year % 100) == 0) + return (year % 400) == 0; + else + return (year % 4) == 0; +} + +inline uint8_t monthLength(const RTCDate *date) +{ + if (date->month != 2 || !isLeapYear(date->year)) + return monthLengths[date->month - 1]; + else + return 29; +} + +/** + * \brief Constructs a new realtime clock handler. + * + * \sa hasUpdates() + */ +RTC::RTC() + : midnight(millis() - 9 * MILLIS_PER_HOUR) // Simulated clock starts at 9am + , nvram(0) +{ + // Start the simulated date at 1 Jan, 2012. + date.day = 1; + date.month = 1; + date.year = 2012; + + // Set all simulated alarms to 6am by default. + for (uint8_t index = 0; index < ALARM_COUNT; ++index) { + alarms[index].hour = 6; + alarms[index].minute = 0; + alarms[index].flags = 0; + } +} + +RTC::~RTC() +{ + if (nvram) + free(nvram); +} + +/** + * \brief Returns true if the realtime clock has updated since the last call to this function. + * + * The default implementation returns true, indicating that an update is + * always available to be read. + */ +bool RTC::hasUpdates() +{ + return true; +} + +/** + * \brief Reads the current time from the realtime clock into \a value. + * + * \sa writeTime(), readDate() + */ +void RTC::readTime(RTCTime *value) +{ + // Determine the number of seconds since the last midnight event. + unsigned long sinceMidnight = millis() - midnight; + if (sinceMidnight >= MILLIS_PER_DAY) { + // We have overflowed into the next day. Readjust midnight. + midnight += MILLIS_PER_DAY; + sinceMidnight -= MILLIS_PER_DAY; + + // Increment the simulated date. + adjustDays(&date, INCREMENT); + } + value->second = (uint8_t)(((sinceMidnight / MILLIS_PER_SECOND) % 60)); + value->minute = (uint8_t)(((sinceMidnight / MILLIS_PER_MINUTE) % 60)); + value->hour = (uint8_t)(sinceMidnight / MILLIS_PER_HOUR); +} + +/** + * \brief Reads the current date from the realtime clock into \a value. + * + * The time should be read first with readTime() as the default implementation + * only advances the date when the time is read and it crosses midnight. + * + * \sa writeDate(), readTime() + */ +void RTC::readDate(RTCDate *value) +{ + *value = date; +} + +/** + * \brief Updates the time in the realtime clock to match \a value. + * + * \sa readTime(), writeDate() + */ +void RTC::writeTime(const RTCTime *value) +{ + // Adjust the position of the last simulated midnight event. + unsigned long sinceMidnight = + value->second * MILLIS_PER_SECOND + + value->minute * MILLIS_PER_MINUTE + + value->hour * MILLIS_PER_HOUR; + midnight = millis() - sinceMidnight; +} + +/** + * \brief Updates the date in the realtime clock to match \a value. + * + * \sa readDate(), writeTime() + */ +void RTC::writeDate(const RTCDate *value) +{ + date = *value; +} + +/** + * \brief Reads the details of the alarm with index \a alarmNum into \a value. + * + * The \a alarmNum parameter must be between 0 and \ref ALARM_COUNT - 1. + * + * Alarm details are stored at the end of the realtime clock's non-volatile + * memory. + * + * \sa writeAlarm(), alarmCount() + */ +void RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value) +{ + *value = alarms[alarmNum]; +} + +/** + * \brief Updates the details of the alarm with index \a alarmNum from \a value. + * + * The \a alarmNum parameter must be between 0 and \ref ALARM_COUNT - 1. + * + * Alarm details are stored at the end of the realtime clock's non-volatile + * memory. + * + * \sa readAlarm(), alarmCount() + */ +void RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value) +{ + alarms[alarmNum] = *value; +} + +/** + * \brief Reads the byte at \a offset within the realtime clock's non-volatile memory. + * + * The \a offset parameter must be between 0 and \ref BYTE_COUNT - 1. + * + * \sa writeByte() + */ +uint8_t RTC::readByte(uint8_t offset) +{ + if (nvram) + return nvram[offset]; + else + return 0; +} + +/** + * \brief Writes \a value to \a offset within the realtime clock's non-volatile memory. + * + * The \a offset parameter must be between 0 and \ref BYTE_COUNT - 1. + * + * \sa readByte() + */ +void RTC::writeByte(uint8_t offset, uint8_t value) +{ + if (nvram) { + nvram[offset] = value; + } else { + nvram = (uint8_t *)malloc(BYTE_COUNT); + if (nvram) { + memset(nvram, 0, BYTE_COUNT); + nvram[offset] = value; + } + } +} + +/** + * \var RTC::INCREMENT + * \brief Increment the day, month, or year in a call to adjustDays(), adjustMonths(), or adjustYears(). + */ + +/** + * \var RTC::DECREMENT + * \brief Decrement the day, month, or year in a call to adjustDays(), adjustMonths(), or adjustYears(). + */ + +/** + * \var RTC::WRAP + * \brief Wrap around to the beginning of the current month/year rather than advance to the next one. + */ + +/** + * \brief Adjusts \a date up or down one day according to \a flags. + * + * \sa adjustMonths(), adjustYears() + */ +void RTC::adjustDays(RTCDate *date, uint8_t flags) +{ + if (flags & DECREMENT) { + --(date->day); + if (date->day == 0) { + if (!(flags & WRAP)) { + --(date->month); + if (date->month == 0) + date->month = 12; + } + date->day = monthLength(date); + } + } else { + ++(date->day); + if (date->day > monthLength(date)) { + if (!(flags & WRAP)) { + ++(date->month); + if (date->month == 13) + date->month = 1; + } + date->day = 1; + } + } +} + +/** + * \brief Adjusts \a date up or down one month according to \a flags. + * + * \sa adjustDays(), adjustYears() + */ +void RTC::adjustMonths(RTCDate *date, uint8_t flags) +{ + if (flags & DECREMENT) { + --(date->month); + if (date->month == 0) { + date->month = 12; + if (!(flags & WRAP) && date->year > 2000) + --(date->year); + } + } else { + ++(date->month); + if (date->month == 13) { + date->month = 1; + if (!(flags & WRAP) && date->year < 2099) + ++(date->year); + } + } + uint8_t len = monthLength(date); + if (date->day > len) + date->day = len; +} + +/** + * \brief Adjusts \a date up or down one year according to \a flags. + * + * \sa adjustDays(), adjustMonths() + */ +void RTC::adjustYears(RTCDate *date, uint8_t flags) +{ + if (flags & DECREMENT) { + --(date->year); + if (date->year < 2000) + date->year = 2000; + } else { + ++(date->year); + if (date->year > 2099) + date->year = 2099; + } + uint8_t len = monthLength(date); + if (date->day > len) + date->day = len; +} + +/** + * \class RTCTime RTC.h + * \brief Stores time information from a realtime clock chip. + * + * \sa RTCDate, RTCAlarm, RTC + */ + +/** + * \var RTCTime::hour + * \brief Hour of the day (0-23) + */ + +/** + * \var RTCTime::minute + * \brief Minute within the hour (0-59) + */ + +/** + * \var RTCTime::second + * \brief Second within the minute (0-59) + */ + +/** + * \class RTCDate RTC.h + * \brief Stores date information from a realtime clock chip. + * + * \sa RTCTime, RTCAlarm, RTC + */ + +/** + * \var RTCDate::year + * \brief Year (4-digit) + */ + +/** + * \var RTCDate::month + * \brief Month of the year (1-12) + */ + +/** + * \var RTCDate::day + * \brief Day of the month (1-31) + */ + +/** + * \class RTCAlarm RTC.h + * \brief Stores alarm information from a realtime clock chip. + * + * \sa RTCTime, RTCDate, RTC + */ + +/** + * \var RTCAlarm::hour + * \brief Hour of the day for the alarm (0-23). + */ + +/** + * \var RTCAlarm::minute + * \brief Minute of the hour for the alarm (0-59). + */ + +/** + * \var RTCAlarm::flags + * \brief Additional flags for the alarm. + * + * The least significant bit will be 0 if the alarm is disabled or + * 1 if the alarm is enabled. Other bits can be used by the application + * for any purpose. + */ diff --git a/libraries/BitBangI2C/RTC.h b/libraries/BitBangI2C/RTC.h new file mode 100644 index 00000000..50aa2e01 --- /dev/null +++ b/libraries/BitBangI2C/RTC.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef RTC_h +#define RTC_h + +#include + +struct RTCTime +{ + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + +struct RTCDate +{ + unsigned int year; + uint8_t month; + uint8_t day; +}; + +struct RTCAlarm +{ + uint8_t hour; + uint8_t minute; + uint8_t flags; +}; + +class RTC +{ +public: + RTC(); + ~RTC(); + + virtual bool hasUpdates(); + + virtual void readTime(RTCTime *value); + virtual void readDate(RTCDate *value); + + virtual void writeTime(const RTCTime *value); + virtual void writeDate(const RTCDate *value); + + static const uint8_t ALARM_COUNT = 4; + + virtual void readAlarm(uint8_t alarmNum, RTCAlarm *value); + virtual void writeAlarm(uint8_t alarmNum, const RTCAlarm *value); + + static const uint8_t BYTE_COUNT = 43; + + virtual uint8_t readByte(uint8_t offset); + virtual void writeByte(uint8_t offset, uint8_t value); + + // Flags for adjustDays(), adjustMonths(), and adjustYears(). + static const uint8_t INCREMENT = 0x0000; + static const uint8_t DECREMENT = 0x0001; + static const uint8_t WRAP = 0x0002; + + static void adjustDays(RTCDate *date, uint8_t flags); + static void adjustMonths(RTCDate *date, uint8_t flags); + static void adjustYears(RTCDate *date, uint8_t flags); + +private: + unsigned long midnight; + RTCDate date; + RTCAlarm alarms[ALARM_COUNT]; + uint8_t *nvram; +}; + +#endif diff --git a/libraries/BitBangI2C/keywords.txt b/libraries/BitBangI2C/keywords.txt index 816555b2..d1828dbd 100644 --- a/libraries/BitBangI2C/keywords.txt +++ b/libraries/BitBangI2C/keywords.txt @@ -1,5 +1,6 @@ BitBangI2C KEYWORD1 DS1307RTC KEYWORD1 +RTC KEYWORD1 start KEYWORD2 stop KEYWORD2