2017-06-24 6 views
1

In externen Interrupt-Funktion, möchte ich durch den Aufruf der Hauptfunktion zurückgesetzt werden. Aber danach, wenn ich einen neuen Interrupt-Trigger habe, denkt die MCU, dass sie in der Interrupt-Funktion handhabt und die Interrupt-Funktion nicht erneut aufruft. Was ist meine Lösung? (In meinem Projekt, ich bin nicht zu nennen Soft-Reset-Funktion erlaubt)stm32 - Interrupt-Handle

+2

Wenn Sie die 'main' Funktion aufrufen, führen Sie keinen Reset durch - Sie rufen nur eine der Funktionen auf. Tatsächlich ist "main" nicht einmal die erste Funktion, die normalerweise nach dem Einschalten oder Zurücksetzen aufgerufen wird. Die eigentliche Frage ist - wenn Sie einen Reset durchführen möchten, warum können Sie nicht eine Reset-Funktion aufrufen? Das ist technisch der richtige Weg, um den Reset durchzuführen. –

+2

"nicht erlaubt ..."? Was für eine irrationale, schlecht informierte Einschränkung ist das? Es gibt möglicherweise echte Gründe, warum Sie nicht möchten, wie die Notwendigkeit, GPIO-Status zu halten und Ausgaben nicht schweben zu lassen, aber "nicht erlaubt" ist kein vernünftiges Argument. – Clifford

+0

@Clifford Ich arbeite mit USB-Peripheriegeräten. Dies erlaubt kein Zurücksetzen des Systems. (Ich benutze "erlauben", es kann falsch oder verwirrt sein, weil mein Englisch nicht gut klingt) –

Antwort

4

Aufruf main() auf jedem Fall eine schlechte Idee ist, es von einem Interrupt-Handler Aufruf ist eine wirklich schlechte Idee, wie Sie entdeckt haben.

Was Sie wirklich brauchen, ist der Stapel und Link-Register zu ändern, so dass, wenn die Interrupt-Kontext Ausfahrten ,, es „Erträge“ zu main(), anstatt von woher sie kam. Das ist eine nicht-triviale Aufgabe, die wahrscheinlich Assembler-Code oder Compiler-Intrinsics erfordert.

Sie müssen feststellen, dass die Hardware nicht in den Reset-Zustand zurückgesetzt wurde; Sie müssen wahrscheinlich mindestens alle Interrupts deaktivieren, um zu verhindern, dass sie auftreten, während das System neu initialisiert wird.

Außerdem wird die Standardbibliothek nicht neu initialisiert, wenn Sie zu main() wechseln; eher als der Reset-Vektor. Insbesondere wird derzeit zugewiesener dynamischer Speicher sofort verloren gehen und unbrauchbar werden. In der Tat alle der C-Laufzeit-Umgebung Initialisierung wird übersprungen werden - unter zum Beispiel static und globale Daten in seinem letzten Zustand, anstatt die richtige Initialisierung.

Kurz gesagt ist es gefährlich, fehleranfällig, zielspezifisch und grundsätzlich schlecht. Das meiste, was Sie tun müssten, um es zum Laufen zu bringen, ist bereits in dem Start-Code gemacht, der ausgeführt wird bevormain() aufgerufen wird, also wäre es viel einfacher, das aufzurufen. Der Unterschied zwischen diesem und dem Erzwingen eines echten Rücksetzens (über den Watchdog oder AICR) besteht darin, dass der On-Chip-Peripheriestatus unberührt bleibt (abgesehen von jeglicher Initialisierung, die beim Start explizit durchgeführt wird). Wenn Sie ein komplexeres Peripheriegerät wie USB verwenden, ist es nach meiner Erfahrung schwierig, das System ohne einen echten Reset sicher neu zu starten (oder es ist zumindest schwer zu bestimmen, wie es sicher funktioniert) und es lohnt sich kaum.

+0

Ein 'longjmp' wird Sie zurück zu' main' leicht genug auf ARM bringen (bitte maskieren Sie alle höher priorisierten Interrupts zuerst). Wie Sie festgestellt haben, besteht das eigentliche Problem darin, den Zustand des Systems sowohl in Bezug auf Peripheriegeräte als auch auf Submodule zuverlässig zurückzusetzen und möglichst zu vermeiden. Gelegentlich benötigen Sie einen _partial_ System Reset und die Dinge werden chaotisch, aber selbst dann blind blind den ganzen Weg zurück zu "Haupt" zu entlasten unwahrscheinlich, um die Antwort zu sein. – doynax

+1

@doynax: 'longjmp()' wird den Stapelkontext wiederherstellen, aber ich glaube nicht, dass Sie aus dem Interruptkontext herauskommen, und Sie würden mit der (wahrscheinlich nicht terminierenden) 'main()' -Funktion im Wesentlichen laufen als ein Unterbrechungshandler, der genau das fragliche Problem ist. Es löst nur das Problem des Wiedereintritts von 'main()', es löst nicht das Problem, den Interrupt-Kontext zu verlassen. – Clifford

+0

@Clifford - Sie sind 100% richtig. Dies ist ein Beispiel für die extrem schlechte Programmierung. Statt die richtige Programmlogik zu haben, versucht jemand eine Prothese zu implementieren. Dies zeigt deutlich, dass der Programmierer seine Programmierkenntnisse verbessern muss. –

2

Reset by calling main() is wrong. Es gibt einen Code vor dem Main, der vom Linker und der C-Runtime eingefügt wird, den Sie mit einem solchen Soft-Reset überspringen.

Rufen Sie stattdessen NVIC_SystemReset() an oder aktivieren Sie IWDG und while(1){} zum Zurücksetzen.
Die HAL sollte Beispieldateien für den Watchdog-Timer haben.

SRAM wird beibehalten. Jeder Wert, der nicht vom Linker-Skript initialisiert wird, ist weiterhin vorhanden.

2

Das Aufrufen von Main() von einem beliebigen Punkt Ihres Codes ist eine falsche Idee, wenn Sie den Stapel nicht zurücksetzen und die Anfangswerte festlegen.

Es gibt immer eine Initialisierungsfunktion (die tatsächlich Main() aufruft), die sich in einem Interrupt-Vektor befindet. Normalerweise kann diese Funktion durch Aufruf der Funktion NVIC_SystemReset(void) ausgelöst werden, damit dieser Interrupt aktiviert wird.

Soweit ich weiß, wenn nach innen erhalten und Code unterbrechen, sind andere Unterbrechungen Sperre, ich auf zwei verschiedene Möglichkeiten denke:

  • die Unterbrechungen innerhalb der Unterbrechung aktivieren und rufen Sie die Funktion NVIC_SystemReset(void)
  • Ändern Sie den Stapel und drücken Sie die Richtung der Funktion NVIC_SystemReset(void), so dass, wenn Sie aus der Unterbrechung herausgehen, könnte es ausgeführt werden.