spi_slave_hd: new driver for spi slave in half duplex mode

This commit is contained in:
Michael (XIAO Xufeng)
2020-04-29 16:20:40 +08:00
parent a50ea8ad55
commit f6dd63d03d
34 changed files with 2673 additions and 118 deletions

View File

@@ -0,0 +1,200 @@
Communication with ESP SDIO Slave
=================================
This document describes the process of initialization of an ESP SDIO Slave device and then provides details on the ESP SDIO Slave protocol - a non-standard protocol that allows an SDIO Host to communicate with an ESP SDIO slave.
The ESP SDIO Slave protocol was created to implement the communication between SDIO host and slave, because the SDIO specification only shows how to access the custom region of a card (by sending CMD52 and CMD53 to Functions 1-7) without any details regarding the underlying hardware implementation.
.. _esp_sdio_slave_caps:
SDIO Slave Capabilities of Espressif chips
------------------------------------------
The services provided by SDIO Slave peripherals of Espressif chips are different. See the table below:
+-----------------------------------------------------------+-------+----------+
| | ESP32 | ESP32-S2 |
+===========================================================+=======+==========+
| SDIO slave | Y | N |
+-----------------------------------------------------------+-------+----------+
| :ref:`Tohost intr <esp_sdio_slave_interrupts>` | 8 | |
+-----------------------------------------------------------+-------+----------+
| :ref:`Frhost intr <esp_sdio_slave_interrupts>` | 8 | |
+-----------------------------------------------------------+-------+----------+
| :ref:`TX DMA <esp_sdio_slave_send_fifo>` | Y | |
+-----------------------------------------------------------+-------+----------+
| :ref:`RX DMA <esp_sdio_slave_rcv_fifo>` | Y | |
+-----------------------------------------------------------+-------+----------+
| :ref:`Shared registers <esp_sdio_slave_shared_registers>` | 56\* | |
+-----------------------------------------------------------+-------+----------+
- \* Not including the interrupt registers
.. _esp_slave_init:
ESP SDIO Slave Initialization
-----------------------------
The host should initialize the ESP32 SDIO slave according to the standard SDIO initialization process (Section 3.1.2 of `SDIO Simplified Specification <https://www.sdcard.org/downloads/pls/>`_). In this specification as well as below, the SDIO slave is called an (SD)IO card. Here is a brief example of an ESP SDIO Slave initialization process:
1. SDIO reset
CMD52 (Write 0x6=0x8)
2. SD reset
CMD0
3. Check whether IO card (optional)
CMD8
4. Send SDIO op cond and wait for card ready
CMD5 arg = 0x00000000
CMD5 arg = 0x00ff8000 (according to the response above, poll until ready)
**Example:**
Arg of R4 after first CMD5 (arg=0x00000000) is 0xXXFFFF00.
Keep sending CMD5 with arg=0x00FFFF00 until the R4 shows card ready (arg bit 31=1).
5. Set address
CMD3
6. Select card
CMD7 (arg address according to CMD3 response)
**Example:**
Arg of R6 after CMD3 is 0x0001xxxx.
Arg of CMD7 should be 0x00010000.
7. Select 4-bit mode (optional)
CMD52 (Write 0x07=0x02)
8. Enable func1
CMD52 (Write 0x02=0x02)
9. Enable SDIO interrupt (required if interrupt line (DAT1) is used)
CMD52 (Write 0x04=0x03)
10. Set Func0 blocksize (optional, default value is 512 (0x200))
CMD52/53 (Read 0x10~0x11)
CMD52/53 (Write 0x10=0x00)
CMD52/53 (Write 0x11=0x02)
CMD52/53 (Read 0x10~0x11, read to check the final value)
11. Set Func1 blocksize (optional, default value is 512 (0x200))
CMD52/53 (Read 0x110~0x111)
CMD52/53 (Write 0x110=0x00)
CMD52/53 (Write 0x111=0x02)
CMD52/53 (Read 0x110~0x111, read to check the final value)
.. _esp_slave_protocol_layer:
ESP SDIO Slave Protocol
-----------------------
The ESP SDIO Slave protocol is based on the SDIO Specification's I/O Read/Write commands, i.e., CMD52 and CMD53. The protocol offers the following services:
- Sending FIFO and receiving FIFO
- 52 8-bit R/W registers shared by host and slave (For details, see Section 8.4 Register summary (table SDIO SLC Host registers) in `{IDF_TARGET_NAME} Technical reference manual`_
- 16 general purpose interrupt sources, 8 from host to slave and 8 from slave to host
To begin communication, the host needs to enable the I/O Function 1 in the slave and access its registers as described below.
Check the code example :example:`peripherals/sdio`.
The :doc:`ESP Serial Slave Link </api-reference/protocols/esp_serial_slave_link>` component implements the logic of this protocol for ESP32 SDIO Host when communicating with an ESP32 SDIO slave.
.. _{IDF_TARGET_NAME} Technical Reference Manual: {IDF_TARGET_TRM_EN_URL}
.. _esp_sdio_slave_shared_registers:
Slave register table
^^^^^^^^^^^^^^^^^^^^
32-bit
""""""
- 0x044 (TOKEN_RDATA): in which bit 27-16 holds the number of the receiving buffer.
- 0x058 (INT_ST): holds the interrupt source bits from slave to host.
- 0x060 (PKT_LEN): holds the accumulated data length (in bytes) already read by host plus the data copied to the buffer but yet to be read.
- 0x0D4 (INT_CLR): write 1 to clear interrupt bits corresponding to INT_ST.
- 0x0DC (INT_ENA): mask bits for interrupts from slave to host.
8-bit
"""""
Shared general purpose registers:
- 0x06C-0x077: R/W registers 0-11 shared by slave and host.
- 0x07A-0x07B: R/W registers 14-15 shared by slave and host.
- 0x07E-0x07F: R/W registers 18-19 shared by slave and host.
- 0x088-0x08B: R/W registers 24-27 shared by slave and host.
- 0x09C-0x0BB: R/W registers 32-63 shared by slave and host.
Interrupt Registers:
- 0x08D (SLAVE_INT): bits for host to interrupt slave. auto clear.
FIFO (sending and receiving)
""""""""""""""""""""""""""""
0x090 - 0x1F7FF are reserved for FIFOs.
The address of CMD53 is related to the length requested to read from or write to the slave in a single transfer, as demonstrated by the equation below:
*requested length = 0x1F800-address*
The slave will respond with data that has a length equal to the length field of CMD53. In cases where the data is longer than the *requested length*, the data will be zero filled (when sending) or discarded (when receiving). This includes both the block and the byte mode of CMD53.
.. note::
The function number should be set to 1, OP Code should be set to 1 (for CMD53).
In order to achieve higher efficiency when accessing the FIFO by an arbitrary length, the block and byte modes of CMD53 can be used in combination. For example, given that the block size is set to 512 by default, you can write/get 1031 bytes of data from the FIFO by doing the following:
1. Send CMD53 in block mode, block count=2 (1024 bytes) to address 0x1F3F9=0x1F800-**1031**.
2. Then send CMD53 in byte mode, byte count=8 (or 7 if your controller supports that) to address 0x1F7F9=0x1F800-**7**.
.. _esp_sdio_slave_interrupts:
Interrupts
^^^^^^^^^^
SDIO interrupts are "level sensitive". For host interrupts, the slave sends an interrupt by pulling the DAT1 line down at a proper time. The host detects when the interrupt line is pulled down and reads the INT_ST register to determine the source of the interrupt. After that, the host can clear the interrupt bits by writing the INT_CLR register and process the interrupt. The host can also mask unneeded sources by clearing the bits in the INT_ENA register corresponding to the sources. If all the sources are cleared (or masked), the DAT1 line goes inactive.
On ESP32, the corresponding host_int bits are: bit 0 to bit 7.
For slave interrupts, the host sends a transfer to write the SLAVE_INT register. Once a bit is set to 1, the slave hardware and the driver will detect it and inform the application.
.. _esp_sdio_slave_rcv_fifo:
Receiving FIFO
^^^^^^^^^^^^^^
To write to the slave's receiving FIFO, the host should complete the following steps:
1. **Read the TOKEN1 field (bits 27-16) of the register TOKEN_RDATA (0x044)**. The buffer number remaining is TOKEN1 minus the number of buffers used by host.
2. **Make sure the buffer number is sufficient** (*buffer_size* x *buffer_num* is greater than the data to write, *buffer_size* is pre-defined between the host and the slave before the communication starts). Otherwise, keep returning to Step 1 until the buffer size is sufficient.
3. **Write to the FIFO address with CMD53**. Note that the *requested length* should not exceed the length calculated at Step 2, and the FIFO address is related to *requested length*.
4. **Calculate used buffers**. Note that a partially used buffer at the tail is counted as used.
.. _esp_sdio_slave_send_fifo:
Sending FIFO
^^^^^^^^^^^^
To read the slave's sending FIFO, the host should complete the following steps:
1. **Wait for the interrupt line to become active** (optional, low by default).
2. **Read (poll) the interrupt bits in the INT_ST register** to monitor if new packets exist.
3. **If new packets are ready, read the PKT_LEN register**. Before reading the packets, determine the length of data to be read. As the host keeps the length of data already read from the slave, subtract this value from PKT_LEN, the result will be the maximum length of data available for reading. If no data has been added to the sending FIFO yet, wait and poll until the slave is ready and update PKT_LEN.
4. **Read from the FIFO using CMD53**. Note that the *requested length* should not be greater than calculated at Step 3, and the FIFO address is related to *requested length*.
5. **Update the read length**.

View File

@@ -12,7 +12,16 @@ bus drivers.
After an `esp_serial_slave_link` device is initialized properly, the application can use it to communicate with the ESP
slave devices conveniently.
For more details about ESP32 SDIO slave protocol, see document :doc:`/api-reference/peripherals/esp_slave_protocol`.
Espressif Device protocols
--------------------------
For more details about Espressif device protocols, see the following documents.
.. toctree::
:maxdepth: 1
esp_sdio_slave_protocol
esp_spi_slave_protocol
Terminology
-----------
@@ -51,7 +60,7 @@ Services provided by ESP slave
There are some common services provided by the Espressif slaves:
1. Tohost Interrupts: The slave can inform the master about certain events by the interrupt line.
1. Tohost Interrupts: The slave can inform the master about certain events by the interrupt line. (optional)
2. Frhost Interrupts: The master can inform the slave about certain events.
@@ -71,12 +80,20 @@ There are some common services provided by the Espressif slaves:
5. Shared registers: the master can read some part of the registers on the slave, and also write
these registers to let the slave read.
The services provided by the slave depends on the slave's model. See
:ref:`esp_sdio_slave_caps` and :ref:`esp_spi_slave_caps` for more details.
Initialization of ESP SDIO Slave Link
-------------------------------------
Initialization of ESP Serial Slave Link
---------------------------------------
The ESP SDIO slave link (ESSL SDIO) devices relies on the sdmmc component. The ESSL device should
be initialized as below:
.. _essl_sdio_slave_init:
ESP SDIO Slave
^^^^^^^^^^^^^^
The ESP SDIO slave link (ESSL SDIO) devices relies on the sdmmc component. It includes the usage
of communicating with ESP SDIO Slave device via SDSPI feature. The ESSL device should be
initialized as below:
1. Initialize a sdmmc card (see :doc:` Document of SDMMC driver </api-reference/storage/sdmmc>`)
structure.
@@ -91,14 +108,23 @@ be initialized as below:
5. Call :cpp:func:`essl_wait_for_ready` to wait for the slave to be ready.
ESP SPI Slave
^^^^^^^^^^^^^
.. note::
If you are communicating with the ESP SDIO Slave device through SPI interface, you should use
the :ref:`SDIO interface <essl_sdio_slave_init>` instead.
Hasn't been supported yet.
APIs
----
After the initialization process above is performed, you can call the APIs below to make use of
the services provided by the slave:
Interrupts
^^^^^^^^^^
Tohost Interrupts (optional)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1. Call :cpp:func:`essl_get_intr_ena` to know which events will trigger the interrupts to the master.
@@ -109,7 +135,10 @@ Interrupts
4. When interrupt is triggered, call :cpp:func:`essl_get_intr` to know which events are active,
and call :cpp:func:`essl_clear_intr` to clear them.
5. Call :cpp:func:`essl_send_slave_intr` to trigger general purpose interrupt of the slave.
Frhost Interrupts
^^^^^^^^^^^^^^^^^
1. Call :cpp:func:`essl_send_slave_intr` to trigger general purpose interrupt of the slave.
TX FIFO
^^^^^^^
@@ -152,3 +181,4 @@ API Reference
.. include-build-file:: inc/essl.inc
.. include-build-file:: inc/essl_sdio.inc
.. include-build-file:: inc/essl_spi.inc

View File

@@ -0,0 +1,170 @@
ESP SPI Slave HD (Half Duplex) Mode Protocol
============================================
.. note::
This protocol is only for ESP32-S2. The driver for other chip versions hasn't be developed
yet.
.. _esp_spi_slave_caps:
SPI Slave Capabilities of Espressif chips
-----------------------------------------
+--------------------+-------+----------+
| | ESP32 | ESP32-S2 |
+====================+=======+==========+
| SPI Slave HD | N | Y (v2) |
+--------------------+-------+----------+
| Tohost intr | | N |
+--------------------+-------+----------+
| Frhost intr | | 2 \* |
+--------------------+-------+----------+
| TX DMA | | Y |
+--------------------+-------+----------+
| RX DMA | | Y |
+--------------------+-------+----------+
| Shared registers | | 72 |
+--------------------+-------+----------+
Introduction
------------
In the half duplex mode, the master has to use the protocol defined by the slave to communicate
with the slave. Each transaction may consists of the following phases (list by the order they
should exist):
- Command: 8-bit, master to slave
This phase determines the rest phases of the transactions. See :ref:`spi_slave_hd_supported_cmds`.
- Address: 8-bit, master to slave, optional
For some commands (WRBUF, RDBUF), this phase specifies the address of shared buffer to write
to/read from. For other commands with this phase, they are meaningless, but still have to
exist in the transaction.
- Dummy: 8-bit, floating, optional
This phase is the turn around time between the master and the slave on the bus, and also
provides enough time for the slave to prepare the data to send to master.
- Data: variable length, the direction is also determined by the command.
This may be a data OUT phase, in which the direction is slave to master, or a data IN phase,
in which the direction is master to slave.
The *direction* means which side (master or slave) controls the MOSI, MISO, WP and HD pins.
Data IO Modes
-------------
In some IO modes, more data wires can be use to send the data. As a result, the SPI clock cycles
required for the same amount of data will be less than in 1-bit mode. For example, in QIO mode,
address and data (IN and OUT) should be sent on all 4 data wires (MOSI, MISO, WP, and HD). Here's
the modes supported by ESP32-S2 SPI slave and the wire number used in corresponding modes.
+-------+------------+------------+--------------+---------+
| Mode | command WN | address WN | dummy cycles | data WN |
+=======+============+============+==============+=========+
| 1-bit | 1 | 1 | 1 | 1 |
+-------+------------+------------+--------------+---------+
| DOUT | 1 | 1 | 4 | 2 |
+-------+------------+------------+--------------+---------+
| DIO | 1 | 2 | 4 | 2 |
+-------+------------+------------+--------------+---------+
| QOUT | 1 | 1 | 4 | 4 |
+-------+------------+------------+--------------+---------+
| QIO | 1 | 4 | 4 | 4 |
+-------+------------+------------+--------------+---------+
| QPI | 4 | 4 | 4 | 4 |
+-------+------------+------------+--------------+---------+
Normally, which mode is used is determined is determined by the command sent by the master (See
:ref:`spi_slave_hd_supported_cmds`), except from the QPI mode.
QPI Mode
^^^^^^^^
The QPI mode is a special state of the SPI Slave. The master can send ENQPI command to put the
slave into the QPI mode state. In the QPI mode, the command is also sent in 4-bit, thus it's not
compatible with the normal modes. The master should only send QPI commands when the slave is in
the QPI mode. To exit form the QPI mode, master can send EXQPI command.
.. _spi_slave_hd_supported_cmds:
Supported Commands
------------------
.. note::
The command name are in a master-oriented direction. For example, WRBUF means master writes
the buffer of slave.
+----------+---------------------+---------+----------+----------------------------------------------------------+
| Name | Description | Command | Address | Data |
+==========+=====================+=========+==========+==========================================================+
| WRBUF | Write buffer | 0x01 | Buf addr | master to slave, no longer than buffer size |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| RDBUF | Read buffer | 0x02 | Buf addr | slave to master, no longer than buffer size |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| WRDMA | Write DMA | 0x03 | 8 bits | master to slave, no longer than length provided by slave |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| RDDMA | Read DMA | 0x04 | 8 bits | slave to master, no longer than length provided by slave |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| SEG_DONE | Segments done | 0x05 | - | - |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| ENQPI | Enter QPI mode | 0x06 | - | - |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| WR_DONE | Write segments done | 0x07 | - | - |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| CMD8 | Interrupt | 0x08 | - | - |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| CMD9 | Interrupt | 0x09 | - | - |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| CMDA | Interrupt | 0x0A | - | - |
+----------+---------------------+---------+----------+----------------------------------------------------------+
| EXQPI | Exit QPI mode | 0xDD | - | - |
+----------+---------------------+---------+----------+----------------------------------------------------------+
Moreover, WRBUF, RDBUF, WRDMA, RDDMA commands have their 2-bit and 4-bit version. To do
transactions in 2-bit or 4-bit mode, send the original command ORed by the corresponding command
mask below. For example, command 0xA1 means WRBUF in QIO mode.
+-------+------+
| Mode | Mask |
+=======+======+
| 1-bit | 0x00 |
+-------+------+
| DOUT | 0x10 |
+-------+------+
| DIO | 0x50 |
+-------+------+
| QOUT | 0x20 |
+-------+------+
| QIO | 0xA0 |
+-------+------+
| QPI | 0xA0 |
+-------+------+
Segment Transaction Mode
------------------------
Segment transaction mode is the only mode supported by the SPI Slave HD driver for now. In this
mode, for a transaction the slave load onto the DMA, the master is allowed to read or write in
segments. This way the master doesn't have to prepare large buffer as the size of data provided
by the slave. After the master finish reading/writing a buffer, it has to send corresponding
termination command to the slave as a synchronization signal. The slave driver will update new
data (if exist) onto the DMA upon seeing the termination command.
The termination command is WR_DONE (0x07) for the WRDMA, and CMD8 (0x08) for the RDDMA.
Here's an example for the flow the master read data from the slave DMA:
1. The slave loads 4092 bytes of data onto the RDDMA
2. The master do seven RDDMA transactions, each of them are 512 bytes long, and reads the first
3584 bytes from the slave
3. The master do the last RDDMA transaction of 512 bytes (equal, longer or shorter than the total
length loaded by the slave are all allowed). The first 508 bytes are valid data from the
slave, while the last 4 bytes are meaningless bytes.
4. The master sends CMD8 to the slave
5. The slave loads another 4092 bytes of data onto the RDDMA
6. The master can start new reading transactions after it sends the CMD8

View File

@@ -16,7 +16,7 @@ Application Protocols
mDNS <mdns>
Modbus <modbus>
Websocket Client <esp_websocket_client>
:esp32: ESP Serial Slave Link <esp_serial_slave_link>
ESP Serial Slave Link <esp_serial_slave_link>
Certificate Bundle <esp_crt_bundle>
Code examples for this API section are provided in the :example:`protocols` directory of ESP-IDF examples.