恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
STM32H7 SDMMC实战:从CubeMX配置到FATFS文件系统完整指南
首页
资讯中心
/
STM32H7 SDMMC实战:从CubeMX配置到FATFS文件系统完整指南
STM32H7 SDMMC实战:从CubeMX配置到FATFS文件系统完整指南
发布时间:2026/8/29 6:13:59
1. 先说结论H7 的 SDMMC 和 F4 的 SDIO 不是一回事很多从 STM32F4 迁移到 STM32H7 的朋友第一次打开 H7 的参考手册都会愣一下怎么原来的 SDIO 控制器变成 SDMMC 了名字换了寄存器也变了连驱动代码都长得不太一样。我最早就是从 F4 直接跳到 H7拿旧工程的 SDIO 驱动改了半天结果卡在初始化阶段死活过不去最后老老实实把 H7 的 SDMMC 主机控制器从头啃了一遍才把这套外设摸透。这篇笔记就是把我啃下来的东西整理出来。它不是什么逐寄存器翻译手册而是围绕“STM32H7 的 SDMMC 到底怎么用”、“为什么这么配”、“实际跑起来会遇到哪些坑”来写的。SDMMC 主机控制器在 H7 上的定位很明确它是芯片内部负责和 SD 卡、microSD 卡、eMMC 通信的硬件模块支持从老式 SDSC 到 SDXC、从默认速度到 UHS-I 高速模式的完整协议栈。和 F4 的 SDIO 相比它加了 UHS-I 支持、内置 IDMA、硬件流控、更宽的 8 位数据总线并且支持 SDMMC1/SDMMC2 两个控制器。这篇内容适合三类人一是刚接触 H7、想在板子上跑通 SD 卡的开发者二是从 F1/F4 迁移过来、被新外设名字搞晕的老手三是在做数据采集、音频、GUI 资源存储、日志系统需要把 SD 卡读写性能压榨出来的朋友。看完之后你应该能独立完成从 CubeMX 配置到 FATFS 文件读写的完整流程并且知道出问题该从哪里查。2. 先搞懂 H7 SDMMC 的硬件底子后面才不会白忙2.1 控制器差异不止改了个名字STM32H7 的 SDMMC 全称是 SD/SDIO/MMC 主机控制器它和 F4 的 SDIO 外设承担的职责类似但内部做了不少升级。我这里直接列一张对照表方便大家建立第一印象。特性F4 的 SDIOH7 的 SDMMC协议支持SD、MMCSD、SDIO、MMC、eMMCUHS-I 支持不支持支持 SDR12/SDR25/SDR50/SDR104/DDR50数据总线宽度1/4 位SDMMC1 支持 1/4 位SDMMC2 支持 1/4/8 位DMA 方式需要外部 DMA 通道内置 IDMAInternal DMA硬件流控部分型号支持建议默认开启防止 FIFO 上溢/下溢时钟来源固定 48MHz 左右可由 PLL1Q、PLL2Q 或 AHB 等提供实际影响最大的是两点UHS-I 和内置 IDMA。UHS-I 意味着你可以把 SD 卡跑到高速模式之上用 50MHz、100MHz 甚至更高的时钟内置 IDMA 则表示你不必再像 F4 那样给 SDIO 分配一个 DMA2 Stream3/Stream6H7 的 HAL 驱动在调用带 _DMA 后缀的读写接口时走的就是这颗内部 DMA少了一层配置也少了很多踩坑点。不过 IDMA 也不是完全无感它需要你关注 buffer 地址对齐、Cache 一致性等问题这一点后面我会专门用一个小节来讲因为这是 H7 上最容易翻车的地方。2.2 时钟树和 CLKDIV 的计算最容易算错的一步SDMMC 在 H7 里的输出时钟 SDMMC_CK是 SDMMC 内核时钟经过分频得到的。公式很简单SDMMC_CK SDMMC 内核时钟 / (2 * CLKDIV)当 CLKDIV 0 时SDMMC_CK 等于内核时钟的一半也就是最高速。CLKDIV 是 8 位寄存器取值范围 0~255。这里有个关键坑识别阶段的时钟必须低于 400kHz因为 SD 卡上电后只接受这个速度范围内的命令。如果你把 SDMMC 内核时钟直接挂在 240MHz 的 HCLK 上要满足 400kHzCLKDIV 需要 240 / (2 * 0.4) 300已经超出 8 位寄存器的上限。我见过不少人在 CubeMX 里图省事把 SDMMC 时钟源直接选成 HCLK结果硬件初始化时 ClockDiv 被截断卡永远不回复 CMD0只能一脸懵地查接线。我常用的办法是在 CubeMX 时钟树里单独给 SDMMC1/SDMMC2 分配一个 100MHz 左右的内核时钟可以从 PLL1Q 或 PLL2Q 引出。这样算起来非常舒服初始化阶段 400kHzCLKDIV 取 125切到高速度模式 25MHzCLKDIV 取 2如果之后想跑 SDR50CLKDIV 取 1 就能得到 50MHz。如果内核时钟给到 200MHz那 CLKDIV0 时 SDMMC_CK 就是 100MHz理论上更接近 UHS-I 的带宽上限但要注意时钟源的选择范围以及 PCB 信号质量是否能扛住 100MHz 的 SDMMC_CK。所以我的建议是不要一上来就追求极限先把一组“100MHz 内核时钟 正确的 CLKDIV”跑通再根据实测去提频。这能帮你省掉至少一个通宵。2.3 引脚分配提前想好别和 LAN8720 打架H7 的引脚复用表虽然丰富但 SDMMC1 和 SDMMC2 的引脚和很多外设存在重叠。特别是你如果同时要用以太网LAN8720 方案和 SD 卡一定要在画原理图之前就把引脚冲突梳理清楚。LAN8720 的 RMII 接口会占用 PA1、PA2、PA7、PC4、PC5 等引脚而这些引脚附近往往有 SDMMC 的复用选项一旦冲突就只能换 AF 或者换控制器。我在一个项目里就吃过这个亏板子上同时有 LAN8720 和 microSD 卡座一开始工程里选了 SDMMC1结果原理图已经画完才发现某根 SDMMC_D0 和 ETH 的 RX_DV 撞了最后只能飞线并换用 SDMMC2。所以这里建议如果只做 SD 卡优先用 SDMMC1它占用引脚相对少4 位总线足够。如果要做 eMMC 或者希望 8 位总线就用 SDMMC2同时留出足够的走线空间。如果同时有以太网先在 CubeMX 的 Pinout 视图里把所有外设引脚打勾看冲突提示再定方案。另外SDMMC 的 GPIO 速度等级记得选 Very High。这不是玄学H7 在 50MHz 以上的 SDMMC_CK 时如果 GPIO 翻转速率不够波形会变得很“圆”导致卡接收端采样出错。我实际遇到过高速模式下偶发读写失败最后就是把所有 SDMMC 相关引脚的速度等级全部拉满才稳定下来。3. 从 CubeMX 到一块能用的 SD 卡完整配置路径3.1 CubeMX 里到底要勾哪些东西拿到一块 H7 开发板最快跑通 SD 卡的方式就是用 STM32CubeMX 生成一个带 FATFS 的工程。以下是我每次新建工程都会检查的几项第一RCC 时钟。H7 一般用 HSE 外部晶振然后通过 PLL1 倍频到 480MHz 内核AHB 分频到 240MHz 或更低。我在 CubeMX 的 Clock Configuration 里会额外找到 SDMMC1 的时钟源输入框手动填入 100MHz或者从下拉框里选 PLL1Q/PLL2Q 对应的频率确保在 400kHz 识别阶段 CLKDIV 不会溢出。第二SDMMC 外设。在 Connectivity 里找到 SDMMC1勾选 Activated。Mode 一般选 SD 4-bit High Speed因为绝大多数 microSD 卡都能在 4 位模式工作。Hardware Flow Control 建议使能这个选项对应 SDMMC 的硬件流控功能打开后 FIFO 快满时控制器会自动延迟时钟避免数据溢出。开发板或自研板如果支持 1.8V 电压切换可以考虑 UHS-I 模式否则先老老实实用 High Speed。第三FATFS 中间件。在 Middleware and Software Packs 里勾选 FATFSMode 选 SD Card。此时 CubeMX 会自动把 diskio 底层和 SD 驱动关联起来。需要注意 _MAX_SS 保持 512这是标准 SD 卡的扇区大小改成 4096 反而可能出问题。Code Page 可以改成 GBK 或 UTF-8看你的文件名需求。第四中断优先级。SDMMC1 的全局中断必须打开否则 HAL 库的 DMA 回调无法触发。优先级建议比系统节拍高一些但不用拉到最高。如果你以后还要跑运动控制、实时插补这类任务把 SDMMC 中断优先级设定为“能及时响应但不抢占关键控制中断”即可。3.2 最小 FATFS 读写代码长什么样CubeMX 生成工程后应用层代码其实很简洁。通常我在 main 函数里会先挂载文件系统再做一个写文件、读文件的验证。FATFS fs; FIL file; FRESULT res; UINT bw; char write_buf[] STM32H7 SDMMC OK\r\n; char read_buf[64]; res f_mount(fs, , 1); if (res ! FR_OK) { // 挂载失败一般要先格式化 res f_mkfs(, 0, 0); res f_mount(fs, , 1); } res f_open(file, test.txt, FA_WRITE | FA_CREATE_ALWAYS); if (res FR_OK) { f_write(file, write_buf, strlen(write_buf), bw); f_close(file); } res f_open(file, test.txt, FA_READ); if (res FR_OK) { f_read(file, read_buf, sizeof(read_buf), bw); f_close(file); }这段代码看起来简单但背后牵扯到 disk_initialize、disk_read、disk_write、disk_ioctl 这一整套底层函数。正常情况下CubeMX 生成的底层会调用 HAL_SD_Init 和 HAL_SD_ReadBlocks/WRITEBlocks。所以应用层不需要关心寄存器级的操作可一旦底层初始化有问题整个链路都会卡住这时候就要回到第二部分的时钟和引脚检查上。3.3 硬件电路上别省的那几个元件软件倒是其次SD 卡能不能稳定工作很多因素在硬件上就决定了。我这里列几个重要经验卡座供电端要加一个 100uF 电解电容和 0.1uF 陶瓷电容并尽量靠近卡座引脚。SD 卡在写入瞬间电流会突然拉高电源不稳最容易导致偶发初始化失败。SDMMC_CK 信号线上可以预留串联 22Ω~33Ω 的电阻用于抑制振铃。H7 输出时钟一旦到 50MHz 以上走线长一点就容易反射尤其在飞线上测试时特别明显。如果搞 UHS-I必须要有 3.3V 和 1.8V 之间的电压切换电路通常用负载开关加电平转换器实现。没有这套电路只改软件去切 UHS-I 是必然失败的。热插拔检测引脚 SD_DETECT 不要悬空。很多开发板的 microSD 卡座根本没接检测脚CubeMX 生成的代码也不知道卡是否存在。量产场景建议接上至少能提前提示用户插卡。4. 初始化流程拆解SD 卡从“沉睡”到“就绪”4.1 上电到 CMDO一条命令都不能乱SD 卡有严格的状态机上电后它处于 idle state主机必须按照规范顺序发命令乱序执行会导致卡完全没反应。HAL 库的 HAL_SD_Init 内部已经封装好了整个流程但我还是建议你理解一遍后面排查问题会快很多。一套最标准的初始化流程是上电至少等待 74 个时钟周期然后依次发送 CMD0GO_IDLE_STATE让卡进入空闲态CMD8SEND_IF_COND检查卡支持的工作电压接着循环发送 ACMD41SD_SEND_OP_COND直到卡返回 ready再通过 CMD2ALL_SEND_CID拿到卡的唯一标识CMD3SEND_RELATIVE_ADDR