Modo de suspensión PIC32 con fallo de activación de watchdog

2

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");
    
pregunta Majenko

1 respuesta

1

Ok, ahora tengo esto funcionando correctamente. Y aquí está cómo:

  1. Lee el manual.
  2. Deseche el manual.
  3. Escriba un programa de trabajo en un IDE / compilador diferente.
  4. Desmonte el programa resultante y vea cómo se debe hacer .

XC32 integra el código para manejar la NMI en su controlador de reinicio por usted, y el código que genera no se parece en nada a lo que el manual dice que debe hacer. En su lugar, examina el estado de NMI de C0. Si se trataba de un NMI, realice un eret ; de lo contrario, inicie el programa normalmente.

Aquí están los bits relevantes del programa desmontado:

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

Así que ya ves, no importa por qué sucedió el NMI, solo es eret s independientemente.

¡Con ese estilo de hacerlo en mi rutina de inicio ahora funciona!

    
respondido por el Majenko

Lea otras preguntas en las etiquetas