esp-idf/docs/en/api-reference/peripherals/spi_slave.rst

259 lines
15 KiB
ReStructuredText

SPI Slave Driver
================
SPI Slave driver is a program that controls {IDF_TARGET_NAME}'s SPI peripherals while they function as slaves.
Overview of {IDF_TARGET_NAME}'s SPI peripherals
-----------------------------------------------
On {IDF_TARGET_NAME}, {SOC_SPI_PERIPH_NUM} SPI controllers are available for general purpose usage. A certain SPI controller has an independent signal bus with the same name.
.. only:: esp32
.. note::
On ESP32, HSPI refers to SPI2, VSPI refers to SPI3.
Terminology
-----------
The terms used in relation to the SPI slave driver are given in the table below.
.. list-table::
:widths: 30 70
:header-rows: 1
* - Term
- Definition
* - Host
- The SPI controller peripheral external to {IDF_TARGET_NAME} that initiates SPI transmissions over the bus, and acts as an SPI Master.
* - Device
- SPI slave device (general purpose SPI controller). Each Device shares the MOSI, MISO and SCLK signals but is only active on the bus when the Host asserts the Device's individual CS line.
* - Bus
- A signal bus, common to all Devices connected to one Host. In general, a bus includes the following lines: MISO, MOSI, SCLK, one or more CS lines, and, optionally, QUADWP and QUADHD. So Devices are connected to the same lines, with the exception that each Device has its own CS line. Several Devices can also share one CS line if connected in the daisy-chain manner.
* - MISO
- Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host.
* - MOSI
- Master Out, Slave In, a.k.a. D. Data transmission from a Host to Device.
* - SCLK
- Serial Clock. Oscillating signal generated by a Host that keeps the transmission of data bits in sync.
* - CS
- Chip Select. Allows a Host to select individual Device(s) connected to the bus in order to send or receive data.
* - QUADWP
- Write Protect signal. Only used for 4-bit (qio/qout) transactions.
* - QUADHD
- Hold signal. Only used for 4-bit (qio/qout) transactions.
* - Assertion
- The action of activating a line. The opposite action of returning the line back to inactive (back to idle) is called *de-assertion*.
* - Transaction
- One instance of a Host asserting a CS line, transferring data to and from a Device, and de-asserting the CS line. Transactions are atomic, which means they can never be interrupted by another transaction.
* - Launch Edge
- Edge of the clock at which the source register *launches* the signal onto the line.
* - Latch Edge
- Edge of the clock at which the destination register *latches in* the signal.
Driver Features
---------------
{IDF_TARGET_MAX_DATA_BUF:default="64", esp32s2="72"}
The SPI slave driver allows using the SPI peripherals as full-duplex Devices. The driver can send/receive transactions up to {IDF_TARGET_MAX_DATA_BUF} bytes in length, or utilize DMA to send/receive longer transactions. However, there are some :ref:`known issues <spi_dma_known_issues>` related to DMA.
The SPI slave driver supports registering the SPI ISR to a certain CPU core. If multiple tasks try to access the same SPI Device, it is recommended to refactor your application so that each SPI peripheral is only accessed by a single task at a time. and use :cpp:member:`spi_bus_config_t::isr_cpu_id` to register the SPI ISR to the same core as SPI peripheral related tasks to ensure thread safe.
SPI Transactions
----------------
A full-duplex SPI transaction begins when the Host asserts the CS line and starts sending out clock pulses on the SCLK line. Every clock pulse, a data bit is shifted from the Host to the Device on the MOSI line and back on the MISO line at the same time. At the end of the transaction, the Host de-asserts the CS line.
The attributes of a transaction are determined by the configuration structure for an SPI peripheral acting as a slave device :cpp:type:`spi_slave_interface_config_t`, and transaction configuration structure :cpp:type:`spi_slave_transaction_t`.
As not every transaction requires both writing and reading data, you have a choice to configure the :cpp:type:`spi_transaction_t` structure for TX only, RX only, or TX and RX transactions. If :cpp:member:`spi_slave_transaction_t::rx_buffer` is set to NULL, the read phase will be skipped. If :cpp:member:`spi_slave_transaction_t::tx_buffer` is set to NULL, the write phase will be skipped.
.. note::
A Host should not start a transaction before its Device is ready for receiving data. It is recommended to use another GPIO pin for a handshake signal to sync the Devices. For more details, see :ref:`transaction_interval`.
Driver Usage
------------
- Initialize an SPI peripheral as a Device by calling the function :cpp:func:`spi_slave_initialize`. Make sure to set the correct I/O pins in the struct `bus_config`. Set the unused signals to ``-1``.
.. only:: esp32
If transactions will be longer than 32 bytes, allow a DMA channel 1 or 2 by setting the parameter ``dma_chan`` to ``1`` or ``2`` respectively. Otherwise, set ``dma_chan`` to ``0``.
.. only:: esp32s2
If transactions will be longer than 32 bytes, allow a DMA channel by setting the parameter ``dma_chan`` to the host device. Otherwise, set ``dma_chan`` to ``0``.
- Before initiating transactions, fill one or more :cpp:type:`spi_slave_transaction_t` structs with the transaction parameters required. Either queue all transactions by calling the function :cpp:func:`spi_slave_queue_trans` and, at a later time, query the result by using the function :cpp:func:`spi_slave_get_trans_result`, or handle all requests individually by feeding them into :cpp:func:`spi_slave_transmit`. The latter two functions will be blocked until the Host has initiated and finished a transaction, causing the queued data to be sent and received.
- (Optional) To unload the SPI slave driver, call :cpp:func:`spi_slave_free`.
Transaction Data and Master/Slave Length Mismatches
---------------------------------------------------
Normally, the data that needs to be transferred to or from a Device is read or written to a chunk of memory indicated by the :cpp:member:`spi_slave_transaction_t::rx_buffer` and :cpp:member:`spi_slave_transaction_t::tx_buffer`. The SPI driver can be configured to use DMA for transfers, in which case these buffers must be allocated in DMA-capable memory using ``pvPortMallocCaps(size, MALLOC_CAP_DMA)``.
The amount of data that the driver can read or write to the buffers is limited by :cpp:member:`spi_slave_transaction_t::length`. However, this member does not define the actual length of an SPI transaction. A transaction's length is determined by the clock and CS lines driven by the Host. The actual length of the transmission can be read only after a transaction is finished from the member :cpp:member:`spi_slave_transaction_t::trans_len`.
If the length of the transmission is greater than the buffer length, only the initial number of bits specified in the :cpp:member:`spi_slave_transaction_t::length` member will be sent and received. In this case, :cpp:member:`spi_slave_transaction_t::trans_len` is set to :cpp:member:`spi_slave_transaction_t::length` instead of the actual transaction length. To meet the actual transaction length requirements, set :cpp:member:`spi_slave_transaction_t::length` to a value greater than the maximum :cpp:member:`spi_slave_transaction_t::trans_len` expected. If the transmission length is shorter than the buffer length, only the data equal to the length of the buffer will be transmitted.
GPIO Matrix and IO_MUX
^^^^^^^^^^^^^^^^^^^^^^
.. only:: esp32
Most of {IDF_TARGET_NAME}'s peripheral signals have direct connection to their dedicated IO_MUX pins. However, the signals can also be routed to any other available pins using the less direct GPIO matrix.
If at least one signal is routed through the GPIO matrix, then all signals will be routed through it. If the driver is configured so that all SPI signals are either routed to their dedicated IO_MUX pins or are not connected at all, the GPIO matrix will be bypassed.
The GPIO matrix introduces flexibility of routing but also increases the input delay of the MISO signal, which makes MISO setup time violations more likely. If SPI needs to operate at high speeds, use dedicated IO_MUX pins.
.. note::
For more details about the influence of the MISO input delay on the maximum clock frequency, see :ref:`timing_considerations`.
The IO_MUX pins for SPI buses are given below.
+----------+------+------+
| Pin Name | SPI2 | SPI3 |
+ +------+------+
| | GPIO Number |
+==========+======+======+
| CS0 | 15 | 5 |
+----------+------+------+
| SCLK | 14 | 18 |
+----------+------+------+
| MISO | 12 | 19 |
+----------+------+------+
| MOSI | 13 | 23 |
+----------+------+------+
| QUADWP | 2 | 22 |
+----------+------+------+
| QUADHD | 4 | 21 |
+----------+------+------+
.. only:: not esp32
{IDF_TARGET_SPI2_IOMUX_PIN_CS:default="N/A", esp32s2="10", esp32s3="10", esp32c2="10", esp32c3="10", esp32c6="16", esp32h2="1"}
{IDF_TARGET_SPI2_IOMUX_PIN_CLK:default="N/A", esp32s2="12", esp32s3="12", esp32c2="6", esp32c3="6", esp32c6="6", esp32h2="4"}
{IDF_TARGET_SPI2_IOMUX_PIN_MOSI:default="N/A", esp32s2="11" esp32s3="11", esp32c2="7" esp32c3="7", esp32c6="7", esp32h2="5"}
{IDF_TARGET_SPI2_IOMUX_PIN_MISO:default="N/A", esp32s2="13" esp32s3="13", esp32c2="2" esp32c3="2", esp32c6="2", esp32h2="0"}
{IDF_TARGET_SPI2_IOMUX_PIN_HD:default="N/A", esp32s2="9" esp32s3="9", esp32c2="4" esp32c3="4", esp32c6="4", esp32h2="3"}
{IDF_TARGET_SPI2_IOMUX_PIN_WP:default="N/A", esp32s2="14" esp32s3="14", esp32c2="5" esp32c3="5", esp32c6="5", esp32h2="2"}
Most of chip's peripheral signals have direct connection to their dedicated IO_MUX pins. However, the signals can also be routed to any other available pins using the less direct GPIO matrix. If at least one signal is routed through the GPIO matrix, then all signals will be routed through it.
When an SPI Host is set to 80 MHz or lower frequencies, routing SPI pins via GPIO matrix will behave the same compared to routing them via IO_MUX.
The IO_MUX pins for SPI buses are given below.
.. list-table::
:widths: 40 30
:header-rows: 1
* - Pin Name
- GPIO Number (SPI2)
* - CS0
- {IDF_TARGET_SPI2_IOMUX_PIN_CS}
* - SCLK
- {IDF_TARGET_SPI2_IOMUX_PIN_CLK}
* - MISO
- {IDF_TARGET_SPI2_IOMUX_PIN_MISO}
* - MOSI
- {IDF_TARGET_SPI2_IOMUX_PIN_MOSI}
* - QUADWP
- {IDF_TARGET_SPI2_IOMUX_PIN_WP}
* - QUADHD
- {IDF_TARGET_SPI2_IOMUX_PIN_HD}
Speed and Timing Considerations
-------------------------------
.. _transaction_interval:
Transaction Interval
^^^^^^^^^^^^^^^^^^^^
The {IDF_TARGET_NAME} SPI slave peripherals are designed as general purpose Devices controlled by a CPU. As opposed to dedicated slaves, CPU-based SPI Devices have a limited number of pre-defined registers. All transactions must be handled by the CPU, which means that the transfers and responses are not real-time, and there might be noticeable latency.
As a solution, a Device's response rate can be doubled by using the functions :cpp:func:`spi_slave_queue_trans` and then :cpp:func:`spi_slave_get_trans_result` instead of using :cpp:func:`spi_slave_transmit`.
You can also configure a GPIO pin through which the Device will signal to the Host when it is ready for a new transaction. A code example of this can be found in :example:`peripherals/spi_slave`.
SCLK Frequency Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^
{IDF_TARGET_MAX_FREQ:default="60", esp32="10", esp32s2="40", esp32c6="40", esp32h2="32"}
The SPI slaves are designed to operate at up to {IDF_TARGET_MAX_FREQ} MHz. The data cannot be recognized or received correctly if the clock is too fast or does not have a 50% duty cycle.
.. only:: esp32
On top of that, there are additional requirements for the data to meet the timing constraints:
- Read (MOSI):
The Device can read data correctly only if the data is already set at the launch edge. Although it is usually the case for most masters.
- Write (MISO):
The output delay of the MISO signal needs to be shorter than half of a clock cycle period so that the MISO line is stable before the next latch edge. Given that the clock is balanced, the output delay and frequency limitations in different cases are given below.
.. list-table::
:widths: 30 40 40
:header-rows: 1
* - /
- Output delay of MISO (ns)
- Freq. limit (MHz)
* - IO_MUX
- 43.75
- <11.4
* - GPIO matrix
- 68.75
- <7.2
Note:
1. If the frequency reaches the maximum limitation, random errors may occur.
2. The clock uncertainty between the Host and the Device (12.5 ns) is included.
3. The output delay is measured under ideal circumstances (no load). If the MISO pin is heavily loaded, the output delay will be longer, and the maximum allowed frequency will be lower.
Exception: The frequency is allowed to be higher if the master has more tolerance for the MISO setup time, e.g., latch data at the next edge, or configurable latching time.
.. _spi_dma_known_issues:
Restrictions and Known Issues
-----------------------------
1. If DMA is enabled, the rx buffer should be word-aligned (starting from a 32-bit boundary and having a length of multiples of 4 bytes). Otherwise, DMA may write incorrectly or not in a boundary aligned manner. The driver reports an error if this condition is not satisfied.
Also, a Host should write lengths that are multiples of 4 bytes. The data with inappropriate lengths will be discarded.
.. only:: esp32
2. Furthermore, DMA requires SPI modes 1 and 3. For SPI modes 0 and 2, the MISO signal has to be launched half a clock cycle earlier to meet the timing. The new timing is as follows:
.. wavedrom:: /../_static/diagrams/spi/spi_slave_miso_dma.json
If DMA is enabled, a Device's launch edge is half of an SPI clock cycle ahead of the normal time, shifting to the Master's actual latch edge. In this case, if the GPIO matrix is bypassed, the hold time for data sampling is 68.75 ns and no longer a half of an SPI clock cycle. If the GPIO matrix is used, the hold time will increase to 93.75 ns. The Host should sample the data immediately at the latch edge or communicate in SPI modes 1 or 3. If your Host cannot meet these timing requirements, initialize your Device without DMA.
Application Example
-------------------
The code example for Device/Host communication can be found in the :example:`peripherals/spi_slave` directory of ESP-IDF examples.
API Reference
-------------
.. include-build-file:: inc/spi_slave.inc