Einführung in die Programmierung des Mikrocontrollers Propeller

Voll abgedreht: Einführung in die Programmierung des Mikrocontrollers Propeller
Keine Kommentare

In der Anfangszeit der Computertechnik entstanden Prozessoren oft mit ihrer eigenen Programmiersprache. Der Erfolg von Hochsprachen wie C und Co. löste diese Bindung. Bei Parallax entstand vor einigen Jahren mit dem Propeller ein hoch interessanter Mikrocontroller, der hardwaremäßig auf Nebenläufigkeit ausgelegt wurde und mit einer eigenen Programmiersprache namens PIN ausgestattet ist.

Der Hersteller Parallax ist seit jeher für seine entwicklerfreundlichen Prozessorsysteme bekannt. Ein als BASIC Stamp bezeichnetes Modul ermöglichte die Programmierung von 8-Bit-Mikrocontrollern unter Nutzung eines zugegebenermaßen stark eingeschränkten Basic-Dialekts und legte so die Grundlagen für den weiteren Erfolg des Unternehmens.

Der Parallax Propeller sollte viele der aus Parallax-Gründer Chip Graceys Sicht „ärgerlichen“ Schwachstellen klassischer Controller umgehen. Als wichtigstes Problem identifizierte er hierbei die Art, wie klassische MCUs mit Hardware bzw. mit Kommunikationsbussen interagieren. Zum Verständnis dieses Problems wollen wir als Erstes einen gewöhnlichen Mikrocontroller betrachten.

Abb. 1: Protokolle werden durch dedizierte Funktionseinheiten abgebildet

Abb. 1: Protokolle werden durch dedizierte Funktionseinheiten abgebildet

Das in Abbildung 1 skizzierte und auf den ersten Blick durchaus gangbar erscheinende Konzept erweist sich in der Praxis aus verschiedener Sicht als ärgerlich: Erstens ist ein derartiger Mikrocontroller auf die Protokolle beschränkt, die sein Entwickler von Haus aus vorgesehen hat. Der PIC16F1503 lässt sich beispielsweise nicht bzw. nur über aufwendiges Bit-Banging um im Automotive-Bereich verbreitete Protokolle wie CAN erweitern.

Das von Gracey entwickelte Konzept basiert hingegen auf als Cog bezeichneten Recheneinheiten. Ein Cog ist hierbei ein „eigenständiger“ Mikrocontroller, der mit dem Rest des Verbunds über diverse geteilte Ressourcen verbunden ist. Die Realisierung von harten Protokollen wie I2C, SPI und Co. erfolgt nun, indem der jeweilige Core mit bestimmter Betriebssoftware ausgestattet wird. Zur Optimierung des Energieverbrauchs gibt es zudem die Möglichkeit, Cogs „einschlafen zu lassen“ bis ein bestimmtes Ereignis auftritt. Die deterministische Aufwachzeit garantiert, dass das System auch Echtzeitanforderungen erfüllen kann.

Der Fluch des Kleinen

Parallax ist im Vergleich zu Atmel, STMicroelectronics (früher SGS Thompson), Microchip Electronics und Co. ein geradezu verschwindend kleines Unternehmen, das nicht ausschließlich vom Vertrieb seiner MCUs leben kann und stattdessen auch diverse Breakout-Boards produziert, um die hauseigenen Maschinen besser auszulasten (Kasten: „In die Produktion“).

Daraus resultiert eine etwas problematische Verfügbarkeit: Evaluationsplatinen finden sich nur bei manchen Händlern. Wir wollen in den folgenden Schritten die in Abbildung 2 gezeigte Propeller-QuickStart-Platine verwenden, deren Alter man schon anhand der Mini-USB-Buchse erkennen kann.

Abb. 2: Diese Platine hilft bei der Realisierung von Propeller-basierten Applikationen

Abb. 2: Diese Platine hilft bei der Realisierung von Propeller-basierten Applikationen

Auf Softwareseite wollen wir ein als Propeller Tool bezeichnetes Programm herunterladen. Die Software funktioniert auf allen halbwegs aktuellen Versionen von Windows – in folgenden Schritten kommt Windows 8.1 auf einer AMD-Workstation zum Einsatz.

