ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
RTC.cpp
1 /*
2  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "RTC.h"
24 #if defined(ARDUINO) && ARDUINO >= 100
25 #include <Arduino.h>
26 #else
27 #include <WProgram.h>
28 #endif
29 #include <stdlib.h>
30 #include <string.h>
31 
58 #define DEFAULT_BYTE_COUNT 43 // Default simulates DS1307 NVRAM size.
59 
60 #define MILLIS_PER_DAY 86400000UL
61 #define MILLIS_PER_SECOND 1000UL
62 #define MILLIS_PER_MINUTE 60000UL
63 #define MILLIS_PER_HOUR 3600000UL
64 
65 static uint8_t monthLengths[] = {
66  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
67 };
68 
69 static unsigned int monthOffsets[] = {
70  0,
71  31,
72  31 + 28,
73  31 + 28 + 31,
74  31 + 28 + 31 + 30,
75  31 + 28 + 31 + 30 + 31,
76  31 + 28 + 31 + 30 + 31 + 30,
77  31 + 28 + 31 + 30 + 31 + 30 + 31,
78  31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
79  31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
80  31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
81  31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
82 };
83 
84 inline bool isLeapYear(unsigned int year)
85 {
86  if ((year % 100) == 0)
87  return (year % 400) == 0;
88  else
89  return (year % 4) == 0;
90 }
91 
92 inline uint8_t monthLength(const RTCDate *date)
93 {
94  if (date->month != 2 || !isLeapYear(date->year))
95  return monthLengths[date->month - 1];
96  else
97  return 29;
98 }
99 
106  : midnight(millis() - 9 * MILLIS_PER_HOUR) // Simulated clock starts at 9am
107  , nvram(0)
108 {
109  // Start the simulated date at 1 Jan, 2000.
110  date.day = 1;
111  date.month = 1;
112  date.year = 2000;
113 
114  // Set all simulated alarms to 6am by default.
115  for (uint8_t index = 0; index < ALARM_COUNT; ++index) {
116  alarms[index].hour = 6;
117  alarms[index].minute = 0;
118  alarms[index].flags = 0;
119  }
120 }
121 
122 RTC::~RTC()
123 {
124  if (nvram)
125  free(nvram);
126 }
127 
135 {
136  return true;
137 }
138 
144 void RTC::readTime(RTCTime *value)
145 {
146  // Determine the number of seconds since the last midnight event.
147  unsigned long sinceMidnight = millis() - midnight;
148  if (sinceMidnight >= MILLIS_PER_DAY) {
149  // We have overflowed into the next day. Readjust midnight.
150  midnight += MILLIS_PER_DAY;
151  sinceMidnight -= MILLIS_PER_DAY;
152 
153  // Increment the simulated date.
154  adjustDays(&date, INCREMENT);
155  }
156  value->second = (uint8_t)(((sinceMidnight / MILLIS_PER_SECOND) % 60));
157  value->minute = (uint8_t)(((sinceMidnight / MILLIS_PER_MINUTE) % 60));
158  value->hour = (uint8_t)(sinceMidnight / MILLIS_PER_HOUR);
159 }
160 
169 void RTC::readDate(RTCDate *value)
170 {
171  *value = date;
172 }
173 
179 void RTC::writeTime(const RTCTime *value)
180 {
181  // Adjust the position of the last simulated midnight event.
182  unsigned long sinceMidnight =
183  value->second * MILLIS_PER_SECOND +
184  value->minute * MILLIS_PER_MINUTE +
185  value->hour * MILLIS_PER_HOUR;
186  midnight = millis() - sinceMidnight;
187 }
188 
194 void RTC::writeDate(const RTCDate *value)
195 {
196  date = *value;
197 }
198 
209 void RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value)
210 {
211  *value = alarms[alarmNum];
212 }
213 
224 void RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value)
225 {
226  alarms[alarmNum] = *value;
227 }
228 
235 int RTC::byteCount() const
236 {
237  return DEFAULT_BYTE_COUNT;
238 }
239 
247 uint8_t RTC::readByte(uint8_t offset)
248 {
249  if (nvram)
250  return nvram[offset];
251  else
252  return 0;
253 }
254 
262 void RTC::writeByte(uint8_t offset, uint8_t value)
263 {
264  if (nvram) {
265  nvram[offset] = value;
266  } else {
267  nvram = (uint8_t *)malloc(DEFAULT_BYTE_COUNT);
268  if (nvram) {
269  memset(nvram, 0, DEFAULT_BYTE_COUNT);
270  nvram[offset] = value;
271  }
272  }
273 }
274 
289 {
290  return NO_TEMPERATURE;
291 }
292 
313 void RTC::adjustDays(RTCDate *date, uint8_t flags)
314 {
315  if (flags & DECREMENT) {
316  --(date->day);
317  if (date->day == 0) {
318  if (!(flags & WRAP)) {
319  --(date->month);
320  if (date->month == 0)
321  date->month = 12;
322  }
323  date->day = monthLength(date);
324  }
325  } else {
326  ++(date->day);
327  if (date->day > monthLength(date)) {
328  if (!(flags & WRAP)) {
329  ++(date->month);
330  if (date->month == 13)
331  date->month = 1;
332  }
333  date->day = 1;
334  }
335  }
336 }
337 
343 void RTC::adjustMonths(RTCDate *date, uint8_t flags)
344 {
345  if (flags & DECREMENT) {
346  --(date->month);
347  if (date->month == 0) {
348  date->month = 12;
349  if (!(flags & WRAP) && date->year > 2000)
350  --(date->year);
351  }
352  } else {
353  ++(date->month);
354  if (date->month == 13) {
355  date->month = 1;
356  if (!(flags & WRAP) && date->year < 2099)
357  ++(date->year);
358  }
359  }
360  uint8_t len = monthLength(date);
361  if (date->day > len)
362  date->day = len;
363 }
364 
370 void RTC::adjustYears(RTCDate *date, uint8_t flags)
371 {
372  if (flags & DECREMENT) {
373  --(date->year);
374  if (date->year < 2000)
375  date->year = 2000;
376  } else {
377  ++(date->year);
378  if (date->year > 2099)
379  date->year = 2099;
380  }
381  uint8_t len = monthLength(date);
382  if (date->day > len)
383  date->day = len;
384 }
385 
400 {
401  // The +4 here adjusts for Jan 1, 2000 being a Saturday.
402  unsigned long daynum = date->day + 4;
403  daynum += monthOffsets[date->month - 1];
404  if (date->month > 2 && isLeapYear(date->year))
405  ++daynum;
406  daynum += 365UL * (date->year - 2000);
407  if (date->year > 2000)
408  daynum += ((date->year - 2001) / 4) + 1;
409  return (DayOfWeek)((daynum % 7) + 1);
410 }
411