Article summary
The Serial Peripheral Interface (SPI) Bus is used for communication between microcontrollers and other digital devices. On the Raspberry Pi it can be very handy for doing things like acquiring values from an Analog-to-Digital converter, reading a temperature sensor, or communicating with another microcontroller.
When using SPI on the Raspberry Pi, there are many options for controlling and interacting with the bus.
Using Straight-up C
If you are comfortable coding in C, you can access the SPI bus using the Unix ioctl()
function.
// Mode options: SPI_LOOP, SPI_CPHA, SPI_CPOL, SPI_LSB_FIRST,
// SPI_CS_HIGH, SPI_3WIRE, SPI_NO_CS, SPI_READY
static uint8_t mode = SPI_CPHA;
static uint8_t bits = 8;
static uint32_t speed = 3000000;
static uint16_t delay = 0;
SPI_RESULT_T SPI_DoTransfer(char * device, uint8_t * p_rx_buf, uint8_t * p_tx_buf, uint32_t len)
{
int fd;
int ret;
// Open the device
fd = open(device, O_RDWR);
if (fd < 0) return SPI_ERR_OPENING_DEVICE;
// Set SPI mode
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) return SPI_ERR_CFGING_PERIPH;
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1) return SPI_ERR_CFGING_PERIPH;
// Set the Bits per Word
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1) return SPI_ERR_CFGING_PERIPH;
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1) return SPI_ERR_CFGING_PERIPH;
// Set the max speed
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) return SPI_ERR_CFGING_PERIPH;
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1) return SPI_ERR_CFGING_PERIPH;
struct spi_ioc_transfer tr =
{
.tx_buf = (unsigned long)p_tx_buf,
.rx_buf = (unsigned long)p_rx_buf,
.len = len,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
close(fd);
if (ret < 1)
return SPI_XFER_ERROR;
else
return SPI_SUCCESS;
}
Ruby Gem: PiPiper
Another option for interacting with the SPI bus is by using a Ruby gem. There are several out there, but (so far) the one I like the best is PiPiper. PiPiper is a nice gem that provides easy access to not only the SPI peripheral but also I2C and GPIO pins. One nice feature that PiPiper provides is the ability to poll a SPI device at regular intervals. You can also setup watches
on GPIO pins which makes it really simple to execute code on pin transitions.
To test out PiPiper, I made a Ruby class to read temperature data from a SPI thermocouple amplifier (Maxim’s MAX6675). Here is what it looks like.
require 'pi_piper'
class Thermocouple
include PiPiper
def initialize()
end
def read_sensor
PiPiper::Spi.begin do |spi|
PiPiper::Spi.set_mode(0,1)
# Setup the chip select behavior
spi.chip_select_active_low(active_low = true, Spi::CHIP_SELECT_0)
# Set the bit order to MSB
spi.bit_order Spi::MSBFIRST
# Set the clock divider to get a clock speed of 2MHz
spi.clock 2000000
# Activate the chip select
spi.chip_select do
# Do the SPI write
return SensorResponse.new spi.read(2)
end
end
end
end
Before using PiPiper, you must install Ruby and the Ruby Development gem on your pi.
sudo apt-get install ruby ruby1.9.1-dev
Then all you have to do is install the pi_piper gem.
sudo gem install pi_piper
One important note is that the PiPiper Ruby gem is still under development. I noticed that the current version on Rubygems.org does not support changing the SPI mode. I tried pulling the latest code from the master branch on GitHub, and the issue appears to be resolved there.
I am interested to use your “Using Straight-up C” which libraries should I use besides the #include
sys / ioctl . h