Zephyr ist skalierbar und eignet sich daher für eine Vielzahl von Controllern mit unterschiedlichen Ressourcenbeschränkungen. Die Skalierbarkeit wird durch eine modulare Architektur erreicht, die es den Entwicklern ermöglicht, nur die benötigten Komponenten einzubinden und so den Platzbedarf des Systems zu minimieren. Auf der Zephyr-Website wird behauptet, dass die Software auf Systemen mit nur 8 KB Speicher bis hin zu Systemen mit einem Gigabyte Speicher läuft.

Breite Hardware-Unterstützung

Zephyr unterstützt eine breite Palette von Architekturen, darunter ARM, x86, RISC-V, Xtensa, MIPS und andere, auch FPGAs mit Nios2- und MicroBlaze-Soft-Cores. Als dieser Artikel geschrieben wurde, listet Zephyr über 600 verwendbare Boards auf, darunter Arduino UNO R4 Minima, GIGA R1 WIFI und Portenta H7, mehrere Varianten des ESP32, beide Versionen des BBC micro:bit, den Raspberry Pi Pico (und sogar den Raspberry Pi 4B+), nRF51- und nRF52-Boards, die NXP MIMXRT1010-EVK und -Familie sowie die STM32-Nucleo und -Discovery-Familien. Und dies sind nur die bei Elektor üblichen Boards, aber es gibt noch viele weitere.

Neben den Prozessorplatinen unterstützt Zephyr auch viele Zusatzplatinen (so genannte Shields) und enthält Treiber für alle möglichen Schnittstellen und mehr als 150 Sensortypen.

Multitasking, Vernetzung und Energieverwaltung

Als Echtzeitbetriebssystem (RTOS) bietet Zephyr Funktionen wie präemptives Multitasking, Inter-Thread-Kommunikation und Unterstützung für Echtzeit-Timer. Das Betriebssystem verfügt außerdem über Netzwerktechnologien und -protokolle wie TCP/IP, Bluetooth und IEEE 802.15.4 (zum Beispiel Zigbee), MQTT, NFS und LoRaWAN. Zusammen mit den Netzwerkfähigkeiten eignen sich die in Zephyr integrierten Energieverwaltungsfunktionen für energieeffiziente IoT-Anwendungen und batteriebetriebene Geräte.

Eine Reihe von Bibliotheken und Middleware vereinfachen gängige Aufgaben wie Kommunikationsprotokolle, Dateisysteme und Gerätetreiber. Zephyr ist auch für Sicherheitszertifizierungen wie ISO 26262 ausgelegt und sich somit bereit für sicherheitskritische Anwendungen.

Inspiriert von Linux

Zephyr ist kein Linux, nutzt aber Konzepte, Techniken und Werkzeuge, die auch Linux verwendet. So wird beispielsweise Kconfig für die Konfiguration des Betriebssystems verwendet, und die Hardwareeigenschaften und -konfigurationen werden mithilfe der Device Tree Specification (DTS) beschrieben. Daher werden sich Linux-Entwickler schnell heimisch fühlen, wenn sie für Zephyr programmieren.

Abonnieren
Tag-Benachrichtigung zu Zephyr jetzt abonnieren!

Offene Quelle

Nicht zuletzt wird Zephyr unter der freizügigen Lizenz Apache-2.0 veröffentlicht, die sowohl die kommerzielle als auch die nicht-kommerzielle Nutzung erlaubt. Die Benutzergemeinschaft, zu der auch Sie beitreten können, bietet Unterstützung und Dokumentation.

Ausprobieren von Zephyr

Zephyr auszuprobieren steht schon seit mehreren Jahren auf meiner To-Do-Liste, aber meine ersten Erfahrungen damit waren nicht sehr ermutigend, so dass ich mich nicht näher damit beschäftigt habe. Damals war eines der Hauptprobleme (neben der nicht fehlerfreien Kompilierung), dass man einen Programmier-Pod zur Programmierung des Ziel-Controllers benötigte, was es für Entwickler weniger geeignet machte. Dank Arduino und seinem Bootloader haben wir uns daran gewöhnt, dass wir keine speziellen Programmiertools brauchen, und so fühlte es sich wie ein Rückschritt an, eines zu benötigen.

Auswahl eines Boards

Die Dinge haben sich seitdem weiterentwickelt. Wie bereits erwähnt, unterstützt Zephyr heute über 600 Mikrocontroller-Boards, und die Chancen stehen gut, dass Sie bereits ein oder mehrere kompatible Boards besitzen. Bei der Durchsicht der Liste habe ich festgestellt, dass ich mehr als ein Dutzend verschiedener unterstützter Boards zur Verfügung habe!

Lang lebe der BBC micro:bit!

Ich habe die meisten von ihnen ausprobiert und mich schließlich für das BBC micro:bit, um damit zu experimentieren (Bild 1 von Zephyr als bbc_microbit oder bbc_microbit_v2 bezeichnet, je nach Version des Boards). Im Vergleich zu meinen anderen Boards ist das BBC micro:bit nicht nur leicht erhältlich, sondern hat auch die beste Zephyr-Unterstützung: Alle Peripheriegeräte sind zugänglich und werden durch einige Beispiele unterstützt, und das Beste von allem ist, dass es ohne zusätzliche Tools programmiert und debugged werden kann.
 

Bild 1. Das winzige Board BBC micro:bit ist ein hervorragendes Ziel, um das Zephyr-RTOS auszuprobieren.
Wer hätte gedacht, dass dieses kleine Board, das 10-Jährigen das Programmieren mit MakeCode, einer Scratch-ähnlichen
grafischen Sprache, beibringen sollte, auch ein hervorragendes Werkzeug für erfahrene Entwickler von eingebetteter
Software sein würde, die ein industrietaugliches Echtzeitbetriebssystem erlernen möchten?

Der beliebte ESP-WROOM-32 (von Zephyr als esp32_devkitc_wroom bezeichnet) ist ebenfalls ein geeigneter Kandidat, aber zum Debuggen ist ein externes Tool erforderlich. Das Board Arduino GIGA R1 WIFI ist eine weitere gute Option, aber sein Bootloader wird bei der Verwendung von Zephyr abgeschossen. Sie können ihn natürlich wiederherstellen, aber das ist dennoch eine missliche Begleiterscheinung. Offiziell benötigt das Arduino UNO R4 Minima einen SWD-fähigen Programmier-Pod (wie viele andere Boards, einschließlich des Raspberry Pi Pico), aber ich habe mit dfu-util einen Weg gefunden, dies zu umgehen (siehe unten). Wie beim GIGA R1 wird allerdings der Arduino-Bootloader von Zephyr zertrampelt.

Abonnieren
Tag-Benachrichtigung zu BBC micro:bit jetzt abonnieren!

Verwenden Sie einen Emulator

Falls Sie kein passendes Board haben, aber Zephyr unbedingt ausprobieren wollen, sollten Sie wissen, dass der Emulator QEMU (nur unter Linux/macOS) Zephyr unterstützt. So können Sie Anwendungen virtuell ausführen und testen. Renode von Antmicro soll zu ähnlichen Leistungen fähig sein; ausprobiert habe ich es nicht.

Installation von Zephyr

Ich habe Zephyr auf einem Computer mit Windows 11 installiert — Linux oder macOS habe ich nicht ausprobiert. Eine detaillierte und funktionierende Installationsanleitung ist online verfügbar . Die einzelnen Schritte sind klar beschrieben und bedürfen keiner weiteren Erläuterung. Ich habe, wie vorgeschlagen, eine virtuelle Python-Umgebung verwendet. Das bedeutet, dass Sie sich den Befehl zum Aktivieren der virtuellen Umgebung irgendwo im PC notieren müssen, da Sie ihn jedes Mal benötigen, wenn Sie mit der Arbeit beginnen wollen. Wenn Sie die PowerShell von Windows verwenden, sollten Sie das Skript activate.ps1 starten; in der Eingabeaufforderung ist es die Batch-Datei activate.bat. Windows PowerShell ist besser in der Lage, Compiler- und Linker-Ausgaben zu verarbeiten (Bild 2).
 

Bild 2. Das Zephyr-Build-Tool west ist für die Ausführung in einem Terminal vorgesehen. cmd.exe (Windows) kann
zwar verwendet werden, ist aber kein Terminal. Die Windows-PowerShell a.k.a. Terminal ist daher besser geeignet.

Zephyr besteht aus zwei Teilen, dem Betriebssystem selbst und einem SDK, das eine Sammlung von zum Zeitpunkt der Erstellung dieses Artikels 21 MCU-Toolchains enthält. Das Betriebssystem und das SDK müssen nicht an der gleichen Stelle installiert werden. Insgesamt verbrauchte es bei mir etwa 12 GB kostbaren Festplattenspeicherplatz. Um etwas Platz zu gewinnen, können Sie nicht benötigte Tool-Chains löschen.

Testen Sie nach der Installation, ob es funktioniert, indem Sie ein Beispiel erstellen und es mit dem folgenden Befehl auf das Board hochladen. Ersetzen Sie <my_board> durch den Namen Ihres Boards, zum Beispiel arduino_uno_r4_minima:

west build -p always -b <my_board> samples/basic/blinky

 

Wenn Sie den Pfad zum Beispiel nicht ändern wollen, müssen Sie diesen Befehl innerhalb des Ordners ausführen (wobei (.venv) anzeigt, dass Sie sich in einer virtuellen Umgebung befinden):

 

(.venv) <mein_pfad_zu_zephyr>\zephyrproject\zephyr

 

Wenn das Beispiel ohne Fehler gebaut wurde, können Sie es mit

 

west flash

 

hochladen, so dass die Standard-LED des Boards mit einer Frequenz von 0,5 Hz zu blinken beginnt.

