# SDIO 卡从机驱动程序

[English]

## 概述

SDIO 从机支持以下三种运行模式：SPI、1 位 SD 和 4 位 SD。从机设备可以根据接口上的信号确定当前模式，并相应地配置自身以适配该模式。随后，从机驱动程序可以与从机设备进行通信，正确处理命令和数据传输。根据 SDIO 规范，无论是在 1 位 SD、4 位 SD 还是 SPI 模式下，CMD 和 DAT0-3 信号线都应设置为高电平。

### 连接方式

管脚名称 | 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 管脚并接地。

备注

请确保使用 10 KOhm - 90 KOhm 的上拉电阻将 SDIO 卡的 CMD 和数据线 DAT0-DAT3 配置为上拉，包括在 1 位模式或 SPI 模式下。大多数官方模组内部并未提供此类上拉电阻，使用官方开发板时，请参阅 兼容性概述，确认所用开发板是否配置此类上拉电阻。

要了解有关上拉电阻的更多技术细节，请参阅 SD 上拉需求。

主机可以配置 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)：传输始终由主机发出的命令符启动，可能包含一个响应和多个数据块。ESP32-C6 SDIO 从机驱动程序的核心机制是通过传输进行数据交换和通信。

- 发送 (sending)：从从机到主机的传输。

- 接收 (receiving)：从主机到从机的传输。

备注

在 ESP32-C6 技术参考手册 > SDIO 从机控制器 [PDF] 中，寄存器从主机的角度进行命名和定义。即，`RX` 寄存器指的是发送寄存器，`TX` 寄存器指的是接收寄存器。 我们在驱动程序中不再使用 `TX` 或 `RX`，以避免产生歧义。

- FIFO：在 Function 1 内的特定地址，可以通过使用 CMD53 命令读写大量数据。该地址与在单个传输中请求从从机读取或写入的长度相关：请求长度 = 0x1F800 – 地址。

- 所有权 (ownership)：拥有缓冲区的所有权时，驱动程序可以随机读写该缓冲区（通常通过 DMA 实现）。在所有权返回给应用程序前，应用程序不应读取/写入该缓冲区。如果应用程序从缓冲区中读取数据时，接收驱动程序拥有缓冲区的所有权，可能会读取到随机数据；如果应用程序向缓冲区写入数据时，发送驱动程序拥有缓冲区的所有权，发送的数据可能会损坏。

- 请求长度 (requested length)：一次传输中的请求长度，由 FIFO 地址确定。

- 传输长度 (transfer length)：一次传输中的请求长度，由 CMD53 字节/块计数字段确定。

备注

请求长度不同于传输长度。在 ESP32-C6 SDIO DMA 中，操作基于 请求长度 而非 传输长度，即 DMA 控制器会根据 请求长度 处理数据传输，确保只传输 请求长度 范围内的数据。传输长度 必须等于或长于 请求长度，并将剩余部分在发送时填充为 0，或在接受时丢弃。

- 接收缓冲区大小 (receiving buffer size)：通信开始前，主机与从机间会预先定义缓冲区大小。初始化过程中，从机应用程序必须通过结构体 `sdio_slave_config_t` 中的 `recv_buffer_size` 设置缓冲区大小。

- 中断 (interrupts)：ESP32-C6 SDIO 从机支持两个方向的中断，即由主机到从机（以下称从机中断）以及由从机到主机（以下称主机中断）。更多详情，请参阅 中断。

- 寄存器 (registers)：通过 CMD52 或 CMD53 命令在 Function 1 中访问的特定地址。

### 与 ESP SDIO 从机通信

在使用主机初始化 SDIO 从机时，应遵循标准 SDIO 初始化流程（请参阅 SDIO 简化规范 的第 3.1.2 节），简化版流程可参考 初始化 ESP SDIO 从机。

此外，在通过 CMD52/CMD53 访问到 Function 1 这一机制的基础上，还存在一个仅适用于 ESP32-C6 的上层通信协议。该特定通信协议中，主机和从机通过 CMD52/CMD53 命令进行数据交换和通信。更多详情，请参阅 ESP SDIO 从机协议。

组件 ESP 串行从机链路 也支持 ESP32-C6 主机与 ESP32-C6 SDIO 从机通信。在开发主机应用程序时，请参阅 peripherals/sdio 中的示例。

