GPIO-Pins

Die großen "Bastelfunktionen" des Raspberry liegen sicher an den generischen IO-Pins. die als Eingang oder Ausgang missbraucht werden können. Allerdings sind nicht alle Ports "immer" frei, da sie teilweise mehrere Funktionen haben.

P1-Anschlussport

Auch wenn bis zu 17 GPIO-Pins bereit stehen, sind einige Pins "besonders", da sie für andere Funktionen nutzbar sind. Zudem sollten Sie genau hinschauen, da die Nummerierung der GPIO-Pins nicht mit der Nummerierung des P1-Anschlusses auf dem RaspBerryPI übereinstimmt. Ich habe mir folgende Ports ausgesucht.

GPIO P1-Port (Revision 2) Beschreibung

3,3 Volt

01

Stromversorgung

5 Volt

02

Stromversorgung

2

03

SDA, I2C-Bus mit internem 1k8 Pullup Wiederstand

3

05

SCL, I2C-Bus mit internem 1k8 Pullup Wiederstand

GND

06

Masse (Ground)

4

07

CPCLK, Teilweise als 1-Wire Port mit "w1-gpio" genutzt.

7

26

SPI CE1 Verfügbar

8

24

SPI CE0 Verfügbar

9

21

SPI MISO Verfügbar

10

19

SPI MOSI Verfügbar

11

23

SPI SCLK Verfügbar

14

08

Aktiv: RS232-Schnittstelle TxD

15

10

Aktiv: RS232-Schnittstelle RxD

17

11

Frei

18

12

PCM Clock Audio

22

15

Frei

23

16

Frei

24

18

Frei

25

22

Frei

27

13

Frei

Nach einen Reset sind nur Port 14/15 als serieller Port aktiv und können zu GPIO-Ports umgeschaltet werden. Alle anderen Ports sind per Default in der Betriebsart GPIO. Wenn man sich aber keine andere Funktionen verbauen möchte, sollte erst einmal die freien GPIO-Ports verplanen: 


Quelle: http://elinux.org/Rpi_Low-level_peripherals#GPIO_hardware_hacking Stand Aug 2013

Beachten Sie unbedingt, dass die Eingänge nicht "Safe" sind, sondern direkt die Beinchen des Prozessors sind und nicht mehr als 3,3V und auch nur bedingt Ströme vertragen. Alles über einer LED sollten Sie mit Transistoren oder Optokopplern o.ä. verstärken. Eingänge sollten zudem gegen zu viel Strom geschützt werden. Ein in Reihe geschalteter 1kOhm Widerstand hilft hier gegen Überlast. Aus dem 3,3V-Pin dürfen maximal 50mA gezogen werden, während aus dem 5V Pin (Rev-B) max. 300mA gezogen werden dürfen.

GPIO im Dateisystem

Unter Unix sind IOs fast immer über das Dateisystem möglich, z.B. viele Geräte sehen einfach wie ein Pfad oder eine Datei im Dateisystem aus. Selbst Systemdaten sind so einfach zu erreichen: Ein paar Beispiele

#Ausgabe der Ethernet Link Geschwindigkeit
cat /sys/class/net/eth0/speed

#Ausgabe der Temperatur des bcm2835
cat /sys/class/thermal/thermal_zone0/temp
cat /sys/class/thermal/thermal_zone0/type

Durch einfaches Schreiben und Lesen in Verzeichnisse und Dateien können die GPIO-Pins angesteuert werden. für generischen GPIO-Pins ist das /sys/class/gpio/. Hier gibt es pro GPIO-Pin ein Verzeichnis mit Werten und Konfiguration, aber nur, wenn der Port auch "exportiert" ist.

# GPIO 24  (P1 Pin 18) auf Ausgang setzen und einschalten
if test ! -d /sys/class/gpio/gpio24; then echo 24 > /sys/class/gpio/export; fi
echo out > /sys/class/gpio/gpio24/direction
echo 1 > /sys/class/gpio/gpio24/value


