如何通过 SPI 初始化/使用 SD 卡?

电器工程 微控制器 spi 记忆 sd
2022-01-16 10:50:29

我看过各种关于使用 SPI 使用微控制器访问SD 卡的博客和论坛帖子、教程和应用说明,但在关注它们时,我在不同的点上遇到了很多困难。

在我的以下回答中,我尝试为其他苦苦挣扎的人更详细地总结初始化序列,并提供一些我希望提前知道的建议。

1个回答

一般建议

  • 根据规范,SDUC(超大容量,> 2 TB)卡不支持 SPI。

  • 有几个库可以使用适当的文件系统显着简化对 SD 卡的访问,以便可以使用计算机上的文件浏览器轻松读取文件。如果可以的话,我建议在这里停下来尝试一下,因为您可能不需要处理低级的事情:

  • 如果由于某些原因您不能使用这些库,您也可以自己初始化 SD 卡(这是我在此处描述的)并直接写入 SD 卡块/扇区,而无需(适当的)文件系统或使用您的拥有一个。但是,缺点是您不能简单地使用文件资源管理器读取数据。相反,您可以使用HxD之类的工具或Linux 上的dd 命令(也可以使用Windows 版本)来读取/复制原始数据。(例如,我有一个实时微控制器系统,它只有很小的周期性时隙,我可以在其中执行 SD 卡记录任务和启动/评估 SPI 序列。我见过的所有库都不是实时的,可以至少根据我的理解,执行需要相当长的时间,所以我们到了。)

  • 虽然可以与 SD 卡和其他从机共享 SPI 总线(例如,仅使用不同的芯片选择 (CS) 线),但我不建议这样做 - 使用单独的 SPI 总线可以省去一些麻烦。

  • 如果您与其他从机共享 SPI 总线,请始终每次与 SD 卡通信后,在访问其他从站之前发送另一个“虚拟”字节(或更多),CS 线处于非活动状态(高电平)。否则,SD 卡将保持 DO/MISO ((master in slave out)) 输出有效并导致两个从机驱动/短路 MISO 线!所以:CS 低,与 SD 卡交换数据,CS 高,发送虚拟数据(例如一个字节 0xFF),然后你可以与其他从机通信。(我通过将数据发送到一个“虚拟”从机来解决这个问题,而这个虚拟/虚拟从机的 CS 引脚未连接 - 这样微控制器就可以自己处理 CS 线。)此外,在不使用任何其他从机的情况下开始,直到初始化序列运行良好,然后您可以重新启用其他从站(在初始化之后甚至在初始化期间)。

  • 使用放置在 SD 卡上的大缓冲电容器(例如 100 µF + 22 µF + 1 µF)。SD 卡在插入时会消耗大量电流,这可能会导致 SD 卡和其他组件断电。编辑:我在 SD 卡附近使用了陶瓷电容器(3x 22 µF、1x 10 µF、1x 1 µF、1x 100 nF),并通过 1 Ω 电阻器与主电源轨隔开,并测量了从 3.30 V 到插入卡时 3.22 V 约 100 µs,这很好。

  • 能够对 SD 卡进行电源循环可能是有益的,例如切断其电源并再次启用它。如果卡“睡着了”,它不会再次唤醒,除非你切断电源重置它或你重新插入它:SD 卡重置问题

  • 请注意,在写入或擦除周期期间电源发生故障时可能会发生数据丢失。当您认为自己不写入数据时也可能发生这种情况,因为 SD 卡控制器可能会在内部处理数据: 如何保护 SD 卡免受意外电源故障的影响?

    根据这个线程,似乎有电源故障容错 SD 卡/工业 SD 卡声称具有电源故障恢复能力我没有测试它们,但您可能想查看它们以获取重要数据。

规格、教程、应用说明

  • 最重要的是规范本身: SD 规范第 1 部分物理层(简化规范),当前版本 8.00。您需要阅读其中的一部分才能生存(至少第 7 章:SPI 模式,对于 SPI,其余部分不需要太多,因为 SPI 仅支持基本命令和功能),我会经常参考它。

