|
| 1 | +<h2 id="spi-port">Serial Peripheral Interface (SPI)</h2> |
| 2 | + |
| 3 | +The **Serial Peripheral Interface** allows you to send or receive a data stream over a synchronous serial interface made of 3 to 4 lines. |
| 4 | + |
| 5 | +- MISO: Master in, slave out. |
| 6 | +- MOSI: Master out, slave in. |
| 7 | +- MCLK: Clock. |
| 8 | +- SS: Slave select. |
| 9 | + |
| 10 | +A typical use case of this interface is with SDCard, memory blocks and DACs or ADCs. |
| 11 | + |
| 12 | +This highly configurable interface has elements you can adjust: |
| 13 | + |
| 14 | +- Frame length. |
| 15 | +- Clocks polarity and phase. |
| 16 | + |
| 17 | +<span class="warnings">**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](#implementing-the-spi-api) section. |
| 18 | + |
| 19 | +### Assumptions |
| 20 | + |
| 21 | +#### Defined behaviors |
| 22 | + |
| 23 | +- `spi_get_module()` returns the `SPIName` unique identifier to the peripheral associated to this SPI channel. |
| 24 | +- `spi_get_capabilities()` fills the given `spi_capabilities_t` instance. |
| 25 | +- `spi_get_capabilities()` should consider the `ssel` pin when evaluating the `support_slave_mode` capability. |
| 26 | +- If the given `ssel` pin cannot be managed by hardware in slave mode, `support_slave_mode` should be false. |
| 27 | +- At least a symbol width of 8 bit must be supported. |
| 28 | +- The supported frequency range must include the range 0.2-2 MHz. |
| 29 | +- The shortest part of the duty cycle must not be shorter than 50% of the expected period. |
| 30 | +- `spi_init()` initializes the pins leaving the configuration registers unchanged. |
| 31 | +- `spi_init()` if `is_slave` is false: |
| 32 | + - If `ssel` is `NC`, the HAL implementation ignores this pin. |
| 33 | + - If `ssel` is not `NC`, then the HAL implementation owns the pin and its management. |
| 34 | +- When managed by the HAL implementation, `ssel` is always considered active low. |
| 35 | +- When the hardware supports the half-duplex (3-wire) mode, if `miso` (exclusive) or `mosi` is missing in any function that expects pins, the bus is assumed to be half-duplex. |
| 36 | +- `spi_free()` resets the pins to their default state. |
| 37 | +- `spi_free()` disables the peripheral clock. |
| 38 | +- `spi_format()` sets: |
| 39 | + - The number of bits per symbol. |
| 40 | + - The mode: |
| 41 | + .0 Clock idle state is *low*, data are sampled when the clock becomes *active* (polarity = 0, phase = 0). |
| 42 | + .1 Clock idle state is *low*, data are sampled when the clock becomes *inactive* (polarity = 0, phase = 1). |
| 43 | + .2 Clock idle state is *high*, data are sampled when the clock becomes *active* (polarity = 1, phase = 0). |
| 44 | + .3 Clock idle state is *high*, data are sampled when the clock becomes *inactive* (polarity = 1, phase = 1). |
| 45 | + - The bit ordering (lsb/msb first). |
| 46 | +- `spi_format()` updates the configuration of the peripheral except the baud rate generator. |
| 47 | +- `spi_frequency()` sets the frequency to use during the transfer. |
| 48 | +- `spi_frequency()` returns the actual frequency that is used. |
| 49 | +- `spi_frequency()` updates the baud rate generator leaving other configurations unchanged. |
| 50 | +- `spi_init()`, `spi_frequency()` and `spi_format()` must be called at least once each before initiating any transfer. |
| 51 | +- `spi_transfer()`: |
| 52 | + - Writes `tx_len` symbols to the bus. |
| 53 | + - Reads `rx_len` symbols from the bus. |
| 54 | + - If `rx` is NULL, then inputs are discarded. |
| 55 | + - If `tx` is NULL, then `fill_symbol` is used instead. |
| 56 | + - Returns the number of symbol clocked on the bus during this transfer. |
| 57 | + - Expects symbols types to be the closest stdint type bigger or equal to its size following the platform's endianness. For example: |
| 58 | + - 7bits => uint8_t. |
| 59 | + - 15bits => uint16_t. |
| 60 | + - 16bits => uint16_t. |
| 61 | + - 17bits => uint32_t. |
| 62 | + - In full-duplex mode: |
| 63 | + - If `rx_len` > `tx_len` then it sends `(rx_len-tx_len)` additional `fill_symbol` to the bus. |
| 64 | + - In half-duplex mode: |
| 65 | + - As master, `spi_transfer()` sends `tx_len` symbols and then reads `rx_len` symbols. |
| 66 | + - As slave, `spi_transfer()` receives `rx_len` symbols and then sends `tx_len` symbols. |
| 67 | +- `spi_transter_async()` schedules a transfer to be process the same way `spi_transfer()` would have but asynchronously. |
| 68 | +- `spi_transter_async()` returns immediately with a boolean indicating whether the transfer was successfully scheduled or not. |
| 69 | +- The callback given to `spi_transfer_async()` is invoked when the transfer completes (with a success or an error). |
| 70 | +- `spi_transfer_async()` saves the handler and the `ctx` pointer. |
| 71 | +- The `ctx` is passed to the callback on transfer completion. |
| 72 | +- Unless the transfer is aborted, the callback is invoked on completion. The completion may be when all symbols have been transmitted |
| 73 | + or when, in slave mode, the master deasserts the chip select. |
| 74 | +- The `spi_transfer_async()` function may use the `DMAUsage` hint to select the appropriate asynchronous algorithm. |
| 75 | +- 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. |
| 76 | +- `spi_transfer_async_abort()` aborts an ongoing asynchronous transfer. |
| 77 | + |
| 78 | +#### Undefined behaviors |
| 79 | + |
| 80 | +- Calling `spi_init()` multiple times on the same `spi_t` without `spi_free()`'ing it first. |
| 81 | +- Calling any method other than `spi_init()` on an uninitialized or freed `spi_t`. |
| 82 | +- Passing both `miso` and `mosi` as `NC` to `spi_get_module` or `spi_init`. |
| 83 | +- Passing `miso` or `mosi` as `NC` on target that does not support half-duplex mode. |
| 84 | +- Passing `mclk` as `NC` to `spi_get_module` or `spi_init`. |
| 85 | +- Passing an invalid pointer as `cap` to `spi_get_capabilities`. |
| 86 | +- Passing pins that cannot be on the same peripheral. |
| 87 | +- Passing an invalid pointer as `obj` to any method. |
| 88 | +- Giving an `ssel` pin to `spi_init()` when using in master mode. |
| 89 | +- SS must be managed by hardware in slave mode and must **NOT** be managed by hardware in master mode. |
| 90 | +- Setting a frequency outside of the range given by `spi_get_capabilities()`. |
| 91 | +- Setting a frequency in slave mode. |
| 92 | +- Setting `bits` in `spi_format` to a value out of the range given by `spi_get_capabilities()`. |
| 93 | +- Passing an invalid pointer as `fill_symbol` to `spi_transfer` and `spi_transfer_async` while they would be required by the transfer (`rx_len != tx_len` or `tx==NULL`). |
| 94 | +- Passing an invalid pointer as `handler` to `spi_transfer_async`. |
| 95 | +- Calling `spi_transfer_async_abort()` while no asynchronous transfer is being processed (no transfer or a synchronous transfer). |
| 96 | +- In half-duplex mode, any mechanism (if any is present) to detect or prevent collision is implementation defined. |
| 97 | + |
| 98 | +#### Other requirements |
| 99 | + |
| 100 | +A target must also define these elements: |
| 101 | + |
| 102 | +- `#define SPI_COUNT (xxxxxU)`. |
| 103 | +- The number of SPI peripherals available on the device. A good place for that macro is `PeripheralNames.h` next to the `SPIName` enumeration. |
| 104 | + |
| 105 | +<span class="notes">**Note:** You can find more details about the design choices in the [SPI design document](https://github.com/ARMmbed/mbed-os/blob/feature-hal-spec-spi/docs/design-documents/hal/0000-spi-overhaul.md).</span> |
| 106 | + |
| 107 | +### Dependencies |
| 108 | + |
| 109 | +Hardware SPI capabilities. |
| 110 | + |
| 111 | +### Implementing the SPI API |
| 112 | + |
| 113 | +You can find the API and specification for the SPI API in the following class reference: |
| 114 | + |
| 115 | +[](http://os.mbed.com/docs/development/feature-hal-spec-spi-doxy/classmbed_1_1_s_p_i.html) |
| 116 | + |
| 117 | +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. |
| 118 | +You can also add the `SPI_ASYNCH` label in the `device_has` option to enable the asynchronous API. |
| 119 | + |
| 120 | +### Testing |
| 121 | + |
| 122 | +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: |
| 123 | + |
| 124 | +``` |
| 125 | +mbed test -t <toolchain> -m <target> -n "tests-mbed_hal-spi*" |
| 126 | +``` |
| 127 | + |
| 128 | +You can read more about the test cases: |
| 129 | + |
| 130 | +[](http://os.mbed.com/docs/development/feature-hal-spec-spi-doxy/group__hal__spi__tests.html) |
0 commit comments