Wie bereits erwähnt, kann zum Flashen ein externes Programmiertool erforderlich sein, zum Beispiel ein J-Link-Adapter oder ein anderes JTAG- oder SWD-fähiges Programmiergerät (Bild 3), und die Treibersoftware dafür muss zugänglich sein (im Suchpfad oder %PATH% unter Windows). Ist dies nicht der Fall, werden Sie durch eine (meist lange und kryptische) Meldung darüber informiert.
 

Bild 3. In vielen (aber nicht allen) Situationen ist ein JTAG- oder SWD
-Programmier/Debug-Pod für die Zephyr-Experimente erforderlich.

Auf dem BBC micro:bit V2 musste ich beim ersten Mal die HEX-Datei manuell mit dem Standardprogrammierverfahren für micro:bit auf das Board kopieren, danach funktionierte der Flash-Befehl aber einwandfrei. Die ausführbare Datei zephyr.hex befindet sich in zephyrproject\zephyr\build\zephyr\.

Der Standard-Flash-Befehl für die Arduino-Boards UNO R4 Minima und GIGA R1 WIFI erfordert, dass sich das Programmierprogramm dfu-util im Suchpfad des Betriebssystems befindet (bevor Sie die virtuelle Umgebung aktivieren, falls Sie eine verwenden). Dieses Dienstprogramm ist in der Arduino-IDE enthalten. Wo genau es sich auf Ihrem Computer befindet, müssen Sie jedoch selbst herausfinden (standardmäßig in %HOMEPATH%\AppData\Local\Arduino15\packages\arduino\tools\dfu-util\<Ihre aktuelle Arduino-IDE-Version>). Das Board muss außerdem in den DFU-Modus versetzt werden. Dies geschieht durch zweimaliges Drücken des Reset-Knopfes. Wenn die LED anfängt zu „pulsieren“, können Sie das Programm flashen.

Blinky-Kompatibilität

Sie haben vielleicht bemerkt, dass ich für das Blinky-Beispiel den Arduino UNO R4 Minima als Board vorgeschlagen habe und nicht den BBC micro:bit, von dem ich erst ja so begeistert war. Der Grund dafür ist, dass das Board zwar 25 LEDs besitzt (die Einschaltanzeige nicht mitgerechnet), aber keine LED, die mit dem Blinky-Beispiel kompatibel ist. Der ESP Wroom 32 hat auch keine, aber der R4 Minima schon.

Das GIGA R1 ist ebenfalls Blinky-kompatibel. Die MCU auf diesem Board hat zwei Kerne (Cortex-M7 und -M4) und Zephyr lässt die Wahl offen, welchen Sie verwenden, indem Sie entweder arduino_giga_r1_m4 oder arduino_giga_r1_m7 für den Build-Befehl auswählen. Sie können zeigen, dass die Kerne tatsächlich unabhängig sind, indem Sie das Blinky-Beispiel zweimal flashen, einmal für den -M4 und einmal für den -M7. Die GIGA hat eine RGB-LED und Blinky verwendet unterschiedliche Farben für jeden Kern: blau für den M4 und rot für den M7. Um die beiden Blinkies deutlicher zu unterscheiden, können Sie die Blinkrate für einen der beiden ändern (ändern Sie in samples\basic\blinky\src\main.c den Wert von SLEEP_TIME_MS).

Hallo Welt!

Für Boards ohne Blinky-LED gibt es das Beispiel hello_world, das einen String auf der seriellen Schnittstelle ausgibt.

 

west build -p always -b <meine_Platine> samples/hello_world

west flash

 

Dieses Beispiel funktioniert sowohl mit dem BBC micro:bit als auch mit dem ESP-WROOM-32-Modul. Um den Ausgabestring zu sehen, öffnen Sie ein serielles Terminalprogramm auf Ihrem Computer. Die Datenrate ist normalerweise 115.200 Baud (115200,n,8,1). Möglicherweise müssen Sie das Board zuerst zurücksetzen, da die Meldung nur einmal ausgegeben wird und Sie sie vielleicht übersehen haben (Bild 4).
 

Bild 4. Das Hello-World-Beispiel ist nicht sehr umfangreich. Wenn Sie das serielle Terminal
zu spät öffnen, werden Sie diese Begrüßungsnachricht nicht sehen.

Auf dem R4 Minima und dem GIGA R1 ist der serielle Ausgang der Pin 1 und nicht, wie man naiverweise erwarten könnte, der USB-C-Port, wie es in der Arduino-IDE der Fall wäre. Das liegt daran, dass der USB-Port ein Peripheriegerät der MCU ist und kein separater Chip. Da Zephyr ein modulares und skalierbares Betriebssystem ist, muss die USB-Unterstützung - wie jedes andere Modul oder Peripheriegerät - explizit für das Projekt aktiviert werden, bevor es verwendet werden kann. Dies geschieht in den Konfigurationsdateien des Projekts. Mehr zu diesen Dateien später.

