doc(sdio_slave): documentation for SDIO slave driver

This commit is contained in:
michael 2017-12-04 20:05:09 +08:00 committed by Michael (XIAO Xufeng)
parent f613859e29
commit 1b45e1117b
6 changed files with 317 additions and 0 deletions

View file

@ -107,6 +107,8 @@ INPUT = \
../../components/driver/include/driver/sdmmc_host.h \
../../components/driver/include/driver/sdmmc_types.h \
../../components/driver/include/driver/sdspi_host.h \
## SDIO slave
../../components/driver/include/driver/sdio_slave.h \
## Non-Volatile Storage
../../components/nvs_flash/include/nvs.h \
../../components/nvs_flash/include/nvs_flash.h \

View file

@ -0,0 +1,85 @@
ESP SDIO slave protocol
=======================
The protocol is based on Function 1 access by CMD52 and CMD53, offering 3 services: (1) sending and receiving FIFO, (2) 52 8-bit R/W
register shared by host and slave, (3) 8 general purpose interrupt sources from host to slave and 8 in the oppsite direction.
The host should access the registers below as described to communicate with slave.
Slave register table
--------------------
32-bit
^^^^^^^
- 0x044 (TOKEN_RDATA): in which bit 27-16 holds the receiving buffer number.
- 0x058 (INT_ST): holds the interrupt source bits from slave to host.
- 0x060 (PKT_LEN): holds the accumulated length (by byte) to be sent from slave to host.
- 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.
.. note:: This includes the CMD52 and CMD53 (block mode or byte mode).
The function number should be set to 1, OP Code should be set to 1 (for CMD53).
The slave will respond with the length according to the length field in CMD53 (1 of CMD52), with the data longer
than *requested length* filled with 0 (sending) or discard (receiving).
Interrupts
----------
For the host interrupts, the slave raise the interrupt by pulling DAT1 line down at a proper time (level sensitive).
The host detect this and read the INT_ST register to see the source. Then the host can clear it by writing the INT_CLR
register and do something with the interrupt. The host can also mask unneeded sources by clearing the bits in INT_ENA
register corresponding to the sources. If all the sources are cleared (or masked), the DAT1 line goes inactive.
``sdio_slave_hostint_t`` (:doc:`sdio_slave`) shows the bit definition corresponding to host interrupt sources.
For the slave interrupts, the host send transfers to write the SLAVE_INT register. Once a bit is written from 0 to 1,
the slave hardware and driver will detect it and inform the app.
Receiving FIFO
--------------
To write the receiving FIFO in the slave, host should work in the following steps:
1. Read the TOKEN1 field (bits 27-16) of TOKEN_RDATA (0x044) register. The buffer number remaining is TOKEN1 minus
the number of buffers used by host.
2. Make sure the buffer number is sufficient (*buffer_size* * *buffer_num* is greater than data to write, *buffer_size*
is pre-defined between the host and the slave before the communication starts). Or go back to step 1 until the buffer
is enough.
3. Write to the FIFO address with CMD53. Note that the *requested length* should not be larger than calculated in step 2,
and the FIFO address is related to *rquested length*.
4. Calculate used buffers, note that non-full buffer at the tail should be seen as one that is used.
Sending FIFO
------------
To read the sending FIFO in the slave, host should work in the following steps:
1. Wait for the interrupt line to be active (optional, low by default).
2. Read (poll) the interrupt bits in INT_ST register to see whether new packets exists.
3. If new packets are ready, reads the PKT_LEN reg. The data length to read from slave is PKT_LEN minuses the length
that has been read from the host. If the PKT_LEN is not larger than used, wait and poll until the slave is ready and
update the PKT_LEN.
4. Read from the FIFO with CMD53. Note that the *requested length* should not be larger than calculated in step3, and
the FIFO address is related to *requested length*.
5. Recored read length.

View file

@ -15,6 +15,7 @@ Peripherals API
Remote Control <rmt>
SDMMC Host <sdmmc_host>
SD SPI Host <sdspi_host>
SDIO Slave <sdio_slave>
Sigma-delta Modulation <sigmadelta>
SPI Master <spi_master>
SPI Slave <spi_slave>

View file

@ -0,0 +1,227 @@
SDIO Card Slave Driver
======================
Overview
--------
.. note:: At the moment, this code has been proven to work on the Wrover-Kit V3. Earlier versions of the Wrover-Kit
and other development kits are electrically incompatible with this code. Functionality on other devboards is untested.
The ESP32 SDIO Card peripherals (Host, Slave) shares two sets of pins as below table.
The first set is usually occupied by SPI0 bus which is responsible for the SPI flash holding the code to run.
This means SDIO slave driver can only runs on the second set of pins while SDIO host is not using it.
+----------+-------+-------+
| Pin Name | Slot1 | Slot2 |
+ +-------+-------+
| | GPIO Number |
+==========+=======+=======+
| CLK | 6 | 14 |
+----------+-------+-------+
| CMD | 11 | 15 |
+----------+-------+-------+
| DAT0 | 7 | 2 |
+----------+-------+-------+
| DAT1 | 8 | 4 |
+----------+-------+-------+
| DAT2 | 9 | 12 |
+----------+-------+-------+
| DAT3 | 10 | 13 |
+----------+-------+-------+
The SDIO slave can run under 3 modes: SPI, 1-bit SD and 4-bit SD modes, which is detected automatically by the
hardware. According to the SDIO specification, the host initialize the slave into SD mode by first sending CMD0 with
DAT3 pin high, while initialize the slave into SPI mode by sending CMD0 with CS pin (the same pin as DAT3) low. After the
initialization, the host can enable the 4-bit SD mode by writing CCCR register 0x07 by CMD52. All the bus detection
process are handled by the slave peripheral.
The host has to communicate with the slave by an ESP-slave-specific protocol. The slave driver offers 3 services over
Function 1 access by CMD52 and CMD53: (1) a sending FIFO and a receiving FIFO, (2) 52 8-bit R/W registers shared by
host and slave, (3) 16 interrupt sources (8 from host to slave, and 8 from slave to host).
Terminology
^^^^^^^^^^^
The SDIO slave driver uses the following terms:
- Transfer: a transfer is always started by a command token from the host, and may contain a reply and several data
blocks. ESP32 slave software is based on transfers.
- Sending: slave to host transfers.
- Receiving: host to slave transfers.
.. note:: Register names in ESP Rechnical Reference Manual are oriented from the point of view of the host, i.e. 'rx'
registers refer to sending, while 'tx' registers refer to receiving. We're not using `tx` or `rx` in the driver to
avoid ambiguities.
- FIFO: specific address in Function 1 that can be access by CMD53 to read/write large amount of data. The address is
related to the length requested to read from/write to the slave in a single transfer:
*requested length* = 0x1F800-address.
- Ownership: When the driver takes ownership of a buffer, it means the driver can randomly read/write the buffer
(mostly by the hardware). The application should not read/write the buffer until the ownership is returned to the
application. If the application reads from a buffer owned by a receiving driver, the data read can be random; if
the application writes to a buffer owned by a sending driver, the data sent may be corrupted.
- Requested length: The length requested in one transfer determined by the FIFO address.
- Transfer length: The length requested in one transfer determined by the CMD53 byte/block count field.
.. note:: Requested length is different from the transfer length. ESP32 slave DMA base on the *requested length* rather
than the *transfer length*. The *transfer length* should be no shorter than the *requested length*, and the rest
part will be filled with 0 (sending) or discard (receiving).
- Receiving buffer size: The buffer size is pre-defined between the host and the slave before communication starts.
Slave application has to set the buffer size during initialization by the ``recv_buffer_size`` member of
``sdio_slave_config_t``.
- Interrupts: the esp32 slave support interrupts in two directions: from host to slave (called slave interrupts below)
and from slave to host (called host interrupts below). See more in :ref:`interrupts`.
- Registers: specific address in Function 1 access by CMD52 or CMD53.
ESP SDIO Slave Protocol
^^^^^^^^^^^^^^^^^^^^^^^
The communication protocol slave used to communicate with the host is ESP32 specific, please refer to
:doc:`esp_slave_protocol`, or example :example:`peripherals/sdio` for designing a host.
.. toctree::
:hidden:
esp_slave_protocol
.. _interrupts:
Interrupts
^^^^^^^^^^
There are interrupts from host to slave, and from slave to host to help communicating conveniently.
Slave Interrupts
""""""""""""""""
The host can interrupt the slave by writing any one bit in the register 0x08D. Once any bit of the register is
set, an interrupt is raised and the SDIO slave driver calls the callback function defined in the ``slave_intr_cb`` member
in the ``sdio_slave_config_t`` structure.
.. note:: The callback function is called in the ISR, do not use any delay, loop or spinlock in the callback.
There's another set of functions can be used. You can call ``sdio_slave_wait_int`` to wait for an interrupt within a
certain time, or call ``sdio_slave_clear_int`` to clear interrupts from host. The callback function can work with the
wait functions perfectly.
Host Interrupts
"""""""""""""""
The slave can interrupt the host by an interrupt line (at certain time) which is level sensitive. When the host see the
interrupt line pulled down, it may read the slave interrupt status register, to see the interrupt source. Host can clear
interrupt bits, or choose to disable a interrupt source. The interrupt line will hold active until all the sources are
cleared or disabled.
There are several dedicated interrupt sources as well as general purpose sources. see ``sdio_slave_hostint_t`` for
more information.
Shared Registers
^^^^^^^^^^^^^^^^
There are 52 8-bit R/W shared registers to share information between host and slave. The slave can write or read the
registers at any time by ``sdio_slave_read_reg`` and ``sdio_slave_write_reg``. The host can access (R/W) the register by CMD52 or CMD53.
Receiving FIFO
^^^^^^^^^^^^^^
When the host is going to send the slave some packets, it has to check whether the slave is ready to receive by reading
the buffer number of slave.
To allow the host sending data to the slave, the application has to load buffers to the slave driver by the following steps:
1. Register the buffer by calling ``sdio_slave_recv_register_buf``, and get the handle of the registered buffer. The driver
will allocate memory for the linked-list descriptor needed to link the buffer onto the hardware.
2. Load buffers onto the driver by passing the buffer handle to ``sdio_slave_recv_load_buf``.
3. Call ``sdio_slave_recv`` to get the received data. If non-blocking call is needed, set ``wait=0``.
4. Pass the handle of processed buffer back to the driver by ``sdio_recv_load_buf`` again.
.. note:: To avoid overhead from copying data, the driver itself doesn't have any buffer inside, the application is
responsible to offer new buffers in time. The DMA will automatically store received data to the buffer.
Sending FIFO
^^^^^^^^^^^^
Each time the slave has data to send, it raises an interrupt and the host will request for the packet length. There are
two sending modes:
- Stream Mode: when a buffer is loaded to the driver, the buffer length will be counted into the packet length requested
by host in the incoming communications. Regardless previous packets are sent or not. This means the host can get data
of several buffers in one transfer.
- Packet Mode: the packet length is updated packet by packet, and only when previous packet is sent. This means that the
host can only get data of one buffer in one transfer.
.. note:: To avoid overhead from copying data, the driver itself doesn't have any buffer inside. Namely, the DMA takes
data directly from the buffer provided by the application. The application should not touch the buffer until the
sending is finished.
The sending mode can be set in the ``sending_mode`` member of ``sdio_slave_config_t``, and the buffer numbers can be
set in the ``send_queue_size``. All the buffers are restricted to be no larger than 4092 bytes. Though in the stream
mode several buffers can be sent in one transfer, each buffer is still counted as one in the queue.
The application can call ``sdio_slave_transmit`` to send packets. In this case the function returns when the transfer
is sucessfully done, so the queue is not fully used. When higher effeciency is required, the application can use the
following functions instead:
1. Pass buffer information (address, length, as well as an ``arg`` indicating the buffer) to ``sdio_slave_send_queue``.
If non-blocking call is needed, set ``wait=0``. If the ``wait`` is not ``portMAX_DELAY`` (wait until success),
application has to check the result to know whether the data is put in to the queue or discard.
2. Call ``sdio_slave_send_get_finished`` to get and deal with a finished transfer. A buffer should be keep unmodified
until returned from ``sdio_slave_send_get_finished``. This means the buffer is actually sent to the host, rather
than just staying in the queue.
There are several ways to use the ``arg`` in the queue parameter:
1. Directly point ``arg`` to a dynamic-allocated buffer, and use the ``arg`` to free it when transfer finished.
2. Wrap transfer informations in a transfer structure, and point ``arg`` to the structure. You can use the
structure to do more things like::
typedef struct {
uint8_t* buffer;
size_t size;
int id;
}sdio_transfer_t;
//and send as:
sdio_transfer_t trans = {
.buffer = ADDRESS_TO_SEND,
.size = 8,
.id = 3, //the 3rd transfer so far
};
sdio_slave_send_queue(trans.buffer, trans.size, &trans, portMAX_DELAY);
//... maybe more transfers are sent here
//and deal with finished transfer as:
sdio_transfer_t* arg = NULL;
sdio_slave_send_get_finished((void**)&arg, portMAX_DELAY);
ESP_LOGI("tag", "(%d) successfully send %d bytes of %p", arg->id, arg->size, arg->buffer);
some_post_callback(arg); //do more things
3. Working with the receiving part of this driver, point ``arg`` to the receive buffer handle of this buffer. So
that we can directly use the buffer to receive data when it's sent::
uint8_t buffer[256]={1,2,3,4,5,6,7,8};
sdio_slave_buf_handle_t handle = sdio_slave_recv_register_buf(buffer);
sdio_slave_send_queue(buffer, 8, handle, portMAX_DELAY);
//... maybe more transfers are sent here
//and load finished buffer to receive as
sdio_slave_buf_handle_t handle = NULL;
sdio_slave_send_get_finished((void**)&handle, portMAX_DELAY);
sdio_slave_recv_load_buf(handle);
More about this, see :example:`peripherals/sdio`.
Application Example
-------------------
Slave/master communication: :example:`peripherals/sdio`.
API Reference
-------------
.. include:: /_build/inc/sdio_slave.inc

View file

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/esp_slave_protocol.rst

View file

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/sdio_slave.rst