ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ESP-IDF 中 esp_driver_sdmmc:ESP32 平台 SDMMC 主机驱动组件的结构、接口与源码级解析

ESP-IDF 中 esp_driver_sdmmc:ESP32 平台 SDMMC 主机驱动组件的结构、接口与源码级解析 ESP-IDF 中 esp_driver_sdmmcESP32 平台 SDMMC 主机驱动组件的结构、接口与源码级解析【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idfESP-IDF 通过sdmmc、esp_driver_sdmmc、esp_driver_sdspi三个组件协作完成 SD 卡SD 模式通信。本文以esp_driver_sdmmc组件为中心讲清它在 SD Host 侧组件架构中的定位、提供的两级 API 接口并结合驱动源码剖析控制器创建、时钟分频、总线位宽与 DMA 传输等关键实现帮助你在 ESP32 / ESP32-P4 / ESP32-S3 等 SoC 上正确接入 SD/SDIO 设备并排查总线配置问题。组件定位SD Host 侧三组件架构esp_driver_sdmmc组件的 README 开宗明义SD 主机侧相关的组件共有三个——sdmmc、esp_driver_sdmmc本组件与esp_driver_sdspi。三者的关系在 SD Host 侧组件架构说明 中给出┌─────────────────────────────────────────────┐ │ SD Protocol Layer Driver: components/sdmmc │ └──────────▲────────────────────▲─────────────┘ │ │ Implements │ │ Implements ┌──────────────────────────────────┴─────┐ ┌────┴───────────────────────────────────┐ │Host Driver: components/esp_driver_sdmmc│ │Host Driver: components/esp_driver_sdspi│ └────────────────────────────────────────┘ └────────────────────────────────────────┘各组件职责与依赖方向如下来自 components/sdmmc/README.mdsdmmcSD 协议层驱动提供 SD 协议相关的定义与接口。配合相应实现驱动sdmmcAPI 可帮助你发送命令、收发数据、处理总线上的错误条件esp_driver_sdmmc面向 ESP SDMMC 硬件外设的 SDMMC 主机驱动实现sdmmc协议接口esp_driver_sdspi面向 ESP GPSPI 硬件的 SDSPI 主机驱动同样实现sdmmc协议接口。依赖关系上esp_driver_sdmmc与esp_driver_sdspi同属驱动层G2都依赖sdmmc而sdmmc不依赖、也不应依赖任何一个主机驱动。这种协议层与硬件驱动层解耦的设计意味着上层应用只需面向sdmmc协议接口编程底层可在 SDMMC 总线并行与 SPI 总线之间切换。esp_driver_sdmmc提供的能力见 组件 README通过 ESP SDMMC 外设完成 SD 事务SD 模式下的命令与数据传输调节 ESP SDMMC 硬件配置例如时钟频率、总线位宽等。文档明确给出了两条使用路径这也是理解本组件的钥匙用本驱动实现sdmmc协议接口——即走sdmmc组件的协议层 API由esp_driver_sdmmc在底层落地直接使用esp_driver_sdmmc的 API——绕过协议层直接操作主机控制器与插槽。组件组成与构建依赖从 CMakeLists.txt 可以看到组件的实际构成仅在CONFIG_SOC_SDMMC_HOST_SUPPORTED为真时才编译源文件legacy/src/sdmmc_transaction.c、legacy/src/sdmmc_host.c、src/sd_host_sdmmc.c、src/sd_trans_sdmmc.c公共头文件目录为include与legacy/include——legacy/目录承载了传统的sdmmc_host_*一族 APIinclude/driver/下则是新的sd_host_sdmmc低层 API依赖声明为REQUIRES esp_driver_sd_intf sdmmcPRIV_REQUIRES esp_timer esp_pm esp_mm esp_driver_gpio esp_hal_sd印证了它上承协议层、下接 HAL的位置。组件的 Kconfig 提供一项配置CONFIG_SD_HOST_SDMMC_ISR_CACHE_SAFE默认关闭使 SD Host SDMMC 驱动的 ISR 变为 Cache-Safe即缓存被禁用时中断处理函数仍然可用。相关的另一个重要选项属于协议层sdmmc组件——CONFIG_SD_ENABLE_SDIO_SUPPORT默认开启SDMMC 协议层 README 建议当你的卡是**纯存储卡Memory-only而非同时具备 I/O 功能与存储的组合卡Combo card**时可将其置为n以关闭 SDIO 支持。该协议层组件声明的适用目标为 ESP32、ESP32-P4、ESP32-S3而本组件的测试应用 test_apps/sdmmc/README.md 中列出的支持目标还包括 ESP32-S31。是否可用最终以对应芯片的SOC_SDMMC_HOST_SUPPORTED能力宏为准。两级 API协议层接口与低层主机接口低层主机接口driver/sd_host_sdmmc.hinclude/driver/sd_host_sdmmc.h 定义了控制器与插槽两级配置结构及创建函数/** SD Host SDMMC 控制器配置 */ typedef struct { uint32_t event_queue_items; // 事件队列项数0 时回退为默认值 4 uint32_t dma_desc_num; // DMA 描述符数量0 时回退为默认值 4 } sd_host_sdmmc_cfg_t; /** SD Host 插槽 IO 配置 */ typedef struct { sd_bus_width_t width; // 插槽总线位宽 gpio_num_t clk_io; // 时钟 gpio_num_t cmd_io; // 命令 gpio_num_t cd_io; // 卡检测 gpio_num_t wp_io; // 写保护 gpio_num_t d0_io; // 数据0 /* ... d1_io ~ d7_io 依次类推 ... */ } sd_host_sdmmc_slot_io_cfg_t; /** SD Host 插槽初始化配置 */ typedef struct { int slot_id; // 插槽 ID sd_mode_t sd_mode; // SD 模式 sd_host_sdmmc_slot_io_cfg_t io_config; // IO 配置 struct { uint32_t internal_pullup: 1; // 是否启用内部上拉 uint32_t wp_active_high: 1; // WP 信号是否高电平有效 } slot_flags; } sd_host_slot_sdmmc_init_cfg_t;对应的两个核心 API 及错误码约定esp_err_t sd_host_create_sdmmc_controller(const sd_host_sdmmc_cfg_t *config, sd_host_ctlr_handle_t *ret_handle); // ESP_OK / ESP_ERR_NO_MEM内存不足/ ESP_ERR_NOT_FOUND无可用控制器/ ESP_ERR_INVALID_ARG esp_err_t sd_host_sdmmc_controller_add_slot(sd_host_ctlr_handle_t ctlr, const sd_host_slot_sdmmc_init_cfg_t *config, sd_host_slot_handle_t *ret_handle); // ESP_OK / ESP_ERR_INVALID_STATE插槽不可用/ ESP_ERR_INVALID_ARG / ESP_ERR_NO_MEM头文件还定义了SD_HOST_SLOT_WIDTH_DEFAULT 0——位宽取 0 表示使用该插槽支持的最大位宽。传统接口legacy/include/driver/sdmmc_host.hlegacy/目录保留了完整的sdmmc_host_*传统 APIsdmmc_host.h这也是历史上最常用的 SDMMC 主机接口主要函数包括sdmmc_host_init(void)/sdmmc_host_deinit(void)/sdmmc_host_deinit_slot(int slot)初始化与反初始化主机非线程安全sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)初始化指定插槽。文档注释说明了 ESP32 上 SDMMC 外设有两个插槽——插槽 0 为 8 位宽映射 PIN MUX 的 HS1_* 信号、插槽 1 为 4 位宽HS2_* 信号卡检测与写保护信号可经 GPIO 矩阵路由到任意 GPIOsdmmc_host_set_bus_width(int slot, size_t width)/sdmmc_host_get_slot_width(int slot)选择/查询总线位宽插槽 0 支持 1/4/8插槽 1 支持 1/4sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)设置卡时钟频率kHz并可通过sdmmc_host_get_real_freq读取实际生效频率sdmmc_host_set_bus_ddr_mode、sdmmc_host_set_cclk_always_onDDR 模式与常开卡时钟控制sdmmc_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)向卡发送命令并获取响应/传输数据。其注意事项明确指出数据缓冲区必须位于 DMA 可用内存且 4 字节对齐若缓冲区在缓存之后缓冲区指针与长度均需按缓存行对齐sdmmc_host_io_int_enable/sdmmc_host_io_int_wait使能 SDIO 中断并阻塞等待支持超时sdmmc_host_set_input_delay/sdmmc_host_set_input_delayline设置采样输入相位延迟/延迟线ESP32 不支持相位延迟返回ESP_ERR_NOT_SUPPORTEDsdmmc_host_check_buffer_alignment、sdmmc_host_is_slot_set_to_uhs1、sdmmc_host_get_state缓冲区对齐检查、UHS-I 状态查询与主机状态查询。sdmmc_slot_config_t中的关键标志位包括SDMMC_SLOT_FLAG_INTERNAL_PULLUPBIT(0)使能内部上拉仅供调试用途仍需外部上拉、SDMMC_SLOT_FLAG_WP_ACTIVE_HIGHBIT(1)WP 极性、SDMMC_SLOT_FLAG_UHS1BIT(2)使能 UHS-I 模式。源码剖析控制器创建与插槽初始化控制器创建流程sd_host_sdmmc.c 中sd_host_create_sdmmc_controller的实现约 L74-L144揭示了驱动的资源模型平台级控制器占位静态结构sd_platform_t维护一个大小为SDMMC_LL_HOST_CTLR_NUMS的控制器数组sd_host_claim_controller扫描并占用第一个空闲控制器开启其总线时钟并做寄存器复位DMA 与事件资源分配按配置或默认 4 个分配 DMA 描述符表、事件队列、IO 中断二值信号量与互斥锁中断注册通过esp_intr_alloc(ETS_SDIO_HOST_INTR_SOURCE, ...)安装sd_host_isr电源管理锁在CONFIG_PM_ENABLE下创建ESP_PM_APB_FREQ_MAX锁防止深度睡眠期间 APB 降频影响总线HAL 初始化与复位sdmmc_hal_init后启用SDMMC_CLK_SRC_DEFAULT时钟源设置默认分频并执行控制器/ DMA / FIFO 复位。插槽注册与 vtable 挂载sd_host_sdmmc_controller_add_slot约 L146-L211在挂载插槽时做了两件事值得注意将插槽初始频率设为400 kHzSD 规范规定的探针频率并置状态为SD_HOST_SLOT_STATE_INIT向插槽的驱动 vtable 挂接一批函数指针约 L175-L182configure、do_transaction、register_cbs、remove_slot、set_cclk_always_on、enable_io_int、wait_io_int、get_info。从源码结构看这正是组件 README 所说实现sdmmc协议接口的落点——协议层通过这组函数指针驱动硬件而不直接依赖具体实现。此外当第一个插槽注册时驱动会打开全局中断SDMMC_LL_EVENT_DEFAULT在最后一个插槽移除时再关闭并清除全部中断。时钟分频两级分频器算法文档提到调节时钟频率是驱动核心能力之一。源码中约 L1065-L1187对时钟体系有非常清晰的注释与实现SDMMC 时钟由两级分频构成——第一级驱动 SDMMC 外设的时钟发生器SDMMC.clock 寄存器产生160MHz/(N1)级别的频率第二级外设内部 4 个卡时钟分频器按2*M0 M 255再分频可旁路。sd_host_slot_get_clk_dividers的选频策略为目标频率 ≤ 总除法 ≤ 16 时只动第一级否则进入两级模式遍历host_div2..16取使中间频率最高、且card_div落在 1..255 内的组合以获得最佳精度。对 UHS-I 场景则走特化分支SDR104 时若芯片支持SDMMC_CLK_SRC_SDIO_200M则直接选用该 200MHz 时钟源freq_khz SDMMC_FREQ_HIGHSPEED时SDR50 用 100MHz 分频。注释同时说明了背景知识非 UHS-1 卡在高速模式最高 50 MHz默认速度模式必选最高 25 MHz是否支持 HS 需读卡 CSD 的TRAN_SPEED字段判断。sd_host_slot_set_card_clk约 L588-L639展示了实际改频流程先关卡时钟 → 计算并写入分频 → 发时钟更新命令 → 重新使能时钟 → 按 100ms 数据超时设定data_timeout并将响应超时固定为最大值 255。硬件能力差异化的运行时校验sd_host_slot_sdmmc_configure约 L216-L290展示了驱动如何按芯片能力做参数校验这是调硬件配置能力的边界说明不支持SDMMC_LL_DELAY_PHASE_SUPPORTED的芯片如 ESP32将相位延迟回退为 0 并打印告警不支持 UHS-I 的芯片!SOC_SDMMC_UHS_I_SUPPORTED拒绝延迟线配置不支持 SDR104/DDR50/SDR50 的芯片会分别拦截超过对应频率阈值的 SDR/DDR 请求针对 ESP32-P4若芯片修订版本低于 v3.0SDR104 直接返回ESP_ERR_NOT_SUPPORTED。引脚路由与 IO 配置sdmmc_slot_io_config约 L1276-L1463实现了几条实用规则排板时值得对照默认走专用 IO MUX若所有数据引脚都未指定s_check_pin_not_set为真驱动直接使用芯片专用引脚iomux否则经 GPIO 矩阵路由。部分芯片插槽如某些芯片的插槽 1不支持 GPIO 矩阵路由会返回ESP_ERR_INVALID_ARGD3 强制高电平技巧非 UHS-I 模式下D3 先被置为 GPIO 高电平以迫使从设备进入 SD 模式待位宽配置完成后再连接到外设内部上拉slot_flags.internal_pullup使能时对 CMD/D0及位宽扩展后的 D1~D7开启内部上拉但不拉 CLK规范中时钟由主机驱动CD / WP 默认值未配置 CD 时接常量 0视为卡始终在位未配置 WP 时接常量 1视为未写保护WP 若为低电平有效则在 GPIO 矩阵中反相因为硬件期望高电平有效信号。事务处理、DMA 与中断事务状态机在 sd_trans_sdmmc.c 中实现内部枚举sdmmc_req_state_t覆盖IDLE / SENDING_CMD / SENDING_DATA / BUSY / SENDING_VOLTAGE_SWITCH / WAITING_VOLTAGE_SWITCH等状态UHS-I 的 1.8V 压摆切换CMD11被拆分为三个阶段处理开常开时钟 → 关时钟、调用volt_switch_cb切到 1800mV、等待 10ms 再恢复时钟 → 完成后时钟回到低功耗模式。DMA 采用描述符链 生产者/消费者模型ISR 中收到SDMMC_LL_EVENT_DMA_NI需要填充事件时统计空闲描述符数并调用sd_host_fill_dma_descriptors补充新数据sd_host_sdmmc.c 约 L889-L952 的sd_host_isr由于描述符表可能位于缓存内存填充/统计前后会做esp_cache_msync同步中断完成后事件被xQueueSendFromISR送入事件队列并可调用用户注册的on_trans_done回调SDIO 中断则通过io_intr_sem二值信号量传递给sdmmc_host_io_int_wait一类的等待逻辑缓冲区约束由sd_host_check_buffer_alignment约 L544-L586固化缓冲区必须整体 DMA 可达、首尾不得跨内存类型且按缓存行或SDMMC_DMA_ALIGNMENT对齐——这与legacyAPI 文档中的注意事项完全一致也是实际开发中最容易踩的坑。命令发起前的检查sd_host_slot_start_command约 L957 起还包括未检测到卡且非时钟更新命令时返回ESP_ERR_NOT_FOUND写命令遇到写保护返回ESP_ERR_INVALID_STATE等待命令被硬件接收时采用指数退避式让出起始 100ms 量级的yield_delay_us每次翻倍后vTaskDelay(1)避免长时间自旋。测试验证方式组件自带完整的回归测试位于 test_apps/sdmmc用例集合components/sdmmc_tests/覆盖探针probe、初始化、读写rw、擦除erase、discard/trim、CD/WP 处理、sanitize 与杂项命令等流程其中公共测试流common_test_flows/与测试板卡抽象sdmmc_test_boards/被复用于不同目标主程序main/test_app_main.c通过 pytest 驱动pytest_sdmmc.py在不同芯片的默认配置sdkconfig.defaults、sdkconfig.defaults.esp32、sdkconfig.defaults.esp32p4、sdkconfig.defaults.esp32s3等上运行。阅读这些测试是了解驱动初始化 → 卡探测 → 事务标准时序的最快途径也是验证你自定义引脚/位宽/时钟配置是否可行的参照。小结esp_driver_sdmmc是 ESP-IDF SD 主机侧架构中协议层sdmmc与ESP SDMMC 硬件之间的桥梁组件它既可以作为sdmmc协议接口的硬件实现被间接使用也通过sdmmc_host_*legacy与sd_host_sdmmc低层两族 API 直接暴露主机控制器与插槽的配置能力。其源码在时钟两级分频、UHS-I 能力校验、D3 强制高电平进入 SD 模式、DMA 描述符环与缓存同步、CD/WP 默认路由等细节上的处理覆盖了 SD 总线开发中最常见的配置与稳定性问题是调试 SD 卡通信问题的第一现场。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表