Low-level SPI peripheral driver. More...

Detailed Description

Low-level SPI peripheral driver.

This interface defines an abstraction for using a CPU's hardware SPI units. The interface only supports SPI master mode.

As SPI buses can have multiple devices connected to them they are to be considered as shared resources. To reflect this, the SPI interface is based on a transaction model. This requires that the bus needs to be acquired before usage and released afterwards, using the spi_acquire() and the spi_release() functions.

This interface supports both software and hardware chip select lines. This is reflected by the cpi_cs_t type, which overloads the gpio_t type with platform specific values for defining platform dependent hardware chip select lines.

Some devices have however very uncommon requirements on the usage and the timings of their chip select line. For those cases this interface allows to manage the chip select line manually from the user code (e.g. by calling gpio_set/clear explicitly) while deactivating the SPI driver internal chip select handling by passing GPIO_UNDEF as CS parameter.

In the time, when the SPI bus is not used, the SPI unit should be in low-power mode to save energy.

The SPI unit's initialization is split into 3 parts:

  1. spi_init() should be called once for each SPI unit defined by a board during system initialization.
  2. spi_init_cs() should be called during device driver initialization, as each chip select pin/line is used uniquely by a specific device, i.e. chip select lines are no shared resource.
  3. spi_acquire() needs to be called for each new transaction. This function configures the bus with specific parameters (clock, mode) for the duration of that transaction.

(Low-) Power Implications

As SPI buses are shared peripherals and the interfaces implements a transaction based paradigm, we leverage this for the SPI peripherals power management. After calling spi_init(), the SPI peripheral should be completely powered off (e.g. through peripheral clock gating). It should subsequently only be powered on and enabled in between spi_acquire() and spi_release() blocks.

In case the SPI driver implementation puts the active thread to sleep during data transfer (e.g. when using DMA), the implementation might need to block certain power states during that time.

Files

file  spi.h
 Low-level SPI peripheral driver interface definition.
 

Data Structures

struct  spi_gpio_mode_t
 SPI gpio mode. More...
 

Macros

#define SPI_DEV(x)   (x)
 Default SPI device access macro.
 
#define SPI_UNDEF   (UINT_MAX)
 Define global value for undefined SPI device.
 
#define SPI_CS_UNDEF   (GPIO_UNDEF)
 Define value for unused CS line.
 
#define SPI_HWCS(x)   (SPI_CS_UNDEF)
 Default SPI hardware chip select access macro. More...
 

Typedefs

typedef unsigned int spi_t
 Default type for SPI devices.
 
typedef gpio_t spi_cs_t
 Chip select pin type overlaps with gpio_t so it can be casted to this.
 

Enumerations

enum  {
  SPI_OK = 0, SPI_NODEV = -ENXIO, SPI_NOCS = -EINVAL, SPI_NOMODE = -EINVAL,
  SPI_NOCLK = -EINVAL
}
 Status codes used by the SPI driver interface. More...
 
enum  spi_mode_t { SPI_MODE_0 = 0, SPI_MODE_1, SPI_MODE_2, SPI_MODE_3 }
 Available SPI modes, defining the configuration of clock polarity and clock phase. More...
 
enum  spi_clk_t {
  SPI_CLK_100KHZ = 0, SPI_CLK_400KHZ, SPI_CLK_1MHZ, SPI_CLK_5MHZ,
  SPI_CLK_10MHZ
}
 Available SPI clock speeds. More...
 

Functions

void spi_init (spi_t bus)
 Basic initialization of the given SPI bus. More...
 
void spi_init_pins (spi_t bus)
 Initialize the used SPI bus pins, i.e. More...
 
int spi_init_cs (spi_t bus, spi_cs_t cs)
 Initialize the given chip select pin. More...
 
void spi_deinit_pins (spi_t dev)
 Change the pins of the given SPI bus back to plain GPIO functionality. More...
 
gpio_t spi_pin_miso (spi_t dev)
 Get the MISO pin of the given SPI bus. More...
 
gpio_t spi_pin_mosi (spi_t dev)
 Get the MOSI pin of the given SPI bus. More...
 
gpio_t spi_pin_clk (spi_t dev)
 Get the CLK pin of the given SPI bus. More...
 
int spi_init_with_gpio_mode (spi_t bus, spi_gpio_mode_t mode)
 Initialize MOSI/MISO/SCLK pins with adapted GPIO modes. More...
 
