Restablecer PIC24FV sin bandera

Estoy trabajando en la creación de un flujo de programa basado en interrupciones utilizando el módulo PIC24FV32KA302 de Microchip. Sin embargo, tengo algunos problemas con los reinicios intermitentes (al parecer).

Actualmente estoy usando el simulador MPLAB X, tratando de averiguar dónde está ocurriendo el problema. Originalmente estaba usando un PICKit3 en hardware real, pero me encontré con algunos problemas en los que, después de algunos ciclos a través del bucle principal, el depurador se detenía y arrojaba un error "PC en 0x0", que es una dirección sin una instrucción válida. No creo que estos problemas estén relacionados, pero realmente no puedo eliminar la posibilidad.

En esencia, el flujo de mi programa es el siguiente:

main()
{
   init();
   print_an_init_message();
   while(1)
   {
       check_some_flags();
       if(flags_are_set)
       {
          do_something_about_them();
       }
   }  
}

Durante la simulación, el programa imprime repetidamente el mensaje de inicio. Capta un punto de interrupción en el bucle while (1), por lo que debe estar ingresando. Además, si continúo desde ese punto de interrupción, volverá a él (no se restablecerá). Si salgo de ese punto de interrupción, el depurador falla.

Entonces, traté de imprimir valores del registro RSON (que se supone que se establece durante cualquier condición de reinicio) en el mensaje de inicio. Ninguno de los valores está establecido. Si agrego un retraso simple (XC16 incorporado __delay_ms()) al ciclo while, se llama a la interrupción _AddressError. La instrucción desde la cual se origina el error de dirección siempre es una instrucción ULNK de una de las funciones utilizadas en do_something_about_them() (que representa múltiples indicadores y funciones, parece posible que sea cualquiera de las funciones, pero con mayor frecuencia es la primero o último).

También vale la pena señalar que si pongo un punto de interrupción en la última línea de main (devuelve -1), nunca se alcanza, pero el simulador imprime continuamente el mensaje de inicio.

¿Es probable que esto sea un problema con el simulador o con mi programa? ¿Podría esto estar relacionado con el problema de mi PC 0x0 cuando se ejecuta en hardware real, o es probable que sean diferentes?

Si alguien quiere un código real para ayudar, puedo proporcionarlo; simplemente no lo proporcioné de inmediato porque siento que ofuscaría mi problema real, que no creo que esté causado por mi código C.

¡Gracias de antemano!

Después de un poco de depuración, logré que el error PC 0x0 desapareciera. Todavía estoy tratando de aterrizar en el error de dirección ISR. Esto ahora es programar hardware real con PICKit3.

Creo que el código que está causando el problema es una cola con la que estoy lidiando.

typedef struct uint16_queue_tag {
    uint16_t *contents;
    int front;
    int back;
    int maxSize;
    int cnt;
} uint16_queue;

bool uint16_InitQueue(uint16_queue *queueP, uint8_t queueSize)
{
    uint16_t newContents[queueSize];

    queueP->contents = &newContents[0];
    queueP->maxSize = queueSize;
    queueP->cnt = 0;
    queueP->front = -1;
    queueP->back = -1;

    return true;
}

bool uint16_IsQueueEmpty(uint16_queue *queueP)
{
    return (bool) (queueP->cnt == 0);
}

bool uint16_IsQueueFull(uint16_queue *queueP)
{
    return (bool) (queueP->cnt == queueP->maxSize);
}

bool uint16_ClearQueue(uint16_queue *queueP)
{
    queueP->front = -1;
    queueP->back = -1;
    queueP->cnt = 0;

    return true;
}

bool uint16_PushQueue(uint16_queue *queueP, uint16_t element)
{
    if (uint16_IsQueueFull(queueP))
    {
        return false; // We can't push to the queue, its full
    }
    else
    {
        queueP->back++;
        queueP->contents[(queueP->back % queueP->maxSize)] = element;
        queueP->cnt++;
        return true;
    }
}

uint16_t uint16_PullQueue(uint16_queue *queueP)
{
    if (uint16_IsQueueEmpty(queueP))
    {
        return NULL;
    }
    else
    {
        queueP->front++;
        queueP->cnt--;
        return queueP->contents[(queueP->front % queueP->maxSize)];
    }
}

Parece que todo funciona bien para algunas iteraciones, pero en algún momento se uint16_PushQueue(&queue_obj, value)rompe el código. Después de inicializar la cola, todo lo que hago es verificar si está llena, empujar y tirar. Extrañamente, parece que el valor del puntero de contenido es diferente: después de que se rompe, el puntero apunta al mismo valor que "frente", que definitivamente no es el mismo valor en el que comenzó. Además, MPLAB informa que el elemento en el lugar al que apunta es 0.0 (que definitivamente no es un flotante).

Así que supongo que esto es solo una especie de estupidez en la asignación del puntero, lo que también explica todo el problema de la dirección: de alguna manera me estoy moviendo hacia donde apunta el puntero de contenido.

