# SDMMC 主机驱动

[English]

## 概述

ESP32-S3 的 SDMMC 主机外设共有两个卡槽，用于插入 SD 卡、连接 SDIO 设备或连接 eMMC 芯片，每个卡槽均可单独使用。

卡槽 `SDMMC_HOST_SLOT_0` 和 `SDMMC_HOST_SLOT_1` 都支持 1、4、8 线的 SD 接口，这些卡槽通过 GPIO 交换矩阵连接到 ESP32-S3 的 GPIO，即每个 SD 卡信号都可以使用任意 GPIO 连接。

## 支持的速率模式

SDMMC 主机驱动支持以下速率模式：

- 默认速率 (20 MHz)：对于 SD 卡，支持 1 线或 4 线传输；对于 3.3 V eMMC，支持 1 线、4 线或 8 线传输。

- 高速模式 (40 MHz)：对于 SD 卡，支持 1 线或 4 线传输；对于 3.3 V eMMC，支持 1 线、4 线或 8 线传输。

- 高速 DDR 模式 (40 MHz)：对于 3.3 V eMMC，支持 4 线传输。

当前尚不支持的速率模式：

- 高速 DDR 模式：不支持 8 线 eMMC 传输

## 使用 SDMMC 主机驱动

在大多数应用程序中，只有下列函数会被直接调用：

- `sdmmc_host_init()`

- `sdmmc_host_init_slot()`

- `sdmmc_host_deinit()`

其他函数将通过 `sdmmc_host_t` 结构体中的函数指针由 SD/MMC 协议层调用，例如：

- `sdmmc_host_set_bus_width()`

- `sdmmc_host_set_card_clk()`

- `sdmmc_host_do_transaction()`

## 配置总线宽度和频率

使用 `sdmmc_host_t` 和 `sdmmc_slot_config_t` 的默认初始化配置，即 `SDMMC_HOST_DEFAULT` 和 `SDMMC_SLOT_CONFIG_DEFAULT` 时，SDMMC 主机驱动会尝试以当前卡所支持的最大总线宽度进行通信（SD 卡为 4 线，eMMC 为 8 线），并使用 20 MHz 的通信频率。

在支持 40 MHz 频率通信的设计中，可以调整 `sdmmc_host_t` 结构体中的 `max_freq_khz` 字段，提升总线频率：

    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;

如需选择标准速率以外的特定频率，请根据所使用的 SD 接口（SDMMC 或 SDSPI）确定适当频率范围，并选择其中的任意值。然而，实际的时钟频率会由底层驱动程序计算，可能与你所需的值不同。

使用 SDMMC 接口时，`max_freq_khz` 即频率上限，因此最终的频率值应始终低于或等于该上限。而使用 SDSPI 接口时，驱动程序会提供最接近的适配频率，因此该值可以大于、等于或小于 `max_freq_khz`。

请配置 `sdmmc_slot_config_t` 的 `width` 字段，配置总线宽度。例如，配置 1 线模式的代码如下：

    sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT();
    slot.width = 1;

## 配置 GPIO

通过配置结构体 `sdmmc_slot_config_t`，ESP32-S3 的 SDMMC 主机可以根据需要，为每个信号配置任意的 GPIO 管脚。

例如，使用以下代码，可以将 GPIO 1-6 分别用于 CLK、CMD、D0-D3 信号：

    sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT();
    slot.clk = GPIO_NUM_1;
    slot.cmd = GPIO_NUM_2;
    slot.d0 = GPIO_NUM_3;
    slot.d1 = GPIO_NUM_4;
    slot.d2 = GPIO_NUM_5;
    slot.d3 = GPIO_NUM_6;

也可以配置 CD 和 WP 管脚。与配置其他信号的方法类似，你只需配置相同结构体的 `cd` 和 `wp` 参数：

    slot.cd = GPIO_NUM_7;
    slot.wp = GPIO_NUM_8;

`SDMMC_SLOT_CONFIG_DEFAULT` 将 CD 和 WP 管脚都配置为 `GPIO_NUM_NC`，表明默认情况下不会使用这两个管脚。

通过上述方式初始化 `sdmmc_slot_config_t` 结构体后，即可在调用 `sdmmc_host_init_slot()` 或其他任意高层函数（如 `esp_vfs_fat_sdmmc_mount()`）时使用该结构体。

## eMMC 芯片的 DDR 模式

