Embedded-Rust

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Diese Seite ist aktuell noch im Aufbau

Warum Rust?

C/C++ ist im Embedded-Bereich noch mehr als sonst der Quasi-Standard für Software aller Art. Versuche, modernere Programmiersprachen auf Mikrocontrollern zum laufen zu bringen sind bisher nur im Arduino-Umfeld bekannt geworden (siehe MicroPython) und mit erheblichen Performanceeinbußen verbunden (je nach Quelle 10-100x). Rust hingegen ist mit einer Performance die je nach Programmierstil zwischen C++ und besser als C (Ja, wirklich!) liegt deutlich konkurrenzfähig. Gleichzeitig bringt Rust eine moderne Syntax, ein hohes Abstraktionsniveau und sehr gute Tool-Integration mit.

Vorteile

  • Bei Rust immer zitiert: Speichersicherheit. In Rust stellt der sogenannte Borrow-Checker als Teil des Typsystems sicher, dass jedes Stück Daten genau einen Besitzer (Owner) und entweder mehrere lesbare Referenzen (borrow) oder eine einzige schreibbare Referenz (mutable borrow) hat. Außerdem stellt er sicher, dass nur valide Referenzen (in C Pointer) verwendet werden können. Sobald eine Variable ihren Gültigkeitsbereich verlässt wird (auch dynamisch allokierter) Speicher automatisch freigegeben. Letztendlich besitzt jedes (safe) Rust-Programm ohne Compilerfehler nachweißlich keine Speicherfehler.
  • Borrow-Checker für Peripherals: Eine der genialsten Funktionen im Embedded-Bereich. Die Garantien für Speichersicherheit werden von den HALs auch für ganze Peripherals erzwungen. Auf diese Art ist es unmöglich einen Pin aus Versehen doppelt oder ohne vorherige Konfiguration zu benutzen. Was bei IOs noch einigermaßen von Hand zu lösen wäre, ist bei DMA-Channels eine echte Erleichterung.
  • Durch das genauere Typsystem können vom Compiler einige Optimierungen durchgeführt werden, die in C nicht möglich wären (v.a. in Zusammenhang mit readonly Pointern)
  • Fokussierung auf den Stack: In Rust läuft fast alles über den (schnellen und deterministischen) Stack. Sehr viele Embedded-Rust Programme (auch mit Netzwerk/BT/USB) haben überhaupt keinen Heap konfiguriert. Es spricht allerdings auch nichts dagegen einen zu verwenden
  • Asynchrone Programmierung: Natürlich kann man auch in Rust blockierende Funktionen programmieren, aber async/await ermöglicht mit wenig Aufwand extrem effiziente Nebenläufigkeit ohne ein RTOS.
  • Debugging: Neben dem Debugging mittels Breakpoints führt häufig auch ein einfaches println zu schnellem Erfolg. Allerdings sind Stringoperationen nicht gerade effizient. Für Rust-Nutzer gibt es da defmt. Dabei werden nur eine ID für jedes Log-Statement und die Formatierungsargumente in einen Ringpuffer geschrieben und vom Debugger ausgelesen. Die eigentliche - beliebig aufwendige - Formatierung erfolgt auf dem Hostrechner

Hardwareunterstützung

  • STM32: sehr gut, nicht nur für einzelne Chips
  • RP2040/RP235x: sehr gut, aufgrund der großen Popularität des Raspberry Pico
  • Nordic Semiconductor nrf: sehr gut, der Artikelautor hat aber keinerlei Erfahrungen mit nrf
  • AVR: vom Compiler unterstützt, das HAL-Projekt ist aber etwas (sehr) eingeschlafen
  • ESP32: sehr gut, wird von Espressif selbst unterstützt, wenn auch etwas gespalten zwischen pure-Rust(no_std)/ESP-IDF(std) und Xtensa/Risc-V

Installation

Beispiel für ein einfaches Programm