KevsRobots Learning Platform
70% Percent Complete
By Kevin McAleer, 9 Minutes
SPI requires more physical connections than I2C, but the wiring is straightforward once you understand the signal flow. This lesson covers connecting SPI devices to your Raspberry Pi Pico, focusing on practical examples and common pitfalls.
Every SPI device needs:
Important: Unlike I2C, SPI devices donβt have addresses. Each device needs its own chip select pin.
The MAX31855 is a great SPI learning device - it reads thermocouple temperature sensors and communicates via SPI. Many modules are available on breakout boards.
Typical breakout board pins:
| Pin | Name | Description |
|---|---|---|
| VCC | Power | 3.0-3.6V (3.3V from Pico) |
| GND | Ground | Common ground |
| SCK | Clock | SPI clock input |
| DO | Data Out | SPI MISO (data to Pico) |
| CS | Chip Select | Active LOW chip select |
Note: Some modules might have βSOβ (Serial Out) instead of βDOβ - itβs the same signal (connects to MISO).
Hereβs the complete connection guide:
Raspberry Pi Pico (SPI0 pins):
βββββββββββββββββββββββββββ
β USB PORT β
βββββββββββββββββββββββββββ€
β 1 GP0 VBUS 40β
β 2 GP1 VSYS 39β
β 3 GND GND 38β β Connect GND
β 4 GP2 (SCK) 3V3_EN 37β
β 5 GP3 (MOSI) 3.3V 36β β Connect VCC
β 6 GP4 (MISO) REF 35β
β 7 GP5 (CS) GP28 34β
β 8 GND GND 33β
βββββββββββββββββββββββββββ
SCK: GP2 (pin 4) β Device SCK/SCLK
MOSI: GP3 (pin 5) β Device MOSI/SDI (if needed)
MISO: GP4 (pin 6) β Device MISO/SDO/DO
CS: GP5 (pin 7) β Device CS/SS/CE
Breadboard:
ββββββββββββββββββββββββββββββββββ
β + + + + + + + + + + + + + + + β β 3.3V rail
β - - - - - - - - - - - - - - - β β GND rail
β β
β [MAX31855 Module] β
β VCC GND SCK DO CS β
β β β β β β β
β β β β β β β
β β β β β ββ To GP5 β
β β β β βββββ To GP4 β
β β β βββββββββ To GP2 β
β β βββββββββββββ To GND β
β βββββββββββββββββ To 3.3V β
β β
β [K-Type Thermocouple] β
β Connected to screw terminals β
ββββββββββββββββββββββββββββββββββ
Letβs also cover a more complex device - an SPI OLED display (like SSD1306 or SH1106):
| Pin | Name | Description |
|---|---|---|
| GND | Ground | Common ground |
| VCC | Power | 3.3V or 5V (check module) |
| SCK | Clock | SPI clock |
| SDA/MOSI | Data | SPI data in (MOSI) |
| RES/RST | Reset | Display reset (GPIO) |
| DC | Data/Command | Register select (GPIO) |
| CS | Chip Select | SPI chip select (GPIO) |
OLED β Pico
GND β GND (pin 38)
VCC β 3.3V (pin 36)
SCK β GP2 (pin 4) - SPI0 SCK
SDA/MOSI β GP3 (pin 5) - SPI0 MOSI
RES/RST β GP6 (pin 9) - Any GPIO
DC β GP7 (pin 10) - Any GPIO
CS β GP8 (pin 11) - Any GPIO
Important: Unlike simple sensors, displays need extra control pins (RST and DC) to manage display operations. These are regular GPIO, not special SPI signals.
Once connected, verify with this test code:
from machine import Pin, SPI
import time
# Initialize SPI
spi = SPI(0,
baudrate=1000000, # 1 MHz (MAX31855 supports up to 5 MHz)
polarity=0,
phase=0,
sck=Pin(2),
mosi=Pin(3), # Not used but must be specified
miso=Pin(4))
# Chip select
cs = Pin(5, Pin.OUT)
cs.value(1) # Start deselected
print("Testing MAX31855 connection...")
# Read 4 bytes from MAX31855
def read_temperature():
"""Read temperature from MAX31855"""
cs.value(0) # Select device
time.sleep_us(1) # Small delay
data = bytearray(4) # MAX31855 sends 32 bits
spi.readinto(data) # Read data
cs.value(1) # Deselect device
# Combine bytes into 32-bit value
raw_value = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]
# Check for errors (bit 16)
if raw_value & 0x00010000:
print("β οΈ Thermocouple error detected")
return None
# Extract temperature (bits 31:18, signed 14-bit value)
temp_raw = (raw_value >> 18) & 0x3FFF
# Check sign bit (bit 13)
if temp_raw & 0x2000:
temp_raw = -((~temp_raw & 0x3FFF) + 1) # Two's complement
# Convert to Celsius (0.25Β°C per LSB)
temperature = temp_raw * 0.25
return temperature
# Test reading
temp = read_temperature()
if temp is not None:
print(f"β
Temperature: {temp:.2f}Β°C ({temp*9/5+32:.2f}Β°F)")
print("SPI connection working!")
else:
print("β Check wiring and thermocouple connection")
Expected output:
Testing MAX31855 connection...
β
Temperature: 22.50Β°C (72.50Β°F)
SPI connection working!
If you see this, your SPI wiring is correct!
Problem: Device doesnβt respond or returns garbage
Symptom: All zeros, all 0xFF, or random data
Solution:
# Check connections:
# Pico MOSI (GP3) β Device MOSI/SDI/DI (data IN to device)
# Pico MISO (GP4) β Device MISO/SDO/DO (data OUT from device)
# If confused, swap them in code:
spi = SPI(0, sck=Pin(2), mosi=Pin(4), miso=Pin(3)) # Try swapped
Problem: Device doesnβt respond
Symptom: No data or timeout errors
Cause: CS might be active-HIGH instead of active-LOW
Solution:
# Try inverted logic
cs.value(0) # Most devices: LOW = selected
# vs
cs.value(1) # Rare devices: HIGH = selected
Problem: Intermittent errors or wrong data
Solution: Reduce SPI speed
# Start slow
spi = SPI(0, baudrate=100000, ...) # 100 kHz
# Once working, increase gradually
spi = SPI(0, baudrate=1000000, ...) # 1 MHz
Problem: Constant wrong data or no response
Solution: Try different modes
# Mode 0 (most common)
spi = SPI(0, baudrate=1000000, polarity=0, phase=0, ...)
# Mode 1
spi = SPI(0, baudrate=1000000, polarity=0, phase=1, ...)
# Mode 2
spi = SPI(0, baudrate=1000000, polarity=1, phase=0, ...)
# Mode 3
spi = SPI(0, baudrate=1000000, polarity=1, phase=1, ...)
Wire multiple devices to the same bus:
from machine import Pin, SPI
# Shared SPI bus
spi = SPI(0, baudrate=1000000, sck=Pin(2), mosi=Pin(3), miso=Pin(4))
# Unique CS for each device
cs_sensor = Pin(5, Pin.OUT)
cs_display = Pin(6, Pin.OUT)
cs_memory = Pin(7, Pin.OUT)
# Deselect all
cs_sensor.value(1)
cs_display.value(1)
cs_memory.value(1)
# Talk to sensor
cs_sensor.value(0)
sensor_data = spi.read(4)
cs_sensor.value(1)
print(f"Sensor: {sensor_data}")
# Talk to display
cs_display.value(0)
spi.write(b'\xFF\x00') # Send command
cs_display.value(1)
# Talk to memory
cs_memory.value(0)
memory_data = spi.read(256)
cs_memory.value(1)
print(f"Memory: {len(memory_data)} bytes read")
Key points:
SPI is fast but sensitive to wire length:
Keep wires short:
Avoid long jumper wires:
Use ground reference:
Ensure compatibility:
# Check device datasheet
# MAX31855: 3.0-3.6V β (Safe with Pico's 3.3V)
# SPI OLED: Often 3.3V or 5V tolerant
# SD Cards: 3.3V only
# If device is 5V only, use level shifter
Most SPI sensors use minimal current:
Pico 3.3V pin can supply up to 300mA - sufficient for most SPI devices.
Practice SPI wiring skills:
Wire your SPI device following the guide
Run the test code to verify connections
Intentionally disconnect MISO - What happens?
# With MISO disconnected, you'll read all 0x00 or 0xFF
Swap MOSI and MISO - Does it work? (It shouldnβt!)
Try different CS pins:
# Move CS wire to GP6 and update code
cs = Pin(6, Pin.OUT)
# Should work the same
from machine import Pin, SPI
def check_spi_wiring():
"""Verify SPI is initialized correctly"""
try:
spi = SPI(0, baudrate=1000000, sck=Pin(2), mosi=Pin(3), miso=Pin(4))
cs = Pin(5, Pin.OUT)
print("β SPI initialized")
print("β CS pin configured")
# Try simple read
cs.value(0)
data = spi.read(1)
cs.value(1)
print(f"β Read test: 0x{data[0]:02X}")
print("\nWiring appears correct!")
except Exception as e:
print(f"β Error: {e}")
print("Check your connections")
check_spi_wiring()
In this lesson, you mastered:
Your SPI device is now physically connected and tested. Youβve verified communication works and understand the signal flow.
With hardware wired and tested, letβs dive into a complete SPI sensor example. In the next lesson, weβll read data continuously, handle errors gracefully, and build a practical temperature monitoring system with the MAX31855.
You can use the arrows β β on your keyboard to navigate between lessons.
Comments