en / de
Expertisen
Methoden
Dienstleistungen
Referenzen
Jobs & Karriere
Firma
Technologie-Trends TechCast WebCast TechBlog News Events Academy

Embedded Rust: Einrichten vom Raspberry Pi Pico zur Entwicklung mit Rust

Rust ist eine momentan heiss diskutierte Programmiersprache. Sie findet under anderem auch in der Embedded-Welt Anklang, denn Rust kann Performance-Mässig in etwa mit C/C++ mithalten. Nur ist sie zusätzlich modern und so konzipiert, dass Entwickler gezwungen sind, Code so sicher wie möglich zu schreiben. Beispielsweise sind unerlaubte Speicherzugriffe wesentlich unwahrscheinlicher, als bei C/C++. Nun, wenn Rust auch auf Embedded Geräten eingesetzt werden kann, wäre es da nicht schön, Rust auf einem Mikrocontroller auszuprobieren?

Ich zeige euch in diesem Artikel, wie man mit etwa 15 CHF Budget bei einem regnerischen Sonntagnachmittag Rust vollständig auf einem Mikrocontroller zum Laufen bringt, inklusive Debugging. Wenn alles einwandfrei läuft, ist mit einem Zeitaufwand von ca. 2 -3 Stunden zu rechnen. Wir verwenden dafür das Raspberry Pi Pico, das erste Mikrocontrollerboard von der Raspberry Pi Foundation, mit hauseigenem Controller, dem RP2040.

Hardware und Tools

Zuerst installieren wir VSCode und Rust, danach kann man schon bald Programme übersetzten und flashen. In dem zweiten Teil wenden wir uns dem Debugging zu.

Rust mit dem Pico ohne Debugging

In diesem Teil schauen wir uns an, wie man Anwendungen übersetzt und dann auf das Target lädt. Dazu benötigen wir nur ein Raspberry Pi Pico.

Als erstes wird Visual Studio Code installiert, der Installer kann von https://code.visualstudio.com/ heruntergeladen werden.

Jetzt installieren wir Rust, hierzu gibt es ebenfalls einen Installer, der Rust Webseite zum Download bereit steht: https://www.rust-lang.org/learn/get-started

Sobald Rust erfolgreich installiert ist, können wir den Target Support für den RP2040 (ARM Cortex-M0+) installieren. Vorher bringen wir aber den Installer «rustup» noch kurz auf den neusten Stand:

rustup self update
rustup update stable
rustup target add thumbv6m-none-eabi

Der RP2040 erwartet ein Binary in der Form einer .uf2-Datei. Dazu gibt es ein Package, das .elf Dateien in .uf2 Dateien übersetzt. Cargo ist der Paketmanager von Rust und wird verwendet, um genau solche Pakete zu installieren. Flip Link wird verwendet, um Stack Overflows zu verhindern und wird vom Beispielprojekt gebraucht. Wir installieren die Packete:

cargo install elf2uf2-rs --locked
cargo install flip-link

(Bei mir funktioniert seit kurzer Zeit die Installation von elf2uf2-rs nicht, da offensichtlich ein Dependency-Error vorliegt. Das heisst, dass wir momentan nicht direkt Anwendungen übersetzten und flashen können. Aber keine Panik, sobald wir das Debugging eingerichtet haben, können wir das Target über die Debug-Probe flashen.)

Den Hardware Abstraction Layer für den Rp2040 wird mit Cargo installiert. Der Quellcode dazu kann unter https://github.com/rp-rs/rp-hal angesehen werden. Vom gleichen GitHub Account gibt es ein Beispielprojekt, bzw. ein Projekttemplate: https://github.com/rp-rs/rp2040-project-template.

Dieses laden wir jetzt herunter und öffnen es in VSCode. In VSCode starten wir dann ein Terminal und wir können die Anwendung, die sich in src/main.rs findet, mit «cargo build» übersetzten.

