6. SPI Operation Guide

6.1. Operation Preparation

The preparation for operation of SPI is as follows:

  • Kernel and file system using published SDK. File systems can use squashFS or ext4 published by the SDK. You can also mount to NFS over the network via the local file system

6.2. Operation Process

  • Load the kernel. The default SPI-related modules are all built into the kernel and no install commands need to be executed.

  • Running SPI read and write commands under the console or writing SPI read and write programs in kernel or user space which allows you to read and write SPI devices mounted on the SPI controller.

6.3. Operation Example

6.3.1. SPI Read-Write Program Example with Kernel Mode:

This operation example demonstrates how to read and write to SPI device through SPI reader and writer in kernel space.

Step 1. Call the SPI core-level function spi_busnum_to_master(), to get a description of the SPI controller architecture:

master = spi_busnum_to_master(bus_num);

// bus_num is the controller number of the SPI device for reading and writing

// master is the spi_master struct type pointer to describe the SPI controller.

Step 2. Call the SPI core layer function by the name of the spi device on the core layer to get the structure that is mounted on the SPI controller to describe the SPI device:

snprintf(str, sizeof(str), “%s.%u”, dev_name(&master->dev), cs);

dev = bus_find_device_by_name(&spi_bus_type, NULL, str);

spi = to_spi_device(dev);

//spi_buf_type is a bus_type structure type variable that describes the SPI bus

// spi is to describe the SPI peripheral spi_device structure type pointer

Step 3. Calling the SPI core layer function will spi_transfer added to spi_message queue. spi_message_init(&m)

spi_message_add_tail(&t, &m)

//t is spi_transfer structure type variable

//m isspi_message structure type variable

Step 4. Call the SPI Core Layer Read-Write function to read and write to device

status = spi_sync(spi, &m);

status = spi_async(spi, &m)

// spi is the spi_device structure type pointer to describe the SPI device

//spi_sync function is used to reads and writes SPI synchronously

//spi_async function is used to reads and writes SPI asynchronous

The code example is as follows: This code sample is for reference only, not for practical use.

// Incoming SPI controller bus number and processor number
static unsigned int busnum;
module_param(busnum, uint, 0);
MODULE_PARM_DESC(busnum, "SPI bus number (default=0)");

static unsigned int cs;
module_param(cs, uint, 0);
MODULE_PARM_DESC(cs, "SPI processor select (default=0)");

extern struct bus_type spi_bus_type;

// Declare the structure of the SPI controller static struct spi_master *master;

// Declare the structure of SPI peripherals
static struct spi_device *spi_device;

static int __init spidev_init(void) {
    char *spi_name;
    struct device *spi;

    master = spi_busnum_to_master(busnum);
    spi_name = kzalloc(strlen(dev_name(&master->dev)), GFP_KERNEL);

if (!spi_name)
        return -ENOMEM;

snprintf(spi_name, sizeof(spi_name), "%s.%u", dev_name(&master->dev), cs);
    spi = bus_find_device_by_name(&spi_bus_type, NULL, spi_name);
    if (spi == NULL)
        return -EPERM;

    spi_device = to_spi_device(spi);
    if (spi_device ==NULL)
        return -EPERM;

    put_device(spi);
    kfree(spi_name);

    return 0;
}

int spi_dev_write(, void *buf, unsigned long len, int buswidth)
{
    struct spi_device *spi = spi_device;
struct spi_transfer t = {
      .speed_hz = 2000000,
      .tx_buf = buf,
          // buf needs to fill in device addr, register addr, write data and other information according to peripheral device specifications
      .len = len,
   };
struct spi_message m;
spi->mode = SPI_MODE_0;

if (buswidth == 16)
      t.bits_per_word = 16;
   else
      t.bits_per_word = 8;

   if (!spi) {
      return - ENODEV;
   }

   spi_message_init(&m);
   spi_message_add_tail(&t, &m);
   return spi_sync(spi, &m);
}

int spi_dev_read(unsigned char devaddr, unsigned char reg_addr,  void *buf, size_t len)
{
     struct spi_device *spi = spi_device;
   int ret;
   u8 txbuf[4] = { 0, };
   struct spi_transfer t = {
      .speed_hz = 2000000,
      .rx_buf = buf,
      .len = len,
      };
   struct spi_message   m;
 spi->mode = SPI_MODE_0;

   if (!spi) {
      return -ENODEV;
   }
   txbuf[0] = devaddr;
     txbuf[1] = 0;
     txbuf[2] = reg_addr; //txbuf[1] &txbuf[2] Fill in 1 byte or 2 bytes depending on the device bit width, this example is 2 bytes bit width
   t.tx_buf = txbuf;

   spi_message_init(&m);
   spi_message_add_tail(&t, &m);
   ret = spi_sync(spi, &m);

   return ret;
}

6.3.2. SPI Read-Write Program Example In User Space

This operation example reads and writes to SPI device mounted on SPI controller 0 in user space. (Specific implementations can refer to tools/spi/spidev_test.c)

Step 1: Open the device file corresponding to the SPI bus to get the file descriptor.

static const char *device = "/dev/spidev32766.0";
…
fd = open(device, O_RDWR);
if (fd < 0)
   pabort("can't open device");

Note: The default node for peripherals mounted on SPI Controller 1 is “dev/spidev32765.0”

The default node for peripherals mounted on SPI Controller 2 is “dev/spidev32764.0”

The default node for peripherals mounted on SPI Controller 3 is “dev/spidev32763.0”

Simply replace the node name and the rest will be the same as the device mounted on the SPI controller 0.

Step 2: Setting SPI transfer mode through ioctl:

/*
 * spi mode
 */
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
   pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
if (ret == -1)
   pabort("can't get spi mode");

Note: Refer to the following figure or the kernel code for the model value configuration include/linux/spi/spi.h,

Ex. mode = SPI_MODE_3 | SPI_LSB_FIRST;

#define   SPI_CPHA   0x01         /* clock phase */
#define   SPI_CPOL   0x02         /* clock polarity */
#define   SPI_MODE_0   (0|0)         /* (original MicroWire) */
#define   SPI_MODE_1   (0|SPI_CPHA)
#define   SPI_MODE_2   (SPI_CPOL|0)
#define   SPI_MODE_3   (SPI_CPOL|SPI_CPHA)

Step 3: Setting SPI transmission bandwidth through ioctl:

/*
 * bits per word
 */
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
   pabort("can't set bits per word");

ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
   pabort("can't get bits per word");

Step 4: Set SPI transfer speed through ioctl (generally recommended speed = 25M):

/*
 * max speed hz
 */
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
   pabort("can't set max speed hz");

ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
   pabort("can't get max speed hz");

Step 5: Read and write data using ioctl:

ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
   pabort("can't send spi message");

Note: tr transmits the first address of the spi_ioc structure array of a message.