Bei Boards ohne eingebauten Seriell-zu-USB-Konverter müssen Sie die serielle Schnittstelle finden (normalerweise Port 0, falls die MCU mehr als einen hat) und sie über einen externen Seriell-zu-USB-Konverter mit Ihrem Computer verbinden.

Noch ein bisschen weiter

Wenn Sie es geschafft haben, sowohl das Blinky- als auch das hello_world-Beispiel auf Ihrem Board zum Laufen zu bringen, sind Sie in einer ziemlich guten Position, um eine funktionierende Anwendung auf dem Zephyr zu erstellen. Wenn aber nur eines der Beispiele funktioniert und Sie möchten, dass das andere auch funktioniert, sind die Dinge etwas komplizierter.

Ich habe das BBC micro:bit als mein bevorzugtes Board für Zephyr-Experimente ausgewählt, auch wenn es nicht mit dem Blinky-Beispiel kompatibel ist. Das ist aber kein wirkliches Problem, denn das Board wird mit einigen Beispielen geliefert (im Unterordner bbc_microbit des Ordners samples\boards\), von denen eines (Display) viel schöner ist als ein Blinky mit nur einer LED. Es gibt auch Beispiele für andere Boards, aber insgesamt doch nur für sehr wenige angesichts der Zahl von über 600 unterstützten Boards (nicht einmal 5 %). Außerdem betreffen die meisten dieser Beispiele fortgeschrittene oder obskure Anwendungsfälle.

Wenn Sie versuchen, Blinky für den BBC micro:bit (oder den ESP-WROOM-32 oder ein anderes inkompatibles Board) zu bauen, werden Sie eine unverständliche Fehlermeldung erhalten. Sie versucht Ihnen mitzuteilen, dass led0 eine unbekannte Entität ist. Dies ist die standardmäßige Blinky-LED (ein bisschen wie LED_BUILTIN auf Arduino). Da der micro:bit einen Erweiterungsport hat, an den man LEDs und anderes anschließen kann, wollen wir versuchen, einen dieser Portpins als led0 zu definieren.

Bevor wir dies tun, erstellen Sie eine Sicherungskopie des Ordners samples/basic/blinky oder eine Kopie mit einem neuen Namen und verwenden Sie diese stattdessen. Im Folgenden wird samples/basic/blinky verwendet.

Der Gerätebaum

Die Definition einer led0 führt uns zum Gerätebaum (device tree), der bereits kurz erwähnt wurde. Der Gerätebaum in einer oder mehreren Textdateien listet die auf einem Board oder in einem Controller verfügbare Peripherie und den Speicher auf. In Zephyr haben diese Dateien die Endung .dts oder .dtsi („i“ für include), und die Dateien können andere Dateien enthalten. .dtsi-Dateien für Prozessoren befinden sich im Ordner dts, .dts- und .dtsi-Dateien für Boards im Ordner boards. Beide Ordner sind nach Prozessorarchitektur geordnet.

Um DTS(I)-Dateien auf eine etwas komfortablere Weise zu betrachten, können Sie das DeviceTree-Plugin für Visual Studio Code verwenden. Dieses Plugin bietet Syntax-Highlighting und Codefaltung, wodurch die Dateien leichter zu lesen sind (DTS-Dateien verwenden eine Syntax im Stil der Sprache C). Bild 5 zeigt einen Auszug aus der .dtsi-Datei für den nRF51822, der das Herzstück des Boards BBC micro:bit V1 ist. Diese Datei ist in der DTS-Datei des Boards enthalten. Sie sollten beispielsweise beachten, dass der Status von uart0 auf disabled gesetzt ist. Dieser Status wird in der DTS-Datei des Boards überschrieben, wo er auf okay gesetzt wird, was bedeutet, dass er verwendet werden kann. Das gleiche gilt für gpio0 und i2c0.
 

Bild 5. Ein Auszug aus der Datei nrf51822.dtsi. Die Ansicht rechts zeigt, wie lang diese Datei ist.

I²C im Gerätebaum

Ein weiterer Schnipsel aus der .dts-Datei für den BBC micro:bit ist in Bild 6 zu sehen. Er zeigt den Gerätebaum für den I²C-Bus. micro:bit verfügt über einen oder zwei Sensoren, die an den Bus angeschlossen sind (je nach Boardvariante) und im Baum durch mma8653fc und lsm303agr repräsentiert werden (letzterer umfasst zwei Sensoren, weshalb er zweimal im Baum erscheint). Der erste hat den Status okay, während die beiden anderen disabled sind. Dies ist korrekt für meine Boardvariante, die zur allerersten micro:bit-V1-Generation zählt.
 

Bild 6. Dieser Abschnitt des Gerätebaums, entnommen aus der Datei bbc_microbit.dts, stellt den I²C-Bus
des BBC-micro.bit-Boards dar, nicht den seines Prozessors.

