Raspberry Pi Hardware en I/O

Raspberry Pi Logo 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.

De beginners-presentatie gehouden op 21 augustus 2012 bij EmSE (Embedded Systems Eindhoven, een hobby-club hier) over mijn bevindingen met de Raspberry Pi is hier te downloaden: EmSE_RaspberryPi.pdf

Deze pagina gaat met name in op de I/O mogelijkheden op de P1 uitbreidingsconnector van de Raspberry Pi. Zie ook het officiële schema van de Raspberry Pi (en de errata). De laatste versies van de Raspberry schijnen nog wat extra pinnen te hebben; even oppassen dus welke versie je hebt.

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!!! Ik heb revisie 1 (van voor oktober 2012). /proc/cpuinfo onthult de versie; 000f is versie 1.

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).

I/O connector P1

Hier als referentie de pin-out van de P1 connector, met zowel de Broadcom datasheet namen (BCM) als de Raspberry Pi namen. Meer details op de 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).

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 rivisie-1 board is bus 0 de bus op de P1 connector. Op een revisie-2 bord is dit bus 1!! En er schijnen meer revisies te zijn...

En: 'I2C clock stretching' is niet goed ondersteund op de BCM2835 chip!!!

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.

I²C aansturen is in het begin wat lastig, omdat de I2C modules niet standaard in raspbian geladen zijn. De i2c-bcm2708 module is 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 .

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 warde doet tussen het lezen van de high- en de low-byte in...

Temperatuurmeting


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

De PCF8574 zou controle nog eenvoudiger moeten maken... Laden van de PCF8574 driver (run het met sudo sh -c '...commando...' omdat sudo direct voor een echo naar een systeemfile niet werkt, of start een root terminal met sudo lxterminal &): met ls wordt het device zichtbaar op bus 0 adres 0x38. Maar wat ik er dan mee moet (link1, link2)? Volgens mij is de driver voor de pfc857x niet in de kernel aanwezig (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). Deze pinnen zijn niet compatibel met een RS-232 seriële poort op een PC/laptop, maar zijn geïnverteerd en 3.3 Volt. Ik had nog een FTDI serieel-naar-USB converter liggen (eigenlijk niet 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 met bijvoorbeeld GtkTerm de verbinding op te zetten (met mijn FTDI-oplossing, poort /dev/ttyUSB0, baudrate 115200) krijg je een console waarop je ook op de Raspberry Pi kunt inloggen (in plaats van bijvoorbeeld via Ethernet en SSH), en ook het hele boot-process kan volgen, handig als je iets overhoop gehaald hebt en het systeem niet meer start.

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? 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 minimon 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 -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! 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 hier een voorbeeld van UART programming op de Raspberry.

Later verder, als ik wat meer tijd heb...