Alarm library for use with mbed.

Revision:
0:33dddd0644c5
Child:
1:f4c7f13e5553
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AlarmTimeDate.cpp	Sat Oct 30 23:41:22 2010 +0000
@@ -0,0 +1,262 @@
+#include "mbed.h"
+#include "AlarmTimeDate.h"
+
+static AlarmTimeDate* list = NULL;
+
+void AlarmTimeDate::PrintAlarm() {
+    if (!iTime) printf("Alarm %x is not set\n",this);
+    else printf("Alarm %x will expire next at %s\n",this,ctime(&iTime));
+}
+
+void AlarmTimeDate::PrintAlarms() {
+    AlarmTimeDate* p = list;
+    while (p) {
+        p->PrintAlarm();
+        p = p->iNext;
+    }
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr)
+        :iTime(0),iFn(aFn),iPtr(aPtr) {
+    Queue();
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr, int aHours,
+                             int aMins, int aSecs, int aDayM, int aMon, int aYear)
+        :iFn(aFn),iPtr(aPtr) {
+    struct tm sectm;
+    sectm.tm_sec = aSecs;
+    sectm.tm_min = aMins;
+    sectm.tm_hour = aHours;
+    sectm.tm_mday = aDayM;
+    sectm.tm_mon = aMon-1;
+    sectm.tm_year = aYear-1970;
+    iTime = mktime(&sectm);
+    Queue();
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr, int aHours, int aMins,
+                             int aSecs, bool aSkipWeekends)
+        :iFn(aFn),iPtr(aPtr) {
+    // current time
+    time_t seconds = time(NULL);
+    struct tm * sectm = localtime(&seconds);
+    sectm->tm_sec = aSecs;
+    sectm->tm_min = aMins;
+    sectm->tm_hour = aHours;
+    time_t secs2 = mktime(sectm);
+    if (secs2<=seconds) {
+        secs2+=24*3600; // move forward one day if alarm is set for hours:mins:secs before current time
+        sectm = localtime(&secs2);
+    }
+    if (aSkipWeekends && !sectm->tm_wday) { // set on a Sunday but skipping weekends move to Monday
+        secs2+=24*3600; // move forward one day if alarm is set for hours:mins:secs before current time
+        sectm = localtime(&secs2);
+    }
+    if (aSkipWeekends && sectm->tm_wday==6) { // set on a Saturday but skipping weekends move to Monday
+        secs2+=2*24*3600; // move forward one day if alarm is set for hours:mins:secs before current time
+        sectm = localtime(&secs2);
+    }
+    iTime = mktime(sectm);
+    if (aSkipWeekends) SetRepeat(EDailyNoWeeked);
+    else SetRepeat(EDaily);
+    Queue();
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr, int aHours, int aMins,
+                             int aSecs, int aDayW, bool aFortNightly)
+        :iFn(aFn),iPtr(aPtr) {
+    // current time
+    time_t seconds = time(NULL);
+    struct tm * sectm = localtime(&seconds);
+    sectm->tm_sec = aSecs;
+    sectm->tm_min = aMins;
+    sectm->tm_hour = aHours;
+    sectm->tm_wday = aDayW;
+    time_t secs2 = mktime(sectm);
+    if (secs2<=seconds && !aFortNightly) {
+        secs2+=7*24*3600; // move forward one week if alarm is set for hours:mins:secs:week day before current time
+        sectm = localtime(&secs2);
+    }
+    if (secs2<=seconds && !aFortNightly) {
+        secs2+=14*24*3600; // move forward one fornight if alarm is set for hours:mins:secs:week day before current time
+        sectm = localtime(&secs2);
+    }
+    iTime = mktime(sectm);
+    if (aFortNightly) SetRepeat(EFortNightly);
+    else SetRepeat(EWeekly);
+    Queue();
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr, int aHours, int aMins,
+                             int aSecs, int aDayM)
+        :iFn(aFn),iPtr(aPtr) {
+    // current time
+    time_t seconds = time(NULL);
+    struct tm * sectm = localtime(&seconds);
+    sectm->tm_sec = aSecs;
+    sectm->tm_min = aMins;
+    sectm->tm_hour = aHours;
+    sectm->tm_mday = aDayM;
+    time_t secs2 = mktime(sectm);
+    if (secs2<=seconds) {
+        if (sectm->tm_mon==11) {
+            sectm->tm_mon = 0;
+            sectm->tm_year++;
+        } else {
+            sectm->tm_mon++;
+        }
+    }
+    iTime = mktime(sectm);
+    SetRepeat(EMonthly);
+    Queue();
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr, int aHours, int aMins,
+                             int aSecs, int aDayM, int aMon)
+        :iFn(aFn),iPtr(aPtr) {
+    // current time
+    time_t seconds = time(NULL);
+    struct tm * sectm = localtime(&seconds);
+    sectm->tm_sec = aSecs;
+    sectm->tm_min = aMins;
+    sectm->tm_hour = aHours;
+    sectm->tm_mday = aDayM;
+    sectm->tm_mon = aMon-1;
+    time_t secs2 = mktime(sectm);
+    if (secs2<=seconds) { // if programmed late in the year
+        sectm->tm_year++;
+    }
+    iTime = mktime(sectm);
+    SetRepeat(EYearly);
+    Queue();
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr, struct tm* aTm )
+        :iFn(aFn),iPtr(aPtr) {
+    iTime = mktime(aTm);
+    Queue();
+}
+
+AlarmTimeDate::AlarmTimeDate(AlarmCbFn aFn, void* aPtr, time_t aSec )
+        :iTime(aSec),iFn(aFn),iPtr(aPtr) {
+    Queue();
+}
+
+AlarmTimeDate::~AlarmTimeDate() {
+    DeQueue();
+    Tick(NULL,NULL);
+}
+
+void AlarmTimeDate::Queue() {
+    iNext = list;
+    list = this;
+    Tick(NULL,NULL);
+}
+
+void AlarmTimeDate::DeQueue() {
+    AlarmTimeDate* p = list;
+    AlarmTimeDate* prev = NULL;
+    while (p) {
+        if (p==this) {
+            if (!prev) list = iNext;
+            else {
+                prev->iNext = iNext;
+            }
+            break;
+        }
+        prev = p;
+        p = p->iNext;
+    }
+}
+
+void AlarmTimeDate::Set() {
+    struct tm* localt = localtime(&iTime);
+    LPC_RTC->ALSEC = localt->tm_sec;
+    LPC_RTC->ALMIN = localt->tm_min;
+    LPC_RTC->ALHOUR = localt->tm_hour;
+    LPC_RTC->ALDOM = localt->tm_mday;
+    LPC_RTC->ALMON = localt->tm_mon+1;
+    LPC_RTC->ALYEAR = localt->tm_year+1900;
+    LPC_RTC->AMR = (uint8_t)~0xcfu; // match those above
+}
+
+void AlarmTimeDate::Expire() {
+    struct tm * sectm;
+    switch (Repeat()) {
+        case EDaily:
+            iTime+=24*3600;
+            break;
+        case EDailyNoWeeked:
+            sectm = localtime(&iTime);
+            if (sectm->tm_wday==6)
+                iTime+=7*24*3600;  // its friday, skip it to sunday
+            iTime+=24*3600;
+        case EWeekly:
+            iTime+=7*24*3600;
+            break;
+        case EFortNightly:
+            iTime+=14*24*3600;
+            break;
+        case EMonthly:
+            sectm = localtime(&iTime);
+            if (++sectm->tm_mon==32) {
+                sectm->tm_mon=1;
+                sectm->tm_year++;
+            }
+            iTime = mktime(sectm);
+            break;
+        case EYearly:
+            sectm = localtime(&iTime);
+            sectm->tm_year++;
+            iTime = mktime(sectm);
+            break;
+        default:
+            iTime = 0;
+    }
+    printf( "Calling %x\n",iFn);
+    iFn(iPtr);
+}
+
+void AlarmTimeDate::Init() {
+    LPC_RTC->CIIR = 0;
+    LPC_RTC->ILR = 0x3;
+    LPC_RTC->AMR = 0xffu;
+    NVIC_SetVector(RTC_IRQn, (uint32_t)&RTCISR);
+    NVIC_EnableIRQ(RTC_IRQn);
+}
+
+void AlarmTimeDate::RTCISR() {
+    LPC_RTC->ILR = 0x3;
+}
+
+void AlarmTimeDate::Tick(SleepFn aFn,void* aParam) {
+    AlarmTimeDate* p = list;
+    AlarmTimeDate* toSet = NULL;
+    time_t seconds = time(NULL);
+    time_t toSetTime = 0;
+    while (p) {
+        if (p->iTime && p->iTime <= seconds) p->Expire();
+        p = p->iNext;
+    }
+    seconds = time(NULL);
+    p = list;
+    while (p) {
+        if (p->iTime) {
+            if (!toSet) {
+                toSet = p;
+                toSetTime = p->iTime;
+            } else {
+                if (p->iTime < toSetTime) {
+                    toSet = p;
+                    toSetTime = p->iTime;
+                }
+            }
+        };
+        p = p->iNext;
+    }
+    if (toSet) {
+        toSet->Set();
+    }
+    if (aFn) aFn(aParam);
+}