Setting up SPI with Raspberry Pi

In this post I discuss the process of setting up SPI with Raspberry Pi. I setup a basic test program and write the output to a Logic analyzer.
For those unfamiliar with SPI, SPI means Serial Peripheral Interface. SPI is an interface bus commonly in embedded systems such to interface with sensors, Ehternet, SD cards and the like. The advantage in using SPI is hosts a select line to choose the device you wish to talk to meaning multiple devices can be on one SPI. To read more about SPI there is an amazing post on Sparkfun.
First we need to enable to SPI interfaces to use it
Using the raspberry configuration tool:

sudo raspi-config

Or using the manual way:

sudo nano /etc/modprobe.d/raspi-blacklist.conf

and add “#” to the following line so it reads as follows

#blacklist spi-bcm2708

Perform:

sudo reboot

Now let’s test the SPI port

ls /dev/spidev*

My list consists of the following:

/dev/spidev0.0
/dev/spidev0.1

which is correct. The 2 spidev devices shown are both the same SPI peripherals represented by 0, second number represents the chip select pins CS0 and CS1.
With SPI it is better to use a library due to the nature of SPI.
To use the SPI, we need to write to the SPI device “file”. This file is a location in memory that the SPI device will use to communicate to the SPI hardware via the i/o controller via a system call.

fd = open("/dev/spidev0.0", O_RDWR);

fd is the file descriptor and spidev0.0 is SPI device. With the device active and open we can pass SPI data to SPI hardware via the IOCTL (input/output control) using system calls.
The Si4463 is designed to run at 10MHz therefore our program needs to be setup for 10MHz

unsigned int speed = 10000000;
ioctl (fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

The spidev can be used to create a structure associated to spi_ioc_transfer that will transmit and receive data. The struct be used both to transfer data and configuration settings that will then be passed to the IOCTL to the target device. The struct variable created is named spi and can be passed by reference to ioctl. All of this is then passed to the memset to allocate memory for the struct:

struct spi_ioc_transfer spi;
memset (&spi, 0, sizeof (spi)) ;

The first byte of SDI data will be one of the firmware commands followed by n bytes of parameter data
which will be variable depending on the specific command. Using a simple code, with one bite width, we can create SPI communication:

gcc -o pispi pispi.c
./pispi -D /dev/spidev0.0
Fist iteration of code:
…
int main (void)
{
   fd = open("/dev/spidev0.0", O_RDWR);
   unsigned int speed = 1000000;
   ioctl (fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

   while (1)
   {
      for (int i = 0; i < sizeof(hello); i++)
      {
         R = spiTxRx(hello[i]);
         cout << R; //output to consle
      }
   }
}
…
int spiTxRx(unsigned char txDat)
{
   unsigned char rxDat;
  struct spi_ioc_transfer spi;
  memset (&spi, 0, sizeof (spi)); /standard SPI Setup

  spi.tx_buf        = (unsigned long)&txDat;
  spi.rx_buf        = (unsigned long)&rxDat;
  spi.len           = 1;

  ioctl (fd, SPI_IOC_MESSAGE(1), &spi);

  return rxDat;
}

Setup of Raspberry pi 3 and Logic analyzer.

Troubleshooting

First attempt of reading the SPI on the Pi

In above logic analyzer snapshot we can see that the SPI enable and the MOSI are not aligned. In addition, the SPI clock is not consistent. Taking a look into the code could no issues could be identified. Additional sample code was looked up online and the pi was retested, data was missing bits but the clock appeared to be better.
After more investigation and work with the code it was identified that the logic analyzer was set to the wrong frequency and a second test of original code was performed and yielded the following:

Second attempt of reading the SPI on the Pi

Zooming in to one value we can see we are clearly in the min range required for SPI timing parameters

Zoomed in view of one of the data points

To get the tDD and tDE values I would need to zoom into the nanosecond (ns) level, unfortunately, my logic analyzer does not allow to view ns level so I am unable to see the max value for tDD and tDE.
Changing the set up and looping back the inputs of the Pi we can read our own values of what is sent. They are as follows:

48 45 4C 4F 20 57
4F 52 4C 44 FF 00
FF 0F F0

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.