MHVLib
20111011
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 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(¤tTimestamp); 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(×tamp); 00478 00479 // Run any events pending 00480 for (i = 0; i < _alarmCount && mhv_timestampGreaterThanOrEqual(×tamp, &(_alarms[i].when)); 00481 i++, current(×tamp)) { 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 }