// At the moment we use the internal RC oscillator
// Check wdt.c in Lib_MCU for details
#define WDT_IRC_OSC_CLK 12000000UL /* Internal RC oscillator frequency */
void feedWatchdogIntsDisabled(void)
{
LPC_WWDT->FEED = 0xaa;
LPC_WWDT->FEED = 0x55;
}
void feedWatchdog(void)
{
__disable_irq();
feedWatchdogIntsDisabled();
__enable_irq();
}
// When using the IRC as clock source, the maximum watchdog time is roughly 5.5 seconds
void setWatchdogTimeoutUs(int feedTimeoutUs)
{
LPC_WWDT->TC = (((WDT_IRC_OSC_CLK) / 1000000) * (feedTimeoutUs/4));
feedWatchdog();
}
void disableWatchdogIntsDisabled(void)
{
LPC_WWDT->MOD = 0;
feedWatchdogIntsDisabled();
}
void disableWatchdog(void)
{
LPC_WWDT->MOD = 0;
feedWatchdog();
}
// Note: By default the watchdog is enabled.
// Only use this operation to re-enable it.
void enableWatchdog(void)
{
LPC_WWDT->CLKSEL = 0;
LPC_WWDT->MOD = 3;
feedWatchdog();
}
bool isWatchdogEnabled(void)
{
return ((LPC_WWDT->MOD & 3) != 0);
}
/* Configure WD Oscilator for lowest frequency */
static void WDT_CLK_Setup ( void )
{
/* Watchdog configuration. */
/* Freq = 0.5MHz, div_sel is 0x1F, divided by 64. WDT_OSC should be 7.8125khz */
LPC_SYSCON->WDTOSCCTRL = (0x1<<5)|0x1F;
LPC_SYSCON->PDRUNCFG &= ~(0x1<<6); /* Let WDT clock run */
/* Enables clock for WDT */
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15);
LPC_WWDT->CLKSEL = 0x1;
}
clock_t sysCtrlSleepMode(uint32_t autoWakeUpTimeSeconds)
{
clock_t sleepModeDuration = 0; // Nof seconds we remain in DEEP SLEEP
bool watchdogEnabled;
/* Remember the state of the watchdog. */
watchdogEnabled = isWatchdogEnabled();
// Remember the state of registers we may/will change
uint32_t run_config = LPC_SYSCON->PDRUNCFG;
uint32_t main_clk_src=LPC_SYSCON->MAINCLKSEL;
uint32_t wdt_clksel = LPC_WWDT->CLKSEL;
uint32_t wdt_oscctrl = LPC_SYSCON->WDTOSCCTRL;
/*
* Disable the watchdog to prevent it from triggering.
*/
if (watchdogEnabled)
{
disableWatchdog();
}
__disable_irq();
// Reset the wake-up event before we check for a pending event!
configureWakeup();
// If a wakeup event is generated before
// the interrupts are disabled, do not go to sleep.
// After the interrupt are disabled, __WFI() will act as a __NOP
// in case an interrupt becomes pending before we enter __WFI().
if (!sysCtrlIsWakeupEventPresent())
{
I2C_EnterSleep();
tsTime_t sleepTimestamp = sysCtrlPeripheralsPowerDown();
sysCtrlConfigurePinsForSleep();
if (autoWakeUpTimeSeconds != 0)
{
// Enable Start Logic 1 to wake from RTC
LPC_SYSCON->STARTAPRP1 |= (1 << 18); // Wake-up on rising edge RTC interrupt
LPC_SYSCON->STARTRSRP1CLR = (1 << 18); // Reset startup signal
LPC_SYSCON->STARTERP1 |= (1 << 18); // Enable wakeup from RTC
clockSetAlarm(autoWakeUpTimeSeconds);
// Power Up WD Osc
WDT_CLK_Setup();
// Change to the WDT clock
if (main_clk_src != 2)
{
LPC_SYSCON->MAINCLKSEL = 2;
LPC_SYSCON->MAINCLKUEN = 0x01;
LPC_SYSCON->MAINCLKUEN = 0x00;
LPC_SYSCON->MAINCLKUEN = 0x01;
while ( !(LPC_SYSCON->MAINCLKUEN & 0x01) );/* Wait until updated */
}
// WDOSC- Turn off SYSOSC,IRC and PLL
LPC_SYSCON->PDRUNCFG |= ((1<<7)|(1<<5)|(1<<1)|(1<<0));
LPC_SYSCON->PDSLEEPCFG = 0x0000FFBF;
LPC_SYSCON->PDAWAKECFG = run_config & ~(1<<6); // Ensure WDT clock is active on wake-up
}
else
{
/*
* Configure the PDSLEEPCFG register
* BOD disabled, Watchdog oscillator disabled
*/
LPC_SYSCON->PDSLEEPCFG = 0x0000FFFF;
/*
* Restore the current analog peripheral power up settings when waking up
* from deep sleep mode.
*/
LPC_SYSCON->PDAWAKECFG = run_config;
/*
* The IRC oscillator should be selected as the main clock before entering
* deep sleep, because only this oscillator can be shut down glitch free.
*
* So select the IRC oscillator as the main clock if this wasn't already
* the current situation.
*/
if (main_clk_src != 0)
{
LPC_SYSCON->MAINCLKSEL = 0;
LPC_SYSCON->MAINCLKUEN = 0;
LPC_SYSCON->MAINCLKUEN = 1;
while ( !(LPC_SYSCON->MAINCLKUEN & 0x01) );/* Wait until updated */
}
}
/*
* Sequence for entering deep sleep mode as specified in the deep sleep mode
* section of the LPC12xx user manual.
*/
LPC_PMU->PCON &= ~0x2;
/*
* Only go to DEEP SLEEP in case the watchdog was enabled at the time of calling.
* If the watchdog is disabled at the time of calling, then it is very likely
* that there is an active debugging session. Going to DEEP SLEEP stops the
* debugger. Going to SLEEP does not.
*/
if (watchdogEnabled)
{
SCB->SCR |= 0x4;
}
__WFI(); // DEEP SLEEP or SLEEP in case of a debug session
SCB->SCR &= ~0x4; // Clear SLEEPDEEP bit so MCU will enter sleep mode on __WFI();
// If the original clock was based on the PLL, wait for it to lock again.
if (main_clk_src== 3)
{
while ((LPC_SYSCON->SYSPLLSTAT & 0x1) == 0x0)
{
}
}
// Switch back to the original main clock
if (LPC_SYSCON->MAINCLKSEL != main_clk_src)
{
LPC_SYSCON->MAINCLKSEL = main_clk_src;
LPC_SYSCON->MAINCLKUEN = 0;
LPC_SYSCON->MAINCLKUEN = 1;
while ( !(LPC_SYSCON->MAINCLKUEN & 0x01) );/* Wait until updated */
}
LPC_WWDT->CLKSEL = wdt_clksel;
// Restore the watchdog to the state it had prior to entering DEEP SLEEP
// Ensure PDRUNCFG has the IRC enabled before doing this to ensure setting
// the watchdog clock source works.
if (watchdogEnabled)
{
enableWatchdog();
// Interrupts enabled at this point. Not a problem.
}
LPC_SYSCON->WDTOSCCTRL = wdt_oscctrl;
// Disable WDT clock if enabled. Leave others as is.
LPC_SYSCON->PDRUNCFG = run_config;
__enable_irq();
sysCtrlConfigurePinsFromSleep();
sysCtrlPeripheralsPowerUp(sleepTimestamp);
// At this point, the RTC still reports the epoch from when
// we entered DEEP SLEEP mode. It takes some time before
// it starts reporting the correct time again.
// 'some' time has been observed as 1 - 1000 ms, so it appears
// to relate to the sleep/awake moment within one RTC duty cycle.
//
// In the unlikely event that the RTC is broken we would block
// in the loop forever, causing a watchdog reset. Prevent that
// by waiting for a maximum of 1100 ms.
tsTime_t rtcWaitStart = currentTime();
do
{
sleepModeDuration = clockGetEpoch() - sleepTime;
} while ((sleepModeDuration == 0) && (!timeInPast(rtcWaitStart + (ONE_MILLISECOND * 1100))));
monotonicTimeAdvance(sleepModeDuration);
}
else
{
__enable_irq();
/* Restore the watchdog state. */
if (watchdogEnabled)
{
enableWatchdog();
}
}
return (sleepModeDuration);
}
|