Respuestas (3)

La forma en que inicializa su cola no está bien:

bool uint16_InitQueue(uint16_queue *queueP, uint8_t queueSize)
{
    uint16_t newContents[queueSize];
    queueP->contents = &newContents[0];
    queueP->maxSize = queueSize;
    queueP->cnt = 0;
    queueP->front = -1;
    queueP->back = -1;
    return true;
}

La newContentsmatriz de longitud variable se inicializará en la uint16_InitQueuepila de . Una vez que la función regrese, queueP->contentsapuntará a la pila y cualquier intento de modificar su contenido corromperá la pila y podría provocar un bloqueo.

Parece que todo funciona bien para algunas iteraciones, pero en algún momento un uint16_PushQueue(&queue_obj, value) rompe el código. Después de inicializar la cola, todo lo que hago es verificar si está llena, empujar y tirar.

Esto tiene mucho sentido, ya que hacer a uint16_PushQueuecorromperá la pila.

La matriz debe asignarse dinámicamente (no es una buena práctica para sistemas integrados sin un sistema operativo) o asignarse globalmente. Al programar un sistema integrado, generalmente establece un tamaño máximo para la cola (o cualquier otro contenedor) en el momento de la compilación.


Algunas sutilezas:

bool uint16_IsQueueEmpty(uint16_queue *queueP)
{
    return (bool) (queueP->cnt == 0);
}

El elenco es totalmente inútil. Puedes hacer: return (queueP->cnt == 0)en su lugar.

Cuanto menos código, mejor.

Esta respuesta parece correcta. Cambié la estructura a uint16_t contenidos[32]; y dejé de inicializar la matriz. Supongo que la esencia de lo que estaba tratando de hacer era crear una nueva matriz, en algún otro lugar de la memoria, y luego señalarla usando la variable de contenido. Sin embargo, esta es la esencia de la asignación, que realmente no puedo hacer en los sistemas integrados, y no funcionó porque lo hice mal :). ¡Agradezco la ayuda!

Suena como uno de los dos posibles problemas. O el código está golpeando un puntero nulo o alguna interrupción está habilitada que no tiene un controlador. En cualquier caso, el código intenta saltar a ninguna parte y ese error podría establecerse.

Para verificar que este es el problema, cree cuatro interrupciones para las trampas integradas. Mire las cuatro interrupciones principales en la sección de interrupciones de la hoja de datos.

  1. Trampa de falla del oscilador
  2. Trampa de error de dirección
  3. Trampa de error de pila
  4. Trampa de error matemático

Depure el código como lo hacía antes y verifique si cae en la trampa de error de Dirección. Esto confirmaría el problema.

Resultado del simulador (no tengo hardware conmigo, lo revisaré más tarde) - Sin demora en while(1): imprime init repetidamente. Con retraso en while(1): Queda atrapado en _AddressError ISR, "Trampa debido a error de pila, acceso a memoria FLASH no implementado, acceso a RAM no implementado" instrucción 0x000A9C, que corresponde a la instrucción ULNK al final de mi función DelayMS (simplemente se hornea ClrWdt y __delay_ms integrados). También habilité una interrupción predeterminada, no aterrizo en eso en absoluto.
Así que aterrizo en la rutina de captura _AddressError, pero el hecho de que no aterrice en _DefaultInterrupt en absoluto significa que no tengo una interrupción adicional habilitada. ¿Puntero nulo entonces? Simplemente parece peculiar que siempre suceda en la instrucción ULNK de una llamada en el ciclo while(1).
@KenK No conozco el ensamblador, pero la instrucción ULNK tiene que ver con los punteros "Unlink Frame Pointer". Sospecharía un problema de puntero nulo.
Parece que podría ser un problema con mi función Delay, lo cual es raro, porque consiste en ClrWdt() y __delay_ms(). No puedo ver por qué eso causaría un puntero nulo; de lo contrario, hago ambas cosas y funciona bien. Creo que podría necesitar probar esto en el hardware real (no en el simulador) antes de poder llegar a alguna parte...

Parece que podría tener un problema con el temporizador de vigilancia. Asegúrese de que el mecanismo de vigilancia esté de hecho desactivado o que esté restableciendo correctamente el temporizador del mecanismo de vigilancia. A veces, el temporizador de vigilancia está habilitado como opción predeterminada. Así que creo que el programa es el problema aquí. Y el problema que está experimentando en el hardware es que la misma PC en 0x0 significa literalmente que el contador del programa vuelve a cero, en otras palabras, el hardware se ha reiniciado.

¡Gracias por su respuesta! Estoy usando el temporizador de vigilancia, pero llamo al comando ClrWdt() al comienzo de cada ciclo hasta while(1), y antes de cada retraso. El WDT también se establece con el preescalador máximo. Acabo de intentar apagar el WDT, el mismo problema. Además, RCONbits.WDTO no se establece en el reinicio, lo que implica que WDT no fue la causa.