十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ESP-IDF 存储控制器 HAL 层解析:esp_hal_sd 组件中的 SDMMC 主机与 SDIO 从机硬件抽象

ESP-IDF 存储控制器 HAL 层解析:esp_hal_sd 组件中的 SDMMC 主机与 SDIO 从机硬件抽象 ESP-IDF 存储控制器 HAL 层解析esp_hal_sd 组件中的 SDMMC 主机与 SDIO 从机硬件抽象【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idfesp_hal_sd是 ESP-IDF 中面向 SD/MMC 与 SDIO 外设的硬件抽象层Hardware Abstraction Layer组件为SDMMC Host ControllerSD 主机控制器与SDIO Slave ControllerSDIO 从机控制器提供统一的操作封装。本文以 components/esp_hal_sd/README.md 为骨架结合组件源码深入讲解其 HAL 分层设计、两类控制器的核心抽象模型与典型调用流程帮助开发者在阅读 IDF 外设驱动或自研底层驱动时快速建立对该 HAL 的整体认知。⚠️稳定性警告原文档明确声明该 HAL 组件当前仍处于重度开发阶段官方不保证各版本间的接口稳定性与向后兼容性。普通应用开发应使用其上层的 IDF 外设驱动如sdmmc、sdio_slave驱动而非直接调用 HAL 接口只有希望自行实现驱动的高级开发者才建议直接使用本层函数。组件定位为谁服务服务什么esp_hal_sd组件的核心职责是为 ESP-IDF 支持的所有芯片目标target提供两种外设的硬件抽象SDMMC Host Controller负责与 SD 卡、eMMC、SDIO 设备如 WiFi/蓝牙模块通信的主机端控制器SDIO Slave Controller让 ESP 芯片自身以 SDIO 从机身份接入外部主机如 Linux 主机、另一颗 MCU实现主机对从机的寄存器访问、中断通知与批量数据传输。在 ESP-IDF 的分层体系中本组件处于外设驱动Driver之下、寄存器层soc之上的中间位置其产出被esp_driver_sdmmc、sdmmc、sdio_slave等上层驱动消费。HAL 双层架构HAL上层与 Low-Level底层原文档指出广义上的 HAL 层由两个子层构成这也是 ESP-IDF 近几代 HAL 重构后的通用组织方式HAL上层定义操作外设所需的步骤与数据例如初始化流程、参数设置、上下文context结构等。它面向如何操作这个外设这一高层语义。Low-Level底层_ll后缀位于soc组件寄存器文件之上的翻译层只负责把通用概念翻译为具体的寄存器配置例如把使能 DMA翻译为对某个状态寄存器写入特定位。这种分层带来的直接收益是上层 HAL 可以做到跨芯片尽量通用而芯片差异被收敛在_ll层新增芯片支持时主要工作量落在 LL 层。在esp_hal_sd中底层 LL 实现按芯片分目录存放例如 esp32/include/hal/sdmmc_ll.h、esp32p4/include/hal/sdmmc_ll.h。SDMMC Host HAL上下文、初始化与电源管理上下文结构HAL 以上下文 显式初始化的方式组织状态。sdmmc_hal.h 定义了最小化的上下文结构typedef struct sdmmc_dev_t *sdmmc_soc_handle_t; typedef struct { sdmmc_soc_handle_t dev; // SDMMC SOC layer handle (即寄存器基地址) } sdmmc_hal_context_t;上下文只保存一个dev句柄即寄存器基地址其余状态由上层驱动自行管理——这是典型的轻量 HAL 设计HAL 不复制驱动的全部状态只封装拿到硬件、释放硬件的步骤。初始化 / 反初始化sdmmc_hal.c 给出了两个入口函数的实际行为void sdmmc_hal_init(sdmmc_hal_context_t *hal) { hal-dev SDMMC_LL_GET_HW(0); sdmmc_ll_mem_power_by_pmu(hal-dev); sdmmc_ll_mem_set_low_power_mode(hal-dev, SDMMC_LL_MEM_LP_MODE_SHUT_DOWN); } void sdmmc_hal_deinit(sdmmc_hal_context_t *hal) { sdmmc_ll_mem_force_low_power(hal-dev); hal-dev NULL; }可以看到sdmmc_hal_init除了取硬件句柄外还额外完成了电源管理PMU配置先通过sdmmc_ll_mem_power_by_pmu让 SDMMC 内存由 PMU 供电再设置低功耗模式下内存进入关闭状态sdmmc_hal_deinit则强制关闭 SDMMC 电源域并清空句柄。这三个 LL 函数定义在 esp32/include/hal/sdmmc_ll.h第 173–194 行附近与 esp32p4/include/hal/sdmmc_ll.h 等目标文件中印证了上层 HAL 定义步骤、底层 LL 翻译为寄存器操作的分工。共享类型定义sd_types.h 提供了 SDMMC/SDIO 主机侧共用的基础类型上层驱动配置参数时经常直接引用总线宽度sd_bus_width_tSD_BUS_WIDTH_1_BIT(1)、SD_BUS_WIDTH_4_BIT(4)、SD_BUS_WIDTH_8_BIT(8)工作模式sd_mode_tSD_MODE_NORMAL普通模式、SD_MODE_UHS1UHS-I 模式采样模式sd_sampling_mode_tSD_SAMPLING_MODE_SDR(1)单数据率、SD_SAMPLING_MODE_DDR双数据率时钟延时相位sdmmc_delay_phase_tSDMMC_DELAY_PHASE_0~SDMMC_DELAY_PHASE_7及SDMMC_DELAY_PHASE_AUTO仅 UHS-I 模式有效。该参数仅在主机工作于SDMMC_FREQ_HIGHSPEED、SDMMC_FREQ_52M、SDR50、DDR50、SDR104 等高速档位时生效驱动会打印实际延时单位皮秒 ps时钟延时线sdmmc_delay_line_tSDMMC_DELAY_LINE_0~SDMMC_DELAY_LINE_7及SDMMC_DELAY_LINE_AUTO。此外该头文件还给出两个实用宏SDMMC_DATA_SIG_NUM数据信号数取自SOC_SDMMC_DATA_WIDTH_MAX未定义时为 0与SDMMC_DMA_ALIGNMENTDMA 对齐要求为 4 字节。槽位与引脚信息主机侧还通过 sdmmc_periph.h 暴露外设的静态描述数据sdmmc_slot_info_t每个槽位的最大总线宽度1/4/8以及 Card Detect、Write Protect、Card Interrupt 信号在 GPIO Matrix 中的编号sdmmc_slot_gpio_num[]/sdmmc_slot_gpio_sig[]按槽位给出 CLK/CMD/D0–D7 等 SD 总线信号的 GPIO 引脚号SOC_SDMMC_USE_IOMUX时或 GPIO Matrix 信号号SOC_SDMMC_USE_GPIO_MATRIX时。具体数值由各芯片目录下的sdmmc_periph.c提供如 esp32/sdmmc_periph.c、esp32s3/sdmmc_periph.c上层驱动据此完成引脚复用配置。SDIO Slave HAL三大独立子系统的抽象SDIO 从机是esp_hal_sd中内容最丰富的部分。原文档指出从机外设硬件上由三部分独立子系统组成HAL 也按此组织——这一设计描述完整记录在 sdio_slave_hal.h 的架构注释中寄存器与中断Register / Interrupt共享寄存器对从机内部总线直接可读写主机则通过 CMD52/53 按地址访问主机写命令中断从机时置位中断寄存器相应位从机需要中断主机时则写寄存器触发 DAT1 线上的中断信号。发送 FIFO外设提供计数器寄存器主机据此判断从机是否就绪并通过 CMD53 读写 FIFO。HAL 在初始化时重置计数器若主机侧重启或丢失计数应通知从机再次重置。接收 FIFO结构同发送侧方向相反。免拷贝的关键设计描述符队列为避免每次传输都在 FIFO 与内存缓冲间拷贝数据HAL 内部维护一个描述符队列以链表实现内存缓冲的描述符被排队后HAL 即取得该缓冲的所有权直到对应的完成函数成功返回。ISR 会被多次调用以迭代已排队的描述符并在缓冲释放时通知上层。发送侧描述符sdio_slave_hal_send_desc_t在 DMA 描述符基础上追加了pkt_len累积长度与arg用户参数两个字段接收侧sdio_slave_hal_recv_desc_t则直接复用 LL 层描述符。HAL 上下文sdio_slave_hal.h 中sdio_slave_context_t是使用 HAL 的核心结构其中需要手动配置的成员包括sending_mode发送模式取值见 sdio_slave_types.h 中的sdio_slave_sending_mode_t——SDIO_SLAVE_SEND_STREAM流模式尽量合并为一个包或SDIO_SLAVE_SEND_PACKET包模式逐个发送timing时序模式sdio_slave_timing_t默认推荐SDIO_SLAVE_TIMING_PSEND_PSAMPLEHS 模式默认值DS 模式通常也无问题no_highspeed位标志置 1 关闭高速支持send_queue_size发送队列中可排队缓冲的最大数量recv_buffer_size每个接收缓冲的大小需主机与从机预先协商一致send_desc_queue保存排队描述符的环形缓冲需先初始化好data与size。其余成员发送状态机、in-flight 链表头尾、接收 STAILQ 等为内部状态使用时无需触碰。发送描述符占用的空间大小由宏SDIO_SLAVE_SEND_DESC_SIZE给出。接收侧典型调用流程原文档给出接收的完整操作步骤实现位于 sdio_slave_hal.csdio_slave_hal_recv_start启动接收 DMA若已有缓冲则优先从它们开始接收sdio_slave_hal_recv_init_desc将缓冲地址与描述符关联HAL 会写入协商好的recv_buffer_sizesdio_slave_hal_load_buf装载接收缓冲——DMA 启动后描述符进入链表接收缓冲计数增加硬件自动按链表把数据写入各缓冲可选启用中断时必需sdio_slave_hal_recv_done检查并清除接收中断位sdio_slave_hal_recv_has_next_item检查是否存在已完成接收的缓冲对每次成功返回调用一次sdio_slave_hal_recv_unload_desc取回描述符可选DMA 停止时sdio_slave_hal_recv_reset_counter仅重置计数器到已装载未使用的缓冲数可选DMA 停止时递归调用sdio_slave_hal_recv_flush_one_buffer丢弃一个或多个缓冲中的数据并重新装载反初始化时可选DMA 停止时递归sdio_slave_hal_recv_unload_desc无条件取回所有缓冲。发送侧典型调用流程发送驱动未使用重启动特性流程为sdio_slave_hal_send_start启动发送 DMA已有数据则立即待发sdio_slave_hal_send_queue把数据排入发送队列启用中断时 ISR 被触发启用中断时必需清除软件调用逻辑使用的中断位sdio_slave_hal_send_new_packet_if_exist检查并发送新包ESP_OK表示 DMA 开始发送新包ESP_ERR_NOT_FOUND表示无包可发ESP_ERR_INVALID_STATE表示仍有包在传输中sdio_slave_hal_send_eof_happened检查上一包是否发送完成同时清除该事件中断位递归调用sdio_slave_hal_send_get_next_finished_arg取回已完成缓冲的arg参数反初始化时可选递归sdio_slave_hal_send_flush_next_buffer取回全部已排队缓冲可选sdio_slave_hal_send_reset_counter把计数器重置到已装载未发送的缓冲数。文档特别提醒执行第 2 步与第 6 步时应使用计数器配对确保队列空间充足且该 HAL 并非线程安全调用方需用自旋锁保证sdio_slave_hal_send_queue等函数不被并发调用。此外DMA 在传输期间已装载的描述符不可修改因此发送侧使用一个环形缓冲保存描述符、让硬件看到的始终是线性链表传输结束后由 ISR 修复链表环结构见 sdio_slave_hal.c 中 Send 章节的设计注释。主机交互Host 部分从机与外部主机之间的控制面交互由以下函数承担sdio_slave_hal_hostint_set_ena/sdio_slave_hal_hostint_get_ena使能/查询发往主机的各类中断主机可同时修改相同寄存器建议仅在初始化阶段使用sdio_slave_hal_hostint_send/sdio_slave_hal_hostint_clear触发通用目的中断或取消发往主机的所有中断这两个函数以并发安全方式实现从机可安全调用sdio_slave_hal_slvint_fetch_clear读取并清除主机发往从机的通用目的中断sdio_slave_hal_host_get_reg/sdio_slave_hal_host_set_reg读写主机与从机共享的通用寄存器位置 0–63但 24–27 除外非并发安全避免两侧同时写同一寄存器sdio_slave_hal_get_intr_status_reg获取中断状态寄存器地址。中断位掩码定义在 sdio_slave_types.hSDIO_SLAVE_HOSTINT_BIT0~BIT7为 8 个通用目的中断位SDIO_SLAVE_HOSTINT_SEND_NEW_PACKETBIT23表示有新包可发。构建与裁剪按芯片能力条件编译从 CMakeLists.txt 可以确认该组件的编译策略组件不支持 POSIX/Linux 模拟器target为linux时直接返回CONFIG_SOC_SDMMC_HOST_SUPPORTED使能时编译sdmmc_hal.c与对应芯片的sdmmc_periph.cCONFIG_SOC_SDIO_SLAVE_SUPPORTED使能时编译sdio_slave_hal.c与对应芯片的sdio_slave_periph.c组件REQUIRES soc hal头文件同时暴露公共include与按芯片追加的${target}/include目录。因此是否包含本组件的 SDMMC/SDIO 从机实现取决于目标芯片的 SOC 能力宏如SOC_SDMMC_HOST_SUPPORTED、SOC_SDIO_SLAVE_SUPPORTED这解释了目录结构中为何不同芯片只保留自己支持的 LL 头文件例如 esp32p4、esp32s3、esp32s31 只含 SDMMC 主机侧而 esp32c5、esp32c6 等只含 SDIO 从机侧esp32则两者兼备。使用建议与注意事项接口稳定性原文档反复强调本 HAL 不保证跨版本稳定性与向后兼容性直接依赖本层接口的代码在升级 ESP-IDF 时可能需要同步修改。使用边界本组件头文件顶部声明 The hal is not public api, dont use in application code即 HAL 并非公共 API不应在应用代码中直接使用它主要为 IDF 外设驱动提供抽象高级开发者自研驱动时可参考其调用模式如sdio_slave_hal.c中的 ISR 处理与环形描述符队列设计。参数一致性SDIO 从机场景中recv_buffer_size等参数必须与主机侧预先协商一致FIFO 计数器在主机重启后会失效需要从机重新复位HAL 初始化时会重置计数器。并发与状态机约束发送队列函数非线程安全需加锁reset_counter、flush系列函数只能在 DMA 停止时调用反初始化时取回缓冲同样必须在 DMA 停止后进行。如需深入可继续阅读组件公共头文件 sdio_slave_hal.h含完整架构注释与逐函数说明、sdmmc_hal.h 与 sd_types.h以及各芯片目录下的_ll底层实现。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表