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 theSPINameunique identifier to the peripheral associated to this SPI channel.spi_get_capabilities()fills the givenspi_capabilities_tinstance.spi_get_capabilities()should consider thesselpin when evaluating thesupport_slave_modecapability.- If the given
sselpin cannot be managed by hardware in slave mode,support_slave_modeshould 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_slaveis false:- If
sselisNC, the HAL implementation ignores this pin. - If
sselis notNC, then the HAL implementation owns the pin and its management.
- If
- When managed by the HAL implementation,
sselis always considered active low. - When the hardware supports the half-duplex (3-wire) mode, if
miso(exclusive) ormosiis 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_lensymbols to the bus. - Reads
rx_lensymbols from the bus. - If
rxis NULL, then inputs are discarded. - If
txis NULL, thenfill_symbolis 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_lenthen it sends(rx_len-tx_len)additionalfill_symbolto the bus.
- If
- In half-duplex mode:
- As master,
spi_transfer()sendstx_lensymbols and then readsrx_lensymbols. - As slave,
spi_transfer()receivesrx_lensymbols and then sendstx_lensymbols.
- 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 thectxpointer.- The
ctxis 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 theDMAUsagehint to select the appropriate asynchronous algorithm. - The
spi_async_event_tmust 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_twithoutspi_free()'ing it first. - Calling any method other than
spi_init()on an uninitialized or freedspi_t. - Passing both
misoandmosiasNCtospi_get_moduleorspi_init. - Passing
misoormosiasNCon target that does not support half-duplex mode. - Passing
mclkasNCtospi_get_moduleorspi_init. - Passing an invalid pointer as
captospi_get_capabilities. - Passing pins that cannot be on the same peripheral.
- Passing an invalid pointer as
objto any method. - Giving an
sselpin 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
bitsinspi_formatto a value out of the range given byspi_get_capabilities(). - Passing an invalid pointer as
fill_symboltospi_transferandspi_transfer_asyncwhile they would be required by the transfer (rx_len != tx_lenortx==NULL). - Passing an invalid pointer as
handlertospi_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.hnext to theSPINameenumeration.
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-spimbed-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: