Interfacing with the SPI driver

This SPI guide uses Raspberry Pi 4 as the target. Hence, it uses the spi-bcm2711 driver. Different boards might require a different driver, but interfacing with the driver should be similar across different boards.

Starting the SPI driver

To start the SPI resource manager on your target if it's not already running, you can use the command:

spi-bcm2711

Visit the "Starting a SPI device driver" page in the SPI Framework Technote for more information on starting the driver.

By default, the SPI driver starts with a default config file located in /etc/system/config/spi/spi.conf. The example below shows how you can make your own custom SPI config file. For more information on the SPI configuration file, visit the "Configuring SPI" page in the SPI Framework Technote.

[globals]
verbose=5

[bus]
busno=0
name=spi0
base=0xfe204000
irq=150
input_clock=500000000
bs=rpanic=48,tpanic=16
dma_attach_opts=num_cbs=256,range_min=0,range_max=14,typed_mem=sysram&below1G
dma_thld=4

[dev]
parent_busno=0
devno=0
name=dev0
clock_rate=5000000
cpha=1
cpol=0
bit_order=msb
word_width=32
idle_insert=1

[dev]
parent_busno=0
devno=1
name=dev1
cpha=0
cpol=1
word_width=32
clock_rate=5000000

[bus]
busno=3
name=spi3
base=0xfe204600
irq=151
input_clock=500000000

[dev]
parent_busno=3
devno=0
name=dev0
cpha=0
cpol=1
word_width=8
clock_rate=5000000

Exchanging data with the SPI device

Once you've started the SPI driver, it exposes a path under /dev/io-spi/spi<bus_number>/dev<device_number>. You can use the devctl() command to interface with the SPI driver. Optionally, you can also use the normal open(), read(), write(), and close() commands to interface with the SPI driver. You can find the full interface in hw/io-spi.h.

The following example demonstrates writing and reading data from a SPI device. First, you need to define the data exchange structure:

typedef struct {
    uint32_t    nbytes;           /* Exchange data length */
    uint8_t     data[0];          /* Exchange data */
} spi_xchng_t;

Before communicating, you need to configure the SPI device by using the spi_cfg_t structure:

typedef struct {
    uint32_t    mode;        /* SPI mode */
    uint32_t    clock_rate;  /* bus speed */
} spi_cfg_t;

An example on how to set up and perform SPI communication could look something like this:

// 1. Open the SPI device
char spi_device_name[20];
sprintf(spi_device_name, "/dev/io-spi/spi%d/dev%d", bus_number, device_number);
int fd = open(spi_device_name, O_RDWR);

// 2. Configure the device
spi_cfg_t spi_device_cfg = {
.mode = mode, // SPI mode
.clock_rate = spi_device_speed_hz // Clock rate in Hz
};
// Configure the SPI device
devctl(fd, DCMD_SPI_SET_CONFIG, &spi_device_cfg, sizeof(spi_cfg_t), NULL);

// 3. Prepare data for transfer
spi_xchng_t spi_xchng_msg = malloc(sizeof(spi_xchng_t) + data_size); // allocate enough memory for the data
spi_xchng_msg->nbytes = data_size;
// Fill data buffer
for (int i = 0; i < data_size; i++) {
spi_xchng_msg->data[i] = data_buffer[i];
}

// 4. Perform the transfer
devctl(fd, DCMD_SPI_DATA_XCHNG, spi_xchng_msg, sizeof(spi_xchng_t) + data_size, NULL);

// 5. Clean up
free(spi_xchng_msg);
close(fd);

For more information on configuring a SPI device, refer to the rpi_spi_configure_device() and rpi_spi_write_read_data() functions. You can take a similar approach for other devctl() DCMDs.

You can find a list of the devctl() commands in the "DCMD_SPI_*" section of the Devctl and Ioctl Commands guide.

Page updated: