Allows easy usage of the LPC1768 RTC interrupts

Dependents:   Mini_DK_clk MakerBotServer GT-ACQUAPLUS_Consumo GT-ACQUAPLUS_Eficiencia ... more

The RTC library allows easy access to the LPC1768s interrupt sources on its RTC. There are two different interrupt sources: periodic interrupts, for example every new second, hour, etc, and an alarm, which is called at a specified time.

Time initialization

This library only allows easy usage of the RTC interrupts. You have to initialize the time functions (so the RTC) the normal way specified here: http://mbed.org/handbook/Time?action=view&revision=11592

Hello World!

#include "mbed.h"
#include "RTC.h"

DigitalOut led(LED1);

void ledFunction( void )
{
    led = 1;
    RTC::detach(RTC::Second);
}

void displayFunction( void )
{
    time_t seconds = time(NULL);
    printf("%s", ctime(&seconds));
}

void alarmFunction( void )
{
    error("Not most useful alarm function");
}

int main()
{
    set_time(1256729737); // Set time to Wed, 28 Oct 2009 11:35:37

    tm t = RTC::getDefaultTM();
    t.tm_sec = 5;
    t.tm_min = 36;

    RTC::alarm(&alarmFunction, t);
    RTC::attach(&displayFunction, RTC::Second);
    RTC::attach(&ledFunction, RTC::Minute);

    while(1);
}

Periodic interrupts

Periodic interrups can be attached by using:

RTC::attach([function], [TimeUnit]);

The TimeUnit specifies which unit should increase for the function to be called. This function is useful if you are making for example a clock: You can simply connect the display update to an RTC interrupt. Of course you can do something similar by using timer objects, but they aren't timed exactly correct, and it is nicer to do it directly on the RTC.

Alarm function

The LPC1768's RTC also allows for one alarm to be activated. The alarm goes off the first time there is a compare match on a specified time and the current time. All fields of the normal C tm structure are supported (http://www.cplusplus.com/reference/ctime/tm/), set a field at -1 for it to be ignored. The RTC::getDefaultTM() function helps with that, it returns a tm structure with every field initialized to -1, so don't care.

So if you want to make an alarm that gets called every monday, every 5 minutes and 30 seconds after the hour, you would use:

tm t = RTC::getDefaultTM();
t.tm_sec = 30;     //30 seconds
t.tm_min = 5;      //5 minute
t.tm_wday = 1;     //monday
RTC::alarm([yourFunction], t);

Attaching member functions

For compactness of the documentation attaching member functions of objects isn't included. However it works exactly the same as for example attaching one to a Ticker object: http://mbed.org/users/mbed_official/code/mbed/docs/63cdd78b2dc1/classmbed_1_1Ticker.html#af92f41ff11906b1f96fa6bbe0b17aa29

Disclaimer

Believe it or not, but I didn't test every possible combination of alarm settings / interrupts. So especially for the larger timescales there is an increased chance on bugs.

Revision:
1:be9d058ee5c7
Parent:
0:39767ffe05e6
diff -r 39767ffe05e6 -r be9d058ee5c7 RTC.cpp
--- a/RTC.cpp	Wed Dec 05 21:03:44 2012 +0000
+++ b/RTC.cpp	Fri Dec 07 20:50:43 2012 +0000
@@ -5,8 +5,25 @@
 
 bool RTC::initialRun = true;
 
+
 void RTC::attach(void (*function)(void), TimeUnit interval)
 {
+    //Set the function pointer
+    attachCB[interval].attach(function);
+    _attach(interval);
+}
+
+template<typename T>
+void RTC::attach(T *object, void (T::*member)(void), TimeUnit interval)
+{
+    //Set the function pointer
+    attachCB[interval].attach(object, member);
+    _attach(interval);
+}
+
+
+void RTC::_attach(TimeUnit interval)
+{
     //Disable IRQs, dont want them to happen while busy here
     NVIC_DisableIRQ(RTC_IRQn);
 
@@ -22,61 +39,76 @@
         LPC_RTC->ILR = 0x03;
     }
 
-    //Set the function pointer
-    attachCB[interval].attach(function);
-
+    //Set/reset correct interrupt source
+    switch (interval) {
+        case Second:
+            LPC_RTC->CIIR |= 1;
+            break;
+        case Minute:
+            LPC_RTC->CIIR |= 2;
+            break;
+        case Hour:
+            LPC_RTC->CIIR |= 4;
+            break;
+        case Day:
+            LPC_RTC->CIIR |= 56;
+            break;
+        case Month:
+            LPC_RTC->CIIR |= 64;
+            break;
+        case Year:
+            LPC_RTC->CIIR |= 128;
+            break;
+    }
 
-    //Set/reset correct interrupt source
-    if (function == NULL) {
-        switch (interval) {
-            case Second:
-                LPC_RTC->CIIR &= ~1;
-                break;
-            case Minute:
-                LPC_RTC->CIIR &= ~2;
-                break;
-            case Hour:
-                LPC_RTC->CIIR &= ~4;
-                break;
-            case Day:
-                LPC_RTC->CIIR &= ~56;
-                break;
-            case Month:
-                LPC_RTC->CIIR &= ~64;
-                break;
-            case Year:
-                LPC_RTC->CIIR &= ~128;
-                break;
-        }
-    } else {
-        switch (interval) {
-            case Second:
-                LPC_RTC->CIIR |= 1;
-                break;
-            case Minute:
-                LPC_RTC->CIIR |= 2;
-                break;
-            case Hour:
-                LPC_RTC->CIIR |= 4;
-                break;
-            case Day:
-                LPC_RTC->CIIR |= 56;
-                break;
-            case Month:
-                LPC_RTC->CIIR |= 64;
-                break;
-            case Year:
-                LPC_RTC->CIIR |= 128;
-                break;
-        }
-    }
 
     //We can always enable IRQs, since if all IRQs are disabled by the user the RTC hardware will never raise its IRQ flag anyway
     NVIC_EnableIRQ(RTC_IRQn);
 }
 
+void RTC::detach(TimeUnit interval)
+{
+    switch (interval) {
+        case Second:
+            LPC_RTC->CIIR &= ~1;
+            break;
+        case Minute:
+            LPC_RTC->CIIR &= ~2;
+            break;
+        case Hour:
+            LPC_RTC->CIIR &= ~4;
+            break;
+        case Day:
+            LPC_RTC->CIIR &= ~56;
+            break;
+        case Month:
+            LPC_RTC->CIIR &= ~64;
+            break;
+        case Year:
+            LPC_RTC->CIIR &= ~128;
+            break;
+    }
+    attachCB[interval].attach(NULL);
+}
 
-void RTC::alarm(void (*function)(void), tm time)
+
+void RTC::alarm(void (*function)(void), tm alarmTime)
+{
+    //Set the function pointer
+    alarmCB.attach(function);
+    _alarm(alarmTime);
+}
+
+template<typename T>
+void RTC::alarm(T *object, void (T::*member)(void), tm alarmTime)
+{
+    //Set the function pointer
+    alarmCB.attach(object, member);
+    _alarm(alarmTime);
+}
+
+
+void RTC::_alarm(tm alarmTime)
 {
     //Disable IRQs, dont want them to happen while busy here
     NVIC_DisableIRQ(RTC_IRQn);
@@ -93,53 +125,69 @@
         LPC_RTC->ILR = 0x03;
     }
 
-    //Set the function pointer
-    alarmCB.attach(function);
-    
     //Set the alarm register
