Kampis Elektroecke

SPI

Raspberry Pi

Hier zeige ich, wie das SPI Interface des Raspberry Pi verwendet werden kann. Das SPI Interface stellt neben dem I2C Bus eine weitere Möglichkeit dar, zusätzliche Bausteine oder Mikrocontroller an den kleinen Computer anzuschließen.

SPI aktivieren:

Vor der eigentlichen Softwareinstallation wird erst einmal die bereits vorhandene Software auf dem Raspberry Pi aktualisiert:

$ sudo apt-get update
$ sudo apt-get upgrade

Als nächstes muss das SPI Modul von der Blacklist gestrichen werden. Dazu wird der Befehl

$ sudo nano /etc/modprobe.d/raspi-blacklist.conf

eingegeben und der Eintrag blacklist spi-bcm2708 gelöscht. Danach wird die Datei gespeichert und geschlossen.

Nun kann das Kernelmodul geladen werden:

$ sudo modprobe spi-bcm2708

Da das Modul nach jedem Neustart erneut aktiviert werden muss, ist es ratsam diesen Vorgang zu automatisieren (ich habe den Befehl dafür einfach in die Datei /etc/rc.local geschrieben).

Für die Einbindung des SPI-Moduls in ein Programm verwende ich das Python Modul spidev, welches sich wie folgt installieren lässt:

$ mkdir python-spi
$ cd python-spi
$ wget https://raw.GitHub.com/doceme/py-spidev/master/setup.py
$ wget https://raw.GitHub.com/doceme/py-spidev/master/spidev_module.c
$ sudo python setup.py install

Für die Installation werden noch Python Entwicklungswerkzeuge benötigt, welche ihr ggf. vorher installieren müsst:

$ sudo apt-get install python-dev

Jetzt ist der SPI-Bus einsatzbereit.

Anbindung eines MCP4812:

Der DAC wird wie folgt mit dem Raspberry Pi verbunden:

 

Für den Chip Select des DAC kann wahlweise ein normaler GPIO oder der Pin CE0 bzw. CE1 verwendet werden.

Werden die Pins CE0 oder CE1 verwendet, können die Daten später mit den Python Methoden xfer1() oder xfer1 des spidev-Moduls versendet werden. Wird ein normaler GPIO für den Chip Select verwendet wird, muss dieser händisch auf Masse gezogen werden, was einem aber die Möglichkeit gibt mehr als zwei SPI-Bausteine an das Raspberry Pi anzuschließen. In diesem Beispiel wird der GPIO 25 als Chip Select verwendet.

In der Software muss als erstes der SPI-Bus 0.0 initialisiert werden:

SPI = spidev.SpiDev()
SPI.open(0, 0)

Jetzt können auch direkt schon Daten gesendet werden. Dazu wird zuerst der Chip Select auf Masse gezogen:

GPIO.output(CS, GPIO.LOW)

Nun können die Daten an den DAC gesendet werden. Die Daten werden als Array an die Methode writebytes übergeben. 

SPI.writebytes([23, 255])

Das erste Byte ist das MSB (Most Significant Byte) und das zweite Byte ist das LSB (Least Significant Byte).

Der DAC wird also folgendermaßen konfiguriert:

  • Kanal A
  • GA = 0, d.h. Sel = 2
  • Active mode operation
  • Die restlichen Bits dienen als Spannungswert

Für die Ausgangsspannung des DACs gilt:

V_{Out}=V_{Ref}\cdot Sel\cdot \frac{Spannung}{4096}

Die Werte sind wie folgt vergeben:

  • VRef  = Interne Spannungsreferenz von 2,048 V
  • Sel = 1, bei GA = 0 und 2, bei GA = 1 gesetzt wird.
  • Spannung = Der digitale 10-Bit Wert, der an den DAC übertragen wird.

Nach dem Übertragen der Daten wird der Chip Select wieder auf High gezogen, wodurch die Übertragung beendet wird:

GPIO.output(CS, GPIO.HIGH)

Jetzt kann das Programm gestartet werden. Das Programm läuft einmal durch und konfiguriert den DAC:

Der DAC gibt darauf hin eine Spannung (hier 2,047 V) aus, die so lange gleich bleibt bis ein neuer Wert in den DAC geschrieben wird. 

Das fertige Python-Skript kann in meinem Git-Repository heruntergeladen werden.

20 Kommentare

  1. Hallo !
    Bist Du mit dem SPI Bus weitergekommen ? Ich habe den Portexpander MPC23s17 mit dem Raspberry verbunden. Ich möchte gerne eine Webschnittstelle wie Webiopi verwenden. Hast Du hier eine Lösung ?

    >Schöne Grüße
    Thomas

    1. Hey,

      ja, ich hatte schon ein paar Versuche gemacht (allerdings mit einem DAC).
      Als Programmiersprache habe ich Python benutzt. Brauchst du dabei Hilfe?

      Gruß
      Daniel

  2. Hallo,
    Ich versuche auch an den SPI Bus einen ADC und einen CAN Controller anzuschließen. Habe als CS jedoch die vorgesehenen CS des RPI benutzt. Leider kommt bei mir nichts raus. Deswegen die Frage ob bei dem Befehl
    SPI.open(0, 1)
    der CS automatisch auf Low gezogen wird, oder muss ich das noch manuell machen?

  3. Hallo,

    habe mir im Dez. 2014 einen Raspberry geholt und wollte die SPI-Schnittstelle nutzen. Dabei bin ich hier auf deine Internetseite gestoßen. Ich habe nun alle Schritte bis „$ sudo python setup.py install “ gemacht. Auch den Schritt danach, da er beim setup mit einem Error abbricht. Nun habe ich weiter gesucht und auf manchen Seiten ein Problem mit der SPI-Schnittstelle gefunden. Kann das auch bei mir das Problem sein? D.H. funktioniert das Skript nur bei älteren Modelen?
    Danke!
    DAVE

    1. Hallo Dave,

      kannst du mir einen Screenshot des Fehlers schicken? (am besten per Mail)
      Dann schaue ich mir das mal an. Ich habe bisher noch von keinen SPI Problemen gehört…

      Gruß
      Daniel

      1. Hi Kampi,
        Mit dem ADC MAX192 klappts auch ganz gut :-)
        Datasheet
        (Kostenlose Muster gibt’s bei Maxim für Selbstständige)

        Er misst 0V – 4,095V (Vref) – Kondensatoren am Vref nicht vergessen.
        Er benötigt 5V und daher einen Spannungsteiler an MISO (Master in Slave out)

        Gesendet wird ein Statusbyte res1 = ‚1xxx 1111‘ und 2x ‚0000 0000‘ mit Antwort res2 und res3. Wobei xxx die Wahl des Analogeingangs ist und res2 und res3 den Messwert enthalten.

        Einzig den CE0 GPIO8 kann ich mit spi.xfer2 oder spi.xfer nicht nutzen. Wie kann ich es machen, dass er erst nach den 3 Byte den CE schaltet? Bin Python-Neuling.

        
        #!/usr/bin/python  
        import spidev
        import RPi.GPIO as GPIO
        import time
        CE = 25
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(CE, GPIO.OUT)
        
        # SPI Device oeffnen
        spi = spidev.SpiDev()
        spi.open(0, 0)
        
        while True:
            GPIO.output(CE, GPIO.LOW)    #GPIO 25 auf Low
            resp0 = spi.xfer2([0x8f]) # Status byte channel 0
            #resp = spi.xfer2([0xcf]) # Status byte channel 1
            resp1 = spi.xfer2([0x00]) # 1. byte Teil-Messwert
            resp2 = spi.xfer([0x00]) # 2. byte Teil-Messwert
            GPIO.output(CE, GPIO.HIGH)    #GPIO 25 auf High
            if resp1[0] > 127:
                resp1[0] = resp1[0] - 128
            wert = ((resp1[0] * 8) + (resp2[0] >> 5)) * 0.004  #Messwert aus den 2 Byte xMMM MMMM - MMMx xxxx
            print wert ," V"
            time.sleep(2)
        

        ralphi

        1. Hey Ralphi,

          in diesem Fall musst du den IO einfach händisch schalten, sprich per IO-Lib auf High und Low setzen :)
          Damit sollte das problemlos gehen.

          Gruß
          Daniel

  4. Hallo,
    ich möchte an einem PI3 ein 3.2″ TFT mit Touch sowie einen MCP2515 CAN-Bus Controller anschliessen.

    Nun ist das folgende Problem:
    TFT benutzt SPI.CE0, das Touchpanel benutzt SPI.CE1.
    Kann man mit der o.g. Methode auch einen GPIO als CE für den CAN-Bus Controller verwenden? Da dieser auch einen IRQ Ausgang hat, müsste doch der CE bei ankommenden IRQ auch geschaltet werden, oder?

    Oder gibt es noch einen andere Methode, mehr SPI.CE`s anzulegen?

    Gruß,
    Wolfram

    1. Hallo Wolfram,

      der Raspberry Pi besitzt zwei Chip-Select, die dem SPI zugeordnet sind. Für den SPI kannst du entweder diese Pins oder andere Pins verwenden und diese dann über eine GPIO-Bibliothek schalten. Der Vorteil an CE0 und CE1 (also den festen Chip-Select) ist, dass der Kernel diese entsprechend schaltet. Über das Attribut SPI.no_cs = True kannst du die internen Chip-Select ignorieren und eigene I/O verwenden. Alternativ kannst du den CS auch mit zusätzlichen Logikbausteinen (z. B. Schieberegistern) versehen um diesen zu erweitern. Du musst dann nur entsprechend viele Pulse am CS erzeugen.
      Über den IRQ teilt der MCP dir mit, dass z. B. eine Nachricht eingegangen ist. Du kannst diesen IRQ berücksichtigen um auf eingehende Nachrichten zu reagieren. Dann musst du den MCP bei einem Interrupt entsprechend auslesen und dafür natürlich auch den CS schalten.

      Gruß
      Daniel

  5. Hallo,

    da das TFT und das Touchpad wohl das SPI.CE0 & SPI.CE1 brauchen, wird ein abschalten wohl nicht gut sein.
    mit
    while True:
    GPIO.output(CE, GPIO.LOW) #GPIO 25 auf Low
    can routine...
    GPIO.output(CE, GPIO.HIGH) #GPIO 25 auf High

    sollte es dann so gehen?
    >Über das Attribut SPI.no_cs = True
    gibt es auch solchen Befehl um andere GPIOs zu definieren?

    Oder gibt es die Möglichkeit einer „Software-SPI“ ?

    1. Hallo Wolfram,

      das stimmt wohl. spidev bringt leider keine Möglichkeit mit um andere Pins als CS zu definieren. Du kannst nur den von mir gewählten Ansatz verwenden, sprich z. B. mittels RPi.GPIO den I/O entsprechend schalten und dann per SPI kommunizieren. Der Nachteil ist dann allerdings, dass bereits fertige Treiber, die CE0 oder CE1 voraussetzen nicht funktionieren. Das musst du dann im Treibercode entsprechend anpassen. Wenn du aber den MCP2515 ohne Treiber mit deinem eigenen Code verwenden möchtest, kannst du die Methode so verwenden.
      Alternativ kannst du aber auch einen Software-SPI programmieren. Das geht halt auch, ist nur mehr Arbeit.

      Gruß
      Daniel

  6. ok, danke, da hast Du mir einiges weitergeholfen,
    da ich gerade ein Shield für den PI layoute.
    Da werde ich dann den CE für den MCP2515 gleich auf einen GPIO setzen.
    Da der CAN-Bus nur langsame Daten in unserer Haussteuerung übertragen muss, kann ich diesen auch „von Hand“ bedienen.
    Gruß,
    Wolfram.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert