Echtzeitbetriebssysteme (RTOS) und ihre Anwendungen
Zur Verfügung gestellt von Nordamerikanische Fachredakteure von DigiKey
2021-02-25
Was ist ein RTOS
Ein Echtzeitbetriebssystem (Real-Time Operating System, RTOS) ist ein leichtgewichtiges Betriebssystem, das zur Vereinfachung von Multitasking und Task-Integration in ressourcen- und zeitbeschränkten Designs verwendet wird, was normalerweise in eingebetteten Systemen der Fall ist. Der Begriff „Echtzeit“ deutet eher auf Vorhersagbarkeit/Determinismus in der Ausführungszeit als auf rohe Geschwindigkeit hin, daher kann ein RTOS aufgrund seines Determinismus in der Regel nachweislich harte Echtzeitanforderungen erfüllen.
Schlüsselkonzepte eines RTOS sind:
Task
Tasks (man könnte sie auch Prozesse/Threads nennen) sind unabhängige Funktionen, die in Endlosschleifen laufen und in der Regel jeweils für eine Funktion zuständig sind. Die Tasks laufen unabhängig voneinander in ihrem eigenen Zeit- (zeitliche Isolation) und Speicherstapel (räumliche Isolation). Die räumliche Trennung zwischen den Tasks kann durch den Einsatz einer Hardware-MPU (Memory Protection Unit) gewährleistet werden, die den zugänglichen Speicherbereich einschränkt und bei Zugriffsverletzungen Fehlerausnahmen auslöst. Normalerweise sind interne Peripheriegeräte speicherabgebildet, so dass eine MPU auch dazu verwendet werden kann, den Zugriff auf Peripheriegeräte zu beschränken.
Tasks können sich in verschiedenen Zuständen befinden:
- Blockiert - Task wartet auf ein Ereignis (z. B. Zeitüberschreitung, Verfügbarkeit von Daten/Ressourcen)
- Bereit - Task ist bereit, auf der CPU zu laufen, läuft aber nicht, weil die CPU von einem anderen Task belegt ist
- Läuft - Der Task wird auf der CPU ausgeführt
Scheduler
Scheduler in the RTOS steuern, welcher Task auf der CPU laufen soll, und es gibt verschiedene Scheduling-Algorithmen. Normalerweise sind das:
- Präemptiv - Task-Ausführung kann unterbrochen werden, wenn ein anderer Task mit höherer Priorität bereit ist
- Kooperativ - Taskwechsel erfolgt nur, wenn der aktuell laufende Task sich selbst unterbricht
Das präemptive Scheduling erlaubt es Tasks mit höherer Priorität, einen Task mit niedrigerer Priorität zu unterbrechen, um Echtzeitbedingungen zu erfüllen, aber es geht auf Kosten von mehr Overhead beim Kontextwechsel.
Inter-Task-Kommunikation (ITC)
Mehrere Tasks müssen normalerweise Informationen oder Ereignisse miteinander teilen. Die einfachste Art der gemeinsamen Nutzung ist das direkte Lesen/Schreiben von gemeinsam genutzten globalen Variablen im RAM, was jedoch aufgrund des Risikos einer Datenbeschädigung durch eine Race-Condition unerwünscht ist. Ein besserer Weg ist das Lesen/Schreiben von dateispezifischen statischen Variablen, auf die über Setter- und Getter-Funktionen zugegriffen werden kann. Race-Conditions können durch das Deaktivieren von Interrupts oder die Verwendung eines Mutual-Exclusion-Objekts (Mutex) innerhalb der Setter/Getter-Funktion verhindert werden. Der sauberere Weg ist die Verwendung von thread-sicheren RTOS-Objekten wie Message-Queues, um Informationen zwischen Tasks zu übergeben.
Neben der gemeinsamen Nutzung von Informationen sind RTOS-Objekte auch in der Lage, die Ausführung von Tasks zu synchronisieren, da Tasks blockiert werden können, um auf die Verfügbarkeit von RTOS-Objekten zu warten. Die meisten RTOS bieten Objekte wie:
- Message-Queue
- FIFO-Queue (First-In-First-Out) zur Weitergabe von Daten
- Daten können per Kopie oder per Referenz (Zeiger) übergeben werden
- Dient zum Senden von Daten zwischen Tasks oder zwischen Interrupt und Task
- Semaphor
- Kann als Referenzzähler behandelt werden, um die Verfügbarkeit einer bestimmten Ressource zu erfassen
- Kann ein binäres oder zählendes Semaphor sein
- Wird verwendet, um die Nutzung von Ressourcen zu überwachen oder die Ausführung von Tasks zu synchronisieren
- Mutex
- Ähnlich wie binäre Semaphore, im Allgemeinen verwendet, um die Verwendung einer einzelnen Ressource zu überwachen (MUTual EXclusion)
- Das FreeRTOS-Mutex verfügt über einen Mechanismus zur Prioritätsvererbung, um das Problem der Prioritätsinversion (Zustand, bei dem eine Aufgabe mit hoher Priorität auf eine Aufgabe mit niedriger Priorität wartet) zu vermeiden.
- Mailbox
- Einfacher Speicherort zur gemeinsamen Nutzung einer einzelnen Variablen
- Kann als eine einzelne Element-Queue betrachtet werden
- Ereignis-Gruppe
- Gruppe von Bedingungen (Verfügbarkeit von Semaphor, Warteschlange, Ereignisflag, usw.)
- Task kann blockiert werden und kann auf die Erfüllung einer bestimmten Kombinationsbedingung warten
- Verfügbar in Zephyr als Polling-API, in FreeRTOS als QueueSets
Systemtaktvariable
Ein RTOS benötigen eine Zeitbasis zur Messung der Zeit, normalerweise in Form einer Systemtackt-Zählervariable, die in einem periodischen Hardware-Timer-Interrupt inkrementiert wird. Mit der Systemtaktvariablen kann eine Anwendung mehr zeitbasierte Dienste (Task-Ausführungsintervall, Wartezeit, Zeitscheibenbildung) mit nur einem einzigen Hardware-Timer aufrechterhalten. Eine höhere Taktrate erhöht jedoch nur die Auflösung der RTOS-Zeitbasis, sie macht die Software nicht schneller.
Warum ein RTOS verwenden?
Organisation
Anwendungen können immer auf einfache Weise geschrieben werden, aber wenn die Komplexität des Codes zunimmt, hilft es, eine Art Struktur zu haben, um die verschiedenen Teile der Anwendung zu verwalten und getrennt zu halten. Darüber hinaus kann ein neues Teammitglied mit einer strukturierten Entwicklungsweise und einer vertrauten Designsprache den Code verstehen und schneller mit der Mitarbeit beginnen. RFCOM Technologies hat Anwendungen mit verschiedenen Mikrocontrollern wie dem Hercules von Texas Instruments, den RL78 und RX von Renesas und dem STM32 von STMicroelectronics auf einem anderen RTOS entwickelt. Ähnliche Entwurfsmuster ermöglichen es uns, Anwendungen auf verschiedenen Mikrocontrollern und sogar einem anderen RTOS zu entwickeln.
Modularität
Teilen und herrschen. Durch die Trennung von Funktionen in verschiedene Tasks können neue Funktionen einfach hinzugefügt werden, ohne dass andere Funktionen beeinträchtigt werden; vorausgesetzt, die neue Funktion überlastet nicht die gemeinsam genutzten Ressourcen wie CPU und Peripheriegeräte. Die Entwicklung ohne RTOS wird normalerweise in einer großen Endlosschleife stattfinden, in der alle Funktionen Teil der Schleife sind. Eine Änderung einer Funktion innerhalb der Schleife wirkt sich auf andere Funktionen aus, wodurch die Software schwer zu ändern und zu warten ist.
Kommunikationsstapel und Treiber
Viele zusätzliche Treiber oder Stapel wie TCP/IP-, USB-, BLE-Stapel und Grafikbibliotheken werden für/auf ein bestehende RTOS entwickelt/portiert. Ein Anwendungsentwickler kann sich auf eine Anwendungsschicht der Software konzentrieren und die Zeit bis zur Markteinführung deutlich reduzieren.
Tipps
Statische Speicherbelegung
Die Verwendung der statischen Speicherzuweisung für RTOS-Objekte bedeutet, dass während der Kompilierungszeit für jedes RTOS-Objekt ein Speicherstapel im RAM reserviert wird. Ein Beispiel für eine statische Zuweisungsfunktion in freeRTOS ist xTaskCreateStatic(). Dadurch wird sichergestellt, dass ein RTOS-Objekt erfolgreich erstellt werden kann, was den Aufwand für die Behandlung einer möglicherweise fehlgeschlagenen Zuweisung erspart und die Anwendung deterministischer macht.
Um zu entscheiden, welche Stapelgröße für einen Task benötigt wird, kann der Task mit einer größeren (mehr als ausreichenden) Stapelgröße ausgeführt werden und dann kann der Stapelverbrauch zur Laufzeit überprüft werden, um die Obergrenze zu bestimmen. Zudem steht auch ein Analyse-Tool für den statischen Speicherverbrauch zur Verfügung.
Betriebssystem-Abstraktionsschicht (OSAL) und sinnvolle Abstraktion
Genau wie die Hardware-Abstraktionsschicht (HAL) ermöglicht die Verwendung der RTOS-Abstraktionsschicht die einfache Migration von Anwendungssoftware auf andere Echtzeitbetriebssysteme. Die Funktionen der Echtzeitbetriebssysteme sind recht ähnlich, so dass die Erstellung der OSAL nicht allzu kompliziert sein sollte. Beispiel:
Direkte Verwendung der freeRTOS-API:
if( xSemaphoreTake( spiMutex, ( TickType_t ) 10 ) == pdTRUE ) { //dosomething }
Einbindung der RTOS-API in die OSAL:
if( osalSemTake( spiMutex, 10 ) == true) { //dosomething }
Verwendung der Abstraktionsschicht für die Inter-Task-Kommunikation, um den Code lesbarer zu machen und den Umfang eines RTOS-Objekts zu minimieren:
if( isSpiReadyWithinMs( 10 ) ) { //doSomething }
Zusätzlich erlaubt die Abstraktion einem Programmierer, das darunter verwendete RTOS-Objekt zu wechseln (z. B. von Mutex zu Zählsemaphore), wenn mehr als ein SPI-Modul vorhanden ist. Die OSAL und andere Abstraktionsschichten helfen auch beim Softwaretest, indem sie das Einfügen von Scheinfunktionen während des Tests vereinfachen.
Auswahl des Tacktintervalls
Im Idealfall ist eine niedrigere Tacktrate besser, da weniger Overhead anfällt. Um eine geeignete Tacktrate auszuwählen, kann der Entwickler zeitliche Einschränkungen von Modulen in einer Anwendung auflisten (Wiederholungsintervall, Timeout-Dauer usw.). Wenn es einige Ausreißermodule gibt, die ein kleines Intervall benötigen, kann ein dedizierter Timer-Interrupt für die Ausreißermodule in Betracht gezogen werden, anstatt die RTOS-Tacktrate zu erhöhen. Wenn die Hochfrequenzfunktion sehr kurz ist (z. B. Schreiben in ein Register, um eine LED ein-/auszuschalten), kann sie innerhalb einer Interrupt-Service-Routine (ISR) ausgeführt werden, andernfalls kann eine verzögerte Interruptbehandlung verwendet werden. Die verzögerte Interrupt-Behandlung ist eine Technik, bei der die Interrupt-Berechnung in einen RTOS-Task verschoben wird. Die ISR erzeugt nur ein Ereignis durch das RTOS-Objekt, dann wird die RTOS-Task durch das Ereignis entsperrt und führt die Berechnung durch.
Taktunterdrückung für Anwendungen mit geringer Leistung
Tickless Idle deaktiviert den Takt-Interrupt, wenn das System für längere Zeit in den Leerlauf geht. Eine wichtige Möglichkeit für Embedded-Firmware, den Stromverbrauch zu reduzieren, besteht darin, das System so lange wie möglich in den Energiesparmodus zu versetzen. Der taktlose Leerlauf wird implementiert, indem der periodische Takt-Interrupt deaktiviert wird und dann ein Countdown-Timer eingerichtet wird, der unterbricht, wenn ein blockierter Task ausgeführt werden soll. Wenn kein Task auf ein Timeout wartet, kann der Takt-Interrupt auf unbestimmte Zeit gesperrt werden, bis ein anderer Interrupt auftritt (z. B. Taste gedrückt). So kann z. B. bei einem BLE-Beacon (Bluetooth Low Energy) die MCU zwischen den Werbeintervallen in den Tiefschlaf versetzt werden. Wie in Abbildung 1 dargestellt, befindet sich das Beacon die meiste Zeit im Tiefschlafmodus und verbraucht dabei Strom im Bereich von einigen zehn µA.
Abbildung 1: Stromaufnahme eines BLE-Beacons (Bildquelle: RFCOM)
Fazit
Ein RTOS bietet Funktionen wie Scheduler, Tasks und RTOS-Objekte für die Inter-Task-Kommunikation sowie Kommunikationsstapel und Treiber. Es ermöglicht Entwicklern, sich auf die Anwendungsschicht der eingebetteten Software zu konzentrieren und Multitasking-Software schnell und leicht zu entwickeln. Allerdings muss es, wie jedes andere Werkzeug auch, richtig eingesetzt werden, um einen Mehrwert zu erzielen. Um sichere und effiziente eingebettete Software zu erstellen, sollten Entwickler wissen, wann sie RTOS-Funktionen verwenden und wie sie ein RTOS konfigurieren.

Haftungsausschluss: Die Meinungen, Überzeugungen und Standpunkte der verschiedenen Autoren und/oder Forumsteilnehmer dieser Website spiegeln nicht notwendigerweise die Meinungen, Überzeugungen und Standpunkte der DigiKey oder offiziellen Politik der DigiKey wider.