Wie das Snippet zeigt, ist dieser Sensor mit dem FXOS8700 und dem MMA8653FC kompatibel, seine Adresse auf dem I²C-Bus ist 0x1d, und es sind zwei int-Signale (Interrupt) deklariert, die mit GPIO-Pin 27 und GPIO-Pin 28 verbunden sind. Wenn Sie es ausprobieren möchten, steht Ihnen ein Demoprogramm zur Verfügung:

west build -p always -b bbc_microbit samples/sensor/fxos8700

west flash

 

Beachten Sie, dass dies nicht mit dem BBC micro:bit V2 funktioniert, da dieser einen anderen Sensor in seinem Gerätebaum hat. Die Ausgabe der Demo ist in Bild 7 zu sehen, aber wir schweifen ab.
 

Bild 7. Die Ausgabe der samples/sensor/fxos8700-Demo, die auf dem BBC◦micro.bit läuft.

Überlagern des Gerätebaums

Zurück zu unserer LED, led0. Im Gerätebaum des Boards wird led0 erwartungsgemäß nicht erwähnt, also müssen wir sie hinzufügen. Wir könnten sie direkt in die Gerätebaumdatei des Boards eintragen, aber das wäre nicht korrekt, da das Board keine led0 hat. Der richtige Weg, einen Gerätebaum zu erweitern, ist das Hinzufügen eines Overlays. Der Inhalt einer Overlay-Datei wird in den Gerätebaum eingefügt. Im Baum vorhandene Abschnitte werden erweitert (im Falle eines neuen Elements) oder überschrieben (falls das Element bereits im Baum vorhanden ist); neue Abschnitte werden hinzugefügt.

Overlays müssen innerhalb des Projektordners in einem Unterordner mit dem Namen boards abgelegt werden. Wenn dieser Unterordner vorhanden ist, sucht der Erstellungsprozess darin nach einer Datei mit dem Namen <my_board>.overlay. In meinem Fall lautet der Dateiname bbc_microbit.overlay (bbc_microbit_v2.overlay für V2-Anwender). Bild 8 zeigt den Inhalt der Datei.
 

Bild 8. Diese Gerätebaum-Overlay-Datei macht den BBC◦micro.bit◦V1 kompatibel mit dem Blinky-Beispiel.

Hinzufügen einer Blinky-LED

Zephyr hat ein spezielles Gerätebaum-Schlüsselwort für LEDs, nämlich leds, also erstellen wir einen Knoten (branch) dafür. Sie können ihn nennen, wie Sie wollen, aber bleiben Sie bei leds, wenn er einem bestehenden leds-Knoten überlagert werden soll. Dieser Knoten soll zum Root des Gerätebaums hinzugefügt werden; daher wird ihm ein Schrägstrich „/“ vorangestellt, da dies in der DT-Sprache Root bedeutet. Die nächste Zeile besagt, dass dieser Zweig mit dem in Zephyr integrierten gpio-leds-Treiber kompatibel ist. Die Schnittstelle für diesen Treiber finden Sie in zephyr\include\zephyr\drivers\led.h.

Child-Knoten

Als nächstes kommt eine Liste von LED-Child-Knoten. Da ich nur eine LED habe, gibt es nur den einen Kindknoten led_0, den ich mit led0 bezeichnet habe. Die Bezeichnung eines Knotens ist zwar optional, aber sie ermöglicht es, den Knoten an anderer Stelle im Baum zu referenzieren, was wir ein paar Zeilen weiter unten auch tun werden. Außerdem können sie von der Anwendung (dem Entwickler) verwendet werden, um Zugriff auf Knoten und deren Eigenschaften zu erhalten.

Ein untergeordneter Knoten muss die Eigenschaften des Geräts angeben. Im Falle einer LED ist nur der GPIO-Pin eine erforderliche Eigenschaft, aber die optionale Eigenschaft namens label kann hinzugefügt werden. Solche Labels können zur Dokumentation verwendet werden oder um für Menschen lesbare Informationen für die Anwendung bereitzustellen. Labels haben darüber hinaus keine andere Funktion.

Als GPIO-Pin habe ich 1 gewählt, was dem großen Loch/Pad 2 auf dem micro:bit-Erweiterungsanschluss entspricht. Wenn Sie einen BBC micro:bit V2 haben, verwenden Sie (anstelle von 1) 4 als GPIO-Pin.

Einen Alias erstellen

Der nächste Schritt ist notwendig, weil das Blinky-Beispiel ihn erwartet. Er besteht darin, den led0-Alias für unsere LED zu erstellen. Man könnte meinen, dass es ausreicht, den Kindknoten zu „belabeln“, aber das ist nicht der Fall, denn Blinky verwendet das Makro DT_ALIAS, um Zugriff auf den LED-Kindknoten zu erhalten. Daher müssen wir etwas bereitstellen, das dieses Makro verdauen kann, was in diesem Fall ein Alias ist. Es befindet sich innerhalb des Blocks aliases. Hätte Blinky stattdessen das Makro DT_NODELABEL verwendet, dann wäre ein Alias überflüssig gewesen, da DT_NODELABEL das Label des led0-Kinderknotens direkt übernimmt. Ich weiß, dass es etwas verwirrend ist, Labels und Aliase mit demselben Namen zu haben, aber es ist für meine Erklärung notwendig.

