Wake-up timer library to wake from deepsleep/power-down

Dependencies:   LPC1114_WakeInterruptIn

Dependents:   LPC812_Sleep_HelloWorld KL05Z_DCF77_RTC_Clock LPC1114_data_logger mBuinoBlinky ... more

Supported Targets

  • LPC812
  • LPC11u24
  • LPC1114
  • All mbed Freescale targets
  • All mbed STM targets except the F1 series

Please read the target specific comments below. A general small warning: If you have other interrupts enabled, and they request attention after the WakeUp interrupt is set, but before deepsleep is entered, and these take long to handle, it is possible that the WakeUp interrupt is handled before you enter deepsleep. In that case there is no interrupt anymore which should wake it from deepsleep.

Example code

// Depending on the LED connections either the LED is off the 2 seconds
// the target spends in deepsleep(), and on for the other second. Or it is inverted 
 
#include "mbed.h"
#include "WakeUp.h"
 
DigitalOut myled(LED1);
 
int main() {
    //The low-power oscillator can be quite inaccurate on some targets
    //this function calibrates it against the main clock
    WakeUp::calibrate();
   
    while(1) {
        //Set LED to zero
        myled = 0;
        
        //Set wakeup time for 2 seconds
        WakeUp::set_ms(2000);
        
        //Enter deepsleep, the program won't go beyond this point until it is woken up
        deepsleep();
  
        //Set LED for 1 second to one
        myled = 1;
        wait(1);
    }
}

Target comments

All targets use different implementations, some of these have some things that need to be taken into account. If your target is supported but not listed here, then there is nothing relevant to mention.

Core M3/M4 microcontrollers

These microcontrollers cannot wake from deepsleep while they are being debugged. Core M0s can, although their power consumption is very high while being debugged. Generally to exit debug mode you need to power cycle the microcontroller, while making sure the debugger isn't powercycled as well.

On NUCLEO boards you can for example break the connection to the target IC with a jumper, which does this. Most Freescale boards have a USB connector for the target IC (in addition to the SDA USB). If you use this one to power the board, the debugger should not get powered.

KLxx

These targets (such as the KL25z, KL05z, etc) use the same LPTMR for both WakeUp and for ticker generation. The WakeUp code is nice, and it will backup the old values when being set, and restore those after waking up, allowing you to continue using your ticker, and a ticker which was already set will continue again. However you are not allowed to set a new ticker after you already set WakeUp, since this will give clashes. Do you for whatever reason need to do it (for example you set WakeUp, and then you wake using an InteruptIn), you can disable the WakeUp timer and restore the ticker functionality by setting WakeUp for 0 seconds.

LPC11u24

This target uses the watchdog timer to generate the necesary interrupts to wake from deepsleep. The reset functionality of the timer is disabled, so you don't need to worry about that. However the library won't work if other code also uses the watchdog timer. Most likely the result is unpredictable.

STM

For STM targets the library uses the RTC of these targets. The calibration subroutine is not (yet) implemented, since it assumes that an RTC is quite accurate (which might not be true if it runs as by default on an internal RC oscillator). Currently it keeps the RTC in the default settings of the mbed code. Due to the nature of these settings the maximum time resolution this lib can achieve on those targets is, depending on if a 32kHz crystal is fitted, 3-4ms, instead of the 1ms of other targets.

Also it might not compile for your specific target even though it has an RTC. In that case send me a message (or you can also look yourself). The required interrupt vector changes place and name depending on the target, and it could be that another define needs to be added for your target.

LPC1114

The LPC1114 is special. Not in a good way special. It lacks any kind of regular low-power timer/RTC/WDT which is suitable to wake it from deepsleep mode. What the library does instead is that when the WakeUp command is called, it sets the entire main clock of the device to the watchdog oscillator (at 20kHz). Clock gating is used to disable all peripherals except one timer, this timer then is used to create a pulse on an output pin. Connected to this output pin is an external interrupt, which wakes the device, and restores the original settings.

The first thing this means is that you need an unused pin. Currently by default it is set for dp24 (P0_1), if you don't add anything this pin is used. You can remap this in your code to pins dp1 (P0_8) and dp2 (P0_9):

