Serial Peripheral Interface(SPI) is a well-known communication protocol used in Embedded Systems. Many external devices/IC’s such as sensors, analogue to digital converters (ADC), displays etc, make use of SPI for communicating with the host controller. Hence, nowadays most of the microcontrollers come with modules that can easily support the use of SPI.
About three years ago, when I was working with microcontrollers such as PIC and AVR, It was necessary to write the SPI driver in order to use the SPI in our application. The SPI driver is nothing but a bunch of API’s that can be used by the Application Engineer to interface external devices. It is mainly responsible for handling the lower level stuff that deals with the configuration of the SPI registers. In order to write a clean and efficient SPI driver, one needs to be very comfortable with bit operations, reading and interpreting information from datasheets.
However, today there are microcontrollers such as TICC3200, CC3100, ESP8266 etc that come with SDK support that will already have the SPI Library. This fastens the development process as the Firmware Engineer need not take the pain of writing the driver.
When we come to using SPI on Embedded Linux such as on BeagleBone(BB), things are slightly different from those of what I described above. On Embedded Linux, there exists an operating system(OS) with a Kernel that handles all operations in the system. In BB, we can use SPI interface via an SPI device driver. The SPI device driver is a Kernel specific code that is responsible for loading the SPI module and exposing it to the userspace.
The SPI device driver only loads the SPI kernel module and makes it available for the userspace. However, If the Application Engineer needs to use the SPI module, he will still need an SPI Library or API’s relevant to performing the standard SPI operations. You can think of it as three layers of software, the first being the SPI device driver, the second layer being the SPI Library and the third being the application layer.
I am currently working on a personal project for which I need to interface an ADIS16260 gyro sensor which uses an SPI interface. I found many examples and test codes for using it on the BB, however, I did not find any standard SPI Library for the BB. When I am working on any projects, I always make effort to follow the best software design practices while keeping my code base organized, modularized and clean. This will make debugging easier and avoid bugs from creeping in as the complexity of the software increases. Hence, I decided to write a standard SPI Library that can be used on the BB for development of any applications.
1. Load the SPI Device Tree Overlay
In order to use the SPI module, we need to load the SPI device tree overlay. The overlay will basically tell the kernel to expose the SPI module for use and will configure the GPIO’s as SPI lines. If you are wondering how to do this, please follow the instructions described here for loading the SPI overlay. The below shows the pins we will be using for SPI communication.
1 2 3 4 5 |
spidev1.0 - SPI Pins P9_17 - CS P9_18 - DOUT P9_21 - DIN P9_22 - SCLK |
2. Source Code and Using the SPI Library on BeagleBone
On BB, the principle working of the SPI is same as that of on any controllers i.e. the underlying communication protocol is the same. The BB supports half duplex communication using the standard read() and write() operations. It also supports full duplex communication. To enable full-duplex communication, we must let the device driver do this by performing several ioctl () requests. This SPI Library supports full duplex communication and gives easy flexibility to change the SPI configuration such as the bus frequency, SPI mode, bits per transaction etc.
Once, you’re done loading the SPI module as described in the previous step, you should be able to see the SPI device as shown below.
1 2 |
root@beaglebone:/dev# ls spidev1.* spidev1.0 spidev1.1 |
For this demonstration, we will be using the spidev1.0. It is very easy to include this SPI Library to your current project. The only thing you need to do is, include the below header(.h) and source files(.c) in your project as shown below and start using the API’s.
1 2 3 4 |
/* Header file to include */ SPI.h /* Source file to include */ SPI.c |
And then you are good to go. You can download the SPI Library from my Github repo or clone it by the running the below command.
1 |
git clone https://github.com/deeplyembeddedWP/BeagleBone-SPI-Library.git |
3. Testing and Analysis of the BeagleBone SPI Library

The image on the left-hand side shows the environment setup to test and analyze the SPI Library. The Library has been tested to work in all the SPI modes.
I was able to debug the SPI Library using a USB Logic Analyzer. I used a Saleae Logic Analyzer, it’s pretty cool for analyzing the bus signals such as SPI, UART, I2C, CAN, PCM, Modbus and much more. It can also be used for debugging Analog and Digital Circuits. It’s a good alternative to the expensive DSO’s for an Engineer to do his/her in-house projects or debug their hardware without heavy investments. But take note that, you cannot debug issues that may creep due to noise and parasitics using USB Logic Analyzers, for Eg. If there are some noise spikes on the SPI bus lines, it cannot be detected by USB Logic Analyzers. For debugging such deeper level issues, DSO’s are much better.
I configured the SPI to work in Mode0 i.e. The clock idle state is LOW and the data changes during the rising edge of the clock. Then I captured the output signals as shown in the image above. From the above image, it can be clearly seen that I am performing two transactions back to back in a chunk of 8 bits. The first data byte transferred is “0xAA” and the second one is “0x0F”. You can also see that the chip select line (Channel 3) goes active low before the transaction starts and goes back high once it ends. Hence, the signals look good and all seems to be working fine.
This SPI library has been tested to work well with a well-defined and easy to use API’s. However, if you still find any bugs or would like to suggest changes for better, please feel free to update the Library or comment below. Happy learning and Adios 🙂
Love your stuff but some things from a non programmers perspective.
First off if i get your stuff from github and try to compile the Main.c i need to add #include “SPI.c” to your program and because of path issues i just put the SPI.c snd SPI.h in the same location after that it compiles correctly. Above when you configure your SPI you say your using spidev1.0 and P9_17 – CS, P9_18 – DOUT, P9_21 – DIN, P9_22 – SCLK. But those pins are for spidev0.0. spidev1.0 should be P9_28 – CS, P9_29 – DOUT, P9_30 – DIN, P9_31 – SCLK. Now if i look at the spidev1.0 pins and run your program as written i get data just as i expect. Now i am using the latest 4.9 kernel, you don’t say what kernel your using which seems to be very important. I am using the default BB-SPIDEV1-00A0.dtbo loaded at boot time. Why i am here is that using all the spi loopback tests i see that run i do not read data in even though i have the DO and DI lined tied together. I was thinking there is something deeper here involved and thats why i dont see anything other than on the scope.
maybe you have some more information?