if test ! -d /sys/class/gpio/gpio17; then echo 17 > /sys/class/gpio/export; fi
echo in > /sys/class/gpio/gpio17/direction
echo 1 > /sys/class/gpio/gpio17/value


if test ! -d /sys/class/gpio/gpio17; then echo 17 > /sys/class/gpio/export; fi
echo out > /sys/class/gpio/gpio17/direction
echo 0 > /sys/class/gpio/gpio17/value
echo 1 > /sys/class/gpio/gpio17/value

echo 0 > /sys/class/gpio/unexport 

Aktuell scheitern diese einfachen Ausgaben bei mir noch daran, dass ich keine Rechte habe und ein SuDO nicht hilft. Der wirkt nur auf den ECHO aber nicht auf die Umleitung auf eine Datei

Wer mag kann natürlich auch über eine X11 GuI diese Strukturen sehen

Das folgende WiringPi macht einiges einfacher und wenn damit die Ports exportiert werden, dann können sie ebenfalls im Dateisystem erreicht werden

wiringPI

Die Problematik mit SuDO und Root-rechten kann man recht einfach umgehen, wenn man die WiringPI-Library installiert. Dazu muss zuerst einmal GIT installiert werden, wenn noch nicht vorhanden um dann das Paket zu laden

# GIT Core Paket installieren
sudo apt-get install git-core

# wiringPI per HTTp aus dem Internet herunter laden
git clone git://git.drogon.net/wiringPi 

# ggfls ein Update pruefen
cd /home/pi/wiringPi/
git pull origin

#
cd wiringPi 

# kompilieren des Code
./build

Danach gibt es das Programm GPIO, welches direkt die Pins schalten kann.

Die Anwendung ist denkbar einfach. Zuerst wird definiert, welche Pins als Ausgang und welche als Eingang fungieren sollen. Passend auf mein Selbstbauboard Raspberry - IOBoard sind das:

$ gpio export 23 out
$ gpio export 24 out
$ gpio export 25 out
$ gpio export 17 in
$ gpio export 27 in

Und dann kann ich recht einfach die Ports setzen und wieder auslesen.

gpio write 23 1
pi@raspberrypi ~/wiringPi $ gpio write 23 0
pi@raspberrypi ~/wiringPi $ gpio write 24 1
pi@raspberrypi ~/wiringPi $ gpio write 24 0
pi@raspberrypi ~/wiringPi $ sudo shutdown -Ph now

Auch eine Information über das Board selbst und alle Ports ist recht einfach möglich

gpio readall

Python und GPIO

Eine Programmiersprache des Raspberry ist Python, die als Skript als auch interaktiv ausgeführt werden kann. Die wichtige Dinge, die für Programmierer vielleicht ungewohnt sind:

  • Case Sensible
    PowerShell und VBScript beachten z.B.: nicht die Schreibweise von Variablen
  • Blockbildung durch Einrücken
    Es gibt keine Klammerung wie bei PowerShell und C# oder "Begin/End"-Worte wie bei VBScript. Bei Python definiert die Einrückung einen Block. Sie ist also nicht nur "Kosmetik" sondern Basis
  • SuDO-Rechte
    Wann immer man aber direkt auf Ports zugreifen will, muss das Skript als Superuser laufen.
  • Interpreter und Shell
    Python-Skripte können einfach wie PowerShell und VBScript gestartet werden. Python ist aber auch eine eigene LaufzeitUmgebung wie PowerShell, d.h. man kann interaktiv auch Code eingeben und ausführen lassen.

Also los in die ersten Versuche.

sudo phyton

# Python Sample fuer einfache IOs

print("Starting Sample GPIO-Control für BerryClip compatible deviucs") 

import RPi.GPIO as GPIO
import time

print("GPIO.VERSION="+GPIO.VERSION)
print("GPIO.VERSION="+str(GPIO.RPI_REVISION))

# use P1 header pin numbering convention
GPIO.setmode(GPIO.BOARD)

# Set up the GPIO channels für intport in (16,18,22):
   print("Setup Port " +str(intport)+" as output")
   GPIO.setup(intport, GPIO.OuT)

