FIXED, but still puzzled: LPC1227 Watchdog device reset works before entering but not after leaving DEEP SLEEP

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

FIXED, but still puzzled: LPC1227 Watchdog device reset works before entering but not after leaving DEEP SLEEP

743 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by OldManVimes on Sat Jun 13 06:02:02 MST 2015
Hello support,

I have a test in my software for an LPC1227 to verify that the watchdog resets my device if the software hangs in a while(1) loop. This test works fine before I enter DEEP SLEEP. After waking up from DEEP SLEEP, I cannot get this to work anymore. I've spent many fruitless hours testing, checking code and reading the user manual, errata and application notes, but have not got it to work yet.

Here's (roughly) the code I have to enter and wake-up from DEEP SLEEP (using timed wakeup via the RTC and WDT clock circuit). sysCtrlSleepMode gets called with a non zero time. Note that the DEEP SLEEP and wakeup behavior works flawlessly. It's just the watchdog reset capability that I cannot get re-initialized properly.

Can you tell me what I'm missing? Thanks in advance.

// 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);
}
Labels (1)
0 Kudos
1 Reply

607 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by OldManVimes on Sun Jun 14 04:07:29 MST 2015
Hi support,

Update to the issue. I've got it to work, but am still a bit puzzled about why.
So I narrowed the cause down to the disabling of the WDT clock (which is not used except in DEEP SLEEP with a timed wakeup) after waking up and restarting the watchdog.
So I changed the code from:

// 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();


to:

// 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 WDT clock config (cosmetic action)
LPC_SYSCON->WDTOSCCTRL = wdt_oscctrl;

// Restore the watchdog to the state it had prior to entering DEEP SLEEP
if (watchdogEnabled)
{
enableWatchdog();
// Interrupts enabled at this point. Not a problem.
}
// **A** Leave WDT clock enabled at this point in time!

__enable_irq();

sysCtrlConfigurePinsFromSleep();
sysCtrlPeripheralsPowerUp(sleepTimestamp);

// Disable WDT clock if enabled. Leave others as is.
// Note that this line of code is placed at this location for a reason.
// If placed at **A** the watchdog counter does not start/run.
// The calls in between cause a big enough delay to prevent that issue.
LPC_SYSCON->PDRUNCFG = run_config;


So there appears to be a timing issue related to switching the LPC_WWDT->CLKSEL register from the WDT clock to the IRC clock and the deactivation of the WDT clock. I have not been able to find any documentation that explains this (potential) link.

I've tested this by monitoring the LPC_WWDT->TV value. With the old code it remains fixed at the value of LPC->WWDT->TC. In the working code it counts down.

Can you shed some light on this?
0 Kudos