int spi_acquire (spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
 Start a new SPI transaction. More...
 
void spi_release (spi_t bus)
 Finish an ongoing SPI transaction by releasing the given SPI bus. More...
 
uint8_t spi_transfer_byte (spi_t bus, spi_cs_t cs, bool cont, uint8_t out)
 Transfer one byte on the given SPI bus. More...
 
void spi_transfer_bytes (spi_t bus, spi_cs_t cs, bool cont, const void *out, void *in, size_t len)
 Transfer a number bytes using the given SPI bus. More...
 
uint8_t spi_transfer_reg (spi_t bus, spi_cs_t cs, uint8_t reg, uint8_t out)
 Transfer one byte to/from a given register address. More...
 
void spi_transfer_regs (spi_t bus, spi_cs_t cs, uint8_t reg, const void *out, void *in, size_t len)
 Transfer a number of bytes to/from a given register address. More...
 

Macro Definition Documentation

◆ SPI_HWCS

#define SPI_HWCS (   x)    (SPI_CS_UNDEF)

Default SPI hardware chip select access macro.

Per default, we map all hardware chip select lines to be not defined. If an implementation makes use of HW chip select lines, this value needs to be overridden by the corresponding CPU.

Definition at line 112 of file spi.h.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum

Status codes used by the SPI driver interface.

Deprecated:
Use negative errno codes instead. The enum is still provided for backwards compatibility
Enumerator
SPI_OK 

everything went as planned

SPI_NODEV 

invalid SPI bus specified

SPI_NOCS 

invalid chip select line specified

SPI_NOMODE 

selected mode is not supported

SPI_NOCLK 

selected clock value is not supported

Definition at line 136 of file spi.h.

◆ spi_clk_t

enum spi_clk_t

Available SPI clock speeds.

The actual speed of the bus can vary to some extend, as the combination of CPU clock and available prescaler values on certain platforms may not make the exact values possible.

Enumerator
SPI_CLK_100KHZ 

drive the SPI bus with 100KHz

SPI_CLK_400KHZ 

drive the SPI bus with 400KHz

SPI_CLK_1MHZ 

drive the SPI bus with 1MHz

SPI_CLK_5MHZ 

drive the SPI bus with 5MHz

SPI_CLK_10MHZ 

drive the SPI bus with 10MHz

Definition at line 177 of file spi.h.

◆ spi_mode_t

enum spi_mode_t

Available SPI modes, defining the configuration of clock polarity and clock phase.

RIOT is using the mode numbers as commonly defined by most vendors (https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers):

  • MODE_0: CPOL=0, CPHA=0 - The first data bit is sampled by the receiver on the first SCK rising SCK edge (this mode is used most often).
  • MODE_1: CPOL=0, CPHA=1 - The first data bit is sampled by the receiver on the second rising SCK edge.
  • MODE_2: CPOL=1, CPHA=0 - The first data bit is sampled by the receiver on the first falling SCK edge.
  • MODE_3: CPOL=1, CPHA=1 - The first data bit is sampled by the receiver on the second falling SCK edge.
Enumerator
SPI_MODE_0 

CPOL=0, CPHA=0.

SPI_MODE_1 

CPOL=0, CPHA=1.

SPI_MODE_2 

CPOL=1, CPHA=0.

SPI_MODE_3 

CPOL=1, CPHA=1.

Definition at line 161 of file spi.h.

Function Documentation

◆ spi_acquire()

int spi_acquire ( spi_t  bus,
spi_cs_t  cs,
spi_mode_t  mode,
spi_clk_t  clk 
)

Start a new SPI transaction.

Starting a new SPI transaction will get exclusive access to the SPI bus and configure it according to the given values. If another SPI transaction is active when this function is called, this function will block until the other transaction is complete (spi_relase was called).

Note
This function expects the bus and the cs parameters to be valid (they are checked in spi_init and spi_init_cs before)
Parameters
[in]busSPI device to access
[in]cschip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver
[in]modemode to use for the new transaction
[in]clkbus clock speed to use for the transaction
Return values
0success
Returns
-EINVAL Invalid mode in mode
-EINVAL Invalid clock in clock
-ENOTSUP Unsupported but valid mode in mode
-ENOTSUP Unsupported but valid clock in clock

◆ spi_deinit_pins()

void spi_deinit_pins ( spi_t  dev)

Change the pins of the given SPI bus back to plain GPIO functionality.

The pin mux of the MISO, MOSI and CLK pins of the bus will be changed back to default (GPIO) mode and the SPI bus is powered off. This allows to use the SPI pins for another function and return to SPI functionality again by calling spi_init_pins()

If you want the pin to be in a defined state, call gpio_init() on it.

The bus MUST not be acquired before initializing it, as this is handled internally by the spi_deinit_pins() function!

Calls to spi_acquire() will block until spi_init_pins() is called again.

Note
Until this is implemented on all platforms, this requires the periph_spi_reconfigure feature to be used.
Parameters
[in]devthe device to de-initialize

◆ spi_init()

void spi_init ( spi_t  bus)

Basic initialization of the given SPI bus.

This function does the basic initialization including pin configuration for MISO, MOSI, and CLK pins. After initialization, the given device should be in power down state.

This function is intended to be called by the board initialization code during system startup to prepare the (shared) SPI device for further usage. It uses the board specific initialization parameters as defined in the board's periph_conf.h.

Errors (e.g. invalid bus parameter) are not signaled through a return value, but should be signaled using the assert() function internally.

Note
This function MUST not be called more than once per bus!
Parameters
[in]busSPI device to initialize

◆ spi_init_cs()

int spi_init_cs ( spi_t  bus,
spi_cs_t  cs 
)

Initialize the given chip select pin.

The chip select can be any generic GPIO pin (e.g. GPIO_PIN(x,y)), or it can be a hardware chip select line. The existence and number of hardware chip select lines depends on the underlying platform and the actual pins used for hardware chip select lines are defined in the board's periph_conf.h.

Define the used chip select line using the SPI_HWCS(x) macro for hardware chip select line x or the GPIO_PIN(x,y) macro for using any GPIO pin for manual chip select.

Parameters
[in]busSPI device that is used with the given CS line
[in]cschip select pin to initialize
Return values
0success
-ENXIOinvalid device
-EINVALinvalid CS pin/line

◆ spi_init_pins()

void spi_init_pins ( spi_t  bus)

Initialize the used SPI bus pins, i.e.

MISO, MOSI, and CLK

After calling spi_init, the pins must be initialized (i.e. spi_init is calling this function internally). In normal cases, this function will not be used. But there are some devices (e.g. CC110x), that use SPI bus lines also for other purposes and need the option to dynamically re-configure one or more of the used pins. So they can take control over certain pins and return control back to the SPI driver using this function.

This function must be called after spi_deinit_pins to return the pins to SPI operation.

The pins used are configured in the board's periph_conf.h.

Parameters
[in]busSPI device the pins are configure for

◆ spi_init_with_gpio_mode()

int spi_init_with_gpio_mode ( spi_t  bus,
spi_gpio_mode_t  mode 
)

Initialize MOSI/MISO/SCLK pins with adapted GPIO modes.

Parameters
[in]busSPI device that is used with the given CS line
[in]modea struct containing the 3 modes to use on each pin
Return values
0success
<0error

◆ spi_pin_clk()

gpio_t spi_pin_clk ( spi_t  dev)

Get the CLK pin of the given SPI bus.

Parameters
[in]devThe device to query
Note
Until this is implemented on all platforms, this requires the periph_spi_reconfigure feature to be used.
Returns
The GPIO used for the SPI CLK line.

◆ spi_pin_miso()

gpio_t spi_pin_miso ( spi_t  dev)

Get the MISO pin of the given SPI bus.

Parameters
[in]devThe device to query
Note
Until this is implemented on all platforms, this requires the periph_spi_reconfigure feature to be used.
Returns
The GPIO used for the SPI MISO line.

◆ spi_pin_mosi()

gpio_t spi_pin_mosi ( spi_t  dev)

Get the MOSI pin of the given SPI bus.

Parameters
[in]devThe device to query
Note
Until this is implemented on all platforms, this requires the periph_spi_reconfigure feature to be used.
Returns
The GPIO used for the SPI MOSI line.

◆ spi_release()

void spi_release ( spi_t  bus)

Finish an ongoing SPI transaction by releasing the given SPI bus.

After release, the given SPI bus should be fully powered down until acquired again.

Parameters
[in]busSPI device to release

◆ spi_transfer_byte()

uint8_t spi_transfer_byte ( spi_t  bus,
spi_cs_t  cs,
bool  cont,
uint8_t  out 
)

Transfer one byte on the given SPI bus.

Parameters
[in]busSPI device to use
[in]cschip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver
[in]contif true, keep device selected after transfer
[in]outbyte to send out
Returns
the received byte

◆ spi_transfer_bytes()

void spi_transfer_bytes ( spi_t  bus,
spi_cs_t  cs,
bool  cont,
const void *  out,
void *  in,
size_t  len 
)

Transfer a number bytes using the given SPI bus.

Parameters
[in]busSPI device to use
[in]cschip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver
[in]contif true, keep device selected after transfer
[in]outbuffer to send data from, set NULL if only receiving
[out]inbuffer to read into, set NULL if only sending
[in]lennumber of bytes to transfer

◆ spi_transfer_reg()

uint8_t spi_transfer_reg ( spi_t  bus,
spi_cs_t  cs,
uint8_t  reg,
uint8_t  out 
)

Transfer one byte to/from a given register address.

This function is a shortcut function for easier handling of SPI devices that implement a register based access scheme.

Parameters
[in]busSPI device to use
[in]cschip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver
[in]regregister address to transfer data to/from
[in]outbyte to send
Returns
value that was read from the given register address

◆ spi_transfer_regs()

void spi_transfer_regs ( spi_t  bus,
spi_cs_t  cs,
uint8_t  reg,
const void *  out,
void *  in,
size_t  len 
)

Transfer a number of bytes to/from a given register address.

This function is a shortcut function for easier handling of SPI devices that implement a register based access scheme.

Parameters
[in]busSPI device to use
[in]cschip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver
[in]regregister address to transfer data to/from
[in]outbuffer to send data from, set NULL if only receiving
[out]inbuffer to read into, set NULL if only sending
[in]lennumber of bytes to transfer