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.

Committer:
Sissors
Date:
Fri Dec 07 20:50:43 2012 +0000
Revision:
1:be9d058ee5c7
Parent:
0:39767ffe05e6
v1.0;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Sissors 0:39767ffe05e6 1 #include "RTC.h"
Sissors 0:39767ffe05e6 2
Sissors 0:39767ffe05e6 3 FunctionPointer RTC::attachCB[6];
Sissors 0:39767ffe05e6 4 FunctionPointer RTC::alarmCB;
Sissors 0:39767ffe05e6 5
Sissors 0:39767ffe05e6 6 bool RTC::initialRun = true;
Sissors 0:39767ffe05e6 7
Sissors 1:be9d058ee5c7 8
Sissors 0:39767ffe05e6 9 void RTC::attach(void (*function)(void), TimeUnit interval)
Sissors 0:39767ffe05e6 10 {
Sissors 1:be9d058ee5c7 11 //Set the function pointer
Sissors 1:be9d058ee5c7 12 attachCB[interval].attach(function);
Sissors 1:be9d058ee5c7 13 _attach(interval);
Sissors 1:be9d058ee5c7 14 }
Sissors 1:be9d058ee5c7 15
Sissors 1:be9d058ee5c7 16 template<typename T>
Sissors 1:be9d058ee5c7 17 void RTC::attach(T *object, void (T::*member)(void), TimeUnit interval)
Sissors 1:be9d058ee5c7 18 {
Sissors 1:be9d058ee5c7 19 //Set the function pointer
Sissors 1:be9d058ee5c7 20 attachCB[interval].attach(object, member);
Sissors 1:be9d058ee5c7 21 _attach(interval);
Sissors 1:be9d058ee5c7 22 }
Sissors 1:be9d058ee5c7 23
Sissors 1:be9d058ee5c7 24
Sissors 1:be9d058ee5c7 25 void RTC::_attach(TimeUnit interval)
Sissors 1:be9d058ee5c7 26 {
Sissors 0:39767ffe05e6 27 //Disable IRQs, dont want them to happen while busy here
Sissors 0:39767ffe05e6 28 NVIC_DisableIRQ(RTC_IRQn);
Sissors 0:39767ffe05e6 29
Sissors 0:39767ffe05e6 30 //Set the IRQ vector
Sissors 0:39767ffe05e6 31 NVIC_SetVector(RTC_IRQn, (uint32_t)&RTC::IRQHandler);
Sissors 0:39767ffe05e6 32
Sissors 0:39767ffe05e6 33 //If this is the first time it is called, delete all interrupt sources
Sissors 0:39767ffe05e6 34 //We need to do this because RTC unit isnt affected by system resets apparently
Sissors 0:39767ffe05e6 35 if (initialRun) {
Sissors 0:39767ffe05e6 36 LPC_RTC->CIIR = 0;
Sissors 0:39767ffe05e6 37 LPC_RTC->AMR = 255;
Sissors 0:39767ffe05e6 38 initialRun = false;
Sissors 0:39767ffe05e6 39 LPC_RTC->ILR = 0x03;
Sissors 0:39767ffe05e6 40 }
Sissors 0:39767ffe05e6 41
Sissors 1:be9d058ee5c7 42 //Set/reset correct interrupt source
Sissors 1:be9d058ee5c7 43 switch (interval) {
Sissors 1:be9d058ee5c7 44 case Second:
Sissors 1:be9d058ee5c7 45 LPC_RTC->CIIR |= 1;
Sissors 1:be9d058ee5c7 46 break;
Sissors 1:be9d058ee5c7 47 case Minute:
Sissors 1:be9d058ee5c7 48 LPC_RTC->CIIR |= 2;
Sissors 1:be9d058ee5c7 49 break;
Sissors 1:be9d058ee5c7 50 case Hour:
Sissors 1:be9d058ee5c7 51 LPC_RTC->CIIR |= 4;
Sissors 1:be9d058ee5c7 52 break;
Sissors 1:be9d058ee5c7 53 case Day:
Sissors 1:be9d058ee5c7 54 LPC_RTC->CIIR |= 56;
Sissors 1:be9d058ee5c7 55 break;
Sissors 1:be9d058ee5c7 56 case Month:
Sissors 1:be9d058ee5c7 57 LPC_RTC->CIIR |= 64;
Sissors 1:be9d058ee5c7 58 break;
Sissors 1:be9d058ee5c7 59 case Year:
Sissors 1:be9d058ee5c7 60 LPC_RTC->CIIR |= 128;
Sissors 1:be9d058ee5c7 61 break;
Sissors 1:be9d058ee5c7 62 }
Sissors 0:39767ffe05e6 63
Sissors 0:39767ffe05e6 64
Sissors 0:39767ffe05e6 65 //We can always enable IRQs, since if all IRQs are disabled by the user the RTC hardware will never raise its IRQ flag anyway
Sissors 0:39767ffe05e6 66 NVIC_EnableIRQ(RTC_IRQn);
Sissors 0:39767ffe05e6 67 }
Sissors 0:39767ffe05e6 68
Sissors 1:be9d058ee5c7 69 void RTC::detach(TimeUnit interval)
Sissors 1:be9d058ee5c7 70 {
Sissors 1:be9d058ee5c7 71 switch (interval) {
Sissors 1:be9d058ee5c7 72 case Second:
Sissors 1:be9d058ee5c7 73 LPC_RTC->CIIR &= ~1;
Sissors 1:be9d058ee5c7 74 break;
Sissors 1:be9d058ee5c7 75 case Minute:
Sissors 1:be9d058ee5c7 76 LPC_RTC->CIIR &= ~2;
Sissors 1:be9d058ee5c7 77 break;
Sissors 1:be9d058ee5c7 78 case Hour:
Sissors 1:be9d058ee5c7 79 LPC_RTC->CIIR &= ~4;
Sissors 1:be9d058ee5c7 80 break;
Sissors 1:be9d058ee5c7 81 case Day:
Sissors 1:be9d058ee5c7 82 LPC_RTC->CIIR &= ~56;
Sissors 1:be9d058ee5c7 83 break;
Sissors 1:be9d058ee5c7 84 case Month:
Sissors 1:be9d058ee5c7 85 LPC_RTC->CIIR &= ~64;
Sissors 1:be9d058ee5c7 86 break;
Sissors 1:be9d058ee5c7 87 case Year:
Sissors 1:be9d058ee5c7 88 LPC_RTC->CIIR &= ~128;
Sissors 1:be9d058ee5c7 89 break;
Sissors 1:be9d058ee5c7 90 }
Sissors 1:be9d058ee5c7 91 attachCB[interval].attach(NULL);
Sissors 1:be9d058ee5c7 92 }
Sissors 0:39767ffe05e6 93
Sissors 1:be9d058ee5c7 94
Sissors 1:be9d058ee5c7 95 void RTC::alarm(void (*function)(void), tm alarmTime)
Sissors 1:be9d058ee5c7 96 {
Sissors 1:be9d058ee5c7 97 //Set the function pointer
Sissors 1:be9d058ee5c7 98 alarmCB.attach(function);
Sissors 1:be9d058ee5c7 99 _alarm(alarmTime);
Sissors 1:be9d058ee5c7 100 }
Sissors 1:be9d058ee5c7 101
Sissors 1:be9d058ee5c7 102 template<typename T>
Sissors 1:be9d058ee5c7 103 void RTC::alarm(T *object, void (T::*member)(void), tm alarmTime)
Sissors 1:be9d058ee5c7 104 {
Sissors 1:be9d058ee5c7 105 //Set the function pointer
Sissors 1:be9d058ee5c7 106 alarmCB.attach(object, member);
Sissors 1:be9d058ee5c7 107 _alarm(alarmTime);
Sissors 1:be9d058ee5c7 108 }
Sissors 1:be9d058ee5c7 109
Sissors 1:be9d058ee5c7 110
Sissors 1:be9d058ee5c7 111 void RTC::_alarm(tm alarmTime)
Sissors 0:39767ffe05e6 112 {
Sissors 0:39767ffe05e6 113 //Disable IRQs, dont want them to happen while busy here
Sissors 0:39767ffe05e6 114 NVIC_DisableIRQ(RTC_IRQn);
Sissors 0:39767ffe05e6 115
Sissors 0:39767ffe05e6 116 //Set the IRQ vector
Sissors 0:39767ffe05e6 117 NVIC_SetVector(RTC_IRQn, (uint32_t)&RTC::IRQHandler);
Sissors 0:39767ffe05e6 118
Sissors 0:39767ffe05e6 119 //If this is the first time it is called, delete all interrupt sources
Sissors 0:39767ffe05e6 120 //We need to do this because RTC unit isnt affected by system resets apparently
Sissors 0:39767ffe05e6 121 if (initialRun) {
Sissors 0:39767ffe05e6 122 LPC_RTC->CIIR = 0;
Sissors 0:39767ffe05e6 123 LPC_RTC->AMR = 255;
Sissors 0:39767ffe05e6 124 initialRun = false;
Sissors 0:39767ffe05e6 125 LPC_RTC->ILR = 0x03;
Sissors 0:39767ffe05e6 126 }
Sissors 0:39767ffe05e6 127
Sissors 0:39767ffe05e6 128 //Set the alarm register
Sissors 1:be9d058ee5c7 129 if ((alarmTime.tm_sec>=0) && (alarmTime.tm_sec<60)) {
Sissors 1:be9d058ee5c7 130 LPC_RTC->ALSEC = alarmTime.tm_sec;
Sissors 0:39767ffe05e6 131 LPC_RTC->AMR &= ~1;
Sissors 0:39767ffe05e6 132 } else
Sissors 0:39767ffe05e6 133 LPC_RTC->AMR |= 1;
Sissors 0:39767ffe05e6 134
Sissors 1:be9d058ee5c7 135 if ((alarmTime.tm_min>=0) && (alarmTime.tm_min<60)) {
Sissors 1:be9d058ee5c7 136 LPC_RTC->ALMIN = alarmTime.tm_min;
Sissors 0:39767ffe05e6 137 LPC_RTC->AMR &= ~2;
Sissors 0:39767ffe05e6 138 } else
Sissors 0:39767ffe05e6 139 LPC_RTC->AMR |= 2;
Sissors 0:39767ffe05e6 140
Sissors 1:be9d058ee5c7 141 if ((alarmTime.tm_hour>=0) && (alarmTime.tm_hour<24)) {
Sissors 1:be9d058ee5c7 142 LPC_RTC->ALHOUR = alarmTime.tm_hour;
Sissors 0:39767ffe05e6 143 LPC_RTC->AMR &= ~4;
Sissors 0:39767ffe05e6 144 } else
Sissors 0:39767ffe05e6 145 LPC_RTC->AMR |= 4;
Sissors 0:39767ffe05e6 146
Sissors 1:be9d058ee5c7 147 if ((alarmTime.tm_mday>=1) && (alarmTime.tm_mday<32)) {
Sissors 1:be9d058ee5c7 148 LPC_RTC->ALDOM = alarmTime.tm_mday;
Sissors 0:39767ffe05e6 149 LPC_RTC->AMR &= ~8;
Sissors 0:39767ffe05e6 150 } else
Sissors 1:be9d058ee5c7 151 LPC_RTC->AMR |= 8;
Sissors 1:be9d058ee5c7 152
Sissors 1:be9d058ee5c7 153 if ((alarmTime.tm_wday>=0) && (alarmTime.tm_wday<7)) {
Sissors 1:be9d058ee5c7 154 LPC_RTC->ALDOW = alarmTime.tm_wday;
Sissors 1:be9d058ee5c7 155 LPC_RTC->AMR &= ~16;
Sissors 1:be9d058ee5c7 156 } else
Sissors 1:be9d058ee5c7 157 LPC_RTC->AMR |= 16;
Sissors 1:be9d058ee5c7 158
Sissors 1:be9d058ee5c7 159 if ((alarmTime.tm_yday>0) && (alarmTime.tm_yday<367)) {
Sissors 1:be9d058ee5c7 160 LPC_RTC->ALDOY = alarmTime.tm_yday;
Sissors 1:be9d058ee5c7 161 LPC_RTC->AMR &= ~32;
Sissors 1:be9d058ee5c7 162 } else
Sissors 1:be9d058ee5c7 163 LPC_RTC->AMR |= 32;
Sissors 1:be9d058ee5c7 164
Sissors 1:be9d058ee5c7 165 if ((alarmTime.tm_mon>=0) && (alarmTime.tm_mon<12)) {
Sissors 1:be9d058ee5c7 166 LPC_RTC->ALMON = alarmTime.tm_mon + 1; //Different definitions
Sissors 0:39767ffe05e6 167 LPC_RTC->AMR &= ~64;
Sissors 0:39767ffe05e6 168 } else
Sissors 1:be9d058ee5c7 169 LPC_RTC->AMR |= 64;
Sissors 0:39767ffe05e6 170
Sissors 1:be9d058ee5c7 171 if ((alarmTime.tm_year>=0) && (alarmTime.tm_year<1000)) {
Sissors 1:be9d058ee5c7 172 LPC_RTC->ALYEAR = alarmTime.tm_year + 1900; //Different definitions
Sissors 0:39767ffe05e6 173 LPC_RTC->AMR &= ~128;
Sissors 0:39767ffe05e6 174 } else
Sissors 0:39767ffe05e6 175 LPC_RTC->AMR |= 128;
Sissors 1:be9d058ee5c7 176
Sissors 1:be9d058ee5c7 177 //DOY and DOW register normally not set
Sissors 1:be9d058ee5c7 178 time_t t = time(NULL);
Sissors 1:be9d058ee5c7 179 LPC_RTC->DOY = localtime(&t)->tm_yday+1;
Sissors 1:be9d058ee5c7 180 LPC_RTC->DOW = localtime(&t)->tm_wday;
Sissors 1:be9d058ee5c7 181
Sissors 0:39767ffe05e6 182 //We can always enable IRQs, since if all IRQs are disabled by the user the RTC hardware will never raise its IRQ flag anyway
Sissors 0:39767ffe05e6 183 NVIC_EnableIRQ(RTC_IRQn);
Sissors 0:39767ffe05e6 184 }
Sissors 0:39767ffe05e6 185
Sissors 1:be9d058ee5c7 186 void RTC::alarmOff( void )
Sissors 1:be9d058ee5c7 187 {
Sissors 0:39767ffe05e6 188 LPC_RTC->AMR = 255;
Sissors 1:be9d058ee5c7 189 alarmCB.attach(NULL);
Sissors 1:be9d058ee5c7 190 }
Sissors 0:39767ffe05e6 191
Sissors 0:39767ffe05e6 192
Sissors 0:39767ffe05e6 193 void RTC::IRQHandler( void )
Sissors 0:39767ffe05e6 194 {
Sissors 0:39767ffe05e6 195 if ((LPC_RTC->ILR & 0x01) == 0x01) {
Sissors 0:39767ffe05e6 196 //Attach interrupt
Sissors 0:39767ffe05e6 197 attachCB[0].call();
Sissors 0:39767ffe05e6 198
Sissors 0:39767ffe05e6 199 //If seconds zero
Sissors 0:39767ffe05e6 200 if (LPC_RTC->SEC == 0) {
Sissors 0:39767ffe05e6 201 attachCB[1].call();
Sissors 0:39767ffe05e6 202
Sissors 0:39767ffe05e6 203 //If minutes zero
Sissors 0:39767ffe05e6 204 if (LPC_RTC->MIN == 0) {
Sissors 0:39767ffe05e6 205 attachCB[2].call();
Sissors 0:39767ffe05e6 206
Sissors 0:39767ffe05e6 207 //If hours zero
Sissors 0:39767ffe05e6 208 if (LPC_RTC->HOUR == 0) {
Sissors 0:39767ffe05e6 209 attachCB[3].call();
Sissors 0:39767ffe05e6 210
Sissors 0:39767ffe05e6 211 //If days zero
Sissors 0:39767ffe05e6 212 if (LPC_RTC->DOM == 0) {
Sissors 0:39767ffe05e6 213 attachCB[4].call();
Sissors 0:39767ffe05e6 214
Sissors 0:39767ffe05e6 215 //If month zero
Sissors 0:39767ffe05e6 216 if (LPC_RTC->MONTH == 0)
Sissors 0:39767ffe05e6 217 attachCB[5].call();
Sissors 0:39767ffe05e6 218 }
Sissors 0:39767ffe05e6 219 }
Sissors 0:39767ffe05e6 220 }
Sissors 0:39767ffe05e6 221 }
Sissors 0:39767ffe05e6 222 }
Sissors 0:39767ffe05e6 223
Sissors 0:39767ffe05e6 224 if ((LPC_RTC->ILR & 0x02) == 0x02)
Sissors 0:39767ffe05e6 225 alarmCB.call();
Sissors 0:39767ffe05e6 226
Sissors 0:39767ffe05e6 227
Sissors 0:39767ffe05e6 228
Sissors 0:39767ffe05e6 229 //Reset interrupt status
Sissors 0:39767ffe05e6 230 LPC_RTC->ILR = 0x03;
Sissors 1:be9d058ee5c7 231 }
Sissors 1:be9d058ee5c7 232
Sissors 1:be9d058ee5c7 233 tm RTC::getDefaultTM( void ) {
Sissors 1:be9d058ee5c7 234 struct tm t;
Sissors 1:be9d058ee5c7 235 t.tm_sec = -1;
Sissors 1:be9d058ee5c7 236 t.tm_min = -1;
Sissors 1:be9d058ee5c7 237 t.tm_hour = -1;
Sissors 1:be9d058ee5c7 238 t.tm_mday = -1;
Sissors 1:be9d058ee5c7 239 t.tm_wday = -1;
Sissors 1:be9d058ee5c7 240 t.tm_yday = -1;
Sissors 1:be9d058ee5c7 241 t.tm_mon = -1;
Sissors 1:be9d058ee5c7 242 t.tm_year = -1;
Sissors 1:be9d058ee5c7 243
Sissors 1:be9d058ee5c7 244 return t;
Sissors 1:be9d058ee5c7 245 }