Alarm library for use with mbed.

AlarmTimeDate.cpp

Committer:
charlesgt
Date:
2010-11-15
Revision:
2:23e65cacea5a
Parent:
1:f4c7f13e5553

File content as of revision 2:23e65cacea5a:

#include "mbed.h"
#include "AlarmTimeDate.h"

volatile uint8_t AlarmTimeDate::iRTCIsSet = 0u;
volatile uint8_t AlarmTimeDate::iIsrCount = 0u;
volatile uint8_t AlarmTimeDate::iPad0 = 0u;
volatile uint8_t AlarmTimeDate::iPad1 = 0u;

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() {
    time_t seconds = time(NULL);
    if (iTime <= seconds) seconds++;
    else seconds = iTime;
    struct tm* localt = localtime(&seconds);
    __disable_irq();
    iRTCIsSet = 1;
    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
    __enable_irq();
}

void AlarmTimeDate::Expire() {
    time_t expiry = time(NULL);
    iFn(iPtr);
    struct tm * sectm;
    switch (Repeat()) {
        case EDaily:
            iTime+=24*3600;
            break;
        case EDailyNoWeeked:
            sectm = localtime(&iTime);
            if (sectm->tm_wday==6)
                iTime+=2*24*3600;  // its friday, skip it to sunday
            iTime+=24*3600;
            break;
        case EWeekly:
            iTime+=7*24*3600;
            break;
        case EFortNightly:
            iTime+=14*24*3600;
            break;
        case EMonthly:
            sectm = localtime(&iTime);
            if (++sectm->tm_mon==12) {
                sectm->tm_mon=0;
                sectm->tm_year++;
            }
            iTime = mktime(sectm);
            break;
        case EYearly:
            sectm = localtime(&iTime);
            sectm->tm_year++;
            iTime = mktime(sectm);
            break;
        default:
            // could have been reset
            if ( expiry >= iTime ) iTime = 0;
    }
}

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;
    iRTCIsSet = 0;
    iIsrCount++;
    printf("I:%d\n",iIsrCount);
}

void AlarmTimeDate::Tick(SleepFn aFn,void* aParam) {
redo:
    printf("ticking %d\n",++iPad0);
    if (aFn) printf("Sleeping at %x\n",time(NULL));
    if (aFn && iRTCIsSet) {
        aFn(aParam);
    }
    __disable_irq();
    if (iIsrCount) iIsrCount--;
    __enable_irq();
    if (aFn ) printf("waking at %x\n",time(NULL));
    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;
    }
    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) {
        printf("found something to set %x\n",toSet->iTime);
        toSet->Set();
    }

    printf("ticking done %d:%d\n",--iPad0,iIsrCount);
    if (iIsrCount) goto redo;
}