docs: provide CN translation for api-reference/peripherals/sdio_slave.rst (Backport v5.1)

This commit is contained in:
Cai Xin Ying 2024-01-02 19:43:24 +08:00 committed by Krzysztof Budzynski
parent 89e7a95f6c
commit 8495a31f46
2 changed files with 424 additions and 154 deletions

View File

@ -1,91 +1,105 @@
SDIO Card Slave Driver
======================
:link_to_translation:`zh_CN:[中文]`
Overview
--------
.. only:: esp32
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.
The ESP32 SDIO Card host and slave peripherals share two sets of pins, as shown in the table below. The first set is usually occupied by the SPI0 bus, which is responsible for the SPI flash holding the code to run. This means the SDIO slave driver can only run on the second set of pins while the SDIO host is not using it.
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, CMD and DAT0-3 lines should be pulled up no matter in 1-bit,
4-bit or SPI mode.
The SDIO slave can run under three modes: SPI, 1-bit SD, and 4-bit SD modes. Based on the signals on the interface, the device can determine the current mode and configure itself to adapt to that mode. Later, the slave driver can communicate with the slave device to properly handle commands and data transfers. According to the SDIO specification, the CMD and DAT0-3 signal lines should be pulled up whether in 1-bit SD, 4-bit SD or SPI mode.
Connections
^^^^^^^^^^^
.. only:: esp32
+----------+---------------+-------+-------+
| Pin Name | Corresponding | Slot1 | Slot2 |
+ + pins in SPI +-------+-------+
| | mode | GPIO Number |
+==========+===============+=======+=======+
| CLK | SCLK | 6 | 14 |
+----------+---------------+-------+-------+
| CMD | MOSI | 11 | 15 |
+----------+---------------+-------+-------+
| DAT0 | MISO | 7 | 2 |
+----------+---------------+-------+-------+
| DAT1 | Interrupt | 8 | 4 |
+----------+---------------+-------+-------+
| DAT2 | N.C. (pullup) | 9 | 12 |
+----------+---------------+-------+-------+
| DAT3 | #CS | 10 | 13 |
+----------+---------------+-------+-------+
.. list-table::
:header-rows: 1
:widths: 25 25 25 25
:align: center
* - Pin Name
- Corresponding Pins in SPI Mode
- GPIO Number (Slot 1)
- GPIO Number (Slot 2)
* - CLK
- SCLK
- 6
- 14
* - CMD
- MOSI
- 11
- 15
* - DAT0
- MISO
- 7
- 2
* - DAT1
- Interrupt
- 8
- 4
* - DAT2
- N.C. (pullup)
- 9
- 12
* - DAT3
- #CS
- 10
- 13
.. only:: esp32c6
+----------+--------------------------------+---------------+
| Pin Name | Corresponding pins in SPI mode | GPIO Number |
+==========+================================+===============+
| CLK | SCLK | 19 |
+----------+--------------------------------+---------------+
| CMD | MOSI | 18 |
+----------+--------------------------------+---------------+
| DAT0 | MISO | 20 |
+----------+--------------------------------+---------------+
| DAT1 | Interrupt | 21 |
+----------+--------------------------------+---------------+
| DAT2 | N.C. (pullup) | 22 |
+----------+--------------------------------+---------------+
| DAT3 | #CS | 23 |
+----------+--------------------------------+---------------+
.. list-table::
:header-rows: 1
:widths: 30 40 30
:align: center
- 1-bit SD mode: Connect CLK, CMD, DAT0, DAT1 pins and the ground.
- 4-bit SD mode: Connect all pins and the ground.
- SPI mode: Connect SCLK, MOSI, MISO, Interrupt, #CS pins and the ground.
* - Pin Name
- Corresponding Pins in SPI Mode
- GPIO Number
* - CLK
- SCLK
- 19
* - CMD
- MOSI
- 18
* - DAT0
- MISO
- 20
* - DAT1
- Interrupt
- 21
* - DAT2
- N.C. (pullup)
- 22
* - DAT3
- #CS
- 23
.. note:: Please check if CMD and DATA lines D0-D3 of the card are properly
pulled up by 10 KOhm resistors. This should be ensured even in 1-bit mode
or SPI mode. Most official modules don't offer these pullups internally.
If you are using official development boards, check
:ref:`compatibility_overview_espressif_hw_sdio` to see whether your
development boards have such pullups.
- 1-bit SD mode: Connect CLK, CMD, DAT0, DAT1 pins, and the ground.
- 4-bit SD mode: Connect all pins, and the ground.
- SPI mode: Connect SCLK, MOSI, MISO, Interrupt, #CS pins, and the ground.
.. note::
Please check if CMD and DATA lines DAT0-DAT3 of the card are properly pulled up by 10 KOhm - 90 KOhm resistors, which should be ensured even in 1-bit mode or SPI mode. Most official modules do not offer these pullups internally. If you are using official development boards, check :ref:`compatibility_overview_espressif_hw_sdio` to see whether your development boards have such pullups.
.. only:: esp32
.. note:: Most official modules have conflicts on strapping pins with the
SDIO slave function. If you are using a ESP32 module with 3.3 V flash
inside, you have to burn the EFUSE when you are developing on the module
for the first time. See :ref:`compatibility_overview_espressif_hw_sdio` to
see how to make your modules compatible with the SDIO.
.. note::
Here is a list for modules/kits with 3.3 V flash:
Most official modules have conflicts on strapping pins with the SDIO slave function. If you are using an ESP32 module with 3.3 V flash inside, when you are developing on the module for the first time, you will need to perform an eFuse burn-in prior to development. This will adjust the pin configuration of the module to make the module compatible with SDIO functionality. See :ref:`compatibility_overview_espressif_hw_sdio` for details on how to configure this.
- Modules: ESP32-PICO-D4, ESP32-WROOM-32 series (including ESP32-SOLO-1),
ESP32-WROVER-B and ESP32-WROVER-IB
- Kits: ESP32-PICO-KIT, ESP32-DevKitC (till v4), ESP32-WROVER-KIT
(v4.1 (also known as ESP32-WROVER-KIT-VB), v2, v1 (also known as DevKitJ
v1))
Here is a list of modules/kits with 3.3 V flash:
You can tell the version of your ESP23-WROVER-KIT version from the module
on it: v4.1 are with ESP32-WROVER-B modules, v3 are with ESP32-WROVER
modules, while v2 and v1 are with ESP32-WROOM-32 modules.
- Modules: All modules except ESP32-WROVER、ESP32-WROVER-I、ESP32-S3-WROOM-2, and please check `Modules Overview <https://www.espressif.com/en/products/modules>`__ for module list
- Kits: ESP32-PICO-KIT, ESP32-DevKitC (up to v4), ESP32-WROVER-KIT (v4.1 [also known as ESP32-WROVER-KIT-VB], v2, v1 [also known as DevKitJ v1])
You can tell the version of your ESP23-WROVER-KIT version from the module on it: v4.1 are with ESP32-WROVER-B modules, v3 are with ESP32-WROVER modules, while v2 and v1 are with ESP32-WROOM-32 modules.
Refer to :doc:`sd_pullup_requirements` for more technical details of the pullups.
@ -94,69 +108,52 @@ Refer to :doc:`sd_pullup_requirements` for more technical details of the pullups
sd_pullup_requirements
The host initialize the slave into SD mode by first sending CMD0 with DAT3
pin high, or in SPI mode by sending CMD0 with CS pin (the same pin as DAT3)
low.
The host initializes the slave into SD mode by sending the CMD0 command with the DAT3 pin set to a high level. Alternatively, the host initializes the SPI mode by sending CMD0 with CS pin low, which is the same pin as DAT3.
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.
After the initialization, the host can enable the 4-bit SD mode by writing CCCR register 0x07 by CMD52. All the bus detection processes 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).
The slave driver offers three services over Function 1 access by CMD52 and CMD53:
(1) sending and receiving FIFO
(2) 52 R/W registers (8-bit) 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. {IDF_TARGET_NAME} SDIO slave software is based on transfers.
- A transfer is initiated by a command token from the host and may consist of a response and multiple data blocks. The core mechanism of the {IDF_TARGET_NAME} SDIO slave driver involves data exchange and communication through transfers.
- Sending: slave to host transfers.
- Receiving: host to slave transfers.
.. note:: Register names in *{IDF_TARGET_NAME} Technical Reference Manual* > *SDIO Slave Controller*
[`PDF <{IDF_TARGET_TRM_EN_URL}#sdioslave>`__] 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.
.. note::
- 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
(usually via DMA). 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.
The register names in **{IDF_TARGET_NAME} Technical Reference Manual** > **SDIO Slave Controller** [`PDF <{IDF_TARGET_TRM_EN_URL}#sdioslave>`__] are organized from the host's perspective. For instance, ``RX`` registers indicate sending, while ``TX`` registers denote receiving. In our driver implementation, we've chosen not to utilize the terms ``TX`` or ``RX`` to prevent any potential ambiguities.
- FIFO: A designated address within Function 1 that can be accessed using CMD53 commands for reading or writing substantial volumes of data. The address corresponds to the length intended for reading from or writing to the slave in a single transfer: **requested length** = 0x1F800 address.
- Ownership: When the driver assumes ownership of a buffer, it means that the driver has the capability to perform random read/write operations on the buffer (often via DMA). 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; similarly, 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. {IDF_TARGET_NAME} SDIO 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).
.. note::
- 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 {IDF_TARGET_NAME} SDIO 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.
Requested length is different from the transfer length. In the context of {IDF_TARGET_NAME} SDIO slave DMA, the operation is based on the **requested length** rather than the **transfer length**. This means the DMA controller will process the data transfer according to the **requested length**, ensuring that only data within the **requested length** is transferred. The **transfer length** should be no shorter than the **requested length**, and the rest part is filled with 0 during sending or discard during receiving.
- Receiving buffer size: The buffer size is pre-defined between the host and the slave before communication starts. The slave application has to set the buffer size during initialization by the ``recv_buffer_size`` parameter in the ``sdio_slave_config_t`` structure.
- Interrupts: The {IDF_TARGET_NAME} SDIO slave supports interrupts in two directions: from host to slave (referred to as slave interrupts) and from slave to host (referred to as host interrupts). For more details, refer to :ref:`interrupts`.
- Registers: Specific addresses in Function 1 accessed by CMD52 or CMD53.
Communication with ESP SDIO Slave
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The host should initialize the {IDF_TARGET_NAME} SDIO slave according to the standard
SDIO initialization process (Sector 3.1.2 of `SDIO Simplified
Specification <https://www.sdcard.org/downloads/pls/>`_), which is described
briefly in :ref:`esp_slave_init`.
The host should initialize the {IDF_TARGET_NAME} SDIO slave according to the standard SDIO initialization process (Sector 3.1.2 of `SDIO Simplified Specification <https://www.sdcard.org/downloads/pls/pdf/?p=PartE1_SDIO_Simplified_Specification_Ver3.00.jpg&f=PartE1_SDIO_Simplified_Specification_Ver3.00.pdf&e=EN_SSE1>`_), which is described briefly in :ref:`esp_slave_init`.
Furthermore, there's an {IDF_TARGET_NAME}-specific upper-level communication protocol upon the CMD52/CMD53 to
Func 1. Please refer to :ref:`esp_slave_protocol_layer`. There is also a component
:doc:`ESP Serial Slave Link </api-reference/protocols/esp_serial_slave_link>`
for {IDF_TARGET_NAME} master to communicate with {IDF_TARGET_NAME} SDIO slave, see example :example:`peripherals/sdio`
when programming your host.
Furthermore, there is an {IDF_TARGET_NAME}-specific upper-level communication protocol built upon the foundation of CMD52/CMD53 to Function 1. Within this particular communication protocol, the master and slave engage in data exchange and communication through the utilization of CMD52/CMD53 commands. For more detailed information, please consult the :ref:`esp_slave_protocol_layer` section.
There is also a component :doc:`/api-reference/protocols/esp_serial_slave_link` designed for {IDF_TARGET_NAME} master to communicate with {IDF_TARGET_NAME} SDIO slave. See example :example:`peripherals/sdio` when programming your host.
.. _interrupts:
@ -169,92 +166,78 @@ There are interrupts from host to slave, and from slave to host to help communic
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.
The host can trigger an interruption in the slave by writing a single bit to the 0x08D register. As soon as any bit within the register is set, an interrupt is generated, prompting the SDIO slave driver to invoke the callback function specified in the ``slave_intr_cb`` member of 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.
.. note::
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.
The callback function is called in the ISR. Do not use any delay, loop or blocking function in the callback, e.g. mutex.
Similar to the previous information, there's an alternative set of functions available. 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.
The slave can interrupt the host by an interrupt line at certain time, which is level-sensitive, i.e., the interrupt signal can be triggered by detecting the level change of the interrupt line. 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 holds 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.
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.
There are 52 R/W shared registers (8-bit) 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.
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. The size of these buffers should equal to the Receiving buffer size.
1. Register the buffer by calling ``sdio_slave_recv_register_buf``, and get the handle of the registered buffer. The driver allocates memory for the linked-list descriptor needed to link the buffer onto the hardware. The size of these buffers should equal to the Receiving buffer size.
2. Load buffers onto the driver by passing the buffer handle to ``sdio_slave_recv_load_buf``.
3. Get the received data by calling ``sdio_slave_recv`` or ``sdio_slave_recv_packet``. If non-blocking call is needed, set ``wait=0``.
3. Get the received data by calling ``sdio_slave_recv`` or ``sdio_slave_recv_packet``. If a non-blocking call is needed, set ``wait=0``.
The difference between two APIs is that, ``sdio_slave_recv_packet`` gives more information about packet, which can consist of several buffers. When ``ESP_ERR_NOT_FINISHED`` is returned by this API, you should call this API iteratively until the return value is ``ESP_OK``. All the continuous buffers returned with ``ESP_ERR_NOT_FINISHED``, together with the last buffer returned with ``ESP_OK``, belong to one packet from the host. Call ``sdio_slave_recv_get_buf`` to get the address of the received data, and the actual length received in each buffer. The packet length is the sum of received length of all the buffers in the packet.
The difference between two APIs is that, ``sdio_slave_recv_packet`` gives more information about packet, which can consist of several buffers.
If the host never send data longer than the Receiving buffer size, or you don't care about the packet boundary (e.g. the data is only a byte stream), you can call the simpler version ``sdio_slave_recv`` instead.
When ``ESP_ERR_NOT_FINISHED`` is returned by this API, you should call this API iteratively until the return value is ``ESP_OK``. All the continuous buffers returned with ``ESP_ERR_NOT_FINISHED``, together with the last buffer returned with ``ESP_OK``, belong to one packet from the host.
Call ``sdio_slave_recv_get_buf`` to get the address of the received data, and the actual length received in each buffer. The packet length is the sum of received length of all the buffers in the packet.
If the host never send data longer than the Receiving buffer size, or you do not care about the packet boundary (e.g., the data is only a byte stream), you can call the simpler version ``sdio_slave_recv`` instead.
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.
.. note::
To minimize data copying overhead, the driver itself does not maintain any internal buffer; it is the responsibility of the application to promptly provide new buffers. The DMA system automatically stores received data into these buffers.
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:
Each time the slave has data to send, it raises an interrupt, and the host requests 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.
- Stream Mode: When a buffer is loaded into the driver, the buffer length is included into the packet length requested by host in the incoming communications. This is irrespective of whether previous packets have been sent or not. In other words, the length of the newly loaded buffer is included into the length of the packet requested by the host, even if there are previously unsent packets. This enables the host to receive data from several buffers in a single 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.
.. note::
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.
To avoid overhead from copying data, the driver itself does not 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, so as to ensure that the data is transferred correctly.
The application can call ``sdio_slave_transmit`` to send packets. In this case the function returns when the transfer
is successfully done, so the queue is not fully used. When higher effeciency is required, the application can use the
following functions instead:
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 successfully done, so the queue is not fully used. When higher efficiency 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 kept 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.
- 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 kept 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::
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;
@ -278,8 +261,7 @@ There are several ways to use the ``arg`` in the queue parameter:
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::
3. Work with the receiving part of this driver, and point ``arg`` to the receive buffer handle of this buffer, so that we can directly use the buffer to receive data when it is 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);
@ -292,7 +274,7 @@ There are several ways to use the ``arg`` in the queue parameter:
sdio_slave_send_get_finished((void**)&handle, portMAX_DELAY);
sdio_slave_recv_load_buf(handle);
More about this, see :example:`peripherals/sdio`.
For more about this, see :example:`peripherals/sdio`.
Application Example

View File

@ -1 +1,289 @@
.. include:: ../../../en/api-reference/peripherals/sdio_slave.rst
SDIO 卡从机驱动程序
===========================
:link_to_translation:`en:[English]`
概述
--------
.. only:: esp32
如下表所示ESP32 SDIO 卡的主机与从机外设共享两组管脚。SPI0 总线通常会占用第一组管脚,该总线负责运行代码,与 SPI flash 相连。因此,在 SDIO 主机不使用第二组管脚时SDIO 从机驱动程序只能在第二组管脚上运行。
SDIO 从机支持以下三种运行模式SPI、1 位 SD 和 4 位 SD。从机设备可以根据接口上的信号确定当前模式并相应地配置自身以适配该模式。随后从机驱动程序可以与从机设备进行通信正确处理命令和数据传输。根据 SDIO 规范,无论是在 1 位 SD、4 位 SD 还是 SPI 模式下CMD 和 DAT0-3 信号线都应设置为高电平。
连接方式
^^^^^^^^^^^
.. only:: esp32
.. list-table::
:header-rows: 1
:widths: 25 25 25 25
:align: center
* - 管脚名称
- SPI 模式的对应管脚
- GPIO 编号(卡槽 1
- GPIO 编号(卡槽 2
* - CLK
- SCLK
- 6
- 14
* - CMD
- MOSI
- 11
- 15
* - DAT0
- MISO
- 7
- 2
* - DAT1
- 中断
- 8
- 4
* - DAT2
- N.C.(拉高)
- 9
- 12
* - DAT3
- #CS
- 10
- 13
.. only:: esp32c6
.. list-table::
:header-rows: 1
:widths: 30 40 30
:align: center
* - 管脚名称
- SPI 模式的对应管脚
- GPIO 编号
* - CLK
- SCLK
- 19
* - CMD
- MOSI
- 18
* - DAT0
- MISO
- 20
* - DAT1
- 中断
- 21
* - DAT2
- N.C.(拉高)
- 22
* - DAT3
- #CS
- 23
- 1 位 SD 模式:连接 CLK、CMD、DAT0 和 DAT1 管脚并接地。
- 4 位 SD 模式:连接所有管脚并接地。
- SPI 模式:连接 SCLK、MOSI、MISO、中断、#CS 管脚并接地。
.. note::
请确保使用 10 KOhm - 90 KOhm 的上拉电阻将 SDIO 卡的 CMD 和数据线 DAT0-DAT3 配置为上拉,包括在 1 位模式或 SPI 模式下。大多数官方模组内部并未提供此类上拉电阻,使用官方开发板时,请参阅 :ref:`compatibility_overview_espressif_hw_sdio`,确认所用开发板是否配置此类上拉电阻。
.. only:: esp32
.. note::
多数官方模组的 strapping 管脚与 SDIO 从机功能配置存在冲突。若在内置 3.3 V flash 的 ESP32 模组上进行首次开发,需要在开发之前进行 eFuse 烧录,以调整模组的管脚配置,使其与 SDIO 功能兼容。请参阅 :ref:`compatibility_overview_espressif_hw_sdio`,了解具体配置方法。
以下是内置 3.3 V flash 的模组/开发板列表:
- 模组:除 ESP32-WROVER、ESP32-WROVER-I、ESP32-S3-WROOM-2 外的所有模组,模组列表见 `模组概览 <https://www.espressif.com/zh-hans/products/modules>`__
- 开发板ESP32-PICO-KIT、ESP32-DevKitC最高版本为 v4、ESP32-WROVER-KITv4.1 [也称 ESP32-WROVER-KIT-VB]、v2、v1 [也称 DevKitJ v1]
通过开发板上模组的型号可以判断 ESP32-WROVER-KIT 的版本v4.1 使用 ESP32-WROVER-B 模组v3 使用 ESP32-WROVER 模组v2 和 v1 使用 ESP32-WROOM-32 模组。
要了解有关上拉电阻的更多技术细节,请参阅 :doc:`sd_pullup_requirements`
.. toctree::
:hidden:
sd_pullup_requirements
主机可以配置 DAT3 管脚为高电平并发送 CMD0 命令,将从机初始化为 SD 模式;或配置 CS 管脚为低电平并发送 CMD0 命令,将从机初始化为 SPI 模式。CS 管脚与 DAT3 管脚相同。
初始化完成后,主机可以发送 CMD52 命令,将数据写入 CCCR 寄存器 0x07启用 4 位 SD 模式。所有总线检测均由从机外设处理。
主机与从机的通信必须通过 ESP 从机特定协议进行。
通过 CMD52 和 CMD53 命令,从机驱动程序基于 Function 1 提供了以下三种服务:
(1) 发送和接收 FIFO
(2) 主机和从机共享的 52 个 8 位 读写寄存器
(3) 16 个中断源8 个从主机到从机8 个从从机到主机)
术语
^^^^^^^^^^^
SDIO 从机驱动程序的相关术语如下:
- 传输 (transfer):传输始终由主机发出的命令符启动,可能包含一个响应和多个数据块。{IDF_TARGET_NAME} SDIO 从机驱动程序的核心机制是通过传输进行数据交换和通信。
- 发送 (sending):从从机到主机的传输。
- 接收 (receiving):从主机到从机的传输。
.. note::
**{IDF_TARGET_NAME} 技术参考手册** > **SDIO 从机控制器** [`PDF <{IDF_TARGET_TRM_CN_URL}#sdioslave>`__] 中,寄存器从主机的角度进行命名和定义。即,``RX`` 寄存器指的是发送寄存器,``TX`` 寄存器指的是接收寄存器。 我们在驱动程序中不再使用 ``TX````RX``,以避免产生歧义。
- FIFO在 Function 1 内的特定地址,可以通过使用 CMD53 命令读写大量数据。该地址与在单个传输中请求从从机读取或写入的长度相关:**请求长度** = 0x1F800 地址。
- 所有权 (ownership):拥有缓冲区的所有权时,驱动程序可以随机读写该缓冲区(通常通过 DMA 实现)。在所有权返回给应用程序前,应用程序不应读取/写入该缓冲区。如果应用程序从缓冲区中读取数据时,接收驱动程序拥有缓冲区的所有权,可能会读取到随机数据;如果应用程序向缓冲区写入数据时,发送驱动程序拥有缓冲区的所有权,发送的数据可能会损坏。
- 请求长度 (requested length):一次传输中的请求长度,由 FIFO 地址确定。
- 传输长度 (transfer length):一次传输中的请求长度,由 CMD53 字节/块计数字段确定。
.. note::
请求长度不同于传输长度。在 {IDF_TARGET_NAME} SDIO DMA 中,操作基于 **请求长度** 而非 **传输长度**,即 DMA 控制器会根据 **请求长度** 处理数据传输,确保只传输 **请求长度** 范围内的数据。**传输长度** 必须等于或长于 **请求长度**,并将剩余部分在发送时填充为 0或在接受时丢弃。
- 接收缓冲区大小 (receiving buffer size):通信开始前,主机与从机间会预先定义缓冲区大小。初始化过程中,从机应用程序必须通过结构体 ``sdio_slave_config_t`` 中的 ``recv_buffer_size`` 设置缓冲区大小。
- 中断 (interrupts){IDF_TARGET_NAME} SDIO 从机支持两个方向的中断,即由主机到从机(以下称从机中断)以及由从机到主机(以下称主机中断)。更多详情,请参阅 :ref:`interrupts`
- 寄存器 (registers):通过 CMD52 或 CMD53 命令在 Function 1 中访问的特定地址。
与 ESP SDIO 从机通信
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
在使用主机初始化 SDIO 从机时,应遵循标准 SDIO 初始化流程(请参阅 `SDIO 简化规范 <https://www.sdcard.org/downloads/pls/pdf/?p=PartE1_SDIO_Simplified_Specification_Ver3.00.jpg&f=PartE1_SDIO_Simplified_Specification_Ver3.00.pdf&e=EN_SSE1>`_ 的第 3.1.2 节),简化版流程可参考 :ref:`esp_slave_init`
此外,在通过 CMD52/CMD53 访问到 Function 1 这一机制的基础上,还存在一个仅适用于 {IDF_TARGET_NAME} 的上层通信协议。该特定通信协议中,主机和从机通过 CMD52/CMD53 命令进行数据交换和通信。更多详情,请参阅 :ref:`esp_slave_protocol_layer`
组件 :doc:`/api-reference/protocols/esp_serial_slave_link` 也支持 {IDF_TARGET_NAME} 主机与 {IDF_TARGET_NAME} SDIO 从机通信。在开发主机应用程序时,请参阅 :example:`peripherals/sdio` 中的示例。
.. _interrupts:
中断
^^^^^^^^^^
为了方便通信SDIO 从机驱动程序中既存在由主机到从机的中断信号,也存在由从机到主机的中断信号。
从机中断
""""""""""""""""
主机可以通过在寄存器 0x08D 中写入任意一个位来向从机发起中断。一旦置位了寄存器中的任意一位,就会产生一个中断,促使 SDIO 从机驱动调用特定回调函数,该回调函数由 ``sdio_slave_config_t`` 结构体中的 ``slave_intr_cb`` 定义。
.. note::
该回调函数在中断服务例程中调用,请勿在其中使用任何延迟、循环或可能阻塞的函数,如互斥锁。
类似前述情况,还有一组备选函数可供使用。可以调用 ``sdio_slave_wait_int`` 在一定时间内等待中断,或调用 ``sdio_slave_clear_int`` 清除来自主机的中断。回调函数可以与等待函数完美配合。
主机中断
"""""""""""""""
从机可以在特定时间通过中断线向主机发起中断,这个中断是电平触发的。主机检测到中断线电平拉低时,它可以读取从机中断状态寄存器,以查看中断源。主机可以清除特定的中断位,或选择禁用中断源。在清除或禁用所有中断源前,中断线会保持激活状态。
在 SDIO 从机驱动程序中,还存在一些专用中断源和通用中断源,详情请参阅 ``sdio_slave_hostint_t``
共享寄存器
^^^^^^^^^^^^^^^^
在主机与从机之间共有 52 个共享的 8 位读写寄存器,用于在主机和从机之间共享信息。通过 ``sdio_slave_read_reg````sdio_slave_write_reg``,从机可以随时读取或写入寄存器。主机可以通过 CMD52 或 CMD53 访问(读写)这些寄存器。
接收 FIFO
^^^^^^^^^^^^^^
准备向从机发送数据包时,主机需要读取从机的缓冲区数据数量,判定从机是否准备好接收数据。
为了支持接收来自主机的数据,应用程序需按照以下步骤,将缓冲区加载到从机驱动程序中:
1. 调用 ``sdio_slave_recv_register_buf`` 注册缓冲区,并获取已注册缓冲区的句柄。驱动程序会为链接到硬件的链表描述符所需的缓冲区分配内存。这些缓冲区的大小应与接收缓冲区大小相等。
2. 将缓冲区句柄传递给 ``sdio_slave_recv_load_buf``,将缓冲区加载到驱动程序中。
3. 调用 ``sdio_slave_recv````sdio_slave_recv_packet`` 获取接收到的数据。如果需要采取非阻塞式调用,可以设置 ``wait`` 为0。
这两个 API 的区别在于,``sdio_slave_recv_packet`` 会提供更多有关数据包的信息,数据包可以由多个缓冲区组成。
当此 API 返回 ``ESP_ERR_NOT_FINISHED`` 时,应循环调用此 API直到返回值为 ``ESP_OK``。此时,在主机发送的数据包中,包含了所有与 ``ESP_ERR_NOT_FINISHED`` 一起返回的连续缓冲区,以及与 ``ESP_OK`` 一起返回的最后一个缓冲区。
调用 ``sdio_slave_recv_get_buf`` 获取所接收数据的地址,以及每个缓冲区实际接收到的长度。数据包的长度是数据包中所有缓冲区接收长度的总和。
如果主机发送的数据始终小于接收缓冲区的大小,或者数据包的边界(例如,数据只是一个字节流)无关紧要,则可以使用更简单的 ``sdio_slave_recv``
4. 调用 ``sdio_recv_load_buf``,将经过处理的缓冲区句柄再次传递给驱动程序。
.. note::
为减少复制数据的开销驱动程序本身不具有任何内部缓冲区应用程序有责任及时提供新的缓冲区DMA 会自动将接收到的数据存储到缓冲区中。
发送 FIFO
^^^^^^^^^^^^
每当从机要发送数据时,它会触发一个中断,并由主机请求数据包长度。发送模式有两种:
- 数据流模式 (stream mode):在此模式下,当缓冲区加载到驱动程序中时,无论之前的数据包是否已经发送,该缓冲区的长度会计入主机在传入通信中请求的数据包长度中。换句话说,即使之前还有未发送的数据包,新加载的缓冲区长度也会包括在主机请求的数据包长度中。这样,主机可以在一次传输中获取多个缓冲区的数据。
- 数据包模式 (packet mode):在此模式下,数据包长度逐个更新,且仅在前一个数据包发送时更新。此时,主机在一次传输中只能获取一个缓冲区的数据。
.. note::
为减少复制数据的开销驱动程序本身没有内部缓冲区DMA 直接从应用程序提供的缓冲区中获取数据。发送完成前,应用程序不应该访问缓冲区,以确保数据传输的正确性。
结构体 ``sdio_slave_config_t`` 中的 ``sending_mode`` 可以设置发送模式,``send_queue_size`` 可以设置缓冲区数量。缓冲区大小均限制在 4092 字节内。尽管在流模式下,一次传输可以发送多个缓冲区,但每个缓冲区在队列中仍然计为一个。
应用程序可以调用 ``sdio_slave_transmit`` 函数发送数据包。此时,函数在传输完成后返回,因此队列并未完全占用。若需要更高效率,应用程序可以改用以下函数:
1. 将缓冲区信息(地址、长度以及表示缓冲区的 ``arg`` 参数)传递给 ``sdio_slave_send_queue``
- 如果需要采用非阻塞调用,请设置 ``wait`` 为 0。
- 如果 ``wait`` 并未设置为 ``portMAX_DELAY`` (等待直到缓冲区传输完成),应用程序应检查返回结果,确认数据是否已放入队列中,或是否已丢弃。
2. 调用 ``sdio_slave_send_get_finished`` 来获取并处理已完成的传输。在缓冲区 ``sdio_slave_send_get_finished`` 返回前不应修改缓冲区。这意味着缓冲区实际上发送给了主机,而非在队列中等待。
要使用队列参数中 ``arg`` ,可以采用以下几种方法:
1. 直接将 ``arg`` 指向一个动态分配的缓冲区,并在传输完成后使用 ``arg`` 释放该缓冲区。
2. 在传输结构体中封装传输信息,并将 ``arg`` 指向该结构体。使用该结构体还可以执行更多操作,例如::
typedef struct {
uint8_t* buffer;
size_t size;
int id;
}sdio_transfer_t;
//发送传输:
sdio_transfer_t trans = {
.buffer = ADDRESS_TO_SEND,
.size = 8,
.id = 3, //第 3 个传输
};
sdio_slave_send_queue(trans.buffer, trans.size, &trans, portMAX_DELAY);
//… 在此还可能发送更多传输
//处理完成的传输:
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); //执行更多操作
3. 用于该驱动程序的接收部分,将 ``arg`` 指向该缓冲区的接收缓冲区句柄。这样,可以在发送数据时直接使用该缓冲区来接收数据::
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);
//… 在此还可能发送更多传输
//加载已完成的传输,准备接收
sdio_slave_buf_handle_t handle = NULL;
sdio_slave_send_get_finished((void**)&handle, portMAX_DELAY);
sdio_slave_recv_load_buf(handle);
更多详情,请参阅 :example:`peripherals/sdio`
应用示例
-------------------
从机/主机通信的相关应用示例请参阅 :example:`peripherals/sdio`
API 参考
-------------
.. include-build-file:: inc/sdio_slave_types.inc
.. include-build-file:: inc/sdio_slave.inc