Startseite>Tricks zum Programmieren der GPIO-Schnittstelle
Aus Raspberry Pi Geek 05/2013

Tricks zum Programmieren der GPIO-Schnittstelle (Seite 2)

Über den Pin 16 (GPIO23) steuert der Raspberry Pi die grüne Status-LED für OK an. Versuchen Sie, diesen Pin anzusteuern, dann scheitert die Operation, weil der Kernel diese Ressource bereits mit Beschlag belegt (Listing 3). Auch andere Systemkomponenten wie etwa der I2C-Kernel-Treiber, können einzelne GPIO-Ports für normale Nutzer blockieren.

Listing 3

 

$ gpio_control 16 export
export failed: Device or resource busy

Der Kernel speichert den Zustand der GPIO-Pins. Exportieren Sie beispielsweise einen Ausgabe-Pin und geben ihn hinterher wieder frei, dann verschwinden zwar die entsprechenden Kontrolldateien, aber der Pin bleibt ein Ausgabe-Pin mit dem letzten gesetzten Wert. Exportieren Sie ihn später erneut, stellt der Kernel die Kontrolldateien und damit den gespeicherten Zustand wieder her.

Interrupts

Viele einfache Programme lesen in einer Endlosschleife den Wert eines Eingabesignals aus und reagieren, sobald sich dessen Wert verändert. Bei einer einzelnen Signalquelle mag das noch angehen – allerdings lastet eine solche Schleife trotzdem die CPU voll aus und bremst damit alle anderen Vorgänge auf dem RasPi.

Eine mögliche Abhilfe wäre eine Verzögerung in der Schleife, indem Sie diese etwa mittels des Kommandos sleep 0.5 zwischen den Abfragen für eine halbe Sekunde pausieren lassen. Das schafft zwar Zeit für andere Aktionen, bedeutet aber auch, dass im Schnitt eine Viertelsekunde vergeht, bevor das Programm eine Veränderung des Eingabewerts mitbekommt.

Mit Interrupts gibt es eine wesentlich effektivere Methode, um direkt auf solche Signalveränderungen zu reagieren. Sie greift insbesondere dann bestens, wenn es gilt, Veränderungen an verschiedenen Pins im Auge zu behalten. Das Python-Programm aus Listing 4 illustriert den Einsatz des GPIO-Interrupt-Handlings.

Das Programm konfiguriert Port 23 als Eingabequelle und setzt die zugehörige Kontrolldatei edge auf den Wert both. Damit erreicht es das sowohl “fallende” als auch “steigende” Veränderungen des Werts einen Interrupt auslösen. Außerdem öffnet es die Datei value, in der das System die ausgelesenen Werte ablegt.

Der Aufruf von select.poll() erzeugt das Abfrageobjekt po, für das dann po.register() die Werte-Datei des Ports als eine der Quellen registriert, die eine folgenden po.poll()-Anfrage bedienen können.

Listing 4

 

#!/usr/bin/python3
# Test interrupts.
import select, time, sys
pin_base = '/sys/class/gpio/gpio23/'
def write_once(path, value):
  f = open(path, 'w')
  f.write(value)
  f.close()
  return
f = open(pin_base + 'value', 'r')
write_once(pin_base + 'direction', 'in')
write_once(pin_base + 'edge', 'both')
po = select.poll()
po.register(f, select.POLLPRI)
state_last = f.read(1)
t1 = time.time()
sys.stdout.write('Initial pin value = {}\n'.format(repr(state_last)))
while 1:
  events = po.poll(60000)
  t2 = time.time()
  f.seek(0)
  state_last = f.read(1)
  if len(events) == 0:
    sys.stdout.write('  timeout  delta = {:8.4f} seconds\n'.format(t2 - t1))
  else:
    sys.stdout.write('value = {}  delta ={:8.4f}\n'.format(state_last, t2 - t1))
    t1 = t2

Zwar nutzt das Programm aus Listing 3 nur eine einzelne Interrupt-Quelle, aber mit dem Poll-Objekt lassen sich bei Bedarf auch weitere GPIO-Ports sowie andere Interrupt-Quellen registrieren. So könnte etwa eine Pipe, die in Verbindung mit einem anderen Prozess steht oder Daten aus dem Netzwerk empfängt, ebenso als Auslöser von Interrupts dienen.

Der zweite Operand von po.register() gibt an, welcher von drei möglichen Zuständen als Interrupt erkannt wird. Der Wert select.POLLPRI sorgt dafür, dass dies bei priority data to read geschieht. Die beiden anderen möglichen Zustände, data available und ready for output, sind für einen GPIO-Pin ohnehin immer gegeben. Sie können aber als Auslöser für andere Interrupt-Quellen dienen.

In manchen Fällen ist auch das Ausbleiben eines erwarteten Signals von Interesse. Der Aufruf po.poll(60000) wartet lediglich 60:000 Millisekunden lang, also eine Minute, auf einen Interrupt. Dann gibt er eine leere Liste von Interrupt-Signalen zurück und signalisiert so einen Timeout.

Für einen GPIO-Pin enthält diese Datei value zwei Bytes Inhalt: Eine O oder 1 repräsentiert den Pin-Zustand, es folgt ein Zeilenvorschub. Der Aufruf f.seek(0) setzt die Positionsmarke für die Datei wieder auf den Anfang, sodass sich der abgespeicherte Wert noch einmal einlesen lässt.

Fazit

Sobald Sie mit dem Raspberry PI ausgefeiltere Aufgaben anpeilen, brauchen Sie auch ein erweitertes Repertoire an Programmiertechniken. Die hier vorgestellten Methoden, vom Zugriff auf GPIO-Pins über deren Export bis hin zur Interruptverarbeitung, leisten Ihnen dabei vor allen Dingen in komplexeren Einsatzszenarien gute Dienste. 

Mehr Pins

Zwar stellt der Raspberry Pi nur eine Handvoll GPIO-Pins zur Verfügung, doch gibt es einige Implementationen, die zeigen, dass sich mit preiswerten ICs wie dem MCP23017 diese Anzahl ausbauen lässt. So stellt beispielsweise das Erweiterungsboard “Slice of Pi/O” [4] nicht weniger als 16 zusätzliche GPIO-Ports bereit und lässt sich in bis zu acht Exemplaren kaskadieren, sodass dann 128 digitale I/O-Pins bereitstehen.

Danksagung

Dieser Artikel ist ursprünglich in Ausgabe 7 von “The MagPi” (http://www.themagpi.com) erschienen und wird von uns mit deren freundlicher Genehmigung veröffentlicht.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
€0,99 – Kaufen
RASPBERRY PI GEEK KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS
Deutschland