print("Setup Port 11/13 as input")
GPIO.setup(11, GPIO.IN)
GPIO.setup(13, GPIO.IN)

# Reading Input
input_value11 = GPIO.input(11)
input_value13 = GPIO.input(13)

# Writing Output
while True: für intport in (16,18,22):
      print("Port " +str(intport)+" High")
      time.sleep(0.1)
      GPIO.output(intport, GPIO.HIGH) für intport in (16,18,22):
      print("Port " +str(intport)+" Low")
      time.sleep(0.1)
      GPIO.output(intport, GPIO.LOW)
   while GPIO.input(11) == 1:
      print("Input 11 is 1 .pausing")
   while GPIO.input(13) == 0:
      print("Input 13 is 0 .blinking light 16")
      GPIO.output(intport, GPIO.HIGH)
      time.sleep(0.1)
      GPIO.output(intport, GPIO.LOW)
      time.sleep(0.1)

# Cleanup end
GPIO.cleanup()

Dies ist ein sehr einfacher Code um die Funktion zu demonstrieren aber gerade das Einlesen ist nicht ganz zuverlässig. Die GPIOs sind eingehend nämlich nicht entprellt. Ein Taster schließt oder öffnet einen Stromkreis nicht zwingend "binär", da Kontaktflächen nie eben sind und selbst zwischen Leiterbahnen Kapazitäten vorhanden sind. Mit dem Auge sind solche Preller nicht zu sehen aber eine CPu, die zigtausendfach pro Sekunde den Status einlesen kann, sieht so etwas schon. Solche ein Prellen kann man mit einem Schmitt-Trigger in Hardware lösen. Aber billiger ist eine Softwarelösung, die einfach einen Port mehrfach mit einer Pause dazwischen einliest und so das Ergebnis richtig macht. Hier mal zwei mögliche Ansätze

' Auf eine Änderung warten und quasi die erste Änderung in einem Zeitfenster nutzten

count = 0
old_input = 0
while count < 10 :
  #take a reading
  input = GPIO.input(26)
   # trick to trigger on change and not on state
  if ((not old_input) and input):
    print("Button pressed")
    #update previous input
    old_input = input
    #slight pause to debounce
    time.sleep(0.05)
   if count >= 5:
     break 

' Einfach z.B. 10 Messwerte in einer Zeit lesen und anhand des Mittelwerts entscheiden.
input=0 für count in (1..10):
input = input + GPIO.input(26)
time.sleep(0.05)
input = input + GPIO.input(26)
time.sleep(0.05)
input = input + GPIO.input(26)
if (input > 2) 

GPIO Ports und Siri

Wer sagt denn, dass die Ports immer nur per Skript oder Webseite geschaltet werden müssen ?. Es gibt Leute die mittels SiriProxy auf dem RasPi auch per Sprache einen Port schalten und z.B. Garagen und Rolläden steuern.

SiriProxy ist ein Proxy, der auf dem iPhone unter SIRI eingetragen werden muss. Alle SIRI-Anfragen gehen dann zu diesem Proxy, der sie zu Apple weiter gibt und die Antworten wieder bekommt. Also Proxy kann er natürlich "reinschauen" und die Antworten auswerten und darauf reagieren.

Damit ist auch klar, dass ihr iPhone zwingend mit dem SiriProxy sprechen muss und das in der Regel nur "intern" geht. Ich ehe nicht davon aus, dass jemand wirklich einen intern installierte SiriProxy aus dem Internet mit einem DNS-Namen oder festen IP-Adresse erreichbar machen will. Eine Variante wäre noch ein VPN nach "Hause". Dennoch ist SiriProxy natürlich zumindest für Internet eine nette Spielerei und zeigt das Potential und die Machbarkeit auf.

Sobald sie aber den zweiten oder mehr RasPIs installieren wollen, klappt das aktuell natürlich nicht mehr, da auf dem IPhone ja nur ein SiriProxy eingetragen sein kann. Die Aufgabe besteht dann darin, die RasPi's miteinander zu verschalten, damit ein zentraler SiriProxy dann eben per LAN den anderen RasPi das Signal gibt.

SiriProxy on Raspberry Pi Home Automation Control
http://www.youtube.com/watch?v=PXmCiaRc9Xu

Interrupts

Auf die Dauer ist es natürlich müßig immer wieder die Ports abzufragen, wenn sich nicht tut. Interessanter ist es, wenn der RasPi selbst aktiv wird, wenn sich Pegel eines Eingangs ändert. Auch das geht, wobei das entsprechende Programm natürlich schon gestartet und aktiv bleiben muss. Aber es enthält eine Funktion, die durch den Interrupt außer der Reihe aufgerufen wird. Das eigentlich Hauptprogramm kann daher immer "schlafen".

Hier ein Beispiel, bei dem Port 17 und 27 mit einer eigenen Routine quasi gleichzeitig behandelt werden.

print "-- GPIO InterruptTest gestartet"

print "-- Erforderliche Module einbinden"
import RPi.GPIO as GPIO
import time

print "-- Variable definieren"
count17 = 0
count27 = 0

print "-- SoC als Pinreferenz waehlen"
GPIO.setmode(GPIO.BCM)

print "-- Pin 17 vom SoC (P1:11-Anschluss) als Input deklarieren"
GPIO.setup(17, GPIO.IN)
print "-- Pin 27 vom SoC (P1:13-Anschluss) als Input deklarieren"
GPIO.setup(27, GPIO.IN)

# bei minimierter externer Beschaltung kann der interne Pull-Down Widerstand aktiviert werden.
# GPIO.setup(17, GPIO.IN, pull_up_down = GPIO.PuD_DOWN)
# GPIO.setup(27, GPIO.IN, pull_up_down = GPIO.PuD_DOWN)  


print "-- Interrupt Service Routine. Diese Funktion wird beim Interrupt aufgerufen"
def isrcount17(channel):  
	# Zugriff auf globale Variable
	global count17
	count17 = count17 + 1
	print "Add count17:" + str(count17)

def isrcount27(channel):  
	# Zugriff auf globale Variable
	global count27
	count27 = count27 + 1
	print "Add count27:" + str(count27)


print "-- Interrupt Event anlegen Pin 17, auf steigende Flanke reagieren"
GPIO.add_event_detect(17, GPIO.RISING, callback = isrcount17, bouncetime = 200) 
GPIO.add_event_detect(27, GPIO.RISING, callback = isrcount27, bouncetime = 200) 

# Endlosschleife
print "-- Interrupttest gestartet.  Stop with CTRL-C"
try:
	while True:
		time.sleep(1)
		print "Ist count17:" + str(count17)  + "Ist count27:" + str(count27)
except KeyboardInterrupt:
	print "-- CTRL-C detected. exiting..."
	print "-- Events deregistrieren"
	GPIO.remove_event_detect(17)
	GPIO.remove_event_detect(27)

print "-- GPIO InterruptTest beendet"

Der time.sleep hält nicht die CPu an, sondern nur das Programm, damit der CPu andere Dinge tun kann. Die 200ms "Bouncetime" verhindern einen erneuten Trigger innerhalb der Zeit. Sie können so maximal 5 Events pro Sekunden verarbeiten.

Selbst dann habe ich bemerkt, dass mancher Tastendruck trotzdem zwei Signale auslöst. Das kann aber durchaus an meinem selbstgebauten IOBoard liegen, obwohl ich sauber mit 10k Pulldown/Pullup) arbeite. Vielleicht sollte man doch noch einen 0,1yF-Kondensator an die Leitung klemmen.

Achtung: Auch wenn Sie das Python-Skript im Interpreter "beenden", sind die Interrupts und Serviceroutinen immer noch aktiv bis Sie diese entweder wie im Beispiel deaktivieren oder die Python-Shell beenden.

Weitere Links

Raspberry Pi GPIO - The Easy Way
http://www.youtube.com/watch?v=0i2C3Qagosc