-    if ((time.tm_sec>=0) && (time.tm_sec<60)) {
-        LPC_RTC->ALSEC = time.tm_sec;
+    if ((alarmTime.tm_sec>=0) && (alarmTime.tm_sec<60)) {
+        LPC_RTC->ALSEC = alarmTime.tm_sec;
         LPC_RTC->AMR &= ~1;
     } else
         LPC_RTC->AMR |= 1;
 
-    if ((time.tm_min>=0) && (time.tm_min<60)) {
-        LPC_RTC->ALMIN = time.tm_min;
+    if ((alarmTime.tm_min>=0) && (alarmTime.tm_min<60)) {
+        LPC_RTC->ALMIN = alarmTime.tm_min;
         LPC_RTC->AMR &= ~2;
     } else
         LPC_RTC->AMR |= 2;
 
-    if ((time.tm_hour>=0) && (time.tm_hour<24)) {
-        LPC_RTC->ALHOUR = time.tm_hour;
+    if ((alarmTime.tm_hour>=0) && (alarmTime.tm_hour<24)) {
+        LPC_RTC->ALHOUR = alarmTime.tm_hour;
         LPC_RTC->AMR &= ~4;
     } else
         LPC_RTC->AMR |= 4;
 
-    if ((time.tm_mday>=1) && (time.tm_mday<32)) {
-        LPC_RTC->ALDOM = time.tm_mday;
+    if ((alarmTime.tm_mday>=1) && (alarmTime.tm_mday<32)) {
+        LPC_RTC->ALDOM = alarmTime.tm_mday;
         LPC_RTC->AMR &= ~8;
     } else
-        LPC_RTC->AMR |= 8;        
-        
-    if ((time.tm_mon>=0) && (time.tm_mon<12)) {
-        LPC_RTC->ALMON = time.tm_mon + 1;   //Different definitions
+        LPC_RTC->AMR |= 8;
+
+    if ((alarmTime.tm_wday>=0) && (alarmTime.tm_wday<7)) {
+        LPC_RTC->ALDOW = alarmTime.tm_wday;
+        LPC_RTC->AMR &= ~16;
+    } else
+        LPC_RTC->AMR |= 16;
+
+    if ((alarmTime.tm_yday>0) && (alarmTime.tm_yday<367)) {
+        LPC_RTC->ALDOY = alarmTime.tm_yday;
+        LPC_RTC->AMR &= ~32;
+    } else
+        LPC_RTC->AMR |= 32;
+
+    if ((alarmTime.tm_mon>=0) && (alarmTime.tm_mon<12)) {
+        LPC_RTC->ALMON = alarmTime.tm_mon + 1;   //Different definitions
         LPC_RTC->AMR &= ~64;
     } else
-        LPC_RTC->AMR |= 64;    
+        LPC_RTC->AMR |= 64;
 
-    if ((time.tm_year>=0) && (time.tm_year<1000)) {
-        LPC_RTC->ALYEAR = time.tm_year + 1900;   //Different definitions
+    if ((alarmTime.tm_year>=0) && (alarmTime.tm_year<1000)) {
+        LPC_RTC->ALYEAR = alarmTime.tm_year + 1900;   //Different definitions
         LPC_RTC->AMR &= ~128;
     } else
         LPC_RTC->AMR |= 128;
-        
+
+    //DOY and DOW register normally not set
+    time_t t = time(NULL);
+    LPC_RTC->DOY = localtime(&t)->tm_yday+1;
+    LPC_RTC->DOW = localtime(&t)->tm_wday;
+
     //We can always enable IRQs, since if all IRQs are disabled by the user the RTC hardware will never raise its IRQ flag anyway
     NVIC_EnableIRQ(RTC_IRQn);
 }
 
-void RTC::alarmOff( void ) {
+void RTC::alarmOff( void )
+{
     LPC_RTC->AMR = 255;
-    }
+    alarmCB.attach(NULL);
+}
 
 
 void RTC::IRQHandler( void )
@@ -180,4 +228,18 @@
 
     //Reset interrupt status
     LPC_RTC->ILR = 0x03;
-}
\ No newline at end of file
+}
+
+tm RTC::getDefaultTM( void ) {
+    struct tm t;
+    t.tm_sec = -1;   
+    t.tm_min = -1;    
+    t.tm_hour = -1;   
+    t.tm_mday = -1;
+    t.tm_wday = -1;
+    t.tm_yday = -1;   
+    t.tm_mon = -1;    
+    t.tm_year = -1; 
+    
+    return t;
+    }
\ No newline at end of file