Software-Timer und Interrupts auf einem Mikrocontroller

Ich habe einige Fragen zu Software-Timern und Interrupts auf einem Mikrocontroller. Nur zur Information, ich verwende einen dsPIC33E-Mikrocontroller.

Das Endziel ist die Implementierung eines seriellen Kommunikationsprotokolls: RS485 mit Modbus. Ich habe es geschafft, eine Nachricht zu senden und zu empfangen, und jetzt muss ich einen Teil der Nachrichtenverarbeitung durchführen.

Da ich eine Reihe von Timern für alle möglichen Aufgaben benötige (z. B. 3,5-Zeichen-Verzögerung für die serielle Kommunikation, eine gewisse Verzögerung für das Entprellen von Tasten usw.), plane ich, Timer in einer Software mit einem einzigen Hardware-Timer zu implementieren. Beispielsweise wird die Hardware-Timer-Periode auf 100 us eingestellt, und bei jedem "Überlauf", dh alle 100 us, wird ein Interrupt erzeugt. In der ISR aktualisiere ich nur globale Zähler, die im Grunde Software-Timer mit einer Auflösung von 100 us sind, während die Hardware-Timer-ISR die höchste Priorität hat. Ist dies ein guter Weg, dies zu tun, oder gibt es einen besseren Weg?

Jeder Zähler (dh ein Software-Timer) hat seine eigene Periode definiert, und sobald der Zähler seinen "Periodenwert" erreicht, möchte ich eine Funktion aufrufen. Nun ist es keine sehr gute Idee, diese Funktion innerhalb der Hardware-Timer-ISR aufzurufen, da diese Funktion mit der höchsten Priorität verarbeitet wird, da die aufrufende Routine die Hardware-Timer-ISR ist. Was ich möchte, ist eine Funktion zu definieren, sagen wir:

void Modbus_Protocol(void);

und ihn als Interrupt definieren zu können, der nicht die höchste Priorität hat. Nur der Main-Hardware-Timer hat in diesem Konzept die höchste Priorität. Auf diese Weise würde der Zähler im Hardware-Timer ISR, sobald er seinen Periodenwert erreicht, seine Funktion nicht aufrufen, sondern nur ein Flag setzen, um einen Interrupt auszulösen (z. B. void Modbus_Protocol(void) ), was sein wird ausgelöst, nachdem die Haupt-ISR zurückgekehrt ist, abhängig von ihrer Priorität. Ist so etwas möglich, dh kann ich Software-Interrupts definieren?

Ich weiß, dass es eine Möglichkeit gibt, Hardware-Interrupts zu verwenden, die nicht verwendet werden, und einfach ein Interrupt-Flag innerhalb der Software zu setzen. Aber ich denke nicht, dass dies eine elegante Möglichkeit ist, Interrupts zu implementieren, wenn benutzerdefinierte Software-Interrupts möglich sind.

Haben Sie versucht, Ihren Code mit einem zweiten Timer-Interrupt zu implementieren, um ein Flag zu setzen, um Ihren Code dann in Ihrer Haupt-While-Schleife auszuführen?
Ich kann dies problemlos mit einem Hardware-Interrupt (Timer) implementieren, möchte aber aus den in der Frage genannten Gründen stattdessen Software-Timer verwenden. In diesem Fall benötige ich auch Software-Interrupts, da Software-Timer innerhalb des ISR mit der höchsten Priorität aktualisiert werden und ich keine anderen Funktionen innerhalb dieses Interrupts aufrufen möchte.
Sie können den Interrupt verwenden, um die verschiedenen Zeiten für jede Aufgabe zu zählen, von denen Sie, glaube ich, sagen, dass sie unterschiedliche Zeitintervalle haben. if(aflag) { do a } if(bflag) { do b } und so weiter in der Hauptschleife im Vordergrund, wobei der Interrupt-Handler die Flags zum geeigneten Zeitpunkt setzt. Grundsätzlich müssen Sie keine alternativen Interrupts verwenden, Sie können einfach die Vordergrundaufgabe haben, diese Flags abzufragen. Das Abtasten der Schaltflächen können Sie im 100us-Timer-Interrupt-Handler ausführen, nicht jedes Mal, wenn nötig, jedes N-te Mal. Speichern Sie dann das Ergebnis in einem Flag, das die Vordergrundaufgabe sieht, wenn sie eine Chance bekommt.
Wenn der Handler nur einen vorskalierten 100us-basierten Timer wie Batuus Antwort generiert, benötigen Sie möglicherweise überhaupt keine Interrupt-Komplikation, verwenden möglicherweise einen Hardware-Prescaler und jeder fragt den realen Timer anstelle eines globalen künstlichen Soft-Zählers ab. Bitbangst du in erster Linie seriell? Holen Sie sich ein anderes Teil mit einem UART, wenn dies der Fall ist.
Ich bitbange nicht seriell, ich benutze dafür einen dedizierten UART. Was meinen Sie mit "einem Hardware-Prescaler" und "dem Realtimer"? Ich habe versucht, den CPU-Timer zu googeln, aber ich habe nichts gefunden. Gibt es ein Register, das bei jedem CPU-Tick aktualisiert wird?

Antworten (2)

Sie benötigen keine speziellen "Software-Interrupts". Sehen Sie sich den folgenden Code an. Aus Gründen der Übersichtlichkeit habe ich einige technische Dinge wie die Variablendeklaration vernachlässigt.

Verwenden Sie Ihre ISR, um einfach einen Timer (Tick) zu zählen. In main() warten Sie darauf, mit diesem Timer zu synchronisieren. Verwenden Sie für jede Aufgabe, die Sie bearbeiten müssen, einen eigenen Timer. Alle diese Timer werden bei jedem ISR-Tick inkrementiert.

Wenn der Aufgabenzeitgeber abläuft, wird die dedizierte Aufgabe verarbeitet. Bei dieser Art der Implementierung gibt es einige Besonderheiten. Wenn Sie die EXPITED-Zeit von Ihrem Timer subtrahieren, anstatt sie auf 0 zu setzen, ist Ihre Software robuster, wenn eine der Aufgaben länger als 100 µs dauert. Es ist eine Art Soft-Realtime-Kriterium.

Sie erhalten ein Pseudo-Multitasking-System.

isr() //100µs
{
    tick++;
}

main()
{    
  while(true)
  {
    while(tick == last_tick);
    last_tick = tick;

    modbus_timer++;
    task1_timer++;
    task2_timer++;

    if(modbus_timer >= MODEBUS_TIMER_EXPIRED)
    {
      modbus_timer -= MODEBUS_TIMER_EXPIRED;
      Modbus_Protocol();
    }

    if(task1_timer >= TASK1_TIMER_EXPIRED)
    {
      task1_timer -= TASK1_TIMER_EXPIRED;
      Task1();
    }

    if(task2_timer >= TASK2_TIMER_EXPIRED)
    {
      task2_timer -= TASK2_TIMER_EXPIRED;
      Task2();
    }
  }//forever-loop
}// main()
Dies ist natürlich eine weitere Möglichkeit, "Software-Timer" zu implementieren. Aber auf diese Weise kann ich "Interrupts" keine Prioritätsstufen zuweisen. ZB möchte ich, dass ein Software-Interrupt Modbus_Protocol() eine höhere Priorität hat als ein anderer Hardware-Interrupt. Ich glaube nicht, dass ich das mit deinem Beispiel machen kann. Eine andere Sache - ich werde diese Timer nicht ständig verwenden, also suche ich nicht wirklich nach einem Echtzeitbetrieb. Damit meine ich, dass ein Modbus-Timer nur gestartet wird, wenn der UART eine Nachricht empfängt, und sobald die Nachricht empfangen wird, stoppt der Timer und wird zurückgesetzt.
Zwei Punkte: Denken Sie über Ihre Softwarearchitektur nach. Modbus_Protocol() scheint eine Art Interpreter/Parser für Modbus-Nachrichten zu sein. Normalerweise ist dies eine Aufgabe mit niedriger Priorität. Verwenden Sie Polling oder usart isr, um die Zeichen/Frames auf der Hardware-E/A zu empfangen, und legen Sie diese Frames in eine Software-Warteschlange oder Fifo. Wenn der Rahmen vollständig ist, rufen Sie Ihren Dolmetscher an (niedrige Priorität).
Zweitens: Abhängig von Ihrem spezifischen Controller können Sie einige Interrupts maskieren, um die Task-Priorität auf main()-Ebene gegenüber Hardware-Interrupts zu realisieren. Aber normalerweise bevorzuge ich den ersten Ansatz, weil er deterministischer ist.
Genau so werde ich den Modbus-Teil implementieren - ein Interrupt mit hoher Priorität (UART_RX ISR) füllt einen Puffer, und sobald die gesamte Nachricht empfangen wurde, wird ein Modbus-Interpreter (niedrige Priorität) aufgerufen, um den Puffer zu leeren und zu die Botschaft interpretieren. Aber es wäre viel flexibler, wenn ich eine Funktion (z. B. einen Modbus-Interpreter oder etwas anderes) definieren könnte, die als Interrupt deklariert wird. Auf diese Weise muss ich mir keine Gedanken darüber machen, welche ISR welche Funktion aufruft, da die Funktion aufgerufen wird, indem ihr Interrupt-Flag gesetzt wird. Auch hier weiß ich, dass ich andere ungenutzte Hardware-Interrupts verwenden kann.
Modifizieren Sie mein Beispiel. Deklarieren Sie ein Flag "modbus_frame_received" und setzen Sie es in Ihrem usart-Handler auf true, falls dies der Fall ist. Anstatt auf einen modbus_timer zu warten, mache einfach if(modbus_frame_received) {Modbus_Protocol(); modbus_frame_received = falsch; }. Hier erhalten Sie eine maximale Verzögerung/Jitter von 100 µs zwischen Empfang abgeschlossen und Interpreter-Aufruf. IMHO ist es nicht erforderlich, dies mit einer Interrupt-Priorität zu tun. Der Umgang mit anderen Hardwareressourcen ist normalerweise wichtiger als Softwareprotokollkram. In den meisten Fällen ist die Kommunikations-E/A ein asynchroner Prozess.
Vielen Dank für Vorschläge, aber ich brauche unabhängig von diesem Beispiel Software-Interrupts. Nehmen wir an, ich habe zwei "Software-Interrupts": (i) einen Interpreter, dessen Verarbeitung 10 ms benötigt, und (ii) einen Entprellalgorithmus, dessen Verarbeitung 10 us benötigt. An einem Punkt wird der Interpreter von innerhalb der Hauptschleife aufgerufen. Das Problem besteht darin, dass der Entprellalgorithmus warten sollte, bis der Modbus-Interpreter beendet ist. Wenn beide als (Funktions-)Interrupts realisiert sind, könnte ich beiden Prioritäten setzen, und der Modbus-Interpreter kann "angehalten" werden, damit der Entprellungsalgorithmus seine Aufgabe ausführen kann.

Nach einigem Googeln und Diskutieren wurde mir klar, dass ich versucht habe, priorisierte Aufgaben zu erreichen, was im Grunde ein RTOS (Echtzeitbetriebssystem) ist. Obwohl ein RTOS auf einem Mikrocontroller implementiert werden kann, erfordert es eine enorme Menge an Ressourcen (RAM usw.).

Die andere Möglichkeit besteht darin, einen Planer zu verwenden, wie Batuu erklärt hat. Auf diese Weise können Aufgaben nicht priorisiert werden (es gibt kein Multitasking), und alle Aufgaben sollten innerhalb eines einzigen Ticks abgeschlossen werden.

Dies ist 'kooperatives Multitasking' en.wikipedia.org/wiki/Cooperative_multitasking
Sie haben Recht, dass Sie grundsätzlich nach einem RTOS fragen. Eine Anmerkung: Wenn Sie einen Planer wie erläutert verwenden, ist es nicht erforderlich, dass jede Aufgabe innerhalb eines einzigen Ticks abgeschlossen wird. Sie können Aufgaben beispielsweise mit einer einfachen Zustandsmaschine in mehrere Unteraufgaben zerlegen. Auf diese Weise können Sie vorgeben, ein Multitasking-System zu verwenden, indem Sie einen statischen Zeitplan ausführen. In meinen Anwendungen läuft das wirklich gut. Der Bedarf an echtem RTOS steigt mit der Systemkomplexität und den dynamischen Verarbeitungsanforderungen.