In einem früheren Artikel habe ich darüber gesprochen, wie es laut Autor möglich ist, die üblichen Aktionen des Mikrocontrollers in Echtzeit zu programmieren und sie in mehrere Aufgaben zu unterteilen, die unabhängig (oder fast unabhängig) voneinander sind.
Es wurde ein Mikrocontroller mit einem Kern aus einer sehr weit verbreiteten Familie von ARM Cortex M ausgewählt. Von den vielen und nicht nur dem Autor bekannten Optionen mit den Nummern 0,3,4 und 7 wurde M4 ausgewählt, weil es zur Hand war.
Die beiden Überlegungen, die uns dazu veranlassten, den rutschigen und wackeligen Weg der „Erfindung eines Fahrrads“ einzuschlagen, wie einige Leser witzig bemerkten, waren eigentlich einfach. Das erste war, dass wir immer noch mit diesen „Kortexen“ leben müssen. Und die zweite besteht darin, zu versuchen, nicht etwas Universelles zu tun (Ruhm und Reichtum zu erlangen), sondern etwas engeres zu tun, in der Hoffnung, Effizienz und Einfachheit zu erreichen. Wer manchmal etwas mit den Händen macht, wird sich leicht daran erinnern, dass ein speziell ausgewählter Schraubendreher in der Regel besser ist als der eines brillanten Universal-Sets.
Ein Assembler-Beispiel wurde gegeben, um zu zeigen, dass nicht mehr als 80 Taktzyklen für das Schalten aufgewendet werden. Und bei einer Taktrate von 72 Megahertz ist es etwas mehr als 1 Mikrosekunde. Ein Tick mit einer Größe von 50 Mikrosekunden ist also nicht so teuer. Nur 2 Prozent des Overheads. Daher, wie eine der Lieblingsfiguren des Autors sagte, "ist es ratsam zu leiden".
Wir haben also N Aufgaben, von denen jede garantiert ein Stück (Tick T) Zeit ausführt und dieses Segment garantiert spätestens nach (N-1) T Ticks wiederholt , plus einer Verzögerung von nicht mehr als D. Diese ärgerliche Verzögerung ist glücklicherweise wird durch die maximal mögliche zeitliche Größe begrenzt, die gleich der Summe der Betriebsdauer aller zulässigen Interrupts ist. Mit anderen Worten, die Aufgabe, die für einen bestimmten Zeitraum vor dem nächsten Tick die meisten potenziellen Unterbrechungen aufweist, ist am unglücklichsten. Für eine längere Zeit kann die Aufgabe nicht verzögert werden. Sie wird unweigerlich ihr Zeitfenster spätestens in (N-1) T + D Mikrosekunden erhalten. Meiner Meinung nach wird dies als harte Echtzeit bezeichnet.
Aufgaben müssen ihre Aufgaben erledigen und über die Implementierung berichten. Wem soll ich mich melden? Anscheinend ist jemand verantwortlich, und es kommt in der Regel vor, dass er viel kleiner als Untergebene ist (in Wahrheit traf der Autor auch Ausnahmen, als es vier Chefs für drei Arbeiter mit beiden linken Händen gab, von denen nur einer das Wort „kannte und respektierte“. Angemessenheit ”).
Und wenn "Sie sind viele, aber ich bin einer", dann bedeutet dies eine Warteschlange. Viele werden anfangen zu schieben und versuchen durchzukommen. Und jemand muss warten und dann zu spät kommen und erklären. Trotz der Tatsache, dass dies alles schrecklich aussieht, heißt es schön: der Kampf um die Ressource. Warteschlangen sind eine bekannte Lösung für alle. Ich kannte viele, die kein Brot füttern - lassen Sie mich in der Schlange stehen.
Aber unsere können es kaum erwarten! In dem Sinne Aufgaben. Sie sind aus schwieriger Echtzeit. Angenommen, zwei Aufgaben lesen die Messwerte einmal pro Sekunde, und die dritte Aufgabe sollte alle 10 Millisekunden etwas messen, auf einen Stapel legen und oben melden. Und sie sagen zu ihr: "Sag mir, wir sind noch nicht mit den Köchen fertig."
Anscheinend muss man sich, gelinde gesagt, nicht ganz in Echtzeit (weiche Echtzeit) drehen.
Lassen Sie uns eine besondere Aufgabe haben, die zu warten weiß und es liebt, es zu tun. Die Ressource, die es bedienen wird, ist der Kommunikationskanal. Wie Sie wissen, werden Sie nicht alles auf einmal hineinschieben.
Sie können jedoch sofort herausfinden, wie schnell der Kanal sein sollte, damit nichts verloren geht. Dazu müssen Sie die Leistung kennen, mit der alle unsere Graphomanen, pah, Aufgaben arbeiten. Natürlich müssen Sie auch die Größe des Puffers oder der Puffer berechnen, aus denen alle Pakete gesendet werden (oder rechts).
Wenn der Kanal keiner ist, ändert sich die Essenz nicht. Für jeden Kanal wird einfach eine separate Aufgabe hinzugefügt, die warten (und natürlich hoffen und glauben) soll.
Ein paar Worte zum Kommunikationskanal mit dem Bediener oder einfacher mit der Person. Hier ist der Kanal bidirektional, aber die Richtung nach außen ist am interessantesten. Machen Sie sofort eine Reservierung, es gibt einen Umstand, den Sie selbst bei starkem Wunsch nicht ausschließen können. Dies ist eine Kanalüberlastung. Während des Testens müssen wir es erreichen und einen Mechanismus haben, um seinen Beginn zu sehen. Ich behaupte nicht, es ist nicht gut zu täuschen, aber ein bisschen kann weggelassen werden. Vaughn, Gerasim, hat es insgesamt missbraucht.
Daher gehen wir sofort davon aus, dass die Nachricht an den Bediener von der Aufgabe verloren gehen kann. Und damit eine Person davon weiß, werden wir sie nummerieren. Dies bestimmt, wo und wie oft unser Bediener nichts mehr hatte. Letztendlich können Sie immer etwas im Code tun oder die Berechnungen hinzufügen oder es sogar in den Stromkreis einbinden, um die Situation zu korrigieren. Es scheint vorerst einfacher zu sein. Für militärische Anwendungen ist dies natürlich nicht erforderlich. Um ehrlich zu sein, sieht der Verlust einer Nachricht nicht nur beim Debuggen wie eine Schande aus.
Lassen Sie uns zum Beispiel eine serielle Duplex-Schnittstelle ohne Bestätigung bei 115200 Baud haben. Zum Beispiel RS422 in der Konfiguration "Wirtschaft" zwei Drähte - dort zwei - zurück. Seine Fähigkeit beträgt ungefähr 10.000 Bytes pro Sekunde. Nehmen wir die durchschnittliche Nachrichtengröße für eine Person, die 50 Bytes entspricht. Wir erhalten 200 Nachrichten pro Sekunde oder eine Nachricht in 5 Millisekunden. Wenn wir drei Aufgaben haben, die etwas kommunizieren möchten, lassen Sie sie alle 15 Millisekunden ausführen. Im Durchschnitt natürlich. Und wenn nicht im Durchschnitt, sind ernsthafte statistische Berechnungen oder ein umfassendes Experiment erforderlich. Wählen Sie den letzten. Wir haben schließlich gelernt, fehlende Nachrichten zu erkennen und werden alles auf dem Bildschirm des Terminalemulators sehen.
Lassen Sie die drei Aufgaben also einzelne Nachrichten erstellen. Lassen Sie die Nachrichten in der Wichtigkeit oder Dringlichkeit des Inhalts unterschiedlich sein und unsere Aufgaben legen sie in den entsprechenden Puffer. Ordnen Sie diese drei Ringpuffer drei Dringlichkeitsstufen zu (siehe Abbildung 1).

