Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SPI

Pico de Gallo drives the RP2350’s SPI0 controller in DMA-backed full-duplex mode.

SignalRP2350 GPIOAvailable on
SCKGPIO 6v1.0+
MOSI (TX)GPIO 7v1.0+
MISO (RX)GPIO 4v1.0+
CSGPIO 5v1.1+

Note

On v1.0 the dedicated CS line isn’t routed to any header. You can still drive chip-select from any of the user GPIO pins (0–3) via the spi_device(cs_pin) HAL accessor or by toggling a GPIO manually around the SPI ops.

Operations

OperationDescription
ReadClock in N bytes (MISO only)
WriteClock out bytes (MOSI only)
TransferFull-duplex: simultaneous TX and RX
FlushWait for any in-flight transactions to complete
BatchSequence of ops under a single chip-select
Set ConfigChange frequency / CPHA / CPOL at runtime
Get ConfigQuery the current configuration

SPI Mode

SPI mode is the (CPOL, CPHA) tuple. Mode is set via set-config / spi_set_config():

ModeCPOLCPHAIdle clockSample edge
000lowrising
101lowfalling
210highfalling
311highrising

The firmware defaults to mode 0.

CLI

$ gallo spi help
SPI access methods

Commands:
  read        Read bytes through SPI bus
  write       Write bytes through SPI bus
  transfer    Full-duplex SPI transfer
  write-read  Write bytes followed by read bytes
  set-config  Set SPI bus configuration (frequency, phase, polarity)
  get-config  Get current SPI bus configuration
  batch       Execute multiple SPI operations atomically under chip-select

Read / Write / Transfer

$ gallo spi read --count 4
00 00 00 00

$ gallo spi write --bytes 0x9f

$ gallo spi transfer --bytes 0x01 0x02 0x03 0x04
00 00 00 00

transfer clocks out the given bytes on MOSI and simultaneously clocks in the same number of bytes on MISO — true full-duplex.

Config

$ gallo spi set-config --frequency 1000000 --phase 0 --polarity 0
$ gallo spi get-config
Frequency: 1000000 Hz, CPHA: 0, CPOL: 0

Batch (Atomic Under CS)

A single transaction with chip-select held low for the duration:

$ gallo spi batch --cs 0 --op write:0x9f --op read:3
Read data (3 bytes):
  0000: ef 40 18                                           .@.

The --cs flag picks which user GPIO (0–3) drives chip-select. See Transaction Batching.

Rust Library

use pico_de_gallo_lib::{PicoDeGallo, SpiBatchOp, SpiPhase, SpiPolarity};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pg = PicoDeGallo::new();

    pg.spi_set_config(1_000_000, SpiPhase::Mode0, SpiPolarity::Low).await?;

    // Read JEDEC ID under CS on GPIO 0
    let ops = [
        SpiBatchOp::Write { data: &[0x9F] },
        SpiBatchOp::Read { len: 3 },
    ];
    let result = pg.spi_batch(0, &ops).await?;
    println!(
        "JEDEC: mfr=0x{:02x} type=0x{:02x} cap=0x{:02x}",
        result[0], result[1], result[2]
    );
    Ok(())
}

HAL

The HAL provides two flavours of SPI access:

  • hal.spi() — a raw embedded_hal::spi::SpiBus / embedded_hal_async::spi::SpiBus implementor. You manage chip-select yourself.
  • hal.spi_device(cs_pin) — an SpiDevice that automatically drives the given GPIO as chip-select around every transaction.
#![allow(unused)]
fn main() {
use embedded_hal::spi::{Operation, SpiDevice};
use pico_de_gallo_hal::Hal;

fn read_jedec(hal: &Hal) -> [u8; 3] {
    let mut spi = hal.spi_device(0);
    let mut id = [0u8; 3];

    // One transaction; CS asserted for the whole thing; batched into
    // one USB round-trip transparently.
    spi.transaction(&mut [
        Operation::Write(&[0x9F]),
        Operation::Read(&mut id),
    ])
    .unwrap();
    id
}
}

C (FFI)

#include "pico_de_gallo.h"
#include <stdio.h>

void read_jedec(PicoDeGallo *gallo) {
    /* mode 0, 1 MHz */
    gallo_spi_set_config(gallo, 1000000, /*phase=*/false, /*polarity=*/false);

    uint8_t cmd[] = {0x9F};
    gallo_spi_write(gallo, cmd, 1);

    uint8_t id[3];
    gallo_spi_read(gallo, id, sizeof(id));
    printf("JEDEC: %02x %02x %02x\n", id[0], id[1], id[2]);
}

For atomic chip-select transactions, batch operations are available — see the gallo_spi_batch_* family in the generated pico_de_gallo.h.

Python

from pyco_de_gallo import PycoDeGallo, SpiPhase, SpiPolarity

pg = PycoDeGallo()
pg.spi_set_config(1_000_000, SpiPhase.Mode0, SpiPolarity.Low)

pg.spi_write(bytes([0x9F]))
id_bytes = pg.spi_read(3)
print("JEDEC:", id_bytes.hex())

Error Handling

VariantMeaning
BufferTooLongRequest exceeds firmware buffer limit
UnsupportedReturned by firmware builds without SPI
OtherCatch-all for firmware-reported SPI failure

See appendix/status-codes.md for the FFI mapping.