In die Produktion

Parallax bietet den Propeller in den drei Gehäuseversionen DIP, QFP und QFN an. Einzelstücke kosten dabei ca. 7,99 $, während man beim Kauf von 1 000 Stück um die 5,19 $ entrichten muss. Zudem gibt es für Benutzer von FPGAs einen „Softcore“, der unter Online im Detail beschrieben ist.

Eigenwillige Programmiersprachen kommen im Allgemeinen mit eigenwilligen Entwicklungsumgebungen. Im Fall des Parallax Propeller hört das Tool auf den Namen Propeller Tool und präsentiert sich wie in Abbildung 3 gezeigt.

Abb. 3: Die IDE ist optisch ungewöhnlich

Abb. 3: Die IDE ist optisch ungewöhnlich

Von Interesse ist der auf der linken Seite permanent eingeblendete Dateisystembrowser. Die IDE zeigt während der Arbeit an, wo Sie sich im Dateisystem befinden und welche Programmdateien an dieser Stelle auf Abarbeitung warten. SPIN-Files haben die Dateiendung .spin, die – Nomen est Omen – für in SPIN gehaltene Programmdateien steht. Die Textbox dient zur Eingabe des eigentlichen Programms, während die darüber befindlichen vier Radiobuttons das Anpassen der Darstellungsweise des Codes erlauben. Bei der Arbeit mit kurzen Programmbeispielen ist die Einstellung FULL SOURCE ideal. CONDENSED zeigt unnötigen Whitespace nicht an, während SUMMARY den Code auf Blockebene reduziert. DOCUMENTATION erlaubt die Auswertung von Dokumentationskommentaren, die wir an dieser Stelle aber nicht weiter besprechen.

Lasset uns coden

Nach diesen einführenden Überlegungen ist es an der Zeit, unser erstes Programm zu erzeugen. Nehmen Sie sich das von der IDE von Haus aus angelegte Tab und ersetzen Sie seinen Inhalt durch folgenden Code:

PUB Puber
DIRA[16..23] := %1111111
repeat
  OUTA[16..23] := !OUTA[16..23]
  waitcnt(clkfreq/4 + cnt)

Der mit Abstand wichtigste – und für die meisten Entwickler extrem ärgerliche – Aspekt von Spin ist, dass die Programmiersprache Whitespace nicht nur zur Steigerung der Übersichtlichkeit einsetzt. Schleifenkörper, Methodenkörper und Ähnliches werden ausschließlich durch die Menge der vorangestellten „Leerzeichen“ gekennzeichnet. Die IDE unterstützt dies durch Einblendung einer Baumstruktur. Einzelne Spin-Dateien realisieren (im Idealfall) ein Programmmodul, das „alleinstehend“ eingesetzt werden kann oder in anderen Modulen Aufnahme findet.

Kenner der österreichischen Populärkultur wundern sich darüber, was der Methodenname Puber zu bedeuten hat. PUB beschreibt die Einleitung einer Methode, die diverse Parameter übernehmen kann. Daraus folgt der Name: Wir wollen uns hier an Österreichs prominentestem Sprayer schadlos halten.

Diese Vorgehensweise ist insofern interessant, als es in der Propeller-Welt immer wieder Diskussionen darüber gibt, wie bzw. wo der Einsprungspunkt in den Programmkörper erfolgt. Beenden wir die Debatte: Laut den offiziellen Informationen von Parallax dient immer die Methode als Einsprungspunkt, die „ganz oben“ in der Spin-Datei steht. Das Übergeben eines Namens wie Start, INIT oder Main mag als „guter Programmierstil“ gelten, ist aus technischer Sicht allerdings ohne jegliche Relevanz – unsere Puber-Methode funktioniert genauso.