Die vierte Aufgabe wählt aus diesen Puffern eine Nachricht gemäß unserem genehmigten Plan aus und versucht, sie zuzustellen. Wenn das Senden noch nicht möglich ist, schätzt die vierte Aufgabe, wie viel es schlafen kann und tut es. Nach dem Schlafen hat sie bereits den nötigen Platz im Ringpuffer zum Senden.
Die Puffer verschiedener Dringlichkeit speichern natürlich nicht die Nachrichten selbst, sondern ihre Adressen (Links). Gleichzeitig müssen die Aufgaben selbst überhaupt nicht warten. Ok? Nein, nicht wirklich. Das funktioniert nicht und hier ist der Grund. Jeder dieser drei Ringpuffer ist eine gemeinsam genutzte Ressource. Stellen Sie sich vor, Aufgabe 1 würde eine Adresse in einen mittleren Puffer stellen. Sie liest das Wort, prüft, ob der Ort leer ist, das heißt, der Wert ist Null und (in diesem Moment wird sie durch eine andere Aufgabe 2 ersetzt, die genau das Gleiche tun möchte und sie erfolgreich ist), die erste Aufgabe, die zurückkehrt, setzt das Wort dort ab und überschreibt alles, was das ist erfolgreich zweiter. Hier bittet ein Kollege um Worte. Ich scheine zu wissen, was er sagen wird.
-Ja, alles ist sehr einfach. Sie können Unterbrechungen für die Dauer des Tests verbieten und es wird nichts Schlimmes passieren, es dauert nicht lange.
- Stimmt nicht lange, aber wie oft? Wie viel Zeit werden wir von der Aufgabe wegnehmen? Und welcher von ihnen? Ich habe vergessen zu warnen, wir verbieten niemals Interrupts, unsere harte Sekte der Echtzeit verbietet uns dies zu tun.
- Und wenn Sie Unterbrechungen nicht verbieten, können Sie unseren Task-Switch bitten, die Nachrichtenadresse dort abzulegen. Er kann es atomar tun.
- Ja, vielleicht, aber dann möchte ich ihn noch etwas fragen, dann noch etwas. Und warum haben wir dann 72 Grad erreicht, um dann alles mit Wasser zu verdünnen? Entschuldigung, ich meinte 72 Zyklen zum Wechseln des Kontexts.
Versuchen wir es einfacher zu machen, wie in Abbildung 2.

