PyRaspBeta

spidev

spidev gives a program direct access to SPI chips, the way the Linux spidev kernel driver does. PyRasp ships a spidev module with the same SpiDev class and methods as the real package (py-spidev on PyPI); the kernel interface it imitates is documented in the Linux kernel's spidev page.

There is one SPI controller: SPI0. Its pins are fixed on the header — SCLK on GPIO11, MOSI on GPIO10, MISO on GPIO9 — with two chip selects, CE0 (GPIO8) and CE1 (GPIO7). open() accepts only bus 0 and device 0 or 1.

Reading an MCP3008

The classic first SPI chip is the MCP3008 eight-channel ADC. Open CE0, set mode 0, and send the three-byte read frame; the result arrives in the reply:

import spidev

spi = spidev.SpiDev()
spi.open(0, 0)                       # bus 0, CE0
spi.mode = 0                         # the MCP3008 expects mode 0
spi.max_speed_hz = 1_000_000

def read_channel(channel):
    reply = spi.xfer2([0x01, (0x08 + channel) << 4, 0x00])
    return ((reply[1] & 0x03) << 8) | reply[2]   # 0 … 1023

while True:
    print(read_channel(0))

spi.close()

Wire the chip to the Pi's SPI pins and to the potentiometer as in the ADC primer; see MCP3008 part page for the full pinout.

If you would rather skip the bit twiddling, gpiozero.MCP3008(channel=0) uses this same spidev support internally (see the gpiozero page).

The SpiDev API

import spidev

spi = spidev.SpiDev()                # or SpiDev(0, 0)
spi.open(0, 0)
spi.mode = 0                         # CPOL/CPHA: 0–3
spi.max_speed_hz = 500_000
spi.bits_per_word = 8
spi.cshigh = False
spi.lsbfirst = False

reply = spi.xfer([0x00, 0x00])       # full duplex; list in, list out
reply = spi.xfer2([0x01, 0x80, 0x00])
reply = spi.xfer3([0x01, 0x80, 0x00])
spi.writebytes([0x40, 0x00])
data = spi.readbytes(4)
spi.close()

xfer(), xfer2() and xfer3() are the same call in the simulator: one transaction of the same number of bytes out and back, with the chip select low for its duration. The speed_hz, delay_usec and bits_per_word arguments to xfer* are accepted and ignored.

Opening anything but /dev/spidev0.0 or /dev/spidev0.1 raises FileNotFoundError, and using the object before open() raises OSError (Bad file descriptor), like closing it first and then transferring.

Going deeper

The simulator models SPI at the bit level for the parts that speak it, currently the MCP3008. The chip watches for its start bit, samples the channel bits and shifts the 10-bit result back MSB first after a null bit — the same framing a real driver sees — so hand-written frames and library drivers both work.

  • A chip select with no part leaves CS0/CS1 idle. The transfer succeeds and every byte reads 0, "MISO idles low"; the bus sniffer marks it no device. On hardware a floating MISO can read random bits.
  • A wrong mode garbles the reply. The part knows its expected mode; if your spi.mode differs the simulator warns and rotates every reply byte by one bit, the way sampling on the wrong clock edge corrupts data.
  • CS wiring is checked. The part's CS pin must reach CE0/CE1, and SCLK/MOSI must reach the fixed SPI0 pins; otherwise the transfer is treated as "no device".
  • MISO must be wired too. A chip with only DIN connected reads back zeros even though it received the bytes.

Simulator vs real hardware

  • Speed does not matter. max_speed_hz is stored but unused: transfers are instant. A too-fast real bus that garbles long wires or exceeds a chip's rating cannot fail here.
  • Full duplex, always. xfer*() returns as many bytes as you sent, from the chip's point of view; there is no half-duplex read mode with the read0 flag, and no spidev.bufsiz chunking. writebytes2() behaves like writebytes().
  • bits_per_word other than 8 is ignored. Real spidev would pack words differently.
  • Only SPI0 with CE0/CE1. There is no /dev/spidev1.*, no open_path() and no udev.
  • Only modelled chips answer. A real SPI display controller without a simulator model reads zeros; the MCP3008 is the chip the shop sells and models. The ssd1306 OLED in the shop is I²C-only.