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

Dependencies:   LPC1114_WakeInterruptIn

Fork of WakeUp by Erik -

Device/WakeUp_LPC11XX.cpp

Committer:
Sissors
Date:
2014-07-30
Revision:
12:779d866b8a2d
Parent:
11:72db657fc572
Child:
13:fd24cec76d5a

File content as of revision 12:779d866b8a2d:

/**
See homepage of this lib for LPC11xx special treatment
**/

#ifdef TARGET_LPC11XX_11CXX

#include "WakeUp.h"
#include "WakeInterruptIn.h"
#include "pinmap.h"

//Pin used, allowed pins = P0_1 (dp24, default), P0_8 (dp1) and P0_9 (dp2)
//By defining WakeUpPin in for example your main.cpp this can be overridden
WEAK PinName WakeUpPin = dp24;
extern PinName WakeUpPin;

WakeInterruptIn IRQ_in(WakeUpPin);
PwmOut pulse_out(WakeUpPin);

FunctionPointer WakeUp::callback;
float WakeUp::cycles_per_ms = 20.0;

static uint32_t old_clk_sel = ~0;
static uint32_t SYSAHBCLKCTRL;
static uint32_t TCR, PR, MR3;
static LPC_TMR_TypeDef *WakeUpTimer;
static uint32_t SYSAHBCLKCTRL_Sleep;
static uint8_t WakeUpTimer_Match;

static inline void restore(void);


void WakeUp::set_ms(uint32_t ms)
{
    if (old_clk_sel == ~0) {                                //Only during first run
        old_clk_sel = LPC_SYSCON->MAINCLKSEL;
        SYSAHBCLKCTRL = LPC_SYSCON->SYSAHBCLKCTRL;
        
        switch(WakeUpPin) {
            case dp24:
                WakeUpTimer = LPC_TMR32B0;
                SYSAHBCLKCTRL_Sleep = 0x15 | (1<<9);
                WakeUpTimer_Match = 2;
                break;
            case dp1:
                WakeUpTimer = LPC_TMR16B0;
                SYSAHBCLKCTRL_Sleep = 0x15 | (1<<7);
                WakeUpTimer_Match = 0;
                break;
            case dp2:
                WakeUpTimer = LPC_TMR16B0;
                SYSAHBCLKCTRL_Sleep = 0x15 | (1<<7);
                WakeUpTimer_Match = 1;
                break;
            default:
                error("Invalid WakeUp pin, choose dp1, dp2 or dp24");
        }            
    }
        
    if (ms != 0) {        
        if (LPC_SYSCON->SYSAHBCLKCTRL != SYSAHBCLKCTRL_Sleep)    //Always when it is different from sleep settings
            SYSAHBCLKCTRL = LPC_SYSCON->SYSAHBCLKCTRL;

        LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_WDTOSC_PD;
        LPC_SYSCON->PDSLEEPCFG = 0x000018B7 | (LPC_SYSCON->PDRUNCFG & (PDRUNCFG_WDTOSC_PD | PDRUNCFG_BOD_PD)); 
        
        //Set oscillator for 20kHz
        LPC_SYSCON->WDTOSCCTRL = 14 | (1<<5);
        
        //Store old PWM
        TCR = WakeUpTimer->TCR;
        PR = WakeUpTimer->PR;
        MR3 = WakeUpTimer->MR3;
        
        //Setup PWM
        WakeUpTimer->TCR = TMR16B0TCR_CRST;
        uint32_t ticks = (float)ms * cycles_per_ms;
        
        //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)
        WakeUpTimer->PR = ticks >> 16;
        WakeUpTimer->MR[WakeUpTimer_Match] = ticks / ((ticks >> 16) + 1);
        WakeUpTimer->MR3 = 0xFFFF;
                
        IRQ_in.rise(irq_handler);
        
        //Disable most peripherals
        LPC_SYSCON->SYSAHBCLKCTRL = SYSAHBCLKCTRL_Sleep;
        
        //Switch clock to WD OSC
        LPC_SYSCON->MAINCLKSEL = 0x2;
        LPC_SYSCON->MAINCLKUEN = 0;
        LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;
        
        //Enable PWM:
        WakeUpTimer->TCR = TMR16B0TCR_CEN;
    } else {
        //Else restore normal settings
        restore();
    }
    
}

void WakeUp::irq_handler(void)
{    
    restore();    
    callback.call();
}

void WakeUp::calibrate(void)
{
    //Set oscillator for 20kHz
    LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_WDTOSC_PD;
    LPC_SYSCON->WDTOSCCTRL = 14 | (1<<5);
    
    //Direct WDT to the CLKOUT pin (dp24), sample it back
    DigitalIn din(dp24);
    Timer timer;
    
    LPC_SYSCON->CLKOUTDIV = 1;
    LPC_SYSCON->CLKOUTCLKSEL = 0x2;
    LPC_SYSCON->CLKOUTUEN = 0;
    LPC_SYSCON->CLKOUTUEN = CLKOUTUEN_ENA;
    pin_function(dp24, 1);
    
    int count = 0;
    timer.start();
    while (timer.read_ms() < 100) {
        while (din.read() == 0);
        while (din.read() == 1);
        count++;
    }
    cycles_per_ms = (float)count / 100.0f;
}

static inline void restore(void) {
        
    WakeUpTimer->MR[WakeUpTimer_Match] = 0xFFFFFFFF;

    if (old_clk_sel == 3)                           //Was running on PLL
        while(LPC_SYSCON->SYSPLLSTAT != SYSPLLSTAT_LOCK);
    
    if (old_clk_sel < 4) {  //If valid setting
        LPC_SYSCON->MAINCLKSEL = old_clk_sel;
        LPC_SYSCON->MAINCLKUEN = 0;
        LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;
    }
    
    IRQ_in.rise(NULL);
    
    LPC_SYSCON->SYSAHBCLKCTRL = SYSAHBCLKCTRL; 
    
    WakeUpTimer->MR3 = MR3;
    WakeUpTimer->PR = PR;
    WakeUpTimer->TCR = TCR;
}

#endif