In diesem Fall hat jede Aufgabe einen eigenen Puffer oder einen eigenen Satz von Puffern, wenn Sie eine andere Dringlichkeit, Pomp und Wichtigkeit wünschen. Persönlich habe ich als einfacher Bediener immer noch die gleiche Bedeutung für alle.
Ein solches Schema lässt Sie nicht um die Ressource kämpfen. Jetzt haben wir eine sehr funktionierende Option. Mag es einfach nicht. Aber was ist, wenn die Aufgaben links im Bild nichts zu senden haben? Dann wäre es klüger, die Aufgabe auf der rechten Seite zu bitten, geweckt zu werden, wenn der Grund erscheint, und nicht aufzuwachen, nur um den Alarm erneut einzustellen. Die Aufgaben auf der linken Seite sind einfacher zu erledigen. Darüber hinaus wurde in einem früheren Beitrag eine Funktion erwähnt, mit der ein Freund geweckt werden kann.
Ich sehe einen Rationalisierungsvorschlag vor: "Lassen Sie den Interrupt von der seriellen Schnittstelle (UART) selbst in die Aufgabe 4 einbezogen werden, die jetzt ausgeführt wird. Es wird Einsparungen geben." Sie können dies tun, aber ich denke nicht, dass es gut ist. Ich werde versuchen zu klären. Die Aufgaben auf der linken Seite können tatsächlich selbst die UART-Interrupt-Prozedur aktivieren, und sie beginnt zu arbeiten und hört erst auf, wenn sie alles erledigt. Die Interrupt-Prozedur sollte nun alles tun, was Aufgabe 4 früher getan hat. Die Zeit, die für die Verarbeitung des Interrupts benötigt wird, schwillt an, und keine einzelne Aufgabe kann eingeschaltet werden, bis die nächste „Spool“ beendet ist. Und was sagen wir unseren Kameraden aus dem hartnäckigen Echtzeitkreis? Uns wurde jedoch gesagt, dass die Reaktion auf externe Interrupts so kurz wie möglich sein sollte. Dies ist nur ein guter Ton. Oder mit anderen Worten: Es ist notwendig, Gutes, Schlechtes zu tun, und ohne dich wird es funktionieren.
Abbildung 3 erklärt, was der Prozess ist und welche Herausforderungen sich befinden.

Nun wenden wir uns der Situation zu, könnte man sagen, einem Spiegel. Dies ist, wenn Informationen von außen kommen. Es sei ein SPI-Kanal mit mehreren Gondolieri mit Gondeln und einem kleinen Amateur-Streichorchester. Nein, es ist zu früh, um über Ruhe nachzudenken. Lassen Sie nur die SPI-Schnittstelle und einige Chips. Zum Beispiel Atmosphärendrucksensor, Beschleunigungsmesser und gespeicherter Speicher.
Ich muss sofort sagen - ein dummes Beispiel. Nicht wegen der Gondoliere mit ihrem ewigen "Ich sollte hinzufügen, Herr." Nein, es ist in der Tat dumm, solche Eingabedaten von unterschiedlicher Bedeutung in einer Schnittstelle zu mischen. In der Tat, wenn Sie die Beschleunigung kennen müssen, dann sicher, um schnell herauszufinden, wann Sie das Gaspedal entfernen oder die Klappen drehen oder Ihre Augen schließen müssen. Diese Informationen werden häufig benötigt. Aber der Druck ändert sich langsam und muss etwa drei Meter nach unten fliegen, damit in den unteren Rängen das Leben wärmer wird.
Was den gespeicherten Speicher betrifft, und wer hat ihn im Allgemeinen auf diesem SPI gespeichert? Gibt es einen zweiten SPI? Und wird nicht erwartet? Nirgendwo muss etwas getan werden. Lenken Sie die Pfeile in Abbildung 2 in die entgegengesetzte Richtung und beginnen Sie zu denken.
Aufgabe 4 dient nun dem SPI und wacht nur durch seine Signale auf. Die Verbindung mit Aufgabe 1, die etwas in den gespeicherten Speicher stellen möchte, ist nach außen gerichtet und wird über die Warteschlange ausgeführt. Es ist auch erforderlich, einen Mechanismus zur Überwachung des Überlaufs des Ringpuffers bereitzustellen. Die Erzeugung von Beschleunigungs- und Druckwerten von Aufgabe 4 sollte ohne die Teilnahme von zwei aufwendigen Aufgaben erfolgen. Sie müssen sich nur drehen und mithalten. Jetzt können wir ein erklärendes Bild skizzieren und eine erklärende Notiz schreiben. In Abbildung 4 sind diese
Aktionen werden schematisch (oder im Blockdiagramm) dargestellt.

Unterlaufprüfung - Mithilfe dieser Aktionen können Sie herausfinden, ob sich der Beschleunigungswert ändern kann, bevor er von der verbrauchenden Aufgabe erneut gelesen wird. Diese Prüfung wird in Abbildung 4 nur durch eine separate Aktion dargestellt, um die Aufmerksamkeit darauf zu lenken. Tatsächlich erfolgt dieser Schritt zusammen mit dem Ablesen des Werts des Beschleunigungsmessers gemäß dem in 5 gezeigten Schema.

Es ist zu beachten, dass es eine gemeinsam genutzte Ressource gibt, da der Speicherort des Ergebnisses auch ein Indikator für Aktionen ist (Semaphor). Hier sind Rennen möglich, die die Sprache der Schaltkreise sprechen, aber für uns ist dies keine Auslassung. Schließlich kann das Schlüpfen in die schließende Tür eines Fahrzeugs nur im Leben als Vermögen angesehen werden. Hier werden wir es zuversichtlich als Verzögerung betrachten.
Der Speicherzugriff erfolgt in Teilen, um die Zeit jedes solchen Schritts zu begrenzen. Auf diese Weise stellen wir sicher, dass die sich schnell ändernden Beschleunigungswerte gleichmäßig abgelesen werden, und dazwischen kümmern wir uns rechtzeitig um den Rest.
Nun bleibt es, etwas Passendes aus Eisen zu finden und zu experimentieren, wie es sollte. Ich denke, dies wird die nächste Geschichte sein.