2009-08-03 13 views
3

Dies ist eigentlich eine Design-Frage für die Firmware in einem eingebetteten System Ich habe zwei ISRs (von gleicher Priorität) unabhängig voneinander ausgeführt. Diese ISRs werden ausgelöst, wenn die h/w Daten generiert. Ich möchte einen Mechanismus, der zwischen task1 und task2 synchronisiert werden muss. Aufgabe 2 muss über die in Aufgabe1 berechneten bestimmten Werte Bescheid wissen, die dann bei der Berechnung bestimmter Werte in Aufgabe2 berücksichtigt werden müssen. Ich habe keine OS-Grundelemente zu verwenden, dh das System hat kein Betriebssystem. Task1 wird im Kontext von ISR1 und Task2 im Kontext von ISR2 ausgeführt. Der Prozessor, die wir verwenden, ist ein STMicroelectronics 32 ControllerSynchronisation zwischen zwei Aufgaben

EDIT: Zusätzliche Informationen Der Prozessor ist mit bestimmten IP-Adressen, die Trigger unterbricht, wenn sie mit Daten bereit sind. Diese IPs fungieren als Typ von Akkumulatoren für Eingangs-Streaming-Frame-Daten.

+0

An welcher Plattform arbeiten Sie? – Amber

+0

Da die beiden ISRs dieselbe Priorität haben, können sie sich gegenseitig unterbrechen? Wenn nicht, benötigen Sie möglicherweise keine zusätzliche Synchronisierung. –

+0

@Matthem - normalerweise ISRs deaktivieren die Interrupts, wenn sie eingeben. Aber das wäre gut zu überprüfen. – Robert

Antwort

2
  • deaktivieren Interrupts vor Lesen oder Schreiben auf die gemeinsamen Werte
  • Interrupts nach Lesung wieder aktivieren oder
2

auf die gemeinsamen Werte zu schreiben ich werde versuchen, Ihnen eine Antwort geben basierend auf den begrenzten Informationen, unter der Annahme:

  • ein einfacher, Homebrew Scheduler verwendet wird Rufen Sie task1 und task2 auf der Basis einfacher Kriterien auf
  • task1 und task2 laufen bis zum Abschluss (d. h. sie nicht präjudizieren)
  • Daten ein Byte-Basis ist Strom (eine etwas andere Implementierung erforderlich, wenn Sie Pakete benötigen)

Was ich in der Regel versuchen, wenn eingebettete Systeme entwerfen, ist die Verwendung von Semaphoren zu minimieren und Modell lock-free Datenflüsse. Ich werde das unten illustrieren:

asynchronous (lock free) communication queues http://www.freeimagehosting.net/uploads/477741db06.gif Die ISRs können durch Verwendung einer threadsicheren FIFO-Warteschlange von Tasks entkoppelt werden. Ein Beispiel findet sich in http://msmvps.com/blogs/vandooren/archive/2007/01/05/creating-a-thread-safe-producer-consumer-queue-in-c-without-using-locks.aspx

Eine solche Implementierung (ohne Sperren) hat keine Betriebssystemabhängigkeiten und sollte trivial einfach zu unterstützen sein. Dies ergibt auch ein klares Hersteller-Verbraucher-Design, das frei von Deadlocks ist.

Angenommen, die Tasks werden von einem Home-Brewn-Scheduler ausgelöst, können Sie dort nach Ereignissen (nicht leerem FIFO) suchen. if (TRUE == fifo_filled (my_read_queue)) {invoke task 1}

