Low-level SPI peripheral driver. More...
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:
spi_init()
should be called once for each SPI unit defined by a board during system initialization.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.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.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 | CONFIG_SPI_DMA_THRESHOLD_BYTES 16 |
Threshold under which polling transfers are used instead of DMA TODO: determine at run-time based on SPI clock. | |
#define | SPI_DEV(x) (x) |
Default SPI device access macro. | |
#define | SPI_UNDEF (UINT_FAST8_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. | |
Typedefs | |
typedef uint_fast8_t | 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. | |
void | spi_init_pins (spi_t bus) |
Initialize the used SPI bus pins, i.e. | |
int | spi_init_cs (spi_t bus, spi_cs_t cs) |
Initialize the given chip select pin. | |
void | spi_deinit_pins (spi_t dev) |
Change the pins of the given SPI bus back to plain GPIO functionality. | |
gpio_t | spi_pin_miso (spi_t dev) |
Get the MISO pin of the given SPI bus. | |
gpio_t | spi_pin_mosi (spi_t dev) |
Get the MOSI pin of the given SPI bus. | |
gpio_t | spi_pin_clk (spi_t dev) |
Get the CLK pin of the given SPI bus. | |
int | spi_init_with_gpio_mode (spi_t bus, const spi_gpio_mode_t *mode) |
Initialize MOSI/MISO/SCLK pins with adapted GPIO modes. | |
void | spi_acquire (spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) |
Start a new SPI transaction. | |
void | spi_release (spi_t bus) |
Finish an ongoing SPI transaction by releasing the given SPI bus. | |
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. | |
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. | |
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. | |
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. | |
static uint16_t | spi_transfer_u16_be (spi_t bus, spi_cs_t cs, bool cont, uint16_t host_number) |
Transfer a 16 bit number in big endian byte order. | |
#define CONFIG_SPI_DMA_THRESHOLD_BYTES 16 |
#define SPI_CS_UNDEF (GPIO_UNDEF) |
#define SPI_HWCS | ( | x | ) | (SPI_CS_UNDEF) |
#define SPI_UNDEF (UINT_FAST8_MAX) |
typedef gpio_t spi_cs_t |
anonymous enum |
Status codes used by the SPI driver interface.
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.
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):
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. |
void 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).
[in] | bus | SPI device to access |
[in] | cs | chip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver |
[in] | mode | mode to use for the new transaction |
[in] | clk | bus clock speed to use for the transaction |
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.
[in] | dev | the device to de-initialize |
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.
[in] | bus | SPI device to initialize |
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.
[in] | bus | SPI device that is used with the given CS line |
[in] | cs | chip select pin to initialize |
0 | success |
-ENXIO | invalid device |
-EINVAL | invalid CS pin/line |
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.
[in] | bus | SPI device the pins are configure for |
int spi_init_with_gpio_mode | ( | spi_t | bus, |
const spi_gpio_mode_t * | mode | ||
) |
Initialize MOSI/MISO/SCLK pins with adapted GPIO modes.
[in] | bus | SPI device that is used with the given CS line |
[in] | mode | a pointer to a struct containing the 3 modes to use on each pin |
0 | success |
<0 | error |
gpio_t spi_pin_clk | ( | spi_t | dev | ) |
Get the CLK pin of the given SPI bus.
[in] | dev | The device to query |
gpio_t spi_pin_miso | ( | spi_t | dev | ) |
Get the MISO pin of the given SPI bus.
[in] | dev | The device to query |
gpio_t spi_pin_mosi | ( | spi_t | dev | ) |
Get the MOSI pin of the given SPI bus.
[in] | dev | The device to query |
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.
[in] | bus | SPI device to release |
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.
[in] | bus | SPI device to use |
[in] | cs | chip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver |
[in] | cont | if true, keep device selected after transfer |
[in] | out | byte to send out |
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.
[in] | bus | SPI device to use |
[in] | cs | chip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver |
[in] | cont | if true, keep device selected after transfer |
[in] | out | buffer to send data from, set NULL if only receiving |
[out] | in | buffer to read into, set NULL if only sending |
[in] | len | number of bytes to transfer |
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.
[in] | bus | SPI device to use |
[in] | cs | chip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver |
[in] | reg | register address to transfer data to/from |
[in] | out | byte to send |
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.
[in] | bus | SPI device to use |
[in] | cs | chip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver |
[in] | reg | register address to transfer data to/from |
[in] | out | buffer to send data from, set NULL if only receiving |
[out] | in | buffer to read into, set NULL if only sending |
[in] | len | number of bytes to transfer |
|
inlinestatic |
Transfer a 16 bit number in big endian byte order.
[in] | bus | SPI device to use |
[in] | cs | chip select pin/line to use, set to SPI_CS_UNDEF if chip select should not be handled by the SPI driver |
[in] | cont | if true, keep device selected after transfer |
[in] | host_number | number to transfer in host byte order |