也可以看看这些:

如果您遇到困难或想了解更多信息:

基本

通常,您通过 SPI 主机发送到 SD 卡(SPI 从机)的命令(参见规范中的 7.3.1.1 命令格式)与 SD 卡进行通信。该命令为 48 位(6 字节)长:1 字节命令,4 字节参数(通常为 0,因此为 0x00000000),1 字节 CRC7 + 结束位。

  • 第一个字节:0b01xxxxxx,其中xxxxxx是命令索引的二进制表示,所以 CMD13 或 ACMD13 =>001101
  • “ACMDxx” = 应用程序特定命令。发送 CMD55 告诉 SD 卡,下一个命令是 ACMD。
  • 命令以及参数(4 个字节)在 7.3.1.3 详细命令说明中的规范中进行了描述
  • 最后一个字节:(CRC7 << 1) | 1- 默认情况下只有 CMD8 需要(在这种情况下可以预先计算),但我建议始终使用它。见文末代码。

SD 卡始终以响应进行响应,如“7.3.2 响应”一章中所述。但是,响应可能会在 48 位消息之后的 0 到 8 个字节之间出现。因此,要么发送单个字节并检查每个字节的响应开头,要么始终传输至少 19 个字节(6 个用于命令,最多 8 个等待,5 个字节用于响应 R3/R7)并找到响应的开头稍后(当您使用在后台运行的 DMA 时很有用 - 当然,如果在实现 DMA 例程时对您有帮助,您也可以读取更多字节,卡只会输出更多的 0xFF。据我记得,一些如果您收发更多字节,卡片甚至会重复响应)。就我而言,16 字节总是足够的(测试了三张卡:金士顿 32 GB、闪迪 16 GB 和 Intenso 8 GB),但可能 19 更安全。

在初始化序列期间,允许的最大时钟频率为 400 kHz(允许 100 - 400 kHz)。初始化后(当时钟不再处于“空闲状态”时),时钟速度可以提高到 25 MHz(对于 SD 卡,对于 MMC:20 MHz?)。

“空闲”或“处于空闲状态”并不意味着“卡已准备好”形式的空闲,而是或多或少与某些人直觉认为的相反:卡在启动时进入“空闲状态” - 您需要初始化卡,使其进入运行/数据传输模式。一旦初始化并准备好传输数据,“空闲”位将为低电平,因此当卡准备好使用时,它不再是“in_idle_state”。

初始化序列

