Estoy intentando que un PIC32MX795F512L funcione con un menor consumo de energía. Estoy tratando de hacer que entre en modo de suspensión y que me despierte el temporizador de vigilancia. Todas las cosas bastante estándar.
Mi código funciona en la primera activación del temporizador de vigilancia, pero la segunda activación no se activa como si estuviera en modo de suspensión, sino en el modo de funcionamiento normal. Como resultado, no continúa la ejecución, sino que restablece todo el chip.
En el PIC32, el temporizador de vigilancia, cuando está en modo inactivo o inactivo, provoca un NMI con el mismo vector que el reinicio. A continuación, verifica algunas banderas en un registro para ver si fue causada por el perro guardián en modo de suspensión, etc.
Mi código de inicio se ve así:
_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
Básicamente, busca 0x18 o 0x14 en el registro RCON, luego borra esos bits y regresa de una interrupción si están configurados.
El modo de reposo se ingresa configurando el bit SLEEP en OSCCON (que debe desbloquearse primero), y de acuerdo con el manual de ahorro de energía para PIC32, se realiza de la siguiente manera:
// Standard unlock sequence
SYSKEY = 0x0;
SYSKEY = 0xAA996655;
SYSKEY = 0x556699AA;
OSCCONSET = 0x10; // Enable sleep mode
SYSKEY = 0x0;
Luego, habilita el temporizador de vigilancia, "patea al perro" como se conoce, y detiene la CPU con una instrucción wait
:
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);
Eso hace que la CPU se detenga y luego el NMI se dispara después de 1.024 segundos. La CPU se reinicia desde el vector de reinicio, el código de inicio comprueba las banderas, encuentra que es un NMI y regresa de la interrupción y continúa con la siguiente línea de código.
La primera vez.
La segunda vez, el registro RCON contiene 0x10 en lugar de 0x18, por lo que actúa como un tiempo de espera para no patear al perro.
Al inspeccionar OSCCON después del tiempo de espera fallido, el bit SLEEP parece haberse restablecido. Establecer el bit SLEEP cada vez que pasa por el bucle principal justo antes de dormir no tiene efecto.
Sin embargo
Si hago exactamente lo mismo pero utilizando el modo inactivo en lugar del modo de suspensión, todo funciona perfectamente. La instrucción wait
continúa después de 1.024 segundos cada vez sin error.
Entonces, ¿por qué esto no funciona como debería hacerlo en el modo de suspensión?
¿Hay algo obvio que me estoy perdiendo?
Actualizar
He intentado forzar que el nivel de prioridad de la CPU sea el más bajo posible antes de dormir, pero no ha tenido ningún efecto. Este es el código que estoy usando para ello:
asm volatile("mfc0 $8, $12");
asm volatile("ins $8, $0, 10, 3");
asm volatile("mtc0 $8, $12");
asm volatile("wait");