|
MHVLib
20111227
An efficiency oriented runtime library for AVR microcontrollers
|
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(¤tTimestamp); 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(×tamp); 00485 00486 // Run any events pending 00487 for (i = 0; i < _alarmCount && mhv_timestampGreaterThanOrEqual(×tamp, &(_alarms[i].when)); 00488 i++, current(×tamp)) { 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 }