Das Raspberry-Pi-Angebot wurde Anfang des Jahres um den Raspberry Pi Zero W erweitert. Einen Rechner mit Funkanbindung für zehn Dollar, dessen sehr kleine Leiterplatte (PCB) nur noch
6 cm x 3 cm misst. Der Raspberry Pi Zero basiert auf dem SoC (System on Chip) BCM2835 von Broadcom. Er enthält einen ARM-Core mit 1 GHz Rechenleistung, eine Grafikverarbeitungseinheit (GPU), eine Videoschnittstelle, mehrere serielle Schnittstellen (USB, UART, SPI, I2C) und eine Schnittstelle für externen Speicher. Letztere ist zur Verwaltung des 512 MB großen DDR2-RAM-Speichers und des Massenspeichers (SD-Karte) erforderlich. Sie sind unter anderem für die Ausführung eines Linux-Betriebssystems (OS) verantwortlich. Das sind beeindruckende Fähigkeiten für einen Single-Chip-Computer, vor allem im Vergleich zu früheren Generationen von Personal Computern. Im Vergleich zum Raspberry Pi Zero haben Mikrocontroller (MCUs), die aktuell in Embedded-Steuerungen zum Einsatz kommen, eine deutlich langsamere Taktgeschwindigkeiten von 10 bis ungefähr 100 MHz und damit auch eine geringere Rechenleistung.
Alle aktuellen MCUs sind mittlerweile praktisch kleine SoCs. Wie man es bei einer MCU erwarten darf, sind der RAM und der Flash-Speicher auf dem Chip integriert. Serielle Schnittstellen (USB, UART, SPI und I2C) sind genauso vorhanden, wie alle Schaltungen zur Stromversorgungsregelung und Spannungsüberwachung. Meist finden sich auch fünf oder mehr unterschiedliche Oszillatoren auf einer MCU. Sie sorgen für eine flexiblere und besser steuerbare Leistungsaufnahme. Hinzu kommt analoge Peripherie, wie ADCs, DACs, Operationsverstärker, Komparatoren, mit großen Ein- und Ausgangs-Multiplexern. Der Raspberry Pi verfügt im Gegensatz dazu über sehr gute Videofunktionen. Das spiegelt den Unterschied beim Design wider, sobald Embedded- anstelle von Computing-Funktionen gewünscht sind.
Es überrascht daher nicht, dass Rasp-
berry-Pi-Nutzer, die sich mit der realen Welt verbinden und nicht nur einfache I/O-Anwendungen wie blinkende LEDs entwickeln wollen, kleinere MCUs verwenden. Meist sind das MCUs mit 8 Bit auf „Hats“, also kleinen Tochterboards, um die notwendigen I/O-Schnittstellen und die erforderlichen Spannungspegel bereitstellen.
Die unfaire Parallele zwischen diesen beiden Welten soll an dieser Stelle nicht weiter bemüht werden. Wichtig ist, dass sie ein gemeinsames Anliegen teilen: die Komplexität möglichst gering zu halten. Die Lösungen dafür sind ähnlich, unterscheiden sich allerdings an einigen wichtigen Punkten. Beide Plattformen bieten kostenlose Software-Tools wie integrierte Entwicklungsumgebungen (IDEs), Compiler, Linker, Simulatoren, Debugger, eine mehr oder weniger offene Middleware, inklusive OS oder Realtime-OS (RTOS), und eine kleine Auswahl an Hardware-Optionen.
Die Unterschiede zwischen den beiden Lagern, Embedded und allgemeines Computing, sind kleiner als man denkt. Beide verlassen sich auf ähnliche, wenn nicht identische Tool-Ketten, die zum größten Teil GNU-basiert sind. Auf der Middleware-Ebene sind sich die Open-Source-Optionen auch wieder sehr ähnlich, sobald man die unteren Treiberschichten korrekt abstrahiert. Auf der OS-Ebene ist der Unterschied am größten, da viele MCUs gerne ein RTOS betreiben, aber die Komplexität eines vollständigen Linux-Kernels nicht tragen können. Hier zeigt sich der wahre Unterschied. Die Echtzeit-Fähigkeit ist eine wichtige Eigenschaft für das OS.
Die Komplexität nimmt zu
Bei der Dokumentation zeigt sich auf beiden Seiten eine inflationäre Komplexität. Ein gutes Beispiel dafür ist zum Beispiel die kleine und einfache MCU PIC16F1619. Sie beruht auf der beliebten 8-Bit-PIC-Architektur und wird häufig zur Steuerung kleiner Geräte eingesetzt. Dafür verfügt sie über einen Flash-Speicher mit 16 KB, ein Dutzend digitaler Peripherie-Schnittstellen und fast genauso viele Analogmodule. Untergebracht ist sie in einem winzigen 20-poligen Gehäuse. Das Datenblatt des PIC16F1619 umfasst jedoch 650 Seiten, und das vor dem Hinzufügen der Charakterisierungsdaten, Diagramme und Grafiken.
Bestimmte Peripherien dieses kleinen SoC, beispielsweise der Timer für die Signalmessung, nehmen 50 Seiten Dokumentation in Anspruch. Das sind fast doppelt soviel Seiten, wie für die Beschreibung des eigentlichen PIC-Cores und seines gesamten Befehlssatzes benötigt werden.
Diese Probleme sind beim Raspberry Pi sogar noch größer, da mehrere Datenblätter berücksichtigt werden müssen. Jedes dieser Datenblatt beschreiben nur einen Teil der Hardwarekomponenten des SoC, etwa seine Peripherie, die GPU oder den Core. Allein der Core nimmt mehr als
750 Seiten in Anspruch.
Von niemandem wird erwartet, das alles zu lesen oder diese ganzen Informationen zu verarbeiten. Entwickler von Embedded-Systemen stehen unter großem Druck, ihre Anwendungen in immer kürzerer Zeit zu erstellen, um eine schnelle Markteinführung zu garantieren. Eine Lösung besteht darin, eine Anwendung mit einer stufenweisen Architektur zu partitionieren und standardisierte Peripherie-Bibliotheken zu verwenden, um die Hardwaredetails zu abstrahieren. Die verschiedenen Ebenen können so dargestellt werden, dass sie einen Stapel bilden. Die Anwendung oberhalb befinden sich auf einer Hardware-Abstraktionsschicht (HAL). Dieser Aufbau lässt sich weiter verfeinern, um die HAL und eine Middleware-Ebene, die sich um die Implementierung gemeinsamer Dienste und Funktionen kümmert, richtig zu identifizieren. Der Stack wird oft weiter verfeinert, indem man eine Treiberschicht und eine Board-Support-Schicht von der HAL trennt.
Diese Softwarearchitektur wird direkt aus der Computing-Welt abgeleitet und funktioniert gut, um allgemeine Fälle zu modellieren. Leider treten bei Embedded-Anwendungen zwei grundlegende Mängel auf:
Die stufenweise Architektur vereinfacht die inflationäre Dokumentation, solange der Fokus auf den Standardfunktionen der oberen Middleware-Schicht liegt. Am unteren Ende des Anwendungsspektrums, wo die Middleware-Schicht sehr dünn ausfällt, wenn sie überhaupt vorhanden ist, ist das Ergebnis hingegen meist eine Verschleierung. Ein Entwickler muss sich auf die HAL-Dokumentation in Form einer großen Programmierschnittstelle (API; Application Programming Interface) verlassen, aber niemals wirklich irgendwelche Gerätespezifikationen erlernen. Der Dokumentationsbedarf ist dabei ähnlich groß und kann sich über mehrere tausend Seiten erstrecken. Treten Probleme auf, wird der Entwickler im Stich gelassen oder gezwungen, tief in unbekanntes Territorium und große Code-Mengen vorzudringen.
Die HAL-Schicht bietet eine enorme Hilfe, um Standard-Middleware-
Dienste zu unterstützen. Aufgrund ihrer sehr starren Natur werden aber alle einzigartigen und differenzierenden Funktionen eines bestimmten Gerätes beseitigt. Diese Besonderheiten bieten oft einen technischen Vorteil in einer bestimmten Anwendung und sind meist der eigentliche Grund, wieso ein bestimmtes Modell ausgewählt wird.Am oberen Ende des Anwendungsspektrums, wo die Middleware-
Schicht sehr breit ist, fügt der Linux-OS-Kernel alleine Millionen Code-Zeilen zu diesem Problem hinzu. Dabei handelt es sich zwar um Open Source Code. Das ändert allerdings nichts für den durchschnittlichen Entwickler, der nicht tiefer in die Materie einsteigen möchte.
Lasst die Maschine tun, was sie am besten macht!
Raspberry-Pi-Entwickler werden letzt-
lich in der Lage sein, die Rechenleistung und die umfangreichen Ressourcen der kleinen Boards sinnvoll und gewinnbringend zu nutzen. Die Bequemlichkeit des Standard-Linux-Betriebssystems wird letzt-
lich die Komplexität und Weite der API mehr als kompensieren. Anders sieht es jedoch für die Entwickler der neuen kleinen SoCs aus: die Nutzer moderner MCUs. Für sie springen nur sehr wenige Vorteile heraus bei der Arbeit mit einem standardisierten HAL. Durch die Verwendung kommt es zu Leistungseinbußen und außerdem werden die einzigartigen Funktionen durch die gestapelte Softwarearchitektur abgeschwächt.
Ein Weg aus diesem Dilemma ist eine neue Generation von Software-Tools für die schnelle Entwicklung. Dabei handelt es sich um eine neue Klasse von Code-Generatoren oder -Konfiguratoren, die vor kurzem im Embedded-Markt erschienen sind. Trotz erheblicher und oft gerechtfertigter Skepsis erweisen sich diese Tools nicht nur als effektiv, sondern für jeden ernsthaften Entwickler von Embedded-Systemen als absolut notwendig.
Zu den wichtigen Merkmalen zählen:
Die vollständige Integration in populäre IDEs, die auf einen Projektkontext aufmerksam machen, etwa bei der Modell-Auswahl und dem Middleware-Bibliotheksbewusstsein.
Die Unterstützung für einzigartige und komplexe Peripherie, zum Beispiel können Timer für die Signalmessung (SMT) dem Benutzer auf einer Seite oder in einem Dialog visuell präsentiert werden, der nur eine Handvoll Scrolling-Listen, Kontrollkästchen und einige intuitive Optionen umfasst.
Die Verwendung einer Templating-Engine, wobei darauf zu achten ist, dass die Konfigurationsmöglichkeiten in einen kleinen Satz vollständig kundenspezifischer Funktionen umgesetzt werden. Es wird nur eine minimale API mit wenigen zu lernenden Funktionen erzeugt – mit konsistenten und intuitiven Namenskonventionen. Die kundenspezifische Funktionsanpassung garantiert, dass der Großteil der Hardwareabstraktion statisch zur Kompilierzeit durchgeführt wird. Das reduziert die Liste der benötigten Parameter, die an jede Funktion übergeben werden, und erhöht somit die Leistungsfähigkeit und die Code-Dichte.
Die Ausgabe setzt sich aus sehr kurzen Quelldateien zusammen, die vom Benutzer vollständig geprüft werden können. Sie bieten eine Lernmöglichkeit, lassen sich aber auch von Experten weiterhin per Hand optimieren. Moderne Code-Generatoren mischen ihren eigenen Code gerne mit User-Code, um die Integrität zu bewahren und wichtige fortschrittliche Hardware-Funktionen voll ausschöpfen zu können.
Grundsätzlich machen Code-Konfiguratoren und -Generatoren das, was Maschinen am besten tun. Gleichförmige und fehleranfällige Arbeiten zu übernehmen. Die sich wiederholende und fehleranfällige Phase der Hardware-Peripherie-Konfiguration, das Erstellen einer HAL, was oft zu vielen langwierigen Studien der Datenblätter führt, erübrigt sich durch sie. Sie verkürzen außerdem die dafür notwendige Zeit erheblich.
Nutzer können viel über spezifische Hardware-Peripherie-Funktionen von der gleichen Benutzeroberfläche lernen und beseitigen oder verkürzen dadurch den Aufwand, das Datenblatt zu konsultieren. Die Hardware-Abstraktionsschicht wird zu einem flexiblen Teil des Projektes und je nach Bedarf oft und schnell wiederhergestellt, um die Anwendungsleistung zu optimieren.
In zehn Code-Zeilen
Sobald die Peripherie-Konfiguration erledigt ist, besteht die Möglichkeit, sich sofort auf die Anwendung zu konzentrieren. Durch Code-Generatoren wird auch im Embedded-Bereich das klassische „Hello-World“-Bespiel zu einer erfrischenden Code-Übung mit zwei Zeilen.
Komplexität ist keine unvermeidliche Folge des technologischen Fortschritts. Moderne Code-Konfiguratoren und -Generatoren helfen Ingenieuren, Software-Entwicklungsprozesse zu erweitern, zu automatisieren. Dadurch erhalten sie wieder die Kontrolle über die schnell wachsende Anzahl von Funktionen und Optionen.