//Add the following global variable to any .cpp file (generally your main.cpp).
PinName WakeUpPin = dp2;  //Or dp1/dp24. If this line is not included it will default to dp24

While this pin generates a pulse, other pwm outputs on the same peripheral which are active will keep running (although very slowly).

The second, and also important part, is that you should NOT set it to immediatly start a timer and enter deepsleep after a reset. Add a wait of a few seconds (random amount) in between (or just other code). When it runs at 20kHz it will refuse to be reprogrammed by the Switch Science LPC1114 mbed board, and I can do the educated guess that ISP programming via the UART also isn't going to work. If you add a wait at the start there is no problem.

Did you ignore my advice and got your LPC1114 bricked? Don't worry (too much), I managed to unbrick all mine again. I used uVision 5, export an LPC1114 project from mbed to have correct device settings. In Project > Options for Target > Debug > Use debugger: CMSIS-DAP > Settings you can change debugger settings. Playing with these can help (I haven't found yet what is required exactly). Now from Flash you can do erase/download. This is giving errors for me. Doesn't matter, what is important is that if you do the right thing (if your code blinks an LED for example that is useful), the code will stop running. Either the uC is set in permanent reset (faint glow of LEDs connected to ground), or it just stops running. At this point you can drag an drop program it again.

The calibrate function requires dp24, regardless of which pin is set as the WakeUpPin. After calibration you can use set it to do something else, however during calibration it needs to be able to toggle.

