Serial Peripheral Interface (SPI)
The Serial Peripheral Interface allows you to send or receive a data stream over a synchronous serial interface made of 3 to 4 lines.
- MISO: Master in, slave out.
- MOSI: Master out, slave in.
- MCLK: Clock.
- SS: Slave select.
A typical use case of this interface is with SDCard, memory blocks and DACs or ADCs.
This highly configurable interface has elements you can adjust:
- Frame length.
- Clocks polarity and phase.
Warning: We are introducing the SPI API in an upcoming release of Mbed OS. This page documents code that exists on a feature branch of Mbed OS. You can find details on how it may affect you in the implementing the SPI API section.
Assumptions
Defined behaviors
spi_get_module()
returns theSPIName
unique identifier to the peripheral associated to this SPI channel.spi_get_capabilities()
fills the givenspi_capabilities_t
instance.spi_get_capabilities()
should consider thessel
pin when evaluating thesupport_slave_mode
capability.- If the given
ssel
pin cannot be managed by hardware in slave mode,support_slave_mode
should be false. - At least a symbol width of 8 bit must be supported.
- The supported frequency range must include the range 0.2-2 MHz.
- The shortest part of the duty cycle must not be shorter than 50% of the expected period.
spi_init()
initializes the pins leaving the configuration registers unchanged.spi_init()
ifis_slave
is false:- If
ssel
isNC
, the HAL implementation ignores this pin. - If
ssel
is notNC
, then the HAL implementation owns the pin and its management.
- If
- When managed by the HAL implementation,
ssel
is always considered active low. - When the hardware supports the half-duplex (3-wire) mode, if
miso
(exclusive) ormosi
is missing in any function that expects pins, the bus is assumed to be half-duplex. spi_free()
resets the pins to their default state.spi_free()
disables the peripheral clock.spi_format()
sets:- The number of bits per symbol.
- The mode: .0 Clock idle state is low, data are sampled when the clock becomes active (polarity = 0, phase = 0). .1 Clock idle state is low, data are sampled when the clock becomes inactive (polarity = 0, phase = 1). .2 Clock idle state is high, data are sampled when the clock becomes active (polarity = 1, phase = 0). .3 Clock idle state is high, data are sampled when the clock becomes inactive (polarity = 1, phase = 1).
- The bit ordering (lsb/msb first).
spi_format()
updates the configuration of the peripheral except the baud rate generator.spi_frequency()
sets the frequency to use during the transfer.spi_frequency()
returns the actual frequency that is used.spi_frequency()
updates the baud rate generator leaving other configurations unchanged.spi_init()
,spi_frequency()
andspi_format()
must be called at least once each before initiating any transfer.spi_transfer()
:- Writes
tx_len
symbols to the bus. - Reads
rx_len
symbols from the bus. - If
rx
is NULL, then inputs are discarded. - If
tx
is NULL, thenfill_symbol
is used instead. - Returns the number of symbol clocked on the bus during this transfer.
- Expects symbols types to be the closest stdint type bigger or equal to its size following the platform's endianness. For example:
- 7bits => uint8_t.
- 15bits => uint16_t.
- 16bits => uint16_t.
- 17bits => uint32_t.
- In full-duplex mode:
- If
rx_len
>tx_len
then it sends(rx_len-tx_len)
additionalfill_symbol
to the bus.
- If
- In half-duplex mode:
- As master,
spi_transfer()
sendstx_len
symbols and then readsrx_len
symbols. - As slave,
spi_transfer()
receivesrx_len
symbols and then sendstx_len
symbols.
- As master,
- Writes
spi_transter_async()
schedules a transfer to be process the same wayspi_transfer()
would have but asynchronously.spi_transter_async()
returns immediately with a boolean indicating whether the transfer was successfully scheduled or not.- The callback given to
spi_transfer_async()
is invoked when the transfer completes (with a success or an error). spi_transfer_async()
saves the handler and thectx
pointer.- The
ctx
is passed to the callback on transfer completion. - Unless the transfer is aborted, the callback is invoked on completion. The completion may be when all symbols have been transmitted or when, in slave mode, the master deasserts the chip select.
- The
spi_transfer_async()
function may use theDMAUsage
hint to select the appropriate asynchronous algorithm. - The
spi_async_event_t
must be filled with the number of symbols clocked on the bus during this transfer and a boolean value indicated if an error has occurred. spi_transfer_async_abort()
aborts an ongoing asynchronous transfer.
Undefined behaviors
- Calling
spi_init()
multiple times on the samespi_t
withoutspi_free()
'ing it first. - Calling any method other than
spi_init()
on an uninitialized or freedspi_t
. - Passing both
miso
andmosi
asNC
tospi_get_module
orspi_init
. - Passing
miso
ormosi
asNC
on target that does not support half-duplex mode. - Passing
mclk
asNC
tospi_get_module
orspi_init
. - Passing an invalid pointer as
cap
tospi_get_capabilities
. - Passing pins that cannot be on the same peripheral.
- Passing an invalid pointer as
obj
to any method. - Giving an
ssel
pin tospi_init()
when using in master mode. - SS must be managed by hardware in slave mode and must NOT be managed by hardware in master mode.
- Setting a frequency outside of the range given by
spi_get_capabilities()
. - Setting a frequency in slave mode.
- Setting
bits
inspi_format
to a value out of the range given byspi_get_capabilities()
. - Passing an invalid pointer as
fill_symbol
tospi_transfer
andspi_transfer_async
while they would be required by the transfer (rx_len != tx_len
ortx==NULL
). - Passing an invalid pointer as
handler
tospi_transfer_async
. - Calling
spi_transfer_async_abort()
while no asynchronous transfer is being processed (no transfer or a synchronous transfer). - In half-duplex mode, any mechanism (if any is present) to detect or prevent collision is implementation defined.
Other requirements
A target must also define these elements:
#define SPI_COUNT (xxxxxU)
.- The number of SPI peripherals available on the device. A good place for that macro is
PeripheralNames.h
next to theSPIName
enumeration.
Note: You can find more details about the design choices in the SPI design document.
Dependencies
Hardware SPI capabilities.
Implementing the SPI API
You can find the API and specification for the SPI API in the following class reference:
Public Member Functions | |
SPI (PinName mosi, PinName miso, PinName sclk, PinName ssel=NC) | |
Create a SPI master connected to the specified pins. More... | |
void | format (int bits, int mode=0) |
Configure the data transmission format. More... | |
uint32_t | frequency (uint32_t hz=1000000) |
Set the SPI bus clock frequency. More... | |
virtual int | write (int value) |
Write to the SPI Slave and return the response. More... | |
virtual int | write (const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length) |
Write to the SPI Slave and obtain the response. More... | |
virtual bool | lock (void) |
Acquire exclusive access to this SPI bus. More... | |
virtual void | unlock (void) |
Release exclusive access to this SPI bus. More... | |
void | set_default_write_value (char data) |
Set default write data. More... | |
template<typename Type > | |
int | transfer (const Type *tx_buffer, int tx_length, Type *rx_buffer, int rx_length, const event_callback_t &callback, int event=0) |
Start non-blocking SPI transfer using 8bit buffers. More... | |
void | abort_transfer () |
Abort the on-going SPI transfer, and continue with transfers in the queue, if any. More... | |
void | clear_transfer_buffer () |
Clear the queue of transfers. More... | |
void | abort_all_transfers () |
Clear the queue of transfers and abort the on-going transfer. More... | |
int | set_dma_usage (DMAUsage usage) |
Configure DMA usage suggestion for non-blocking transfers. More... |
To enable SPI support in Mbed OS, add the SPI
label in the device_has
option of the target's section in the targets.json
file.
You can also add the SPI_ASYNCH
label in the device_has
option to enable the asynchronous API.
Testing
The Mbed OS HAL provides a set of conformance tests for SPI. You can use these tests to validate the correctness of your implementation. To run the SPI HAL tests, use the following command:
mbed test -t <toolchain> -m <target> -n "tests-mbed_hal-spi*"
You can read more about the test cases:
To test SPI using the FPGA test shield:
-
Check out
feature-hal-spec-spi
mbed-os branch. -
Run the tests:
- SPI master:
mbed test -t GCC_ARM -m K64F -n tests-mbed_hal_fpga_ci_test_shield-spi_master -v
- SPI slave:
mbed test -t GCC_ARM -m K64F -n tests-mbed_hal_fpga_ci_test_shield-spi_slave -v
- SPI master: