PIC24FV Reset ohne Flag

Ich arbeite daran, einen Interrupt-basierten Programmablauf mit dem PIC24FV32KA302-Modul von Microchip zu erstellen. Allerdings habe ich einige Probleme mit intermittierenden Resets (scheint es).

Ich verwende derzeit den MPLAB X Simulator und versuche herauszufinden, wo das Problem auftritt. Ich habe ursprünglich ein PICKit3 auf echter Hardware verwendet, aber ich bin auf einige Probleme gestoßen, bei denen der Debugger nach einigen Zyklen durch die Hauptschleife angehalten und einen Fehler "PC bei 0x0" ausgegeben hat, was eine Adresse ohne gültige Anweisung ist. Ich glaube nicht, dass diese Probleme zusammenhängen, aber ich kann die Möglichkeit nicht wirklich ausschließen.

Im Wesentlichen ist mein Programmablauf wie folgt:

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

Während der Simulation gibt das Programm wiederholt die Initialisierungsnachricht aus. Es fängt an einem Haltepunkt in der While(1)-Schleife ab - also muss es eintreten. Wenn ich von diesem Haltepunkt aus fortfahre, wird es auch dorthin geschleift (nicht zurückgesetzt). Wenn ich diesen Breakpoint verlasse, stürzt der Debugger ab.

Also habe ich versucht, Werte aus dem RSON-Register (das während eines Reset-Zustands gesetzt werden soll) in der Init-Nachricht auszudrucken. Keiner der Werte ist gesetzt. Wenn ich der While-Schleife eine einfache Verzögerung (eingebautes XC16 __delay_ms()) hinzufüge, wird der _AddressError-Interrupt aufgerufen. Die Anweisung, von der der Adressfehler ausgeht, ist immer eine ULNK-Anweisung von einer der in do_something_about_them() verwendeten Funktionen (die mehrere Flags und Funktionen darstellt, es scheint möglich zu sein, dass es sich um eine der Funktionen handelt, aber meistens ist es die zuerst oder zuletzt).

Erwähnenswert ist auch, dass, wenn ich einen Haltepunkt in die letzte Zeile von main (return -1) setze, dieser nie getroffen wird, aber der Simulator druckt kontinuierlich die Init-Nachricht.

Ist dies wahrscheinlich ein Problem mit dem Simulator oder meinem Programm? Könnte dies mit meinem PC-0x0-Problem zusammenhängen, wenn es auf echter Hardware ausgeführt wird, oder sind diese wahrscheinlich anders?

Wenn jemand echten Code als Hilfe haben möchte, kann ich ihn bereitstellen - ich habe ihn nur nicht sofort bereitgestellt, weil ich das Gefühl habe, dass er mein eigentliches Problem verschleiern würde, von dem ich nicht glaube, dass es von meinem C-Code verursacht wird.

Vielen Dank im Voraus!

Nach ein wenig Debugging gelang es mir, den PC 0x0-Fehler zu beseitigen. Ich beschäftige mich immer noch mit der Landung im Adressfehler ISR. Dieser programmiert nun echte Hardware mit dem PICKit3.

Ich glaube, der Code, der das Problem verursacht, ist eine Warteschlange, mit der ich es zu tun habe.

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)];
    }
}

Dies scheint alles für einige Iterationen gut zu funktionieren, aber irgendwann uint16_PushQueue(&queue_obj, value)bricht der Code. Nachdem die Warteschlange initialisiert wurde, prüfe ich nur, ob sie voll ist, Push und Pull. Seltsamerweise scheint der Wert des Inhaltszeigers anders zu sein - nachdem er unterbrochen wurde, zeigt der Zeiger auf denselben Wert wie "front", was definitiv nicht derselbe Wert ist, mit dem er gestartet wurde. Außerdem wird das Element an der Stelle, auf die es zeigt, von MPLAB als 0,0 gemeldet (was definitiv kein Float ist).

Ich denke, das ist nur eine Art Dummheit bei der Zeigerzuweisung, was auch das ganze Adressproblem erklärt - ich bewege mich irgendwie dahin, wohin der Inhaltszeiger zeigt.

Antworten (3)

Die Art und Weise, wie Sie Ihre Warteschlange initialisieren, ist nicht in Ordnung:

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;
}

Das newContentsArray mit variabler Länge wird auf dem uint16_InitQueueStack von initialisiert. Sobald die Funktion zurückkehrt, queueP->contentszeigt das auf den Stack und jeder Versuch, seinen Inhalt zu ändern, wird den Stack beschädigen und möglicherweise einen Absturz verursachen.

Dies scheint alles für einige Iterationen gut zu funktionieren, aber irgendwann unterbricht ein uint16_PushQueue(&queue_obj, value) den Code. Nachdem die Warteschlange initialisiert wurde, prüfe ich nur, ob sie voll ist, Push und Pull.

Dies ist absolut sinnvoll, da das Ausführen von uint16_PushQueuea den Stapel beschädigt.

