Electronic – Low power sleep with WDT wakeup on PIC32MM

microchipmplabxpicsleepxc32

I'm trying to put a PIC32MM0256GPM028 into sleep mode and have it woken up by the Sleep Mode Watchdog Timer (say 1/4 second later- adjustable by SWDTPS anyway). Once it goes to sleep (current drops), it just stays there.

This post seems the closest PIC32 sleep mode with watchdog wakeup failing, but that seems to deal with a reset occurring instead of a wakeup.

I've got this as a standalone function:

void sleep(uint32_t t){                     //sleep for t WDT periods 
    static uint32_t oldRCON=0;
    SYSKEY = 0x0;
    SYSKEY = 0xAA996655;                    //unlock
    SYSKEY = 0x556699AA;
    //OSCCONbits.SLPEN=0;                     //enable idle
    OSCCONbits.SLPEN=1;                     //enable sleep
    SYSKEY = 0x0;                           //lock
    while(t){
        WDTCONbits.ON=1;                    //enable
        WDTCONbits.WDTCLRKEY=0x5743;        //magic sequence to reset WDT
        oldRCON=RCON;                       //dummy read
        RCONCLR=0xDF;                       //clear all reset reason flags
        __builtin_disable_interrupts();      //disable interrupts

        asm volatile( "wait" );             //sleep/idle as per SLPEN

        Nop();
        WDTCONbits.ON=0;                    //disable
        t--;
    }
}

WDT Fuses (PS4096 is 4096ms etc, slowed down so I can watch what it does):

// FWDT
//#pragma config SWDTPS = PS1024       // Sleep Mode Watchdog Timer Postscale Selection bits ~in milliseconds
#pragma config SWDTPS = PS4096       // Sleep Mode Watchdog Timer Postscale Selection bits ~in milliseconds
#pragma config FWDTWINSZ = PS25_0       // Watchdog Timer Window Size bits (Watchdog timer window size is 25%)
#pragma config WINDIS = OFF             // Windowed Watchdog Timer Disable bit (Watchdog timer is in non-window mode)
#pragma config RWDTPS = PS1048576       // Run Mode Watchdog Timer Postscale Selection bits (1:1048576)
#pragma config RCLKSEL = LPRC           // Run Mode Watchdog Timer Clock Source Selection bits (Clock source is LPRC (same as for sleep mode))
#pragma config FWDTEN = OFF             // Watchdog Timer Enable bit 
//#pragma config FWDTEN = ON             // Watchdog Timer Enable bit 

The Power Saving Modes section (61130) of the PIC32 manual gives a code snippet (example 10-1) which is even less than what I've got (eg this code deosn't do the syskey unlock):

// Code example to put the Device in sleep and then wake the device using
// the WDT
OSCCONSET = 0x10; // set Power-Saving mode to Sleep
WDTCONCLR = 0x0002; // Disable WDT window mode
WDTCONSET = 0x8000; // Enable WDT
// WDT time-out period is set in the device
// configuration
... user code ...
WDTCONSET = 0x01; // service the WDT
asm volatile( “wait” ); // put device in selected power-saving mode
// code execution will resume here after wake
... user code ...
// The following code fragment is at the beginning of the ‘C’ start-up code
// to find whether the wake from Sleep is due to the WDT
if ( RCON & 0x18 ) // The WDT caused a wake from Sleep
{
asm volatile( “eret” ); // return from interrupt
}

I've got the eret code in my reset and nmi handler and at the start of main, but it's not even getting to reset. I'm not setting window mode off as its disabled in the fuses, and it shouldn't stop the WDT from triggering anyway.

Most of examples etc seem to be for MX/MZ variants, so I wonder if there is a subtle difference I've missed.

How do I get the PIC32MM to go to sleep and resume on WDT?

Best Answer

It appears the key is clearing the the WDTS flag in RNMICON. According to the datasheet, this should be zero at reset, so the function should work once (but doesn't appear to).

The below code works (ie sleep(1); sleeps for 1 WDT period), although it may still have some unnecessary lines in it.

void sleep(uint32_t t){
    SYSKEY = 0x0;
    SYSKEY = 0xAA996655;                    //unlock
    SYSKEY = 0x556699AA;
    //OSCCONbits.SLPEN=0;                     //enable idle
    OSCCONbits.SLPEN=1;                     //enable sleep
    SYSKEY = 0x0;                           //lock
    while(t){
        RCONCLR=0xDF;                       //clear all reset reason flags
        __builtin_disable_interrupts();
        SYSKEY = 0x0;
        SYSKEY = 0xAA996655;
        SYSKEY = 0x556699AA;
        RNMICONbits.WDTS=0;                 //clear sleep WDT flag
        SYSKEY = 0x0;
        WDTCONbits.WDTCLRKEY=0x5743;        //magic sequence to reset WDT
        WDTCONbits.ON=1;                    //enable
        asm volatile( "wait" );             //sleep/idle as per SLPEN
        Nop();
        WDTCONbits.ON=0;                    //disable
        t--;
    }
}