ArduinoLibs
RTC.cpp
00001 /*
00002  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included
00012  * in all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00019  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00020  * DEALINGS IN THE SOFTWARE.
00021  */
00022 
00023 #include "RTC.h"
00024 #if defined(ARDUINO) && ARDUINO >= 100
00025 #include <Arduino.h>
00026 #else
00027 #include <WProgram.h>
00028 #endif
00029 #include <stdlib.h>
00030 #include <string.h>
00031 
00058 #define DEFAULT_BYTE_COUNT  43  // Default simulates DS1307 NVRAM size.
00059 
00060 #define MILLIS_PER_DAY      86400000UL
00061 #define MILLIS_PER_SECOND   1000UL
00062 #define MILLIS_PER_MINUTE   60000UL
00063 #define MILLIS_PER_HOUR     3600000UL
00064 
00065 static uint8_t monthLengths[] = {
00066     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
00067 };
00068 
00069 static unsigned int monthOffsets[] = {
00070     0,
00071     31,
00072     31 + 28,
00073     31 + 28 + 31,
00074     31 + 28 + 31 + 30,
00075     31 + 28 + 31 + 30 + 31,
00076     31 + 28 + 31 + 30 + 31 + 30,
00077     31 + 28 + 31 + 30 + 31 + 30 + 31,
00078     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
00079     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
00080     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
00081     31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
00082 };
00083 
00084 inline bool isLeapYear(unsigned int year)
00085 {
00086     if ((year % 100) == 0)
00087         return (year % 400) == 0;
00088     else
00089         return (year % 4) == 0;
00090 }
00091 
00092 inline uint8_t monthLength(const RTCDate *date)
00093 {
00094     if (date->month != 2 || !isLeapYear(date->year))
00095         return monthLengths[date->month - 1];
00096     else
00097         return 29;
00098 }
00099 
00105 RTC::RTC()
00106     : midnight(millis() - 9 * MILLIS_PER_HOUR) // Simulated clock starts at 9am
00107     , nvram(0)
00108 {
00109     // Start the simulated date at 1 Jan, 2000.
00110     date.day = 1;
00111     date.month = 1;
00112     date.year = 2000;
00113 
00114     // Set all simulated alarms to 6am by default.
00115     for (uint8_t index = 0; index < ALARM_COUNT; ++index) {
00116         alarms[index].hour = 6;
00117         alarms[index].minute = 0;
00118         alarms[index].flags = 0;
00119     }
00120 }
00121 
00122 RTC::~RTC()
00123 {
00124     if (nvram)
00125         free(nvram);
00126 }
00127 
00134 bool RTC::hasUpdates()
00135 {
00136     return true;
00137 }
00138 
00144 void RTC::readTime(RTCTime *value)
00145 {
00146     // Determine the number of seconds since the last midnight event.
00147     unsigned long sinceMidnight = millis() - midnight;
00148     if (sinceMidnight >= MILLIS_PER_DAY) {
00149         // We have overflowed into the next day.  Readjust midnight.
00150         midnight += MILLIS_PER_DAY;
00151         sinceMidnight -= MILLIS_PER_DAY;
00152 
00153         // Increment the simulated date.
00154         adjustDays(&date, INCREMENT);
00155     }
00156     value->second = (uint8_t)(((sinceMidnight / MILLIS_PER_SECOND) % 60));
00157     value->minute = (uint8_t)(((sinceMidnight / MILLIS_PER_MINUTE) % 60));
00158     value->hour = (uint8_t)(sinceMidnight / MILLIS_PER_HOUR);
00159 }
00160 
00169 void RTC::readDate(RTCDate *value)
00170 {
00171     *value = date;
00172 }
00173 
00179 void RTC::writeTime(const RTCTime *value)
00180 {
00181     // Adjust the position of the last simulated midnight event.
00182     unsigned long sinceMidnight =
00183         value->second * MILLIS_PER_SECOND +
00184         value->minute * MILLIS_PER_MINUTE +
00185         value->hour   * MILLIS_PER_HOUR;
00186     midnight = millis() - sinceMidnight;
00187 }
00188 
00194 void RTC::writeDate(const RTCDate *value)
00195 {
00196     date = *value;
00197 }
00198 
00209 void RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value)
00210 {
00211     *value = alarms[alarmNum];
00212 }
00213 
00224 void RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value)
00225 {
00226     alarms[alarmNum] = *value;
00227 }
00228 
00235 int RTC::byteCount() const
00236 {
00237     return DEFAULT_BYTE_COUNT;
00238 }
00239 
00247 uint8_t RTC::readByte(uint8_t offset)
00248 {
00249     if (nvram)
00250         return nvram[offset];
00251     else
00252         return 0;
00253 }
00254 
00262 void RTC::writeByte(uint8_t offset, uint8_t value)
00263 {
00264     if (nvram) {
00265         nvram[offset] = value;
00266     } else {
00267         nvram = (uint8_t *)malloc(DEFAULT_BYTE_COUNT);
00268         if (nvram) {
00269             memset(nvram, 0, DEFAULT_BYTE_COUNT);
00270             nvram[offset] = value;
00271         }
00272     }
00273 }
00274 
00288 int RTC::readTemperature()
00289 {
00290     return NO_TEMPERATURE;
00291 }
00292 
00313 void RTC::adjustDays(RTCDate *date, uint8_t flags)
00314 {
00315     if (flags & DECREMENT) {
00316         --(date->day);
00317         if (date->day == 0) {
00318             if (!(flags & WRAP)) {
00319                 --(date->month);
00320                 if (date->month == 0)
00321                     date->month = 12;
00322             }
00323             date->day = monthLength(date);
00324         }
00325     } else {
00326         ++(date->day);
00327         if (date->day > monthLength(date)) {
00328             if (!(flags & WRAP)) {
00329                 ++(date->month);
00330                 if (date->month == 13)
00331                     date->month = 1;
00332             }
00333             date->day = 1;
00334         }
00335     }
00336 }
00337 
00343 void RTC::adjustMonths(RTCDate *date, uint8_t flags)
00344 {
00345     if (flags & DECREMENT) {
00346         --(date->month);
00347         if (date->month == 0) {
00348             date->month = 12;
00349             if (!(flags & WRAP) && date->year > 2000)
00350                 --(date->year);
00351         }
00352     } else {
00353         ++(date->month);
00354         if (date->month == 13) {
00355             date->month = 1;
00356             if (!(flags & WRAP) && date->year < 2099)
00357                 ++(date->year);
00358         }
00359     }
00360     uint8_t len = monthLength(date);
00361     if (date->day > len)
00362         date->day = len;
00363 }
00364 
00370 void RTC::adjustYears(RTCDate *date, uint8_t flags)
00371 {
00372     if (flags & DECREMENT) {
00373         --(date->year);
00374         if (date->year < 2000)
00375             date->year = 2000;
00376     } else {
00377         ++(date->year);
00378         if (date->year > 2099)
00379             date->year = 2099;
00380     }
00381     uint8_t len = monthLength(date);
00382     if (date->day > len)
00383         date->day = len;
00384 }
00385 
00399 RTC::DayOfWeek RTC::dayOfWeek(const RTCDate *date)
00400 {
00401     // The +4 here adjusts for Jan 1, 2000 being a Saturday.
00402     unsigned long daynum = date->day + 4;
00403     daynum += monthOffsets[date->month - 1];
00404     if (date->month > 2 && isLeapYear(date->year))
00405         ++daynum;
00406     daynum += 365UL * (date->year - 2000);
00407     if (date->year > 2000)
00408         daynum += ((date->year - 2001) / 4) + 1;
00409     return (DayOfWeek)((daynum % 7) + 1);
00410 }
00411 
 All Classes Files Functions Variables Typedefs Enumerations Enumerator