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 inline bool isLeapYear(unsigned int year)
00070 {
00071     if ((year % 100) == 0)
00072         return (year % 400) == 0;
00073     else
00074         return (year % 4) == 0;
00075 }
00076 
00077 inline uint8_t monthLength(const RTCDate *date)
00078 {
00079     if (date->month != 2 || !isLeapYear(date->year))
00080         return monthLengths[date->month - 1];
00081     else
00082         return 29;
00083 }
00084 
00090 RTC::RTC()
00091     : midnight(millis() - 9 * MILLIS_PER_HOUR) // Simulated clock starts at 9am
00092     , nvram(0)
00093 {
00094     // Start the simulated date at 1 Jan, 2000.
00095     date.day = 1;
00096     date.month = 1;
00097     date.year = 2000;
00098 
00099     // Set all simulated alarms to 6am by default.
00100     for (uint8_t index = 0; index < ALARM_COUNT; ++index) {
00101         alarms[index].hour = 6;
00102         alarms[index].minute = 0;
00103         alarms[index].flags = 0;
00104     }
00105 }
00106 
00107 RTC::~RTC()
00108 {
00109     if (nvram)
00110         free(nvram);
00111 }
00112 
00119 bool RTC::hasUpdates()
00120 {
00121     return true;
00122 }
00123 
00129 void RTC::readTime(RTCTime *value)
00130 {
00131     // Determine the number of seconds since the last midnight event.
00132     unsigned long sinceMidnight = millis() - midnight;
00133     if (sinceMidnight >= MILLIS_PER_DAY) {
00134         // We have overflowed into the next day.  Readjust midnight.
00135         midnight += MILLIS_PER_DAY;
00136         sinceMidnight -= MILLIS_PER_DAY;
00137 
00138         // Increment the simulated date.
00139         adjustDays(&date, INCREMENT);
00140     }
00141     value->second = (uint8_t)(((sinceMidnight / MILLIS_PER_SECOND) % 60));
00142     value->minute = (uint8_t)(((sinceMidnight / MILLIS_PER_MINUTE) % 60));
00143     value->hour = (uint8_t)(sinceMidnight / MILLIS_PER_HOUR);
00144 }
00145 
00154 void RTC::readDate(RTCDate *value)
00155 {
00156     *value = date;
00157 }
00158 
00164 void RTC::writeTime(const RTCTime *value)
00165 {
00166     // Adjust the position of the last simulated midnight event.
00167     unsigned long sinceMidnight =
00168         value->second * MILLIS_PER_SECOND +
00169         value->minute * MILLIS_PER_MINUTE +
00170         value->hour   * MILLIS_PER_HOUR;
00171     midnight = millis() - sinceMidnight;
00172 }
00173 
00179 void RTC::writeDate(const RTCDate *value)
00180 {
00181     date = *value;
00182 }
00183 
00194 void RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value)
00195 {
00196     *value = alarms[alarmNum];
00197 }
00198 
00209 void RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value)
00210 {
00211     alarms[alarmNum] = *value;
00212 }
00213 
00220 int RTC::byteCount() const
00221 {
00222     return DEFAULT_BYTE_COUNT;
00223 }
00224 
00232 uint8_t RTC::readByte(uint8_t offset)
00233 {
00234     if (nvram)
00235         return nvram[offset];
00236     else
00237         return 0;
00238 }
00239 
00247 void RTC::writeByte(uint8_t offset, uint8_t value)
00248 {
00249     if (nvram) {
00250         nvram[offset] = value;
00251     } else {
00252         nvram = (uint8_t *)malloc(DEFAULT_BYTE_COUNT);
00253         if (nvram) {
00254             memset(nvram, 0, DEFAULT_BYTE_COUNT);
00255             nvram[offset] = value;
00256         }
00257     }
00258 }
00259 
00280 void RTC::adjustDays(RTCDate *date, uint8_t flags)
00281 {
00282     if (flags & DECREMENT) {
00283         --(date->day);
00284         if (date->day == 0) {
00285             if (!(flags & WRAP)) {
00286                 --(date->month);
00287                 if (date->month == 0)
00288                     date->month = 12;
00289             }
00290             date->day = monthLength(date);
00291         }
00292     } else {
00293         ++(date->day);
00294         if (date->day > monthLength(date)) {
00295             if (!(flags & WRAP)) {
00296                 ++(date->month);
00297                 if (date->month == 13)
00298                     date->month = 1;
00299             }
00300             date->day = 1;
00301         }
00302     }
00303 }
00304 
00310 void RTC::adjustMonths(RTCDate *date, uint8_t flags)
00311 {
00312     if (flags & DECREMENT) {
00313         --(date->month);
00314         if (date->month == 0) {
00315             date->month = 12;
00316             if (!(flags & WRAP) && date->year > 2000)
00317                 --(date->year);
00318         }
00319     } else {
00320         ++(date->month);
00321         if (date->month == 13) {
00322             date->month = 1;
00323             if (!(flags & WRAP) && date->year < 2099)
00324                 ++(date->year);
00325         }
00326     }
00327     uint8_t len = monthLength(date);
00328     if (date->day > len)
00329         date->day = len;
00330 }
00331 
00337 void RTC::adjustYears(RTCDate *date, uint8_t flags)
00338 {
00339     if (flags & DECREMENT) {
00340         --(date->year);
00341         if (date->year < 2000)
00342             date->year = 2000;
00343     } else {
00344         ++(date->year);
00345         if (date->year > 2099)
00346             date->year = 2099;
00347     }
00348     uint8_t len = monthLength(date);
00349     if (date->day > len)
00350         date->day = len;
00351 }
00352 
 All Classes Files Functions Variables Enumerations Enumerator