kopia lustrzana https://github.com/espressif/esp-idf
docs: provide Chinese translation for api-reference/peripherals/spi_master
rodzic
a3d0f5dd3e
commit
05fe748c1e
|
@ -69,7 +69,7 @@ Data and event flow in the diagram
|
|||
|
||||
* ``********`` Events aggregated in ESP-NETIF propagates to driver, user code and network stack
|
||||
|
||||
* ``|`` User settings and runtime configuration
|
||||
* ``|`` User settings and runtime configuration
|
||||
|
||||
ESP-NETIF interaction
|
||||
---------------------
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
SPI Master Driver
|
||||
=================
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
SPI Master driver is a program that controls {IDF_TARGET_NAME}'s SPI peripherals while they function as masters.
|
||||
|
||||
|
||||
Overview of {IDF_TARGET_NAME}'s SPI peripherals
|
||||
Overview of {IDF_TARGET_NAME}'s SPI Peripherals
|
||||
-----------------------------------------------
|
||||
|
||||
{IDF_TARGET_SPI2_CS_NUM:default="6", esp32="3"}
|
||||
|
@ -16,8 +17,7 @@ Overview of {IDF_TARGET_NAME}'s SPI peripherals
|
|||
|
||||
.. only:: esp32
|
||||
|
||||
There are quite a few limitations when using SPI Master driver on the SPI1 bus, see
|
||||
:ref:`spi_master_on_spi1_bus`.
|
||||
There are quite a few limitations when using SPI Master driver on the SPI1 bus, see :ref:`spi_master_on_spi1_bus`.
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
|
@ -25,49 +25,69 @@ Overview of {IDF_TARGET_NAME}'s SPI peripherals
|
|||
|
||||
.. only:: esp32
|
||||
|
||||
- SPI2 and SPI3 are general purpose SPI controllers, sometimes referred to as HSPI and VSPI, respectively. They are open to users. SPI2 and SPI3 have independent bus signals with the same respective names. Each bus has three CS lines to drive up to same number of SPI slaves.
|
||||
- SPI2 and SPI3 are general-purpose SPI controllers, sometimes referred to as HSPI and VSPI. They are open to users. SPI2 and SPI3 have independent bus signals with the same respective names. Each bus has three CS lines to drive up to a same number of SPI slaves.
|
||||
|
||||
.. only:: esp32s2 or esp32s3
|
||||
|
||||
- SPI2 and SPI3 are general purpose SPI controllers. They are open to users. SPI2 and SPI3 have independent signal buses with the same respective names. SPI2 has {IDF_TARGET_SPI2_CS_NUM} CS lines. SPI3 has {IDF_TARGET_SPI3_CS_NUM} CS lines. Each CS line can be used to drive one SPI slave.
|
||||
- SPI2 and SPI3 are general-purpose SPI controllers. They are open to users. SPI2 and SPI3 have independent signal buses with the same respective names. SPI2 has {IDF_TARGET_SPI2_CS_NUM} CS lines. SPI3 has {IDF_TARGET_SPI3_CS_NUM} CS lines. Each CS line can be used to drive one SPI slave.
|
||||
|
||||
.. only:: esp32c3 or esp32c2 or esp32c6 or esp32h2
|
||||
|
||||
- SPI2 is a general purpose SPI controller. It has an independent signal bus with the same name. The bus has {IDF_TARGET_SPI2_CS_NUM} CS lines to drive up to {IDF_TARGET_SPI2_CS_NUM} SPI slaves.
|
||||
- SPI2 is a general-purpose SPI controller. It has an independent signal bus with the same name. The bus has {IDF_TARGET_SPI2_CS_NUM} CS lines to drive up to {IDF_TARGET_SPI2_CS_NUM} SPI slaves.
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
The terms used in relation to the SPI master driver are given in the table below.
|
||||
The terms used in relation to the SPI Master driver are given in the table below.
|
||||
|
||||
================= =========================================================================================
|
||||
Term Definition
|
||||
================= =========================================================================================
|
||||
**Host** The SPI controller peripheral inside {IDF_TARGET_NAME} that initiates SPI transmissions over the bus, and acts as an SPI Master.
|
||||
**Device** SPI slave device. An SPI bus may be connected to one or more Devices. 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.
|
||||
**MOSI** Master Out, Slave In, a.k.a. D. Data transmission from a Host to Device. Also data0 signal in Octal/OPI mode.
|
||||
**MISO** Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host. Also data1 signal in Octal/OPI mode.
|
||||
**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. Used for 4-bit (qio/qout) transactions. Also for data2 signal in Octal/OPI mode.
|
||||
**QUADHD** Hold signal. Used for 4-bit (qio/qout) transactions. Also for data3 signal in Octal/OPI mode.
|
||||
**DATA4** Data4 signal in Octal/OPI mode.
|
||||
**DATA5** Data5 signal in Octal/OPI mode.
|
||||
**DATA6** Data6 signal in Octal/OPI mode.
|
||||
**DATA7** Data7 signal in Octal/OPI mode.
|
||||
**Assertion** The action of activating a line.
|
||||
**De-assertion** The action of returning the line back to inactive (back to idle) status.
|
||||
**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.
|
||||
================= =========================================================================================
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Term
|
||||
- Definition
|
||||
* - Host
|
||||
- The SPI controller peripheral inside {IDF_TARGET_NAME} initiates SPI transmissions over the bus and acts as an SPI Master.
|
||||
* - Device
|
||||
- SPI slave Device. An SPI bus may be connected to one or more Devices. 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 a daisy-chain manner.
|
||||
* - MOSI
|
||||
- Master Out, Slave In, a.k.a. D. Data transmission from a Host to Device. Also data0 signal in Octal/OPI mode.
|
||||
* - MISO
|
||||
- Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host. Also data1 signal in Octal/OPI mode.
|
||||
* - SCLK
|
||||
- Serial Clock. The oscillating signal generated by a Host 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. Used for 4-bit (qio/qout) transactions. Also for the data2 signal in Octal/OPI mode.
|
||||
* - QUADHD
|
||||
- Hold signal. Used for 4-bit (qio/qout) transactions. Also for the data3 signal in Octal/OPI mode.
|
||||
* - DATA4
|
||||
- Data4 signal in Octal/OPI mode.
|
||||
* - DATA5
|
||||
- Data5 signal in Octal/OPI mode.
|
||||
* - DATA6
|
||||
- Data6 signal in Octal/OPI mode.
|
||||
* - DATA7
|
||||
- Data7 signal in Octal/OPI mode.
|
||||
* - Assertion
|
||||
- The action of activating a line.
|
||||
* - De-assertion
|
||||
- The action of returning the line back to inactive (back to idle) status.
|
||||
* - 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
|
||||
---------------
|
||||
|
||||
The SPI master driver governs communications of Hosts with Devices. The driver supports the following features:
|
||||
The SPI Master driver governs the communications between Hosts and Devices. The driver supports the following features:
|
||||
|
||||
- Multi-threaded environments
|
||||
- Transparent handling of DMA transfers while reading and writing data
|
||||
|
@ -75,9 +95,9 @@ The SPI master driver governs communications of Hosts with Devices. The driver s
|
|||
|
||||
.. warning::
|
||||
|
||||
The SPI master driver has the concept of multiple Devices connected to a single bus (sharing a single {IDF_TARGET_NAME} SPI peripheral). As long as each Device is accessed by only one task, the driver is thread safe. However, if multiple tasks try to access the same SPI Device, the driver is **not thread-safe**. In this case, it is recommended to either:
|
||||
The SPI Master driver allows multiple Devices to be connected on a same SPI bus (sharing a single {IDF_TARGET_NAME} SPI peripheral). As long as each Device is accessed by only one task, the driver is thread-safe. However, if multiple tasks try to access the same SPI Device, the driver is **not thread-safe**. In this case, it is recommended to either:
|
||||
|
||||
- Refactor your application so that each SPI peripheral is only accessed by a single task at a time. You can 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 safety.
|
||||
- Refactor your application so that each SPI peripheral is only accessed by a single task at a time. You can 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 safety.
|
||||
- Add a mutex lock around the shared Device using :c:macro:`xSemaphoreCreateMutex`.
|
||||
|
||||
.. toctree::
|
||||
|
@ -92,24 +112,31 @@ An SPI bus transaction consists of five phases which can be found in the table b
|
|||
|
||||
{IDF_TARGET_ADDR_LEN:default="32", esp32="64"}
|
||||
|
||||
============== =========================================================================================================
|
||||
Phase Description
|
||||
============== =========================================================================================================
|
||||
**Command** In this phase, a command (0-16 bit) is written to the bus by the Host.
|
||||
**Address** In this phase, an address (0-{IDF_TARGET_ADDR_LEN} bit) is transmitted over the bus by the Host.
|
||||
**Dummy** This phase is configurable and is used to meet the timing requirements.
|
||||
**Write** Host sends data to a Device. This data follows the optional command and address phases and is indistinguishable from them at the electrical level.
|
||||
**Read** Device sends data to its Host.
|
||||
============== =========================================================================================================
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Phase
|
||||
- Description
|
||||
* - Command
|
||||
- In this phase, a command (0-16 bit) is written to the bus by the Host.
|
||||
* - Address
|
||||
- In this phase, an address (0-{IDF_TARGET_ADDR_LEN} bit) is transmitted over the bus by the Host.
|
||||
* - Dummy
|
||||
- This phase is configurable and is used to meet the timing requirements.
|
||||
* - Write
|
||||
- Host sends data to a Device. This data follows the optional command and address phases and is indistinguishable from them at the electrical level.
|
||||
* - Read
|
||||
- Device sends data to its Host.
|
||||
|
||||
.. todo::
|
||||
|
||||
Add a package diagram.
|
||||
|
||||
|
||||
The attributes of a transaction are determined by the bus configuration structure :cpp:type:`spi_bus_config_t`, device configuration structure :cpp:type:`spi_device_interface_config_t`, and transaction configuration structure :cpp:type:`spi_transaction_t`.
|
||||
The attributes of a transaction are determined by the bus configuration structure :cpp:type:`spi_bus_config_t`, Device configuration structure :cpp:type:`spi_device_interface_config_t`, and transaction configuration structure :cpp:type:`spi_transaction_t`.
|
||||
|
||||
An SPI Host can send full-duplex transactions, during which the read and write phases occur simultaneously. The total transaction length is determined by the sum of the following members:
|
||||
An SPI Host can send full-duplex transactions, during which the Read and Write phases occur simultaneously. The total transaction length is determined by the sum of the following members:
|
||||
|
||||
- :cpp:member:`spi_device_interface_config_t::command_bits`
|
||||
- :cpp:member:`spi_device_interface_config_t::address_bits`
|
||||
|
@ -117,13 +144,13 @@ An SPI Host can send full-duplex transactions, during which the read and write p
|
|||
|
||||
While the member :cpp:member:`spi_transaction_t::rxlength` only determines the length of data received into the buffer.
|
||||
|
||||
In half-duplex transactions, the read and write phases are not simultaneous (one direction at a time). The lengths of the write and read phases are determined by :cpp:member:`spi_transaction_t::length` and :cpp:member:`spi_transaction_t::rxlength` respectively.
|
||||
In half-duplex transactions, the Read and Write phases are not simultaneous (one direction at a time). The lengths of the Write and Read phases are determined by :cpp:member:`spi_transaction_t::length` and :cpp:member:`spi_transaction_t::rxlength` respectively.
|
||||
|
||||
The command and address phases are optional, as not every SPI device requires a command and/or address. This is reflected in the Device's configuration: if :cpp:member:`spi_device_interface_config_t::command_bits` and/or :cpp:member:`spi_device_interface_config_t::address_bits` are set to zero, no command or address phase will occur.
|
||||
The Command and Address phases are optional, as not every SPI Device requires a command and/or address. This is reflected in the Device's configuration: if :cpp:member:`spi_device_interface_config_t::command_bits` and/or :cpp:member:`spi_device_interface_config_t::address_bits` are set to zero, no Command or Address phase will occur.
|
||||
|
||||
The read and write phases can also be optional, as not every transaction requires both writing and reading data. If :cpp:member:`spi_transaction_t::rx_buffer` is NULL and :c:macro:`SPI_TRANS_USE_RXDATA` is not set, the read phase is skipped. If :cpp:member:`spi_transaction_t::tx_buffer` is NULL and :c:macro:`SPI_TRANS_USE_TXDATA` is not set, the write phase is skipped.
|
||||
The Read and Write phases can also be optional, as not every transaction requires both writing and reading data. If :cpp:member:`spi_transaction_t::rx_buffer` is NULL and :c:macro:`SPI_TRANS_USE_RXDATA` is not set, the Read phase is skipped. If :cpp:member:`spi_transaction_t::tx_buffer` is NULL and :c:macro:`SPI_TRANS_USE_TXDATA` is not set, the Write phase is skipped.
|
||||
|
||||
The driver supports two types of transactions: the interrupt transactions and polling transactions. The programmer can choose to use a different transaction type per Device. If your Device requires both transaction types, see :ref:`mixed_transactions`.
|
||||
The driver supports two types of transactions: interrupt transactions and polling transactions. The programmer can choose to use a different transaction type per Device. If your Device requires both transaction types, see :ref:`mixed_transactions`.
|
||||
|
||||
|
||||
.. _interrupt_transactions:
|
||||
|
@ -133,7 +160,7 @@ Interrupt Transactions
|
|||
|
||||
Interrupt transactions will block the transaction routine until the transaction completes, thus allowing the CPU to run other tasks.
|
||||
|
||||
An application task can queue multiple transactions, and the driver will automatically handle them one-by-one in the interrupt service routine (ISR). It allows the task to switch to other procedures until all the transactions complete.
|
||||
An application task can queue multiple transactions, and the driver will automatically handle them one by one in the interrupt service routine (ISR). It allows the task to switch to other procedures until all the transactions are complete.
|
||||
|
||||
|
||||
.. _polling_transactions:
|
||||
|
@ -145,70 +172,128 @@ Polling transactions do not use interrupts. The routine keeps polling the SPI Ho
|
|||
|
||||
All the tasks that use interrupt transactions can be blocked by the queue. At this point, they will need to wait for the ISR to run twice before the transaction is finished. Polling transactions save time otherwise spent on queue handling and context switching, which results in smaller transaction duration. The disadvantage is that the CPU is busy while these transactions are in progress.
|
||||
|
||||
The :cpp:func:`spi_device_polling_end` routine needs an overhead of at least 1 us to unblock other tasks when the transaction is finished. It is strongly recommended to wrap a series of polling transactions using the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus` to avoid the overhead. For more information, see :ref:`bus_acquiring`.
|
||||
The :cpp:func:`spi_device_polling_end` routine needs an overhead of at least 1 µs to unblock other tasks when the transaction is finished. It is strongly recommended to wrap a series of polling transactions using the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus` to avoid the overhead. For more information, see :ref:`bus_acquiring`.
|
||||
|
||||
.. _transaction-line-mode:
|
||||
|
||||
Transaction Line Mode
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Supported line modes for {IDF_TARGET_NAME} are listed as follows, to make use of these modes, set the member `flags` in the struct :cpp:type:`spi_transaction_t` as shown in the `Transaction Flag` column. If you want to check if corresponding IO pins are set or not, set the member `flags` in the :cpp:type:`spi_bus_config_t` as shown in the `Bus IO setting Flag` column.
|
||||
Supported line modes for {IDF_TARGET_NAME} are listed as follows, to make use of these modes, set the member ``flags`` in the struct :cpp:type:`spi_transaction_t` as shown in the ``Transaction Flag`` column. If you want to check if corresponding IO pins are set or not, set the member ``flags`` in the :cpp:type:`spi_bus_config_t` as shown in the ``Bus IO setting Flag`` column.
|
||||
|
||||
|
||||
.. only:: not SOC_SPI_SUPPORT_OCT
|
||||
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Mode name | Command Line Width | Address Line Width | Data Line Width | Transaction Flag | Bus IO setting Flag |
|
||||
+==============+====================+====================+=================+============================+=========================+
|
||||
| Normal SPI | 1 | 1 | 1 | 0 | 0 |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Dual Output | 1 | 1 | 2 | SPI_TRANS_MODE_DIO | |
|
||||
| | | | | | |
|
||||
| | | | | | SPICOMMON_BUSFLAG_DUAL |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Dual I/O | 1 | 2 | 2 | SPI_TRANS_MODE_DIO | | |
|
||||
| | | | | SPI_TRANS_MULTILINE_ADDR | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Quad Output | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
|
||||
| Quad I/O | 1 | 4 | 4 | SPI_TRANS_MODE_QIO | | SPICOMMON_BUSFLAG_QUAD |
|
||||
| | | | | SPI_TRANS_MULTILINE_ADDR | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
.. list-table::
|
||||
:widths: 30 40 40 40 50 50
|
||||
:header-rows: 1
|
||||
|
||||
* - Mode name
|
||||
- Command Line Width
|
||||
- Address Line Width
|
||||
- Data Line Width
|
||||
- Transaction Flag
|
||||
- Bus IO Setting Flag
|
||||
* - Normal SPI
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 0
|
||||
- 0
|
||||
* - Dual Output
|
||||
- 1
|
||||
- 1
|
||||
- 2
|
||||
- {SPI_TRANS_MODE_DIO}
|
||||
- {SPICOMMON_BUSFLAG_DUAL}
|
||||
* - Dual I/O
|
||||
- 1
|
||||
- 2
|
||||
- 2
|
||||
- * {SPI_TRANS_MODE_DIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
-
|
||||
* - Quad Output
|
||||
- 1
|
||||
- 1
|
||||
- 4
|
||||
- {SPI_TRANS_MODE_QIO}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
* - Quad I/O
|
||||
- 1
|
||||
- 4
|
||||
- 4
|
||||
- * {SPI_TRANS_MODE_QIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
|
||||
.. only:: SOC_SPI_SUPPORT_OCT
|
||||
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Mode name | Command Line Width | Address Line Width | Data Line Width | Transaction Flag | Bus IO setting Flag |
|
||||
+==============+====================+====================+=================+============================+=========================+
|
||||
| Normal SPI | 1 | 1 | 1 | 0 | 0 |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Dual Output | 1 | 1 | 2 | SPI_TRANS_MODE_DIO | |
|
||||
| | | | | | |
|
||||
| | | | | | SPICOMMON_BUSFLAG_DUAL |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Dual I/O | 1 | 2 | 2 | SPI_TRANS_MODE_DIO | | |
|
||||
| | | | | SPI_TRANS_MULTILINE_ADDR | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Quad Output | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
|
||||
| Quad I/O | 1 | 4 | 4 | SPI_TRANS_MODE_QIO | | SPICOMMON_BUSFLAG_QUAD |
|
||||
| | | | | SPI_TRANS_MULTILINE_ADDR | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
| Octal Output | 1 | 1 | 8 | SPI_TRANS_MODE_OCT | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
|
||||
| OPI | 8 | 8 | 8 | SPI_TRANS_MODE_OCT | | SPICOMMON_BUSFLAG_OCTAL |
|
||||
| | | | | SPI_TRANS_MULTILINE_ADDR | | |
|
||||
| | | | | SPI_TRANS_MULTILINE_CMD | |
|
||||
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
|
||||
.. list-table::
|
||||
:widths: 30 40 40 40 50 50
|
||||
:header-rows: 1
|
||||
|
||||
* - Mode name
|
||||
- Command Line Width
|
||||
- Address Line Width
|
||||
- Data Line Width
|
||||
- Transaction Flag
|
||||
- Bus IO Setting Flag
|
||||
* - Normal SPI
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 0
|
||||
- 0
|
||||
* - Dual Output
|
||||
- 1
|
||||
- 1
|
||||
- 2
|
||||
- {SPI_TRANS_MODE_DIO}
|
||||
- {SPICOMMON_BUSFLAG_DUAL}
|
||||
* - Dual I/O
|
||||
- 1
|
||||
- 2
|
||||
- 2
|
||||
- * {SPI_TRANS_MODE_DIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
-
|
||||
* - Quad Output
|
||||
- 1
|
||||
- 1
|
||||
- 4
|
||||
- {SPI_TRANS_MODE_QIO}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
* - Quad I/O
|
||||
- 1
|
||||
- 4
|
||||
- 4
|
||||
- * {SPI_TRANS_MODE_QIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
* - Octal Output
|
||||
- 1
|
||||
- 1
|
||||
- 8
|
||||
- {SPI_TRANS_MODE_OCT}
|
||||
- {SPICOMMON_BUSFLAG_OCTAL}
|
||||
* - OPI
|
||||
- 8
|
||||
- 8
|
||||
- 8
|
||||
- * {SPI_TRANS_MODE_OCT}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
* {SPI_TRANS_MULTILINE_CMD}
|
||||
- {SPICOMMON_BUSFLAG_OCTAL}
|
||||
|
||||
Command and Address Phases
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
During the command and address phases, the members :cpp:member:`spi_transaction_t::cmd` and :cpp:member:`spi_transaction_t::addr` are sent to the bus, nothing is read at this time. The default lengths of the command and address phases are set in :cpp:type:`spi_device_interface_config_t` by calling :cpp:func:`spi_bus_add_device`. If the flags :c:macro:`SPI_TRANS_VARIABLE_CMD` and :c:macro:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_t::flags` are not set, the driver automatically sets the length of these phases to default values during Device initialization.
|
||||
During the Command and Address phases, the members :cpp:member:`spi_transaction_t::cmd` and :cpp:member:`spi_transaction_t::addr` are sent to the bus, nothing is read at this time. The default lengths of the Command and Address phases are set in :cpp:type:`spi_device_interface_config_t` by calling :cpp:func:`spi_bus_add_device`. If the flags :c:macro:`SPI_TRANS_VARIABLE_CMD` and :c:macro:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_t::flags` are not set, the driver automatically sets the length of these phases to default values during Device initialization.
|
||||
|
||||
If the lengths of the command and address phases need to be variable, declare the struct :cpp:type:`spi_transaction_ext_t`, set the flags :c:macro:`SPI_TRANS_VARIABLE_CMD` and/or :c:macro:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_ext_t::base` and configure the rest of base as usual. Then the length of each phase will be equal to :cpp:member:`spi_transaction_ext_t::command_bits` and :cpp:member:`spi_transaction_ext_t::address_bits` set in the struct :cpp:type:`spi_transaction_ext_t`.
|
||||
If the lengths of the Command and Address phases need to be variable, declare the struct :cpp:type:`spi_transaction_ext_t`, set the flags :c:macro:`SPI_TRANS_VARIABLE_CMD` and/or :c:macro:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_ext_t::base` and configure the rest of base as usual. Then the length of each phase will be equal to :cpp:member:`spi_transaction_ext_t::command_bits` and :cpp:member:`spi_transaction_ext_t::address_bits` set in the struct :cpp:type:`spi_transaction_ext_t`.
|
||||
|
||||
If the command and address phase need to be as the same number of lines as data phase, you need to set `SPI_TRANS_MULTILINE_CMD` and/or `SPI_TRANS_MULTILINE_ADDR` to the `flags` member in the struct :cpp:type:`spi_transaction_t`. Also see :ref:`transaction-line-mode`.
|
||||
If the Command and Address phase need to have the same number of lines as the data phase, you need to set ``SPI_TRANS_MULTILINE_CMD`` and/or ``SPI_TRANS_MULTILINE_ADDR`` to the ``flags`` member in the struct :cpp:type:`spi_transaction_t`. Also see :ref:`transaction-line-mode`.
|
||||
|
||||
|
||||
Write and Read Phases
|
||||
|
@ -216,31 +301,31 @@ Write and Read Phases
|
|||
|
||||
Normally, the data that needs to be transferred to or from a Device will be read from or written to a chunk of memory indicated by the members :cpp:member:`spi_transaction_t::rx_buffer` and :cpp:member:`spi_transaction_t::tx_buffer`. If DMA is enabled for transfers, the buffers are required to be:
|
||||
|
||||
1. Allocated in DMA-capable internal memory. If :ref:`external PSRAM is enabled<dma-capable-memory>`, this means using ``pvPortMallocCaps(size, MALLOC_CAP_DMA)``.
|
||||
2. 32-bit aligned (staring from a 32-bit boundary and having a length of multiples of 4 bytes).
|
||||
1. Allocated in DMA-capable internal memory (MALLOC_CAP_DMA), see :ref:`DMA-Capable Memory<dma-capable-memory>`.
|
||||
2. 32-bit aligned (starting from a 32-bit boundary and having a length of multiples of 4 bytes).
|
||||
|
||||
If these requirements are not satisfied, the transaction efficiency will be affected due to the allocation and copying of temporary buffers.
|
||||
|
||||
If using more than one data lines to transmit, please set `SPI_DEVICE_HALFDUPLEX` flag for the member `flags` in the struct :cpp:type:`spi_device_interface_config_t`. And the member `flags` in the struct :cpp:type:`spi_transaction_t` should be set as described in :ref:`transaction-line-mode`.
|
||||
If using more than one data line to transmit, please set ``SPI_DEVICE_HALFDUPLEX`` flag for the member ``flags`` in the struct :cpp:type:`spi_device_interface_config_t`. And the member ``flags`` in the struct :cpp:type:`spi_transaction_t` should be set as described in :ref:`transaction-line-mode`.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. note::
|
||||
|
||||
Half-duplex transactions with both read and write phases are not supported when using DMA. For details and workarounds, see :ref:`spi_known_issues`.
|
||||
Half-duplex transactions with both Read and Write phases are not supported when using DMA. For details and workarounds, see :ref:`spi_known_issues`.
|
||||
|
||||
.. only:: esp32s3 or esp32c3 or esp32c2 or esp32c6 or esp32h2
|
||||
|
||||
.. note::
|
||||
|
||||
Half-duplex transactions with both read and write phases are not supported. Please use full duplex mode.
|
||||
Half-duplex transactions with both Read and Write phases are not supported. Please use full duplex mode.
|
||||
|
||||
.. _bus_acquiring:
|
||||
|
||||
Bus Acquiring
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Sometimes you might want to send SPI transactions exclusively and continuously so that it takes as little time as possible. For this, you can use bus acquiring, which helps to suspend transactions (both polling or interrupt) to other devices until the bus is released. To acquire and release a bus, use the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus`.
|
||||
Sometimes you might want to send SPI transactions exclusively and continuously so that it takes as little time as possible. For this, you can use bus acquiring, which helps to suspend transactions (both polling or interrupt) to other Devices until the bus is released. To acquire and release a bus, use the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus`.
|
||||
|
||||
|
||||
Driver Usage
|
||||
|
@ -248,23 +333,23 @@ Driver Usage
|
|||
|
||||
.. todo::
|
||||
|
||||
Organize the Driver Usage into subsections that will reflect the general usage experience of the users, e.g.,
|
||||
Organize the Driver Usage into subsections that will reflect the general user experience of the users, e.g.,
|
||||
|
||||
Configuration
|
||||
|
||||
Add stuff about the configuration API here, and the various options in configuration (e.g., configure for interrupt vs. polling), and optional configuration
|
||||
Add stuff about the configuration API here, the various options in configuration (e.g., configure for interrupt vs. polling), and optional configuration
|
||||
|
||||
Transactions
|
||||
|
||||
Describe how to execute a normal transaction (i.e., where data is larger than 32 bits). Describe how to configure between big and little-endian.
|
||||
|
||||
- Add subsub section on how to optimize when transmitting less than 32 bits
|
||||
- Add subsub section on how to transmit mixed transactions to the same device
|
||||
- Add a sub-sub section on how to optimize when transmitting less than 32 bits
|
||||
- Add a sub-sub section on how to transmit mixed transactions to the same Device
|
||||
|
||||
|
||||
- Initialize an SPI bus by calling the function :cpp:func:`spi_bus_initialize`. Make sure to set the correct I/O pins in the struct :cpp:type:`spi_bus_config_t`. Set the signals that are not needed to ``-1``.
|
||||
|
||||
- Register a Device connected to the bus with the driver by calling the function :cpp:func:`spi_bus_add_device`. Make sure to configure any timing requirements the device might need with the parameter ``dev_config``. You should now have obtained the Device's handle which will be used when sending a transaction to it.
|
||||
- Register a Device connected to the bus with the driver by calling the function :cpp:func:`spi_bus_add_device`. Make sure to configure any timing requirements the Device might need with the parameter ``dev_config``. You should now have obtained the Device's handle which will be used when sending a transaction to it.
|
||||
|
||||
- To interact with the Device, fill one or more :cpp:type:`spi_transaction_t` structs with any transaction parameters required. Then send the structs either using a polling transaction or an interrupt transaction:
|
||||
|
||||
|
@ -276,11 +361,11 @@ Driver Usage
|
|||
|
||||
- (Optional) To perform back-to-back transactions with a Device, call the function :cpp:func:`spi_device_acquire_bus` before sending transactions and :cpp:func:`spi_device_release_bus` after the transactions have been sent.
|
||||
|
||||
- (Optional) To unload the driver for a certain Device, call :cpp:func:`spi_bus_remove_device` with the Device handle as an argument.
|
||||
- (Optional) To remove a certain Device from the bus, call :cpp:func:`spi_bus_remove_device` with the Device handle as an argument.
|
||||
|
||||
- (Optional) To remove the driver for a bus, make sure no more drivers are attached and call :cpp:func:`spi_bus_free`.
|
||||
- (Optional) To remove the driver from the bus, make sure no more devices are attached and call :cpp:func:`spi_bus_free`.
|
||||
|
||||
The example code for the SPI master driver can be found in the :example:`peripherals/spi_master` directory of ESP-IDF examples.
|
||||
The example code for the SPI Master driver can be found in the :example:`peripherals/spi_master` directory of ESP-IDF examples.
|
||||
|
||||
|
||||
Transactions with Data Not Exceeding 32 Bits
|
||||
|
@ -289,16 +374,16 @@ Transactions with Data Not Exceeding 32 Bits
|
|||
When the transaction data size is equal to or less than 32 bits, it will be sub-optimal to allocate a buffer for the data. The data can be directly stored in the transaction struct instead. For transmitted data, it can be achieved by using the :cpp:member:`spi_transaction_t::tx_data` member and setting the :c:macro:`SPI_TRANS_USE_TXDATA` flag on the transmission. For received data, use :cpp:member:`spi_transaction_t::rx_data` and set :c:macro:`SPI_TRANS_USE_RXDATA`. In both cases, do not touch the :cpp:member:`spi_transaction_t::tx_buffer` or :cpp:member:`spi_transaction_t::rx_buffer` members, because they use the same memory locations as :cpp:member:`spi_transaction_t::tx_data` and :cpp:member:`spi_transaction_t::rx_data`.
|
||||
|
||||
|
||||
Transactions with Integers Other Than ``uint8_t``
|
||||
Transactions with Integers Other than ``uint8_t``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
An SPI Host reads and writes data into memory byte by byte. By default, data is sent with the most significant bit (MSB) first, as LSB first used in rare cases. If a value less than 8 bits needs to be sent, the bits should be written into memory in the MSB first manner.
|
||||
An SPI Host reads and writes data into memory byte by byte. By default, data is sent with the most significant bit (MSB) first, as LSB is first used in rare cases. If a value of fewer than 8 bits needs to be sent, the bits should be written into memory in the MSB first manner.
|
||||
|
||||
For example, if ``0b00010`` needs to be sent, it should be written into a ``uint8_t`` variable, and the length for reading should be set to 5 bits. The Device will still receive 8 bits with 3 additional "random" bits, so the reading must be performed correctly.
|
||||
|
||||
On top of that, {IDF_TARGET_NAME} is a little-endian chip, which means that the least significant byte of ``uint16_t`` and ``uint32_t`` variables is stored at the smallest address. Hence, if ``uint16_t`` is stored in memory, bits [7:0] are sent first, followed by bits [15:8].
|
||||
|
||||
For cases when the data to be transmitted has the size differing from ``uint8_t`` arrays, the following macros can be used to transform data to the format that can be sent by the SPI driver directly:
|
||||
For cases when the data to be transmitted has a size differing from ``uint8_t`` arrays, the following macros can be used to transform data to the format that can be sent by the SPI driver directly:
|
||||
|
||||
- :c:macro:`SPI_SWAP_DATA_TX` for data to be transmitted
|
||||
- :c:macro:`SPI_SWAP_DATA_RX` for data received
|
||||
|
@ -309,7 +394,7 @@ For cases when the data to be transmitted has the size differing from ``uint8_t`
|
|||
Notes on Sending Mixed Transactions to the Same Device
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To reduce coding complexity, send only one type of transactions (interrupt or polling) to one Device. However, you still can send both interrupt and polling transactions alternately. The notes below explain how to do this.
|
||||
To reduce coding complexity, send only one type of transaction (interrupt or polling) to one Device. However, you still can send both interrupt and polling transactions alternately. The notes below explain how to do this.
|
||||
|
||||
The polling transactions should be initiated only after all the polling and interrupt transactions are finished.
|
||||
|
||||
|
@ -323,63 +408,43 @@ To have better control of the calling sequence of functions, send mixed transact
|
|||
|
||||
.. _spi_master_on_spi1_bus:
|
||||
|
||||
Notes on Using the SPI Master driver on SPI1 Bus
|
||||
Notes on Using the SPI Master Driver on SPI1 Bus
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. note::
|
||||
|
||||
Though the :ref:`spi_bus_lock` feature makes it possible to use SPI Master driver on the SPI1
|
||||
bus, it's still tricky and needs a lot of special treatment. It's a feature for advanced
|
||||
developers.
|
||||
Though the :ref:`spi_bus_lock` feature makes it possible to use SPI Master driver on the SPI1 bus, it's still tricky and needs a lot of special treatment. It's a feature for advanced developers.
|
||||
|
||||
To use SPI Master driver on SPI1 bus, you have to take care of two problems:
|
||||
|
||||
1. The code and data, required at the meanwhile the driver is operating SPI1 bus, should be
|
||||
in the internal memory.
|
||||
1. The code and data should be in the internal memory when the driver is operating on SPI1 bus.
|
||||
|
||||
SPI1 bus is shared among devices and the cache for data (code) in the Flash as well as the
|
||||
PSRAM. The cache should be disabled during the other drivers are operating the SPI1 bus.
|
||||
Hence the data (code) in the flash as well as the PSRAM cannot be fetched at the meanwhile
|
||||
the driver acquires the SPI1 bus by:
|
||||
SPI1 bus is shared among Devices and the cache for data (code) in the flash as well as the PSRAM. The cache should be disabled when other drivers are operating on the SPI1 bus. Hence the data (code) in the flash as well as the PSRAM cannot be fetched while the driver acquires the SPI1 bus by:
|
||||
|
||||
- Explicit bus acquiring between :cpp:func:`spi_device_acquire_bus` and
|
||||
:cpp:func:`spi_device_release_bus`.
|
||||
- Implicit bus acquiring between :cpp:func:`spi_device_polling_start` and
|
||||
:cpp:func:`spi_device_polling_end` (or inside :cpp:func:`spi_device_polling_transmit`).
|
||||
- Explicit bus acquiring between :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus`.
|
||||
- Implicit bus acquiring between :cpp:func:`spi_device_polling_start` and :cpp:func:`spi_device_polling_end` (or inside :cpp:func:`spi_device_polling_transmit`).
|
||||
|
||||
During the time above, all other tasks and most ISRs will be disabled (see
|
||||
:ref:`iram-safe-interrupt-handlers`). Application code and data used by current task
|
||||
should be placed in internal memory (DRAM or IRAM), or already in the ROM. Access to
|
||||
external memory (flash code, const data in the flash, and static/heap data in the PSRAM)
|
||||
will cause a `Cache disabled but cached memory region accessed` exception. For differences
|
||||
between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout
|
||||
<memory-layout>` documentation.
|
||||
During the time above, all other tasks and most ISRs will be disabled (see :ref:`iram-safe-interrupt-handlers`). Application code and data used by the current task should be placed in internal memory (DRAM or IRAM), or already in the ROM. Access to external memory (flash code, const data in the flash, and static/heap data in the PSRAM) will cause a ``Cache disabled but cached memory region accessed`` exception. For differences between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
|
||||
|
||||
To place functions into the IRAM, you can either:
|
||||
|
||||
1. Add `IRAM_ATTR` (include "esp_attr.h") to the function like:
|
||||
1. Add ``IRAM_ATTR`` (include ``esp_attr.h``) to the function like:
|
||||
|
||||
IRAM_ATTR void foo(void) { }
|
||||
|
||||
Please note that when a function is inlined, it will follow its caller's segment, and
|
||||
the attribute will not take effect. You may need to use `NOLINE_ATTR` to avoid this.
|
||||
Please note that when a function is inlined, it will follow its caller's segment, and the attribute will not take effect. You may need to use ``NOLINE_ATTR`` to avoid this. Please also note that the compiler may transform some code into a lookup table in the const data, so ``noflash_text`` is not safe.
|
||||
|
||||
2. Use the `noflash` placement in the `linker.lf`. See more in
|
||||
:doc:`../../api-guides/linker-script-generation`. Please note that, some code may be
|
||||
transformed into lookup table in the const data by the compiler, so `noflash_text` is not
|
||||
safe.
|
||||
2. Use the ``noflash`` placement in the ``linker.lf``. See more in :doc:`../../api-guides/linker-script-generation`. Please note that the compiler may transform some code into a lookup table in the const data, so ``noflash_text`` is not safe.
|
||||
|
||||
Please do take care that the optimization level may affect the compiler behavior of inline,
|
||||
or transforming some code into lookup table in the const data, etc.
|
||||
Please do take care that the optimization level may affect the compiler behavior of inline, or transform some code into a lookup table in the const data, etc.
|
||||
|
||||
To place data into the DRAM, you can either:
|
||||
|
||||
1. Add `DRAM_ATTR` (include "esp_attr.h") to the data definition like:
|
||||
1. Add ``DRAM_ATTR`` (include ``esp_attr.h``) to the data definition like:
|
||||
|
||||
DRAM_ATTR int g_foo = 3;
|
||||
|
||||
2. Use the `noflash` placement in the linker.lf. See more in
|
||||
:doc:`../../api-guides/linker-script-generation`.
|
||||
2. Use the ``noflash`` placement in the linker.lf. See more in :doc:`../../api-guides/linker-script-generation`.
|
||||
|
||||
Please also see the example :example:`peripherals/spi_master/hd_eeprom`.
|
||||
|
||||
|
@ -389,7 +454,7 @@ GPIO Matrix and IO_MUX
|
|||
|
||||
.. only:: esp32
|
||||
|
||||
Most of ESP32'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.
|
||||
Most of ESP32's peripheral signals have a 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.
|
||||
|
||||
The GPIO matrix introduces flexibility of routing but also brings the following disadvantages:
|
||||
|
||||
|
@ -402,23 +467,31 @@ GPIO Matrix and IO_MUX
|
|||
|
||||
The IO_MUX pins for SPI buses are given below.
|
||||
|
||||
+----------+------+------+
|
||||
| Pin Name | SPI2 | SPI3 |
|
||||
+ +------+------+
|
||||
| | GPIO Number |
|
||||
+==========+======+======+
|
||||
| CS0 [1]_ | 15 | 5 |
|
||||
+----------+------+------+
|
||||
| SCLK | 14 | 18 |
|
||||
+----------+------+------+
|
||||
| MISO | 12 | 19 |
|
||||
+----------+------+------+
|
||||
| MOSI | 13 | 23 |
|
||||
+----------+------+------+
|
||||
| QUADWP | 2 | 22 |
|
||||
+----------+------+------+
|
||||
| QUADHD | 4 | 21 |
|
||||
+----------+------+------+
|
||||
.. list-table::
|
||||
:widths: 40 20 20
|
||||
:header-rows: 1
|
||||
|
||||
* - Pin Name
|
||||
- SPI 2 (GPIO Number)
|
||||
- SPI 3 (GPIO Number)
|
||||
* - CS0 [1]_
|
||||
- 15
|
||||
- 5
|
||||
* - SCLK
|
||||
- 14
|
||||
- 18
|
||||
* - MISO
|
||||
- 12
|
||||
- 19
|
||||
* - MOSI
|
||||
- 13
|
||||
- 23
|
||||
* - QUADWP
|
||||
- 2
|
||||
- 22
|
||||
* - QUADHD
|
||||
- 4
|
||||
- 21
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
|
@ -429,9 +502,9 @@ GPIO Matrix and IO_MUX
|
|||
{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.
|
||||
Most of the chip's peripheral signals have a 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 80MHz or lower frequencies, routing SPI pins via GPIO matrix will behave the same comparing to routing them via IOMUX.
|
||||
When an SPI Host is set to 80MHz or lower frequencies, routing SPI pins via the GPIO matrix will behave the same compared to routing them via IOMUX.
|
||||
|
||||
The IO_MUX pins for SPI buses are given below.
|
||||
|
||||
|
@ -478,15 +551,15 @@ Transaction Duration
|
|||
{IDF_TARGET_TRANS_TIME_INTR_CPU:default="N/A", esp32="25", esp32s2="22", esp32c3="27", esp32s3="24", esp32c2="40", esp32c6="32", esp32h2="54"}
|
||||
{IDF_TARGET_TRANS_TIME_POLL_CPU:default="N/A", esp32="8", esp32s2="8", esp32c3="9", esp32s3="9", esp32c2="15", esp32c6="15", esp32h2="24"}
|
||||
|
||||
Transaction duration includes setting up SPI peripheral registers, copying data to FIFOs or setting up DMA links, and the time for SPI transaction.
|
||||
Transaction duration includes setting up SPI peripheral registers, copying data to FIFOs or setting up DMA links, and the time for SPI transactions.
|
||||
|
||||
Interrupt transactions allow appending extra overhead to accommodate the cost of FreeRTOS queues and the time needed for switching between tasks and the ISR.
|
||||
|
||||
For **interrupt transactions**, the CPU can switch to other tasks when a transaction is in progress. This saves the CPU time but increases the transaction duration. See :ref:`interrupt_transactions`. For **polling transactions**, it does not block the task but allows to do polling when the transaction is in progress. For more information, see :ref:`polling_transactions`.
|
||||
For **interrupt transactions**, the CPU can switch to other tasks when a transaction is in progress. This saves CPU time but increases the transaction duration. See :ref:`interrupt_transactions`. For **polling transactions**, it does not block the task but allows to do polling when the transaction is in progress. For more information, see :ref:`polling_transactions`.
|
||||
|
||||
If DMA is enabled, setting up the linked list requires about 2 us per transaction. When a master is transferring data, it automatically reads the data from the linked list. If DMA is not enabled, the CPU has to write and read each byte from the FIFO by itself. Usually, this is faster than 2 us, but the transaction length is limited to 64 bytes for both write and read.
|
||||
If DMA is enabled, setting up the linked list requires about 2 µs per transaction. When a master is transferring data, it automatically reads the data from the linked list. If DMA is not enabled, the CPU has to write and read each byte from the FIFO by itself. Usually, this is faster than 2 µs, but the transaction length is limited to 64 bytes for both write and read.
|
||||
|
||||
Typical transaction duration for one byte of data are given below.
|
||||
The typical transaction duration for one byte of data is given below.
|
||||
|
||||
- Interrupt Transaction via DMA: {IDF_TARGET_TRANS_TIME_INTR_DMA} µs.
|
||||
- Interrupt Transaction via CPU: {IDF_TARGET_TRANS_TIME_INTR_CPU} µs.
|
||||
|
@ -498,75 +571,100 @@ Note that these data are tested with :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` enable
|
|||
SPI Clock Frequency
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Clock source of the GPSPI peripherals can be selected by setting :cpp:member:`spi_device_handle_t::cfg::clock_source`. You can refer to :cpp:type:`spi_clock_source_t` to know the supported clock sources.
|
||||
By default driver will set :cpp:member:`spi_device_handle_t::cfg::clock_source` to `SPI_CLK_SRC_DEFAULT`. This usually stands for the highest frequency among GPSPI clock sources. Its value will be different among chips.
|
||||
The clock source of the GPSPI peripherals can be selected by setting :cpp:member:`spi_device_handle_t::cfg::clock_source`. You can refer to :cpp:type:`spi_clock_source_t` to know the supported clock sources.
|
||||
By default driver will set :cpp:member:`spi_device_handle_t::cfg::clock_source` to ``SPI_CLK_SRC_DEFAULT``. This usually stands for the highest frequency among GPSPI clock sources. Its value will be different among chips.
|
||||
|
||||
Actual clock frequency of a device may not be exactly equal to the number you set, it will be re-calculated by the driver to the nearest hardware compatible number, and not larger than the clock frequency of the clock source. You can call :cpp:func:`spi_device_get_actual_freq` to know the actual frequency computed by the driver.
|
||||
The actual clock frequency of a Device may not be exactly equal to the number you set, it will be re-calculated by the driver to the nearest hardware-compatible number, and not larger than the clock frequency of the clock source. You can call :cpp:func:`spi_device_get_actual_freq` to know the actual frequency computed by the driver.
|
||||
|
||||
Theoretical maximum transfer speed of Write or Read phase can be calculated according to the table below:
|
||||
The theoretical maximum transfer speed of the Write or Read phase can be calculated according to the table below:
|
||||
|
||||
.. only:: not SOC_SPI_SUPPORT_OCT
|
||||
|
||||
+--------------------------------+------------------------+
|
||||
| Line Width of Write/Read phase | Speed (Bps) |
|
||||
+================================+========================+
|
||||
| 1-Line | *SPI Frequency / 8* |
|
||||
+--------------------------------+------------------------+
|
||||
| 2-Line | *SPI Frequency / 4* |
|
||||
+--------------------------------+------------------------+
|
||||
| 4-Line | *SPI Frequency / 2* |
|
||||
+--------------------------------+------------------------+
|
||||
.. list-table::
|
||||
:widths: 40 30
|
||||
:header-rows: 1
|
||||
|
||||
* - Line Width of Write/Read phase
|
||||
- Speed (Bps)
|
||||
* - 1-Line
|
||||
- *SPI Frequency / 8*
|
||||
* - 2-Line
|
||||
- *SPI Frequency / 4*
|
||||
* - 4-Line
|
||||
- *SPI Frequency / 2*
|
||||
|
||||
.. only:: SOC_SPI_SUPPORT_OCT
|
||||
|
||||
+--------------------------------+------------------------+
|
||||
| Line Width of Write/Read phase | Speed (Bps) |
|
||||
+================================+========================+
|
||||
| 1-Line | *SPI Frequency / 8* |
|
||||
+--------------------------------+------------------------+
|
||||
| 2-Line | *SPI Frequency / 4* |
|
||||
+--------------------------------+------------------------+
|
||||
| 4-Line | *SPI Frequency / 2* |
|
||||
+--------------------------------+------------------------+
|
||||
| 8-Line | *SPI Frequency* |
|
||||
+--------------------------------+------------------------+
|
||||
.. list-table::
|
||||
:widths: 40 30
|
||||
:header-rows: 1
|
||||
|
||||
The transfer speed calculation of other phases(command, address, dummy) are similar.
|
||||
* - Line Width of Write/Read phase
|
||||
- Speed (Bps)
|
||||
* - 1-Line
|
||||
- *SPI Frequency / 8*
|
||||
* - 2-Line
|
||||
- *SPI Frequency / 4*
|
||||
* - 4-Line
|
||||
- *SPI Frequency / 2*
|
||||
* - 8-Line
|
||||
- *SPI Frequency*
|
||||
|
||||
The transfer speed calculation of other phases(Command, Address, Dummy) is similar.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
If the clock frequency is too high, the use of some functions might be limited. See :ref:`timing_considerations`.
|
||||
|
||||
Cache Miss
|
||||
^^^^^^^^^^
|
||||
Cache Missing
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The default config puts only the ISR into the IRAM. Other SPI related functions, including the driver itself and the callback, might suffer from cache misses and will need to wait until the code is read from flash. Select :ref:`CONFIG_SPI_MASTER_IN_IRAM` to put the whole SPI driver into IRAM and put the entire callback(s) and its callee functions into IRAM to prevent cache misses.
|
||||
The default config puts only the ISR into the IRAM. Other SPI-related functions, including the driver itself and the callback, might suffer from cache misses and will need to wait until the code is read from flash. Select :ref:`CONFIG_SPI_MASTER_IN_IRAM` to put the whole SPI driver into IRAM and put the entire callback(s) and its callee functions into IRAM to prevent cache missing.
|
||||
|
||||
.. note::
|
||||
|
||||
SPI driver implementation is based on FreeRTOS APIs, to use :ref:`CONFIG_SPI_MASTER_IN_IRAM`, you should not enable :ref:`CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH`.
|
||||
|
||||
For an interrupt transaction, the overall cost is *20+8n/Fspi[MHz]* [us] for n bytes transferred in one transaction. Hence, the transferring speed is: *n/(20+8n/Fspi)*. An example of transferring speed at 8 MHz clock speed is given in the following table.
|
||||
For an interrupt transaction, the overall cost is *20+8n/Fspi[MHz]* [µs] for n bytes transferred in one transaction. Hence, the transferring speed is: *n/(20+8n/Fspi)*. An example of transferring speed at 8 MHz clock speed is given in the following table.
|
||||
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
| Frequency | Transaction Interval | Transaction Length | Total Time | Total Speed |
|
||||
| | | | | |
|
||||
| (MHz) | (us) | (bytes) | (us) | (KBps) |
|
||||
+===========+======================+====================+============+=============+
|
||||
| 8 | 25 | 1 | 26 | 38.5 |
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
| 8 | 25 | 8 | 33 | 242.4 |
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
| 8 | 25 | 16 | 41 | 490.2 |
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
| 8 | 25 | 64 | 89 | 719.1 |
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
| 8 | 25 | 128 | 153 | 836.6 |
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
.. list-table::
|
||||
:widths: 30 45 40 30 30
|
||||
:header-rows: 1
|
||||
|
||||
When a transaction length is short, the cost of transaction interval is high. If possible, try to squash several short transactions into one transaction to achieve a higher transfer speed.
|
||||
* - Frequency (MHz)
|
||||
- Transaction Interval (µs)
|
||||
- Transaction Length (bytes)
|
||||
- Total Time (µs)
|
||||
- Total Speed (KBps)
|
||||
* - 8
|
||||
- 25
|
||||
- 1
|
||||
- 26
|
||||
- 38.5
|
||||
* - 8
|
||||
- 25
|
||||
- 8
|
||||
- 33
|
||||
- 242.4
|
||||
* - 8
|
||||
- 25
|
||||
- 16
|
||||
- 41
|
||||
- 490.2
|
||||
* - 8
|
||||
- 25
|
||||
- 64
|
||||
- 89
|
||||
- 719.1
|
||||
* - 8
|
||||
- 25
|
||||
- 128
|
||||
- 153
|
||||
- 836.6
|
||||
|
||||
Please note that the ISR is disabled during flash operation by default. To keep sending transactions during flash operations, enable :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` and set :c:macro:`ESP_INTR_FLAG_IRAM` in the member :cpp:member:`spi_bus_config_t::intr_flags`. In this case, all the transactions queued before starting flash operations will be handled by the ISR in parallel. Also note that the callback of each Device and their callee functions should be in IRAM, or your callback will crash due to cache miss. For more details, see :ref:`iram-safe-interrupt-handlers`.
|
||||
When a transaction length is short, the cost of the transaction interval is high. If possible, try to squash several short transactions into one transaction to achieve a higher transfer speed.
|
||||
|
||||
Please note that the ISR is disabled during flash operation by default. To keep sending transactions during flash operations, enable :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` and set :c:macro:`ESP_INTR_FLAG_IRAM` in the member :cpp:member:`spi_bus_config_t::intr_flags`. In this case, all the transactions queued before starting flash operations will be handled by the ISR in parallel. Also note that the callback of each Device and their callee functions should be in IRAM, or your callback will crash due to cache missing. For more details, see :ref:`iram-safe-interrupt-handlers`.
|
||||
|
||||
|
||||
.. only:: esp32
|
||||
|
@ -593,25 +691,36 @@ Please note that the ISR is disabled during flash operation by default. To keep
|
|||
|
||||
.. _dummy_bit_workaround:
|
||||
|
||||
**Dummy bit workaround**: Dummy clocks, during which the Host does not read data, can be inserted before the read phase begins. The Device still sees the dummy clocks and sends out data, but the Host does not read until the read phase comes. This compensates for the lack of the MISO setup time required by the Host and allows the Host to do reading at a higher frequency.
|
||||
**Dummy bit workaround**: Dummy clocks, during which the Host does not read data, can be inserted before the Read phase begins. The Device still sees the dummy clocks and sends out data, but the Host does not read until the Read phase comes. This compensates for the lack of the MISO setup time required by the Host and allows the Host to do reading at a higher frequency.
|
||||
|
||||
In the ideal case, if the Device is so fast that the input delay is shorter than an APB clock cycle - 12.5 ns - the maximum frequency at which the Host can read (or read and write) in different conditions is as follows:
|
||||
|
||||
+-------------+-------------+------------+-----------------------------+
|
||||
| Frequency Limit (MHz) | Dummy Bits | Comments |
|
||||
+-------------+-------------+ Used + +
|
||||
| GPIO matrix | IO_MUX pins | By Driver | |
|
||||
+=============+=============+============+=============================+
|
||||
| 26.6 | 80 | No | |
|
||||
+-------------+-------------+------------+-----------------------------+
|
||||
| 40 | -- | Yes | Half-duplex, no DMA allowed |
|
||||
+-------------+-------------+------------+-----------------------------+
|
||||
.. list-table::
|
||||
:widths: 30 45 40 30
|
||||
:header-rows: 1
|
||||
|
||||
If the Host only writes data, the *dummy bit workaround* and the frequency check can be disabled by setting the bit `SPI_DEVICE_NO_DUMMY` in the member :cpp:member:`spi_device_interface_config_t::flags`. When disabled, the output frequency can be 80MHz, even if the GPIO matrix is used.
|
||||
* - Frequency Limit (MHz)
|
||||
- Frequency Limit (MHz)
|
||||
- Dummy Bits Used by Driver
|
||||
- Comments
|
||||
* - GPIO Matrix
|
||||
- IO_MUX Pins
|
||||
-
|
||||
-
|
||||
* - 26.6
|
||||
- 80
|
||||
- No
|
||||
-
|
||||
* - 40
|
||||
- --
|
||||
- Yes
|
||||
- Half-duplex, no DMA allowed
|
||||
|
||||
If the Host only writes data, the *dummy bit workaround* and the frequency check can be disabled by setting the bit ``SPI_DEVICE_NO_DUMMY`` in the member :cpp:member:`spi_device_interface_config_t::flags`. When disabled, the output frequency can be 80 MHz, even if the GPIO matrix is used.
|
||||
|
||||
:cpp:member:`spi_device_interface_config_t::flags`
|
||||
|
||||
The SPI master driver still works even if the :cpp:member:`spi_device_interface_config_t::input_delay_ns` in the structure :cpp:type:`spi_device_interface_config_t` is set to 0. However, setting an accurate value helps to:
|
||||
The SPI Master driver still works even if the :cpp:member:`spi_device_interface_config_t::input_delay_ns` in the structure :cpp:type:`spi_device_interface_config_t` is set to 0. However, setting an accurate value helps to:
|
||||
|
||||
- Calculate the frequency limit for full-duplex transactions
|
||||
- Compensate the timing correctly with dummy bits for half-duplex transactions
|
||||
|
@ -620,19 +729,22 @@ Please note that the ISR is disabled during flash operation by default. To keep
|
|||
|
||||
Please note that the actual PCB layout design and excessive loads may increase the input delay. It means that non-optimal wiring and/or a load capacitor on the bus will most likely lead to input delay values exceeding the values given in the Device specification or measured while the bus is floating.
|
||||
|
||||
Some typical delay values are shown in the following table. (These data are retrieved when the slave device is on a different physical chip)
|
||||
Some typical delay values are shown in the following table. These data are retrieved when the slave Device is on a different physical chip.
|
||||
|
||||
+----------------------------------------+------------------+
|
||||
| Device | Input delay (ns) |
|
||||
+========================================+==================+
|
||||
| Ideal Device | 0 |
|
||||
+----------------------------------------+------------------+
|
||||
| ESP32 slave using IO_MUX* | 50 |
|
||||
+----------------------------------------+------------------+
|
||||
| ESP32 slave using GPIO_MUX* | 75 |
|
||||
+----------------------------------------+------------------+
|
||||
.. list-table::
|
||||
:widths: 40 20
|
||||
:header-rows: 1
|
||||
|
||||
The MISO path delay (valid time) consists of a slave's *input delay* plus master's *GPIO matrix delay*. This delay determines the frequency limit above which full-duplex transfers will not work as well as the dummy bits used in the half-duplex transactions. The frequency limit is:
|
||||
* - Device
|
||||
- Input Delay (ns)
|
||||
* - Ideal Device
|
||||
- 0
|
||||
* - ESP32 slave using IO_MUX
|
||||
- 50
|
||||
* - ESP32 slave using GPIO_MATRIX
|
||||
- 75
|
||||
|
||||
The MISO path delay (valid time) consists of a slave's *input delay* plus the master's *GPIO matrix delay*. The delay determines the above frequency limit for full-duplex transfers. Once exceeding, full-duplex transfers will not work as well as the half-duplex transactions that use dummy bits. The frequency limit is:
|
||||
|
||||
*Freq limit [MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
|
||||
|
||||
|
@ -642,21 +754,43 @@ Please note that the ISR is disabled during flash operation by default. To keep
|
|||
|
||||
Corresponding frequency limits for different Devices with different *input delay* times are shown in the table below.
|
||||
|
||||
+--------+------------------+----------------------+-------------------+
|
||||
| Master | Input delay (ns) | MISO path delay (ns) | Freq. limit (MHz) |
|
||||
+========+==================+======================+===================+
|
||||
| IO_MUX | 0 | 0 | 80 |
|
||||
+ (0ns) +------------------+----------------------+-------------------+
|
||||
| | 50 | 50 | 16 |
|
||||
+ +------------------+----------------------+-------------------+
|
||||
| | 75 | 75 | 11.43 |
|
||||
+--------+------------------+----------------------+-------------------+
|
||||
| GPIO | 0 | 25 | 26.67 |
|
||||
+ (25ns) +------------------+----------------------+-------------------+
|
||||
| | 50 | 75 | 11.43 |
|
||||
+ +------------------+----------------------+-------------------+
|
||||
| | 75 | 100 | 8.89 |
|
||||
+--------+------------------+----------------------+-------------------+
|
||||
When the master is IO_MUX (0 ns):
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 40 40
|
||||
:header-rows: 1
|
||||
|
||||
* - Input Delay (ns)
|
||||
- MISO Path Delay (ns)
|
||||
- Freq. Limit (MHz)
|
||||
* - 0
|
||||
- 0
|
||||
- 80
|
||||
* - 50
|
||||
- 50
|
||||
- 16
|
||||
* - 75
|
||||
- 75
|
||||
- 11.43
|
||||
|
||||
When the master is GPIO_MATRIX (25 ns):
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 40 40
|
||||
:header-rows: 1
|
||||
|
||||
* - Input Delay (ns)
|
||||
- MISO Path Delay (ns)
|
||||
- Freq. Limit (MHz)
|
||||
* - 0
|
||||
- 25
|
||||
- 26.67
|
||||
* - 50
|
||||
- 75
|
||||
- 11.43
|
||||
* - 75
|
||||
- 100
|
||||
- 8.89
|
||||
|
||||
|
||||
.. only:: esp32
|
||||
|
@ -666,7 +800,7 @@ Please note that the ISR is disabled during flash operation by default. To keep
|
|||
Known Issues
|
||||
------------
|
||||
|
||||
1. Half-duplex transactions are not compatible with DMA when both writing and reading phases are used.
|
||||
1. Half-duplex transactions are not compatible with DMA when both the Write and Read phases are used.
|
||||
|
||||
If such transactions are required, you have to use one of the alternative solutions:
|
||||
|
||||
|
@ -675,19 +809,19 @@ Please note that the ISR is disabled during flash operation by default. To keep
|
|||
``ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0);``
|
||||
|
||||
This can prohibit you from transmitting and receiving data longer than 64 bytes.
|
||||
3. Try using the command and address fields to replace the write phase.
|
||||
3. Try using the command and address fields to replace the Write phase.
|
||||
|
||||
2. Full-duplex transactions are not compatible with the *dummy bit workaround*, hence the frequency is limited. See :ref:`dummy bit speed-up workaround <dummy_bit_workaround>`.
|
||||
|
||||
3. ``dummy_bits`` in :cpp:type:`spi_device_interface_config_t` and :cpp:type:`spi_transaction_ext_t` are not available when SPI read and write phases are both enabled (regardless of full duplex or half duplex mode).
|
||||
3. ``dummy_bits`` in :cpp:type:`spi_device_interface_config_t` and :cpp:type:`spi_transaction_ext_t` are not available when SPI Read and Write phases are both enabled (regardless of full duplex or half duplex mode).
|
||||
|
||||
4. ``cs_ena_pretrans`` is not compatible with the command and address phases of full-duplex transactions.
|
||||
4. ``cs_ena_pretrans`` is not compatible with the Command and Address phases of full-duplex transactions.
|
||||
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
The code example for using the SPI master half duplex mode to read/write a AT93C46D EEPROM (8-bit mode) can be found in the :example:`peripherals/spi_master/hd_eeprom` directory of ESP-IDF examples.
|
||||
The code example for using the SPI master half duplex mode to read/write an AT93C46D EEPROM (8-bit mode) can be found in the :example:`peripherals/spi_master/hd_eeprom` directory of ESP-IDF examples.
|
||||
|
||||
|
||||
API Reference - SPI Common
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
.. include:: ../../../en/api-reference/network/esp_netif.rst
|
||||
|
||||
.. include:: ../../../en/api-reference/network/esp_netif.rst
|
|
@ -1 +1,837 @@
|
|||
.. include:: ../../../en/api-reference/peripherals/spi_master.rst
|
||||
SPI 主机驱动程序
|
||||
===================
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
SPI 主机驱动程序是用于控制 {IDF_TARGET_NAME} 的 SPI 外设作为 SPI 主机的软件程序。
|
||||
|
||||
|
||||
{IDF_TARGET_NAME} 的 SPI 外设概述
|
||||
-------------------------------------------
|
||||
|
||||
{IDF_TARGET_SPI2_CS_NUM:default="6", esp32="3"}
|
||||
{IDF_TARGET_SPI3_CS_NUM:default="3"}
|
||||
|
||||
{IDF_TARGET_NAME} 集成了 {IDF_TARGET_SOC_SPI_PERIPH_NUM} 个 SPI 外设。
|
||||
|
||||
- SPI0 与 SPI1 用于内部访问 {IDF_TARGET_NAME} 所连接的 flash。两个控制器共享同一 SPI 总线信号,并由仲裁器裁定控制器访问权。
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
在 SPI1 总线上使用 SPI 主机驱动程序存在诸多限制,请参阅 :ref:`spi_master_on_spi1_bus`。
|
||||
|
||||
.. only:: not esp32
|
||||
|
||||
当前,SPI 主机驱动程序不支持 SPI1 总线。
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
- SPI2 和 SPI3 为通用 SPI 控制器,也被称作 HSPI 和 VSPI,使用权对用户开放。SPI2 和 SPI3 具有独立的总线信号,各自的信号线名称相同。每条总线具有三条 CS 线,可驱动多达相同数量的 SPI 从机设备。
|
||||
|
||||
.. only:: esp32s2 or esp32s3
|
||||
|
||||
- SPI2 和 SPI3 为通用 SPI 控制器,使用权对用户开放。SPI2 和 SPI3 具有独立的信号总线,名称分别相同。SPI2 具有 {IDF_TARGET_SPI2_CS_NUM} 条 CS 线,SPI3 具有 {IDF_TARGET_SPI3_CS_NUM} 条 CS 线。每条 CS 线可用于驱动一个 SPI 从机设备。
|
||||
|
||||
.. only:: esp32c3 or esp32c2 or esp32c6 or esp32h2
|
||||
|
||||
- SPI2 为通用 SPI 控制器,具有独立的信号总线且名称相同。SPI2 具有 {IDF_TARGET_SPI2_CS_NUM} 条 CS 线,可驱动多达 {IDF_TARGET_SPI2_CS_NUM} 个 SPI 从机设备.
|
||||
|
||||
术语
|
||||
--------
|
||||
|
||||
下表为 SPI 主机驱动的相关术语。
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - 术语
|
||||
- 定义
|
||||
* - 主机 (Host)
|
||||
- {IDF_TARGET_NAME} 内置的 SPI 控制器外设。用作 SPI 主机,在总线上发起 SPI 传输。
|
||||
* - 设备 (Device)
|
||||
- SPI 从机设备。一条 SPI 总线与一或多个设备连接。每个设备共享 MOSI、MISO 和 SCLK 信号,但只有当主机向设备的专属 CS 线发出信号时,设备才会在总线上处于激活状态。
|
||||
* - 总线 (Bus)
|
||||
- 信号总线,由连接到同一主机的所有设备共用。一般来说,每条总线包括以下线:MISO、MOSI、SCLK、一条或多条 CS 线,以及可选的 QUADWP 和 QUADHD。因此,除每个设备都有单独的 CS 线外,所有设备都连接在相同的线下。多个设备也可以菊花链的方式共享一条 CS 线。
|
||||
* - MOSI
|
||||
- 主机输出,从机输入,也写作 D。数据从主机发送至设备。在 Octal/OPI 模式下也表示为 data0 信号。
|
||||
* - MISO
|
||||
- 主机输入,从机输出,也写作 Q。数据从设备发送至主机。在 Octal/OPI 模式下也表示为 data1 信号。
|
||||
* - SCLK
|
||||
- 串行时钟。由主机产生的振荡信号,使数据位的传输保持同步。
|
||||
* - CS
|
||||
- 片选。允许主机选择连接到总线上的单个设备,以便发送或接收数据。
|
||||
* - QUADWP
|
||||
- 写保护信号。只用于 4 位 (qio/qout) 传输。在 Octal/OPI 模式下也表示为 data2 信号。
|
||||
* - QUADHD
|
||||
- 保持信号。只用于 4 位 (qio/qout) 传输。在 Octal/OPI 模式下也表示为 data3 信号。
|
||||
* - DATA4
|
||||
- 在 Octal/OPI 模式下表示为 data4 信号。
|
||||
* - DATA5
|
||||
- 在 Octal/OPI 模式下表示为 data5 信号。
|
||||
* - DATA6
|
||||
- 在 Octal/OPI 模式下表示为 data6 信号。
|
||||
* - DATA7
|
||||
- 在 Octal/OPI 模式下表示为 data7 信号。
|
||||
* - 断言 (Assertion)
|
||||
- 指激活一条线路的操作。
|
||||
* - 去断言 (De-assertion)
|
||||
- 指将线路恢复到非活动状态(回到空闲状态)的操作。
|
||||
* - 传输事务 (Transaction)
|
||||
- 即主机断言设备的 CS 线,向设备发送数据/从设备读取数据,接着去断言 CS 线的过程。传输事务为原子操作,不可打断。
|
||||
* - 发射沿 (Launch Edge)
|
||||
- 源寄存器将信号 *发射* 到线路上的时钟边沿。
|
||||
* - 锁存沿 (Latch Edge)
|
||||
- 目的寄存器 *锁存* 信号的时钟边沿。
|
||||
|
||||
|
||||
主机驱动特性
|
||||
---------------
|
||||
|
||||
SPI 主机驱动程序负责管理主机与设备间的通信,具有以下特性:
|
||||
|
||||
- 支持多线程环境使用
|
||||
- 读写数据过程中 DMA 透明传输
|
||||
- 同一信号总线上不同设备的数据可自动时分复用,请参阅 :ref:`spi_bus_lock`。
|
||||
|
||||
.. warning::
|
||||
|
||||
SPI 主机驱动允许总线上连接多个设备(共享单个 {IDF_TARGET_NAME} SPI 外设)。每个设备仅由一个任务访问时,驱动程序线程安全。反之,若多个任务尝试访问同一 SPI 设备,则驱动程序 **非线程安全**。此时,建议执行以下任一操作:
|
||||
|
||||
- 重构应用程序,确保每个 SPI 外设在同一时间仅由一个任务访问。使用 :cpp:member:`spi_bus_config_t::isr_cpu_id` 将 SPI ISR 注册到与 SPI 外设相关任务相同的内核,以确保线程安全。
|
||||
- 使用 :c:macro:`xSemaphoreCreateMutex` 为共享设备添加互斥锁。
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
SPI 特性 <spi_features>
|
||||
|
||||
SPI 传输事务
|
||||
--------------------
|
||||
|
||||
SPI 总线传输事务由五个阶段构成,详见下表(任意阶段均可跳过)。
|
||||
|
||||
{IDF_TARGET_ADDR_LEN:default="32", esp32="64"}
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - 阶段名称
|
||||
- 描述
|
||||
* - 命令阶段 (Command)
|
||||
- 在此阶段,主机向总线发送命令字段,长度为 0-16 位。
|
||||
* - 地址阶段 (Address)
|
||||
- 在此阶段,主机向总线发送地址字段,长度为 0-{IDF_TARGET_ADDR_LEN} 位。
|
||||
* - Dummy 阶段
|
||||
- 此阶段可自行配置,用于适配时序要求。
|
||||
* - 写入阶段 (Write)
|
||||
- 此阶段主机向设备传输数据,这些数据在紧随命令阶段(可选)和地址阶段(可选)之后。从电平的角度来看,数据与命令没有区别。
|
||||
* - 读取阶段 (Read)
|
||||
- 此阶段主机读取设备数据。
|
||||
|
||||
.. todo::
|
||||
|
||||
Add a package diagram.
|
||||
|
||||
|
||||
传输事务属性由总线配置结构体 :cpp:type:`spi_bus_config_t`、设备配置结构体 :cpp:type:`spi_device_interface_config_t` 和传输事务配置结构体 :cpp:type:`spi_transaction_t` 共同决定。
|
||||
|
||||
一个 SPI 主机可以发送全双工传输事务,此时读取和写入阶段同步进行。传输事务总长度取决于以下结构体成员长度总和:
|
||||
|
||||
- :cpp:member:`spi_device_interface_config_t::command_bits`
|
||||
- :cpp:member:`spi_device_interface_config_t::address_bits`
|
||||
- :cpp:member:`spi_transaction_t::length`
|
||||
|
||||
而 :cpp:member:`spi_transaction_t::rxlength` 则决定了接收到的数据包长度。
|
||||
|
||||
在半双工传输事务中,读取和写入阶段独立进行(一次一个方向)。写入和读取阶段的长度由 :cpp:member:`spi_transaction_t::length` 和 :cpp:member:`spi_transaction_t::rxlength` 分别决定。
|
||||
|
||||
并非每个 SPI 设备都要求命令和/或地址,因此命令阶段和地址阶段为可选项。这反映在设备的配置中:如果 :cpp:member:`spi_device_interface_config_t::command_bits` 和/或 :cpp:member:`spi_device_interface_config_t::address_bits` 被设置为零,则不会唤起命令或地址阶段。
|
||||
|
||||
并非每个传输事务都需要写入和读取数据,因此读取和写入阶段也是可选项。如果将 :cpp:member:`spi_transaction_t::rx_buffer` 设置为 NULL,且未设置 :c:macro:`SPI_TRANS_USE_RXDATA`,读取阶段将被跳过。如果将 :cpp:member:`spi_transaction_t::tx_buffer` 设置为 NULL,且未设置 :c:macro:`SPI_TRANS_USE_TXDATA`,写入阶段将被跳过。
|
||||
|
||||
主机驱动程序支持两种类型的传输事务:中断传输事务和轮询传输事务。用户可以选择在不同设备上使用不同的传输事务类型。若设备需要同时使用两种传输事务类型,请参阅 :ref:`mixed_transactions`。
|
||||
|
||||
|
||||
.. _interrupt_transactions:
|
||||
|
||||
中断传输事务
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
中断传输事务将阻塞传输事务程序,直至传输事务完成,以使 CPU 运行其他任务程序。
|
||||
|
||||
应用任务中可以将多个传输事务加入到队列中,驱动程序将在中断服务程序 (ISR) 中自动逐一发送队列中的数据。在所有传输事务完成以前,任务可切换到其他程序中。
|
||||
|
||||
|
||||
.. _polling_transactions:
|
||||
|
||||
轮询传输事务
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
轮询传输事务不依赖于中断,程序将不断轮询 SPI 主机的状态位,直到传输事务完成。
|
||||
|
||||
所有执行中断传输事务的任务都可能被队列阻塞。在此情况下,用户需要等待 ISR 和传输事务传输完成。轮询传输事务节省了原本用于队列处理和上下文切换的时间,减少了传输事务持续时间。传输事务类型的缺点是在这些事务进行期间,CPU 将被占用而处于忙碌状态。
|
||||
|
||||
传输事务完成后,:cpp:func:`spi_device_polling_end` 程序需要至少 1 µs 的时间来解除阻塞其他任务。此处强烈建议调用函数 :cpp:func:`spi_device_acquire_bus` 和 :cpp:func:`spi_device_release_bus` 来打包一系列轮询传输事务以避免开销。详情请参阅 :ref:`bus_acquiring`。
|
||||
|
||||
.. _transaction-line-mode:
|
||||
|
||||
传输线模式配置
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
{IDF_TARGET_NAME} 支持的线路模式如下。要使用这些模式,请在结构体 :cpp:type:`spi_transaction_t` 中设置 ``flags``,如 ``传输事务标志信号`` 一栏所示。要检查相应的 IO 管脚是否被设置,请在 :cpp:type:`spi_bus_config_t` 中设置 ``flags``,如 `总线 IO 设置标志信号` 一栏所示。
|
||||
|
||||
|
||||
.. only:: not SOC_SPI_SUPPORT_OCT
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 30 30 30 50 50
|
||||
:header-rows: 1
|
||||
|
||||
* - 模式
|
||||
- 命令位宽
|
||||
- 地址位宽
|
||||
- 数据位宽
|
||||
- 传输事务标志信号
|
||||
- 总线 IO 设置标志信号
|
||||
* - 普通 SPI 模式
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 0
|
||||
- 0
|
||||
* - 双线输出模式
|
||||
- 1
|
||||
- 1
|
||||
- 2
|
||||
- {SPI_TRANS_MODE_DIO}
|
||||
- {SPICOMMON_BUSFLAG_DUAL}
|
||||
* - 双线 I/O 模式
|
||||
- 1
|
||||
- 2
|
||||
- 2
|
||||
- * {SPI_TRANS_MODE_DIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
-
|
||||
* - 四线输出模式
|
||||
- 1
|
||||
- 1
|
||||
- 4
|
||||
- {SPI_TRANS_MODE_QIO}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
* - 四线 I/O 模式
|
||||
- 1
|
||||
- 4
|
||||
- 4
|
||||
- * {SPI_TRANS_MODE_QIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
|
||||
.. only:: SOC_SPI_SUPPORT_OCT
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 40 40 40 50 50
|
||||
:header-rows: 1
|
||||
|
||||
* - 模式
|
||||
- 命令位宽
|
||||
- 地址位宽
|
||||
- 数据位宽
|
||||
- 传输事务标志信号
|
||||
- 总线 IO 设置标志信号
|
||||
* - 普通 SPI 模式
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 0
|
||||
- 0
|
||||
* - 双线输出模式
|
||||
- 1
|
||||
- 1
|
||||
- 2
|
||||
- {SPI_TRANS_MODE_DIO}
|
||||
- {SPICOMMON_BUSFLAG_DUAL}
|
||||
* - 双线 I/O 模式
|
||||
- 1
|
||||
- 2
|
||||
- 2
|
||||
- * {SPI_TRANS_MODE_DIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
-
|
||||
* - 四线输出模式
|
||||
- 1
|
||||
- 1
|
||||
- 4
|
||||
- {SPI_TRANS_MODE_QIO}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
* - 四线 I/O 模式
|
||||
- 1
|
||||
- 4
|
||||
- 4
|
||||
- * {SPI_TRANS_MODE_QIO}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
- {SPICOMMON_BUSFLAG_QUAD}
|
||||
* - 八线输出模式
|
||||
- 1
|
||||
- 1
|
||||
- 8
|
||||
- {SPI_TRANS_MODE_OCT}
|
||||
- {SPICOMMON_BUSFLAG_OCTAL}
|
||||
* - OPI 模式
|
||||
- 8
|
||||
- 8
|
||||
- 8
|
||||
- * {SPI_TRANS_MODE_OCT}
|
||||
* {SPI_TRANS_MULTILINE_ADDR}
|
||||
* {SPI_TRANS_MULTILINE_CMD}
|
||||
- {SPICOMMON_BUSFLAG_OCTAL}
|
||||
|
||||
命令阶段和地址阶段
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
在命令阶段和地址阶段,:cpp:member:`spi_transaction_t::cmd` 和 :cpp:member:`spi_transaction_t::addr` 将被发送到总线,该过程中无数据读取。命令阶段和地址阶段的默认长度通过调用 :cpp:func:`spi_bus_add_device` 在 :cpp:type:`spi_device_interface_config_t` 中设置。如果 :cpp:member:`spi_transaction_t::flags` 中的标志信号 :c:macro:`SPI_TRANS_VARIABLE_CMD` 和 :c:macro:`SPI_TRANS_VARIABLE_ADDR` 未设置,则驱动程序将在设备初始化期间自动将这些阶段的长度设置为默认值。
|
||||
|
||||
如需更改命令阶段和地址阶段的长度,可通过以下步骤实现:声明结构体 :cpp:type:`spi_transaction_ext_t`,在 :cpp:member:`spi_transaction_ext_t::base` 中设置标志信号 :c:macro:`SPI_TRANS_VARIABLE_CMD` 和/或 :c:macro:`SPI_TRANS_VARIABLE_ADDR`,随后按正常步骤完成余下配置。这样一来,各阶段的长度将等于结构体 :cpp:type:`spi_transaction_ext_t` 中设置的 :cpp:member:`spi_transaction_ext_t::command_bits` 和 :cpp:member:`spi_transaction_ext_t::address_bits` 长度。
|
||||
|
||||
如果需要命令阶段和地址阶段的线数与数据阶段保持一致,则应在结构体 :cpp:type:`spi_transaction_t` 中将 ``SPI_TRANS_MULTILINE_CMD`` 和/或 ``SPI_TRANS_MULTILINE_ADDR`` 设置进该结构体的 ``flags`` 成员变量。请参阅 :ref:`transaction-line-mode`。
|
||||
|
||||
|
||||
写入和读取阶段
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
一般而言,需要传输到设备或由设备读取的数据将由 :cpp:member:`spi_transaction_t::rx_buffer` 和 :cpp:member:`spi_transaction_t::tx_buffer` 指向的内存块中读取或写入。如果传输时启用了 DMA,则缓冲区应:
|
||||
|
||||
1. 申请支持 DMA 的内存。具体操作请参阅 :ref:`支持 DMA 的内存 <dma-capable-memory>`。
|
||||
2. 32 位对齐(从 32 位边界开始,长度为 4 字节的倍数)。
|
||||
|
||||
若未满足以上要求,传输事务效率将受到临时缓冲区分配和复制的影响。
|
||||
|
||||
如果使用多条数据线传输,请在结构体 :cpp:type:`spi_device_interface_config_t` 中的 `flags` 设置 ``SPI_DEVICE_HALFDUPLEX`` 标志信号。结构体 :cpp:type:`spi_transaction_t` 中的 ``flags`` 应按照 :ref:`transaction-line-mode` 中的描述设置。
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. note::
|
||||
|
||||
当启用 DMA 时,不支持同时具有读取阶段和写入阶段的半双工传输事务。有关细节和解决方法,请参阅 :ref:`spi_known_issues`。
|
||||
|
||||
.. only:: esp32s3 or esp32c3 or esp32c2 or esp32c6 or esp32h2
|
||||
|
||||
.. note::
|
||||
|
||||
不支持同时具有读取和写入阶段的半双工传输事务。请使用全双工模式。
|
||||
|
||||
.. _bus_acquiring:
|
||||
|
||||
获取总线
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
若需连续发送专门的 SPI 传输事务以提高效率,可采用获取总线的方式。获取总线后,与其他设备间的传输事务(包括轮询传输事务或中断传输事务)将处于待处理状态,直到总线被释放。要获取和释放总线,请调用函数 :cpp:func:`spi_device_acquire_bus` 和 :cpp:func:`spi_device_release_bus`。
|
||||
|
||||
|
||||
使用驱动程序
|
||||
-----------------
|
||||
|
||||
.. todo::
|
||||
|
||||
Organize the Driver Usage into subsections that will reflect the general user experience of the users, e.g.,
|
||||
|
||||
Configuration
|
||||
|
||||
Add stuff about the configuration API here, the various options in configuration (e.g., configure for interrupt vs. polling), and optional configuration
|
||||
|
||||
Transactions
|
||||
|
||||
Describe how to execute a normal transaction (i.e., where data is larger than 32 bits). Describe how to configure between big and little-endian.
|
||||
|
||||
- Add a sub-sub section on how to optimize when transmitting less than 32 bits
|
||||
- Add a sub-sub section on how to transmit mixed transactions to the same device
|
||||
|
||||
|
||||
- 通过调用函数 :cpp:func:`spi_bus_initialize` 初始化 SPI 总线。确保在结构体 :cpp:type:`spi_bus_config_t` 中设置正确的 I/O 管脚。不需要的信号设置为 ``-1``。
|
||||
|
||||
- 通过调用函数 :cpp:func:`spi_bus_add_device` 注册连接到总线的设备。确保用参数 `dev_config` 配置设备可能需要的任何时序要求。完成上述操作后可获得设备句柄,以便在向设备发送传输事务时使用。
|
||||
|
||||
- 要与设备交互,请在一个或多个 :cpp:type:`spi_transaction_t` 结构体中填充所需的传输事务参数,随后使用轮询传输事务或中断传输事务发送这些结构体:
|
||||
|
||||
- :ref:`中断传输事务 <interrupt_transactions>`
|
||||
调用函数 :cpp:func:`spi_device_queue_trans` 将传输事务添加到队列中,随后使用函数 :cpp:func:`spi_device_get_trans_result` 查询结果,或将所有请求输入 :cpp:func:`spi_device_transmit` 实现同步处理。
|
||||
|
||||
- :ref:`轮询传输事务 <polling_transactions>`
|
||||
调用函数 :cpp:func:`spi_device_polling_transmit` 发送轮询传输事务。若有插入内容的需要,也可使用 :cpp:func:`spi_device_polling_start` 和 :cpp:func:`spi_device_polling_end` 发送传输事务。
|
||||
|
||||
- (可选)若要向设备发送背对背传输事务,请在发送传输事务前调用函数 :cpp:func:`spi_device_acquire_bus`,并在发送传输事务后调用函数 :cpp:func:`spi_device_release_bus`。
|
||||
|
||||
- (可选)要从总线上移除特定设备,请以设备句柄为参数调用函数 :cpp:func:`spi_bus_remove_device`。
|
||||
|
||||
- (可选)要复位(或重置)总线,请确保总线上未连接其他设备,并调用函数 :cpp:func:`spi_bus_free`。
|
||||
|
||||
SPI 主机驱动程序的示例代码存放在 ESP-IDF 示例项目的 :example:`peripherals/spi_master` 目录下。
|
||||
|
||||
|
||||
传输数据小于 32 位的传输事务
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
当传输事务数据等于或小于 32 位时,为数据分配一个缓冲区将是次优的选择。实际上,数据可以直接存储于传输事务结构体中。对已传输的数据,可通过调用函数 :cpp:member:`spi_transaction_t::tx_data` 并在传输时设置 :c:macro:`SPI_TRANS_USE_TXDATA` 标志信号来实现。对已接收的数据,可通过调用函数 :cpp:member:`spi_transaction_t::rx_data` 并设置 :c:macro:`SPI_TRANS_USE_RXDATA` 来实现。在这两种情况下,请勿修改 :cpp:member:`spi_transaction_t::tx_buffer` 或 :cpp:member:`spi_transaction_t::rx_buffer`,因为它们与 :cpp:member:`spi_transaction_t::tx_data` 和 :cpp:member:`spi_transaction_t::rx_data` 的内存位置相同。
|
||||
|
||||
|
||||
非 ``uint8_t`` 的整数传输事务
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
SPI 主机逐字节地将数据读入和写入内存。默认情况下,数据优先以最高有效位 (MSB) 发送,极少数情况下会优先使用最低有效位 (LSB)。如果需要发送一个小于 8 位的值,这些位应以 MSB 优先的方式写入内存。
|
||||
|
||||
例如,如果需要发送 ``0b00010``,则应将其写成 ``uint8_t`` 变量,读取长度设置为 5 位。此时,设备仍然会收到 8 位数据,并另有 3 个“随机”位,所以读取过程必须准确。
|
||||
|
||||
此外,{IDF_TARGET_NAME} 属于小端芯片,即 ``uint16_t`` 和 ``uint32_t`` 变量的最低有效位存储在最小的地址。因此,如果 ``uint16_t`` 存储在内存中,则首先发送位 [7:0],其次是位 [15:8]。
|
||||
|
||||
在某些情况下,要传输的数据大小与 ``uint8_t`` 数组不同,可使用以下宏将数据转换为可由 SPI 驱动直接发送的格式:
|
||||
|
||||
- 需传输的数据,使用 :c:macro:`SPI_SWAP_DATA_TX`
|
||||
- 接收到的数据,使用 :c:macro:`SPI_SWAP_DATA_RX`
|
||||
|
||||
|
||||
.. _mixed_transactions:
|
||||
|
||||
向同一设备发送混合传输事务的注意事项
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
为避免代码过于复杂,请一次只向单个设备发送一种类型的传输事务(中断或轮询)。如有需要,可交替发送中断传输事务和轮询传输事务,详见下文。
|
||||
|
||||
所有的轮询传输事务和中断传输事务完成后,方可发送轮询传输事务。
|
||||
|
||||
由于未完成的轮询传输事务将阻塞其他传输事务,请务必在 :cpp:func:`spi_device_polling_start` 之后调用函数 :cpp:func:`spi_device_polling_end` 以允许其他事务或其他设备使用总线。如果在轮询过程中无需切换到其他任务,可调用函数 :cpp:func:`spi_device_polling_transmit` 启动传输事务,则该传输事务将自动结束。
|
||||
|
||||
ISR 会干扰飞行中的轮询传输事务,以适应中断传输事务。在调用 :cpp:func:`spi_device_polling_start` 前,需确保所有发送到 ISR 的中断传输事务已经完成。为此可持续调用 :cpp:func:`spi_device_get_trans_result`,直至全部传输事务返回。
|
||||
|
||||
为更好地控制函数的调用顺序,只在单个任务中向同一设备发送混合传输事务。
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. _spi_master_on_spi1_bus:
|
||||
|
||||
在 SPI1 总线上使用 SPI 主机驱动程序的注意事项
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. note::
|
||||
|
||||
因具备 :ref:`spi_bus_lock` 特性,SPI 主机驱动程序可在 SPI1 总线上运行,但该过程十分棘手,且需要许多特殊处理。这是一个适合高级开发者的功能。
|
||||
|
||||
要在 SPI1 总线上运行 SPI 主机驱动程序,需注意以下两个问题:
|
||||
|
||||
1. 当驱动在 SPI1 上运行时,代码和数据应在内部存储器中。
|
||||
|
||||
总线由设备、flash 中的数据(代码)缓存以及 PSRAM 共享。当其他驱动在 SPI1 总线上运行时,缓存应被禁用。此时,flash 上的数据(代码)以及 PSRAM 就不会在驱动获取 SPI 总线时被抓取。驱动可经由以下方式获取 SPI1 总线:
|
||||
|
||||
- 在 :cpp:func:`spi_device_acquire_bus` 和 :cpp:func:`spi_device_release_bus` 之间显式获取总线。
|
||||
- 在 :cpp:func:`spi_device_polling_start` 和 :cpp:func:`spi_device_polling_end` 之间隐式获取总线(或在 :cpp:func:`spi_device_polling_transmit` 中)。
|
||||
|
||||
上述时间内,所有的其他任务和大多数 ISR 将被禁用(见 :ref:`iram-safe-interrupt-handlers`)。当前任务所使用的应用程序代码和数据应放置于内存(DRAM 或 IRAM)中,或者已置于 ROM 中。访问外部存储器(flash 代码、flash 中的常量数据和 PSRAM 中的静态/堆积数据)将导致 ``缓存禁用情况下访问缓存内存区域`` 异常。关于 IRAM、DRAM、和 flash 缓存之间的区别,请参阅 :ref:`应用程序内存布局 <memory-layout>`。
|
||||
|
||||
要将函数置入 IRAM,请选择:
|
||||
|
||||
1. 将 ``IRAM_ATTR`` (包括 ``esp_attr.h``)添加到函数中,如:
|
||||
|
||||
IRAM_ATTR void foo(void) { }
|
||||
|
||||
函数内联时将跟随其调用者的段,且属性无法生效,使用 ``NOLINE_ATTR`` 可避免此类情况。注意,编译器可能会将部分代码转化为常量数据中的查找表,所以 ``noflash_text`` 并不安全。
|
||||
|
||||
2. 或使用 ``linker.lf`` 中的 ``noflash`` 位置,请参阅 :doc:`../../api-guides/linker-script-generation`。注意,编译器可能会将部分代码转化为常量数据中的查找表,所以 ``noflash_text`` 并不安全。
|
||||
|
||||
请注意,优化水平可能会影响编译器的内联行为,或将一些代码转化为常量数据中的查找表等。
|
||||
|
||||
要将数据置入 DRAM,请选择:
|
||||
|
||||
1. 在数据定义中加入 ``DRAM_ATTR`` (包括 ``esp_attr.h``),如:
|
||||
|
||||
DRAM_ATTR int g_foo = 3;
|
||||
|
||||
2. 或使用 ``linker.lf`` 中的 ``noflash`` 位置,请参阅 :doc:`../../api-guides/linker-script-generation`。
|
||||
|
||||
查看更多示例,请参阅 :example:`peripherals/spi_master/hd_eeprom`。
|
||||
|
||||
|
||||
GPIO 矩阵与 IO_MUX 管脚
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
ESP32 的多数外设信号都直接与之专用的 IO_MUX 管脚连接,但这些信号也可以通过 GPIO 交换矩阵传输到任何其他可用的管脚。只要有一个信号通过 GPIO 交换矩阵传输,那么所有的信号都将通由其传输。
|
||||
|
||||
GPIO 交换矩阵提高了信号传输的灵活性,但也存在以下缺陷:
|
||||
|
||||
- 加大了 MISO 信号的输入延迟,增加了 MISO 设置时序违反的可能性。如果需要高速运行 SPI,请使用专用的 IO_MUX 管脚。
|
||||
- 只能传输最大时钟频率为 40 MHz 的信号,而使用 IO_MUX 管脚时可以传输的最大时钟频率为 80 MHz。
|
||||
|
||||
.. note::
|
||||
|
||||
了解有关 MISO 输入延迟对最大时钟频率影响的更多信息,请参阅 :ref:`timing_considerations`。
|
||||
|
||||
SPI 总线的 IO_MUX 管脚如下表所示。
|
||||
|
||||
.. list-table::
|
||||
:widths: 40 20 20
|
||||
:header-rows: 1
|
||||
|
||||
* - 管脚名称
|
||||
- GPIO 编号 (SPI2)
|
||||
- GPIO 编号 (SPI3)
|
||||
* - CS0 [1]_
|
||||
- 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"}
|
||||
|
||||
芯片的大多数外围信号都与之专用的 IO_MUX 管脚连接,但这些信号也可以通过较不直接的 GPIO 矩阵路由到任何其他可用的管脚。只要有一个信号是通过 GPIO 矩阵路由的,那么所有的信号都将通过它路由。
|
||||
|
||||
当 SPI 主机被设置为 80 MHz 或更低的频率时,通过 GPIO 矩阵路由 SPI 管脚的行为将与通过 IOMUX 路由相同。
|
||||
|
||||
SPI 总线的 IO_MUX 管脚如下表所示。
|
||||
|
||||
.. list-table::
|
||||
:widths: 40 30
|
||||
:header-rows: 1
|
||||
|
||||
* - 管脚名称
|
||||
- GPIO 编号 (SPI2)
|
||||
* - CS0 [1]_
|
||||
- {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}
|
||||
|
||||
.. [1] 只有连接到总线的第一台设备可以使用 CS0 管脚。
|
||||
|
||||
.. _speed_considerations:
|
||||
|
||||
传输速度的影响因素
|
||||
--------------------------------
|
||||
|
||||
传输速度主要有以下三个限制因素
|
||||
|
||||
- 传输事务间隔时间
|
||||
- SPI 时钟频率
|
||||
- 缓存缺失的 SPI 函数,包括回调
|
||||
|
||||
影响大传输事务传输速度的主要参数是时钟频率。而多个小传输事务的传输速度主要由传输事务间隔时长决定。
|
||||
|
||||
|
||||
传输事务持续时间
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
{IDF_TARGET_TRANS_TIME_INTR_DMA:default="N/A", esp32="28", esp32s2="23", esp32c3="28", esp32s3="26", esp32c2="42", esp32c6="34", esp32h2="58"}
|
||||
{IDF_TARGET_TRANS_TIME_POLL_DMA:default="N/A", esp32="10", esp32s2="9", esp32c3="10", esp32s3="11", esp32c2="17", esp32c6="17", esp32h2="28"}
|
||||
{IDF_TARGET_TRANS_TIME_INTR_CPU:default="N/A", esp32="25", esp32s2="22", esp32c3="27", esp32s3="24", esp32c2="40", esp32c6="32", esp32h2="54"}
|
||||
{IDF_TARGET_TRANS_TIME_POLL_CPU:default="N/A", esp32="8", esp32s2="8", esp32c3="9", esp32s3="9", esp32c2="15", esp32c6="15", esp32h2="24"}
|
||||
|
||||
传输事务持续时间包括设置 SPI 外设寄存器,将数据复制到 FIFO 或设置 DMA 链接,以及 SPI 传输事务时间。
|
||||
|
||||
中断传输事务允许附加额外的开销,以适应 FreeRTOS 队列的成本以及任务与 ISR 切换所需的时间。
|
||||
|
||||
对于 **中断传输事务**,CPU 可以在传输事务进行过程中切换到其他任务,这能够节约 CPU 时间,会延长传输事务持续时间,请参阅 :ref:`interrupt_transactions`。对于 **轮询传输事务**,它不会阻塞任务,但允许在传输事务进行时轮询。详情请参阅 :ref:`polling_transactions`。
|
||||
|
||||
如果 DMA 被启用,每个传输事务设置链接列表需要约 2 µs。当主机传输数据时,它会自动从链接列表中读取数据。如果不启用 DMA,CPU 必须自己从 FIFO 中写入和读取每个字节。这一过程时长通常不到 2 µs,但写入和读取的传输事务长度都被限制在 64 字节。
|
||||
|
||||
单个字节数据的典型传输事务持续时间如下。
|
||||
|
||||
- 使用 DMA 的中断传输事务:{IDF_TARGET_TRANS_TIME_INTR_DMA} µs。
|
||||
- 使用 CPU 的中断传输事务:{IDF_TARGET_TRANS_TIME_INTR_CPU} µs。
|
||||
- 使用 DMA 的轮询传输事务:{IDF_TARGET_TRANS_TIME_POLL_DMA} µs。
|
||||
- 使用 CPU 的轮询传输事务:{IDF_TARGET_TRANS_TIME_POLL_CPU} µs。
|
||||
|
||||
请注意,以上数据测试时,:ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` 选项处于启用状态,SPI 传输事务相关的代码放置在 IRAM 中。若关闭此选项(例如为了节省 IRAM),可能影响传输事务持续时间。
|
||||
|
||||
SPI 时钟频率
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg::clock_source` 选择,可用的时钟源请参阅 :cpp:type:`spi_clock_source_t`。
|
||||
默认情况下,驱动程序将把 :cpp:member:`spi_device_handle_t::cfg::clock_source` 设置为 ``SPI_CLK_SRC_DEFAULT``。这往往代表 GPSPI 时钟源中的最高频率,在不同的芯片中这一数值会有所不同。
|
||||
|
||||
设备的实际时钟频率可能不完全等于所设置的数字,驱动会将其重新计算为与硬件兼容的最接近的数字,并且不超过时钟源的时钟频率。调用函数 :cpp:func:`spi_device_get_actual_freq` 以了解驱动计算的实际频率。
|
||||
|
||||
写入或读取阶段的理论最大传输速度可根据下表计算:
|
||||
|
||||
.. only:: not SOC_SPI_SUPPORT_OCT
|
||||
|
||||
.. list-table::
|
||||
:widths: 40 30
|
||||
:header-rows: 1
|
||||
|
||||
* - 写入/读取阶段的线宽
|
||||
- 速度 (Bps)
|
||||
* - 1-Line
|
||||
- *SPI 频率 / 8*
|
||||
* - 2-Line
|
||||
- *SPI 频率 / 4*
|
||||
* - 4-Line
|
||||
- *SPI 频率 / 2*
|
||||
|
||||
.. only:: SOC_SPI_SUPPORT_OCT
|
||||
|
||||
.. list-table::
|
||||
:widths: 40 30
|
||||
:header-rows: 1
|
||||
|
||||
* - 写入/读取阶段的线宽
|
||||
- 速度 (Bps)
|
||||
* - 1-Line
|
||||
- *SPI 频率 / 8*
|
||||
* - 2-Line
|
||||
- *SPI 频率 / 4*
|
||||
* - 4-Line
|
||||
- *SPI 频率 / 2*
|
||||
* - 8-Line
|
||||
- *SPI 频率*
|
||||
|
||||
其他阶段(命令阶段、地址阶段、Dummy 阶段)的传输速度计算与此类似。
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
时钟频率过高可能会限制部分功能的使用,请参阅 :ref:`timing_considerations`。
|
||||
|
||||
缓存缺失
|
||||
^^^^^^^^^^
|
||||
|
||||
默认配置只将 ISR 置于 IRAM 中。其他 SPI 相关功能,包括驱动本身和回调都可能发生缓存缺失,需等待代码从 flash 中读取。为避免缓存缺失,可参考 :ref:`CONFIG_SPI_MASTER_IN_IRAM`,将整个 SPI 驱动置入 IRAM,并将整个回调及其 callee 函数一起置入 IRAM。
|
||||
|
||||
.. note::
|
||||
|
||||
SPI 驱动是基于 FreeRTOS 的 API 实现的,在使用 :ref:`CONFIG_SPI_MASTER_IN_IRAM` 时,不得启用 :ref:`CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH`。
|
||||
|
||||
单个中断传输事务传输 n 字节的总成本为 *20+8n/Fspi[MHz]* [µs],故传输速度为 *n/(20+8n/Fspi)*。8 MHz 时钟速度的传输速度见下表。
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 45 40 30 30
|
||||
:header-rows: 1
|
||||
|
||||
* - 频率 (MHz)
|
||||
- 传输事务间隔 (µs)
|
||||
- 传输事务长度 (bytes)
|
||||
- 传输时长 (µs)
|
||||
- 传输速度 (KBps)
|
||||
* - 8
|
||||
- 25
|
||||
- 1
|
||||
- 26
|
||||
- 38.5
|
||||
* - 8
|
||||
- 25
|
||||
- 8
|
||||
- 33
|
||||
- 242.4
|
||||
* - 8
|
||||
- 25
|
||||
- 16
|
||||
- 41
|
||||
- 490.2
|
||||
* - 8
|
||||
- 25
|
||||
- 64
|
||||
- 89
|
||||
- 719.1
|
||||
* - 8
|
||||
- 25
|
||||
- 128
|
||||
- 153
|
||||
- 836.6
|
||||
|
||||
传输事务长度较短时将提高传输事务间隔成本,因此应尽可能将几个短传输事务压缩成一个传输事务,以提升传输速度。
|
||||
|
||||
注意,ISR 在 flash 操作期间默认处于禁用状态。要在 flash 操作期间继续发送传输事务,请启用 :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM`,并在 :cpp:member:`spi_bus_config_t::intr_flags` 中设置 :c:macro:`ESP_INTR_FLAG_IRAM`。此时,flash 操作前列队的传输事务将由 ISR 并行处理。此外,每个设备的回调和它们的 callee 函数都应该在 IRAM 中,避免回调因缓存丢失而崩溃。详情请参阅 :ref:`iram-safe-interrupt-handlers`。
|
||||
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. _timing_considerations:
|
||||
|
||||
时序影响因素
|
||||
---------------------
|
||||
|
||||
如图所示,SCLK 发射沿之后、信号被内部寄存器锁存之前,MISO 线存在延迟。因此,MISO 管脚的设置时间是 SPI 时钟速度的限制因素。当延迟过长时,设置松弛度 < 0,违反了设置时序要求,读取可能有误。
|
||||
|
||||
.. image:: /../_static/spi_miso.png
|
||||
:scale: 40 %
|
||||
:align: center
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/spi/miso_timing_waveform.json
|
||||
|
||||
最大有效频率取决于:
|
||||
|
||||
- :cpp:member:`spi_device_interface_config_t::input_delay_ns` - SCLK 上的一个时钟周期开始后 MISO 总线上的最大数据有效时间
|
||||
- 是否使用了 IO_MUX 管脚或 GPIO 矩阵
|
||||
|
||||
使用 GPIO 矩阵时,最大有效频率降至现有 *输入延迟* 的 33~77%。使用 IO_MUX 管脚或 *dummy 位* 可以保留较高的频率,调用函数 :cpp:func:`spi_get_freq_limit` 能够获取主机的最大读取频率。
|
||||
|
||||
.. _dummy_bit_workaround:
|
||||
|
||||
**Dummy 位**:在读取阶段开始之前,可以插入 Dummy 时钟,在此期间,主机不读取数据。设备能看到 Dummy 时钟并发出数据,但主机在读取阶段之前不会读取。这就弥补了主机 MISO 设置时间不足的问题,并使主机达到更高的读取频率。
|
||||
|
||||
理想状态下,如果设备的速度足以使输入延迟控制在一个 APB 时钟周期 (12.5 ns) 以内,则在不同的条件下,主机读取(或读取和写入)的最大频率如下表所示:
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 30 40 50
|
||||
:header-rows: 1
|
||||
|
||||
* - 频率限制 (MHz)
|
||||
- 频率限制 (MHz)
|
||||
- 驱动是否使用 Dummy 位
|
||||
- 备注
|
||||
* - GPIO 矩阵
|
||||
- IO_MUX 管脚
|
||||
-
|
||||
-
|
||||
* - 26.6
|
||||
- 80
|
||||
- 否
|
||||
-
|
||||
* - 40
|
||||
- --
|
||||
- 是
|
||||
- 半双工,禁用 DMA
|
||||
|
||||
如果主机仅写入数据,可以通过设置 :cpp:member:`spi_device_interface_config_t::flags` 中的 ``SPI_DEVICE_NO_DUMMY`` 位来禁用 *Dummy 位* 和频率检查机制。禁用 *Dummy 位* 和频率检查机制后,即便在使用 GPIO 矩阵的情况下,输出频率也可达 80 MHz。
|
||||
|
||||
:cpp:member:`spi_device_interface_config_t::flags`
|
||||
|
||||
即使 :cpp:type:`spi_device_interface_config_t` 结构体中的 :cpp:member:`spi_device_interface_config_t::input_delay_ns` 设置为 0,SPI 主机驱动程序仍会运作,但设置精确值有助于:
|
||||
|
||||
- 计算全双工传输事务的频率限制
|
||||
- 用半双工传输事务的 Dummy 位正确补偿时序
|
||||
|
||||
可根据设备规格说明中 AC 特性章节的数据估计 SPI 时钟发射沿后的最大数据有效时间,或者使用示波器或逻辑分析仪测量该时间。
|
||||
|
||||
请注意,实际 PCB 布局设计和过度负载可能会增加输入延迟。这意味着非最佳布线和/或总线上的负载电容很可能导致输入延迟值超过设备规格说明中给出的数值或在总线浮动时测量的数值。
|
||||
|
||||
典型的延迟值如下表所示,以下数据由从机设备使用不同芯片测试所得:
|
||||
|
||||
.. list-table::
|
||||
:widths: 40 20
|
||||
:header-rows: 1
|
||||
|
||||
* - 设备
|
||||
- 输入延迟 (ns)
|
||||
* - 理想设备
|
||||
- 0
|
||||
* - 使用 IO_MUX 的 ESP32 从机设备
|
||||
- 50
|
||||
* - 使用 GPIO_MATRIX 的 ESP32 从机设备
|
||||
- 75
|
||||
|
||||
MISO 路径延迟(有效时间)由从机的 *输入延迟* 与主机的 *GPIO 矩阵延迟* 组成。该延迟决定了频率限制,超过这个频率的全双工传输将无法如半双工交易中使用的 Dummy 位一样工作。该频率限制的计算方法为:
|
||||
|
||||
*Freq limit [MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
|
||||
|
||||
频率限制与输入延迟间的关系如图所示。如果主机使用 GPIO 矩阵,应在 MISO 延迟上额外增加两个 APB 时钟周期。
|
||||
|
||||
.. image:: /../_static/spi_master_freq_tv.png
|
||||
|
||||
不同设备在不同 *输入延迟* 时间下对应的频率限制如下表所示。
|
||||
|
||||
当主机驱动为 IO_MUX (0 ns) 时:
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 40 40
|
||||
:header-rows: 1
|
||||
|
||||
* - 输入延迟 (ns)
|
||||
- MISO 通道延迟 (ns)
|
||||
- 频率限制 (MHz)
|
||||
* - 0
|
||||
- 0
|
||||
- 80
|
||||
* - 50
|
||||
- 50
|
||||
- 16
|
||||
* - 75
|
||||
- 75
|
||||
- 11.43
|
||||
|
||||
当主机驱动为 GPIO_MATRIX (25 ns) 时:
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 40 40
|
||||
:header-rows: 1
|
||||
|
||||
* - 输入延迟 (ns)
|
||||
- MISO 通道延迟 (ns)
|
||||
- 频率限制 (MHz)
|
||||
* - 0
|
||||
- 25
|
||||
- 26.67
|
||||
* - 50
|
||||
- 75
|
||||
- 11.43
|
||||
* - 75
|
||||
- 100
|
||||
- 8.89
|
||||
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
.. _spi_known_issues:
|
||||
|
||||
已知问题
|
||||
------------
|
||||
|
||||
1. 写入和读取阶段同时进行时,半双工传输事务与 DMA 不兼容。
|
||||
|
||||
如需执行此类传输事务,可使用以下替代方案:
|
||||
|
||||
1. 执行全双工传输事务。
|
||||
2. 将总线初始化函数的最后一个参数设置为 0 以禁用 DMA,即:
|
||||
``ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0);``
|
||||
|
||||
此举可避免传输和接收超过 64 字节的数据。
|
||||
1. 尝试用命令和地址字段代替写入阶段。
|
||||
|
||||
2. 全双工传输事务与 *Dummy 位* 不兼容,因此存在频率限制,请参阅 :ref:`Dummy 位加速方案 <dummy_bit_workaround>`。
|
||||
|
||||
3. SPI 读取和写入阶段同时进行时,全双工模式和半双工模式下,:cpp:type:`spi_device_interface_config_t` 和 :cpp:type:`spi_transaction_ext_t` 中的 ``dummy_bits`` 均会失效。
|
||||
|
||||
4. 全双工传输事务模式中,命令阶段和地址阶段与 ``cs_ena_pretrans`` 不兼容。
|
||||
|
||||
|
||||
应用示例
|
||||
-------------------
|
||||
|
||||
查看使用 SPI 主机驱动程序在半双工模式下读取/写入 AT93C46D EEPROM(8 位模式)的示例代码,请前往 ESP-IDF 示例的 :example:`peripherals/spi_master/hd_eeprom` 目录。
|
||||
|
||||
|
||||
API 参考 - SPI Common
|
||||
--------------------------
|
||||
|
||||
.. include-build-file:: inc/spi_types.inc
|
||||
.. include-build-file:: inc/spi_common.inc
|
||||
|
||||
|
||||
API 参考 - SPI Master
|
||||
--------------------------
|
||||
|
||||
.. include-build-file:: inc/spi_master.inc
|
||||
|
|
Ładowanie…
Reference in New Issue