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 10:c41bc9154a7c 1 /**
Sissors 10:c41bc9154a7c 2 See homepage of this lib for LPC11xx special treatment
Sissors 10:c41bc9154a7c 3 **/
Sissors 10:c41bc9154a7c 4
Sissors 10:c41bc9154a7c 5 #ifdef TARGET_LPC11XX_11CXX
Sissors 10:c41bc9154a7c 6
Sissors 10:c41bc9154a7c 7 #include "WakeUp.h"
Sissors 10:c41bc9154a7c 8 #include "WakeInterruptIn.h"
Sissors 12:779d866b8a2d 9 #include "pinmap.h"
Sissors 13:fd24cec76d5a 10 #include "toolchain.h"
Sissors 10:c41bc9154a7c 11
Sissors 11:72db657fc572 12 //Pin used, allowed pins = P0_1 (dp24, default), P0_8 (dp1) and P0_9 (dp2)
Sissors 11:72db657fc572 13 //By defining WakeUpPin in for example your main.cpp this can be overridden
Sissors 11:72db657fc572 14 WEAK PinName WakeUpPin = dp24;
Sissors 11:72db657fc572 15 extern PinName WakeUpPin;
Sissors 11:72db657fc572 16
Sissors 11:72db657fc572 17 WakeInterruptIn IRQ_in(WakeUpPin);
Sissors 11:72db657fc572 18 PwmOut pulse_out(WakeUpPin);
Sissors 10:c41bc9154a7c 19
Sissors 23:69a0c843e4bd 20 Callback<void()> WakeUp::callback;
Sissors 10:c41bc9154a7c 21 float WakeUp::cycles_per_ms = 20.0;
Sissors 10:c41bc9154a7c 22
Sissors 10:c41bc9154a7c 23 static uint32_t old_clk_sel = ~0;
Sissors 10:c41bc9154a7c 24 static uint32_t SYSAHBCLKCTRL;
Sissors 10:c41bc9154a7c 25 static uint32_t TCR, PR, MR3;
Sissors 11:72db657fc572 26 static LPC_TMR_TypeDef *WakeUpTimer;
Sissors 11:72db657fc572 27 static uint32_t SYSAHBCLKCTRL_Sleep;
Sissors 11:72db657fc572 28 static uint8_t WakeUpTimer_Match;
Sissors 10:c41bc9154a7c 29
Sissors 10:c41bc9154a7c 30 static inline void restore(void);
Sissors 10:c41bc9154a7c 31
Sissors 10:c41bc9154a7c 32
Sissors 10:c41bc9154a7c 33 void WakeUp::set_ms(uint32_t ms)
Sissors 10:c41bc9154a7c 34 {
Sissors 11:72db657fc572 35 if (old_clk_sel == ~0) { //Only during first run
Sissors 11:72db657fc572 36 old_clk_sel = LPC_SYSCON->MAINCLKSEL;
Sissors 11:72db657fc572 37 SYSAHBCLKCTRL = LPC_SYSCON->SYSAHBCLKCTRL;
Sissors 10:c41bc9154a7c 38
Sissors 11:72db657fc572 39 switch(WakeUpPin) {
Sissors 11:72db657fc572 40 case dp24:
Sissors 11:72db657fc572 41 WakeUpTimer = LPC_TMR32B0;
Sissors 11:72db657fc572 42 SYSAHBCLKCTRL_Sleep = 0x15 | (1<<9);
Sissors 11:72db657fc572 43 WakeUpTimer_Match = 2;
Sissors 11:72db657fc572 44 break;
Sissors 11:72db657fc572 45 case dp1:
Sissors 11:72db657fc572 46 WakeUpTimer = LPC_TMR16B0;
Sissors 11:72db657fc572 47 SYSAHBCLKCTRL_Sleep = 0x15 | (1<<7);
Sissors 11:72db657fc572 48 WakeUpTimer_Match = 0;
Sissors 11:72db657fc572 49 break;
Sissors 11:72db657fc572 50 case dp2:
Sissors 11:72db657fc572 51 WakeUpTimer = LPC_TMR16B0;
Sissors 11:72db657fc572 52 SYSAHBCLKCTRL_Sleep = 0x15 | (1<<7);
Sissors 11:72db657fc572 53 WakeUpTimer_Match = 1;
Sissors 11:72db657fc572 54 break;
Sissors 11:72db657fc572 55 default:
Sissors 11:72db657fc572 56 error("Invalid WakeUp pin, choose dp1, dp2 or dp24");
Sissors 11:72db657fc572 57 }
Sissors 11:72db657fc572 58 }
Sissors 11:72db657fc572 59
Sissors 11:72db657fc572 60 if (ms != 0) {
Sissors 11:72db657fc572 61 if (LPC_SYSCON->SYSAHBCLKCTRL != SYSAHBCLKCTRL_Sleep) //Always when it is different from sleep settings
Sissors 10:c41bc9154a7c 62 SYSAHBCLKCTRL = LPC_SYSCON->SYSAHBCLKCTRL;
Sissors 10:c41bc9154a7c 63
Sissors 10:c41bc9154a7c 64 LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_WDTOSC_PD;
Sissors 10:c41bc9154a7c 65 LPC_SYSCON->PDSLEEPCFG = 0x000018B7 | (LPC_SYSCON->PDRUNCFG & (PDRUNCFG_WDTOSC_PD | PDRUNCFG_BOD_PD));
Sissors 10:c41bc9154a7c 66
Sissors 10:c41bc9154a7c 67 //Set oscillator for 20kHz
Sissors 10:c41bc9154a7c 68 LPC_SYSCON->WDTOSCCTRL = 14 | (1<<5);
Sissors 10:c41bc9154a7c 69
Sissors 10:c41bc9154a7c 70 //Store old PWM
Sissors 11:72db657fc572 71 TCR = WakeUpTimer->TCR;
Sissors 11:72db657fc572 72 PR = WakeUpTimer->PR;
Sissors 11:72db657fc572 73 MR3 = WakeUpTimer->MR3;
Sissors 10:c41bc9154a7c 74
Sissors 10:c41bc9154a7c 75 //Setup PWM
Sissors 11:72db657fc572 76 WakeUpTimer->TCR = TMR16B0TCR_CRST;
Sissors 10:c41bc9154a7c 77 uint32_t ticks = (float)ms * cycles_per_ms;
Sissors 10:c41bc9154a7c 78
Sissors 10:c41bc9154a7c 79 //whatever timer it is, we treat it as 16-bit (with PR that is 32-bit still, do the math, it is enough for this)
Sissors 11:72db657fc572 80 WakeUpTimer->PR = ticks >> 16;
Sissors 11:72db657fc572 81 WakeUpTimer->MR[WakeUpTimer_Match] = ticks / ((ticks >> 16) + 1);
Sissors 11:72db657fc572 82 WakeUpTimer->MR3 = 0xFFFF;
Sissors 10:c41bc9154a7c 83
Sissors 10:c41bc9154a7c 84 IRQ_in.rise(irq_handler);
Sissors 10:c41bc9154a7c 85
Sissors 10:c41bc9154a7c 86 //Disable most peripherals
Sissors 11:72db657fc572 87 LPC_SYSCON->SYSAHBCLKCTRL = SYSAHBCLKCTRL_Sleep;
Sissors 10:c41bc9154a7c 88
Sissors 10:c41bc9154a7c 89 //Switch clock to WD OSC
Sissors 10:c41bc9154a7c 90 LPC_SYSCON->MAINCLKSEL = 0x2;
Sissors 10:c41bc9154a7c 91 LPC_SYSCON->MAINCLKUEN = 0;
Sissors 10:c41bc9154a7c 92 LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;
Sissors 10:c41bc9154a7c 93
Sissors 10:c41bc9154a7c 94 //Enable PWM:
Sissors 11:72db657fc572 95 WakeUpTimer->TCR = TMR16B0TCR_CEN;
Sissors 10:c41bc9154a7c 96 } else {
Sissors 10:c41bc9154a7c 97 //Else restore normal settings
Sissors 10:c41bc9154a7c 98 restore();
Sissors 10:c41bc9154a7c 99 }
Sissors 10:c41bc9154a7c 100
Sissors 10:c41bc9154a7c 101 }
Sissors 10:c41bc9154a7c 102
Sissors 10:c41bc9154a7c 103 void WakeUp::irq_handler(void)
Sissors 10:c41bc9154a7c 104 {
Sissors 10:c41bc9154a7c 105 restore();
Sissors 10:c41bc9154a7c 106 callback.call();
Sissors 10:c41bc9154a7c 107 }
Sissors 10:c41bc9154a7c 108
Sissors 10:c41bc9154a7c 109 void WakeUp::calibrate(void)
Sissors 10:c41bc9154a7c 110 {
Sissors 14:6bf547e1e62d 111 //Save current pin function
Sissors 14:6bf547e1e62d 112 __IO uint32_t *reg = (__IO uint32_t*)(LPC_IOCON_BASE + (dp24 & 0xFF));
Sissors 14:6bf547e1e62d 113 uint32_t old_pinfun = *reg;
Sissors 14:6bf547e1e62d 114
Sissors 12:779d866b8a2d 115 //Set oscillator for 20kHz
Sissors 12:779d866b8a2d 116 LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_WDTOSC_PD;
Sissors 12:779d866b8a2d 117 LPC_SYSCON->WDTOSCCTRL = 14 | (1<<5);
Sissors 12:779d866b8a2d 118
Sissors 12:779d866b8a2d 119 //Direct WDT to the CLKOUT pin (dp24), sample it back
Sissors 12:779d866b8a2d 120 DigitalIn din(dp24);
Sissors 12:779d866b8a2d 121 Timer timer;
Sissors 12:779d866b8a2d 122
Sissors 12:779d866b8a2d 123 LPC_SYSCON->CLKOUTDIV = 1;
Sissors 12:779d866b8a2d 124 LPC_SYSCON->CLKOUTCLKSEL = 0x2;
Sissors 12:779d866b8a2d 125 LPC_SYSCON->CLKOUTUEN = 0;
Sissors 12:779d866b8a2d 126 LPC_SYSCON->CLKOUTUEN = CLKOUTUEN_ENA;
Sissors 12:779d866b8a2d 127 pin_function(dp24, 1);
Sissors 12:779d866b8a2d 128
Sissors 12:779d866b8a2d 129 int count = 0;
Sissors 12:779d866b8a2d 130 timer.start();
Sissors 12:779d866b8a2d 131 while (timer.read_ms() < 100) {
Sissors 12:779d866b8a2d 132 while (din.read() == 0);
Sissors 12:779d866b8a2d 133 while (din.read() == 1);
Sissors 12:779d866b8a2d 134 count++;
Sissors 12:779d866b8a2d 135 }
Sissors 12:779d866b8a2d 136 cycles_per_ms = (float)count / 100.0f;
Sissors 14:6bf547e1e62d 137
Sissors 14:6bf547e1e62d 138 //Set old pin function back, disable CLKOUT
Sissors 14:6bf547e1e62d 139 *reg = old_pinfun;
Sissors 14:6bf547e1e62d 140 LPC_SYSCON->CLKOUTDIV = 0;
Sissors 10:c41bc9154a7c 141 }
Sissors 10:c41bc9154a7c 142
Sissors 10:c41bc9154a7c 143 static inline void restore(void) {
Sissors 10:c41bc9154a7c 144
Sissors 11:72db657fc572 145 WakeUpTimer->MR[WakeUpTimer_Match] = 0xFFFFFFFF;
Sissors 10:c41bc9154a7c 146
Sissors 10:c41bc9154a7c 147 if (old_clk_sel == 3) //Was running on PLL
Sissors 10:c41bc9154a7c 148 while(LPC_SYSCON->SYSPLLSTAT != SYSPLLSTAT_LOCK);
Sissors 10:c41bc9154a7c 149
Sissors 10:c41bc9154a7c 150 if (old_clk_sel < 4) { //If valid setting
Sissors 10:c41bc9154a7c 151 LPC_SYSCON->MAINCLKSEL = old_clk_sel;
Sissors 10:c41bc9154a7c 152 LPC_SYSCON->MAINCLKUEN = 0;
Sissors 10:c41bc9154a7c 153 LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;
Sissors 10:c41bc9154a7c 154 }
Sissors 10:c41bc9154a7c 155
Sissors 10:c41bc9154a7c 156 IRQ_in.rise(NULL);
Sissors 10:c41bc9154a7c 157
Sissors 10:c41bc9154a7c 158 LPC_SYSCON->SYSAHBCLKCTRL = SYSAHBCLKCTRL;
Sissors 10:c41bc9154a7c 159
Sissors 11:72db657fc572 160 WakeUpTimer->MR3 = MR3;
Sissors 11:72db657fc572 161 WakeUpTimer->PR = PR;
Sissors 11:72db657fc572 162 WakeUpTimer->TCR = TCR;
Sissors 10:c41bc9154a7c 163 }
Sissors 10:c41bc9154a7c 164
Sissors 10:c41bc9154a7c 165 #endif