SPI-Protokoll-Architektur & Hochgeschwindigkeits-Peripherie
Wenn die Bandbreitenbegrenzung des 2-Draht-I2C-Busses (üblicherweise 100 kHz bis 400 kHz) für eine Anwendung nicht mehr ausreicht, bietet das Serial Peripheral Interface (SPI) synchrone Vollduplex-Datenübertragung im Multi-Megahertz-Bereich. Ursprünglich von Motorola entwickelt, ist SPI der Standard für datenintensive Peripherie: Analog-Digital-Wandler (ADCs), schnelle Flash-Speicher, hochauflösende TFT- und E-Paper-Displays, Ethernet-Controller (ENC28J60) und CAN-Bus-Controller (MCP2515).
Da SPI mit aktiven Push-Pull-Treibern und dedizierten Chip-Select-Leitungen (CE) arbeitet, entfallen die verlangsamenden RC-Ladezeiten offener Leitungen sowie der Adress-Overhead im Datenstrom.
1. SPI-Bus-Topologie & Signalleitungen
Standard-SPI setzt auf eine 4-Draht-Topologie in einer Controller/Target-Konfiguration (früher Master/Slave):
%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
subgraph Controller["Raspberry Pi (Host-Controller)"]
direction TB
BUS_LINES["Gemeinsame SPI-Leitungen:<br/>SCLK (GPIO 11) + MOSI (GPIO 10) + MISO (GPIO 9)"]
CE_LINES["Getrennte Chip-Selects:<br/>CE0 (GPIO 8) & CE1 (GPIO 7)"]
BUS_LINES --- CE_LINES
end
subgraph Dev0["Peripherie 0: MCP3008 ADC (Aktiv bei CE0 LOW)"]
direction TB
P0_IO["CLK + DIN + DOUT"]
P0_CS["CS0 (Select-Leitung)"]
P0_IO --- P0_CS
end
subgraph Dev1["Peripherie 1: ST7789 Display (Aktiv bei CE1 LOW)"]
direction TB
P1_IO["SCK + MOSI (Dateneingang)"]
P1_CS["CS1 (Select-Leitung)"]
P1_IO --- P1_CS
end
Controller ==> Dev0
Dev0 ==> Dev1
Signalleitungen & Pinbelegung
| Signalleitung | BCM-Name | Physischer Pin | Funktion |
|---|---|---|---|
| SCLK | GPIO 11 | Pin 23 | Serieller Takt, wird ausschließlich vom Raspberry Pi erzeugt |
| MOSI (COPI) | GPIO 10 | Pin 19 | Controller Output, Peripheral Input (Daten zum Sensor) |
| MISO (CIPO) | GPIO 9 | Pin 21 | Controller Input, Peripheral Output (Daten vom Sensor) |
| CE0 (SS0) | GPIO 8 | Pin 24 | Aktiv-LOW Chip-Select für erstes SPI-Gerät |
| CE1 (SS1) | GPIO 7 | Pin 26 | Aktiv-LOW Chip-Select für zweites SPI-Gerät |
Da die MISO-Leitung nur von dem Baustein aktiv getrieben wird, dessen Chip-Select-Leitung auf LOW gezogen ist (alle inaktiven Bauteile schalten MISO auf hochohmig/High-Z), können mehrere Peripheriegeräte dieselben Daten- und Taktleitungen kollisionsfrei teilen.
2. Die vier SPI-Modi: CPOL und CPHA
SPI besitzt keinen einheitlich festgeschriebenen Taktstandard. Das zeitliche Zusammenspiel wird über zwei Parameter festgelegt:
- CPOL (Clock Polarity): Bestimmt, ob der Takt im Ruhezustand LOW (0) oder HIGH (1) ist.
- CPHA (Clock Phase): Bestimmt, ob die Datenbits auf der ersten (führenden) oder zweiten Flanke abgetastet werden.
Aus diesen Parametern ergeben sich die vier Standard-SPI-Modi:
| SPI-Modus | CPOL (Ruhetakt) | CPHA (Abtastflanke) | Beschreibung |
|---|---|---|---|
| Modus 0 | 0 (Low) | 0 (Steigende Flanke) | Takt ruht Low; Abtastung bei steigender Flanke (Am weitesten verbreitet: MCP3008, SD-Karten) |
| Modus 1 | 0 (Low) | 1 (Fallende Flanke) | Takt ruht Low; Abtastung bei fallender Flanke |
| Modus 2 | 1 (High) | 0 (Fallende Flanke) | Takt ruht High; Abtastung bei fallender Flanke |
| Modus 3 | 1 (High) | 1 (Steigende Flanke) | Takt ruht High; Abtastung bei steigender Flanke (Häufig bei Industrie-Flash/FRAM) |
Vorsicht bei Modus-Konflikten
Wird der falsche SPI-Modus gewählt, verschieben sich alle gelesenen Bits um einen halben Taktzyklus oder die Werte werden komplett invertiert. Prüfe immer das Datenblatt des jeweiligen Sensors.
3. Kernel-Aktivierung & Linux Spidev-Geräte
Unter Raspberry Pi OS Bookworm wird SPI in /boot/firmware/config.txt aktiviert:
Folgenden Eintrag prüfen oder ergänzen:
Werden mehr als zwei SPI-Geräte benötigt, lässt sich der sekundäre SPI1-Controller zuschalten:
Datei speichern und neu starten:
Prüfen, ob die Linux-Gerätedateien vorhanden sind:
Ausgabe:
/dev/spidev0.0 steuert das Gerät an CE0 (GPIO 8), /dev/spidev0.1 das Gerät an CE1 (GPIO 7).
4. Hardware-Labor: Analoge Sensoren mit MCP3008 ADC auslesen
Der Raspberry Pi besitzt auf seiner Stiftleiste keine analogen Eingänge (ADC). Um analoge Spannungen (von Potentiometern, Fotowiderständen, NTC-Temperatursensoren oder 4–20-mA-Stromschleifen) einzulesen, ist ein externer Analog-Digital-Wandler wie der Microchip MCP3008 (10 Bit Auflösung, 8 Kanäle, SPI-Interface) der Standard.
Schaltplan & Verdrahtung
Verbindung des 16-Pin DIP MCP3008 mit dem Raspberry Pi:
| MCP3008-Pin | Signal | Verbindung Raspberry Pi | Hinweis |
|---|---|---|---|
| Pin 1 | CH0 | Analoges Messsignal | Spannung von 0,0 V bis 3,3 V |
| Pin 2–8 | CH1–CH7 | Analoge Eingänge | Weitere Sensoren |
| Pin 9 | DGND | Pin 6 (GND) | Digitale Masse |
| Pin 10 | CS | Pin 24 (GPIO 8 / CE0) | Chip-Select |
| Pin 11 | DIN | Pin 19 (GPIO 10 / MOSI) | Daten-Eingang |
| Pin 12 | DOUT | Pin 21 (GPIO 9 / MISO) | Daten-Ausgang |
| Pin 13 | CLK | Pin 23 (GPIO 11 / SCLK) | Takt |
| Pin 14 | AGND | Pin 14 (GND) | Analoge Masse |
| Pin 15 | VREF | Pin 1 (3,3 V) | Referenzspannung (3,30 V) |
| Pin 16 | VDD | Pin 1 (3,3 V) | Versorgungsspannung (3,3 V) |
Störunterdrückung & Abblockkondensator
Schalte einen 100-nF-Keramikkondensator direkt zwischen Pin 16 (VDD) und Pin 9 (DGND) unmittelbar am Baustein, um Spannungseinbrüche abzufangen. Für präzise Messungen gehört ein weiterer 100-nF-Kondensator zwischen VREF und AGND.
5. Vollduplex-Programmierung mit Python und spidev
Der MCP3008 erwartet ein 3-Byte-Befehlspaket zur Kanalabfrage. Da SPI stets im Vollduplex-Betrieb läuft, empfängt der Raspberry Pi während des Sendens der 3 Bytes simultan 3 Antwort-Bytes:
%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
subgraph TX["Gesendet vom Raspberry Pi (MOSI)"]
direction TB
T1["Byte 1: Start-Bit (0x01)"]
T2["Byte 2: Single-Ended Konfig (0x80 | Ch<<4)"]
T3["Byte 3: Füllbyte (0x00)"]
T1 --> T2 --> T3
end
subgraph RX["Empfangen vom MCP3008 (MISO)"]
direction TB
R1["Byte 1: High-Z Unbekannt"]
R2["Byte 2: Null-Bit + Bits 9 & 8"]
R3["Byte 3: Untere 8 Datenbits"]
R1 --> R2 --> R3
end
TX ==>|"Vollduplex-Übertragung"| RX
Der 10-Bit-Messwert setzt sich aus den niederwertigsten 2 Bits von Byte 2 und den 8 Bits von Byte 3 zusammen:
10-Bit ADC Zusammensetzung:
Rohwert = ((Byte[1] & 0x03) · 256) + Byte[2]
Spannung = (Rohwert ÷ 1023) · VREF
Produktionsreife Python-Implementierung
Paket python3-spidev installieren:
Skript mcp3008_monitor.py anlegen:
#!/usr/bin/env python3
"""
Hochleistungs-SPI-ADC-Treiber fuer Microchip MCP3008
Liest analoge Messwerte mit bis zu 1,35 MHz Taktfrequenz ein.
"""
import time
import sys
import spidev
class MCP3008ADC:
def __init__(self, bus: int = 0, device: int = 0, speed_hz: int = 1350000, vref: float = 3.3):
"""
Initialisiert die SPI-Verbindung zum MCP3008.
Maximal zulaessiger SPI-Takt bei 3,3V Versorgungsspannung ist 1,35 MHz.
"""
self.vref = vref
self.spi = spidev.SpiDev()
self.spi.open(bus, device)
self.spi.max_speed_hz = speed_hz
self.spi.mode = 0b00 # SPI Modus 0 (CPOL=0, CPHA=0)
self.spi.bits_per_word = 8
def read_channel(self, channel: int) -> tuple[int, float]:
"""
Liest einen 10-Bit Rohwert (0..1023) und berechnet die Spannung.
Kanal muss eine Zahl von 0 bis 7 sein.
"""
if not 0 <= channel <= 7:
raise ValueError(f"Ungueltiger Kanal {channel}: Erlaubt ist 0 bis 7.")
# Befehlspaket: Start-Bit (1), Single-Ended (1), Kanal-ID
tx_buffer = [1, (8 + channel) << 4, 0]
# Vollduplex-Uebertragung durchfuehren
rx_buffer = self.spi.xfer2(tx_buffer)
# 10-Bit Ergebnis zusammensetzen
raw_val = ((rx_buffer[1] & 3) << 8) | rx_buffer[2]
voltage = (raw_val / 1023.0) * self.vref
return raw_val, voltage
def close(self) -> None:
self.spi.close()
def main() -> None:
print("Initialisiere MCP3008 ADC...")
adc = MCP3008ADC(bus=0, device=0, speed_hz=1350000, vref=3.3)
try:
print("Lese Kanal 0 aus (Potentiometer / Analogsensor)...")
print("--------------------------------------------------")
while True:
raw, volts = adc.read_channel(0)
percentage = (raw / 1023.0) * 100.0
sys.stdout.write(f"\rCH0 Rohwert: {raw:4d} | Spannung: {volts:5.3f} V | Skala: {percentage:5.1f}%")
sys.stdout.flush()
time.sleep(0.1)
except KeyboardInterrupt:
print("\n\nMessung beendet.")
finally:
adc.close()
if __name__ == "__main__":
main()
6. Signalintegrität bei hohen Taktfrequenzen
Wird der SPI-Takt auf 10 MHz bis 32 MHz hochgeschraubt, treten Übertragungseffekte auf:
- Leitungsreflexionen (Ringing): Schnelle Flanken erzeugen Überschwinger. Bei Signalverzerrungen auf SCLK empfiehlt sich ein serieller 33-Ω- bis 47-Ω-Bedämpfungswiderstand direkt am Ausgang des Raspberry Pi.
- Übersprechen: Verlege in Flachbandkabeln niemals SCLK direkt neben MISO ohne dazwischenliegende Masseleitung.
- Leitungslänge: Standard-SPI ist für kurze Platinenverbindungen (< 20 cm) ausgelegt. Müssen Distanzen über 1 Meter überbrückt werden, muss der Takt unter 500 kHz gesenkt werden oder es müssen differentielle Bustreiber (RS-485 / LVDS) eingesetzt werden.
Nächste Schritte
Vom synchronen SPI-Bus geht es weiter zur differentiellen Industrie-Kommunikation:
- UART-Seriell & Industrielles RS-485: SPS-Anbindung und Modbus RTU über lange verdrillte Zweidrahtleitungen.
- Dallas 1-Wire & Hardware-PWM: Digitale Temperatur-Busse und jitterfreie Pulsweitenmodulation.