Über den SPI-Bus einen MCP3008-Analog-Digitalwandler auslesen

Einen Analog-Digital-Wandler hatten wir schon einmal in dem Projekt Über den I2C-Bus des Raspberry Pi einen Analog-Digital-Wandler (PCF8591) ansteuern. Ein AD-Wandler wandelt eine am Eingang anliegende Spannung, die zwischen 0 Volt und der Versorgungsspannung (meist 3.3V) liegt, als digitale Zahl zurück. Je nachdem, wieviel Bit Auflösung ein AD-Wandler ist, sind dies Zahlen zwischen 0 und 255 (8 Bit), 0 und 1023 (10 Bit), 0 und 4095 (12 Bit) und 0 und 65535 (16 Bit). Dementsprechend ist die Feinheit der Messung unterschiedlich. Während z. B. bei 3.3V 8 Bit Wandler nur mit 12,94 mV Genauigkeit messen, sind dies bei 16 Bit 0,05 mV Genauigkeit, also 257 mal genauer.

Die digitale Zahl des AD-Wandlers muss irgendwie übermittelt werden. Dies geschieht als Byte(s) über einen Bus. Beim PCF8591 war es der I2C-Bus mit 2 Datenleitungen. Der MCP3008, der über 10 Bit Auflösung und gleich 8 Eingänge verfügt, benötigt den SPI-Bus.

Der SPI-Bus

Der SPI-Bus benötigt drei Datenleitungen und eine Select-Leitung, über die kommuniziert wird. Diese sind: SPI Server (Raspi) SPI Client 1 SPI Client 2 SCLK (Serial Clock) ---> SCLK SCLK MOSI (Master Out / Slave In) ---> MOSI MOSI MISO (Master In / Slave Out) <--- MISO MISO CS0 (Chip Select 0) ---> CS CS1 (Chip Select 1) ---------------------> CS Der Raspi verfügt über zwei SPI-Busse (0 und 1) mit jeweils zwei CS-Leitungen CS0 und CS1. Diese sind defaultmäßig: Bus 0 Bus 1 SCLK 11 21 MOSI 10 20 MISO 9 19 CS0 8 16 CS1 7 Der Raspi verfügt also nur über 2 Client-Select-Leitungen. Damit lassen sich zwei SPI-Geräte anschließen. Ein drittes könnte man über den 2. Bus anschließen, bräuchte dann aber nochmals 4 Kabel. Da erkläre ich doch spontan den I2C-Bus zum Sieger, der mit nur 2 Kabeln über 100 Geräte ansteuern kann. Aber manche Geräte unterstützen halt nur den SPI-Bus und dann muss man den verwenden. Den Unterschied zwischen den Bussen sollte man beim Bauteile-Kaufe im Hinterkopf behalten, denn oft gibt es SPI und I2C-Versionen. Manche Bauteile unterstützen sogar beides.

Den SPI-Bus auf dem Raspi installieren

Zuerst rufen wir mit sudo raspi-config das Raspberry-Konfigurationsprogramm in der Kommandozeile auf. Unter Interfacing Options finden wir den Eintrag SPI, den wir auswählen und enablen.

Danach sollte mindestens ein Device und ls /dev/spi* gelistet sein. pi@raspberrypi:~ $ ls /dev/spi* /dev/spidev0.0 /dev/spidev0.1 Wenn nicht, ist es Zeit für einen Reboot (sudo reboot).

Danach brauchen wir noch die Libraries für Python: sudo apt-get install -y python-dev python3-dev sudo apt-get install -y python-spidev python3-spidev

Der MCP3008


Kommen wir zum AD-Wandler, dem MCP308 von Microchip. Er verfügt gleich über 8 Analog-Eingänge (sein kleiner Bruder MCP3004 hat übrigens nur 4) und wandelt mit 10 Bit Genauigkeit. Das ergibt Werte zwischen 0 und 1023, die über die SPI-Schnittstelle übertragen werden.

Technische Daten: Anschlüsse: CH0 - CH7: Analog-Eingänge <-- analoge Sensoren VDD --> 3.3V (5V möglich) VRef --> 3.3V (5V möglich) AGND --> GND CLK --> SCLK SPI0 (BCM 11) (orange) DOUT --> MISO (Master IN) (BCM 9) (grün) DIN --> MOSI (Master OUT) (BCM 10) (gelb) CS --> Chip Select CS0 (BCM 8) (braun) DGND --> GND An den linken 8 Pins (Kerbe oben) des MCP3008 stehen uns nun 8 analoge Eingänge zur Verfügung. Bitte darauf achten, hier nicht mehr Spannung anzulegen als 3.3V bzw. VRef. In der nachfolgenden Schaltung habe ich wieder einen Fotowiderstand verbaut, weil sich diese so wunderbar einfach mit einer Taschenlampe überprüfen lassen.

Die Schaltung



Der MCP3008 wird wie oben beschrieben verdrahtet. Hier daran denken Slave OUT = Master IN. Das MISO-Kabel vom Raspi (grün) gehört also an den Data OUT-Port vom MCP3008.

Leider liegen die SPI-Kabel an der GPIO-Leiste des Raspberry Pi ein wenig verteilt. Die CS0-Leitung (manchmal auch mit CE0 auf GPIO Extension Boards bezechnet) liegt auf der anderen Seite.

Sind die vier Leitungen zum MCP3008 gezogen ist der GPIO-Teil auch schon abgefertigt und wir können uns den Sensoren an den Analog-Eingängen am MCP3008 zuwenden.









Der linke untere Pin (Kerbe links) ist Channel 0. Darauf folgen die ganze untere Pin-Reihe lang die anderen Channels bis einschließlich 7.

Ich habe an Channel 0 ein Foto-Widerstand angeschlossen und das andere Ende mit Masse verbunden.

Mit einem zweiten Widerstand von 1 kΩ zu 3.3V vom MCP-Channel 0-Pin realisiere ich einen Spannungsteiler, so dass ein je nach Widerstand des Foto-Widerstands eine andere Spannung an Channel 0 anliegt. Um einen möglichst großen Wertebereich zu bekommen, empfiehlt es sich, den Teilungswiderstand in etwa so groß wie den Normalwiderstand des Fotowiderstands zu wählen.

Da beim Foto-Widerstand der Widerstand mit einfallendem Licht abnimmt, wird die maximale Voltzahl von 3.3V bei absoluter Dunkelheit erreicht. 0V hingegen bei absoluter Helligkeit.



Python-Sourcecode

Die Messergebnisse wollen wir natürlich weiterverarbeiten, um sie z. B. auf dem Always-On-Display anzuzeigen. Dazu schreiben wir ein kleines Python-Programm. Die Installation der benötigten Libraries haben wir ja bereits weiter oben schon erledigt. # -*- encoding: utf-8 -*- # (C) 2018 by Oliver Kuhlemann # Bei Verwendung freue ich mich um Namensnennung, # Quellenangabe und Verlinkung # Quelle: http://cool-web.de/raspberry/ import spidev from time import sleep # damit müssen wir nur noch sleep() statt time.sleep schreiben spi = spidev.SpiDev() # SPI-Bus initialisieren spi.open(0,0) # SPI Bus 0, Client 0 öffnen spi.max_speed_hz=1000000 # Max. Geschw. begrenzen für stabiles Auslesen def readMCP3008(channel): adc=spi.xfer2([1,(8+channel)<<4,0]) wert = ((adc[1]&3) << 8) + adc[2] return wert # --- Ende Funktionen --- Beginn Hauptprogramm ------------------------------------------- while 1: for i in range(0,8): v=readMCP3008(i) print "Channel", i, ": ", v, "Einh. = ", round(v/1023.*3.3,4), "V = ", round(v/10.23,1), "%" sleep (1) print Wir benutzen die SpiDev Library, die wir anfangs importieren und dann initialisieren. Dabei ist wichtig, die maximale Geschwindigkeit herabzusetzen mit der Zeile spi.max_speed_hz=1000000. Bei mir kamen sonst keine Daten an.

In der Funktion readMCP3008(channel) wird das schicken von Daten an den MCP3008 (welcher Channel soll es sein?) und das Empfangen des Ergebnisses bewerkstelligt. Diese Funktion führen wir dann in einer Schleife aus und geben die jeweiligen Werte der 8 Channel aus. Das sieht dann in etwa so aus:
pi@raspberrypi:~ $ python mcp3008-spi.py Channel 0 : 928 Einh. = 2.9935 V = 90.7 % Channel 1 : 0 Einh. = 0.0 V = 0.0 % Channel 2 : 0 Einh. = 0.0 V = 0.0 % Channel 3 : 1 Einh. = 0.0032 V = 0.1 % Channel 4 : 2 Einh. = 0.0065 V = 0.2 % Channel 5 : 11 Einh. = 0.0355 V = 1.1 % Channel 6 : 34 Einh. = 0.1097 V = 3.3 % Channel 7 : 58 Einh. = 0.1871 V = 5.7 % An Channel 0 hängt unserer Foto-Widerstand. Der vermeldet: 90.7% Dunkelheit bzw. 9.3% Helligkeit. Halte ich den Sensor zu, geht der Wert auf über 1000 und blende ich den Sensor mit der Taschenlampe, fällt der Wert auf unter 150 Einheiten. Die anderen Channels zeigen irgendwelche kleinen Werte an - Luftspannung sozusagen - und haben keine weitere Aussagekraft. Hier kann ich später noch weitere Sensoren anschließen.