### 中断

为了方便通信，SDIO 从机驱动程序中既存在由主机到从机的中断信号，也存在由从机到主机的中断信号。

#### 从机中断

主机可以通过在寄存器 0x08D 中写入任意一个位来向从机发起中断。一旦置位了寄存器中的任意一位，就会产生一个中断，促使 SDIO 从机驱动调用特定回调函数，该回调函数由 `sdio_slave_config_t` 结构体中的 `slave_intr_cb` 定义。

备注

该回调函数在中断服务例程中调用，请勿在其中使用任何延迟、循环或可能阻塞的函数，如互斥锁。

类似前述情况，还有一组备选函数可供使用。可以调用 `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`，将经过处理的缓冲区句柄再次传递给驱动程序。

备注

为减少复制数据的开销，驱动程序本身不具有任何内部缓冲区；应用程序有责任及时提供新的缓冲区，DMA 会自动将接收到的数据存储到缓冲区中。

### 发送 FIFO

每当从机要发送数据时，它会触发一个中断，并由主机请求数据包长度。发送模式有两种：

- 数据流模式 (stream mode)：在此模式下，当缓冲区加载到驱动程序中时，无论之前的数据包是否已经发送，该缓冲区的长度会计入主机在传入通信中请求的数据包长度中。换句话说，即使之前还有未发送的数据包，新加载的缓冲区长度也会包括在主机请求的数据包长度中。这样，主机可以在一次传输中获取多个缓冲区的数据。

- 数据包模式 (packet mode)：在此模式下，数据包长度逐个更新，且仅在前一个数据包发送时更新。此时，主机在一次传输中只能获取一个缓冲区的数据。

备注

为减少复制数据的开销，驱动程序本身没有内部缓冲区，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);

>    更多详情，请参阅 peripherals/sdio。

## 应用示例

- peripherals/sdio/host 和 peripherals/sdio/slave 演示了如何使用主机与 ESP SDIO 从机进行通信。

## API 参考

### Header File

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

- This header file can be included with:

  >     #include "hal/sdio_slave_types.h"

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

  参数:

    config -- Configuration of the sdio slave driver.

  返回:

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

  备注

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

  返回:

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

  备注

  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.

  返回:

    always return ESP_OK.

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.

  备注

  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.

  参数:

    start -- The start address of the buffer.

  返回:

    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.

  参数:

    handle -- Handle to the buffer to release.

  返回:

    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.

  参数:

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

  返回:

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

  备注

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

  参数:

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

  返回:

    - 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, TickType_t wait)

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

  备注

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

  参数:

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

  返回:

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

  参数:

    - handle -- Handle to get the buffer.

    - len_o -- Output of buffer length

  返回:

    buffer address if success, otherwise NULL.

esp_err_t sdio_slave_send_queue(uint8_t *addr, size_t len, void *arg, TickType_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.

  参数:

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

    - len -- Length of the data, should not be longer than 4092 bytes (may support longer in the future).

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

  返回:

    - 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, TickType_t wait)

  Return the ownership of a finished transaction.

  参数:

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

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

  返回:

    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.

  参数:

    - addr -- Start address of the buffer to send

    - len -- Length of buffer to send.

  返回:

    - 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 spi slave register shared with host.

  备注

  register 28 to 31 are reserved for interrupt vector.

  参数:

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

  返回:

    value of the register.

esp_err_t sdio_slave_write_reg(int pos, uint8_t reg)

  Write the spi slave register shared with host.

  备注

  register 29 and 31 are used for interrupt vector.

  参数:

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

    - reg -- the value to write.

  返回:

    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.

  返回:

    the interrupt mask.

void sdio_slave_set_host_intena(sdio_slave_hostint_t mask)

  Set the interrupt enable for host.

  参数:

    mask -- Enable mask for host interrupt.

esp_err_t sdio_slave_send_host_int(uint8_t pos)

  Interrupt the host by general purpose interrupt.

  参数:

    pos -- Interrupt num, 0-7.

  返回:

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

  参数:

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

esp_err_t sdio_slave_wait_int(int pos, TickType_t wait)

  Wait for general purpose interrupt from host.

  备注

  this clears the interrupt at the same time.

  参数:

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

    - wait -- Time to wait before interrupt triggered.

  返回:

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

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。