Wie Echtzeit-Software auch ohne Echtzeit-Betriebssystem entwickelt werden kann

Autor / Redakteur: David Kalinsky* / Sebastian Gerstl |

Ist ein Echtzeit-Betriebssystem notwendig, wenn Echtzeit-Software entwickelt wird? David Kalinsky beleuchtet in unserem Beitrag grundlegende Designthemen.

Anbieter zum Thema

Echtzeit-Software muss nicht immer auf Echtzeit-Betriebssystem-Plattformen erstellt werden
Echtzeit-Software muss nicht immer auf Echtzeit-Betriebssystem-Plattformen erstellt werden
( Gerd Altmann, pixelio.de)

Im folgenden Artikel werden grundlegende Designthemen der Embedded- und Echtzeit-Software beleuchtet. Dazu gehören das Task-Scheduling, die Kommunikation zwischen Tasks oder zwischen Interrupt Service Routinen und Tasks sowie die Unterschiede zwischen Do-it-Yourself-Lösungen und einem RTOS.

Als erstes soll der Task-Scheduler als Kernstück eines Echtzeit-Softwaresystems vorgestellt werden. Die Auswahl ist groß und reicht vom einfachen zyklischen Executive, der sich leicht selbst entwickeln lässt, bis hin zum prioritätsbasierten preemptiven Scheduler in handelsüblichen RTOS-Systemen.

Bildergalerie
Bildergalerie mit 16 Bildern

Je nach Applikation und I/O-Anforderungen kann ein Scheduler gewählt werden, der in Eigenregie entwickelt wird. Die oberen sechs Task-Scheduler-Typen lassen sich in Eigenregie entwickeln; Einzelheiten hierzu werden später erklärt. Die restlichen drei stehen in vielen handelsüblichen RTOS-Systemen zur Verfügung, sollten jedoch nur in Projekten eingesetzt werden, bei denen die Einschränkungen, welche die Typen 1-6 mit sich bringen, nicht tolerierbar sind.

In weniger komplexen Embedded-Systemen lässt sich Applikationssoftware einfach als Endlosschleife programmieren. Die in der Schleife programmierten Aktivitäten werden sequentiell ausgeführt. Verzweigungen und geschachtelte Schleifen sind möglich, vorausgesetzt, der Code wird nach der Ausführung in der Schleife wieder zum Anfang zurückgeführt.

Echtzeit-Software ohne RTOS: Eine Frage der Komplexität

In einfachen Embedded-Systemen funktioniert diese Art der Programmierung gut. Vor allem ist sie dann geeignet, wenn die Software die Codesequenz schnell ausführt und rasch zurückführen kann. Dieser Programmierstil beeinträchtigt jedoch oft die Performance. Interrupts von Hardware-Geräten lassen sich damit nicht bearbeiten, und für die Interaktion mit Software-in-the-Loop müssen die Geräte abgefragt werden.

In komplexeren Systemen lässt sich der Endlosschleifen-Ansatz weiter ausbauen. In diesen Systemen gibt es Hunderttausende von Codezeilen, die der Entwickler in separate Einheiten, den Tasks, unterteilt. Die Tasks, oder auch Prozesse genannt, sollten möglichst unabhängig voneinander sein und nur minimal interagieren. In Softwaredesigns mit einfachem zyklischen Executive werden die Tasks innerhalb einer Endlosschleife in Standardsequenzen ausgeführt. Im Großen und Ganzen entspricht dies dem Ansatz mit Endlosschleife, jedoch sind die ausgeführten Tasks hier umfangreicher. Diese Methode ist als Round-Robin-Scheduling bekannt.

Die Tasks können Informationen untereinander weitergeben, indem sie gemeinsam genutzte Daten schreiben und lesen. Eine Task wird grundsätzlich vollständig ausgeführt, ehe die nächste Task beginnt. Damit ist gewährleistet, dass eine Task keinen unvollständigen Daten weitergibt. Auch mit dieser Methode lassen sich Interrupts von Hardware-Geräten nicht bearbeiten, und für die Interaktion mit Software-in-the-Loop müssen die Geräte abgefragt werden. Diese Methode könnte man auch als „Echtzeit“-Task-Scheduling bezeichnen, vorausgesetzt, die gesamte Software wird schnell in der Schleife ausgeführt und die Schleife kann immer wieder sehr schnell laufen.

