GPIO Reloaded II: Bash
Das ist der zweite Teil einer Mini-Serie zur GPIO-Nutzung am Raspberry Pi 5:
- GPIO Reloaded I: Python (gpiozero, lgpio, gpiod, rpi-lgpio)
- GPIO Reloaded II: Bash (gpiod, gpioget, gpioset, pinctrl)
- GPIO Reloaded III: Kamera (rpicam-xxx, Picamera2)
Zu den wichtigsten Neuerungen beim Raspberry Pi 5 zählt nicht nur der viel schnellere SoC (System-on-a-Chip), sondern auch ein eigener I/O-Controller, der als eigener Chip realisiert ist (RP1). Dieser I/O-Chip bringt mit sich, dass etablierte Mechanismen zur GPIO-Steuerung nicht mehr funktionieren. Besonders stark betroffen sind Kommandos, die im Terminal oder in Bash-Scripts aufgerufen werden.
Veraltet: WiringPi, »gpio«, »raspi-gpio« und »pigpiod/pigs«
Im Verlauf eines Jahrzehnts haben sich diverse Kommandos etabliert, die mittlerweile veraltet sind. Dazu zählt das Kommando gpio
aus dem WiringPi-Projekt, das bereits 2019 eingestellt wurde. Ebenfalls verabschieden müssen Sie sich von dessen Nachfolger-Kommando raspi-gpio
: Das Kommando ist nicht mit dem neuen I/O-Chip RP1 kompatibel. Glücklicherweise lässt sich das Kommando relativ einfach durch pinctrl
ersetzen.
Deutlich ärgerlicher ist, dass auch der beliebte Dämon pigpiod
und das dazugehörende Kommando pigs
der Kompatibilität zu RP1 zum Opfer gefallen ist. Absurderweise kann der Dienst Anfang 2024 im Raspberry-Pi-Konfigurationsprogramm als GPIO-Fernzugriff scheinbar weiterhin aktiviert werden.
journalctl -u pigpiod
beweist aber, dass der Dienst nicht funktioniert:
journalctl -u pigpiod
systemd[1]: Starting pigpiod.service - Daemon required to control GPIO pins via pigpio...
systemd[1]: Started pigpiod.service - Daemon required to control GPIO pins via pigpio.
pigpiod[88161]: 2023-12-29 11:02:24 gpioHardwareRevision: unknown rev code (d04170)
pigpiod[88161]: 2023-12-29 11:02:24 initCheckPermitted:
pigpiod[88161]: +---------------------------------------------------------+
pigpiod[88161]: |Sorry, this system does not appear to be a raspberry pi. |
pigpiod[88161]: |aborting. |
pigpiod[88161]: +---------------------------------------------------------+
pigpiod[88161]: Can't initialise pigpio library
systemd[1]: pigpiod.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: pigpiod.service: Failed with result 'exit-code'.
Das Problem ist bekannt, aber es sieht nicht so aus, als könnte es behoben werden: https://github.com/joan2937/pigpio/issues/589
gpioget und gpioset
Welche Kommandos funktionieren dann noch? Sie haben die Wahl zwischen den gpioxxx
-Kommandos aus dem Paket gpiod
sowie pinctrl
(siehe den folgenden Abschnitt). Das Paket gpiod
ist standardmäßig installiert. Die darin enthaltenen Kommandos nutzen zur Kommunikation mit dem Kernel die Device-Dateien /dev/gpiochip<n>
und die Bibliothek libgpiod2
.
Der größte Nachteil der Kommandos gpioget
, gpioset
usw. besteht darin, dass Sie als ersten Parameter die GPIO-Chip-Nummer angeben müssen. Diese variiert je nach Raspberry-Pi-Modell. Bei den Modellen der Serie 1 bis 4 müssen Sie die Nummer 0 angeben, ab Modell 5 die Nummer 4.
# LED ein- und ausschalten, die über den GPIO 7 gesteuert wird
# (= Pin 26 des J8-Headers)
# gpioset auf dem Raspberry Pi 5
gpioset 4 7=1; sleep 3; gpioset 4 7=0
# gpioset auf dem Raspberry Pi 1 bis 4
gpioset 0 7=1; sleep 3; gpioset 0 7=0
Warum variiert die GPIO-Chip-Nummer? Weil beim Raspberry Pi 4 die Kernel-Schnittstelle /dev/gpiochip0
für die GPIO-Steuerung verantwortlich ist (das sind in den BCM 2711 integrierte Funktionen), beim Pi 5 aber der RP1 (ein externer Chip) mit der Kernel-Schnittstelle /dev/gpiochip4
. Informationen darüber, welche GPIO-Schnittstellen es gibt und welche GPIO-Funktion wie »verdrahtet« ist, geben die Kommandos gpiodetect
und gpioinfo
. Die folgenden Ausgaben gelten für den Raspberry Pi 5:
gpiodetect
gpiochip0 [gpio-brcmstb@107d508500] (32 lines)
gpiochip1 [gpio-brcmstb@107d508520] ( 4 lines)
gpiochip2 [gpio-brcmstb@107d517c00] (17 lines)
gpiochip3 [gpio-brcmstb@107d517c20] ( 6 lines)
gpiochip4 [pinctrl-rp1] (54 lines)
gpioinfo
gpiochip0 - 32 lines:
line 0: "-" unused input active-high
line 1: "2712_BOOT_CS_N" "spi10 CS0" output active-low
line 2: "2712_BOOT_MISO" unused input active-high
...
gpiochip1 - 4 lines:
line 0: "WIFI_SDIO_D0" unused input active-high
line 1: "WIFI_SDIO_D1" unused input active-high
...
gpiochip2 - 17 lines:
line 0: "RP1_SDA" unused input active-high
line 1: "RP1_SCL" unused input active-high
line 2: "RP1_RUN" "RP1 RUN pin" output active-high
...
gpiochip3 - 6 lines:
line 0: "HDMI0_SCL" unused input active-high
line 1: "HDMI0_SDA" unused input active-high
...
gpiochip4 - 54 lines:
line 0: "ID_SD" unused input active-high
line 1: "ID_SC" unused input active-high
line 2: "PIN3" unused input active-high
line 3: "PIN5" unused input active-high
line 4: "PIN7" "onewire@0" output active-high
line 5: "PIN29" "onewire@0" output active-low
line 6: "PIN31" unused input active-high
line 7: "PIN26" unused input active-high
line 8: "PIN24" unused input active-high
line 9: "PIN21" unused input active-high
line 10: "PIN19" unused input active-high
...
line 28: "PCIE_RP1_WAKE" unused input active-high
line 29: "FAN_TACH" unused input active-high
line 30: "HOST_SDA" unused input active-high
line 31: "HOST_SCL" unused input active-high
line 32: "ETH_RST_N" "phy-reset" output active-low
...
Um Scripts zu programmieren, die universell funktionieren, können Sie die folgenden Zeilen in den Code einbauen:
# chip=4 für RPi5, chip=0 für ältere Modelle
if gpiodetect | grep -q "pinctrl-rp"; then
chip=4
else
chip=0
fi
In der einfachsten Form schalten Sie mit gpioset
einen GPIO-Ausgang auf High oder Low. In den folgenden Beispielen bezieht sich der erste Parameter auf die gpiochip
-Nummer. 7
gibt die GPIO-Nummer in BCM-Nomenklatur an, 1
oder 0
den gewünschten Zustand:
gpioset $chip 7=1 # GPIO 7 (Pin 26) auf High stellen
gpioset $chip 7=0 # GPIO 7 (Pin 26) auf Low stellen
Sie können auch mehrere Ausgänge auf einmal steuern (hier GPIO 7, 8 und 25):
gpioset $chip 7=0 8=1 25=0
Durch diverse Optionen können Sie weitere Funktionen steuern (siehe auch man gpioset
):
--bias=as-is|disable|pull-down|pull-up
aktiviert die internen Pull-up- oder Pull-down-Widerstände.-
--mode=exit|wait|time|signal
gibt an, wie lange das Kommando laufen soll. Standardmäßig giltexit
, das Kommando wird also sofort beendet. Mitwait
wartet das Programm, bis der Benutzer[Return]
drückt. Bei der Einstellungtime
können Sie mit--sec=<n>
oder--usec=<n>
die gewünschte Wartezeit einstellen.signal
bedeutet, dass das Programm weiterläuft, bis es mit[Strg]
+[C]
beendet wird. -
--background
führt das Kommando als Hintergrunddienst weiter.
gpioget
funktioniert analog zu gpioset
: Sie übergeben im ersten Parameter die gpiochip
-Nummer (in aller Regel 0), im zweiten Parameter die BCM-Nummer des GPIOs, dessen Input Sie auswerten wollen. Das Ergebnis des Kommandos lautet 0
oder 1
, je nachdem, welchen Zustand der Eingang hat.
gpioget $chip 9 # Zustand von GPIO 9 (Pin 21) auslesen
0
pinctrl
Auch mit pinctrl
aus dem Paket raspi-utils
können Sie GPIO-Funktionen steuern. Der Vorteil von pinctrl
besteht darin, dass das Kommando zur Zeit mit allen Raspberry-Pi-Modellen kompatibel ist. Eine Fallunterscheidung, ob das Script auf einem alten oder neuen Modell mit RP1-Chip läuft, entfällt. Außerdem ist das Kommando syntaktisch weitestgehend zu raspi-gpio
kompatibel.
Gegen den Einsatz des Kommandos spricht der Umstand, dass das Kommando laut pinctrl -h
(der einzigen mir bekannten Dokumentation) nur für Debugging-Zwecke gedacht ist.
Die folgende Aufzählung fasst die wichtigsten Anwendungen des Kommandos zusammen:
pinctrl get [gpionr]
ermittelt den aktuellen Status aller GPIOs bzw. des angegebenen GPIOs.-
pinctrl funcs [gpionr]
ermittelt, welche alternativen Funktionen der angegebene GPIO bzw. alle GPIOs erfüllen können. -
pinctrl set gpionr options
verändert den Status des angegeben GPIOs. Mögliche Optionen sind:ip
= Inputop
= Outputdl
= Zustand Low (Drive Low)dh
= Zustand High (Drive High)pu
= Pull-up-Widerstand aktivpd
= Pull-down-Widerstand aktivpn
= keine Pull-up/down-Funktiona0
bisa7
= alternative Funktion n aktivierenno
= Deaktivieren (no function)
Soweit sich sinnvolle Kombinationen ergeben, dürfen mehrere der obigen Optionen auf einmal übergeben werden, jeweils getrennt durch Leerzeichen. Welche alternative Funktionen ein GPIO unterstützt und wie sie nummeriert sind, geht aus pinctrl funcs
hervor.
Das folgende Kommando ermittelt, welche Funktionen der GPIO mit der BCM-Nummer 23 unterstützt. Auf dem Raspberry Pi ist dieser GPIO mit Pin 16 des J8-Headers verbunden. GPIO23 kann diverse Funktionen übernehmen:
pinctrl funcs 23
23, PIN16/GPIO23, SD0_CMD, DPI_D19, I2S0_SDO1, SCL3,
I2S1_SDO1, SYS_RIO023, PROC_RIO023, PIO23
Wenn Sie über Pin 26 (BCM-Nummer 07) eine Leuchtdiode angeschlossen haben, dann können Sie die LED wie folgt ein- und ausschalten:
pinctrl set 7 op dh # LED an Pin 26 ein
pinctrl set 7 op dl # LED an Pin 26 aus