Das Array muss entweder dynamisch zugewiesen werden (keine gute Praxis für eingebettete Systeme ohne Betriebssystem) oder global zugewiesen werden. Beim Programmieren eingebetteter Systeme legen Sie normalerweise zur Kompilierzeit eine maximale Größe für die Warteschlange (oder jeden anderen Container) fest.


Einige Spitzfindigkeiten:

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

Die Besetzung ist völlig unbrauchbar. Sie können return (queueP->cnt == 0)stattdessen :

Je weniger Code, desto besser.

Diese Antwort scheint richtig zu sein. Ich habe die Struktur in uint16_t content[32]; geändert und die Initialisierung des Arrays gestoppt. Ich nehme an, die Essenz dessen, was ich zu tun versuchte, war, ein neues Array an einer anderen Stelle im Speicher zu erstellen und dann mit der Inhaltsvariablen darauf zu zeigen. Dies ist jedoch die Essenz der Zuordnung, die ich in eingebetteten Systemen nicht wirklich ausführen kann und nicht funktioniert hat, weil ich es falsch gemacht habe :). Ich weiß die Hilfe zu schätzen!

Das klingt nach einem von zwei möglichen Problemen. Entweder trifft der Code auf einen Nullzeiger oder ein Interrupt ist aktiviert, der keinen Handler hat. In beiden Fällen versucht der Code, effektiv ins Nirgendwo zu springen, und dieser Fehler könnte gesetzt werden.

Um zu überprüfen, ob dies das Problem ist, erstellen Sie vier Interrupts für die eingebauten Traps. Sehen Sie sich die vier obersten Interrupts im Interrupt-Abschnitt des Datenblatts an.

  1. Oszillatorausfallfalle
  2. Fehlerfalle adressieren
  3. Stack-Fehlerfalle
  4. Mathematische Fehlerfalle

Debuggen Sie den Code wie zuvor und prüfen Sie, ob Sie in der Adressfehlerfalle landen. Dies würde das Problem bestätigen.

Simulator-Ergebnis (ich habe keine Hardware dabei, werde später nachsehen) - Ohne Verzögerung in while(1): gibt wiederholt init aus. Mit Verzögerung in while(1): Wird in _AddressError ISR, „Trap due to Stack Error, nicht implementierter FLASH-Speicherzugriff, nicht implementierter RAM-Zugriff“-Anweisung 0x000A9C abgefangen, die der ULNK-Anweisung am Ende meiner DelayMS-Funktion entspricht (backt einfach zusammen ClrWdt und __delay_ms eingebaut). Ich habe auch einen Default-Interrupt aktiviert, darin lande ich überhaupt nicht.
Ich lande also in der _AddressError-Trap-Routine, aber die Tatsache, dass ich überhaupt nicht in _DefaultInterrupt lande, bedeutet, dass ich keinen zusätzlichen Interrupt aktiviert habe. Nullzeiger also? Es scheint nur seltsam, dass dies immer bei der ULNK-Anweisung eines Aufrufs in der while(1)-Schleife passiert.
@KenK Ich kenne Assembler nicht, aber die ULNK-Anweisung hat mit Zeigern "Unlink Frame Pointer" zu tun. Ich würde ein Nullzeigerproblem vermuten.
Es sieht so aus, als ob es ein Problem mit meiner Delay-Funktion geben könnte, was seltsam ist, weil sie aus ClrWdt() und __delay_ms() besteht. Ich kann nicht sehen, warum das einen Nullzeiger verursachen würde - ich mache beides sonst und es funktioniert gut. Ich denke, ich muss das vielleicht auf der tatsächlichen Hardware (nicht im Simulator) ausprobieren, bevor ich irgendwo hinkomme ...

Es scheint, dass Sie ein Watchdog-Timer-Problem haben könnten. Stellen Sie sicher, dass der Watchdog tatsächlich deaktiviert ist oder dass Sie den Watchdog-Timer ordnungsgemäß zurücksetzen. Manchmal ist der Watchdog-Timer als Standardoption aktiviert. Ich glaube also, dass das Programm hier das Problem ist. Und das Problem, das Sie in der Hardware haben, ist, dass derselbe PC bei 0x0 buchstäblich bedeutet, dass der Programmzähler auf Null zurückgesetzt wurde, mit anderen Worten, die Hardware wurde zurückgesetzt.

Vielen Dank für Ihre Antwort! Ich verwende den Watchdog-Timer, aber ich rufe den Befehl ClrWdt() zu Beginn jedes Zyklus durch while(1) und vor jeder Verzögerung auf. Auch der WDT wird mit dem maximalen Prescaler eingestellt. Ich habe gerade versucht, den WDT auszuschalten, das gleiche Problem. Außerdem wird RCONbits.WDTO beim Zurücksetzen nicht gesetzt, was impliziert, dass das WDT nicht die Ursache war.