Zephyr-Makros

Auch wenn Makros in der C/C++-Programmierung verpönt sind, werden sie in Zephyr häufig eingesetzt. Makros wie DT_ALIAS und DT_NODELABEL ermöglichen es den Werkzeugen zur Anwendungs- und Projektkonfiguration, Informationen aus dem Gerätebaum zu extrahieren, und es gibt sie in Hülle und Fülle. Sie können die Beschreibungen der Makros im Zephyr-Handbuch im Kapitel „Devicetree API&ldquo; finden.

Interessant ist die Tatsache, dass viele (alle?) Zephyr-Makros erwarten, dass ihre Argumente klein geschrieben werden, wobei alle Zeichen, die keine Buchstaben (a bis z) oder Zahlen (0 bis 9) sind, durch Unterstriche ersetzt werden. Dies wird als „lowercase-and-underscores-kompatibel“ bezeichnet. Stellen Sie sich zum Beispiel vor, ich hätte den LED-Unterknoten von vorhin mit LED-0 statt led0 bezeichnet. Dann wäre das Argument für DT_NODELABEL led_0 gewesen, das heißt DT_NODELABEL(led_0), denn der Bindestrich ist weder ein Buchstabe noch eine Zahl, und Buchstaben müssen klein geschrieben werden. Mit anderen Worten: Für den Entwickler der Anwendung, der Makros für den Gerätebaum verwendet, ist der Unterstrich ein Platzhalter. So kann sich led_0 in der Anwendung entweder auf led_0, led-0, Led_0, LED-0 und ledé0 (und jede andere Variation, die Sie sich ausdenken können) im Gerätebaum beziehen. In diesem Sinne ist es sehr empfehlenswert, die Dokumentation der Zephyr-Makros sorgfältig zu lesen!

Abonnieren
Tag-Benachrichtigung zu RTOS jetzt abonnieren!

Beachten Sie, dass Fehler im Gerätebaum dadurch bestraft werden, dass der Compiler Sie mit „FATAL ERROR“ anschreit, ohne weitere Informationen zu liefern.
 

Makellose Builds

Wenn Sie mit dem Device-Tree oder Ihrer Anwendung herumspielen, werden Sie Ihr Projekt wahrscheinlich häufig neu erstellen (müssen). Um die Dinge etwas zu beschleunigen, entfernen Sie das –p always („p“ von pristine, makellos) aus dem Build-Befehl. Dadurch wird verhindert, dass alles von Grund auf neu erstellt wird. Wenn Sie hingegen viele verschiedene Beispiele nacheinander ausprobieren, sollten Sie den Befehl beibehalten, da er Ihnen eine lästige Fehlermeldung erspart, dass der Build-Ordner nicht für Ihr Projekt bestimmt ist.

Beachten Sie, dass der Flash-Befehl auch den letzten Build-Befehl auslöst, so dass es ausreicht, nur den Flash-Befehl jedes Mal auszuführen, wenn Sie etwas ändern.

Einen Device-Treiber verwenden

Das Blinky-Beispiel ruft die Funktion gpio_pin_toggle_dt() auf, um den Zustand der LED umzuschalten. Dies ist eine Funktion des GPIO-Treibers. Das ist natürlich völlig in Ordnung, aber Zephyr enthält auch eine Sammlung von LED-Treibern. Die Verwendung eines LED-Treibers macht das Programm nicht nur lesbarer, sondern auch flexibler und portabel, da ein LED-Treiber durch einen anderen ersetzt werden kann, ohne die Anwendung selbst zu ändern. Hier kommen die Skalierbarkeit und Modularität von Zephyr ins Spiel.

Kconfig hat eine GUI

Die Einbindung eines LED-Treibers in unser Programm erfordert einige Schritte. Zunächst muss das Projekt neu konfiguriert werden, damit es den Treiber enthält. Die Projektkonfiguration wird von Kconfig vorgenommen, dem Kernel-Konfigurations-System, das auch von Linux verwendet wird. Es gibt mehrere Möglichkeiten, mit diesem System zu interagieren, und eine davon ist eine grafische Benutzeroberfläche (GUI). In Zephyr öffnen Sie sie wie folgt:

 

west build -t guiconfig

 

Es dauert eine Weile, bis sich die grafische Benutzeroberfläche öffnet, aber dann sieht sie aus wie in Bild 9. Die Oberfläche zeigt eine Menge Informationen über das in Entwicklung befindliche Projekt. Beachten Sie den Project-under-development-Teil. Damit Kconfig tatsächlich an Ihrem Projekt arbeitet, führen Sie einen ursprünglichen Build (mit dem Flag –p always) Ihres Projekts durch, bevor Sie das Kconfig-GUI starten.
 

Bild 9. Die Benutzeroberfläche des Kconfig-Projektkonfigurationswerkzeugs. Es gibt viele Optionen.

So viele Optionen...

Nehmen Sie sich etwas Zeit, um den Konfigurationsbaum zu erkunden. Klappen Sie Zweige auf, indem Sie auf die +-Symbole klicken. Erforschen Sie die Optionen, indem Sie darauf klicken. Dadurch werden einige Informationen im unteren Bereich angezeigt. Beachten Sie, dass die Fließkomma-Unterstützung für printf() eine Konfigurationsoption ist, ebenso wie die Unterstützung der Sprache C++. In ähnlicher Weise finden Sie unter Build and Link Features Optionen zur Optimierung des Compilers.

Es gibt eine Unmenge von Konfigurationsoptionen. Die für uns interessanten befinden sich im Zweig Device Drivers. Klappen Sie ihn auf und scrollen Sie nach unten, während Sie sich alle verfügbaren Optionen ansehen. Der LED-Treiber befindet sich etwa auf halber Strecke nach unten: Light-Emitting Diode (LED) Drivers. Aktivieren Sie das Kontrollkästchen und belassen Sie die Optionen im Unterzweig auf ihren Standardwerten (Bild 10). Klicken Sie auf den Save-Knopf und beachten Sie den Pfad der Konfigurationsdatei, der am unteren Rand des Fensters angegeben ist. Es ist sehr aufschlussreich, diese Datei zu inspizieren, um zu sehen, was sie enthält (eine Menge). Schließen Sie das GUI.
 

Bild 10. Wählen Sie Light-Emitting Diode (LED) Drivers und lassen Sie die Child-Werte unverändert.

Von nun an sollten Sie in den Build-Befehlen nicht mehr das Flag –p always angeben, da es die oben vorgenommenen Änderungen rückgängig machen würde. Ich werde Ihnen gleich zeigen, wie man die Konfigurationsänderung dauerhaft macht.

Blinky mit LED-Treiber

Jetzt können wir das neue Blinky-Programm schreiben - siehe Bild 11. Es beginnt mit der Einbindung der Header-Dateien für das Gerät und den LED-Treiber. Dann verwenden wir in der Main-Datei das Makro DEVICE_DT_GET_ANY, um eine Gerätereferenz für die LED aus dem Gerätebaum zu erhalten. Beachten Sie, dass das Argument gpio_leds des Makros kleingeschrieben und unterstrichkompatibel ist, so dass es mit dem gpio-leds-Wert der Eigenschaft compatible des leds-Knotens im Gerätebaum übereinstimmt (wie oben erklärt). Wenn es kein Gerät findet, weil Sie sich vertippt haben, gibt das neue Blinky die Meldung „No device with compatible gpio-leds found“ aus. Diese Meldung wird auch ausgelöst, wenn die Eigenschaft status eines Geräts auf disabled gesetzt wird.
 

Bild 11. Das Blinky-Programm wurde für einen LED-Treiber umgeschrieben. Beachten Sie, dass es keinen board-spezifischen Code gibt. Dieses Programm sollte auf jedem Board laufen, das einen Treiber gpio_leds (oder gpio-leds) in seinem Gerätebaum hat.

Die Verwendung des Wortes „compatible“ als Substantiv in Zephyr ist etwas gewöhnungsbedürftig. Daher bedeutet die obige Fehlermeldung nicht, dass es kein kompatibles Gerät gibt, sondern dass es kein Gerät gibt, das eine Eigenschaft mit dem Namen compatible mit dem Wert gpio-leds hat (und auch nicht mit gpio_leds, denken Sie daran, dass der Unterstrich jedes Zeichen außer a...z und 0...9 ersetzt).

Eine zweite Prüfung stellt fest, ob das Gerät ordnungsgemäß initialisiert wurde. Wenn dies der Fall ist, fahren wir fort.

In der Endlosschleife while() wird die LED mit den vom Treiber bereitgestellten Befehlen led_on und led_off ein- und ausgeschaltet. Das Argument 0 steht für das erste (und einzige) Gerät, das vom Makro DEVICE_DT_GET_ANY gefunden wurde, nämlich led0.

Rückgabewerte prüfen

Da wir einen Gerätetreiber verwenden, anstatt einen GPIO-Pin direkt auf der Ebene der Hardware-Register umzuschalten, ist es eine gute Praxis, die Rückgabewerte aller Funktionsaufrufe des Treibers zu überprüfen, da sie aus irgendeinem Grund fehlschlagen können. Ein Treiber muss bestimmte Funktionen und Rückrufe bereitstellen, kann aber auch optionale Eigenschaften haben. Ein LED-Treiber muss zum Beispiel led_on und led_off implementieren, led_blink ist jedoch optional. Wenn Sie led_blink in unserem Projekt aufrufen, wird nichts passieren, weil es nicht implementiert ist. Es existiert zwar, aber es ist leer. Der Rückgabewert zeigt Ihnen das an. Im Allgemeinen ist es eine gute Programmierpraxis, den Rückgabewert jedes Funktionsaufrufs zu überprüfen.

