Raspberry Pi Hardware en I/O

 Ps: dit is de hardware pagina, zie ook mijn algemene Raspberry Pi pagina!

Raspberry Pi Logo2012: de Raspberry Pi, de $25 Linux computer? OK, de zaak ligt iets genuanceerder, maar het is toch een heel interessant project. Oorspronkelijk is dit project in de UK opgezet om kinderen op scholen weer in aanraking te laten komen met 'echte computers' in plaats van kant-en-klare kastjes. En, juist om ook weer met hardware te kunnen spelen is de Raspberry Pi heel geschikt.

Deze pagina laat wat mogelijkheden zien van I/O van de originele Raspberry, zoals de P1 uitbreidingsconnector van de originele RaspberryPi B. Zie ook het officiële schema van de Raspberry Pi (en de errata). De latere versies van de Raspberry hebben extra pinnen; even opletten dus welke versie je hebt.

Work in progressIk heb ondertussen ook een Raspberry 3B, maar heb deze pagina daar niet volledig op aangepast (werk in uitvoering). Voor de 40-pins connector op de 3 kan je kijken op de eLinux RPi Low-level peripherals pagina's. Let op, er zijn subtiele verschillen!

Raspberry Pi board

P1 is de header linksboven

Er zijn een aantal 'general-purpose I/O' pinnen (GPIO's) die je als input of als output vrij kunt programmeren, plus een aantal pinnen met speciale functies (die je overigens ook als general purpose kunt gebruiken). De pinnen met speciale functies maken het mogelijk complexere modules aan te sturen, bijvoorbeeld via I2C: een 2-draads (plus 2 voor voeding) bus-systeem om bijvoorbeeld temperatuursensors, lcd's en zo aan te sturen.

Welke versie?

Er zijn verschillende revisies van de Raspberry, met verschillen in de I/O!!! Deze pagina is voor de Raspberry Pi 1 model B, revisie 1 (van voor oktober 2012) met maar 256 MB geheugen. /proc/cpuinfo onthult de versie; 000f is versie 1.

Ondertussen is er ook een B+ versie met extra I/O, en zelfs al een quad-core Raspberry Pi 2 en Raspberry Pi 3, voor dezelfde prijs.

Je kan de general-purpose I/O pinnen op de header P1 op diverse manieren benaderen, zoals vanuit C of Python, of zelfs als files vanuit bash scripts. De header P1 is te zien in de linkerbovenhoek van de foto hiernaast. Er zijn kant-en-klare printjes voor te koop om er op aan te sluiten en makkelijk te kunnen experimenteren, zoals het officiëuze GertBoard en deze goedkope prototype-bordjes, maar ik heb even zelf een printje gemaakt waarop ik de belangrijkste pinnen op aparte connectoren heb gezet (gpio, i2c, serial; zie de foto verderop).

Raspberry 1B: I/O connector P1

Hier als referentie de 26-polige pin-out van de P1 connector op de originele 256 MB B-versie (zie deze eLinux pagina voor de 40-polige versie), met zowel de Broadcom datasheet namen (BCM) als de Raspberry Pi namen. Meer details op de eLinux RPi BCM2835 GPIO pagina. Let op: hoewel er een +5 Volt pin op de connector zit, zijn alle I/O's 3.3 Volt, en kunnen niet tegen 5 Volt!! Max stroom per pin is blijkbaar instelbaar van 2 tot 16 mA, maar kan de details niet vinden. GPIO pinnummering (BCM of Raspberry) is afhankelijk van je programmeertaal, zie onderstaande tabel.

Functie
BCM
Raspberry
Pin
Pin
Raspberry
BCM
Functie
50 mA max!
+3V3
+3V3
1
2
+5
+5V

I2C0_SDA (*1)
GPIO0 SDA0
3
4
--
--

I2C0_SCL (*1)
GPIO1
SCL0
5
6
GND
GND

GP CLK0
GPIO4
GPIO_GCLK
7
8
TXD0
GPIO14
115200 bit/s

--
--
9
10
RXD0
GPIO15
115200 bit/s

GPIO17
GPIO-GEN0
11
12
GPIO-GEN1
GPIO18
PCM_CLK / PWM0
PCM-OUT
GPIO21 (*2)
GPIO-GEN2
13
14
--
--


GPIO22
GPIO-GEN3
15
16
GPIO-GEN4
GPIO23


--
--
17
18
GPIO-GEN5
GPIO24

SPI-0 MOSI
GPIO10
SPI_MOSI
19
20
--
--

SPI-0 MISO
GPIO9
SPI_MISO
21
22
GPIO_GEN6
GPIO25

SPI-0 SCLK
GPIO11
SPI_SCLK
23
24
SPI_CE0_N
GPIO8
SPI-0 CE0

--
--
25
26
SPI_CE1_N
GPIO7
SPI-0 CE1

(*1): I2C1_SDA/GPIO2 en I2C1_SCL/GPIO3 op revisie-2 borden!
(*2): GPIO27/TMS op revisie-2 borden! Op een revisie-2 bord zit bovendien een extra GPIO connector met 4 extra GPIO's (28-31).

Raspberry 3: Extra pinnen

Functie
BCM
Raspberry
Pin
Pin
Raspberry
BCM
Functie

GPIO0
ID_SD
27
28
ID_SC
GPIO1


GPIO5
29
30
GND
GND


GPIO6

31
32

GPIO12


GPIO13

33
34
GND
GND


GPIO19

35
36
CE#2
GPIO16


GPIO26
MISO
37
38
MOSI
GPIO20


GND
GND
39
40
SCLK
GPIO21

GPIO low-level aansturen

GPio vanuit andere talen?

Op deze RPi Low-level peripherals pagina ook voorbeelden van GPIO vanuit andere talen, zoals Perl, C, C#, Java en Ruby

Ook op die pagina een hulpprogramma om vanuit bash op een meer praktische manier met de gpio om te kunnen gaan.

GPIO vanaf de bash prompt

Als allereerste experiment: een simpel voorbeeld van GPIO aansturing vanuit bash-scripts. Ik heb hiervoor een LED op GPIO 1 aangesloten met een serie-weerstand van 680 Ohm tussen pin P1-12 (gpio-1) en P1-6 (ground). GPIO 1 (volgens de Raspberry nummering, gpio18 volgens de BCM nummering) kan ik zo hoog en laag te maken, en zo de LED aan en uit zetten. Lezen kan op een vergelijkbare manier (met b.v. cat). Let op: gebruik hierbij de pinnummering zoals Broadcom ze voor de BCM aangeeft!

kees@raspberrypi ~ $ sudo -i
root@raspberrypi ~ # echo "18" >/sys/class/gpio/export
root@raspberrypi ~ # echo "out" >/sys/class/gpio/gpio18/direction
root@raspberrypi ~ # echo "1" >/sys/class/gpio/gpio18/value; # LED gaat aan
root@raspberrypi ~ # echo "0" >/sys/class/gpio/gpio18/value; # LED gaat weer uit
root@raspberrypi ~ # echo "1" >/sys/class/gpio/gpio18/value

GPIO vanuit Python

Eerst éénmalig installeren van de python headers en de gpio library (download van PiPy):

sudo apt-get install python-dev
wget http://pypi.python.org/packages/source/R/RPi.GPIO/RPi.GPIO-0.3.1a.tar.gz
tar zxf RPi.GPIO-0.3.1a.tar.gz
cd RPi.GPIO-0.3.1a
sudo python setup.py install
cd ..
sudo rm -rf RPi.GPIO-0.3.1a/
rm RPi.GPIO-0.3.1a.tar.gz

Dan kan je vervolgens in een python-script of vanaf de prompt de gpio aan, voorbeeld:

kees@raspberrypi ~ $ sudo python
Python 2.7.3rc2 (default, May 6 2012, 20:02:25)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import RPi.GPIO as GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setup(18, GPIO.OUT)
>>> GPIO.output(18, True)
>>> GPIO.output(18, False)
>>> ^D

I2C

Let op!

Er zijn twee I2C bussen op de BCM2835 chip, op een revisie-1 B-board is bus 0 de bus op de P1 connector. Op een revisie-2 bord en op de PI 2 en 3, is dit bus 1!!

En: 'I2C clock stretching' is niet goed ondersteund op de BCM283x chips (dus voor alle Raspberries), zie hier en hier!!!

Op de Raspberry zit (zoals op zo veel systemen) een I2C bus (vergelijkbaar met de SMB bus). Op deze 2-pins (plus ground/voeding...) bus kan je ook je eigen hardware aansluiten (tot max 128 I2C chips). Het is een ideale manier om bijvoorbeeld extra I/O pinnen te krijgen met behulp van een PCF8574A chip (8 extra I/O pinnen). Ook bijvoorbeeld LCD displays, temperatuursensors, real-time clock, A/D converters en dergelijken kunnen via I2C worden aangesloten. Plus, je kan voor speciale taken je 'eigen I2C-chips' maken voor een paar Euro's, bijvoorbeeld voor stappenmotoren aan te sturen. De (1K8) pull-up weerstanden voor I2C zitten al op de Raspberry, hoeven dus niet extern te worden aangesloten.

I²C aansturen is in het begin wat lastig, omdat de I2C modules niet standaard in Raspbian geladen zijn. Op de Raspberry Pi 1 is de i2c-bcm2708 module blacklisted in /etc/modprobe.d/raspi-blacklist.conf, eerst deze file editen en het commentaarteken # voor de regel met i2c-bcm2708 zetten. Denk dat daarna een reboot nodig is. Daarna ook de I2C-dev module laden met sudo modprobe i2c-dev. Dit is overigens een tijdelijke oplossing, meer definitief is te zorgen dat deze module in /etc/modules opgenomen is (i2c_bcm2708 wordt al automatisch geladen als de blacklist verwijderd is). Daarnaast zal je ook de I2C tools moeten installeren: sudo apt-get install i2c-tools .

Voor de Rasperry Pi 3 kan je met sudo raspi-config de I2C aanzetten en ook zorgen dat de drivers geladen worden, niet meer nodig om in blacklist-files en zo te spitten.

PCF8574 voor extra I/O

Ik heb het hiernaast staande schema'tje met een PCF8574A (kost zo'n € 2) op een stukje gaatjesprint gemaakt, en met een flatcable aangesloten op de I2C-pinnen van de Raspberry. Op de print zitten ook twee LEDs en een switch. Het was nog even lastig de PCF te 'vinden' op de bus; het adres op de bus was een bit naar rechts verschoven t.o.v. de documentatie van de PCF8574A; dus 0x38 in plaats van 0x70. Dank zij i2cdetect -y 0 (uit i2c-tools) was hij echter snel gevonden:

I2C testschakeling met PCF8574Akees@raspberrypi ~ $ i2cdetect -l
i2c-0    unknown     bcm2708_i2c.0      N/A
i2c-1    unknown     bcm2708_i2c.1      N/A
kees@raspberrypi ~ $ sudo chmod o+rw /dev/i2c-0
kees@raspberrypi ~ $ i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- 38 -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Connectorprint en PCF8574 via I2C
Connectorprint en PCF8574 via I²C

Standaard is de I²C-poort niet voor iedereen toegankelijk (kan hardware aansturen die de werking van het systeem kan verstoren, zoals op een PC). Hier hebben we de I2C-bus zelf in de hand, dus om niet steeds sudo voor een commando te moeten zetten is het handig de poort world-access te geven: sudo chmod o+rw /dev/i2c-0 . Met het commando i2cget -y 0 0x38 0xFF (ook uit i2c-tools) worden de uitgangen op 0xFF (allemaal hoog) gezet, en vervolgens teruggelezen. Afhankelijk van de toestand van de switch komt er nu 0x7F (ingedrukt) of 0xFF (los) terug. Om de LEDs aan te zetten moeten de bijhorende uitgangen laag gemaakt worden: i2cget -y 0 0x38 0xFC. Nog wat pcf8574-voorbeeldjes (python, C). Hier de one-liner voor een wissel-knipperlicht totdat de knop wordt ingedrukt:

while [ "0xfe" = `i2cget -y 0 0x38 0xfe` ]; do sleep 1; i2cget -y 0 0x38 0xfd; sleep 1; done

De LM75 op mijn vingertopTemperatuursensor LM75

Een ander leuk I2C blokje is de temperatuursensor LM75. Ik heb de low-cost NXP LM75BD, met een resolutie van 1/8 graad (11-bit) en een maximum onnauwkeurigheid van ±2 ºC van -25ºC tot +100ºC, en de mogelijkheid iets aan en uit te schakelen op instelbare temperatuurpunten via een aparte uitgang. Nauwkeurigheid wordt wat groter door calibratie; in ijswater wisselde dit bij mij tussen de 0 °C en de 0.375 °C (met chips uit dezelfde serie) dus zeg max een halve graad afwijking.

LM75BD temperatuursensor pinout in SO8De LM75BD zit in een SO8-behuizing (5x5 mm). Dit is zo'n beetje de kleinste die ik nog zo aan kan om uit de hand te solderen. 5 dunne draadjes aangesoldeerd plus een 100 nF condensator over Vcc (3.3 .. 5V) en aarde. De draden door een stukje isolatie geregen (van een andere draad gestript) samen met een stukje dun enkel-kern draad voor de mechanische stevigheid, en ik had de montage zoals hiernaast op mijn vingertop vergroot te zien is. Hier is later nog een druppel twee-componentenlijm omheen gegaan voor bescherming.