Laut dem Schaltplan des QuickStart-Boards sind die acht von Parallax spendierten Leuchtdioden mit den Port-Pins 16 bis 23 verbunden. Wie so gut alle anderen Controller startet auch der Propeller im Allgemeinen inert (Kasten: „Eigenwillige Startprozedur“), weshalb wir im ersten Schritt das DIRA-Register mit einer Konstante bevölkern, die die Pins in Outputs umwandelt. Hier zeigen sich zwei Besonderheiten von Spin: Erstens ermöglicht die Syntax mit den beiden Punkten das Ansprechen von „Ausschnitten“ eines Felds, während die Prozentdeklaration das Festlegen von Bitfeldern ermöglicht. Mit Pascal aufgewachsene Entwickler freuen sich derweil über den Zuweisungsoperator :=. Als Nächstes folgt eine Endlosschleife, die das Ausgaberegister invertiert. Auch hier zeigt sich, dass Spin mit diversen „Helfermethoden“ ausgestattet wurde, die bei der Interaktion mit Bitfeldern hilfreich sind. Danach folgt noch ein Aufruf von waitcnt, um den Prozessrechner zum „Warten“ zu animieren. Er wird so lange angehalten, bis der systemeigene Zähler den an die Funktion übergebenen Wert erreicht. cnt beschreibt dabei den aktuellen Zählerstand, während die Variable clkfreq angibt, wie viele „Ticks“ pro Sekunde vergehen. Unser Programm wartet also eine Viertelsekunde lang, bevor es die nächste Änderung des Ausgabearrays durchführt.

Eigenwillige Startprozedur

Während die meisten Controller komplett inert starten, tanzt der Propeller an dieser Stelle aus der Reihe. Er sucht nach dem Start im ersten Schritt nach einem angeschlossenen PC, von dem er Code herunterladen will. Scheitert dies, so folgt der Versuch, ein an bestimmten PINs anzuschließendes EEPROM auszulesen. Ist auch dieses nicht vorhanden, so geht der IS schlafen. Die von Parallax angebotenen Versionen des Controllers haben keinen internen Flash-Speicher, was insbesondere im Bereich der Kopiersicherheit Probleme verursacht: Externe EEPROMs sind nunmal sehr leicht auslesbar.

Kompiliertes

Speichern Sie die .spin-Datei im nächsten Schritt an einem beliebigen Ort im Dateisystem. Das Propeller Tool besteht darauf, dass Files vor der Nutzung ins Dateisystem geschrieben werden. Im nächsten Schritt klicken Sie auf RUN | COMPILE CURRENT | VIEW INFO. Das Propeller Tool blendet sodann Informationen über den Speicherverbrauch des gerade geöffneten .spin-Files ein.

Das Anklicken von RUN | IDENTIFY HARDWARE animiert die IDE zur Suche nach angeschlossenen Propeller-Boards: Die hier verwendete QuickStart-Platine erscheint im Gerätemanager als serieller Port. Zu guter Letzt klicken Sie auf RUN | COMPILE CURRENT | LOAD RAM, um das Programm in den Arbeitsspeicher des Propeller zu kopieren. Erfreuen Sie sich einer blinkenden LED-Zeile.

Der Parallax Propeller bietet wie viele andere Prozessrechner, darunter auch der Tessel, die Möglichkeit an, Code entweder ins RAM oder in das im Kasten erwähnte Flash zu schreiben. Da die Anzahl der möglichen Schreibzyklen eines EEPROMs beschränkt ist (und das Deployment länger dauert), bietet es sich an, während der Entwicklung ins RAM zu schreiben: Der aus dem Flash geladene Code wird sowieso im RAM des Controllers zwischengespeichert, bevor er zur Ausführung gelangt.

Nachdem wir uns nun der korrekten Funktion des Systems versichert und zudem einen ersten Schritt in die eigenwillige Welt von Spin unternommen haben, ist es nun an der Zeit, die als Cog bezeichneten Prozessorkerne näher anzusehen. Hierzu betrachten wir das in Abbildung 4 gezeigte Architekturdiagramm, das aus dem Datenblatt des Controllers stammt.

Abb. 4: Ein Propeller besteht aus einer Gruppe von „Unterprozessoren“ (Bildquelle: Parallax)

