![]() |
GPIO beim Raspberry PiProf. Jürgen Plate |
Zusätzlich zu USB, Ethernet, Audio, Video und HDMI bietet hat der Raspberry Pi verschiedene digitale Ein-/Ausgabeleitungen, die GPIOs (General Purpose Input/Output). Sie sind über die Stiftleiste P1 zugänglich. Beachten Sie, dass alle Signale der GPIO-Pins mit einem Logikpegel von 3,3 V arbeiten und nicht 5-V-tolerant sind. Einige Pins können auch als SPI-, I2C- oder UART-Schnittstelle verwendet werden.

Eine genaue Beschreibung der GPIO-Pins finden Sie in der GPIO-Einführung. Gerade für Anfänger sind die ersten Schritte oft schwierig und wer noch nicht viel Erfahrung mit Linux gemacht hat, findet oft nicht die einfachsten Lösungen. (Mehr zu UNIX/Linux finden Sie im UNIX/Linux-Skript.)
Bei UNIX galt das Prinzip schon immer und beim Linux des Raspberry Pi gilt es natürlich auch: Alles ist Datei! Mit anderen Worten, es werden alle Schnittstellen des Computers auf das Dateisystem abgebildet - und nicht nur das, am /proc-Verzeichnis kann man studieren, wie sich auch Informationen des Betriebssystem-Kerns auf ein Pseudo-Dateisystem (sogenannte virtuelle Dateien) abbinden lassen.
Auch die GPIOs können direkt über ein System virtueller Dateien angesprochen werden. Für die GPIO-Pins werden die Dateien in /sys/class/gpio/ angesprochen. Dies kann jedoch nur mit root-Rechten erledigt werden. Nach dem Booten befinden sich dort nur zwei Dateien, export und unexport. Alle anderen Dateien müssen Sie erst erzeugen.
Zuerst müssen die Files via export zugänglich gemacht werden. Dies geschieht, indem man die Nummer des gewünschten GPIO (nicht etwa die Pinnummer der Steckerleiste!) in export schreibt, z. B.:
echo "23" > /sys/class/gpio/exportMit dem Kommando ls /sys/class/gpio können Sie nun ein neues Verzeichnis mit dem Namen 'gpio23' sehen. In diesem Verzeichnis befinden sich u. a. zwei Dateien direction und value. Diese Dateien dienen zum Steuern des GPIO-Pins.
Nun wird entschieden, ob der GPIO-Port Eingang oder Ausgang sein soll, z.B.:
echo "in" > /sys/class/gpio/gpio23/directionoder
echo "out" > /sys/class/gpio/gpio23/direction echo "0" > /sys/class/gpio/gpio23/valueBei einem Ausgang ist es sinnvoll, diesem gleich mit einem Initialwert zu setzen. Im Beispiel oben wird er ausgeschaltet. Normalerweise sind alle (Pseudo-)Dateien unterhalb von /sys/class/gpio/ dem Benutzer 'root' und der Gruppe 'root' zugeordnet. Damit man mit dem GPIO auch spielen kann, wenn man nicht 'root' ist, setzt man auch gleich noch die Zugriffsrechte für alle:
chmod 660 /sys/class/gpio/gpio23/direction chmod 660 /sys/class/gpio/gpio23/value
Hat der Raspberry Pi eine feste Aufgabe, kann die Initialisierung aller benötigten GPIOs in ein Shellscript geschrieben werden, das dann von /etc/rc.local beim Booten aufgerufen wird. Auf diese Weise können alle weiteren Aktionen vom "normalen" Useraccount aus erledigt werden und man muss keine programmtechnische Verrenkungen machen.
Bei mehreren Ports lohnt sich sowieso ein Script. Für die weiter unten beschriebene Testplatine mit zwei Tastern und vier LEDs sieht das Script 'led-init.sh' folgendermaßen aus. Nur dieses Script muss mit root-Rechten ausgeführt werden:
#!/bin/sh
# Input-Ports (Taster)
for Port in 17 27
do
echo "$Port" > /sys/class/gpio/export
echo "in" >/sys/class/gpio/gpio${Port}/direction
chmod 660 /sys/class/gpio/gpio${Port}/direction
chmod 660 /sys/class/gpio/gpio${Port}/value
done
# Output-Ports (LED)
for Port in 22 23 24 25
do
echo "$Port" > /sys/class/gpio/export
echo "out" >/sys/class/gpio/gpio${Port}/direction
echo "0" >/sys/class/gpio/gpio${Port}/value
chmod 660 /sys/class/gpio/gpio${Port}/direction
chmod 660 /sys/class/gpio/gpio${Port}/value
done
Achtung: Auch wenn das Kommando chmod 660 ... durch
chmod 666 ... (Zugriffsrecht fuer alle) wird kein
Zugriffsrecht für others zugelassen. Wenn andere Benutzeraccounts
als der Standarduser "pi" auf die GPIO-Pins zugreifen sollen, müssen
Sie zusätzlich in die Gruppe gpio eingetragen werden:
sudo usermod -a -G gpio <Username>Die Änderungen der Gruppe werden erst nach dem nächsten Login des Users wirksam.
Wenn Ihnen das Gefrickel mit den Gruppen- und Zugriffrechten zu aufwendig erscheint, gibt es noch einen zweiten Weg über die systemweite Konfiguration im Verzeichnis /etc/udev/. Für den GPIO-Zugriff legen Sie im darunter befindlichen Regel-Verzeichnis rules.d eine neue Regeldatei namens
/etc/udev/rules.d/80-gpio-noroot.rulesan. Wichtig ist eigentlich nur die Endung ".rules". Die Zahl am Anfang legt die Reihenfolge fest, in der die Regeldateien abgearbeitet werden. 80 ist da ein guter Kompromiss. Für das Beispiel verwende ich die oben schon erwähnte Gruppe "gpio", es wäre im Prinzip auch jede andere Gruppe möglich. In den Regeln wird nun festgelegt, dass für die beiden dem GPIO zugeordneten Verzeichnisse die Gruppe auf "gpio" gesetzt wird und die Zugriffsrechte auf Lesen und Schreiben. Zusätzlich wird auch noch das Sticky-Bit für die Gruppe gesetzt, was bedeutet, dass neue Dateien oder Unterverzeichnisse auch automatisch die Gruppe "gpio" erhalten:
# /etc/udev/rules.d/80-gpio-noroot.rules # Zugriff auf GPIO ohne root-Rechte ermoeglichen # # Gruppe aendern SUBSYSTEM=="gpio", RUN+="/bin/chown -R root.gpio /sys/class/gpio" SUBSYSTEM=="gpio", RUN+="/bin/chown -R root.gpio /sys/devices/virtual/gpio" # Sticky-Bit setzen SUBSYSTEM=="gpio", RUN+="/bin/chmod g+s /sys/class/gpio" SUBSYSTEM=="gpio", RUN+="/bin/chmod g+s /sys/devices/virtual/gpio" # Zugriffsrechte setzen SUBSYSTEM=="gpio", RUN+="/bin/chmod -R ug+rw /sys/class/gpio" SUBSYSTEM=="gpio", RUN+="/bin/chmod -R ug+rw /sys/devices/virtual/gpio"Jetzt müssen Sie nur noch den udev-Daemon von den Änderungen wissen lassen (beim nächsten Reboot passiert das dann automatisch):
sudo service udev restart sudo udevadm trigger --subsystem-match=gpio
Durch Schreiben von "0" oder "1" in die virtuelle Datei value eines GPIO wird der entsprechende Pin der Pfostenleiste auf 0 V oder 3,3 V gesetzt. Das Lesen eines Eingangsports erfolgt auf ähnliche Art und Weise - hier liefert der GPIO "0" oder "1", je nachdem welcher Logikpegel am entsprechenden Pin anliegt. In einer Shell-Variablen läßt sich der Wert durch folgendes Kommando speichern:
Val=$(cat /sys/class/gpio/gpio23/value)
In der Praxis kann es durchaus vorkommen, dass die Signale invertiert anliegen. Um im Programm nicht mit invertierter Logik arbeiten zu müssen, kann man die GPIOs direkt das Eingangssignal invertieren lassen. Um die Pinlogik umzukehren, reicht das Kommando:
echo 1 >/sys/class/gpio/gpio23/active_lowWenn active_low auf 1 gesetzt ist, liegt am Pin "0" an, wenn in value "1" steht, und umgekehrt.
Das folgende Shell-Script haben Sie eventuell schon im Kapitel GPIO beim Raspberry Pi gesehen; es arbeitet mit dem dort beschriebenen Testboard. Das Script fragt zum einen die beiden Taster ab und gibt deren Wert in der Shell aus und blinkt mit allen vier LEDs. Für das Blinken gibt es eine Shell-Funktion, die den Wert toggelt und eine Kontrollausgabe erzeugt.
#!/bin/sh
toggle()
{
# Zustand des Pins einlesen
LedVal=$(cat /sys/class/gpio/gpio$1/value)
# LED toggeln
if [ $LedVal -eq "0" ]; then
LedVal="1"
else
LedVal="0"
fi
# Kontrollausgabe auf der Konsole
echo "Port $1 := $LedVal"
# Pin auf 0 oder 1 setzen
echo $LedVal > /sys/class/gpio/gpio$1/value
}
while :
do
# Taster einlesen und anzeigen
for Port in 17 27
do
LED=$(cat /sys/class/gpio/gpio${Port}/value)
echo "Port $Port = $LED"
done
# mit den LEDs blinken
for Port in 22 23 24 25
do
toggle $Port
done
echo ""
sleep 1
done
trap 'GPIO-aus.sh ; exit' 0 1 2 3 15Dabei sorgt das Script GPIO-aus.sh für das Abschalten aller LEDS:
#!/bin/sh
for Port in 22 23 24 25
do
echo "0" > /sys/class/gpio/gpio${Port}/value
done
Durch Schreiben der GPIO-Nummer in die virtuelle Datei /sys/class/gpio/unexport kann der GPIO wieder deaktiviert werden. Auch diese Aktion darf nur 'root' ausführen. Das folgende Script deaktiviert die verwendeten Ports vollständig:
#!/bin/sh
for Port in 22 23 24 25
do
echo "0" > /sys/class/gpio/gpio${Port}/value
echo "$Port" > /sys/class/gpio/unexport
done
Zum Schluss noch ein Script für das Testboard, diesmal ein Lauflicht:
#!/bin/sh
trap 'echo "0" > /sys/class/gpio/gpio${Port}/value; exit' 0 1 2 3 15
Port="22"
echo "1" > /sys/class/gpio/gpio22/value
while :
do
echo "0" > /sys/class/gpio/gpio${Port}/value
Port=$(( $Port + 1 ))
if [ $Port -eq "26" ]; then
Port="22"
fi
echo "1" > /sys/class/gpio/gpio${Port}/value
sleep 1
done
Noch übersichtlicher geht es, wenn man Arrays verwendet, wie sie die Bash bietet. Zum einen kann man die verwendeten GPIO-Ports übersichtlich definieren und zum anderen auch die Kommandozeilenparameter mit wenigen Befehlen verarbeiten. Das folgende Programm besitzt noch ein weiteres Feature: Falls einer der verwendeten Ports noch nicht initialisiert wurde, holt das Programm dies nach. Dabei wird geprüft, ob die (Pseudo-)Datei /sys/class/gpio/gpio${Port}/direction schon existiert. Ist dies der Fall, wird nichts gemacht. Andernfalls erfolgt die Initialisierung des angegebenen Ports.
#!/bin/bash
# Schaltet die LEDs an oder aus
# Programmaufruf: anzeige.sh <LED 1> <LED 2> <LED 3> <LED4>
# Fuer die LEDS sind drei Werte moeglich:
# - 0: LED aus
# - 1: LED an
# - x: alten Wert beibehalten
# Beispiele:
# anzeige.sh 0 1 0 0 schaltet LED 2 an und die anderen aus
# anzeige.sh 1 x x 0 schaltet LED 1 an, LED 4 aus und laesst
# LED 2 und LED 3 unveraendert
#
# Port-Zuordnung zu LED 1, LED 2, LED 3 und LED 4
GPIO=(22 23 24 25)
if [ $USER != "root" ]; then
echo "Das Programm muss als root-User gestartet werden!"
exit 1
fi
# alle Parameter vorhanden?
if [ $# -lt 3 ] ; then
echo "usage: $0 LED1 LED2 LED3 LED4 (0 oder 1; x fuer alten Wert behalten)"
exit 1
fi
# Parameter in einem Array speichern
PARAM=($@)
# Ports initialisieren; aber nur, wenn noch nicht erfolgt
for Port in $(echo ${GPIO[@]})
do
if [ ! -f /sys/class/gpio/gpio${Port}/direction ] ; then
echo "Initialisiere GPIO $Port"
echo "$Port" > /sys/class/gpio/unexport
echo "$Port" > /sys/class/gpio/export
echo "out" >/sys/class/gpio/gpio${Port}/direction
echo "0" >/sys/class/gpio/gpio${Port}/value
chmod 666 /sys/class/gpio/gpio${Port}/direction
chmod 666 /sys/class/gpio/gpio${Port}/value
fi
done
for N in 0 1 2 3
do
PA=${PARAM[$N]}
GP=${GPIO[$N]}
if [ $PA = "0" -o $PA = "1" ] ; then
echo "$PA" >/sys/class/gpio/gpio${GP}/value
echo "GPIO ${GP} auf $PA gesetzt"
fi
done