如何在 STM32H750 使用 Phat 库,通过 SDMMC1 读取 SD 卡
设计需求
我的目标是通过 SD 卡读取 FAT 文件系统里面的 AVI 文件进行播放,视频分辨率是 320x240,帧数 30fps;音频使用单声道 PCM S16LE 44100Hz,因此有带宽需求,也就是带宽必须满足以足够的帧数播放 AVI 文件的比特率要求。按 MJPEG 编码的视频文件,把品质调到最高,视频方面的比特率撑死 5Mbit/s(我测试的视频文件里面视频流比特率最大的就是 5199kbps);音频部分因为并没有压缩,比特率是定死的 705kbps,全部加起来就算 6 Mbit/s。那因此我的 SD 卡的通信频率不能低于这个值。通过时钟分频,我给 SDMMC1 外设提供 200 MHz 的时钟输入,然后设置分频系数为 1,这样可以支持能够达到 100 MHz 速度的 SD 卡。
STM32CubeMX 的配置
SDMMC1 外设配置
我不打算使用外部控制器来控制电压转换、检测 SD 卡插入,我打算直接用STM32H750 的 GPIO 去莽连 SD 卡。
在 STM32CubeMX 的 Pinout & Configuration 里面找到左边栏,有 Connectivity,将其展开,找到 SDMMC,然后中间栏上部有 Mode,设为 SD 4 bits Wide bus(SD 卡四线宽总线)
-
模式: SD 4 bits Wide bus
-
参数设置:
- Clock transition on which the bit capture is made(在哪个时钟边沿采样数据):因为是 SD 卡所以选 Rising transition。
- SDMMC Clock output enable when the bus is idle(总线闲置时时钟信号是否仍然保持开启):选 Disable the power save for the clock,以避免它为了省电而在闲置时把时钟设为高阻抗,导致振铃,或者意外的时钟信号发生。
- SDMMC hardware flow control(硬件流控制):设为启用,我要配合 DMA 去使用它。
- SDMMC clock divide factor(时钟配置):我给 SDMMC1 提供的时钟是 200 MHz,但是我期望的 SD 卡通信频率是 100 MHz,因此我把它设为 1。
- Is external transceiver present?(是否有外部的收发器)设为 no。
-
因此 GPIO 只有这些:
- SDMMC_CK
- SDMMC_CMD
- SDMMC_D0
- SDMMC_D1
- SDMMC_D2
- SDMMC_D3
- SDMMC_CD(卡检测。数字输入模式,这个 GPIO 不被 SDMMC 外设控制,我用了 PD15 脚。)
关于 Phat 库
请看:
https://www.0xaa55.com/thread-27644-1-1.html
Repo
GitHub
Gitee
以上两个 Repo 内容相同,你如果无法使用 GitHub 则可以使用 Gitee。
结合 Phat 的使用
具体用法请看 Phat 库的 Phat/test.c 源码文件。
坑点
-
STM32H750 的 SDMMC1 的 DMA 只支持 IDMA,而 IDMA 只支持 AXI SRAM,也就是 0x24000000 到 0x2407FFFF 区域的 RAM。除了这个区域以外的区域都无法写入,IDMA 会卡死。
-
使用 IDMA 搬数据的时候,如果地址不是 4 字节对齐的,它自己会默默地强行对齐地址,然后开始搬数据。因此如果读写扇区,但是给了一个非对齐缓冲区地址的话,它会把地址改到往前攒了几个字节的位置,并且不会触发任何异常。
-
当你拔了 SD 卡,然后你却尝试使用 SDMMC1 去读写 SD 卡,正常会失败(因此可以判定为 SD 卡被拔/无法识别);但是卡被重新插回去后仍然无法初始化 SDMMC1。这是因为一个 HAL_SD_DeInit(&hsd1) 并不足以使其重置。必须使用以下的方式才能完全重置 SDMMC1 外设:
__HAL_RCC_SDMMC1_FORCE_RESET();
HAL_Delay(2);
__HAL_RCC_SDMMC1_RELEASE_RESET();
// 此后可以用 HAL_SD_Init(&hds1) 初始化成功。
主板设计
布线规则
先说结论:就正常布线就行了,不用过多考虑等长,10 cm 的差异值都是可以接受的。
最快的传输方式应该是以 200 MHz 的频率进行四线传输,但是实际上支持这样的速度的 SD 卡是非常少而且非常贵的,如果需要支持这样的超级高速 SD 卡,你还必须要有外部电压转换器,使用 1.7V 左右的电压去控制 SD 卡。然而根据 HAL 的代码,看起来 STM32H750 似乎是不支持这种卡的高速通信的。
那按照 100 MHz 的布线要求来,实际上等于没啥等长方面的要求。反倒是不要过分为了做等长而导致串扰。如果你要做等长布线,请务必让每根线之间都有辅地,或者使用多层板提供尽可能靠近布线的 GND 平面,以提供寄生电容并减少串扰,然后再给每根线串接阻尼电阻(至少提供焊盘)。注意布线越长,支持的通信频率就越低,我这边布线 7cm 左右,100 MHz 带 DMA 可以正常通讯,PIO 通讯会提示 CRC 错误但是读出的数据似乎没有问题。
阻尼电阻 + 寄生电容 = 上升沿变缓,阻值越大,上升沿越缓。
用示波器实际测量 SDMMC_CK 的波形和其余每根数据线的波形,确保数据线的波形先到,SDMMC_CK 的波形后到。如果你的 SDMMC_CK 布线较短,可以给 SDMMC_CK 串接较大的阻尼电阻如 100Ω,其它较短的布线串接 50Ω 或者 22Ω,较大的阻值可以减少过冲和振铃,但是如果本来就没啥过冲,那就要减少阻值。较长的布线串接较小的阻尼电阻,仅抑制过冲和振铃。
VCC 供电的线要稍微粗一点。
滤波电容
每个负载加上一个 100nF 的去耦电容就行了。
焊接
我焊接 TF 卡座的时候手法比较糙,经常焊接失败,因为如果整个 TF 卡座都被加热了的话,里面的塑料片会变软,弹簧会破坏卡座的塑料片;以及如果助焊剂进入了 TF 卡座的底部,就会黏住自弹卡座使其无法自弹。按照我的经验,焊接 TF 卡座的时候 推荐用电烙铁 而不是风枪。使用锡浆而不是锡丝。用锡浆的针筒在焊盘上加点锡浆,然后加助焊剂,再用电烙铁烫开锡浆,让助焊剂里自由流动的锡珠自动吸附到焊盘和引脚上。
焊完后,需要用手用力在卡座的盖子上面摁一下,这样它就能挤压 TF 卡使其触点能够良好接触。
|