Abb. 4: Ein Propeller besteht aus einer Gruppe von „Unterprozessoren“ (Bildquelle: Parallax)

Jeder der acht Cogs ist ein vom Rest des Controllers mehr oder weniger unabhängiger Rechenkern. Unser Programm ergreift nach dem Start einfach den nächsten freien Cog, bevölkert ihn mit einem Spin-Interpreter und schickt das Resultat auf die Reise.

Dieses Verhalten lässt sich überprüfen, da Spin mit der Methode cogid eine Funktion mitbringt, die die „Selbstreflexion“ im gerade laufenden Code erlaubt. Die nächste Aufgabe besteht deshalb darin, die Cog-ID über die acht Leuchtdioden des Boards auszugeben. Eine erste Version des neuen Programms sieht aus wie in Listing 1.

VAR
  byte Cog

PUB Puber
DIRA[16..23] := %11111111
Cog:=cogid
OUTA[16..23] := %00000000
OUTA[16+Cog] := 1
cogstop(Cog)

Interessant ist an dieser Stelle, dass Chip Gracey das von Pascal bekannte „Aufteilen“ der Codedatei in Sektoren übernimmt. Am Anfang findet sich ein VAR-Block, der die für das gesamte File geltende Variable deklariert. Wir legen ein Byte an, um die ID des Cogs, der für die Programmausführung zuständig ist, abzuspeichern. Als Nächstes kommt wie gewohnt der Einsprungspunkt, der als Erstes die acht Ausgabepins initialisiert. Dann schreiben wir den in der Funktion cogid enthaltenen Wert in die Variable Cog. Sie erlaubt uns ab sofort den Zugriff auf die Cog-ID.

Methoden können in Spin Werte zurückliefern. Besonders interessant ist hierbei, dass der Aufruf einer Funktion nicht unbedingt mit Klammern erfolgen muss. Die Klammern sind nur dann erforderlich, wenn zur Ausführung des befindlichen Unterprogramms weitere Parameter erforderlich sind.

API Summit 2018

From Bad to Good – OpenID Connect/OAuth

mit Daniel Wagner (VERBUND) und Anton Kalcik (business.software.engineering)

Als Nächstes folgt ein vergleichsweise einfacher Prozess. Wir schalten alle LEDs aus, um danach die von der ID beschriebene LED einzuschalten. Zu guter Letzt folgt ein Aufruf von cogstop, um den betreffenden Code anzuhalten. Rein theoretisch wäre das Programm an dieser Stelle ausführbar. In der Praxis zeigt der Propeller aber hier seine Zähne. Sobald alle Cogs „irreversibel“ gestoppt werden, stellt er die Arbeit ein. Aus diesem Grund leuchtet die LED nicht auf. Eine korrekt funktionierende Version des Programms würde folgendermaßen aussehen:

Cog:=cogid
repeat
  OUTA[16..23] := %00000000
  OUTA[16+Cog] := 1
  waitcnt(clkfreq * 4 + cnt)

Diese Version des Programms unterscheidet sich nur insofern von seinem Vorgänger, als wir nach dem Abarbeiten des Cog-Codes eine längere Pause einschreiben. Senden Sie das Programm in Richtung des Boards, um sich am Aufleuchten der LED zu erfreuen.

Objektorientiert? Objektbasiert!

Die Patterns der objektorientierten Programmierung sind klassischer Universitätsstoff, der in den meisten modernen Programmiersprachen mehr oder weniger eins zu eins übernommen wird. Das hinter Spin stehende Entwicklerteam spricht explizit davon, dass die eigene Programmiersprache nicht objektorientiert, sondern stattdessen objektbasiert sei. Hinter dieser auf den ersten Blick semantischen Unterschiedlichkeit verbirgt sich ein gravierender konzeptioneller Unterschied: Während man in C++, Java und Co. mit Vererbung arbeitet, ist ein Spin-Programm – Ähnlichkeiten zur bada-Programmierumgebung sind rein zufällig – eine Ansammlung von Objekten, die gemeinsam an der Erfüllung eines Ziels arbeiten. Einzelne „Objekte“ entstehen dabei stets in Form einer eigenen .spin-Datei, die bei Bedarf weitere Dateien einbinden kann.