(阅读说明书中的图 7-1 或 7-2 以便更好地理解。)

  1. 一旦检测到 SD 卡(例如使用卡检测电路),您需要在卡的 V DD稳定后至少等待 1 毫秒(“设备应在检测到 V 后 1 毫秒内准备好接受第一个命令DD min." --> 6.4.1.1. Power Up Time of Card),所以可以尝试 2 ms 作为开始。

  2. 发送至少 74 个 CS 为高电平的时钟,例如发送 10-16 个字节的 0xFF 到同一 SPI 总线上的虚拟从机或发送到 SD 卡,CS 手动拉高。(这里的时钟速度也可能更高,但为了安全起见,可能从 400 kHz 开始。)

(从这里开始,正常使用CS,即收发数据时设置active/LOW,之后设置inactive/HIGH。)

  1. 发送 CMD0(进入空闲状态)0x40 00 00 00 00 95,直到收到有效响应(即0x01)。如果收到0xFF,则需要收发更多数据(响应最多 8 个字节后)或重新发送 CMD0 直到收到响应。如果您收到0x09,则表示您使用错误的 CRC7 代码发送了命令(除了需要正确 CRC 的 CMD8 之外,这也可以)。对于以 0 位开头的任何其他响应,请参阅“7.3.2.1 格式 R1”。

  2. 发送 CMD8(接口条件),例如0x48 00 00 01 AA 87(参见“4.3.13 发送接口条件命令(CMD8)”)。参数中的“1”表示我们支持 2.7-3.6 V,“AA”可以是任何值 - 这是您将再次通过卡接收并用于检查连接和电源是否良好的检查模式。如果更改,请调整 CRC7。回复在“7.3.2.6 Format R7”和“4.9.6 R7(卡接口条件)”中描述。如果卡的响应以0x05(illegal cmd) 或0x0D(+CRC error) 开头,则很可能您的旧 V1.X SD 卡 (<= 2 GB) 不支持该命令。否则响应应该是0x01 .. .. .1 AA(。= 4 位无关,1 = 电压范围 2.7-3.6 V,AA = 我们的检查模式)。如果这有效(或不支持该命令),

  3. 发送 CMD58(读取 OCR)7A 00 00 00 00 FD响应将是 R3(“7.3.2.4 格式 R3”和“5.1 OCR 寄存器”),应该类似于0x01 OCR[31..0]. 检查卡允许的电压范围(位 15-23)是否适合您的应用。就我而言,我只检查 if (OCR & 0x00380000) == 0x00380000,这意味着 19-21 位为高电平,因此该卡至少支持 3.1 - 3.4 V。如果合适,请继续(暂时忽略其他位)。如果没有,规范说您不应该再访问该卡。

  4. 发送 CMD55(表示下一个命令是特定于应用程序的命令,因此 ACMDxx),即0x77 00 00 00 00 65. 响应应该是0x01(或0x00)。

  5. 发送 ACMD41(发送操作条件)。将 HCS 设置为 1(参数:)0x40000000以支持 SDHC/SDXC 卡(否则您将仅支持标准容量卡 <= 2 GB)0x69 40 00 00 00 77:. 响应应该是0x01(表示我们仍处于“空闲”状态,需要重复步骤 6-7(发送 CMD55+ACMD41)或0x00,表示 SD 卡离开“空闲”状态并准备运行。如果您收到0x00.

  6. 再次发送 CMD58(读取 OCR)7A 00 00 00 00 FD此时“卡上电状态位”应为 1。如果是,则卡容量状态(CCS)有效。如果 CCS 位为 1,我们有一张 SDHC (2-32 GB) 或 SDXC (32GB-2TB) 卡。如果为 0,我们有一张 SDSC 卡(<= 2 GB)。

  7. 您现在可以将 SPI 总线从 400 kHz 加速到高达 25 MHz (SD) 或 20 MHz (MMC),并使用该卡进行数据传输。

  8. 读出一些寄存器可能很有用:SCR、CSD 和 CID(您也可以将其作为测试,以在将内容写入卡之前评估您的读取功能是否有效)。CSD 可能是最重要的,因为您可以计算 SD 卡的可用数据大小。参见“5.3 CSD 寄存器”、“5.2 CID 寄存器”和“5.6 SCR 寄存器”。

而已!

如果您想使用 DMA 与 SD 卡通信,这可能有点棘手:对于基本命令,您只需要 <= 19 个字节。读写,除了命令,至少需要1个起始字节+实际数据块512个字节+2个CRC16字节,但是读取,还需要提前一些延迟,然后SD卡开始发送数据. 也许您找到了更好的解决方案,但我通过使用三个 DMA 解决了它:

  • A) 用于命令的 128 位/16 字节 SPI TX/RX DMA(尽管可能建议使用 19 或 20 字节)
  • B) SPI TX DMA 用于以可变传输长度进行连续数据传输
  • C) SPI RX DMA 用于具有可变传输长度的连续数据传输

对于初始化和基本命令,仅使用 DMA A。

读取/写入数据扇区或 CSD/CID 寄存器:

  • 使用 DMA A 发送/接收命令/响应。如果响应正常:
  • 根据命令调整 DMA B/C 的数据传输传输大小(例如,对于读取 CSD 和 CID 寄存器,收发 128 字节应该没问题,而对于从/到数据扇区的常规单块读/写操作,我使用 920 字节),然后启动 DMA B/C。请注意,如果 SD 卡快速回复,部分读取数据可能已经在 DMA A 中开始。

其他有用的东西