Nach dem Übersetzten führt man die Anwendung Rust-typisch mit «cargo run» aus. In der Datei .cargo/config.toml können wir verschiedene Runner auswählen. Runner sind Kommandos, die beim Ausführen von «cargo run» ausgeführt werden. Jetzt kann der Runner «elf2uf2-rs -d» ausgewählt werden.

# runner = "probe-run --chip RP2040"
runner = "elf2uf2-rs -d"
# runner = "probe-rs-cli run --chip RP2040 --protocol swd"

Was passiert jetzt? Mir «cargo build» haben wir die Anwendung übersetzt und jetzt können wir mit «cargo run» verschiedene Post Build Steps ausführen. In unserem Fall wollen wir die übersetzte Datei in eine .uf2 Datei umwandeln, die wir dann direkt auf das Target flashen wollen. Dazu wird ein Pico mit gedrücktem Bootselect-Button über USB an den Host angeschlossen. Nach dem Einstecken kann der Button losgelassen werden. Im File Explorer taucht das Pico nun als externen Datenträger, also wie ein USB Stick, auf und erwartet jetzt ein .uf2 Binary zum Ausführen.

Das Raspberry Pi Pico meldet sich als Datenträger an.

Wenn wir beispielsweise ein .uf2 File haben, kann dieses (per Drag und Drop) auf das Pico kopiert werden, danach meldet sich das Pico ab und die Anwendung läuft auf dem Target. Beim Ausführen von «cargo run» wird das .uf2 File erstellt und direkt auf das Pico geladen, sofern dies mit gedrücktem Button eingesteckt wurde. Das manuelle Kopieren der Datei wird uns also abgenommen. Nach «cargo run» sollte sich das Pico vom Dateisystem abmelden und nun sollte die LED blinken. Sehr gut, die erste Rust Anwendung läuft jetzt auf unserem Mikrocontroller!

Jetzt können wir bereits Applikationen übersetzten und flashen. Weiter geht es mit dem Einrichten des Debug-Supports.

Debugging mit RP2040

In diesem Teil verwenden wir nun zwei Raspberry Pi Picos. Eines ist das Target, also auf dem soll die Anwendung laufen und eines dient als Debug-Probe. Dieser Teil befasst sich hauptsächlich damit, die Debug-Probe einzurichten.

Bis Debugging möglich ist, benötigen wir nochmals einige Tools und müssen auch das Template Projekt noch ein wenig anpassen. Für die Installation der folgenden Tools und Applikationen habe ich diese Orderstruktur gewählt:

Vorgeschlagene Orderstruktur für die Tools, die wir für das Aufsetzten des Picos für Rust benötigen.

Dies muss man natürlich nicht so machen. Ich habe mir den Order aber in C: angelegt, um möglichst kurze Pfade zu haben und damit alles an dem gleichen Ort installiert ist. Im Folgenden werden die Installationen gemäss dieser Orderstruktur ausgeführt und erklärt.

OpenOCD

OpenOCD ist ein Tool, das vom Host mit der Debug-Probe kommuniziert. Dabei unterstützt es viele Debug-Protokolle und Target-Mikrocontroller, jedoch leider (noch) nicht den RP2040. Deshalb gibt es eine Version von Raspberry Pi, die mit dem RP2040 kompatibel ist.

Diese Version müssen wir jedoch selbst kompilieren, und dafür verwenden wir «Git for Windows SDK». Dieses muss installiert werden und danach werden noch zusätzliche Packages benötigt, die sich ebenfalls über Git for Windows SDK herunterladen lassen.

Git for Windows SDK laden wir von dieser Webseite https://gitforwindows.org/#download-sdk heruntergeladen und installieren es. Wir wählen die neuste Version aus und installieren sie mit allen Standardeinstellungen.

Um verschiedene Packages zu installieren, führen wir nun C:/git-sdk-64/git-bash.exe aus. Es ist darauf zu achten, dass exakt dieses Programm ausgeführt wird. Wichtig ist, dass wir das Terminal erst schliessen, nachdem alles installiert ist. Folgende Befehle führen wir nun nacheinander aus:

pacman -Syu
pacman -Su
pacman -S mingw-w64-x86_64-toolchain git make libtool pkg-config autoconf automake texinfo wget

Danach sind alle Meldungen, die ausgegeben werden, entweder mit Enter oder Y + Enter zu bestätigen. Nun benötigen wir noch weitere Packages, dazu sind folgende Kommandos auszuführen und wiederum mit Y + Enter zu bestätigen:

cd ~/Downloads
wget http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-libusb-1.0.26-1-any.pkg.tar.zst
pacman -U mingw-w64-x86_64-libusb-1.0.26-1-any.pkg.tar.zst

Es kann vorkommen, dass in einiger Zeit diese Version veraltet ist und gar nicht mehr zum Download angeboten wird. Dann kann unter der Webseite http://repo.msys2.org/mingw/x86_64/ nach dem neusten Release gesucht und dann diese Version installiert werden.

Wenn dies alles eingerichtet ist, kann von GitHub die OpenOCD Version für den RP2040- Mikrocontroller nach C:/rp2040_tools (oder an einen sonst beliebigen Ort) heruntergeladen und übersetzt werden. Dazu werden im gleichen Terminal wie vorher die untenstehenden Kommandos eingegeben:

cd /c/rp2040_tools
git clone https://github.com/raspberrypi/openocd.git --branch picoprobe --depth=1
cd openocd
./bootstrap
./configure --enable-picoprobe --disable-werror
make

Jetzt wird OpenOCD übersetzt, was ca. 30 Minuten dauern sollte, ein perfekter Zeitpunkt für eine Kaffeepause.

Sobald der Vorgang abgeschlossen ist, muss man die vorher heruntergeladene libusb-1.0.dll in das gleiche Verzeichnis wie die openocd.exe kopieren. Dies erreichen wir durch den folgenden Befehl:

cp /c/git-sdk-64/mingw64/bin/libusb-1.0.dll src/libusb-1.0.dll

Nun ist OpenOCD als ausführbare Datei vorhanden und Git for Windows SDK kann man durch löschen des Orderns C:/git-sdk-64 wieder entfernen. Ich würde empfehlen, dass man den Pfad zu OpenOCD zu den Systemvariablen hinzufügt. Dies ist aber kein Muss, wenn z.B. bereits andere Versionen von OpenOCD auf dem Rechner installiert sind und man selber entscheiden möchte, welche man denn gerade verwenden will. Die Systemvariable ergänzt man folgendermassen:

  1. In der Windows-Suchleiste «env» eingeben
  2. Umgebungsvariablen…
  3. Bei «Benutzervariablen» die Variable «Path» anwählen und dann bearbeiten
  4. Neue Variable erstellen: «C:\rp2040_tools\openocd\src»
  5. Alles mit OK bestätigen

OpenOCD wird den Systemvariablen hinzugefügt.

Nun testen wir über eine Kommandozeile, ob das Betriebssystem OpenOCD findet. Dazu einfach den Befehl «openocd» ausführen und schauen, ob eine Meldung zur Version ausgegeben wird. Ansonsten ist ein Neustart des Computers angebracht.

Picoprobe

Picoprobe ist eine Firmware von Raspberry Pi, mit der ein Pico Debug-Befehle ausführen kann, es wird also in eine Debug-Probe umfunktioniert. Das Binary (picoprobe.uf2) kann von der GitHub Seite von Raspberry Pi heruntergeladen werden: https://github.com/raspberrypi/picoprobe/releases.

Wie zu sehen ist, ist dies bereits im Richtigen Dateiformat .uf2, um es auf ein Pico zu laden. Wir wissen ja jetzt, wie wir ein Pico mit einer .uf2 Datei flashen können, also machen wir das. Jetzt müssen wir uns natürlich merken, auf welchem von unseren Boards die Picoprobe Firmware läuft, dies ist unsere Debug-Probe.

Nun müssen wir noch den korrekten USB-Treiber der Debug-Probe installieren. Dazu benötigen wir die Applikation «Zadig», welche wir von https://zadig.akeo.ie/ herunterladen. Zadig ist eine Standalone-Applikation, ich habe diese dann ebenfalls in die angelegte Ordnerstruktur verschoben.