Wir wollen dieses Paradigma in folgenden Schritten zur Realisierung eines kleinen „Latenzmessers“ einspannen, der uns das Messen der Aufweckzeit ermöglicht. Dazu zunächst eine kurze theoretische Überlegung: In gewöhnlichen Mikrocontrollern kann der Kern „schlafen gelegt“ werden, wenn aktuell keine Arbeit ansteht. Auf diese Art und Weise lässt sich insbesondere im mobilen Einsatz wertvolle Energie einsparen. Im Fall von Propeller gibt es eine ähnliche Möglichkeit, um die Cogs schlafen zu schicken. Derzeit gibt es in der Sprachspezifikation viele verschiedene Wartebefehle, die in der Tabelle zusammengefasst sind.

Funktion Kurzbeschreibung
WAITCNT Wartet, bis der Systemzähler einen bestimmten Wert erreicht hat.
WAITPEQ Wartet, bis die Ausgabepins bzw. die Pins einen bestimmten Wert aufweisen; ähnlich dem Patterntrigger von Logikanalysatoren.
WAITPNE Wartet, bis das an einer Gruppe von Eingabepins anliegende Muster nicht mehr dem übergebenen Wert entspricht; auch hier Ähnlichkeiten zu Logikanalysatoren.
WAITVID Wartet, bis der – in diesem Artikel nicht genutzte – Videogenerator zur Aufnahme weiterer Informationen bereit ist.

Tabelle 1: Wartebefehle von Propeller laut Spezifikation

Im Interesse der Einfachheit wollen wir allerdings auch hier mit einem primitiven Programm beginnen, das seine Untätigkeit auf einem einzelnen Cog entfaltet (Listing 2).

VAR
  byte Cog

PUB Puber
DIRA[16..23] := %11111111
DIRA[0..7] := %00000000
Cog:=cogid
repeat
  waitpeq(%1, %1, 0)
  OUTA[16]:=%1
  waitcnt(clkfreq / 20000 + cnt)
  OUTA[16]:=%0
  waitpne(%1, %1, 0)

Die hinter diesem Programm stehende Idee ist aus elektronischer Sicht simpel. Wir füttern den Propeller über einen Funktionsgenerator mit einem Rechtecksignal, dessen Wellenform wir mehr oder weniger nach Belieben verändern können. Der erste Teil des Programms schaltet den Ausgangspin ein: waitpeq dient zum Warten.

Der erste Parameter beschreibt hierbei die zu überwachenden Pins, während der zweite Parameter das Muster beschreibt. Chip Gracey arbeitet seit längerer Zeit an einer zweiten Version von Propeller, die zwei Ports mitbringt. Aus diesem Grund sind die Funktionen mit einem numerischen Parameter ausgestattet, der den anzusprechenden Port beschreibt. Da wir allerdings nur einen einzelnen Port vor uns haben, wird immer null übergeben.

Beim Eintreffen einer Änderung, also einer positiven Flanke, setzen wir den Ausgangspin, um ihn daraufhin nach kurzer Zeit low zu ziehen. Danach folgt ein weiterer Wartebefehl, der die Programmausführung so lange blockiert, bis die Wellenform wieder in den low-Pegel gefallen ist. Dieser Zusammenhang wird in Abbildung 5 grafisch dargestellt.

Abb. 5: Unser kleines Programm verwandelt die Rechteckquelle in eine Folge von Impulsen

Abb. 5: Unser kleines Programm verwandelt die Rechteckquelle in eine Folge von Impulsen

Zum erfolgreichen Test des Programms müssen wir einen Testharnisch aufsetzen. Sie benötigen Informationen über den auf der Oberseite der Platine befindlichen Header. Laut Datenblatt präsentiert sich sein Layout wie in Abbildung 6 gezeigt.

Abb. 6: Achtung: Das Propeller-QuickStart-Board und der Raspberry Pi sind nicht kommunal

