I am in the process of trying to get a PIC32MX795F512L to run with lower power consumption. I'm trying to get it to enter sleep mode and then be woken by the watchdog timer. All pretty standard stuff.
My code works on the first triggering of the watchdog timer, but the second triggering isn't triggered as if it were in sleep mode, but in normal running mode. As a result it doesn't continue execution but instead resets the whole chip.
On the PIC32 the watchdog timer, when in sleep or idle mode, causes an NMI with the same vector as reset. You then check some flags in a register to see if it was caused by the watchdog in sleep mode, etc.
My startup code looks like this:
_reset: la k0, RCON # Load address of RCON register lw k1, 0(k0) # Get contents of the register and k1, k1, 0x18 # We are only interested in 0x18 sub k1, 0x18 # Subtract 0x18 beqz k1, _ret_nmi # and if the result is 0 (i.e., equal to 0x18) then branch. lw k1, 0(k0) # Same again but looking for 0x14. and k1, k1, 0x14 sub k1, 0x14 beqz k1, _ret_nmi nop la k0, _startup jr k0 # Jump to startup code nop _ret_nmi: lw k1, 0(k0) and k1, 0xFFE3 sw zero, 0(k0) eret
Basically it looks for 0x18 or 0x14 being set in the RCON register, then clearing those bits and returning from an interrupt if they are set.
Sleep mode is entered by setting the SLEEP bit in OSCCON (which needs unlocking first), and according to the power saving manual for PIC32 is done like this:
// Standard unlock sequence SYSKEY = 0x0; SYSKEY = 0xAA996655; SYSKEY = 0x556699AA; OSCCONSET = 0x10; // Enable sleep mode SYSKEY = 0x0;
You then enable the watchdog timer, "kick the dog" as it's known, and stop the CPU with a
WDTCONSET = 1<<15; // Turn on WDTCONSET = 0x01; // Kick the dog! uint32_t i = disableInterrupts(); // We don't want any old interrupt waking us up asm volatile("wait"); restoreInterrupts(i);
So that causes the CPU to stop and then the NMI triggers after 1.024 seconds. The CPU restarts from the reset vector, the startup code checks the flags, finds it's an NMI, and returns from interrupt continuing with the next line of code.
The first time.
The second time the RCON register contains 0x10 instead of 0x18, so it acts like a timeout from not kicking the dog.
Inspecting OSCCON after the failed timeout the SLEEP bit seems to have been reset. Setting the SLEEP bit every time through the main loop just before sleeping has no effect.
If I do the exact same thing but using idle mode instead of sleep mode, everything works perfectly. The
wait instruction continues after 1.024 seconds every time without failure.
So why does this not work as it should do in sleep mode?
Is there something obvious I am missing?
I have tried forcing the CPU priority level to be the lowest possible before sleeping, but it has had no effect. This is the code I am using for it:
asm volatile("mfc0 $8, $12"); asm volatile("ins $8, $0, 10, 3"); asm volatile("mtc0 $8, $12"); asm volatile("wait");