Estoy en el proceso de intentar que un PIC32MX795F512L funcione con un menor consumo de energía. Estoy tratando de hacer que entre en modo de suspensión y luego 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 modo de ejecución normal. Como resultado, no continúa la ejecución, sino que reinicia todo el chip.
En el PIC32, el temporizador de vigilancia, cuando está en modo de reposo o inactivo, provoca un NMI con el mismo vector que el reinicio. Luego verifica algunas banderas en un registro para ver si fue causado 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 configurados en el registro RCON, luego borra esos bits y regresa de una interrupción si están configurados.
El modo de suspensión se ingresa configurando el bit SLEEP en OSCCON (que primero debe desbloquearse), y de acuerdo con el manual de ahorro de energía para PIC32 se hace así:
// 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 wait
instrucción:
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 activa después de 1.024 segundos. La CPU se reinicia desde el vector de reinicio, el código de inicio verifica 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 que el registro RCON contiene 0x10 en lugar de 0x18, 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 reiniciado. Establecer el bit SLEEP cada vez que pasa por el bucle principal justo antes de dormir no tiene ningún efecto.
Sin embargo
Si hago exactamente lo mismo pero usando el modo inactivo en lugar del modo de suspensión, todo funciona perfectamente. La wait
instrucción continúa después de 1,024 segundos cada vez sin fallar.
Entonces, ¿por qué esto no funciona como debería en el modo de suspensión?
¿Hay algo obvio que me estoy perdiendo?
Actualizar
He intentado forzar el nivel de prioridad de la CPU para que 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");
Ok, ahora tengo esto funcionando correctamente. Y así es como:
XC32 incorpora un código para manejar el NMI en su controlador de reinicio por usted, y el código que genera no se parece a lo que el manual dice que debe hacer. En su lugar, examina el estado NMI de C0. Si era un NMI, haga un eret
, de lo contrario, inicie el programa normalmente.
Aquí están las partes relevantes del programa desmontadas:
bfc00000: 401a6000 mfc0 k0,c0_status
bfc00004: 7f5a04c0 ext k0,k0,0x13,0x1
bfc00008: 13400005 beqz k0,bfc00020 <_no_nmi>
bfc0000c: 00000000 nop
bfc00010: 3c1a9d00 lui k0,0x9d00
bfc00014: 275a02a8 addiu k0,k0,680
bfc00018: 03400008 jr k0 <_nmi_handler>
bfc0001c: 00000000 nop
9d0002a8 <_nmi_handler>:
9d0002a8: 401a6000 mfc0 k0,c0_status
9d0002ac: 3c1bffbf lui k1,0xffbf
9d0002b0: 377bffff ori k1,k1,0xffff
9d0002b4: 035bd024 and k0,k0,k1
9d0002b8: 409a6000 mtc0 k0,c0_status
9d0002bc: 42000018 eret
Como puede ver, no importa por qué sucedió el NMI, simplemente eret
es independientemente.
Con ese estilo de hacerlo en mi rutina de inicio, ¡ahora funciona!
evan
Majenko
Majenko
evan
Majenko
wait
casi de inmediato debido a otras interrupciones. Con ellos desactivados se comporta. Sin embargo, dormir no, es lo mismo.evan
Majenko
evan
Majenko
Majenko