420 lines
14 KiB
C
420 lines
14 KiB
C
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@file pmu.c
|
||
|
@author K. Townsend (microBuilder.eu)
|
||
|
@date 22 March 2010
|
||
|
@version 0.10
|
||
|
|
||
|
@section DESCRIPTION
|
||
|
|
||
|
Controls the power management features of the LPC1343, allowing you
|
||
|
to enter sleep/deep-sleep or deep power-down mode.
|
||
|
|
||
|
For examples of how to enter either mode, see the comments for the
|
||
|
functions pmuSleep(), pmuDeepSleep() and pmuPowerDown().
|
||
|
|
||
|
@section LICENSE
|
||
|
|
||
|
Software License Agreement (BSD License)
|
||
|
|
||
|
Copyright (c) 2010, microBuilder SARL
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
1. Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
2. Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
3. Neither the name of the copyright holders nor the
|
||
|
names of its contributors may be used to endorse or promote products
|
||
|
derived from this software without specific prior written permission.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
|
||
|
#include "core/gpio/gpio.h"
|
||
|
#include "core/cpu/cpu.h"
|
||
|
#include "core/timer32/timer32.h"
|
||
|
#include "pmu.h"
|
||
|
|
||
|
#ifdef CFG_CHIBI
|
||
|
#include "drivers/chibi/chb_drvr.h"
|
||
|
#endif
|
||
|
#define PMU_WDTCLOCKSPEED_HZ 7812
|
||
|
|
||
|
void pmuSetupHW(void);
|
||
|
void pmuRestoreHW(void);
|
||
|
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
Wakeup interrupt handler
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void WAKEUP_IRQHandler(void)
|
||
|
{
|
||
|
uint32_t regVal;
|
||
|
|
||
|
// Reconfigure system clock/PLL
|
||
|
cpuPllSetup(CPU_MULTIPLIER_6);
|
||
|
|
||
|
// Clear match bit on timer
|
||
|
TMR_TMR32B0EMR = 0;
|
||
|
|
||
|
// Clear pending bits
|
||
|
SCB_STARTRSRP0CLR = SCB_STARTRSRP0CLR_MASK;
|
||
|
|
||
|
// Clear SLEEPDEEP bit
|
||
|
SCB_SCR &= ~SCB_SCR_SLEEPDEEP;
|
||
|
|
||
|
// Disable the deep sleep timer
|
||
|
TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_DISABLED;
|
||
|
|
||
|
/* This handler takes care of all the port pins if they
|
||
|
are configured as wakeup source. */
|
||
|
regVal = SCB_STARTSRP0;
|
||
|
if (regVal != 0)
|
||
|
{
|
||
|
SCB_STARTRSRP0CLR = regVal;
|
||
|
}
|
||
|
|
||
|
// Reconfigure CT32B0
|
||
|
timer32Init(0, TIMER32_DEFAULTINTERVAL);
|
||
|
timer32Enable(0);
|
||
|
|
||
|
// Perform peripheral specific and custom wakeup tasks
|
||
|
pmuRestoreHW();
|
||
|
|
||
|
/* See tracker for bug report. */
|
||
|
__asm volatile ("NOP");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
Setup the clock for the watchdog timer. The default is 7.8125kHz.
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
static void pmuWDTClockInit (void)
|
||
|
{
|
||
|
/* Enable WDT clock */
|
||
|
SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_WDTOSC);
|
||
|
|
||
|
/* Configure watchdog clock */
|
||
|
/* Freq. = 0.5MHz, div = 64: WDT_OSC = 7.8125kHz */
|
||
|
SCB_WDTOSCCTRL = SCB_WDTOSCCTRL_FREQSEL_0_5MHZ |
|
||
|
SCB_WDTOSCCTRL_DIVSEL_DIV64;
|
||
|
|
||
|
// Switch main clock to WDT output
|
||
|
SCB_MAINCLKSEL = SCB_MAINCLKSEL_SOURCE_WDTOSC;
|
||
|
SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE; // Update clock source
|
||
|
SCB_MAINCLKUEN = SCB_MAINCLKUEN_DISABLE; // Toggle update register once
|
||
|
SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE;
|
||
|
|
||
|
// Wait until the clock is updated
|
||
|
while (!(SCB_MAINCLKUEN & SCB_MAINCLKUEN_UPDATE));
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief Initialises the power management unit
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void pmuInit( void )
|
||
|
{
|
||
|
/* Enable all clocks, even those turned off at power up. */
|
||
|
SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_WDTOSC_MASK |
|
||
|
SCB_PDRUNCFG_SYSOSC_MASK |
|
||
|
SCB_PDRUNCFG_ADC_MASK);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief Puts select peripherals in sleep mode.
|
||
|
|
||
|
This function will put the device into sleep mode. Most gpio pins
|
||
|
can be used to wake the device up, but the pins must first be
|
||
|
configured for this in pmuInit.
|
||
|
|
||
|
@section Example
|
||
|
|
||
|
@code
|
||
|
// Configure wakeup sources before going into sleep/deep-sleep.
|
||
|
// By default, pin 0.1 is configured as wakeup source (falling edge)
|
||
|
pmuInit();
|
||
|
|
||
|
// Enter sleep mode
|
||
|
pmuSleep();
|
||
|
@endcode
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void pmuSleep()
|
||
|
{
|
||
|
SCB_PDAWAKECFG = SCB_PDRUNCFG;
|
||
|
__asm volatile ("WFI");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief Turns off select peripherals and puts the device in deep-sleep
|
||
|
mode.
|
||
|
|
||
|
The device can be configured to wakeup from deep-sleep mode after a
|
||
|
specified delay by supplying a non-zero value to the wakeupSeconds
|
||
|
parameter. This will configure CT32B0 to toggle pin 0.1 (CT32B0_MAT2)
|
||
|
after x seconds, waking the device up. The timer will be configured
|
||
|
to run off the WDT OSC while in deep-sleep mode, meaning that WDTOSC
|
||
|
should not be powered off (using the sleepCtrl parameter) when a
|
||
|
wakeup delay is specified.
|
||
|
|
||
|
The sleepCtrl parameter is used to indicate which peripherals should
|
||
|
be put in sleep mode (see the SCB_PDSLEEPCFG register for details).
|
||
|
|
||
|
@param[in] sleepCtrl
|
||
|
The bits to set in the SCB_PDSLEEPCFG register. This
|
||
|
controls which peripherals will be put in sleep mode.
|
||
|
@param[in] wakeupSeconds
|
||
|
The number of seconds to wait until the device will
|
||
|
wakeup. If you do not wish to wakeup after a specific
|
||
|
delay, enter a value of 0.
|
||
|
|
||
|
@code
|
||
|
uint32_t pmuRegVal;
|
||
|
|
||
|
// Initialise power management unit
|
||
|
pmuInit();
|
||
|
|
||
|
// Put peripherals into sleep mode
|
||
|
pmuRegVal = SCB_PDSLEEPCFG_IRCOUT_PD |
|
||
|
SCB_PDSLEEPCFG_IRC_PD |
|
||
|
SCB_PDSLEEPCFG_FLASH_PD |
|
||
|
SCB_PDSLEEPCFG_USBPLL_PD |
|
||
|
SCB_PDSLEEPCFG_SYSPLL_PD |
|
||
|
SCB_PDSLEEPCFG_SYSOSC_PD |
|
||
|
SCB_PDSLEEPCFG_ADC_PD |
|
||
|
SCB_PDSLEEPCFG_BOD_PD;
|
||
|
|
||
|
// Enter deep sleep mode (wakeup after 5 seconds)
|
||
|
// By default, pin 0.1 is configured as wakeup source
|
||
|
pmuDeepSleep(pmuRegVal, 5);
|
||
|
@endcode
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void pmuDeepSleep(uint32_t sleepCtrl, uint32_t wakeupSeconds)
|
||
|
{
|
||
|
// Setup the board for deep sleep mode, shutting down certain
|
||
|
// peripherals and remapping pins for lower power
|
||
|
pmuSetupHW();
|
||
|
SCB_PDAWAKECFG = SCB_PDRUNCFG;
|
||
|
sleepCtrl |= (1 << 9) | (1 << 11);
|
||
|
SCB_PDSLEEPCFG = sleepCtrl;
|
||
|
SCB_SCR |= SCB_SCR_SLEEPDEEP;
|
||
|
|
||
|
/* Configure system to run from WDT and set TMR32B0 for wakeup */
|
||
|
if (wakeupSeconds > 0)
|
||
|
{
|
||
|
// Make sure WDTOSC isn't disabled in PDSLEEPCFG
|
||
|
SCB_PDSLEEPCFG &= ~(SCB_PDSLEEPCFG_WDTOSC_PD);
|
||
|
|
||
|
// Disable 32-bit timer 0 if currently in use
|
||
|
TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_DISABLED;
|
||
|
|
||
|
// Disable internal pullup on 0.1
|
||
|
gpioSetPullup(&IOCON_PIO0_1, gpioPullupMode_Inactive);
|
||
|
|
||
|
/* Enable the clock for CT32B0 */
|
||
|
SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT32B0);
|
||
|
|
||
|
/* Configure 0.1 as Timer0_32 MAT2 */
|
||
|
IOCON_PIO0_1 &= ~IOCON_PIO0_1_FUNC_MASK;
|
||
|
IOCON_PIO0_1 |= IOCON_PIO0_1_FUNC_CT32B0_MAT2;
|
||
|
|
||
|
/* Set appropriate timer delay */
|
||
|
TMR_TMR32B0MR0 = PMU_WDTCLOCKSPEED_HZ * wakeupSeconds;
|
||
|
|
||
|
/* Configure match control register to raise an interrupt and reset on MR0 */
|
||
|
TMR_TMR32B0MCR |= (TMR_TMR32B0MCR_MR0_INT_ENABLED | TMR_TMR32B0MCR_MR0_RESET_ENABLED);
|
||
|
|
||
|
/* Configure external match register to set 0.1 high on match */
|
||
|
TMR_TMR32B0EMR &= ~(0xFF<<4); // Clear EMR config bits
|
||
|
TMR_TMR32B0EMR |= TMR_TMR32B0EMR_EMC2_HIGH; // Set MAT2 (0.1) high on match
|
||
|
|
||
|
/* Enable wakeup interrupts (any I/O pin can be used as a wakeup source) */
|
||
|
//NVIC_EnableIRQ(WAKEUP0_IRQn); // P0.0
|
||
|
NVIC_EnableIRQ(WAKEUP1_IRQn); // P0.1 (CT32B0_MAT2)
|
||
|
//NVIC_EnableIRQ(WAKEUP2_IRQn); // P0.2
|
||
|
//NVIC_EnableIRQ(WAKEUP3_IRQn); // P0.3
|
||
|
//NVIC_EnableIRQ(WAKEUP4_IRQn); // P0.4
|
||
|
//NVIC_EnableIRQ(WAKEUP5_IRQn); // P0.5
|
||
|
//NVIC_EnableIRQ(WAKEUP6_IRQn); // P0.6
|
||
|
//NVIC_EnableIRQ(WAKEUP7_IRQn); // P0.7
|
||
|
//NVIC_EnableIRQ(WAKEUP8_IRQn); // P0.8
|
||
|
//NVIC_EnableIRQ(WAKEUP9_IRQn); // P0.9
|
||
|
//NVIC_EnableIRQ(WAKEUP10_IRQn); // P0.10
|
||
|
//NVIC_EnableIRQ(WAKEUP11_IRQn); // P0.11
|
||
|
//NVIC_EnableIRQ(WAKEUP12_IRQn); // P1.0
|
||
|
//NVIC_EnableIRQ(WAKEUP13_IRQn); // P1.1
|
||
|
//NVIC_EnableIRQ(WAKEUP14_IRQn); // P1.2
|
||
|
//NVIC_EnableIRQ(WAKEUP15_IRQn); // P1.3
|
||
|
//NVIC_EnableIRQ(WAKEUP16_IRQn); // P1.4
|
||
|
//NVIC_EnableIRQ(WAKEUP17_IRQn); // P1.5
|
||
|
//NVIC_EnableIRQ(WAKEUP18_IRQn); // P1.6
|
||
|
//NVIC_EnableIRQ(WAKEUP19_IRQn); // P1.7
|
||
|
//NVIC_EnableIRQ(WAKEUP20_IRQn); // P1.8
|
||
|
//NVIC_EnableIRQ(WAKEUP21_IRQn); // P1.9
|
||
|
//NVIC_EnableIRQ(WAKEUP22_IRQn); // P1.10
|
||
|
//NVIC_EnableIRQ(WAKEUP23_IRQn); // P1.11
|
||
|
//NVIC_EnableIRQ(WAKEUP24_IRQn); // P2.0
|
||
|
//NVIC_EnableIRQ(WAKEUP25_IRQn); // P2.1
|
||
|
//NVIC_EnableIRQ(WAKEUP26_IRQn); // P2.2
|
||
|
//NVIC_EnableIRQ(WAKEUP27_IRQn); // P2.3
|
||
|
//NVIC_EnableIRQ(WAKEUP28_IRQn); // P2.4
|
||
|
//NVIC_EnableIRQ(WAKEUP29_IRQn); // P2.5
|
||
|
//NVIC_EnableIRQ(WAKEUP30_IRQn); // P2.6
|
||
|
//NVIC_EnableIRQ(WAKEUP31_IRQn); // P2.7
|
||
|
//NVIC_EnableIRQ(WAKEUP32_IRQn); // P2.8
|
||
|
//NVIC_EnableIRQ(WAKEUP33_IRQn); // P2.9
|
||
|
//NVIC_EnableIRQ(WAKEUP34_IRQn); // P2.10
|
||
|
//NVIC_EnableIRQ(WAKEUP35_IRQn); // P2.11
|
||
|
//NVIC_EnableIRQ(WAKEUP36_IRQn); // P3.0
|
||
|
//NVIC_EnableIRQ(WAKEUP37_IRQn); // P3.1
|
||
|
//NVIC_EnableIRQ(WAKEUP38_IRQn); // P3.2
|
||
|
//NVIC_EnableIRQ(WAKEUP39_IRQn); // P3.3
|
||
|
|
||
|
/* Use RISING EDGE for wakeup detection. */
|
||
|
SCB_STARTAPRP0 |= SCB_STARTAPRP0_APRPIO0_1;
|
||
|
|
||
|
/* Clear all wakeup sources */
|
||
|
SCB_STARTRSRP0CLR = SCB_STARTRSRP0CLR_MASK;
|
||
|
|
||
|
/* Enable Port 0.1 as wakeup source. */
|
||
|
SCB_STARTERP0 |= SCB_STARTERP0_ERPIO0_1;
|
||
|
|
||
|
// Reconfigure clock to run from WDTOSC
|
||
|
pmuWDTClockInit();
|
||
|
|
||
|
/* Start the timer */
|
||
|
TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_ENABLED;
|
||
|
}
|
||
|
|
||
|
__asm volatile ("WFI");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief Puts the device in deep power-down mode.
|
||
|
|
||
|
This function will configure the PMU control register and enter
|
||
|
deep power-down mode. Pre-determined values are stored in the four
|
||
|
general-purpose registers (PMU_GPREG0..3), which can be used to persist
|
||
|
any essential system settings while the device is in deep power-down
|
||
|
mode, so long as 3.3V is still available.
|
||
|
|
||
|
@warning The only way to wake a device up from deep power-down mode
|
||
|
is to set a low-level on P1.4. If 3.3V power is lost, the
|
||
|
values stored in the four general-purpose registers will
|
||
|
also be lost.
|
||
|
|
||
|
@section Example
|
||
|
|
||
|
@code
|
||
|
#include "core/cpu/cpu.h"
|
||
|
#include "core/pmu/pmu.h"
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
cpuInit();
|
||
|
pmuInit();
|
||
|
|
||
|
// Enter power-down mode
|
||
|
pmuPowerDown();
|
||
|
|
||
|
while(1)
|
||
|
{
|
||
|
// Device was woken up by WAKEUP pin
|
||
|
}
|
||
|
}
|
||
|
@endcode
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void pmuPowerDown( void )
|
||
|
{
|
||
|
uint32_t regVal;
|
||
|
|
||
|
// Make sure HW and external devices are in low power mode
|
||
|
pmuSetupHW();
|
||
|
|
||
|
if ( (PMU_PMUCTRL & ((0x1<<8) | (PMU_PMUCTRL_DPDFLAG))) != 0x0 )
|
||
|
{
|
||
|
/* Check sleep and deep power down bits. If sleep and/or
|
||
|
deep power down mode are entered, clear the PCON bits. */
|
||
|
regVal = PMU_PMUCTRL;
|
||
|
regVal |= ((0x1<<8) |
|
||
|
(PMU_PMUCTRL_DPDEN_SLEEP) |
|
||
|
(PMU_PMUCTRL_DPDFLAG));
|
||
|
PMU_PMUCTRL = regVal;
|
||
|
|
||
|
if ( (PMU_GPREG0 != 0x12345678)||(PMU_GPREG1 != 0x87654321)
|
||
|
||(PMU_GPREG2 != 0x56781234)||(PMU_GPREG3 != 0x43218765) )
|
||
|
{
|
||
|
while (1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* If in neither sleep nor deep-sleep mode, enter deep power down mode. */
|
||
|
PMU_GPREG0 = 0x12345678;
|
||
|
PMU_GPREG1 = 0x87654321;
|
||
|
PMU_GPREG2 = 0x56781234;
|
||
|
PMU_GPREG3 = 0x43218765;
|
||
|
SCB_SCR |= SCB_SCR_SLEEPDEEP;
|
||
|
PMU_PMUCTRL = PMU_PMUCTRL_DPDEN_DEEPPOWERDOWN;
|
||
|
__asm volatile ("WFI");
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief Configures parts and system peripherals to use lower power
|
||
|
before entering sleep mode
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void pmuSetupHW(void)
|
||
|
{
|
||
|
#ifdef CFG_CHIBI
|
||
|
chb_sleep(TRUE);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief Restores parts and system peripherals to an appropriate
|
||
|
state after waking up from deep-sleep mode
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void pmuRestoreHW(void)
|
||
|
{
|
||
|
#ifdef CFG_CHIBI
|
||
|
// Wakeup Chibi/Transceiver
|
||
|
chb_sleep(FALSE);
|
||
|
#endif
|
||
|
}
|