Abb. 6: Achtung: Das Propeller-QuickStart-Board und der Raspberry Pi sind nicht kommunal

Ein weiteres wichtiges Ärgernis ist die Signalspannung von 3,3 V. Danach können Sie den Funktionsgenerator auch schon mit dem Eingangspin verbinden, während der Ausgangspin an einen Oszillographen weitergeleitet wird. Stellen Sie den Funktionsgenerator, der Autor setzt hier auf seinen altbewährten AWG2021, wie in Abbildung 7 gezeigt ein, um das bereits in Abbildung 5 gezeigte Bild auf einem Oszillographen zu erhalten.

Abb. 7: Achtung: Der AWG2021 setzt Terminierung voraus und liefert sonst zu hohe Spannungen

Abb. 7: Achtung: Der AWG2021 setzt Terminierung voraus und liefert sonst zu hohe Spannungen

Interessant ist in diesem Zusammenhang, dass das Ein- und Ausschalten der Portpins des Propeller nicht beliebig schnell erfolgen kann. Bei einem Divisionsfaktor von etwa 40 000 ist am Bildschirm keine „kontinuierliche“ Welle mehr zu sehen (Kasten: „Analog ist besser?“).

Wie dem auch sei, lassen Sie uns an dieser Stelle zur Parallelisierung der Aufgabe schreiten. Dazu muss der Code auf einen anderen Cog übersiedelt werden, um den „Haupt-Cog“ mit anderen Aufgaben beschäftigen zu können.

Analog ist besser?

Zur Analyse von „variablen Latenzen“ empfehlen sich Analogoszillographen oder DSOs, die mit der Fähigkeit zur Darstellung unter variabler Persistenz ausgestattet sind. Fehlt dieses Feature, so würde die „Häufigkeitsinformationen“ verloren gehen.

Klicken Sie im ersten Schritt auf FILE | NEW, um das Propeller Tool zum Öffnen einer weiteren Datei zu animieren. Kopieren Sie den Inhalt der Methode Puber. Der Inhalt des neuen Tabs sollte sich folgendermaßen präsentieren:

PUB worker
repeat
  waitpeq(%1, %1, 0)
  OUTA[16]:=%1
  waitcnt(clkfreq / 20000 + cnt)
  OUTA[16]:=%0
  waitpne(%1, %1, 0)

Achten Sie dann darauf, die Datei unter einem bekannten Namen zu speichern. In den folgenden Schritten hört das File auf den Namen susrunner.spin. Diese Festlegung ist keine Pedanterie des Autors: Der Name ist bei einer .spin-Datei erforderlich, wenn sie an „anderer“ Stelle in ein Spin-Objekt eingebunden werden soll. Wir wollen an dieser Stelle genau dies bewerkstelligen, weshalb der neue Korpus des Hauptprogramms wie in Listing 3 aussieht.

OBJ
  sus:"susrunner"

VAR
  byte Cog

PUB Puber
DIRA[16..23] := %11111111
DIRA[0..7] := %00000000
Cog:=cogid
sus.worker

An dieser Stelle zeigt sich die Beziehung zwischen Spin und Pascal. Der mit Obj beginnende Block ist für das Einbinden von Objekten erforderlich. Hier kommt eine zweiwertige Syntax zum Einsatz. Vor dem Doppelpunkt findet sich der Name, unter dem das Modul im Rest des Programms ansprechbar sein soll. Danach folgt ein String, der den Dateinamen – die Dateiendung muss unbedingt weggelassen werden – aufnimmt. Sodann lassen sich die als „öffentlich“ deklarierten Teile des Moduls wie gewohnt aufrufen, was zu einer wesentlichen Verkürzung der Hauptfunktion führt.

Aus technischer Sicht findet sich in der neuen Version des Programms kein Unterschied zum Vorgänger, auch die Latenz ist im Großen und Ganzen gleichgeblieben. Zum Verschieben von Programmen auf neue Cogs gibt es in Spin zwei Funktionen. COGINIT erlaubt das Zuweisen eines bestimmten Programmcodes, der vom als Parameter bestimmten Cog ausgeführt wird. Die Funktion cognew unterscheidet sich insofern davon, als sie beim „Scheduler“ des Propeller anfragt, ob ein freier Cog verfügbar ist. Ist dies der Fall, so wird der neue Code dort platziert und ausgeführt. Gibt es keinen freien Cog, so wird ein Fehler retourniert.

