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:
Wed Jun 14 08:56:55 2017 +0000
Revision:
23:69a0c843e4bd
Parent:
19:9d355da2770e
Switched WakeUp to use the new callback system, to remove warnings during compilation.; ; Attached normal functions should still work the same. Attaching member functions is now:; WakeUp::attach(callback(&object, &Class::function));

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Sissors 16:f3adba7cf7c4 1 #if defined(TARGET_Freescale)
Sissors 7:bb411115f814 2
Sissors 7:bb411115f814 3 #include "WakeUp.h"
Sissors 7:bb411115f814 4 #include "us_ticker_api.h"
Sissors 7:bb411115f814 5
Sissors 23:69a0c843e4bd 6 Callback<void()> WakeUp::callback;
Sissors 7:bb411115f814 7 float WakeUp::cycles_per_ms = 1.0;
Sissors 7:bb411115f814 8
Sissors 7:bb411115f814 9 static uint16_t remainder_count;
Sissors 7:bb411115f814 10 static uint32_t oldvector;
Sissors 7:bb411115f814 11 static uint8_t oldPSR;
Sissors 7:bb411115f814 12
Sissors 18:13aed323e040 13 //See if we have a 32kHz crystal on the clock input
Sissors 18:13aed323e040 14 //Check if the DMX32 bit is set, not perfect, but most cases will work
Sissors 18:13aed323e040 15 static inline bool is32kXtal(void) {
Sissors 18:13aed323e040 16 return (MCG->C4 & MCG_C4_DMX32_MASK);
Sissors 18:13aed323e040 17 }
Sissors 18:13aed323e040 18
Sissors 7:bb411115f814 19 void restore(void);
Sissors 7:bb411115f814 20
Sissors 7:bb411115f814 21 void WakeUp::set_ms(uint32_t ms)
Sissors 7:bb411115f814 22 {
Sissors 7:bb411115f814 23 /* Clock the timer */
Sissors 7:bb411115f814 24 SIM->SCGC5 |= 0x1u;
Sissors 7:bb411115f814 25
Sissors 7:bb411115f814 26 //Check if it is running, in that case, store current values
Sissors 7:bb411115f814 27 remainder_count = 0;
Sissors 7:bb411115f814 28 if (NVIC_GetVector(LPTimer_IRQn) != (uint32_t)WakeUp::irq_handler) {
Sissors 7:bb411115f814 29 oldvector = NVIC_GetVector(LPTimer_IRQn);
Sissors 7:bb411115f814 30 oldPSR = LPTMR0->PSR;
Sissors 7:bb411115f814 31
Sissors 7:bb411115f814 32 if (LPTMR0->CSR & LPTMR_CSR_TIE_MASK) {
Sissors 7:bb411115f814 33 //Write first to sync value
Sissors 7:bb411115f814 34 LPTMR0->CNR = 0;
Sissors 7:bb411115f814 35 uint16_t countval = LPTMR0->CNR;
Sissors 7:bb411115f814 36 if (countval < LPTMR0->CMR)
Sissors 7:bb411115f814 37 remainder_count = countval - LPTMR0->CMR;
Sissors 7:bb411115f814 38 }
Sissors 7:bb411115f814 39 }
Sissors 7:bb411115f814 40
Sissors 7:bb411115f814 41 LPTMR0->CSR = 0;
Sissors 7:bb411115f814 42
Sissors 7:bb411115f814 43 if (ms != 0) {
Sissors 7:bb411115f814 44 /* Set interrupt handler */
Sissors 7:bb411115f814 45 NVIC_SetVector(LPTimer_IRQn, (uint32_t)WakeUp::irq_handler);
Sissors 7:bb411115f814 46 NVIC_EnableIRQ(LPTimer_IRQn);
Sissors 7:bb411115f814 47
Sissors 18:13aed323e040 48 uint32_t counts;
Sissors 18:13aed323e040 49 //Set clock
Sissors 18:13aed323e040 50 if (is32kXtal()) {
Sissors 18:13aed323e040 51 SIM->SOPT1 &= ~SIM_SOPT1_OSC32KSEL_MASK; //Put RTC/LPTMR on 32kHz external.
Sissors 19:9d355da2770e 52 #ifdef OSC0
Sissors 18:13aed323e040 53 OSC0->CR |= OSC_CR_EREFSTEN_MASK;
Sissors 19:9d355da2770e 54 #else
Sissors 19:9d355da2770e 55 OSC->CR |= OSC_CR_EREFSTEN_MASK;
Sissors 19:9d355da2770e 56 #endif
Sissors 18:13aed323e040 57 LPTMR0->PSR = LPTMR_PSR_PCS(2);
Sissors 18:13aed323e040 58 counts = (uint32_t)((float)ms * 32.768f);
Sissors 18:13aed323e040 59 } else {
Sissors 18:13aed323e040 60 //Clock from the 1kHz LPO
Sissors 18:13aed323e040 61 LPTMR0->PSR = LPTMR_PSR_PCS(1);
Sissors 18:13aed323e040 62 counts = (uint32_t)((float)ms * cycles_per_ms);
Sissors 18:13aed323e040 63 }
Sissors 18:13aed323e040 64
Sissors 7:bb411115f814 65 //If no prescaler is needed
Sissors 7:bb411115f814 66 if (counts <= 0xFFFF)
Sissors 7:bb411115f814 67 LPTMR0->PSR |= LPTMR_PSR_PBYP_MASK;
Sissors 7:bb411115f814 68 else { //Otherwise increase prescaler until it fits
Sissors 7:bb411115f814 69 counts >>= 1;
Sissors 7:bb411115f814 70 uint32_t prescaler = 0;
Sissors 7:bb411115f814 71 while (counts > 0xFFFF) {
Sissors 7:bb411115f814 72 counts >>= 1;
Sissors 7:bb411115f814 73 prescaler++;
Sissors 7:bb411115f814 74 }
Sissors 7:bb411115f814 75 LPTMR0->PSR |= LPTMR_PSR_PRESCALE(prescaler);
Sissors 7:bb411115f814 76 }
Sissors 7:bb411115f814 77 LPTMR0->CMR = counts;
Sissors 7:bb411115f814 78
Sissors 7:bb411115f814 79 LPTMR0->CSR = LPTMR_CSR_TIE_MASK;
Sissors 7:bb411115f814 80 LPTMR0->CSR |= LPTMR_CSR_TEN_MASK;
Sissors 7:bb411115f814 81 } else {
Sissors 7:bb411115f814 82 restore();
Sissors 7:bb411115f814 83 }
Sissors 7:bb411115f814 84
Sissors 7:bb411115f814 85 }
Sissors 7:bb411115f814 86
Sissors 7:bb411115f814 87
Sissors 7:bb411115f814 88 void WakeUp::irq_handler(void)
Sissors 7:bb411115f814 89 {
Sissors 7:bb411115f814 90 // write 1 to TCF to clear the LPT timer compare flag
Sissors 7:bb411115f814 91 LPTMR0->CSR |= LPTMR_CSR_TCF_MASK;
Sissors 7:bb411115f814 92 restore();
Sissors 7:bb411115f814 93 callback.call();
Sissors 7:bb411115f814 94 }
Sissors 7:bb411115f814 95
Sissors 7:bb411115f814 96 void WakeUp::calibrate(void)
Sissors 7:bb411115f814 97 {
Sissors 18:13aed323e040 98 if (!is32kXtal()) {
Sissors 18:13aed323e040 99 wait_us(1); //Otherwise next wait might overwrite our settings
Sissors 18:13aed323e040 100 cycles_per_ms = 1.0;
Sissors 18:13aed323e040 101 set_ms(1100);
Sissors 18:13aed323e040 102 wait_ms(100);
Sissors 18:13aed323e040 103
Sissors 18:13aed323e040 104 //Write first to sync value
Sissors 18:13aed323e040 105 LPTMR0->CNR = 0;
Sissors 18:13aed323e040 106 uint32_t ticks = LPTMR0->CNR;
Sissors 18:13aed323e040 107 cycles_per_ms = ticks / 100.0;
Sissors 18:13aed323e040 108 set_ms(0);
Sissors 18:13aed323e040 109 }
Sissors 7:bb411115f814 110 }
Sissors 7:bb411115f814 111
Sissors 7:bb411115f814 112 void restore(void){
Sissors 7:bb411115f814 113 /* Reset */
Sissors 7:bb411115f814 114 LPTMR0->CSR = 0;
Sissors 7:bb411115f814 115
Sissors 7:bb411115f814 116 /* Set interrupt handler */
Sissors 7:bb411115f814 117 NVIC_SetVector(LPTimer_IRQn, oldvector);
Sissors 7:bb411115f814 118 NVIC_EnableIRQ(LPTimer_IRQn);
Sissors 7:bb411115f814 119
Sissors 7:bb411115f814 120 /* Clock at (1)MHz -> (1)tick/us */
Sissors 7:bb411115f814 121 LPTMR0->PSR = oldPSR;
Sissors 7:bb411115f814 122
Sissors 7:bb411115f814 123 if (remainder_count) {
Sissors 7:bb411115f814 124 /* Set the compare register */
Sissors 7:bb411115f814 125 LPTMR0->CMR = remainder_count;
Sissors 7:bb411115f814 126
Sissors 7:bb411115f814 127 /* Enable interrupt */
Sissors 7:bb411115f814 128 LPTMR0->CSR |= LPTMR_CSR_TIE_MASK;
Sissors 7:bb411115f814 129
Sissors 7:bb411115f814 130 /* Start the timer */
Sissors 7:bb411115f814 131 LPTMR0->CSR |= LPTMR_CSR_TEN_MASK;
Sissors 7:bb411115f814 132 }
Sissors 7:bb411115f814 133 }
Sissors 7:bb411115f814 134
Sissors 7:bb411115f814 135 #endif