Führen Sie ein Build des Programms durch und laden Sie es hoch (beachten Sie, dass das Flag –p always nicht vorhanden ist).

 

west build -b bbc_microbit samples/basic/blinky

west flash

 

Konfigurieren des Projekts

Wenn die LED mit 0,5 Hz zu blinken beginnt, haben wir ein funktionierendes Programm. Damit das so bleibt, müssen wir die aktuelle Konfiguration dauerhaft machen. Dazu öffnen wir die Datei prj.conf im Ordner unseres Blinky und fügen die folgende Zeile ein (im Gegensatz zu Gerätebaumdateien, die eine C-Syntax verwenden, verwenden Kconfig-Konfigurationsdateien eine Python-Syntax):

 

CONFIG_LED=y

 

Um zu überprüfen, ob es funktioniert, führen Sie einen Pristine-Build Ihres Projekts aus und laden Sie die ausführbare Datei auf das Board.

Debugging

Wenn Ihr Board es zulässt (wie das BBC micro:bit) oder Sie ein geeignetes Debugging-Tool haben, können Sie die Anwendung debuggen mit

 

west debug

 

Dadurch wird ein gdb-Server gestartet und ein Terminal geöffnet (Bild 12). Konsultieren Sie das Internet, um zu lernen, wie man mit gdb arbeitet, da dies den Rahmen dieses Artikels sprengen würde.
 

Bild 12. Das Board BBC◦micro.bit unterstützt das Debuggen mit gdb von Haus aus, so dass keine zusätzlichen Tools benötigt werden.

Zephyr vs. Arduino

Nachdem Sie nun gesehen haben, wie man mit dem Zephyr-Betriebssystem arbeitet, fragen Sie sich vielleicht, warum Sie es verwenden sollten. Ist es nicht viel einfacher, Arduino zu verwenden? Wie Zephyr unterstützt auch Arduino mehrere Prozessorarchitekturen und Hunderte von Platinen. Tausende von Treibern und Bibliotheken sind für Arduino geschrieben worden. Wenn eine Anwendung oder Bibliothek die Arduino-Core-API verwendet, kann sie, genau wie eine Zephyr-Anwendung, leicht auf jede andere unterstützte Plattform mit ähnlicher Peripherie portiert werden. Beides ist Open Source. Und nun?

Nun, Zephyr ist als industrietaugliches, robustes RTOS mit Funktionen wie Aufgabenplanung, Speicherverwaltung und Gerätetreiber gedacht. Darüber hinaus ist Zephyr für ein breites Spektrum an Schwierigkeitsgrade der Projekte ausgelegt, von kleinen IoT-Geräten bis hin zu komplexen eingebetteten Systemen. Es bietet mehr Flexibilität, erfordert aber ein tieferes Verständnis der Embedded-Entwicklung.

Arduino bietet zwar einige Echtzeitfähigkeiten, ist aber kein RTOS, sondern ein Framework für Single-Thread-Anwendungen mit dem Schwerpunkt auf Einfachheit und Benutzerfreundlichkeit. Arduino abstrahiert viele Low-Level-Details, wodurch es auch für Anfänger zugänglich ist. Für komplexere Anwendungen kann es jedoch zusätzlich zu einem RTOS wie Mbed OS verwendet werden. Es wird daran gearbeitet, die Arduino-Core-API auf Zephyr lauffähig zu machen .

Es liegt an Ihnen zu entscheiden, ob Sie Zephyr für Ihr nächstes Projekt benötigen oder nicht. Sie könnten es zumindest ausprobieren, denn dies macht sich gut im Lebenslauf eines jeden Embedded-Entwicklers.

Weitere Lektüre

Das war's für den Moment. Wie Sie gesehen haben, ist das Zephyr-Betriebssystem kompliziert und hat eine etwas steile Lernkurve. In diesem Artikel habe ich versucht, den Aufstieg ein wenig zu erleichtern. Es gibt jedoch noch viel, viel mehr über Zephyr zu sagen und zu lernen, also denken Sie nicht, dass Sie jetzt schon alles wissen. Die Verweise und enthalten Links zu zwei Themen, die Sie interessieren könnten.
 


Haben Sie Fragen oder Kommentare?

Haben Sie technische Fragen oder Kommentare zu diesem Artikel? Schicken Sie eine E-Mail an den Autor unter clemens.valens@elektor.com oder kontaktieren Sie das Elektor-Team unter redaktion@elektor.de.


Dieser Artikel (230713-01) erscheint in Elektor März/April 2024.

Abonnieren
Tag-Benachrichtigung zu Embedded & AI jetzt abonnieren!