MaixCAM MaixPy SPI Serial Peripheral Interface Usage Guide

Update history
Date Version Author Update content
2025-08-08 1.1.0 Neucrack Refactored document, easier for beginners to understand
2024-06-11 1.0.0 iawak9lkm Initial version of the document

Prerequisites

Please first learn how to use the pinmap module to set pin functions.

To enable a pin for SPI functionality, first use pinmap to set the corresponding pin function to SPI.

Introduction to SPI

Earlier we introduced I2C, which enables one-to-many bus communication with only two wires. However, it has limitations, such as relatively low speed (typically 200k/400k). SPI (Serial Peripheral Interface) is also a one-to-many bus communication method, but it is faster and requires 4 wires for communication:

  • MISO: Master In Slave Out — this pin sends data in slave mode and receives data in master mode.
  • MOSI: Master Out Slave In — this pin sends data in master mode and receives data in slave mode.
  • SCK: Serial clock, output by the master and input to the slave.
  • NSS/CS: Slave Select — chip select pin that allows the master to communicate individually with a specific slave device, avoiding conflicts on the data lines.

Common uses include:

  • Reading and writing Flash memory.
  • Communication between two devices.
  • Protocol conversion, such as SPI to Ethernet.
  • LCD display drivers.
  • Outputting specific square waves, e.g., WS2812 LEDs. Apart from using GPIO control, SPI’s square-wave output capability can also be used to output specific waveforms.

In terms of protocol behavior, SPI generally works as follows:

  • SPI supports one master and multiple slaves. The master selects the slave to communicate with via the chip select pin. In most cases, a slave device needs only one chip select pin, while the master’s number of chip select pins equals the number of devices. When the chip select signal for a specific slave is enabled, that slave responds to all requests from the master; other slaves ignore all bus data.

  • SPI has four modes, determined by polarity (CPOL) and phase (CPHA) settings.

    Polarity affects the clock signal level when the SPI bus is idle.

    1. CPOL = 1: Idle level is high.
    2. CPOL = 0: Idle level is low.

    Phase determines which clock edge is used to sample data.

    1. CPHA = 0: Sampling starts at the first clock edge.
    2. CPHA = 1: Sampling starts at the second clock edge.

    Combining polarity and phase yields the four SPI modes:

Mode CPOL CPHA
0 0 0
1 0 1
2 1 0
3 1 1
  • SPI usually supports both full-duplex and half-duplex communication.

  • SPI has no defined maximum transfer rate, no addressing scheme, no acknowledgment mechanism, and no flow control rules.

SPI is a very common communication interface, and through SPI, SoCs can control various peripheral devices.

Choosing the Right SPI to Use

First, we need to know which pins and SPI interfaces the device provides, as shown in the table:

Device Model Pin Diagram Pin Multiplexing Description
MaixCAM On the silkscreen, A24 is the pin name, SPI4_CS is the function name
MaixCAM-Pro maixcam_pro_io The first name, such as A24, is the pin name; SPI4_CS is the function name
MaixCAM2 maixcam2_io The first name, such as IO1_A21, is the pin name; SPI2_CS1 is the function name

Note that pins may be used for other purposes by default; it’s best to avoid those pins. See the pinmap documentation for details.

For example:

  • MaixCAM/MaixCAM-Pro: Due to SPI peripheral limitations, they can only be used as SPI masters. MaixCAM's SPI currently does not support changing the active level of the hardware CS pin; all hardware SPI CS pins are active low. If you need another CS active level, configure it in the SPI API using a software CS pin and its active level. SPI4 is a software-simulated SPI, with a tested maximum speed of 1.25 MHz; usage is the same as hardware SPI.
  • MaixCAM2: Has two hardware SPI interfaces by default; SPI2’s default function is SPI. SPI1’s pins require multiplexing setup first; see pinmap for details.

Using SPI in MaixPy

To use SPI in MaixPy, first configure pinmap, then create an SPI object to communicate.

Here’s an example: connect the SPI’s MOSI and MISO together to perform a full-duplex loopback test.

from maix import spi, pinmap, sys, err

# get pin and SPI number according to device id
device_id = sys.device_id()
if device_id == "maixcam2":
    pin_function = {
        "IO1_A21": "SPI2_CS1",
        "IO1_A19": "SPI2_MISO",
        "IO1_A18": "SPI2_MOSI",
        "IO1_A20": "SPI2_SCK"
    }
    spi_id = 2
else:
    pin_function = {
        "A24": "SPI4_CS",
        "A23": "SPI4_MISO",
        "A25": "SPI4_MOSI",
        "A22": "SPI4_SCK"
    }
    spi_id = 4

for pin, func in pin_function.items():
    err.check_raise(pinmap.set_pin_function(pin, func), f"Failed set pin{pin} function to {func}")


spidev = spi.SPI(spi_id, spi.Mode.MASTER, 1250000)

### Example of full parameter passing.
# spidev = spi.SPI(id=4,                  # SPI ID
#                  mode=spi.Mode.MASTER,  # SPI mode
#                  freq=1250000,          # SPI speed
#                  polarity=0,            # CPOL 0/1, default is 0
#                  phase=0,               # CPHA 0/1, default is 0
#                  bits=8,                # Bits of SPI, default is 8
#                  cs_enable=True,        # Use soft CS pin? True/False, default is False
#                  cs='GPIOA19')          # Soft cs pin number, default is 'GPIOA19'

b = bytes(range(0, 8))

res = spidev.write_read(b, len(b))
if res == b:
    print("loopback test succeed")
else:
    print("loopback test failed")
    print(f"send:{b}\nread:{res}")

More Examples

See MaixPy examples.

API Documentation

For more APIs, see the SPI API documentation