-
Notifications
You must be signed in to change notification settings - Fork 0
SPI
This document describes the SPI implementation in Gopper for RP2040/RP2350 platforms.
Gopper implements both hardware and software SPI support, matching Klipper's SPI protocol. This allows communication with SPI devices such as:
- TMC stepper driver chips (TMC2130, TMC5160, etc.)
- Accelerometers (ADXL345, LIS2DW, etc.)
- SD cards
- Display controllers
- Other SPI peripherals
The SPI implementation follows Gopper's standard three-layer architecture:
-
HAL Interface (
core/spi_hal.go): Abstract interface for SPI operations -
Command Layer (
core/spi.go): Klipper protocol command handlers -
Platform Layer (
targets/rp2040/spi.go,targets/rp2040/spi_software.go): Hardware-specific implementation
The RP2040 and RP2350 have two hardware SPI controllers (SPI0 and SPI1), each supporting multiple GPIO pin configurations. Gopper supports all 9 standard bus configurations from Klipper:
| Bus ID | Controller | MISO Pin | MOSI Pin | SCK Pin | Name |
|---|---|---|---|---|---|
| 0 | SPI0 | GPIO0 | GPIO3 | GPIO2 | spi0a |
| 1 | SPI0 | GPIO4 | GPIO7 | GPIO6 | spi0b |
| 2 | SPI0 | GPIO16 | GPIO19 | GPIO18 | spi0c |
| 3 | SPI0 | GPIO20 | GPIO23 | GPIO22 | spi0d |
| 4 | SPI0 | GPIO4 | GPIO3 | GPIO2 | spi0e |
| 5 | SPI1 | GPIO8 | GPIO11 | GPIO10 | spi1a |
| 6 | SPI1 | GPIO12 | GPIO15 | GPIO14 | spi1b |
| 7 | SPI1 | GPIO24 | GPIO27 | GPIO26 | spi1c |
| 8 | SPI1 | GPIO12 | GPIO11 | GPIO10 | spi1d |
Note: The chip select (CS) pin is specified separately in the config_spi command and can be any available GPIO pin.
- Bus 2 (spi0c): Commonly used for ADXL345 accelerometer on Raspberry Pi Pico boards
- Bus 5 (spi1a): Alternative for SPI devices when SPI0 is in use
- Bus 0 (spi0a): Default SPI bus on many RP2040 boards
Software SPI (bit-banging) is available as a fallback when:
- Hardware SPI pins are already in use
- Non-standard pin combinations are needed
- Lower speeds are acceptable
Software SPI can use any GPIO pins but is slower than hardware SPI and consumes more CPU time.
Configure an SPI device with a chip select pin.
Format: config_spi oid=%c pin=%u cs_active_high=%c
Parameters:
-
oid: Object identifier (unique per SPI device) -
pin: GPIO pin number for chip select -
cs_active_high: CS polarity (0=active low, 1=active high)
Example:
config_spi oid=5 pin=17 cs_active_high=0
Configure an SPI device without automatic chip select management.
Format: config_spi_without_cs oid=%c
Parameters:
-
oid: Object identifier
Example:
config_spi_without_cs oid=6
Set the SPI bus parameters for a configured device.
Format: spi_set_bus oid=%c spi_bus=%u mode=%u rate=%u
Parameters:
-
oid: Object identifier -
spi_bus: Hardware bus ID (0-8 for hardware, ≥128 for software SPI) -
mode: SPI mode (0-3) -
rate: Clock rate in Hz
SPI Modes:
- Mode 0: CPOL=0, CPHA=0 (clock idle low, sample on rising edge)
- Mode 1: CPOL=0, CPHA=1 (clock idle low, sample on falling edge)
- Mode 2: CPOL=1, CPHA=0 (clock idle high, sample on falling edge)
- Mode 3: CPOL=1, CPHA=1 (clock idle high, sample on rising edge)
Example:
spi_set_bus oid=5 spi_bus=2 mode=3 rate=4000000
Send data to SPI device and receive response.
Format: spi_transfer oid=%c data=%*s
Parameters:
-
oid: Object identifier -
data: Byte array to transmit
Response: spi_transfer_response oid=%c response=%*s
Example:
spi_transfer oid=5 data=\x12\x00
→ Response: spi_transfer_response oid=5 response=\x00\xFF
Send data to SPI device without expecting a response (more efficient for write-only operations).
Format: spi_send oid=%c data=%*s
Parameters:
-
oid: Object identifier -
data: Byte array to transmit
Example:
spi_send oid=5 data=\xFF\xAA\x55
Configure a message to send during MCU shutdown (emergency stop).
Format: config_spi_shutdown oid=%c spi_oid=%c shutdown_msg=%*s
Parameters:
-
oid: Shutdown object identifier -
spi_oid: SPI device object identifier -
shutdown_msg: Byte array to send on shutdown
Example:
config_spi_shutdown oid=10 spi_oid=5 shutdown_msg=\x00\x00\x00\x00
Typical sequence for using an SPI device:
# 1. Configure SPI device with CS pin
config_spi oid=5 pin=17 cs_active_high=0
# 2. Set bus parameters (bus 2, mode 3, 4MHz)
spi_set_bus oid=5 spi_bus=2 mode=3 rate=4000000
# 3. Optional: Configure shutdown message for safety
config_spi_shutdown oid=10 spi_oid=5 shutdown_msg=\x00\x00
# 4. Perform transfers
spi_transfer oid=5 data=\x8B\x00 # Read WHO_AM_I register
→ Response: spi_transfer_response oid=5 response=\xE5\x00
- Uses TinyGo's
machine.SPIAPI - Supports full-duplex transfers
- Configurable clock rate (up to 62.5 MHz on RP2040)
- All 4 SPI modes supported
- Automatic GPIO muxing for SPI function
- Bit-banged implementation using GPIO
- Works on any GPIO pins
- Configurable timing based on requested rate
- Supports all 4 SPI modes
- Suitable for lower-speed devices (typically < 1 MHz)
- Automatic CS assertion/deassertion around transfers
- Supports both active-low (default) and active-high CS
- CS pin configured as GPIO output
- For devices without CS, use
config_spi_without_cs
- Mutex protection on bus configuration
- Safe concurrent access from multiple devices (different OIDs)
- Same bus can be shared by multiple devices with different CS pins
Klipper configuration for ADXL345 on RP2040:
[adxl345]
cs_pin: rpi:gpio17
spi_bus: spi0c # Bus 2: GPIO16(MISO), GPIO19(MOSI), GPIO18(SCK)
spi_speed: 4000000This translates to the following commands:
config_spi oid=5 pin=17 cs_active_high=0
spi_set_bus oid=5 spi_bus=2 mode=3 rate=4000000
- Raspberry Pi Pico or similar RP2040 board
- SPI device (e.g., ADXL345 accelerometer)
- Appropriate wiring for the selected bus
# Connect to MCU
~/klippy-env/bin/python ~/klipper/klippy/console.py -v /dev/ttyACM0
# Configure SPI device
SEND config_spi oid=5 pin=17 cs_active_high=0
SEND spi_set_bus oid=5 spi_bus=2 mode=3 rate=4000000
# Read ADXL345 WHO_AM_I register (should return 0xE5)
SEND spi_transfer oid=5 data="\x00\x00"- Max Speed: ~62.5 MHz (RP2040 peripheral limit)
- Typical Speed: 1-10 MHz for most devices
- CPU Overhead: Low (DMA capable)
- Timing Accuracy: Excellent
- Max Speed: ~500 kHz (depends on CPU load)
- Typical Speed: 100-250 kHz
- CPU Overhead: High (busy-wait loops)
- Timing Accuracy: Good (subject to interrupts)
- Software SPI timing may be affected by interrupt latency
- Maximum SPI clock rate limited by peripheral and PCB design
- No DMA support yet (could be added for improved performance)
- CS pin must be held constant during multi-byte transfers
- DMA support for hardware SPI
- Multi-byte transfer optimization
- SPI transaction queuing
- Enhanced error reporting
- Support for SPI slave mode