Nun zur Synchronisation von task1 und task2. Wenn Task1 nur Daten erzeugt, kann derselbe Mechanismus verwendet werden: Wenn Sie eine Warteschlange (fifo) haben, in der Task1 Daten schreiben kann, die von Task 2 gelesen werden können, dann sind die Tasks entkoppelt. Auch dies kann in Ihrem Scheduler überprüft werden (if (TRUE == fifo_filled (task1_to_2_queue()) {invoke task2)}

Wenn Sie mehr benötigen (dh wenn Aufgaben nicht vollständig ausgeführt werden, sondern vorläufig sind) Sie ' Ich brauche einen Mechanismus, um zu synchronisieren.Optionen beinhalten: - verwenden Sie ein (freies) Betriebssystem oder einen einfachen Scheduler sowieso - brauen Sie Ihre eigenen (meine Erfahrung: sollte nur getan werden, wenn es so einfach ist wie eine for-Schleife und ein paar if-Anweisungen) - verwenden Sie einen einfachen Scheduler (viel leichter als ein komplettes RTOS) - Refactor Code um Task1 und Task2 in einem zu integrieren. In diesem Fall werden Sie den Scheduler effektiv in Ihren Anwendungscode einfügen.

Hinweis: Die Beispielfunktion, die ich in dieser Erklärung verwendet habe (fifo_filled()) ist nicht Teil des Beispielcodes. Es sollte true zurückgeben, wenn (lesen! = Schreiben) Der Beispielcode verwendet auch globale Variablen lesen und schreiben; Sie können entweder eine Funktion aufrufen, die mehrere Warteschlangen verwalten kann, oder Lese-/Schreib- und Puffervariablen in die Struktur verschieben und auf die Struktur in den Parametern verweisen.

Eine alternative Möglichkeit besteht darin, einen kritischen Abschnitt durch Interrupt-Sperre zu erstellen. Ich versuche jedoch, dies zu begrenzen, da es oft eine starke Systemkopplung erzeugt und der Code schwieriger zu verwenden ist.

+0

Könnten Sie bitte Ihre Abbildung vergrößern? –

+0

erledigt. Versehentlich hatte einen Link zu einem Thumbnail .... – Adriaan

+0

Großartig, danke! –

4

Ich würde keine schwere Verarbeitung im Interrupt-Kontext tun, nur die Daten lesen und ein Flag setzen.

Die Flags können dann in einem einfachen Scheduler in der Hauptschleife überprüft werden, um die Aufgaben nach Bedarf auszuführen. So können sich Tasks nicht gegenseitig unterbrechen und können keine inkonsistente Ausgabe der anderen Task sehen.

Eine Task könnte auch ein solches Flag setzen, um eine andere Task zu aktivieren. Z.B. Task1 könnte Task2 aktivieren, da Task2 Werte von Task1 benötigt.

Für die Daten, die im ISR gelesen werden, benötigen Sie einen Puffer. Abhängig vom Zeitpunkt Ihrer eingehenden Daten, die möglicherweise ein Ringpuffer oder ein Doppelpuffer sind.

2

Ich komme von einem Standpunkt des kein OS, kein echtes Framework anders als C und Register und so. Die Art, wie ich es tun würde, ist eine Zustandsvariable für jede ISR, die von der anderen ISR gesehen werden kann. Wenn Sie dann ISR1 eingeben, überprüfen Sie einfach den Status der ISR2-Aufgabe und arbeiten wie erforderlich. Dann wird ISR2 aufgerufen und prüft seinen eigenen Zustand und den ISR1-Zustand und arbeitet so, wie er es für richtig hält. Ich würde ein ENUM in einer Header-Datei verwenden, um die Zustände der Übersichtlichkeit halber aufzuzählen (INIT, WAITONDATA, usw.) und dann einen Schalter in der ISR, um jeden Zustand zu behandeln. Dann müssen Sie nur sicherstellen, dass die Daten, die geteilt werden müssen, von beiden ISRs gesehen werden können und Sie eingestellt sind.

Natürlich haben andere gesagt, dass Sie nicht viele Berechnungen in ISRs machen wollen. Sie möchten nur ein Flag setzen, dass ein Ereignis aufgetreten ist oder einige Daten vorhanden sind und dann die Daten in der Hauptschleife behandeln. Dann wird Ihr System nicht zu lange für die Verarbeitung eines ISR gesperrt. Sie tun dies in der gleichen Weise - eine Statusvariable, die in der Hauptschleife gesehen werden kann (oder zumindest die Funktion, die Sie aufrufen, um die Datenmanipulation aus der Hauptschleife auszuführen) und die ISR. Ändern Sie den Status im ISR und prüfen Sie diesen Status in der Hauptschleife. Mach Aufgaben wie benötigt.

Nicht vergessen - Wenn Sie Variablen in ISRs aktualisieren, müssen sie als flüchtig definiert werden!

flüchtiger uint8 state = INIT;

zum Beispiel.

Verwandte Themen