Nach dem Starten von Zadig wählen wir «Options» und dann «List All Devices». Das Pico, welches die Debug-Probe ist, muss über USB mit dem PC verbunden sein. Im Dropdown Menu sollten nun zwei Interfaces für Picoprobe erscheinen. Wir wählen das CMSIS-DAP Interface und ersetzen den aktuellen Treiber mit dem «libusb-win32». Dieser kann mit den beiden Pfeilen ausgewählt und mit «Replace Driver» bestätigt werden. Der neue Treiber erscheint dann in der linken Hälfte als «libusb0».

Zadig brauchen wir, um den richtigen Treiber für die Debug-Probe zu installieren.

GNU Toolchain

Wir laden nun die neuste Arm GNU Toolchain von https://developer.arm.com/downloads/-/gnu-rm herunter.

Die Installation erfolgt dann in den von uns angelegten Order, oder wiederum an einem anderen Ort. Wichtig ist auch, dass hier ebenfalls der Systempfad zur Toolchain ergänzt wird, dies kann bei der Installation beim letzten Schritt angewählt werden.

Installation der GNU Arm Embedded Toolchain.

Auch hier ist allenfalls ein Neustart erforderlich.

Konfiguration der Hardware und von VSCode

Nun haben wir alles, was wir benötigen. Jetzt müssen wir noch die Hardware, VSCode und das Projekt konfigurieren, damit wir mit der IDE debuggen können.

Hardware: Die Verbindungen zwischen der Probe und dem Target sind in folgendem Bild dargestellt:

Debugging des Raspberry Pi Picos mit einem zweiten Pico, das als Debug-Probe fungiert.

Quelle: https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf

Veröffentlicht von Raspberry Pi Ltd unter der «Creative Commons Attribution-NoDerivatives 4.0 International (CC BY-ND 4.0)» Lizenz.

Links im Bild sehen wir die Probe, rechts das Target. Die Spannungsversorgung kommt von dem USB-Kabel, das am anderen Ende am Host angeschlossen ist. Damit wird dann auch über die roten und schwarzen Kabel das Target gespeist. Über das blaue und violette Kabel läuft das Single-Wire-Debug Protokoll und über das orange und gelbe Kabel läuft eine UART-Verbindung zur Kommunikation der beiden Picos. Bei mir sieht dies so aus:

So könnte das Debuggen vom Raspberry Pi Pico in echt aussehen.

Projekt: Im Template Projekt die Datei .vscode/launch.json abändern, dass nur folgender Inhalt darin enthalten ist:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug rp2040-project-template",
            "request": "launch",
            "type": "cortex-debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/rp2040-project-template",
            "preLaunchTask": "Build binary",
            "servertype": "external",
            // This may need to be gdb-multiarch depending on your system (i.e. Linux vs Mac)
            "gdbPath": "arm-none-eabi-gdb",
            // Connect to an already running OpenOCD instance
            "gdbTarget": "localhost:3333",
            // If you have the Pico SDK installed, allows the
            // display of the RP2040 CPU registers in VS Code
            //"svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd",
            "runToMain": true,
            "preLaunchCommands": [
                "monitor init",
                "monitor reset init",
                "monitor halt",
            ],
            // Work around for stopping at main on restart
            "postRestartCommands": [
                "break main",
                "continue"
            ],
        }
    ]
}

Hier definieren wir Kommandos und Informationen für den Debugger, beispielsweise wo sich das Binary befindet und dass es vor dem Debuggen übersetzt werden soll.

