SPI is another type of clocked serial bus. It typically requires at least four pins 1 :

  • CLK – Master timing reference for all SPI transfers

  • MOSI – Master Out Slave In – data line FROM the master TO the slaves

  • MISO – Master In Slave Out – data line FROM the slaves TO the master

  • CS – At least one Chip Select

Numerous options complicate the use of SPI:

  • Clock Polarity – The clock signal may or may not need to be inverted

  • Clock Phase – The edge of the clock actually used varies between SPI devices

  • Data Order – Some devices expect/require Most Significant Bit (MSB) first, others only work with Least Significant Bit (LSB) first

  • Data Width – Some SPI devices are 8-bit, some are 12-bit, some are 16-bit, etc.

The SPI support routines in SNAPpy can deal with all these variations, but you will have to make sure the options you specify in your SNAPpy scripts match the settings required by your external devices. Dedicated SPI support (master emulation only) has been added to the set of SNAPpy built-in functions. Four functions (callable from SNAPpy but implemented in optimized C code) support reading and writing SPI data:

  • spiInit() – Setup for SPI (supporting many options!)

  • spiWrite() – Send data out SPI

  • spiRead() – Receive data in from SPI (3 wire only)

  • spiXfer() – Bidirectional SPI transfer (4 wire only)

Three-wire SPI interfaces omit the MISO pin. Some three-wire devices are read-only, and you must use the spiRead() function. Even if the slave does send data in a three-wire SPI interface, it will do so over the MOSI pin. Four-wire SPI interfaces transfer data in both directions simultaneously and should use the spiXfer() function. Some SPI devices are write-only, and you should use spiWrite() function to send data to them for both three-wire and four-wire hookup.

The data width for SPI devices is not standardized. Devices that use a data width that is a multiple of 8 are trivial (for example, send 2 bytes to make 16 bits total). However, device widths such as 12 bits are common. To support these “non-multiples-of-8” device widths, you can specify how much of the last byte to actually send or receive. For example:

spiWrite("\x12\x34", 4)

This will send a total of 12 bits: all 8 bits of the first byte (0x12), and 4 bits of the second byte (0x34). The “send LSB first” setting will determine which nibble of the second byte (the first or last) is sent. This setting is specified as part of the spiInit() function.

To allow these SPI functions to be as fast as possible, the IO pins used for CLK, MOSI, and MISO are fixed. The IO pins that are reserved for SPI varies by platform, so refer to RF Modules. These pins are not dedicated for SPI, so if you are not using the SPI functions, you may use the reserved pins for other functions.

You will also need as many Chip Select (CS) pins as you have external SPI devices. You can choose any available GPIO pin(s) to be your SPI CS pins. The basic program flow becomes:

# Select the desired SPI device, assuming the chip select is active-low
writePin(your_cs_pin, False)

# Transfer data to the selected SPI device

# Deselect the SPI device, assuming the chip select is active-low
writePin(your_cs_pin, True)

SPI reads are handled in a similar fashion.

The specifics of which bytes to send to a given SPI slave device (and what the response will look like) depend on the SPI device itself. You will have to refer to the manufacturer’s data sheet for any given device to which you wish to interface.



SPI also exists in a three-wire variant, with the MOSI pin serving double-duty.