MHVLib  20111011
An efficiency oriented runtime library for AVR microcontrollers
A:/eclipse/mhvlib/MHV_RTC.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011, Make, Hack, Void Inc
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *  * Redistributions of source code must retain the above copyright
00008  *    notice, this list of conditions and the following disclaimer.
00009  *  * Redistributions in binary form must reproduce the above copyright
00010  *    notice, this list of conditions and the following disclaimer in the
00011  *    documentation and/or other materials provided with the distribution.
00012  *  * Neither the name of the Make, Hack, Void nor the
00013  *    names of its contributors may be used to endorse or promote products
00014  *    derived from this software without specific prior written permission.
00015  *
00016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019  * DISCLAIMED. IN NO EVENT SHALL MAKE, HACK, VOID BE LIABLE FOR ANY
00020  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  */
00027 
00028 #include <MHV_RTC.h>
00029 #include <avr/pgmspace.h>
00030 #include <string.h> // Needed for memmove
00031 
00044 // First some helper functions
00045 
00052 bool mhv_timestampLessThan(MHV_TIMESTAMP *first, MHV_TIMESTAMP *second) {
00053         if (first->timestamp < second->timestamp) {
00054                 return true;
00055         }
00056 
00057         if (first->timestamp == second->timestamp) {
00058                 if (first->milliseconds < second->milliseconds) {
00059                                 return true;
00060                         }
00061         }
00062 
00063         return false;
00064 }
00065 
00072 bool mhv_timestampGreaterThanOrEqual(MHV_TIMESTAMP *first, MHV_TIMESTAMP *second) {
00073         if (first->timestamp > second->timestamp) {
00074                 return true;
00075         }
00076 
00077         if (first->timestamp == second->timestamp) {
00078                 if (first->milliseconds >= second->milliseconds) {
00079                                 return true;
00080                         }
00081         }
00082 
00083         return false;
00084 }
00085 
00089 bool mhv_isLeapYear(uint16_t year) {
00090         if ((0 == year % 4 && year % 100) || 0 == year % 400) {
00091                 return true;
00092         }
00093         return false;
00094 }
00095 
00102 void mhv_timestampIncrement(MHV_TIMESTAMP *timestamp, uint32_t seconds, uint16_t milliseconds) {
00103         timestamp->milliseconds += milliseconds;
00104 
00105         while (timestamp->milliseconds >= 1000) {
00106                 timestamp->timestamp++;
00107                 timestamp->milliseconds -= 1000;
00108         }
00109 
00110         timestamp->timestamp += seconds;
00111 }
00112 
00118 void mhv_timestampIncrement(MHV_TIMESTAMP *timestamp, MHV_TIMESTAMP *timestamp2) {
00119         timestamp->milliseconds += timestamp2->milliseconds;
00120 
00121         while (timestamp->milliseconds >= 1000) {
00122                 timestamp->timestamp++;
00123                 timestamp->milliseconds -= 1000;
00124         }
00125 
00126         timestamp->timestamp += timestamp2->timestamp;
00127 }
00128 
00129 
00130 const uint8_t mhv_daysInMonthArray[] PROGMEM = {
00131                 31,     // Jan
00132                 28,     // Feb
00133                 31,     // Mar
00134                 30, // Apr
00135                 31, // May
00136                 30, // Jun
00137                 31, // Jul
00138                 30, // Aug
00139                 30, // Sep
00140                 31, // Oct
00141                 30, // Nov
00142                 31 // Dec
00143 };
00144 
00151 uint8_t mhv_daysInMonth(MHV_MONTH month, uint16_t year) {
00152         if (MHV_FEBRUARY == month && mhv_isLeapYear(year)) {
00153                 return 29;
00154         }
00155 
00156         return pgm_read_byte(mhv_daysInMonthArray + month - 1);
00157 }
00158 
00159 
00167 MHV_RTC::MHV_RTC(MHV_Timer8 *timer, MHV_ALARM *eventBuffer, uint8_t eventCount, int16_t timezone) {
00168         _timer = timer;
00169 
00170         _ticks = 0;
00171         _ticksPerMillisecond = 1;
00172 
00173         _tzOffset = timezone;
00174 
00175         mhv_memClear (eventBuffer, sizeof(*eventBuffer), eventCount);
00176 
00177         _alarms = eventBuffer;
00178         _alarmMax = eventCount;
00179         _alarmCount = 0;
00180 }
00181 
00185 void MHV_RTC::synchronise(void) {
00186         uint32_t ticksPerMillisecond = F_CPU / _timer->getPrescalerMultiplier() / (_timer->getTop() + 1) / 1000;
00187         _ticksPerMillisecond = ticksPerMillisecond;
00188 }
00189 
00195 void MHV_RTC::setTime(uint32_t timestamp, uint16_t milliseconds) {
00196         do {
00197                 _milliseconds = milliseconds;
00198                 _timestamp = timestamp;
00199         } while (milliseconds != _milliseconds); // Retry if the value changed while updating
00200 }
00201 
00206 void MHV_RTC::setTime(MHV_TIMESTAMP *timestamp) {
00207         do {
00208                 _milliseconds = timestamp->milliseconds;
00209                 _timestamp = timestamp->timestamp;
00210         } while (timestamp->milliseconds != _milliseconds); // Retry if the value changed while updating
00211 }
00212 
00213 
00217 void MHV_RTC::tick(void) {
00218         if (++_ticks != _ticksPerMillisecond) {
00219                 return;
00220         }
00221 
00222         _ticks = 0;
00223 
00224         _milliseconds++;
00225         if (_milliseconds > 999) {
00226                 _milliseconds = 0;
00227                 _timestamp++;
00228         }
00229 }
00230 
00234 void MHV_RTC::tick1ms(void) {
00235         _milliseconds++;
00236         if (_milliseconds > 999) {
00237                 _milliseconds = 0;
00238                 _timestamp++;
00239         }
00240 }
00241 
00245 void MHV_RTC::tickAndRunEvents(void) {
00246         if (++_ticks != _ticksPerMillisecond) {
00247                 return;
00248         }
00249 
00250         _ticks = 0;
00251 
00252         _milliseconds++;
00253         if (_milliseconds > 999) {
00254                 _milliseconds = 0;
00255                 _timestamp++;
00256         }
00257 
00258         handleEvents();
00259 }
00260 
00264 void MHV_RTC::tick1msAndRunEvents(void) {
00265         _milliseconds++;
00266         if (_milliseconds > 999) {
00267                 _milliseconds = 0;
00268                 _timestamp++;
00269         }
00270 }
00271 
00272 
00276 void MHV_RTC::current(MHV_TIMESTAMP *timestamp) {
00277         do {
00278                 timestamp->milliseconds = _milliseconds;
00279                 timestamp->timestamp = _timestamp;
00280         } while (timestamp->milliseconds != _milliseconds); // Retry if the value changed while updating
00281 }
00282 
00283 
00289 void MHV_RTC::elapsed(MHV_TIMESTAMP *since, MHV_TIMESTAMP *elapsed) {
00290         MHV_TIMESTAMP currentTimestamp;
00291 
00292         current(&currentTimestamp);
00293 
00294         elapsed->timestamp = currentTimestamp.timestamp - since->timestamp;
00295         if (currentTimestamp.milliseconds > since->milliseconds) {
00296                 elapsed->milliseconds = currentTimestamp.milliseconds - since->milliseconds;
00297         } else if (elapsed->timestamp) {
00298                 elapsed->timestamp--;
00299                 elapsed->milliseconds = 1000 + currentTimestamp.milliseconds - since->milliseconds;
00300         } else {
00301                 elapsed->milliseconds = 0;
00302         }
00303 }
00304 
00305 // Used in toTime: Cumulative totals at the end of the month in a normal year
00306 const uint32_t mhv_secondsFromYearStart[] PROGMEM = {
00307                  2678400,       // Jan
00308                  5097600,       // Feb
00309                  7776000,       // Mar
00310                 10368000,       // Apr
00311                 13046400,       // May
00312                 15638400,       // Jun
00313                 18316800,       // Jul
00314                 20995200,       // Aug
00315                 23587200,       // Sep
00316                 26265600,       // Oct
00317                 28857600,       // Nov
00318                 31536000        // Dec
00319 };
00320 
00321 
00327 void MHV_RTC::toTime(MHV_TIME *to, MHV_TIMESTAMP *from) {
00328         uint16_t year = 1970;
00329         bool leapYear = false;
00330         uint32_t seconds = from->timestamp + _tzOffset;
00331 
00332         to->timezone = _tzOffset;
00333 
00334         uint32_t secondsThisYear = 365 * 86400;
00335         uint32_t secondsSoFar = 0;
00336         while (seconds > secondsThisYear) {
00337                 seconds -= secondsThisYear;
00338                 secondsSoFar += secondsThisYear;
00339 
00340                 if ((mhv_isLeapYear(++year))) {
00341                         secondsThisYear = 366 * 86400;
00342                 } else {
00343                         secondsThisYear = 365 * 86400;
00344                 }
00345         }
00346         to->year = year;
00347 
00348         uint8_t month;
00349         leapYear = mhv_isLeapYear(year);
00350         for (month = 0; month < sizeof(mhv_secondsFromYearStart); month++) {
00351                 if (leapYear) {
00352                         if (seconds < pgm_read_dword(&mhv_secondsFromYearStart[month] + (1 == month) ? 86400 : 0)) {
00353                                 break;
00354                         }
00355                 } else {
00356                         if (seconds < pgm_read_dword(&mhv_secondsFromYearStart[month])) {
00357                                 break;
00358                         }
00359                 }
00360         }
00361         to->yearday = seconds / 86400;
00362 
00363         if (month) {
00364                 seconds -= pgm_read_dword(&mhv_secondsFromYearStart[month - 1]);
00365         }
00366 
00367         if (leapYear && month > 1) {
00368                 seconds -= 86400;
00369         }
00370 
00371         // Increment so we start numbering from 1
00372         to->month = (MHV_MONTH)(month + 1);
00373 
00374         uint32_t day = seconds / 86400;
00375         to->day = (uint8_t)day;
00376         seconds -= day * 86400;
00377         to->day += 1; // Increment so we start numbering from 1
00378 
00379         uint32_t hours = seconds / 3600;
00380         to->hours = (uint8_t)hours;
00381         seconds -= hours * 3600;
00382 
00383         uint32_t minutes = seconds / 60;
00384         to->minutes = (uint8_t)minutes;
00385         seconds -= minutes * 60;
00386 
00387         to->seconds = seconds;
00388         to->milliseconds = from->milliseconds;
00389 }
00390 
00396 void MHV_RTC::toTimestamp(MHV_TIMESTAMP *to, MHV_TIME *from) {
00397         to->milliseconds = from->milliseconds;
00398 
00399         uint32_t seconds = from->seconds;
00400 
00401         seconds += (uint32_t)from->minutes * 60;
00402 
00403         seconds += (uint32_t)from->hours * 3600;
00404 
00405         if (from->yearday) {
00406                 seconds += from->yearday * 86400;
00407         } else {
00408                 seconds += (from->day - 1) * 86400;
00409                 switch (from->month) {
00410                 case MHV_JANUARY:
00411                         break;
00412                 case MHV_FEBRUARY:
00413                         seconds += 5097600;
00414                         break;
00415                 default:
00416                         seconds += pgm_read_dword(&mhv_secondsFromYearStart[(uint8_t)from->month - 2]);
00417                         if (mhv_isLeapYear(from->year)) {
00418                                 seconds += 86400;
00419                         }
00420                         break;
00421                 }
00422         }
00423 
00424         for (uint16_t year = from->year - 1; year >= 1970; year--) {
00425                 seconds += 365 * 86400;
00426                 if (mhv_isLeapYear(year)) {
00427                         seconds += 86400;
00428                 }
00429         }
00430 
00431         to->timestamp = seconds - _tzOffset;
00432 }
00433 
00434 
00443 bool MHV_RTC::addAlarm(MHV_ALARM *alarm) {
00444         if (_alarmCount == _alarmMax) {
00445                 return true;
00446         }
00447 
00448         // Figure out where it needs to be inserted
00449         uint8_t i;
00450         for (i = 0; i < _alarmCount; ++i) {
00451                 if (mhv_timestampLessThan(&(alarm->when), &(_alarms[i].when))) {
00452                         break;
00453                 }
00454         }
00455         // i now is the offset of where the timestamp should be inserted
00456         if (i < _alarmCount) {
00457                 memmove(_alarms + i + 1, _alarms + i, (_alarmCount - i) * sizeof(*_alarms));
00458                 memmove(_alarms + i, alarm, sizeof(*_alarms));
00459         } else {
00460                 memcpy(&(_alarms[_alarmCount]), alarm, sizeof(*_alarms));
00461         }
00462 
00463         _alarmCount++;
00464 
00465         return false;
00466 }
00467 
00474 void MHV_RTC::handleEvents(void) {
00475         uint8_t i;
00476         MHV_TIMESTAMP timestamp;
00477         current(&timestamp);
00478 
00479         // Run any events pending
00480         for (i = 0; i < _alarmCount && mhv_timestampGreaterThanOrEqual(&timestamp, &(_alarms[i].when));
00481                         i++, current(&timestamp)) {
00482                 _alarms[i].listener->alarm(&(_alarms[i]));
00483 
00484                 // Repeat the event if necessary
00485                 if (0 != _alarms[i].repeat.milliseconds || 0 != _alarms[i].repeat.timestamp) {
00486                         current(&(_alarms[i].when));
00487                         mhv_timestampIncrement(&(_alarms[i].when), &(_alarms[i].repeat));
00488                         addAlarm(&(_alarms[i]));
00489                 }
00490         }
00491 
00492         if (0 == i) {
00493                 return;
00494         }
00495 
00496         // Shift remaining events down
00497         if (i < _alarmCount) {
00498                 memmove(_alarms, &(_alarms[i]), (_alarmCount - i) * sizeof(*_alarms));
00499         }
00500 
00501         _alarmCount -= i;
00502 }
00503 
00508 uint8_t MHV_RTC::alarmsPending() {
00509         return _alarmCount;
00510 }
00511 
00516  void MHV_RTC::removeAlarm(MHV_AlarmListener *listener) {
00517         for (uint8_t i = 0; i < _alarmCount; i++) {
00518                 if (_alarms[i].listener == listener) {
00519                         // Shift remaining events down
00520                         if (i < _alarmCount) {
00521                                 memmove(_alarms, &(_alarms[i]), (_alarmCount - i) * sizeof(*_alarms));
00522                         }
00523 
00524                         _alarmCount -= i;
00525                 }
00526         }
00527 }