Wer meine letzten Artikel gelesen hat weiss, dass ich die uralte Programmiersprache Forth gerne auf Mikrocontrollern einsetze. Aber wieso eine 45 Jahre alte Sprache Heute noch einsetzen?
Der erste Teil dieses Blog-Beitrags beschreibt kurz meine Motivation und wo aus meiner Sicht die Vorteile gegenüber Arduino liegen. Das wird abgeschlossen mit einem schönen Beispiel (Multitasking!). Der zweite Teil sollte eigentlich eine Lösung für ein bekanntes Problem in der Elektronik aufzeigen. Sollte? Nun, der Artikel wurde länger als geplant und daher verschiebe ich das auf einen späteren Artikel. 😀
Fort auf STM32
Nun denn… wieso also eine 45 Jahre alte Programmiersprache einsetzten? Heute hat jeder Mikrocontroller mehr Leistung als die grössten Computer vor 45 Jahren. Aus dieser Erkenntnis folgt, dass man einen aktuellen Mikrocontroller als Ersatz für einen ganzen Computer nutzen kann. Klingt seltsam, bringt aber einen genialen Vorteil beim Schreiben von Programmen.
Bei Arduino schreibt man das Programm auf dem PC, kompiliert dieses in der Arduino IDE und überträg das binäre Programm auf den Mikrocontroller, wo es dann ausgeführt wird. Bei Fort kann man direkt auf dem Mikrocontroller entwickeln. Jeder Befehl wird sofort kompiliert und ausgeführt. Man kann also Stück für Stück das Programm aus den einfachsten Bestandteilen zusammenbauen und jede Komponente sofort testen.
Das erkauft man sich aber mit einer extrem ungewohnten Art der Programmierung. Forth ist Stack-Orientiert. Erst werden die Werte auf den Stack gelegt und danach von einem Befehl verarbeitet. Auf jeelabs.org gibt es eine gute Einführung in Forth, ich will das hier nicht wiederholen.
Multitasking in Forth
Wie einfach ist es doch eine LED blinken zu lassen mit einem Mikrocontroller. LED ein, kurz warten, LED aus, kurz warten – wiederholen. Nur, was macht man, wenn man eine zweite LED mit einem anderen Rhythmus blinken lassen will?
Das ist nur ein einfaches Beispiel und betrifft nicht nur LEDs. Immer wieder will man mehrere Sachen gleichzeitig machen. Die Arduino Umgebung kann aber kein Multitasking – kann also nicht mehrere Dinge gleichzeitig erledigen. Ganz richtig ist das nicht, denn durch Interrupts kann das „Hauptprogramm“ jederzeit unterbrochen werden und das Resultat ist fast wie Multitasking. Man könnte das LED-Blinken also so lösen:
- Eine Funktion für die erste LED als Interrupt an TIMER1 anhängen.
- Eine Funktion für die zweite LED an TIMER2 anhängen.
- Beide Timer konfigurieren.
- Endlosschleife im Hauptprogramm ausführen (Interrupts unterbrechen und schalten die LEDs)
Das funktioniert, ist aber kompliziert, schwierig zu debuggen und allzu viele von diesen Timern gibt es auch nicht.
Forth kann „kooperatives Multitasking“. Es können daher mehrere Funktionen (beinahe) gleichzeitig ausgeführt werden, solange sie regelmässig die Kontrolle an andere Funktionen abgeben. Sie müssen also Kooperieren (daher der Name „kooperatives Multitasking). Diese Kooperation ist in Forth in einigen Funktionen eingebaut. Zum Beispiel in „ms“, das dem „delay“ aus Arduino entspricht. Also jedes Mal, wenn das Programm warten soll, wird die Kontrolle automatisch an eine andere Komponente übergeben – genial!
- Die Funktion 1 schaltet die LED ein, wartet, schaltet die LED aus, wartet – wiederholen.
- Die Funktion 2 macht das selbe mit einer anderen LED.
- Beide Funktionen werden als Hintergrundprozess gestartet
Wie sieht das nun aus?
So sieht das Programm in Forth aus:
PA4 constant LED-A \ Port PA4 nennen wir LED-A PA5 constant LED-B \ Port PA5 ist LED-B task: a-blink \ Speicher reservieren für Hintergrundprozess : a-blink& ( - ) \ Hintergrundprozess definieren a-blink activate OMODE-PP LED-A io-mode! \ Pin von LED-A in Push-Pull Modus setzen begin LED-A iox! \ Pin LED-A umschalten 250 ms \ 250 ms warten - dahinter steckt das Multitasking again ; \ Endlosschleife von begin zu again task: b-blink \ Speicher reservieren für Hintergrundprozess : b-blink& ( - ) \ Hintergrundprozess definieren b-blink activate OMODE-PP LED-B io-mode! \ Pin von LED-B in Push-Pull Modus setzen begin LED-B iox! \ Pin LED-B umschalten 300 ms \ 300 ms warten - dahinter steckt das Multitasking again ; \ Endlosschleife von begin zu again a-blink& \ Hintergrundprozess für LED-A starten b-blink& \ und für LED-B multitask \ Multitasker aktivieren - LEDs blinken!
Ich habe euch gewarnt :). Die Sprache ist sehr ungewohnt. Aber dieses Beispiel zeigt, wie viel Potenzial in solch einer Lösung steckt.
Und nicht vergessen, jede dieser Zeilen kann einzeln, direkt auf dem Mikrocontroller eingegeben werden. Kein Kompilieren, kein Übertragen, kein Reset des Mikrocontrollers bei jeder Änderung. Nach der Eingabe der letzten Zeile blinken die LEDs und die nächsten Befehle können eingegeben werden. Auch diese „Konsole“ auf dem Mikrocontroller ist ein eigener „Prozess“, der mit den anderen Hintergrundprozessen kooperiert.
Ein sehr ausgezeichneter Post, der alles ausgezeichnet zusammen fasst. Ich habe mich damals auch durch alle Anfänger-Guides gelesen bevor ich dann nutzliche Bescheid gefunden habe. Bei mir ist es immer so, dass ich die meisten Fingerzeigen für mehr Einflussbereiche zwar kenne, aber dann doch nicht umsetzte. So einen Beitrag zu lesen motiviert dann wieder – habe ich gedacht und bin weiter gesurft. Sowas ist äußerst helfend! Aber es gibt noch ein Betreff dazu – Virtuelle Datenräume . Es ist vor allem schön zu wissen, dass alle mal klein angefangen habe. Das ist zwar eigentlich klar, aber wird gern vergessen.