MHVLib  20111227
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 
00166 MHV_RTC::MHV_RTC(MHV_ALARM eventBuffer[], uint8_t eventCount, int16_t timezone) :
00167                 _alarms(eventBuffer),
00168                 _alarmCount(0),
00169                 _alarmMax(eventCount),
00170                 _milliseconds(0),
00171                 _ticks(0),
00172                 _ticksPerMillisecond(1),
00173                 _tzOffset(timezone) {
00174         mhv_memClear (eventBuffer, sizeof(*eventBuffer), eventCount);
00175 }
00176 
00182 MHV_RTC::MHV_RTC(MHV_ALARM eventBuffer[], uint8_t eventCount) :
00183                 _alarms(eventBuffer),
00184                 _alarmCount(0),
00185                 _alarmMax(eventCount),
00186                 _milliseconds(0),
00187                 _ticks(0),
00188                 _ticksPerMillisecond(1),
00189                 _tzOffset(0) {
00190         mhv_memClear (eventBuffer, sizeof(*eventBuffer), eventCount);
00191 }
00192 
00197 void MHV_RTC::synchronise(MHV_Timer8 &timer) {
00198         uint32_t ticksPerMillisecond = F_CPU / timer.getPrescalerMultiplier() / (timer.getTop() + 1) / 1000;
00199         _ticksPerMillisecond = ticksPerMillisecond;
00200 }
00201 
00207 void MHV_RTC::setTime(uint32_t timestamp, uint16_t milliseconds) {
00208         do {
00209                 _milliseconds = milliseconds;
00210                 _timestamp = timestamp;
00211         } while (milliseconds != _milliseconds); // Retry if the value changed while updating
00212 }
00213 
00218 void MHV_RTC::setTime(MHV_TIMESTAMP *timestamp) {
00219         do {
00220                 _milliseconds = timestamp->milliseconds;
00221                 _timestamp = timestamp->timestamp;
00222         } while (timestamp->milliseconds != _milliseconds); // Retry if the value changed while updating
00223 }
00224 
00228 inline void MHV_RTC::incrementMilliseconds() {
00229         _milliseconds++;
00230 
00231         if (_milliseconds > 999) {
00232                 _milliseconds = 0;
00233                 _timestamp++;
00234         }
00235 }
00236 
00240 void MHV_RTC::tick(void) {
00241         if (++_ticks != _ticksPerMillisecond) {
00242                 return;
00243         }
00244 
00245         _ticks = 0;
00246 
00247         incrementMilliseconds();
00248 }
00249 
00253 void MHV_RTC::tick1ms(void) {
00254         incrementMilliseconds();
00255 }
00256 
00260 void MHV_RTC::tickAndRunEvents(void) {
00261         if (++_ticks != _ticksPerMillisecond) {
00262                 return;
00263         }
00264 
00265         _ticks = 0;
00266 
00267         incrementMilliseconds();
00268 
00269         handleEvents();
00270 }
00271 
00275 void MHV_RTC::tick1msAndRunEvents(void) {
00276         incrementMilliseconds();
00277 }
00278 
00279 
00283 void MHV_RTC::current(MHV_TIMESTAMP *timestamp) {
00284         do {
00285                 timestamp->milliseconds = _milliseconds;
00286                 timestamp->timestamp = _timestamp;
00287         } while (timestamp->milliseconds != _milliseconds); // Retry if the value changed while updating
00288 }
00289 
00290 
00296 void MHV_RTC::elapsed(MHV_TIMESTAMP *since, MHV_TIMESTAMP *elapsed) {
00297         MHV_TIMESTAMP currentTimestamp;
00298 
00299         current(&currentTimestamp);
00300 
00301         elapsed->timestamp = currentTimestamp.timestamp - since->timestamp;
00302         if (currentTimestamp.milliseconds > since->milliseconds) {
00303                 elapsed->milliseconds = currentTimestamp.milliseconds - since->milliseconds;
00304         } else if (elapsed->timestamp) {
00305                 elapsed->timestamp--;
00306                 elapsed->milliseconds = 1000 + currentTimestamp.milliseconds - since->milliseconds;
00307         } else {
00308                 elapsed->milliseconds = 0;
00309         }
00310 }
00311 
00312 // Used in toTime: Cumulative totals at the end of the month in a normal year
00313 const uint32_t mhv_secondsFromYearStart[] PROGMEM = {
00314                  2678400,       // Jan
00315                  5097600,       // Feb
00316                  7776000,       // Mar
00317                 10368000,       // Apr
00318                 13046400,       // May
00319                 15638400,       // Jun
00320                 18316800,       // Jul
00321                 20995200,       // Aug
00322                 23587200,       // Sep
00323                 26265600,       // Oct
00324                 28857600,       // Nov
00325                 31536000        // Dec
00326 };
00327 
00328 
00334 void MHV_RTC::toTime(MHV_TIME *to, MHV_TIMESTAMP *from) {
00335         uint16_t year = 1970;
00336         bool leapYear = false;
00337         uint32_t seconds = from->timestamp + _tzOffset;
00338 
00339         to->timezone = _tzOffset;
00340 
00341         uint32_t secondsThisYear = 365 * 86400;
00342         uint32_t secondsSoFar = 0;
00343         while (seconds > secondsThisYear) {
00344                 seconds -= secondsThisYear;
00345                 secondsSoFar += secondsThisYear;
00346 
00347                 if ((mhv_isLeapYear(++year))) {
00348                         secondsThisYear = 366 * 86400;
00349                 } else {
00350                         secondsThisYear = 365 * 86400;
00351                 }
00352         }
00353         to->year = year;
00354 
00355         uint8_t month;
00356         leapYear = mhv_isLeapYear(year);
00357         for (month = 0; month < sizeof(mhv_secondsFromYearStart); month++) {
00358                 if (leapYear) {
00359                         if (seconds < pgm_read_dword(&mhv_secondsFromYearStart[month] + (1 == month) ? 86400 : 0)) {
00360                                 break;
00361                         }
00362                 } else {
00363                         if (seconds < pgm_read_dword(&mhv_secondsFromYearStart[month])) {
00364                                 break;
00365                         }
00366                 }
00367         }
00368         to->yearday = seconds / 86400;
00369 
00370         if (month) {
00371                 seconds -= pgm_read_dword(&mhv_secondsFromYearStart[month - 1]);
00372         }
00373 
00374         if (leapYear && month > 1) {
00375                 seconds -= 86400;
00376         }
00377 
00378         // Increment so we start numbering from 1
00379         to->month = (MHV_MONTH)(month + 1);
00380 
00381         uint32_t day = seconds / 86400;
00382         to->day = (uint8_t)day;
00383         seconds -= day * 86400;
00384         to->day += 1; // Increment so we start numbering from 1
00385 
00386         uint32_t hours = seconds / 3600;
00387         to->hours = (uint8_t)hours;
00388         seconds -= hours * 3600;
00389 
00390         uint32_t minutes = seconds / 60;
00391         to->minutes = (uint8_t)minutes;
00392         seconds -= minutes * 60;
00393 
00394         to->seconds = seconds;
00395         to->milliseconds = from->milliseconds;
00396 }
00397 
00403 void MHV_RTC::toTimestamp(MHV_TIMESTAMP *to, MHV_TIME *from) {
00404         to->milliseconds = from->milliseconds;
00405 
00406         uint32_t seconds = from->seconds;
00407 
00408         seconds += (uint32_t)from->minutes * 60;
00409 
00410         seconds += (uint32_t)from->hours * 3600;
00411 
00412         if (from->yearday) {
00413                 seconds += from->yearday * 86400;
00414         } else {
00415                 seconds += (from->day - 1) * 86400;
00416                 switch (from->month) {
00417                 case MHV_JANUARY:
00418                         break;
00419                 case MHV_FEBRUARY:
00420                         seconds += 5097600;
00421                         break;
00422                 default:
00423                         seconds += pgm_read_dword(&mhv_secondsFromYearStart[(uint8_t)from->month - 2]);
00424                         if (mhv_isLeapYear(from->year)) {
00425                                 seconds += 86400;
00426                         }
00427                         break;
00428                 }
00429         }
00430 
00431         for (uint16_t year = from->year - 1; year >= 1970; year--) {
00432                 seconds += 365 * 86400;
00433                 if (mhv_isLeapYear(year)) {
00434                         seconds += 86400;
00435                 }
00436         }
00437 
00438         to->timestamp = seconds - _tzOffset;
00439 }
00440 
00441 
00450 bool MHV_RTC::addAlarm(MHV_ALARM *alarm) {
00451         if (_alarmCount == _alarmMax) {
00452                 return true;
00453         }
00454 
00455         // Figure out where it needs to be inserted
00456         uint8_t i;
00457         for (i = 0; i < _alarmCount; ++i) {
00458                 if (mhv_timestampLessThan(&(alarm->when), &(_alarms[i].when))) {
00459                         break;
00460                 }
00461         }
00462         // i now is the offset of where the timestamp should be inserted
00463         if (i < _alarmCount) {
00464                 memmove(_alarms + i + 1, _alarms + i, (_alarmCount - i) * sizeof(*_alarms));
00465                 memmove(_alarms + i, alarm, sizeof(*_alarms));
00466         } else {
00467                 memcpy(&(_alarms[_alarmCount]), alarm, sizeof(*_alarms));
00468         }
00469 
00470         _alarmCount++;
00471 
00472         return false;
00473 }
00474 
00481 void MHV_RTC::handleEvents(void) {
00482         uint8_t i;
00483         MHV_TIMESTAMP timestamp;
00484         current(&timestamp);
00485 
00486         // Run any events pending
00487         for (i = 0; i < _alarmCount && mhv_timestampGreaterThanOrEqual(&timestamp, &(_alarms[i].when));
00488                         i++, current(&timestamp)) {
00489                 _alarms[i].listener->alarm(_alarms[i]);
00490 
00491                 // Repeat the event if necessary
00492                 if (0 != _alarms[i].repeat.milliseconds || 0 != _alarms[i].repeat.timestamp) {
00493                         current(&(_alarms[i].when));
00494                         mhv_timestampIncrement(&(_alarms[i].when), &(_alarms[i].repeat));
00495                         addAlarm(&(_alarms[i]));
00496                 }
00497         }
00498 
00499         if (0 == i) {
00500                 return;
00501         }
00502 
00503         // Shift remaining events down
00504         if (i < _alarmCount) {
00505                 memmove(_alarms, &(_alarms[i]), (_alarmCount - i) * sizeof(*_alarms));
00506         }
00507 
00508         _alarmCount -= i;
00509 }
00510 
00515 uint8_t MHV_RTC::alarmsPending() {
00516         return _alarmCount;
00517 }
00518 
00523 void MHV_RTC::removeAlarm(MHV_AlarmListener &listener) {
00524         for (uint8_t i = 0; i < _alarmCount; i++) {
00525                 if (_alarms[i].listener == &listener) {
00526                         // Shift remaining events down
00527                         if (i < _alarmCount) {
00528                                 memmove(_alarms, &(_alarms[i]), (_alarmCount - i) * sizeof(*_alarms));
00529                         }
00530 
00531                         _alarmCount -= i;
00532                 }
00533         }
00534 }
00535 
00540 void MHV_RTC::removeAlarm(MHV_AlarmListener *listener) {
00541         for (uint8_t i = 0; i < _alarmCount; i++) {
00542                 if (_alarms[i].listener == listener) {
00543                         // Shift remaining events down
00544                         if (i < _alarmCount) {
00545                                 memmove(_alarms, &(_alarms[i]), (_alarmCount - i) * sizeof(*_alarms));
00546                         }
00547 
00548                         _alarmCount -= i;
00549                 }
00550         }
00551 }