Committer:
Sissors
Date:
Tue Jul 04 07:59:06 2017 +0000
Revision:
24:65c04a02ad45
Parent:
23:69a0c843e4bd
Added attach documentation

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Sissors 9:29bdf5fed21a 1 #ifdef TARGET_STM
Sissors 9:29bdf5fed21a 2
Sissors 9:29bdf5fed21a 3 #include "WakeUp.h"
Sissors 9:29bdf5fed21a 4 #include "rtc_api.h"
Sissors 9:29bdf5fed21a 5
Sissors 9:29bdf5fed21a 6 #define BYTE2BCD(byte) ((byte % 10) | ((byte / 10) << 4))
Sissors 9:29bdf5fed21a 7
Sissors 9:29bdf5fed21a 8 //Most things are pretty similar between the different STM targets.
Sissors 9:29bdf5fed21a 9 //Only the IRQ number the alarm is connected to differs. Any errors
Sissors 9:29bdf5fed21a 10 //with RTC_IRQn/RTC_Alarm_IRQn in them are related to this
Sissors 15:b2a710aca356 11 #if defined(TARGET_M4) || defined(TARGET_M3)
Sissors 9:29bdf5fed21a 12 #define RTC_IRQ RTC_Alarm_IRQn
Sissors 9:29bdf5fed21a 13 #else
Sissors 9:29bdf5fed21a 14 #define RTC_IRQ RTC_IRQn
Sissors 9:29bdf5fed21a 15 #endif
Sissors 9:29bdf5fed21a 16
Sissors 20:68f2ee917691 17 // Some things to handle Disco L476VG (and similar ones)
Sissors 20:68f2ee917691 18 #if defined(TARGET_STM32L4)
Sissors 20:68f2ee917691 19 #define IMR IMR1
Sissors 20:68f2ee917691 20 #define EMR EMR1
Sissors 20:68f2ee917691 21 #define RTSR RTSR1
Sissors 20:68f2ee917691 22 #define FTSR FTSR2
Sissors 20:68f2ee917691 23 #define PR PR1
Sissors 20:68f2ee917691 24 #endif
Sissors 20:68f2ee917691 25
Sissors 17:49d9e3a3e904 26 //Disabling the Backup Powerdomain does not seem to work nicely anymore if you want to use other RTC functions afterwards.
Sissors 17:49d9e3a3e904 27 //For now I have disabled it in code, if you find WakeUp increases your powerconsumption, try enabling it again (code is still there, just commented)
Sissors 17:49d9e3a3e904 28
Sissors 23:69a0c843e4bd 29 Callback<void()> WakeUp::callback;
Sissors 9:29bdf5fed21a 30
Sissors 9:29bdf5fed21a 31 void WakeUp::set_ms(uint32_t ms)
Sissors 9:29bdf5fed21a 32 {
Sissors 9:29bdf5fed21a 33 if (!rtc_isenabled()) { //Make sure RTC is running
Sissors 9:29bdf5fed21a 34 rtc_init();
Sissors 9:29bdf5fed21a 35 wait_us(250); //The f401 seems to want a delay after init
Sissors 9:29bdf5fed21a 36 }
Sissors 9:29bdf5fed21a 37
Sissors 17:49d9e3a3e904 38 //PWR->CR |= PWR_CR_DBP; //Enable power domain
Sissors 9:29bdf5fed21a 39 RTC->WPR = 0xCA; //Disable RTC write protection
Sissors 9:29bdf5fed21a 40 RTC->WPR = 0x53;
Sissors 9:29bdf5fed21a 41
Sissors 9:29bdf5fed21a 42 //Alarm must be disabled to change anything
Sissors 9:29bdf5fed21a 43 RTC->CR &= ~RTC_CR_ALRAE;
Sissors 9:29bdf5fed21a 44 while(!(RTC->ISR & RTC_ISR_ALRAWF));
Sissors 9:29bdf5fed21a 45
Sissors 9:29bdf5fed21a 46 if (ms == 0) { //Just disable alarm
Sissors 17:49d9e3a3e904 47 //PWR->CR &= ~PWR_CR_DBP; //Disable power domain
Sissors 9:29bdf5fed21a 48 RTC->WPR = 0xFF; //Enable RTC write protection
Sissors 9:29bdf5fed21a 49 return;
Sissors 9:29bdf5fed21a 50 }
Sissors 9:29bdf5fed21a 51
Sissors 9:29bdf5fed21a 52 //RTC prescaler + calculate how many sub-seconds should be added
Sissors 9:29bdf5fed21a 53 uint32_t prescaler = (RTC->PRER & 0x7FFF) + 1;
Sissors 9:29bdf5fed21a 54 uint32_t subsecsadd = ((ms % 1000) * prescaler) / 1000;
Sissors 9:29bdf5fed21a 55
Sissors 9:29bdf5fed21a 56 if ((ms < 1000) && (subsecsadd < 2))
Sissors 9:29bdf5fed21a 57 subsecsadd = 2; //At least 2 subsecs delay to be sure interrupt is called
Sissors 9:29bdf5fed21a 58
Sissors 9:29bdf5fed21a 59 __disable_irq(); //At this point we don't want IRQs anymore
Sissors 9:29bdf5fed21a 60
Sissors 9:29bdf5fed21a 61 //Get current time
Sissors 9:29bdf5fed21a 62 uint32_t subsecs = RTC->SSR;
Sissors 9:29bdf5fed21a 63 time_t secs = rtc_read();
Sissors 9:29bdf5fed21a 64
Sissors 9:29bdf5fed21a 65 //Calculate alarm values
Sissors 9:29bdf5fed21a 66 //Subseconds is countdown, so substract the 'added' sub-seconds and prevent underflow
Sissors 9:29bdf5fed21a 67 if (subsecs < subsecsadd) {
Sissors 9:29bdf5fed21a 68 subsecs += prescaler;
Sissors 9:29bdf5fed21a 69 secs++;
Sissors 9:29bdf5fed21a 70 }
Sissors 9:29bdf5fed21a 71 subsecs -= subsecsadd;
Sissors 9:29bdf5fed21a 72
Sissors 9:29bdf5fed21a 73 //Set seconds correctly
Sissors 9:29bdf5fed21a 74 secs += ms / 1000;
Sissors 9:29bdf5fed21a 75 struct tm *timeinfo = localtime(&secs);
Sissors 9:29bdf5fed21a 76
Sissors 9:29bdf5fed21a 77 //Enable rising edge EXTI interrupt of the RTC
Sissors 20:68f2ee917691 78 EXTI->IMR |= RTC_EXTI_LINE_ALARM_EVENT;
Sissors 20:68f2ee917691 79 EXTI->EMR &= ~RTC_EXTI_LINE_ALARM_EVENT;
Sissors 20:68f2ee917691 80 EXTI->RTSR |= RTC_EXTI_LINE_ALARM_EVENT;
Sissors 20:68f2ee917691 81 EXTI->FTSR &= ~RTC_EXTI_LINE_ALARM_EVENT;
Sissors 9:29bdf5fed21a 82
Sissors 9:29bdf5fed21a 83 //Calculate alarm register values
Sissors 9:29bdf5fed21a 84 uint32_t alarmreg = 0;
Sissors 9:29bdf5fed21a 85 alarmreg |= BYTE2BCD(timeinfo->tm_sec) << 0;
Sissors 9:29bdf5fed21a 86 alarmreg |= BYTE2BCD(timeinfo->tm_min) << 8;
Sissors 9:29bdf5fed21a 87 alarmreg |= BYTE2BCD(timeinfo->tm_hour) << 16;
Sissors 9:29bdf5fed21a 88 alarmreg |= BYTE2BCD(timeinfo->tm_mday) << 24;
Sissors 9:29bdf5fed21a 89
Sissors 9:29bdf5fed21a 90 //Enable RTC interrupt
Sissors 9:29bdf5fed21a 91 RTC->ALRMAR = alarmreg;
Sissors 9:29bdf5fed21a 92 RTC->ALRMASSR = subsecs | RTC_ALRMASSR_MASKSS; //Mask no subseconds
Sissors 9:29bdf5fed21a 93 RTC->CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE; //Enable Alarm
Sissors 9:29bdf5fed21a 94
Sissors 9:29bdf5fed21a 95 RTC->WPR = 0xFF; //Enable RTC write protection
Sissors 17:49d9e3a3e904 96 //PWR->CR &= ~PWR_CR_DBP; //Disable power domain
Sissors 9:29bdf5fed21a 97
Sissors 9:29bdf5fed21a 98 __enable_irq(); //Alarm is set, so irqs can be enabled again
Sissors 9:29bdf5fed21a 99
Sissors 9:29bdf5fed21a 100 //Enable everything else
Sissors 9:29bdf5fed21a 101 NVIC_SetVector(RTC_IRQ, (uint32_t)WakeUp::irq_handler);
Sissors 9:29bdf5fed21a 102 NVIC_EnableIRQ(RTC_IRQ);
Sissors 9:29bdf5fed21a 103 }
Sissors 9:29bdf5fed21a 104
Sissors 9:29bdf5fed21a 105
Sissors 9:29bdf5fed21a 106 void WakeUp::irq_handler(void)
Sissors 9:29bdf5fed21a 107 {
Sissors 9:29bdf5fed21a 108 //Clear RTC + EXTI interrupt flags
Sissors 17:49d9e3a3e904 109 //PWR->CR |= PWR_CR_DBP; //Enable power domain
Sissors 9:29bdf5fed21a 110 RTC->ISR &= ~RTC_ISR_ALRAF;
Sissors 17:49d9e3a3e904 111 RTC->WPR = 0xCA; //Disable RTC write protection
Sissors 17:49d9e3a3e904 112 RTC->WPR = 0x53;
Sissors 17:49d9e3a3e904 113 RTC->CR &= ~RTC_CR_ALRAE;
Sissors 17:49d9e3a3e904 114 RTC->WPR = 0xFF; //Enable RTC write protection
Sissors 20:68f2ee917691 115 EXTI->PR = RTC_EXTI_LINE_ALARM_EVENT;
Sissors 17:49d9e3a3e904 116 //PWR->CR &= ~PWR_CR_DBP; //Disable power domain
Sissors 9:29bdf5fed21a 117 callback.call();
Sissors 9:29bdf5fed21a 118 }
Sissors 9:29bdf5fed21a 119
Sissors 9:29bdf5fed21a 120 void WakeUp::calibrate(void)
Sissors 9:29bdf5fed21a 121 {
Sissors 9:29bdf5fed21a 122 //RTC, we assume it is accurate enough without calibration
Sissors 9:29bdf5fed21a 123 }
Sissors 9:29bdf5fed21a 124
Sissors 9:29bdf5fed21a 125
Sissors 9:29bdf5fed21a 126 #endif