Wir wollen den folgenden Schritt auf die rücksichtsvollere Methode setzen, was zu einer Änderung des Hauptprogramms führt (Listing 4).

OBJ
  sus:"susrunner"

VAR
  byte Cog
  long SqStack[10]

PUB Puber
DIRA[16..23] := %11111111
DIRA[0..7] := %00000000
Cog:=cogid
cognew(sus.worker, @SqStack)

Cognew ist insofern interessant, als die Methode zwei Parameter übernimmt: Erstens einen „Verweis“ auf die auszuführende Routine, und zweitens einen Zeiger auf einen Speicherbereich, der als Stack für die lokalen Variablen verwendet werden soll. Unser Programm ist vergleichsweise sparsam, weshalb der Bereich von zehn Longs ausreicht. Die Variable wird wie gewohnt im VAR-Block deklariert. Das Nutzen des @-Operator informiert Spin darüber, dass an dieser Stelle ein Zeiger übergeben werden soll.

Wer dieses Programm auf dem Propeller ausführt und das Gerät mit einem Oszillographen verbindet, sieht die von weiter oben bekannte Wellenform. Auf den ersten Blick sieht alles so aus, als ob das Programm korrekt funktionieren würde. Leider ist das in der Praxis nicht der Fall. Zum Feststellen der Ursachen wollen wir ein wenig tiefer in das Propeller-Betriebssystem einsteigen.

Als erste Maßnahme wollen wir hierbei eine Bibliothek laden, die uns das Übertragen von Informationen in Richtung des Desktops ermöglicht. Die eingeschränkte Hardware von Propeller erlaubt kein vollständiges Debugging, weshalb wir als Entwickler mit dem „Aussenden“ von Statusmeldungen unter Nutzung von Terminal, Logic Analyzer und Oszillograph vorliebnehmen müssen. Eine Anmerkung in eigener Sache an dieser Stelle: Auf youtube finden Sie ein zehnminütiges Video von mir, das das Debugging unter Nutzung von Digitalspeicheroszillographen im Detail erklärt.

Erfreulicherweise ist das für die Ausgabe von seriellen Informationen vorgesehene Objekt Teil der Standarddistribution, weshalb es folgendermaßen in das Hauptprogramm eingebunden werden kann:

OBJ
  pst : "Parallax Serial Terminal"

Fehlende Objekte finden sich normalerweise im Propeller Object Exchange. Neben diversen Mathematikklassen finden sich hier auch schlüsselfertige Implementierungen für diverse häufig verwendete Kommunikationsprotokolle.

Als nächste Amtshandlung müssen wir susrunner zur Ausgabe der Cog-ID befähigen. So sind folgende Änderungen erforderlich:

PUB worker
pst.Start(115_200)
pst.Clear
pst.Str(string("Arbeite auf Cog"))
pst.Dec(cogid)

Die Parallax-Terminalklasse exponiert eine Gruppe von Methoden, die das Übertragen von Informationen ermöglichen: Da Spin nur leidliche Unterstützung für Polymorphie bietet, ist diese Vorgehensweise „sauberer“. Klicken Sie im nächsten Schritt auf RUN | PARALLAX SERIAL TERMINAL, um das in Abbildung 8 gezeigte Terminalfenster zu öffnen.

Abb. 8: Achtung: Serieller Port und Baud-Rate müssen korrekt eingestellt sein

Abb. 8: Achtung: Serieller Port und Baud-Rate müssen korrekt eingestellt sein

An dieser Stelle tritt ein kleines Problem auf: Das Terminal startet erst nach einem Klick auf Enable; unser Board gibt zu diesem Zeitpunkt allerdings schon Informationen aus. Das Drücken des Reset-Buttons auf dem Board würde den Arbeitsspeicher löschen. Als Lösung bietet sich das Brennen des Flash-Speichers an. Danach fehlt noch ein Reboot: Im Monitor erscheint daraufhin die wenig Freude auslösende Nachricht, dass der Code nach wie vor auf Cog Null ausgeführt wird.