I2C in Python

Een project dat een I2C-library voor Python gemaakt heeft is Quick2wire, een voorbeeldtoepassing is deze temperatuursensor en A/D

Bij het aan aarde leggen van de 3 adrespinnen A0..A2 meldt de chip zich keurig met i2cdetect -y 0 op adres 48. Uitlezen van het eerste byte van de temperatuur ( i2cget -y 0 0x48 0x00 leest register 0) geeft 0x15 (21 °C), wat oploopt naar 0x20 (32 °C) als ik de sensor tussen mijn vingers houd. Uitlezen van de twee temperatuursetpoints (register 2 en 3) geven de juiste waarden: 75 en 80 °C. Een setpoint instellen gaat eenvoudig: de bovengrens verhogen van 80 naar 96 graden (0x60 hexadecimaal) kan met i2cset -y 0 0x48 0x03 0x60. Wil je meer, zoals alle 11 bits van de temperatuur uitlezen, dan komt er toch een programma'tje bij kijken (voorbeeld lm75 uitlezing).

Hieronder een grafiekje van de temperatuur in de kamer (meting per kwartier), waarbij in de loop van de week de buitentemperatuur flink naar beneden ging.De eerste dagen was het zonnig, en daalde de temperatuur zelfs 's nachts nauwelijks onder de 20 graden. Daarna snel kouder, overdag hield de verwarming de zaak op 20 graden (thermostaat aan andere kant van de kamer) afgezien van wat werkzaamheden waarbij de deur veel open moest, 's nachts koelde het snel af omdat het buiten maar iets boven de nul was. De hakkeltjs zijn wat eigenaardig: soms lijkt het of de LM75BD niet helemaal goed uitleest, en een update van de waarde doet tussen het lezen van de high- en de low-byte in... Heb ik later in software aangepast.

Temperatuurmeting
Kamertemperatuur gedurende een week


Temperatuur laatste 24 uur
Metingen drie sensoren over 24 uur

En nog een plaatje van meer recente datum, geplukt van de webview waarmee ik de resultaten op afstand kan bekijken (gebruik makend van Google Charts). Data is over 25 uur, met metingen om het uur.

Aan subtiele veranderingen in de temperatuur kan je heel wat aflezen: bijvoorbeeld de kamertemperatuur is mooi constant, maar je ziet iets variatie

  • de sensor 'Kamer' hangt te dicht bij de TV, scheelt ongeveer een graad
  • De sensor voor 'Zolder' zit op de warmwaterleiding, je ziet wanneer er warm water wordt gepakt. Dit wordt gebruikt door het home control system om bijvoorbeeld de afzuiging in de badkamer aan te zetten

Als je dit weet, en je weet het leefritme, kan je hier weer je systeem op laten reageren.

Specifieke PCF8574 driver werkt niet?


LM75BD sensor list

Sensor
Addr
0 °C uitlezing
Use
0
0x48
0.38 °C Test
1
0x49
0.00 °C Test
2
0x4A
0.25 °C D1
3
0x4E
0.25 °C AZ1
4
0x49
0.25 °C CV1
5
0x4D
0.00 °C CV2
6
0x4E
0.12 °C CV3
7
0x4F
?? °C Ext

De PCF8574 zou controle nog eenvoudiger moeten maken... Helaas lukt het niet de driver voor de PCF8574 te laden omdat deze niet standaard in de compiler is meegecompileerd (modprobe pcf857x geeft een fout). Ik kreeg van Jarno Kamminga de volgende extra informatie hierover:

Er bestaat een kernel module genaamd gpio-pcf8574x, standaard is deze niet meegeleverd met bijvoorbeeld Raspbian wheezy en het vereist wat werk om deze te compileren, maar als deze gecompileerd is kan je hem laden met "modprobe gpio-pcf8574x". Zodra de driver geladen is kan je met "echo pcf8574 0x38 > /sys/class/i2c-adapter/i2c-1/new_device" de IC toevoegen aan I2C, dit zorgt ervoor dat je vervolgens de poorten/pins kan benaderen via de sysfs interface, zoals jij toont in "GPIO vanaf de bash prompt".

Zodra je het nieuwe I2C apparaat geladen hebt wordt er een nieuwe GPIO chip gecreerd (/sys/class/gpio/gpiochip248). Je kan nu de acht GPIO pinnen van de PCF8574 benaderen als GPIO248 tot GPIO255. Bijvoorbeeld:

kees@raspberrypi ~ $ sudo -i
root@raspberrypi ~ # echo "248" >/sys/class/gpio/export
root@raspberrypi ~ # echo "out" >/sys/class/gpio/gpio248/direction
root@raspberrypi ~ # echo "1" >/sys/class/gpio/gpio248/value; # LED gaat aan
root@raspberrypi ~ # echo "0" >/sys/class/gpio/gpio248/value; # LED gaat weer uit
root@raspberrypi ~ # echo "1" >/sys/class/gpio/gpio248/value

Voor het cross-compileren van de kernelmodule kan je terecht op deze pagina: elinux.org/RPi_Kernel_Compilation. Met rpi-update zorg je ervoor dat je de laatste versie van de kernel geinstalleerd hebt. Nadat je rpi-update hebt uitgevoerd moet je de rpi herstarten om de nieuwe kernel te laden. Je hebt dan dezelfde kernel geladen als de broncode die je uitcheckt bij git://github.com/raspberrypi/linux.git.Voer na het uitvoeren van "oldconfig" het volgende uit en selecteerd de kernel module voor de driver om ervoor te zorgen dat deze ook gecompileerd wordt:

make ARCH=arm CROSS_COMPILE=${CCPREFIX} menuconfig

Seriële poort

Boot-voortgang en Console

Op de P1 connector zit ook een seriële poort, dat wil zeggen een RxD en TxD pin (receive en transmit data, respectievelijk pin 10 en pin 8). Deze pinnen zijn niet compatibel met een RS-232 seriële poort op een PC/laptop, maar zijn geïnverteerd en slechts 3.3 Volt (in plaats van de plus en min 5 .. 25 Volt van een echte RS232). Ik wilde hem toch aan de pC hangen om te testen, en had nog een FTDI serieel-naar-USB converter liggen (niet precies die, maar ok), die dat aan kan; alternatief kan je bijvoorbeeld met een MAX232 naar echte RS-232 signalen (klassieke serial port) over gaan voor oudere computers.

Door nu op de PC met bijvoorbeeld GtkTerm de verbinding op te zetten (met mijn FTDI-oplossing, poort /dev/ttyUSB0, baudrate 115200) krijg je een 'console'-scherm te zien waarop je ook op de Raspberry Pi kunt inloggen (in plaats van bijvoorbeeld via Ethernet en SSH). Ook krijg je hier bij het booten het hele boot-process te zien, handig als je iets overhoop gehaald hebt en het systeem niet meer lijkt te starten.

Poort in eigen programma's

Maar wat nu als je de seriële poort voor iets anders wilt gebruiken, bijvoorbeeld voor aansturing van een serieel home control system? Hoe koppel je de poort los van de console-software? Dit staat beschreven op "Using the Raspberry Pi's serial port". In het kort twee stappen, aanpassen van /boot/cmdline.txt (verwijder console=ttyAMA0,115200 kgdboc=ttyAMA0,115200) om tijdens booten de poort niet te gebruiken, en aanpassen van /etc/inittab (comment out de regel met 2:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100) zodat de console niet wordt herstart op de seriële poort. Dan kan je bijvoorbeeld op de Raspberry minicom installeren om de verbinding te testen.

Minicom commands

De basis-commando's:
^a B : scrolling back in buffer
^a Q : quit (no reset)
^a X : eXit and reset
^a Z: help

sudo apt-get install minicom
sudo minicom -b 9600 -o -c on -D /dev/ttyAMA0
# of: sudo chmod 666 /dev/ttyAMA0; minicom -b 9600 -o -D /dev/ttyAMA0

Met mijn serial-naar-USB converter op mijn PC aangesloten en daar GtkTerm gestart (/dev/ttyUSB0, baudrate 9600), werkt! (Ps: kan nodig zijn om de hardware flow control uit te zetten als lijnen als DSR niet aangesloten zijn: start minicom met ook de -s parameter, ga naar 'Serial Port Setup', keuze F -> No). De poort is nu dus vrij voor eigen applicaties. Uitleg over gebruik van de seriële poort in Linux is onder andere te vinden op "Serial Port Programming in Windows and Linux". En een voorbeeld van UART programming op de Raspberry helpt ook.

Serial port op de raspberry Pi 3

Helaas ligt de zaak met de Raspberry Pi 3 wat moeilijker. Dit komt omdat standaard de ttyAMA0 UART poort voor Bluetooth BLE gebruikt wordt... In plaats daarvan is er nu een software UART ('mini UART' ttyS0) aan pin 8 (TxD) en 10 (RxD) geknoopt. Om de UART op de oude manier te gebruiken moet je een 'device tree overlay' genaamd pi3-miniuart-bt installeren, die de UART op de ouderwetse manier gebruikt. Op Raspberry Pi 3 UART Boot Overlay staat dit beschreven. Let op, hangt af van welke Raspbian release je gebruikt! Als je nu pin 8 en 10 met elkaar verbindt, en minicom start zoals hierboven beschreven, moet elk karakter dat je typt ook weer op het scherm te zien zijn --> ja, lukt!

Uiteraard werkt nu Bluetooth niet meer, maar op dit moment is voor mij de seriële poort belangrijker. De software UART heeft ook als nadeel dat bij verandering van de kloksnelheid (bijvoorbeeld als de Raspberry warm wordt) de UART klok ook verandert, waarmee je dus je verbinding kwijt raakt. Ook dit is weer te corrigeren, bijvoorbeeld door de kloksnelheid vast te zetten (en /lib/systemd/system/hciuart.service bij te werken, zoek in de overlay GitHub readme), maar dit is voor mij van later zorg.

Ps: uitschakelen van BT en/of Wifi wordt in deze post behandeld: How to disable the Pi3's WLAN & Bluetooth ?

Raspberry Temperatuur-sensor

De Raspberry heeft overigens ook een ingebouwde temperatuursensor, en ook spanningssensoren etc; uit te lezen met de VideoCore driver VCHIQ.

sudo /opt/vc/bin/vcgencmd measure_temp; # geeft b.v.: temp=45.6'C (rust, 18°C kamer)
sudo /opt/vc/bin/vcgencmd measure_volts; # volt=1.2000V

Hier meer informatie over beschikbare vcgencmd commando's. Om dit ook als gewone gebruiker te kunnen doen moet je wat extra instellingen doen (en een reset):

sudo echo 'SUBSYSTEM=="vchiq",GROUP="video",MODE="0660"' > /etc/udev/rules.d/10-vchiq-permissions.rules
sudo usermod -a -G video kees; # zelf toevoegen aan de groep 'video'