Präzises Timing mit einem zeitgesteuerten zyklischen Executive

Manchen Applikationen ist die Vorstellung, die ein einfacher zyklischer Executive von „Echtzeit“ hat, nicht präzise genug. Ein einfacher zyklischer Executive will seine Tasks so schnell und so oft wie möglich ausführen. In komplexeren Applikationen ist ein präzises Timing aber oft wichtiger als Geschwindigkeit. Mit einem zeitgesteuerten zyklischen Executive lässt sich diese Anforderung besser erfüllen. Über einen Hardwaretimer-Interrupt wird die Ausführung aller Tasks angestoßen. Alle Tasks laufen nacheinander und vollständig ab. Damit ein zeitgesteuerter zyklischer Executive richtig funktioniert, muss die letzte Task der Task-Kette vollständig ausgeführt werden, ehe der nächste Timer-Interrupt erfolgt. Die Tasks müssen auf Basis der Wiederholrate der Hardwaretimer-Interrupts ausgeführt werden.

Trotz der Hardwaretimer-Interrupts können die Tasks einfach Daten untereinander weitergeben, indem sie gemeinsam genutzte Daten schreiben und lesen. Eine Task wird immer vollständig ausgeführt, ehe die nächste Task beginnt. Interrupts von Hardware-Geräten, mit Ausnahme des Timers, lassen sich mit diesem Programmierstil nicht bearbeiten, und für die Interaktion mit den Tasks müssen die Geräte abgefragt werden.

Unterschiedliche Wiederholrate mit dem zyklischen Multirate-Executive

Der zeitgesteuerte zyklische Executive geht davon aus, dass alle Tasks mit identischer Wiederholrate laufen. Manchmal ist es jedoch erforderlich, dass Tasks mit unterschiedlicher Wiederholrate ausgeführt werden. Mit einem modifizierten, zeitgesteuerten zyklischen Executive, dem zyklischen Multirate-Executive, lässt sich dies gut umsetzen, wenn die höhere Wiederholrate ein ganzzahliges Vielfaches der „Basisrate“ ist.

In einem zyklischen Multirate-Executive werden Basisraten-Tasks einmal je Timer-Interrupt und Tasks mit höherer Wiederholrate mehrmals je Timer-Interrupt ausgeführt. Diese Anzahl ist ein ganzzahliges Vielfaches der Basisrate. Die Ausführungen der Tasks mit höherer Wiederholrate sollten möglichst gleichmäßig über die Tasksequenz verteilt werden, die auf einen Timer-Interrupt folgt. Die Basisrate wird oft als „Major Cycle“ (Hauptzyklus) bezeichnet; höhere Wiederholraten deuten auf einen so genannten „Minor Cycle“ (Nebenzyklus) hin. Jedoch hat dieses Verfahren auch Einschränkungen:

Mithilfe eines Hardwaretimer-Interrupts lassen sich Tasks mit gleichmäßigen oder unterschiedlichen Wiederholraten ausführen. Tasks können über gemeinsam genutzte Daten miteinander kommunizieren, ohne dass die Datenintegrität beeinträchtigt wird. Hardware-Geräte mit Ausnahme des Timers werden abgefragt und sind nicht interruptgesteuert.

Häufiges Abfragen der Hardware-Geräte führt zu erheblichen Einschränkungen. Wird das Gerät nicht oft genug abgefragt, können wichtige Vorgänge übersehen werden. Wird das Gerät jedoch zu oft abgefragt, beansprucht der Abfragevorgang übermäßig viel Rechenleistung.

Bildergalerie
Bildergalerie mit 16 Bildern

Timing der Tasks lässt sich nur schätzen

Daher eignen sich interruptgesteuerte Peripheriegeräte normalerweise eher für I/O. Es spricht auch gegen zyklische Executives, dass sich das Timing einer Task-Ausführung nicht präzise steuern lässt. Selbst wenn die Ausführung einer Task-Kette über Hardware-Timing ausgelöst wird, lässt sich nur das Timing für die erste Task in einer Kette präzise definieren. Die zweite Task wird erst dann ausgeführt, wenn die erste beendet wurde. Enthalten diese Tasks Code mit variabler Prozessorlast, wie datenabhängige Schleifen, hängt das Timing aller nachfolgenden Tasks in einer Kette von den Inhalten der vorangegangenen Tasks ab. Das Timing von Tasks in einer Task-Kette lässt sich also nicht präzise steuern.

