Are you already comfortable working with Serial Peripheral Interface (SPI) parts and looking for a challenge? We suspect many of you have cut your teeth on 8-bit through 32-bit microcontrollers but how much time have you spent playing with hardware interfaces on embedded Linux? Here a new quest, should you choose to accept it. [Matt Porter] spoke in detail about the Linux SPI Subsystem during his presentation at FOSDEM 2017. Why not grab an embedded Linux board and try your hand at connecting some extra hardware to one of the SPI buses?
The hardware side of this is exactly what you’d expect from any embedded SPI you’ve worked on: MOSI, MISO, a clock, and a slave select. [Matt] gives a succinct overview of SPI and reading datasheets. Our own [Elliot Williams] has done an excellent job of digging through the basics and most common gotchas if you need to get up to speed on all the SPI basics.
The fun details in the talk start at about 18:30 into the video when [Matt] jumps into the Linux side of SPI. You need a controller driver and a protocol driver. The controller driver is responsible for dealing with the pins (actual hardware) and the protocol driver handles the job of making sense of the SPI packets (messages containing any number of transfers) going in or out. In other words, the controller drive just want bits and pushes them in or out on hardware, the protocol driver makes those bits meaningful to the Linux system.
Adding SPI devices (think devices like LCDs and sensors) to your own embedded systems means telling the OS the particulars about that hardware, like max speed and SPI mode. There are three ways to handle this but the Device Tree is the preferred method for modern systems. This paves the way for the controller driver which implements an API set that the Linux SPI subsystem will use to work with your new hardware.
Protocol drivers follow the standard Linux driver model and are pretty straight forward. With these two drivers in place the new device is hooked into the OS and opens up common SPI API calls: spi_async(), spi_sync(), spi_write(), and spi_read(), and a few others.
This sounds like a ton of fun to us, but if you feel in a bit over your head you could take on another fun topic: using Direct Memory Access (DMA) for your SPI needs.
[Thanks for the tip Drew]
It does sound like kind of a pain (haven’t watched yet) compared to what we typically have to do on MCUs, but I really appreciate that it’s open and available under Linux, as is the I2C subsystem. I’ve wanted to interface the I2C bus several times before (*not* the EDID video one) and Windows just *doesn’t* support it (same for SPI). Getting to the I2C bus via software is a huge undertaking, and the only realistic option is physically soldering an MCU to the bus (onto a memory stick or anywhere else)
It should mentioned that depending on the SPI controller Linux might use DMA to do the transfer. It is therefore not allowed to pass rx/tx buffers to spi_sync/spi_async that are on the kernel stack or that share a cache line with other data that might be accessed by the CPU during the transfer.
SPI and I2C are pretty accessible on the raspberry pi. No obvious dma for spi and in my experience, i2c works better there.
is this also supported in NuttX or Zephyr?