4. 存储设备操作命令(mmc/sf/sata):MMC/SD 卡探测与读写、SPI Flash 操作、SATA 硬盘操作
存储设备操作,是 U-Boot 调试中最常打交道的一类命令。说白了,你总得把内核镜像、设备树、文件系统塞到某个地方去。MMC/SD 卡、SPI Flash、SATA 硬盘,这三样几乎覆盖了嵌入式设备 90% 的存储场景。我个人习惯,拿到一块新板子,第一件事就是把这几个命令挨个敲一遍,确认存储通路是通的。
4.1 MMC/SD 卡操作:从探测到读写
MMC 子系统是 U-Boot 里最成熟的驱动之一。但成熟不代表不出问题。我遇到过好几次,板子启动后执行 mmc list 啥也看不到,最后发现是硬件上电时序没满足。
4.1.1 设备探测与信息查看
先看几个最常用的命令:
# 列出所有 MMC 设备
U-Boot> mmc list
# 输出示例:
# mmc@fe320000: 0 (eMMC)
# mmc@fe330000: 1 (SD)
# 切换到指定设备
U-Boot> mmc dev 0
# 切换到 mmc 0 (eMMC)
# 查看当前设备详细信息
U-Boot> mmc info
# 输出示例:
# Device: mmc@fe320000
# Manufacturer ID: 15
# OEM: 100
# Name: 8GTF4R
# Bus Speed: 52000000
# Mode: HS200
# Rd Block Len: 512
# Capacity: 7.3 GiB
# ...
这里有个细节:mmc info 显示的 Bus Speed 和 Mode 非常关键。如果速度上不去,或者模式不对(比如本该跑 HS400 却只跑了 HS200),大概率是时序配置或者电压切换出了问题。
mmc info,把容量、速度、模式记下来。后面调试启动速度时,这些数据就是基准线。
4.1.2 分区查看与读写操作
MMC 设备通常有多个分区。eMMC 更是有 Boot 分区、RPMB 分区、用户数据分区。你得知道怎么切分区:
# 查看分区表
U-Boot> mmc part
# 切换到 eMMC 的 Boot 分区 1
U-Boot> mmc dev 0
U-Boot> mmc partconf 0 1 1 0
# 读取数据:从 mmc 0 的 0x200 扇区开始,读 0x100 个扇区到内存 0x10000000
U-Boot> mmc read 0x10000000 0x200 0x100
# 写入数据:将内存 0x10000000 的数据写入 mmc 0 的 0x200 扇区,写 0x100 个扇区
U-Boot> mmc write 0x10000000 0x200 0x100
嗯,这里要注意 扇区地址 的单位。U-Boot 里 MMC 的地址默认是 512 字节块 地址,不是字节地址。我见过有人把 mmc read 的地址当字节地址用,结果读出来的数据全是乱的。
mmc write 总是写到一半卡死。排查了半天,发现是 eMMC 的 Boot 分区被写保护了。执行 mmc wp 查看写保护状态,果然 Boot 分区是只读的。解决方案:先切到用户数据分区再写。
4.1.3 常见故障与定位
| 故障现象 | 可能原因 | 排查命令/方法 |
|---|---|---|
mmc list 无输出 |
硬件未上电、时钟未使能、驱动未匹配 | 检查 clk_summary,确认 GPIO 上电时序 |
mmc read 返回错误 |
地址越界、CRC 校验失败、电压不匹配 | 用 mmc dev 确认当前设备,检查电压切换日志 |
| 读写速度极慢 | 总线宽度未配置、时钟频率过低 | 查看 mmc info 的 Bus Speed,检查 dts 中 bus-width 属性 |
| 写操作卡死 | 写保护、坏块、电源不稳 | 执行 mmc wp 查看保护状态,换一个扇区测试 |
4.2 SPI Flash 操作:小而精的存储
SPI Flash 虽然容量小,但在嵌入式系统里地位特殊——它通常用来存 U-Boot 本身、环境变量、或者 FPGA 配置文件。你想想看,如果 SPI Flash 读写出了问题,板子可能连 U-Boot 都起不来。
4.2.1 探测与擦除
SPI Flash 的操作命令以 sf 开头。先探测:
# 探测 SPI Flash 设备
U-Boot> sf probe
# 输出示例:
# SF: Detected w25q128 with page size 256 Bytes, erase size 4 KiB, total 16 MiB
# 擦除:从 0x100000 地址开始,擦除 0x10000 字节
U-Boot> sf erase 0x100000 0x10000
这里有个关键信息:erase size。SPI Flash 的擦除是以扇区(Sector)为单位的,不同型号的扇区大小不一样。W25Q128 是 4 KiB,有些老型号是 64 KiB。如果你擦除的起始地址不对齐扇区边界,命令会直接报错。
sf erase 先清空目标区域,再用 sf write 写入。虽然多了一步,但稳。
4.2.2 读写操作
# 读取:从 SPI Flash 0x100000 地址读 0x10000 字节到内存 0x10000000
U-Boot> sf read 0x10000000 0x100000 0x10000
# 写入:将内存 0x10000000 的数据写入 SPI Flash 0x100000 地址,写 0x10000 字节
U-Boot> sf write 0x10000000 0x100000 0x10000
注意,sf 命令的地址是 字节地址,跟 MMC 的扇区地址不一样。这个很容易搞混。我刚开始用的时候也犯过这个错,把 SPI Flash 的地址当扇区地址用,结果读出来的数据长度差了 512 倍。
4.2.3 保护与校验
SPI Flash 通常有硬件写保护引脚(WP#)和状态寄存器保护位。如果你发现写不进去,先检查保护状态:
# 查看 SPI Flash 状态寄存器
U-Boot> sf protect
# 输出示例:
# SPI Flash protection: Blocks 0-255 are protected
# 解除保护(如果支持)
U-Boot> sf protect off
4.3 SATA 硬盘操作:大容量存储的王者
SATA 硬盘在嵌入式系统里用得相对少一些,但一旦用上,就是大容量场景——比如 NAS、视频监控、边缘服务器。U-Boot 对 SATA 的支持已经很成熟了,操作命令跟 MMC 类似。
4.3.1 设备探测与初始化
# 扫描 SATA 总线,探测设备
U-Boot> sata init
# 输出示例:
# SATA link 0 found.
# SATA device 0: Model: ST1000DM003-1CH162, Capacity: 931.5 GiB
# 列出所有 SATA 设备
U-Boot> sata info
sata init 这一步很关键。如果 SATA 链路没有建立,后面的读写操作全都会失败。我遇到过一种情况:硬盘本身是好的,但 SATA 线缆接触不良,导致链路训练失败。重新插拔一下就好了。
4.3.2 分区与读写
# 查看分区表
U-Boot> sata part
# 读取:从 sata 0 的 0x800 扇区开始,读 0x2000 个扇区到内存 0x20000000
U-Boot> sata read 0x20000000 0x800 0x2000
# 写入:将内存 0x20000000 的数据写入 sata 0 的 0x800 扇区
U-Boot> sata write 0x20000000 0x800 0x2000
跟 MMC 一样,SATA 的地址也是 512 字节扇区地址。但 SATA 硬盘的扇区大小可能是 4K(Advanced Format),U-Boot 内部会做转换,你不用担心。
4.3.3 性能与故障排查
| 故障现象 | 可能原因 | 排查命令/方法 |
|---|---|---|
sata init 找不到设备 |
SATA 链路未建立、电源不足、驱动未加载 | 检查 SATA 电源指示灯,用示波器看 SATA 差分信号 |
| 读写速度慢 | SATA 版本协商为 1.5Gbps、DMA 未开启 | 查看 sata info 的链路速度,检查 dts 中 ahci 配置 |
| 写入时断时续 | 电源纹波大、线缆屏蔽不良 | 换电源、换线缆、检查接地 |
sata init 看链路速度,如果只有 1.5Gbps,说明硬件协商出了问题。我遇到过 SATA 线缆太长导致信号衰减,换了一根短一点的线,速度直接翻倍。
4.4 知识体系总览
下面这张图,把本章的三大存储接口的操作流程和关键命令串起来了。你可以把它当作调试时的速查地图。
4.5 本章小结
存储设备操作命令,说白了就是三板斧:探测、查看、读写。MMC、SPI Flash、SATA 的命令虽然不同,但逻辑是一样的。你只要记住每个接口的地址单位(MMC/SATA 是扇区地址,SPI Flash 是字节地址),再记住先擦除后写入的原则,基本不会出大问题。
嗯,最后再啰嗦一句:调试存储设备时,日志是最好的朋友。U-Boot 的串口输出里,驱动初始化阶段会打印很多关键信息——比如 MMC 的电压切换日志、SPI Flash 的 JEDEC ID、SATA 的链路协商结果。把这些日志抓下来,很多问题一眼就能看出来。