Selbst wenn keine Tasks Code mit variabler Prozessorlast enthalten, lässt sich das Timing der einzelnen Tasks nur schätzen. Die beispielhafte Abbildung eines zyklischen Multirate-Executives veranschaulicht dies. In diesem Diagramm soll die als Stern dargestellte Task mit einer Wiederholrate von 40 Hz ausgeführt werden. Zwischen den sukzessiven Ausführungen dieser Task müssen also genau 25 ms oder 25.000.00 µs liegen. Betrachtet man das Diagramm als Kreis, dessen Gesamtumfang eine Basisperiode mit 10 Hz darstellt, müsste die „Stern-Task“ genau bei einem Winkel von 0°, 90°, 180° und 170° laufen, doch dem ist nicht so. Manchmal läuft sie etwas eher, dann wieder später – je nachdem, wann die vorherige Task beendet wird und wie viel Zeit die nachfolgende Task beansprucht. Wie bereits erwähnt, muss jede Task immer vollständig ablaufen und darf dabei nicht unterbrochen werden.

Ein Lösungsansatz für dieses Timingproblem ist das Zählen der Maschinenzyklen der Rechneranweisungen, die von jeder Task auszuführen sind. Damit soll präzise ermittelt werden, welcher Anteil einer Task ausgeführt werden kann, ehe eine andere Task mit genau definiertem Timing ausgeführt werden kann. Auf diese Weise ließe sich der erste Teil einer Task ausführen, dann würde die Task mit dem definierten Timing eingefügt, und anschließend der Rest der zurückgestellten Task ablaufen.

Doch diese Methode verursacht Probleme:

  • Wenn die an einem solchen Task-Switch beteiligten Tasks gemeinsame Datenstrukturen nutzen, könnte eine Umschaltung mitten im Ablauf der Task (Mid-Task-Switch) zu Dateninkonsistenz und Zahlenfehlern bei der Datenausgabe einer Task führen.
  • Nach einer Software-Wartung und den damit verbundenen Codeänderungen oder -ergänzungen bei Tasks, die vor dem Mid-Task-Switch ausgeführt werden, müssen die Maschinenzyklen erneut gezählt und das Task-Timing neu errechnet werden. Bei dieser neuen Codesituation muss eine Task dann vielleicht an einer anderen Stelle für den Mid-Task-Switch geteilt werden.

Zyklischer Multirate-Executive für periodische Tasks

Wenn alle Tasks periodisch sind, aber unterschiedliche Wiederholraten haben, eignen sich Multirate-Executives oft besser als zyklische Executives. Bei einem solchen Scheduler muss die Wiederholrate der Timer-Interrupts dem kleinsten gemeinsamen Vielfachen aller Raten der Task entsprechen. Tasks können bei jedem Timer-Interrupt (Tick) ausgeführt werden. Muss beispielsweise eine Task mit 50, 60 und 100 Hz ausgeführt werden, sind die Timer-Interrupts mit 300 Hz bereitzustellen. Die 100-Hz-Task wird bei jedem dritten Tick ausgeführt, die 60-Hz-Task bei jedem fünften und die 50-Hz-Task bei jedem sechsten.

Wenn es nicht erforderlich ist, das Timing der Tasks zu synchronisieren, können sie auch bei versetzten Ticks ausgeführt werden. Nicht alle drei Tasks müssen bei Tick 0 laufen. Die 100-Hz-Task läuft erstmals bei Tick 0, die 60-Hz-Task bei Tick 1 und die 50-Hz-Task bei Tick 2.

Jede Task muss vollständig ausgeführt werden, ehe die nächste Task startet. Wie bei den zyklischen Executives können Tasks Informationen einfach durch Schreiben und Lesen gemeinsam genutzter Daten untereinander weitergeben. Alle Hardware-Geräte ohne Timer müssen abgefragt werden.

Artikelfiles und Artikellinks

(ID:24232920)