From 1b45e1117b192885aeb9f2afcc3ad6506fed0b04 Mon Sep 17 00:00:00 2001 From: michael Date: Mon, 4 Dec 2017 20:05:09 +0800 Subject: [PATCH] doc(sdio_slave): documentation for SDIO slave driver --- docs/Doxyfile | 2 + .../peripherals/esp_slave_protocol.rst | 85 +++++++ docs/en/api-reference/peripherals/index.rst | 1 + .../api-reference/peripherals/sdio_slave.rst | 227 ++++++++++++++++++ .../peripherals/esp_slave_protocol.rst | 1 + .../api-reference/peripherals/sdio_slave.rst | 1 + 6 files changed, 317 insertions(+) create mode 100644 docs/en/api-reference/peripherals/esp_slave_protocol.rst create mode 100644 docs/en/api-reference/peripherals/sdio_slave.rst create mode 100644 docs/zh_CN/api-reference/peripherals/esp_slave_protocol.rst create mode 100644 docs/zh_CN/api-reference/peripherals/sdio_slave.rst diff --git a/docs/Doxyfile b/docs/Doxyfile index 481eaa3fe5..53b31af2b6 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -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 \ diff --git a/docs/en/api-reference/peripherals/esp_slave_protocol.rst b/docs/en/api-reference/peripherals/esp_slave_protocol.rst new file mode 100644 index 0000000000..04cecca534 --- /dev/null +++ b/docs/en/api-reference/peripherals/esp_slave_protocol.rst @@ -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. + diff --git a/docs/en/api-reference/peripherals/index.rst b/docs/en/api-reference/peripherals/index.rst index 0b0d18a8ad..930e12a7b3 100644 --- a/docs/en/api-reference/peripherals/index.rst +++ b/docs/en/api-reference/peripherals/index.rst @@ -15,6 +15,7 @@ Peripherals API Remote Control SDMMC Host SD SPI Host + SDIO Slave Sigma-delta Modulation SPI Master SPI Slave diff --git a/docs/en/api-reference/peripherals/sdio_slave.rst b/docs/en/api-reference/peripherals/sdio_slave.rst new file mode 100644 index 0000000000..9e9b222ee3 --- /dev/null +++ b/docs/en/api-reference/peripherals/sdio_slave.rst @@ -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 + diff --git a/docs/zh_CN/api-reference/peripherals/esp_slave_protocol.rst b/docs/zh_CN/api-reference/peripherals/esp_slave_protocol.rst new file mode 100644 index 0000000000..6fc80e9b9d --- /dev/null +++ b/docs/zh_CN/api-reference/peripherals/esp_slave_protocol.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/peripherals/esp_slave_protocol.rst \ No newline at end of file diff --git a/docs/zh_CN/api-reference/peripherals/sdio_slave.rst b/docs/zh_CN/api-reference/peripherals/sdio_slave.rst new file mode 100644 index 0000000000..98a10bd77f --- /dev/null +++ b/docs/zh_CN/api-reference/peripherals/sdio_slave.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/peripherals/sdio_slave.rst \ No newline at end of file