默认情况下，如果满足以下条件，将使用 DDR 模式：

- 在 `sdmmc_host_t` 结构体中将 SDMMC 主机频率配置为 `SDMMC_FREQ_HIGHSPEED`，且

- eMMC 芯片在其 CSD 寄存器中报告支持 DDR 模式

DDR 模式对信号完整性要求更高。如果要在保持 `SDMMC_FREQ_HIGHSPEED` 频率的同时禁用 DDR 模式，请在 `sdmmc_host_t` 结构体的 `sdmmc_host_t::flags` 字段中清除 `SDMMC_HOST_FLAG_DDR` 位：

    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
    host.flags &= ~SDMMC_HOST_FLAG_DDR;

## 相关文档

- SD/SDIO/MMC 驱动程序：介绍了实现协议层的高层驱动程序。

- SD SPI 主机驱动程序：介绍了一种类似驱动，该驱动使用 SPI 控制器且受限于 SD 协议的 SPI 模式。

- SD 上拉需求 介绍了模组和开发套件上的上拉支持和兼容信息。

## API 参考

### Header File

- components/esp_driver_sdmmc/legacy/include/driver/sdmmc_host.h

- This header file can be included with:

  >     #include "driver/sdmmc_host.h"

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

  >     REQUIRES esp_driver_sdmmc

  > or

  >     PRIV_REQUIRES esp_driver_sdmmc

### Functions

esp_err_t sdmmc_host_init(void)

  Initialize SDMMC host peripheral.

  备注

  This function is not thread safe

  返回:

    - ESP_OK on success or if sdmmc_host_init was already initialized with this function

    - ESP_ERR_NO_MEM if memory can not be allocated

esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)

  Initialize given slot of SDMMC peripheral.

  On the ESP32, SDMMC peripheral has two slots:

  - Slot 0: 8-bit wide, maps to HS1_* signals in PIN MUX

  - Slot 1: 4-bit wide, maps to HS2_* signals in PIN MUX

  Card detect and write protect signals can be routed to arbitrary GPIOs using GPIO matrix.

  备注

  This function is not thread safe

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - slot_config -- additional configuration for the slot

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if host has not been initialized using sdmmc_host_init

    - ESP_ERR_INVALID_ARG if GPIO pins from slot_config are not valid

esp_err_t sdmmc_host_set_bus_width(int slot, size_t width)

  Select bus width to be used for data transfer.

  SD/MMC card must be initialized prior to this command, and a command to set bus width has to be sent to the card (e.g. SD_APP_SET_BUS_WIDTH)

  备注

  This function is not thread safe

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - width -- bus width (1, 4, or 8 for slot 0; 1 or 4 for slot 1)

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if slot number or width is not valid

size_t sdmmc_host_get_slot_width(int slot)

  Get bus width configured in `sdmmc_host_init_slot` to be used for data transfer.

  参数:

    slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

  返回:

    configured bus width of the specified slot.

esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)

  Set card clock frequency.

  Currently only integer fractions of 40MHz clock can be used. For High Speed cards, 40MHz can be used. For Default Speed cards, 20MHz can be used.

  备注

  This function is not thread safe

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - freq_khz -- card clock frequency, in kHz

  返回:

    - ESP_OK on success

    - other error codes may be returned in the future

esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled)

  Enable or disable DDR mode of SD interface.

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - ddr_enabled -- enable or disable DDR mode

  返回:

    - ESP_OK on success

    - ESP_ERR_NOT_SUPPORTED if DDR mode is not supported on this slot

esp_err_t sdmmc_host_set_cclk_always_on(int slot, bool cclk_always_on)

  Enable or disable always-on card clock When cclk_always_on is false, the host controller is allowed to shut down the card clock between the commands. When cclk_always_on is true, the clock is generated even if no command is in progress.

  参数:

    - slot -- slot number

    - cclk_always_on -- enable or disable always-on clock

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if the slot number is invalid

esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)

  Send command to the card and get response.

  This function returns when command is sent and response is received, or data is transferred, or timeout occurs.

  Attention

    Data buffer passed in cmdinfo->data must be in DMA capable memory and aligned to 4 byte boundary. If it's behind the cache, both cmdinfo->data and cmdinfo->buflen need to be aligned to cache line boundary.

  备注

  This function is not thread safe w.r.t. init/deinit functions, and bus width/clock speed configuration functions. Multiple tasks can call sdmmc_host_do_transaction as long as other sdmmc_host_* functions are not called.

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - cmdinfo -- pointer to structure describing command and data to transfer

  返回:

    - ESP_OK on success

    - ESP_ERR_TIMEOUT if response or data transfer has timed out

    - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed

    - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response

    - ESP_ERR_INVALID_SIZE if the size of data transfer is not valid in SD protocol

    - ESP_ERR_INVALID_ARG if the data buffer is not in DMA capable memory

esp_err_t sdmmc_host_io_int_enable(int slot)

  Enable IO interrupts.

  This function configures the host to accept SDIO interrupts.

  参数:

    slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

  返回:

    returns ESP_OK, other errors possible in the future

esp_err_t sdmmc_host_io_int_wait(int slot, uint32_t timeout_ticks)

  Block until an SDIO interrupt is received, or timeout occurs.

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - timeout_ticks -- number of RTOS ticks to wait for the interrupt

  返回:

    - ESP_OK on success (interrupt received)

    - ESP_ERR_TIMEOUT if the interrupt did not occur within timeout_ticks

esp_err_t sdmmc_host_deinit_slot(int slot)

  Disable SDMMC host and release allocated resources gracefully.

  备注

  If there are more than 1 active slots, this function will just decrease the reference count and won't actually disable the host until the last slot is disabled

  备注

  This function is not thread safe

  参数:

    slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if SDMMC host has not been initialized

    - ESP_ERR_INVALID_ARG if invalid slot number is used

esp_err_t sdmmc_host_deinit(void)

  Disable SDMMC host and release allocated resources forcefully.

  备注

  This function will deinitialize the host immediately, regardless of the number of active slots

  备注

  This function is not thread safe

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if SDMMC host has not been initialized

esp_err_t sdmmc_host_get_real_freq(int slot, int *real_freq_khz)

  Provides a real frequency used for an SD card installed on specific slot of SD/MMC host controller.

  This function calculates real working frequency given by current SD/MMC host controller setup for required slot: it reads associated host and card dividers from corresponding SDMMC registers, calculates respective frequency and stores the value into the 'real_freq_khz' parameter

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - real_freq_khz -- [out] output parameter for the result frequency (in kHz)

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG on real_freq_khz == NULL or invalid slot number used

esp_err_t sdmmc_host_set_input_delay(int slot, sdmmc_delay_phase_t delay_phase)

  set input delay

  - This API sets delay when the SDMMC Host samples the signal from the SD Slave.

  - This API will check if the given `delay_phase` is valid or not.

  - This API will print out the delay time, in picosecond (ps)

  备注

  ESP32 doesn't support this feature, you will get an `ESP_ERR_NOT_SUPPORTED`

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - delay_phase -- delay phase, this API will convert the phase into picoseconds and print it out

  返回:

    - ESP_OK: ON success.

    - ESP_ERR_INVALID_ARG: Invalid argument.

    - ESP_ERR_NOT_SUPPORTED: ESP32 doesn't support this feature.

esp_err_t sdmmc_host_set_input_delayline(int slot, sdmmc_delay_line_t delay_line)

  set input delayline

  - This API sets delay when the SDMMC Host samples the signal from the SD Slave.

  - This API will check if the given `delay_line` is valid or not.

  - This API will print out the delay time, in picosecond (ps)

  参数:

    - slot -- slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - delay_line -- delay line, this API will convert the line into picoseconds and print it out

  返回:

    - ESP_OK: ON success.

    - ESP_ERR_INVALID_ARG: Invalid argument.

    - ESP_ERR_NOT_SUPPORTED: Some chips don't support this feature.

bool sdmmc_host_check_buffer_alignment(int slot, const void *buf, size_t size)

  Check if the buffer meets the alignment requirements.

  参数:

    - slot -- [in] slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)

    - buf -- [in] buffer pointer

    - size -- [in] buffer size

  返回:

    True for aligned buffer, false for not aligned buffer

esp_err_t sdmmc_host_is_slot_set_to_uhs1(int slot, bool *is_uhs1)

  Check if the slot is set to uhs1 or not.

  参数:

    - slot -- [in] Slot id

    - is_uhs1 -- [out] Is uhs1 or not

  返回:

    - ESP_OK: on success

    - ESP_ERR_INVALID_STATE: driver not in correct state

