Enables lower power Sleep and power down modes for LPC11U24 systems than the mbed API supports directly.

Dependents:   mBuDice SleepyCounting

Provides a clean method to put the mBuino into various power save modes while minimizing power draw. This code is specifically for the mBuino, for other LPC11U24 based systems use this version.

When the mBuino was released the mbed library Sleep() and DeepSleep() commands causes the system to crash, even if fixed in a later release of the library the mBed API doesn't allow access to the lowest power modes that you need when running from a watch battery. Hence this library.

When commanded to sleep the system will pause in a power saving state until it receives an interrupt at which point things carry on as before.

The basic use is

#include "mBuinoSleep.h"

main() {
mBuinoSleep(PowerDown);
}

In order to shut off the LEDs the library will create a BusOut object called LEDs containing all 7 mBuino LEDs in order. When entering sleep mode the LEDs will be turned off, when waking the previous state will be restored. Within your code you can set the state of all 7 LEDs by setting the value of LEDs directly e.g. LEDs = 0x7f would turn them all on. LEDs=0x2A would turn LEDs 2,4 and 6 on. See BusOut for more details.

Similarly a DigitalIn called progMode is created on port pin 0_3 in order to disable the internal pullup on this pin (required to reduce power consumption).

If either of these cause a problem in your code then use the mBuino_Sleep_Minimal library which has the same sleep code but without any IO control. If doing this ensure you disable the pullup and turn off the LEDs in your code.

Finally if you add the line

sleep_CleanShutdown = true;

to your code anywhere before entering sleep mode then the system clock will be switched from the PLL to the IRC before entering DeepSleep or PowerDown and switched back after waking up. This is recommended by the CPU user manual in order to avoid clock glitches but in practice doesn't seem necessary. The down side to doing this is that any code in the interrupt routine that wakes the system up again will run at the IRC frequency not the normal clock frequency.

The supported sleep modes are:

  • Sleep - About a 50% power saving. All timers run as normal.
  • DeepSleep - Power consumption in the 350uA region (a few weeks or so on a watch battery). All timers are disabled, only external interrupts can wake the system.
  • DeepSleepWD - As DeepSleep but also keeping the Watch Dog timer powered up. Tiny increase in power (4uA) but the watchdog wakeup library can be used to wake the system on a timer.
  • PowerDown - Power consumption in the 3uA region (Battery life in the decades range, in practice battery life will be determined by other factors). Similar to DeepSleep but takes a few ms longer to wake up.
  • PowerDownWD - PowerDown but with the watch dog timer running resulting in a power consumption of 7uA.
  • DeepPowerDown - Sub uA power consumption but can only wake from the reset or wakeup pin (which also causes a reset).

In DeepSleep or PowerDown modes it is NOT is possible to use a normal Ticker or Timeout to wake up from sleep mode. To wake up on a timer use the modes ending in WD and use this library to configure the watchdog. See this program for an example.

An IO interrupt (i.e. anything triggered by a pin that is defined as an InterruptIn) will wake the system up from all sleep modes other than DeepPowerDown.

NOTES:

DO NOT enter sleep mode from within an interrupt (that includes Timers, Tickers, Timeouts etc...) unless your wakeup interrupt is higher priority (this is not the mbed default, if you aren't sure or don't know what I'm talking about then it isn't). If you do this you will never wake up.

DeepPowerDown requires an external pullup on the wakeup pin, without this the system will wake up as soon as it enters power down. On the mBuino this resistor is not there, you need to add it. For other platforms check the schematic.

All deepsleep and powerdown modes currently disable the brownout detection circuit. Keeping this powered would add about 50uA to the power consumption. If for some reason you need this circuit active then this would require a minor change to the library, specifically the value programmed into PDSLEEPCFG around line 40.

mBuinoSleep.cpp

Committer:
AndyA
Date:
2014-09-28
Revision:
7:bcb4ed255791
Parent:
6:23973d4b6b34

File content as of revision 7:bcb4ed255791:

#include "mBuinoSleep.h"


BusOut LEDs(LED1, LED2, LED3, LED4, LED5, LED6, LED7);

DigitalIn progMode(P0_3,PullNone);

bool sleep_CleanShutdown = false;

void mBuinoSleep(enum sleepMode_t mode)
{

    uint8_t oldLEDState = LEDs;
    LEDs = 0;

    progMode.mode(PullNone);

    if ((mode == DeepPowerDown) && (LPC_PMU->PCON & 0x08)) // bit 3 high blocks deep power down.
        mode = PowerDown;

    switch (mode) {
        default:
        case Sleep:
            LPC_PMU->PCON = 0x0;
            SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
            __WFI();
            break;

        case DeepSleep:
        case PowerDown:
        case PowerDownWD:
        case DeepSleepWD:
         {
            if ((mode == PowerDown) || (mode == PowerDownWD))
                LPC_PMU->PCON = 0x2;
            else
                LPC_PMU->PCON = 0x1;

            LPC_SYSCON->PDSLEEPCFG |= 0x7f;  // shut off everything we can.

            if ((mode == DeepSleepWD) || (mode == PowerDownWD))
              LPC_SYSCON->PDSLEEPCFG &= ~(1<<6);
              
            SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

            bool IRCPowered = (LPC_SYSCON->PDRUNCFG & 0x01); // only used for cleen shutdown but defined here for scope reasons.
            if (!IRCPowered)
                LPC_SYSCON->PDRUNCFG &= 0xfffffffe; // power up the IRC

            if(sleep_CleanShutdown) {
                LPC_SYSCON->MAINCLKSEL    = 0x00; // switch to IRC to avoid glitches
                LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */
                LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */
                LPC_SYSCON->MAINCLKUEN    = 0x01;
                while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */
            }

            LPC_SYSCON->PDAWAKECFG = LPC_SYSCON->PDRUNCFG; // switch on everything that is currently on when we wake up.
            __WFI();

            if(sleep_CleanShutdown) {
                LPC_SYSCON->MAINCLKSEL    = 0x03; // switch to PLL output
                LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */
                LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */
                LPC_SYSCON->MAINCLKUEN    = 0x01;
                while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */
            }

            if (!IRCPowered)
                LPC_SYSCON->PDRUNCFG |= 0x01; // power down the IRC if it was off before
        }
        break;
        case DeepPowerDown:
            LPC_PMU->PCON = 0x3;
            LPC_SYSCON->PDSLEEPCFG |= 0x7f;  // shut off everything we can.
            SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
            __WFI();
    }
    LEDs = oldLEDState;
}