Dann erstellen wir im .vscode Ordner ein neues File, das den Name «tasks.json» bekommt. Es hat folgenden Inhalt:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Cargo build",
            "type": "shell",
            "command": "cargo",
            "args": [
                "build"
            ],
            "problemMatcher": [
                "$rustc"
            ],
            "group": "build"
        },
        {
            "label": "Build binary",
            "type": "shell",
            "command": "arm-none-eabi-objcopy",
            "args": [
                "--output-target",
                "binary",
                # Reads from an ELF binary file
                "./target/thumbv6m-none-eabi/debug/rp2040-project-template",
                # Outputs a raw binary file
                "./target/thumbv6m-none-eabi/debug/rp2040-project-template.bin"
            ],
            "problemMatcher": [
                "$rustc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "dependsOn": "Cargo build"
        }
    ]
}

Diese Datei definiert den gesamten Build der Applikation.

VSCode: Nun brauchen wir noch eine Extension in VSCode, die «Cortex-Debug» Extension. Diese lässt sich unter dem Extensions-Menu sehr einfach installieren.

Die Cortex-Debug Erweiterung in Visual Studio Code.

Moment der Wahrheit: In VSCode öffnen wir ein Terminal und starten nun OpenOCD mit dem folgenden Kommando:

openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -s C:/rp2040_tools/openocd/tcl

Ich musste mit der Option -s den absoluten Pfad zu den Ordern «interface» und «target» angeben. Auch hier kann der Pfad variieren, falls ein anderer Installationsort gewählt wurde. Wenn OpenOCD nicht bei dem Systemvariablen ergänzt wurde, muss ebenfalls zur ausführbaren Datei der gesamte Pfad angegeben werden.

Folgende Meldung sollte erscheinen:

Open On-Chip Debugger 0.11.0-g4f2ae61 (2023-05-05-14:21)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
adapter speed: 5000 kHz

...

Info : CMSIS-DAP: Interface ready
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x10000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections

Nun können wir im Sourcecode nach unseren Wünschen Breakpoints setzten und in VSCode in dem Menu «Run and Debug» mit dem Play-Knopf die Debug-Session starten.

Somit sollte nun alles funktionieren und man kann das Target wunderbar debuggen.

In Visual Studio Code lassen sich die Rust Applikationen einfach debuggen.

Fazit

Wir haben nun Rust vollständig auf dem Raspberry Pi Pico eingerichtet, dazu braucht es einiges an Tools und ein wenig Geduld. Nichtsdestotrotz lohnt sich der Aufwand, wenn man den eigenen Horizont erweitern möchte und selber mal «Hands-On» Rust entwickeln möchte. Der Vorteil bei diesem Setup ist, dass beide Communities, die von Raspberry Pi und die von Rust, riesig sind und sich so der Einstieg in Embedded Rust deutlich erleichtern kann.

Nützliche Referenzen

Hier sind noch einige Referenzen, für alle, die mehr wissen wollen:

Pico & RP2040 Dokumentation: https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html

RP2040 HAL Dokumentation: https://docs.rs/rp2040-hal/latest/rp2040_hal/

Embedded Rust Book: https://docs.rust-embedded.org/book/

Quellen

Folgende Informationen für diesen Beitrag wurden den folgenden Quellen entnommen:

Embedded Rust Book: https://docs.rust-embedded.org/book/

Shawn Hymel: «How to Build OpenOCD and Picotool for the Raspberry Pi Pico on Windows», https://shawnhymel.com/2168/how-to-build-openocd-and-picotool-for-the-raspberry-pi-pico-on-windows/

Jim Hodapp: «Getting Started with Rust on a Raspberry Pi Pico (Part 3)», https://reltech.substack.com/p/getting-started-with-rust-on-a-raspberry-a88

Kommentare

Eine Antwort zu “Embedded Rust: Einrichten vom Raspberry Pi Pico zur Entwicklung mit Rust”

  1. […] Schluss sein wird. Auch in unseren Blogbeiträgen gibts bereits Artikel zu Rust, zum Beispiel wie Rust für den Raspberry Pi Pico aufgesetzt wird. Doch direkte Berührungspunkte hatte ich bis jetzt noch keine. Um ein fundiertere Aussage […]

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Newsletter - aktuelle Angebote, exklusive Tipps und spannende Neuigkeiten

 Jetzt anmelden
NACH OBEN
Privacy Policy Cookie Policy
Zur Webcast Übersicht