esp_err_t sdmmc_host_get_state(sdmmc_host_state_t *state)

  Get the state of SDMMC host.

  参数:

    state -- [out] output parameter for SDMMC host state structure

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG on invalid argument

### Structures

struct sdmmc_slot_config_t

  Extra configuration for SDMMC peripheral slot

  Public Members

  gpio_num_t clk

    GPIO number of CLK signal.

  gpio_num_t cmd

    GPIO number of CMD signal.

  gpio_num_t d0

    GPIO number of D0 signal.

  gpio_num_t d1

    GPIO number of D1 signal.

  gpio_num_t d2

    GPIO number of D2 signal.

  gpio_num_t d3

    GPIO number of D3 signal.

  gpio_num_t d4

    GPIO number of D4 signal. Ignored in 1- or 4- line mode.

  gpio_num_t d5

    GPIO number of D5 signal. Ignored in 1- or 4- line mode.

  gpio_num_t d6

    GPIO number of D6 signal. Ignored in 1- or 4- line mode.

  gpio_num_t d7

    GPIO number of D7 signal. Ignored in 1- or 4- line mode.

  gpio_num_t gpio_cd

    GPIO number of card detect signal.

  gpio_num_t cd

    GPIO number of card detect signal; shorter name.

  gpio_num_t gpio_wp

    GPIO number of write protect signal.

  gpio_num_t wp

    GPIO number of write protect signal; shorter name.

  uint8_t width

    Bus width used by the slot (might be less than the max width supported)

  uint32_t flags

    Features used by this slot.

struct sdmmc_host_state_t

  SD/MMC host state structure

  Public Members

  bool host_initialized

    Whether the host is initialized.

  int num_of_init_slots

    Number of initialized slots.

### Macros

SDMMC_SLOT_FLAG_INTERNAL_PULLUP

  Enable internal pullups on enabled pins. The internal pullups are insufficient however, please make sure external pullups are connected on the bus. This is for debug / example purpose only.

SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH

  GPIO write protect polarity. 0 means "active low", i.e. card is protected when the GPIO is low; 1 means "active high", i.e. card is protected when GPIO is high.

SDMMC_SLOT_FLAG_UHS1

  Enable UHS-I mode for this slot

### Header File

- components/sdmmc/include/sd_pwr_ctrl.h

- This header file can be included with:

  >     #include "sd_pwr_ctrl.h"

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

  >     REQUIRES sdmmc

  > or

  >     PRIV_REQUIRES sdmmc

### Functions

esp_err_t sd_pwr_ctrl_set_io_voltage(sd_pwr_ctrl_handle_t handle, int voltage_mv)

  Set SD IO voltage by a registered SD power control driver handle.

  参数:

    - handle -- [in] SD power control driver handle

    - voltage_mv -- [in] Voltage in mV

### Type Definitions

typedef struct sd_pwr_ctrl_drv_t *sd_pwr_ctrl_handle_t

  SD power control handle.

### Header File

- components/sdmmc/include/sd_pwr_ctrl_by_on_chip_ldo.h

- This header file can be included with:

  >     #include "sd_pwr_ctrl_by_on_chip_ldo.h"

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

  >     REQUIRES sdmmc

  > or

  >     PRIV_REQUIRES sdmmc

### Functions

esp_err_t sd_pwr_ctrl_new_on_chip_ldo(const sd_pwr_ctrl_ldo_config_t *configs, sd_pwr_ctrl_handle_t *ret_drv)

  New an SD power control driver via on-chip LDO.

  参数:

    - configs -- [in] On-chip LDO power control driver configurations

    - ret_drv -- [out] Created power control driver handle

  返回:

    - ESP_OK

    - ESP_ERR_INVALID_ARG Invalid arguments

    - ESP_ERR_NO_MEM Out of memory

esp_err_t sd_pwr_ctrl_del_on_chip_ldo(sd_pwr_ctrl_handle_t ctrl_handle)

  Delete a previously created on-chip LDO power control driver.

  参数:

    ctrl_handle -- [in] Power control driver handle

  返回:

    - ESP_OK

    - ESP_ERR_INVALID_ARG Invalid arguments

### Structures

struct sd_pwr_ctrl_ldo_config_t

  LDO configurations.

  Public Members

  int ldo_chan_id

    On-chip LDO channel ID, e.g. set to `4` is the `LDO_VO4` is connected to power the SDMMC IO.

此文档对您有帮助吗？

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

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