Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Echtzeit-Multitasking auf dem Arduino Mega: Einführung in nicht-präemptive Scheduler für Lab 3

Lerne, wie du auf dem Arduino Mega mehrere Tasks gleichzeitig ausführst – mit Round-Robin, ISR-gesteuerten Schedulern und Task-Control-Blocks. Perfekt für dein CSE/ECE474 Lab 3.

Arduino Mega Multitasking nicht-präemptiver Scheduler Round Robin Arduino ISR Scheduler Arduino Task Control Block Arduino CSE/ECE474 Lab 3 Lösung Echtzeitprogrammierung Arduino Funktionszeiger Array Arduino sleep_474 Funktion Timer Interrupt Arduino 2ms 7-Segment Anzeige Arduino Mario Theme Arduino Embedded C Scheduler Multitasking Mikrocontroller Arduino ohne delay()

Einführung in die Scheduler-Welt

In der Embedded-Entwicklung ist Multitasking unverzichtbar. Stell dir vor, dein Arduino Mega soll gleichzeitig eine LED blinken lassen, einen Summer ansteuern und eine 7-Segment-Anzeige aktualisieren – alles scheinbar parallel. Genau das lernst du in diesem Tutorial. Wir bauen drei nicht-präemptive Scheduler: Round-Robin (RR), synchronisierten Round-Robin mit ISR (SRRI) und einen TCB-basierten Scheduler. Diese Konzepte sind nicht nur für dein Lab 3 essenziell, sondern auch für reale Anwendungen wie Smart-Home-Steuerungen oder Robotik.

Grundlagen: Was ist ein Scheduler?

Ein Scheduler entscheidet, welcher Task als Nächstes läuft. In nicht-präemptiven Systemen gibt ein Task die CPU freiwillig ab – ähnlich wie bei einem Rundenbasierten Spiel. Unser Ziel: Fünf Tasks mit verschiedenen Schedulern zum Laufen bringen. Die Tasks sind: LED-Blinken (250ms an, 750ms aus), Mario-Theme-Song über Lautsprecher, 7-Segment-Zähler alle 100ms, sowie kombinierte Tasks für den Countdown und die Frequenzanzeige.

Task 1: Round-Robin (RR) Scheduler

Der einfachste Scheduler ist eine while(1)-Schleife, die nacheinander alle Tasks aufruft. Ein kurzer Delay sorgt für die gewünschte Periode. Beispiel:

void loop() { while(1) { task1(); task2(); task3(); delayMicroseconds(100); } }
Wichtig: Jeder Task muss kurz und nicht blockierend sein – sonst leidet die Echtzeitfähigkeit.

Task 2: Synchronisierter Round-Robin mit ISR (SRRI)

Hier kommt ein Hardware-Timer ins Spiel. Ein 8-Bit-Timer erzeugt alle 2ms einen Interrupt. Die ISR setzt ein globales Flag (sFlag) auf DONE. Ein spezieller Task schedule_sync() wartet auf dieses Flag, aktualisiert dann die Schlafzeiten aller Tasks und weckt sie auf. Tasks können sich mit sleep_474(t) selbst schlafen legen – die Genauigkeit beträgt ±2ms. Die Task-Liste ist ein Array von Funktionspointern (max. 10 Tasks), abgeschlossen durch NULL.

Task 3: TCB-basierter Scheduler

Task Control Blocks (TCBs) speichern Metadaten: ID, Name (max. 20 Zeichen), Status (READY, RUNNING, SLEEPING) und eine Start-Zählung. Der Scheduler durchläuft die TCB-Liste und führt nur Tasks im Status READY aus. Neue Funktionen: task_self_quit() beendet einen Task, task_start(TCB *task) startet einen anderen. Das erlaubt dynamische Abläufe – z.B. Task 2 stoppt nach zweimaligem Abspielen des Songs, dann startet ein Countdown.

Praktische Umsetzung: Die fünf Tasks im Detail

Task 1: LED Blinken

Schließe eine LED über einen 250-Ohm-Widerstand an Pin 13 an. Die Routine schaltet die LED für 250ms ein und 750ms aus – simuliert mit delay() oder Timer. Achte darauf, dass der Task nicht blockiert, wenn er in einem Scheduler läuft.

Task 2: Mario-Theme über Lautsprecher

Direkte Timer-Manipulation: Konfiguriere einen Ausgangspin mit PWM, um Töne zu erzeugen. Nutze die Tonhöhen aus dem Anhang (z.B. Noten: E, E, F, G, G, F, E, D, C, C, D, E, E, D, D). Nach dem Abspielen schläft der Task 4 Sekunden. Verwende sleep_474(4000) im SRRI-Scheduler.

Task 3: 7-Segment-Anzeige zählt hoch

Eine 4-stellige 7-Segment-Anzeige (gemeinsame Kathode) wird über 7 Pins + 4 Steuerleitungen angeschlossen. Der Task erhöht alle 100ms einen Zähler und zeigt ihn an. Multiplexing ist nötig – aber das übernimmt der Task selbst.

Task 4: Kombinierte Countdown- und Frequenzanzeige

Nachdem Task 2 zweimal gelaufen ist, stoppt er. Ein Countdown von 3 Sekunden (in 100ms-Schritten) wird auf dem Display angezeigt. Danach startet Task 2 ein letztes Mal. Während der Musik zeigt das Display die Frequenz des aktuellen Tons in Hz an – die du aus deiner Ton-Tabelle auslesen kannst. Nach dem Ende der Musik erscheint ein Smiley (Muster aus dem Anhang) für 2 Sekunden.

Task 5: Smiley und Task-Steuerung

Der Smiley wird als neues Muster auf dem Display dargestellt. Nach 2 Sekunden werden alle Tasks außer Task 1 beendet – mit task_self_quit() oder durch Setzen des Status auf SLEEPING.

Integration und Debugging-Tipps

Beginne mit dem RR-Scheduler und einem einzelnen Task. Erweitere dann auf SRRI: Schreibe zuerst die ISR minimal – setze nur ein Flag. Teste mit einem Oszilloskop, ob der Interrupt alle 2ms kommt. Dann füge schedule_sync() hinzu. Nutze serielle Ausgabe, um Zustände zu verfolgen. Der TCB-Scheduler erfordert eine Liste – initialisiere alle TCBs und lasse den Scheduler zyklisch laufen. Wichtig: Kein Task darf länger als 2ms laufen, sonst wird der Zeitplan zerstört.

Häufige Fehler und Lösungen

  • ISR zu lang: Halte die ISR extrem kurz – setze nur ein Flag. Verschiebe aufwändige Arbeiten in den Hauptloop.
  • Blockierende Delays: Verwende sleep_474() statt delay(), damit der Scheduler andere Tasks ausführen kann.
  • Vergessene NULL-Terminierung: Das Funktionszeiger-Array muss mit NULL enden, sonst crasht der Scheduler.
  • Timer-Konfiguration: Für 2ms Tick bei 16 MHz: Prescaler 256, Compare-Wert 125 (ergibt 2ms).

Fazit und Ausblick

Mit diesen Schedulern beherrschst du die Grundlagen des Echtzeit-Multitaskings auf Mikrocontrollern. Die Konzepte sind übertragbar auf Betriebssysteme wie FreeRTOS oder Embedded Linux. Für dein Lab 3: Baue Schritt für Schritt, teste jeden Scheduler separat und dokumentiere die Ergebnisse. Viel Erfolg!