# SDIO Card Slave Driver

[中文]

## Overview

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 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

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

- 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 Overview of Compatibility to see whether your development boards have such pullups.

Note

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 Overview of Compatibility for details on how to configure this.

> Here is a list of modules/kits with 3.3 V flash:

> - Modules: All modules except ESP32-WROVER, ESP32-WROVER-I, ESP32-S3-WROOM-2, and please check Modules Overview 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 SD Pull-up Requirements for more technical details of the pullups.

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 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 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:

- 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 ESP32 SDIO slave driver involves data exchange and communication through transfers.

- Sending: slave to host transfers.

- Receiving: host to slave transfers.

Note

The register names in ESP32 Technical Reference Manual > SDIO Slave Controller [PDF] 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. In the context of ESP32 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 ESP32 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 Interrupts.

- Registers: Specific addresses in Function 1 accessed by CMD52 or CMD53.

### Communication with ESP SDIO Slave

The host should initialize the ESP32 SDIO slave according to the standard SDIO initialization process (Sector 3.1.2 of SDIO Simplified Specification), which is described briefly in ESP SDIO Slave Initialization.

Furthermore, there is an ESP32-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 ESP SDIO Slave Protocol section.

There is also a component ESSL designed for ESP32 master to communicate with ESP32 SDIO slave. See example peripherals/sdio/basic when programming your host.

### Interrupts

There are interrupts from host to slave, and from slave to host to help communicating conveniently.

#### Slave Interrupts

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 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, 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.

### Shared Registers

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.

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 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 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.

   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 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 requests the packet length. There are two sending modes:

- 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 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 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`. Each buffer is restricted by the maximum size supported by a single SDIO slave DMA descriptor, which is chip-dependent. 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.

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 information 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. 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);
>        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);

>    For more about this, see peripherals/sdio/basic.

### Reset SDIO

Calling `sdio_slave_reset` can reset PKT_LEN (Packet length accumulator value) and TOKEN1 (Receiving buffers accumulated number) at the SDIO slave driver software level to resynchronize the transmit and receive counts with the host.

If there is a usage scenario where the ESP chip remains powered on but the HOST is powered off. During the power-off period of the HOST, some unknown signals may be generated on the SDIO signal line, causing the SDIO hardware state machine to be abnormal. The HOST restarts and executes the card identification process, and the ESP will not respond normally. In this case, consider calling `sdio_slave_reset_hw` to reset the SDIO hardware.

Note

Reset the SDIO hardware. The interrupt enable status and shared register values ​​will be lost. You may need to call `sdio_slave_set_host_intena` and `sdio_slave_write_reg` to set them.

### Signal Quality Checks

For SDIO slave links, command-only initialization can succeed even when one or more data lines are disconnected or have poor signal quality. In the ESP-IDF host stack, SDIO card discovery and configuration use `CMD0`, `CMD5`, and `CMD52` transactions on the command line. For example, the peripherals/sdio/hw_test host can complete `sdmmc_card_init()` and its `enable_slave_function()` step before any payload transfer starts on `DAT0-DAT3`.

The first payload transfer on `DAT0` appears when the host starts the first `CMD53` data transaction in 1-bit mode, so this example does contain a real 1-bit-only payload path when the user selects 1-line mode before initialization. `DAT1-DAT3` do not carry payload traffic until 4-bit mode has already been enabled and the first 4-bit `CMD53` transfer begins. Enabling 4-bit mode itself still uses command-line `CMD52` transactions, so 1-bit mode and 4-bit mode should be tested separately by choosing the bus width before initialization; the 4-bit flow does not first send payload in 1-bit mode.

When checking wiring, use the following checkpoints:

- If the link fails before command-only initialization completes, inspect `CMD` and `CLK` first.

- If command-only initialization succeeds but 1-bit mode fails, inspect `DAT0` first.

- If 1-bit mode works but 4-bit reads fail, inspect `DAT1-DAT3`, pull-ups, and the return path between host and slave.

If the problem is caused by simultaneous switching noise (SSN), the symptoms usually look like this:

- 1-line mode works, while 4-line mode fails consistently or intermittently.

- Communication fails when the boards are connected with jumper wires or other high-impedance ground paths, but improves after slightly changing the grounding or adding more ground connections, and disappears on a well-grounded setup.

- Adjusting the data-line drive strength or adding small series resistors on the data lines reduces or removes the failure.

- The host reports timeout or CRC errors.

- Lowering the clock frequency has limited effect, and the issue can still appear even at 400 kHz.

- The failure is easiest to reproduce when four data lines switch together, for example with the repeated `FF 00` payload used by peripherals/sdio/hw_test.

To confirm this type of problem, use a logic analyzer or oscilloscope and probe as close to the slave pins as possible. The peripherals/sdio/hw_test example is one convenient way to generate a repeatable pattern, but the same checks can also be done with any setup that can issue repeated 4-bit SDIO reads with payload that makes all four data lines switch at the same time.

- With a logic analyzer, capture `CMD`, `CLK`, and `DAT0-DAT3`. A common symptom is data compression: some bits disappear because the slave interprets a clock glitch as an extra clock edge, so later data appears earlier than expected. With the `hw_test` example, the normal 4-bit read pattern is one low start bit on all four data lines, then a repeating sequence of two clocks high and two clocks low for eight cycles, followed by CRC. Missing data in this pattern indicates that the slave observed a false clock edge.

- With an oscilloscope, observe the clock near the slave pins, especially after the data phase begins. If this issue is present, the later part of the clock waveform often becomes visibly worse. The effect can become smaller after improving grounding, adjusting drive strength, or adding small series resistors.

- Correlate both captures. When data compression is visible on the logic analyzer, there should be a severe clock glitch shortly before the missing data point.

The typical fix is to improve the grounding between host and slave.

To reproduce this type of issue, let the host issue repeated 4-bit SDIO reads with payload that makes all four data lines switch together. The receive mode of peripherals/sdio/hw_test is one such repeatable traffic source: its fixed `FF 00` frame makes all four data lines switch during every transfer.

One representative case happens during slave-to-host reads in 4-bit mode. When the host outputs a rising clock edge and the slave drives all four data lines from `1` to `0` at the same time, the slave ground can bounce upward. This can distort the observed clock at the slave, create a false extra edge, shorten the expected low-level width of the start bit or data bits, and finally cause a CRC error in the host driver. During practical debugging, changing the GPIO drive strength on the host or slave side may also change how severe the interference appears; peripherals/sdio/hw_test lets the host adjust drive strength from the menu and the slave from macros.

## Application Example

- peripherals/sdio/basic/host and peripherals/sdio/basic/slave demonstrate how to use a host to communicate with an ESP SDIO slave device.

- peripherals/sdio/hw_test/host and peripherals/sdio/hw_test/slave show how to turn the SDIO host/slave pair into a signal-integrity test setup.

## API Reference

### Header File

- components/esp_hal_sd/include/hal/sdio_slave_types.h

- This header file can be included with:

  >     #include "hal/sdio_slave_types.h"

- This header file is a part of the API provided by the `esp_hal_sd` component. To declare that your component depends on `esp_hal_sd`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_hal_sd

  > or

  >     PRIV_REQUIRES esp_hal_sd

### Enumerations

enum sdio_slave_hostint_t

  Mask of interrupts sending to the host.

  Values:

  enumerator SDIO_SLAVE_HOSTINT_BIT0

    General purpose interrupt bit 0.

  enumerator SDIO_SLAVE_HOSTINT_BIT1
  enumerator SDIO_SLAVE_HOSTINT_BIT2
  enumerator SDIO_SLAVE_HOSTINT_BIT3
  enumerator SDIO_SLAVE_HOSTINT_BIT4
  enumerator SDIO_SLAVE_HOSTINT_BIT5
  enumerator SDIO_SLAVE_HOSTINT_BIT6
  enumerator SDIO_SLAVE_HOSTINT_BIT7
  enumerator SDIO_SLAVE_HOSTINT_SEND_NEW_PACKET

    New packet available.

enum sdio_slave_timing_t

  Timing of SDIO slave.

  Values:

  enumerator SDIO_SLAVE_TIMING_PSEND_PSAMPLE

    Send at posedge, and sample at posedge. Default value for HS mode. If :c:macro:`SDIO_SLAVE_FLAG_HIGH_SPEED` is specified in :cpp:class:`sdio_slave_config_t`, this should be selected. Normally there's no problem using this to work in DS mode.

  enumerator SDIO_SLAVE_TIMING_NSEND_PSAMPLE

    Send at negedge, and sample at posedge. Default value for DS mode and below. If :c:macro:`SDIO_SLAVE_FLAG_DEFAULT_SPEED` is specified in :cpp:class:`sdio_slave_config_t`, this should be selected.

  enumerator SDIO_SLAVE_TIMING_PSEND_NSAMPLE

    Send at posedge, and sample at negedge.

  enumerator SDIO_SLAVE_TIMING_NSEND_NSAMPLE

    Send at negedge, and sample at negedge.

enum sdio_slave_sending_mode_t

  Configuration of SDIO slave mode.

  Values:

  enumerator SDIO_SLAVE_SEND_STREAM

    Stream mode, all packets to send will be combined as one if possible.

  enumerator SDIO_SLAVE_SEND_PACKET

    Packet mode, one packets will be sent one after another (only increase packet_len if last packet sent).

### Header File

- components/esp_driver_sdio/include/driver/sdio_slave.h

- This header file can be included with:

  >     #include "driver/sdio_slave.h"

- This header file is a part of the API provided by the `esp_driver_sdio` component. To declare that your component depends on `esp_driver_sdio`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_sdio

  > or

  >     PRIV_REQUIRES esp_driver_sdio

### Functions

esp_err_t sdio_slave_initialize(sdio_slave_config_t *config)

  Initialize the sdio slave driver

  Parameters:

    config -- Configuration of the sdio slave driver.

  Returns:

    - ESP_ERR_NOT_FOUND if no free interrupt found.

    - ESP_ERR_INVALID_STATE if already initialized.

    - ESP_ERR_NO_MEM if fail due to memory allocation failed.

    - ESP_OK if success

void sdio_slave_deinit(void)

  De-initialize the sdio slave driver to release the resources.

esp_err_t sdio_slave_start(void)

  Start hardware for sending and receiving, as well as set the IOREADY1 to 1.

  Note

  The driver will continue sending from previous data and PKT_LEN counting, keep data received as well as start receiving from current TOKEN1 counting. See `sdio_slave_reset`.

  Returns:

    - ESP_ERR_INVALID_STATE if already started.

    - ESP_OK otherwise.

void sdio_slave_stop(void)

  Stop hardware from sending and receiving, also set IOREADY1 to 0.

  Note

  this will not clear the data already in the driver, and also not reset the PKT_LEN and TOKEN1 counting. Call `sdio_slave_reset` to do that.

esp_err_t sdio_slave_reset(void)

  Clear the data still in the driver, as well as reset the PKT_LEN and TOKEN1 counting.

  Returns:

    - ESP_ERR_INVALID_STATE if already started.

    - ESP_OK otherwise.

esp_err_t sdio_slave_reset_hw(void)

  Reset sdio hardware, and clear the data still in the driver, as well as reset the PKT_LEN and TOKEN1 counting.

  Returns:

    - ESP_ERR_INVALID_STATE if already started.

    - ESP_OK otherwise.

sdio_slave_buf_handle_t sdio_slave_recv_register_buf(uint8_t *start)

  Register buffer used for receiving. All buffers should be registered before used, and then can be used (again) in the driver by the handle returned.

  Note

  The driver will use and only use the amount of space specified in the `recv_buffer_size` member set in the `sdio_slave_config_t`. All buffers should be larger than that. The buffer is used by the DMA, so it should be DMA capable and 32-bit aligned.

  Parameters:

    start -- The start address of the buffer.

  Returns:

    The buffer handle if success, otherwise NULL.

esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle)

  Unregister buffer from driver, and free the space used by the descriptor pointing to the buffer.

  Parameters:

    handle -- Handle to the buffer to release.

  Returns:

    ESP_OK if success, ESP_ERR_INVALID_ARG if the handle is NULL or the buffer is being used.

esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle)

  Load buffer to the queue waiting to receive data. The driver takes ownership of the buffer until the buffer is returned by `sdio_slave_send_get_finished` after the transaction is finished.

  Parameters:

    handle -- Handle to the buffer ready to receive data.

  Returns:

    - ESP_ERR_INVALID_ARG if invalid handle or the buffer is already in the queue. Only after the buffer is returened by `sdio_slave_recv` can you load it again.

    - ESP_OK if success

esp_err_t sdio_slave_recv_packet(sdio_slave_buf_handle_t *handle_ret, uint32_t wait)

  Get buffer of received data if exist with packet information. The driver returns the ownership of the buffer to the app.

  When you see return value is `ESP_ERR_NOT_FINISHED`, 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.

  You can call simpler `sdio_slave_recv` instead, 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).

  Note

  Call `sdio_slave_load_buf` with the handle to re-load the buffer onto the link list, and receive with the same buffer again. The address and length of the buffer got here is the same as got from `sdio_slave_get_buffer`.

  Parameters:

    - handle_ret -- Handle of the buffer holding received data. Use this handle in `sdio_slave_recv_load_buf()` to receive in the same buffer again.

    - wait -- Time to wait before data received.

  Returns:

    - ESP_ERR_INVALID_ARG if handle_ret is NULL

    - ESP_ERR_TIMEOUT if timeout before receiving new data

    - ESP_ERR_NOT_FINISHED if returned buffer is not the end of a packet from the host, should call this API again until the end of a packet

    - ESP_OK if success

esp_err_t sdio_slave_recv(sdio_slave_buf_handle_t *handle_ret, uint8_t **out_addr, size_t *out_len, uint32_t wait)

  Get received data if exist. The driver returns the ownership of the buffer to the app.

  Note

  Call `sdio_slave_load_buf` with the handle to re-load the buffer onto the link list, and receive with the same buffer again. The address and length of the buffer got here is the same as got from `sdio_slave_get_buffer`.

  Parameters:

    - handle_ret -- Handle to the buffer holding received data. Use this handle in `sdio_slave_recv_load_buf` to receive in the same buffer again.

    - out_addr -- [out] Output of the start address, set to NULL if not needed.

    - out_len -- [out] Actual length of the data in the buffer, set to NULL if not needed.

    - wait -- Time to wait before data received.

  Returns:

    - ESP_ERR_INVALID_ARG if handle_ret is NULL

    - ESP_ERR_TIMEOUT if timeout before receiving new data

    - ESP_OK if success

uint8_t *sdio_slave_recv_get_buf(sdio_slave_buf_handle_t handle, size_t *len_o)

  Retrieve the buffer corresponding to a handle.

  Parameters:

    - handle -- Handle to get the buffer.

    - len_o -- Output of buffer length

  Returns:

    buffer address if success, otherwise NULL.

esp_err_t sdio_slave_send_queue(uint8_t *addr, size_t len, void *arg, uint32_t wait)

  Put a new sending transfer into the send queue. The driver takes ownership of the buffer until the buffer is returned by `sdio_slave_send_get_finished` after the transaction is finished.

  Parameters:

    - addr -- Address for data to be sent. The buffer should be DMA capable and 32-bit aligned.

    - len -- Length of the data, should not exceed the maximum size supported by a single SDIO slave DMA descriptor on the current chip.

    - arg -- Argument to returned in `sdio_slave_send_get_finished`. The argument can be used to indicate which transaction is done, or as a parameter for a callback. Set to NULL if not needed.

    - wait -- Time to wait if the buffer is full.

  Returns:

    - ESP_ERR_INVALID_ARG if the length is not greater than 0.

    - ESP_ERR_TIMEOUT if the queue is still full until timeout.

    - ESP_OK if success.

esp_err_t sdio_slave_send_get_finished(void **out_arg, uint32_t wait)

  Return the ownership of a finished transaction.

  Parameters:

    - out_arg -- Argument of the finished transaction. Set to NULL if unused.

    - wait -- Time to wait if there's no finished sending transaction.

  Returns:

    ESP_ERR_TIMEOUT if no transaction finished, or ESP_OK if succeed.

esp_err_t sdio_slave_transmit(uint8_t *addr, size_t len)

  Start a new sending transfer, and wait for it (blocked) to be finished.

  Parameters:

    - addr -- Start address of the buffer to send

    - len -- Length of buffer to send.

  Returns:

    - ESP_ERR_INVALID_ARG if the length of descriptor is not greater than 0.

    - ESP_ERR_TIMEOUT if the queue is full or host do not start a transfer before timeout.

    - ESP_OK if success.

uint8_t sdio_slave_read_reg(int pos)

  Read the sdio slave register shared with host.

  Note

  register 28 to 31 are reserved for interrupt vector.

  Parameters:

    pos -- register address, 0-27 or 32-63.

  Returns:

    value of the register.

esp_err_t sdio_slave_write_reg(int pos, uint8_t reg)

  Write the sdio slave register shared with host.

  Note

  register 29 and 31 are used for interrupt vector.

  Parameters:

    - pos -- register address, 0-11, 14-15, 18-19, 24-27 and 32-63, other address are reserved.

    - reg -- the value to write.

  Returns:

    ESP_ERR_INVALID_ARG if address wrong, otherwise ESP_OK.

sdio_slave_hostint_t sdio_slave_get_host_intena(void)

  Get the interrupt enable for host.

  Returns:

    the interrupt mask.

void sdio_slave_set_host_intena(sdio_slave_hostint_t mask)

  Set the interrupt enable for host.

  Parameters:

    mask -- Enable mask for host interrupt.

esp_err_t sdio_slave_send_host_int(uint8_t pos)

  Interrupt the host by general purpose interrupt.

  Parameters:

    pos -- Interrupt num, 0-7.

  Returns:

    - ESP_ERR_INVALID_ARG if interrupt num error

    - ESP_OK otherwise

void sdio_slave_clear_host_int(sdio_slave_hostint_t mask)

  Clear general purpose interrupt to host.

  Parameters:

    mask -- Interrupt bits to clear, by bit mask.

esp_err_t sdio_slave_wait_int(int pos, uint32_t wait)

  Wait for general purpose interrupt from host.

  Note

  this clears the interrupt at the same time.

  Parameters:

    - pos -- Interrupt source number to wait for. is set.

    - wait -- Time to wait before interrupt triggered.

  Returns:

    ESP_OK if success, ESP_ERR_TIMEOUT if timeout.

### Structures

struct sdio_slave_config_t

  Configuration of SDIO slave.

  Public Members

  sdio_slave_timing_t timing

    timing of sdio_slave. see `sdio_slave_timing_t`.

  sdio_slave_sending_mode_t sending_mode

    mode of sdio_slave. `SDIO_SLAVE_MODE_STREAM` if the data needs to be sent as much as possible; `SDIO_SLAVE_MODE_PACKET` if the data should be sent in packets.

  int send_queue_size

    max buffers that can be queued before sending.

  size_t recv_buffer_size

    If buffer_size is too small, it costs more CPU time to handle larger number of buffers. If buffer_size is too large, the space larger than the transaction length is left blank but still counts a buffer, and the buffers are easily run out. Should be set according to length of data really transferred. All data that do not fully fill a buffer is still counted as one buffer. E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer. Buffer size of the slave pre-defined between host and slave before communication. It must not exceed the maximum size supported by a single SDIO slave DMA descriptor on the current chip, and all receive buffer given to the driver should be larger than this.

  sdio_event_cb_t event_cb

    when the host interrupts slave, this callback will be called with interrupt number (0-7).

  uint32_t flags

    Features to be enabled for the slave, combinations of `SDIO_SLAVE_FLAG_*`.

### Macros

SDIO_SLAVE_RECV_MAX_BUFFER

SDIO_SLAVE_FLAG_DAT2_DISABLED

  It is required by the SD specification that all 4 data lines should be used and pulled up even in 1-bit mode or SPI mode. However, as a feature, the user can specify this flag to make use of DAT2 pin in 1-bit mode. Note that the host cannot read CCCR registers to know we don't support 4-bit mode anymore, please do this at your own risk.

SDIO_SLAVE_FLAG_HOST_INTR_DISABLED

  The DAT1 line is used as the interrupt line in SDIO protocol. However, as a feature, the user can specify this flag to make use of DAT1 pin of the slave in 1-bit mode. Note that the host has to do polling to the interrupt registers to know whether there are interrupts from the slave. And it cannot read CCCR registers to know we don't support 4-bit mode anymore, please do this at your own risk.

SDIO_SLAVE_FLAG_INTERNAL_PULLUP

  Enable internal pullups for enabled pins. It is required by the SD specification that all the 4 data lines should be pulled up even in 1-bit mode or SPI mode. Note that the internal pull-ups are not sufficient for stable communication, please do connect external pull-ups on the bus. This is only for example and debug use.

SDIO_SLAVE_FLAG_DEFAULT_SPEED

  Disable the highspeed support of the hardware.

SDIO_SLAVE_FLAG_HIGH_SPEED

  Enable the highspeed support of the hardware. This is the default option. The host will see highspeed capability, but the mode actually used is determined by the host.

### Type Definitions

typedef void (*sdio_event_cb_t)(uint8_t event)

typedef void *sdio_slave_buf_handle_t

  Handle of a receive buffer, register a handle by calling `sdio_slave_recv_register_buf`. Use the handle to load the buffer to the driver, or call `sdio_slave_recv_unregister_buf` if it is no longer used.

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.