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 0:fc439458a359 1 #include "mbed.h"
Sissors 0:fc439458a359 2
Sissors 0:fc439458a359 3 /**
Sissors 8:8d9a6ac0fba8 4 * Class to make wake up a microcontroller from deepsleep using a low-power timer.
Sissors 8:8d9a6ac0fba8 5 *
Sissors 8:8d9a6ac0fba8 6 * @code
Sissors 8:8d9a6ac0fba8 7 * // Depending on the LED connections either the LED is off the 2 seconds
Sissors 8:8d9a6ac0fba8 8 * // the target spends in deepsleep(), and on for the other second. Or it is inverted
Sissors 8:8d9a6ac0fba8 9 *
Sissors 8:8d9a6ac0fba8 10 * #include "mbed.h"
Sissors 8:8d9a6ac0fba8 11 * #include "WakeUp.h"
Sissors 8:8d9a6ac0fba8 12 *
Sissors 8:8d9a6ac0fba8 13 * DigitalOut myled(LED1);
Sissors 8:8d9a6ac0fba8 14 *
Sissors 8:8d9a6ac0fba8 15 * int main() {
Sissors 10:c41bc9154a7c 16 * wait(5);
Sissors 10:c41bc9154a7c 17 *
Sissors 8:8d9a6ac0fba8 18 * //The low-power oscillator can be quite inaccurate on some targets
Sissors 8:8d9a6ac0fba8 19 * //this function calibrates it against the main clock
Sissors 8:8d9a6ac0fba8 20 * WakeUp::calibrate();
Sissors 8:8d9a6ac0fba8 21 *
Sissors 8:8d9a6ac0fba8 22 * while(1) {
Sissors 8:8d9a6ac0fba8 23 * //Set LED to zero
Sissors 8:8d9a6ac0fba8 24 * myled = 0;
Sissors 8:8d9a6ac0fba8 25 *
Sissors 8:8d9a6ac0fba8 26 * //Set wakeup time for 2 seconds
Sissors 8:8d9a6ac0fba8 27 * WakeUp::set_ms(2000);
Sissors 8:8d9a6ac0fba8 28 *
Sissors 8:8d9a6ac0fba8 29 * //Enter deepsleep, the program won't go beyond this point until it is woken up
Sissors 8:8d9a6ac0fba8 30 * deepsleep();
Sissors 8:8d9a6ac0fba8 31 *
Sissors 8:8d9a6ac0fba8 32 * //Set LED for 1 second to one
Sissors 8:8d9a6ac0fba8 33 * myled = 1;
Sissors 8:8d9a6ac0fba8 34 * wait(1);
Sissors 8:8d9a6ac0fba8 35 * }
Sissors 8:8d9a6ac0fba8 36 * }
Sissors 8:8d9a6ac0fba8 37 * @endcode
Sissors 8:8d9a6ac0fba8 38 */
Sissors 0:fc439458a359 39 class WakeUp
Sissors 0:fc439458a359 40 {
Sissors 0:fc439458a359 41 public:
Sissors 0:fc439458a359 42 /**
Sissors 0:fc439458a359 43 * Set the timeout
Sissors 0:fc439458a359 44 *
Sissors 8:8d9a6ac0fba8 45 * @param s required time in seconds
Sissors 0:fc439458a359 46 */
Sissors 0:fc439458a359 47 static void set(uint32_t s) {
Sissors 0:fc439458a359 48 set_ms(1000 * s);
Sissors 0:fc439458a359 49 }
Sissors 0:fc439458a359 50
Sissors 0:fc439458a359 51 /**
Sissors 0:fc439458a359 52 * Set the timeout
Sissors 0:fc439458a359 53 *
Sissors 8:8d9a6ac0fba8 54 * @param ms required time in milliseconds
Sissors 0:fc439458a359 55 */
Sissors 0:fc439458a359 56 static void set_ms(uint32_t ms);
Sissors 0:fc439458a359 57
Sissors 0:fc439458a359 58 /**
Sissors 0:fc439458a359 59 * Attach a function to be called after timeout
Sissors 0:fc439458a359 60 *
Sissors 0:fc439458a359 61 * This is optional, if you just want to wake up you
Sissors 0:fc439458a359 62 * do not need to attach a function.
Sissors 0:fc439458a359 63 *
Sissors 8:8d9a6ac0fba8 64 * Important: Many targets will run the wake-up routine
Sissors 8:8d9a6ac0fba8 65 * at reduced clock speed, afterwards clock speed is restored.
Sissors 8:8d9a6ac0fba8 66 * This means that clock speed dependent functions, such as printf
Sissors 8:8d9a6ac0fba8 67 * might end up distorted.
Sissors 8:8d9a6ac0fba8 68 *
Sissors 24:65c04a02ad45 69 * @code
Sissors 24:65c04a02ad45 70 * // Attaching regular function
Sissors 24:65c04a02ad45 71 * WakeUp::attach(&yourFunc);
Sissors 24:65c04a02ad45 72 * // Attaching member function inside another library
Sissors 24:65c04a02ad45 73 * WakeUp::attach(callback(this, &YourLib::yourLibFunction));
Sissors 24:65c04a02ad45 74 * @endcode
Sissors 24:65c04a02ad45 75 *
Sissors 23:69a0c843e4bd 76 * It uses the new Callback system to attach functions.
Sissors 0:fc439458a359 77 *
Sissors 8:8d9a6ac0fba8 78 * @param *function function to call
Sissors 0:fc439458a359 79 */
Sissors 23:69a0c843e4bd 80 static void attach(Callback<void()> function) {
Sissors 23:69a0c843e4bd 81 callback = function;
Sissors 0:fc439458a359 82 }
Sissors 0:fc439458a359 83
Sissors 0:fc439458a359 84 /**
Sissors 0:fc439458a359 85 * Calibrate the timer
Sissors 0:fc439458a359 86 *
Sissors 8:8d9a6ac0fba8 87 * Some of the low-power timers have very bad accuracy.
Sissors 8:8d9a6ac0fba8 88 * This function calibrates it against the main timer.
Sissors 0:fc439458a359 89 *
Sissors 0:fc439458a359 90 * Warning: Blocks for 100ms!
Sissors 0:fc439458a359 91 */
Sissors 0:fc439458a359 92 static void calibrate(void);
Sissors 0:fc439458a359 93
Sissors 0:fc439458a359 94
Sissors 0:fc439458a359 95 private:
Sissors 23:69a0c843e4bd 96 static Callback<void()> callback;
Sissors 0:fc439458a359 97 static void irq_handler(void);
Sissors 0:fc439458a359 98 static float cycles_per_ms;
Sissors 0:fc439458a359 99 };