Dieses auf den ersten Blick widersinnig erscheinende Verhalten liegt in einer Designentscheidung Chip Graceys begründet: Ein Objekt darf sich nur selbst auf einen anderen Cog verschieben. Wer cognew mit einer Methode aus einem anderen Objekt aufruft, lässt den Korpus der Methode auf seinem eigenen Cog ablaufen. Zur Umgehung dieses Problems müssen wir unser susrunner-Objekt wie in Listing 5 anpassen.

OBJ
  pst : "Parallax Serial Terminal"

VAR
  long SqStack[10]  

PUB raiser
RESULT := cognew(worker, @SqStack)

PRI worker
repeat
DIRA[16..23] := %11111111
DIRA[0..7] := %00000000
repeat
  waitpeq(%1, %1, 0)
  OUTA[16]:=%1
  waitcnt(clkfreq / 20000 + cnt)
  OUTA[16]:=%0
  waitpne(%1, %1, 0)

Der wichtigste Unterschied zum vorigen Code ist der, dass wir nun eine Start-Methode exponieren und den Stack in susrunner anlegen. Zudem ist der eigentliche Methodenkörper nun in einer Funktion enthalten, die über das Schlüsselwort PRI entsteht: Es handelt sich dabei, Nomen est omen, um eine Kurzform für den Zugriffsqualifikator private.

Von besonderem Interesse ist hier nur die Duplizierung des Konfigurationscodes für DIRA. Die Konfiguration der Register erfolgt durch OR-ren der DIRA-Inhalte aller aktiven Cogs. Die Konfiguration muss deshalb in worker erfolgen – raiser läuft ja noch im „alten“ Thread.

Mit diesem Wissen bewaffnet können wir abermals zum Hauptprogramm zurückkehren, wo die Aktivierung wie in Listing 6 erfolgt.

OBJ
  sus:"susrunner"
  pst : "Parallax Serial Terminal" 

VAR
  byte Cog
  
PUB Puber
DIRA[16..23] := %11111111
DIRA[0..7] := %00000000
pst.Start(115_200)
pst.Clear
pst.Str(string("test1 auf : "))
pst.Dec(cogid)
pst.Str(string(" Raiser schreibt auf : "))
pst.Dec(sus.raiser)

repeat
  waitcnt(cnt+clkfreq)

Schicken Sie die zweite Version des Programms in Richtung des Entwicklungsboards, um sich an der in der Abbildung 9 gezeigten Ausgabe zu erfreuen.

Abb. 9: Dank korrekter Platzierung rennt die Routine nun am korrekten Cog

Abb. 9: Dank korrekter Platzierung rennt die Routine nun am korrekten Cog

Fazit

Chip Gracey geht mit dem Propeller in vielerlei Hinsicht neue Wege. Die Programmiersprache bietet unter anderem innovative Methoden zum Umgang mit Feldern und zur Handhabung von Nebenläufigkeit. Wer sich jemals beim Design einer neuen Programmiersprache mit diesen Problemen herumschlagen muss, sollte versuchen, von der Arbeit des Parallax-Teams zu profitieren.

Ob man den Propeller in einem eigenen Design einsetzen sollte, ist derweil eine schwierigere Frage. Der Propeller der ersten Generation bietet im Vergleich zu anderen Prozessoren relativ wenig Rechenleistung und nimmt viel Strom auf. Wer ihn nicht gerade in eigenes FPGAs packen kann, ist – auch ob des hohen Preises – in vielen Fällen mit Produkten anderer Hersteller besser bedient. Der Propeller 2 sollte dieses Problem lösen. Leider wartet die Entwicklerschaft beinahe zwei Äonen auf ihn.

Entwickler Magazin

Entwickler Magazin abonnierenDieser Artikel ist im Entwickler Magazin erschienen.

Natürlich können Sie das Entwickler Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -