Arduino Mikrocontroller Tutorials - Schnittstellen

SPI Bus

SPI Busaufbau

Die SPI Schnittstelle (Serial Peripheral Interface) ist ein Bussystem welches wie I2C auf Platinenebene, also für kurze Entfernungen zwischen den Teilnehmern verwendet wird. Meist wird SPI für die Kommunikation zwischen einen Mikrocontroller (Master) und einem oder mehrerer Sensoren (Slaves) verwendet. Der Bus wird durch eine Clock-Leitung (SCLK) vom Master synchronisiert, d.h. die Übertragungsrate muss nicht vorher zwischen den Teilnehmern abgestimmt werden.

Als Minimum werden zwei Leitungen benötigt. Die SCLK Leitung und die MOSI (Master Out Slave In) Leitung für die Datenübertragung vom Master zum Slave. Sollen auch Informationen vom Slave empfangen werden ist dafür eine dritte Leitung notwendig (MISO). Mit je einer Leitung für die beiden Kommunikationsrichtungen ist SPI voll Duplexfähig (= Daten können in beide Richtungen gleichzeitig übertragen werden). Soll mit mehreren Slaves kommuniziert werden müssen diese über eine vierte Leitung adressiert werden. Zu jedem Slave geht dann vom Master eine CS (Chip Select) Leitung. Wenn der Master diese CS Leitung auf LOW leg ist der Slave adressiert. SPI funktioniert nur mit einem Master.

Alternative Pin-Bezeichnungen:
MOSI = COPI (Controller OUT Peripheral In) = DOut* = SDO*
MISO = CIPO = Din* = SDI*
CS = SS (Slave Select)
* Eingänge heißen bei Master und Slave unterschiedlich

SPI Protokoll

SPI Busaufbau

Vor einer SPI Übertragung muss man zunächst zwei Werte definieren. Die Clock Polarity (CPOL) and die Clock Phase (CPHA). CPOL gibt den Leerlaufzustand des Clock-Signals an. Bei Idle HIGH ist CPOL=1), bei Idle LOW ist CPOL=0. CPHA definiert den Trigger bei dem der Wert auf der Datenleitung übernommen wird. Bei der ersten Clock-Flanke (CPHA = 0) oder bei der zweiten Clock-Flanke (CPHA = 1). Ob es sich bei der ersten oder zweiten Flanke um eine steigende oder fallende handelt ist vom Leerkaufzustand (CPOL) abhängig. Die CPOL und CPHA Einstellung sind in vier Modi zusammengefasst. Meist initialisiert man den Master mit dem entsprechenden Modi gemäß den Anforderungen des Slaves.

Modus CPOL CPHA
0 0 (LOW) 0 (steigende Flanke)
1 0 (LOW) 1 (fallende Flanke)
2 1 (HIGH) 0 (fallende Flanke)
3 1 (HIGH) 1 (steigende Flanke)

Im Folgenden gehen wir davon aus, dass wir nach Modus 3 kommunizieren:

SPI Busaufbau

Der Master zieht die CS Leitung des Slaves, mit dem kommuniziert werden soll, auf LOW. Danach beginnt mit dem Clock-Signal auch gleich die Kommunikation. SPI hat kein Overhead wie Start- oder Stopp-Bits. Es werden nur Nutzdaten gesendet. Noch vor der ersten steigenden Flanke des Clock-Signals liegt auf der Datenleitung das nächste Bit an. Bei der steigenden Flanke wird dieses dann vom Slave in ein Buffer-Register geladen. Danach wird auf der Datenleitung der Pegel des nächsten Bits angelegt, welches bei der nächsten Steigenden Flanke dann wieder vom Slave übernommen wird. Die Bit-Anzahl ist bei SPI nicht definiert sondern muss an die Anforderungen des Slaves individuell abgestimmt werden.

SPI Übertragung mit der Bibliothek SPI.h

SPI Busaufbau

SPI.begin()

Initialisiert den SPI Bus indem SCK, MOSI und SS als Ausgänge definiert werden (SS ist hier der Adress-Pin wenn der Arduino als Slave verwendet wird)

SPI.setDataMode(SPI_MODE3)

Legt den Übertragungsmodus anhand der oberen Tabelle fest
(SPI_MODE0, SPI_MODE1, SPI_MODE2 oder SPI_MODE3)

SPI.setClockDivider(SPI_CLOCK_DIV64)

Definiert die Geschwindigkeit des Clock-Signals. Ausgehend von der µC-Taktfrequenz von 16Mhz sind folgende Geschwindigkeiten möglich:
SPI_CLOCK_DIV2(8 MHz)
SPI_CLOCK_DIV4 (4 MHz)
SPI_CLOCK_DIV8 (2 MHz)
SPI_CLOCK_DIV16 (1 MHz)
SPI_CLOCK_DIV32 (500.000 KHz)
SPI_CLOCK_DIV64 (250.000 KHz)
SPI_CLOCK_DIV128 (125.000 KHz)

SPI.setBitOrder(MSBFIRST)

Definiert die Reihenfolge in welcher die Bits übertragen werden
(MSBFIRST oder LSBFIRST)

SPI.transfer(), SPI.transfer16()

Bei SPI werden Daten simultan gesendet und empfangen. Ein Bit wird hardwareseitig aus einem SISO Schieberegister gesendet und das gleichzeitig empfangene Bit wird eingehend von der anderen Seite des Schieberegisters gespeichert. Nach 8 Bit entspricht der Inhalt des Schieberegisters dem empfangenen Byte und wird von der Funktion zurückgegeben und bei Bedarf dann in einer Variable gespeichert.
Mit SPI.transfer(buffer, size) kann ein ganzes Array zum Senden übergeben werden.

Programmcode

// SS/CS=5, MOSI=11, MISO=12, SCLK=13 #include "SPI.h" #define CS 5 void setup() { pinMode(CS, OUTPUT); digitalWrite(CS, HIGH); SPI.begin(); SPI.setDataMode(SPI_MODE3); SPI.setClockDivider(SPI_CLOCK_DIV64); SPI.setBitOrder(MSBFIRST); } void loop() { delay(500); // falls gleichzeitig die Werte der MISO Leitung gespeichert werden sollen digitalWrite(CS, LOW); byte empfangen = SPI.transfer(B00001001); digitalWrite(CS, HIGH); // falls nur gesendet werden soll digitalWrite(CS, LOW); SPI.transfer(B00001001); digitalWrite(CS, HIGH); // falls ein ganzes Array gesendet werden soll byte zumSenden[] = {1, 5, 8, 9, 64}; digitalWrite(CS, LOW); SPI.transfer(zumSenden, 5); digitalWrite(CS, HIGH); }