ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SD卡初始化流程全解析:从协议命令到uboot源码与实测波形

2026/10/4 9:34:21 拓冰建站 浏览量
SD卡初始化流程全解析:从协议命令到uboot源码与实测波形 第一次在自制板卡上调 SD 卡启动的时候我拿着逻辑分析仪盯着 CMD 线上的波形看了整整一下午。CMD0 发出去了卡没有回应CMD8 发出去依旧只有主机侧的帧。当时我甚至怀疑是 uboot 的代码没编对后来把 SD 卡规范、uboot 的 mmc 框架源码和实际波形三者对照着看才发现问题几乎都出在对协议握手阶段的理解上——不是代码不会跑而是你根本不知道卡在哪个环节等你。这篇文章就把 SDIO 接口下 SD 卡初始化的完整流程摊开讲常用命令CMD0、CMD8、ACMD41、CMD2、CMD3、CMD7、CMD9、CMD17的格式与参数意图、uboot 里从 mmc_init 到 mmc_startup 的代码执行链路以及逻辑分析仪实测波形的判读方法。适合正在调 uboot 启动、移植 bootloader 到新板卡或者单纯想把 SD 协议栈吃透的嵌入式开发者参考。1. 为什么初始化是调 SD 卡的第一道坎先看懂 SDIO 总线与会话模型1.1 SDIO 总线上到底有哪些信号驱动关系是什么SDIO 这个缩写在项目标题里通常指的是 SD 卡赖以工作的总线接口。它最少需要三根线CLK 时钟、CMD 命令/响应线和一根 DAT0 数据线完整模式下扩展为 DAT0~DAT3 四根数据线。CLK 永远是主机输出SD 卡只负责在时钟沿上采样和输出数据。CMD 和 DAT 线都是半双工双向的主机发命令时驱动总线卡返回响应时接管驱动。空闲状态下CMD 和所有 DAT 线都要被上拉电阻拉到高电平——这个细节非常关键很多初始化失败就是上拉电阻漏焊或者根本没接。GPIO 上拉电阻的取值有讲究。常见的是 10kΩ~47kΩ 对 VDDSD 规范要求的是通信线上的上拉同时还要注意卡侧的内部上拉有时会和外部的分压。我通常先在原理图上确认 CMD 和 DAT0~DAT3 的 pull-up 都接了再考虑软件问题。曾经有一块板子 CMD 线只留了 Pad 没贴上拉电阻uboot 里反复 mmc rescan 都识别不到卡焊上 10k 电阻立刻正常这个坑记忆犹新。SDIO 的时钟速度也不是一开始就能跑很高。卡上电后主机必须先把时钟设置在 100kHz~400kHz 的识别频率区间完成整套初始化之后才能升到 25MHz 默认速度或 50MHz 高速。uboot 的主机控制器驱动在初始化时做的第一件事就是把 SDHCI 时钟分频器设置成 400kHz 左右。如果你看到 CLK 一上来就是几十兆那大概率是驱动配置有问题而不是协议问题。1.2 SD 卡的六种关键状态命令顺序不能乱的根因SD 卡内部是一个严格的状态机绝大多数命令只在特定状态下才被接受。我把和初始化最相关的几个状态拎出来inactive卡进入永久休眠只能重新上电才能唤醒。电压不匹配会给到这个状态。idleCMD0 复位后所处的状态等待主机发送 ACMD41。readyACMD41 初始化完成卡已就绪等待主机读取 CID。identCID 被主机读取后进入等待分配 RCA 地址。stbyCMD3 分配完相对地址卡处于待机状态未被选中。tranCMD7 选中后进入传输状态可以正常读写数据块。看清楚这张状态流转图就能理解为什么 CMD2 必须在 ACMD41 成功之后发CMD7 又必须等 CMD3 拿到 RCA 之后才能发。命令顺序错了卡会直接返回错误或者干脆不做任何响应。我在 uboot 里最常见的误操作是给一张已经被初始化过一次但没断电的卡直接重新发 CMD0 序列。有些卡在非 idle 状态下对 CMD0 的响应并不干净最稳妥的做法是整卡掉电重新上电再走一遍完整流程。规范要求的 74 个空时钟周期本质上就是给卡一个从上电到稳定工作的缓冲时间。1.3 一张表看懂初始化全过程的命令序列把整个初始化过程压缩成一张表后面所有的细节都是围绕这张表展开的阶段命令参数要点期望响应作用复位CMD0 (GO_IDLE_STATE)0x00000000无响应卡回到 idle 状态电压握手CMD8 (SEND_IF_COND)0x000001AAR7 (echo 0x1AA)确认卡支持 2.7~3.6V容量/电压协商CMD55ACMD41HCS 电压窗口R3 (OCR)初始化卡声明主机容量支持读取 CIDCMD2 (ALL_SEND_CID)0x00000000R2 (136 bit)获取卡唯一标识分配地址CMD3 (SEND_RELATIVE_ADDR)0x00000000R6 (含 RCA)卡给自己分配相对地址选中卡CMD7 (SELECT_CARD)RCA 16R1让卡进入 tran 状态读 CSDCMD9 (SEND_CSD)RCA 16R2 (CSD)解析容量、速度参数切位宽CMD55ACMD60x2R11-bit 切换 4-bit读 SCRCMD55ACMD510x0R1 数据块确认 SD spec 版本关注一个容易忽略的细节ACMD 命令并不是独立的命令编号而是先发 CMD55APP_CMD把下一帧命令标记为应用专用命令然后紧接着发对应的命令号。所以 ACMD41 在协议栈里实际是 CMD55 后跟 CMD41ACMD6 是 CMD55 后跟 CMD6。uboot 代码里发 ACMD41 的地方你一定能看到相邻两个 mmc_send_cmd 调用。2. 常用命令拆解命令帧格式、参数设计意图和响应判读2.1 48 位命令帧与 CRC7bit-bang 时必须自己算SD 模式下的命令帧是 48 位按位序排列如下位域长度内容[47]1 bit起始位恒为 0[46]1 bit传输方向主机-卡为 1[45:40]6 bit命令索引CMD0~CMD63[39:8]32 bit命令参数[7:1]7 bitCRC7 校验[0]1 bit结束位恒为 1如果你用的是 SDHCI 控制器uboot、Linux 里绝大多数场景CRC7 和结束位由控制器硬件自动生成软件只需要把命令索引和参数填到 SDHCI 的 COMMAND 寄存器和 ARGUMENT 寄存器。但如果你在做 51 单片机模拟 SDIO 这类位操作实现CRC7 就必须自己算。CRC7 的生成多项式是 x^7 x^3 1覆盖范围为 40 位命令索引 参数。分享一段实测可用的 CRC7 计算函数它返回的是 CRC7 左移一位再或上结束位后的最后一字节static uint8_t sd_crc7(const uint8_t *data, uint8_t n) { uint8_t crc 0; for (uint8_t i 0; i n; i) { uint8_t d data[i]; for (uint8_t j 0; j 8; j) { crc 1; if ((d 0x80) ^ (crc 0x80)) crc ^ 0x09; d 1; } } return (crc 1) | 1; }拿 CMD0 验证data 为 {0x40, 0x00, 0x00, 0x00, 0x00}函数返回 0x95和 SD 规范里 CMD0 的标准帧 0x40 00 00 00 00 95 完全一致。CMD8 参数 0x1AA 时标准帧是 0x48 00 00 01 AA 87。用这两个帧做自测如果你的实现结果不同先检查字节序。2.2 CMD0/CMD8/CMD55ACMD41复位与电压/容量握手的三个动作CMD0GO_IDLE_STATE参数恒为 0SD 模式下无响应。它的目的是无论卡当前处于什么状态除非 inactive都把卡拉回 idle。uboot 的 mmc_go_idle 实现很朴素发完 CMD0 后 udelay(2000) 等一下让卡完成内部复位static int mmc_go_idle(struct mmc *mmc) { struct mmc_cmd cmd; int err; udelay(1000); cmd.cmdidx MMC_CMD_GO_IDLE_STATE; cmd.cmdarg 0; cmd.resp_type MMC_RSP_NONE; err mmc_send_cmd(mmc, cmd, NULL); if (err) return err; udelay(2000); return 0; }CMD8SEND_IF_COND是 SD 2.0 协议新增的电压握手。参数低 12 位的设计很巧妙bit[11:8] 声明主机支持的电压范围0b0001 表示 2.7~3.6Vbit[7:0] 是一个 0xAA 校验模式。卡如果支持该电压会把 0x1AA 原样回显在 R7 响应里主机据此判断双方电压兼容。uboot 里参数是这样构造的cmd.cmdidx SD_CMD_SEND_IF_COND; cmd.cmdarg ((mmc-voltages 0xff8000) ! 0) 8 | 0xaa; cmd.resp_type MMC_RSP_R7;mmc-voltages 来自板级配置默认覆盖 2.7~3.6V。注意响应判读uboot 只检查响应低 8 位是否等于 0xAA这已经足够确认卡活着且电压范围匹配。如果卡不响应 CMD8说明这是只支持 SD 1.x 的老卡后续要跳过 SD 2.0 的逻辑这个分支代码里也有。CMD55 ACMD41是整个初始化过程中唯一的轮询环节。ACMD41 的参数是主机向卡声明两件事支持哪些电压bit[23:0]以及是否支持高容量卡bit[30] HCS。卡的 R3 响应里会返回 OCR 寄存器内容其中 bit[31] 是 busy 位表示卡是否完成上电初始化bit[30] CCS 表示卡是不是 SDHC/SDXC。uboot 的循环逻辑如下int sd_send_op_cond(struct mmc *mmc) { int timeout 1000; int err; struct mmc_cmd cmd; do { /* CMD55 先告诉卡下一个命令是应用命令 */ cmd.cmdidx MMC_CMD_APP_CMD; cmd.resp_type MMC_RSP_R1; cmd.cmdarg 0; err mmc_send_cmd(mmc, cmd, NULL); if (err) return err; /* ACMD41HCS 电压窗口 */ cmd.cmdidx SD_CMD_APP_SEND_OP_COND; cmd.resp_type MMC_RSP_R3; cmd.cmdarg mmc-cfg-host_caps MMC_CAP_HC ? OCR_HCS | OCR_VOLTAGE : OCR_VOLTAGE; err mmc_send_cmd(mmc, cmd, NULL); if (err) return err; udelay(1000); } while (!(cmd.response[0] OCR_BUSY) timeout--); if (timeout 0) return -ETIMEDOUT; if (mmc-version ! SD_VERSION_2) mmc-version SD_VERSION_1_0; mmc-high_capacity ((cmd.response[0] OCR_CCS) OCR_CCS); mmc-ocr cmd.response[0]; return 0; }OCR_VOLTAGE 在 include/mmc.h 里定义是 0x00FF8000覆盖 2.7~3.6V。如果你看到 ACMD41 反复发出但 busy 位始终不置 1优先怀疑卡供电不足、电压窗口参数错误或者卡已经因为之前的操作进入 inactive 状态。有个小经验轮询时不要在 CMD55 和 ACMD41 之间插其他命令。我见过有代码在循环里顺带读卡状态结果把 ACMD 语义打断卡迟迟不置 busy最后超时。CMD55 只对紧随其后的一帧命令生效这个语义必须严格执行。2.3 CMD2/CMD3/CMD7识别卡、分配地址、选中卡ACMD41 成功之后卡从 idle 进入 ready 状态。紧接着的 CMD2ALL_SEND_CID让所有卡把自己的 CID 寄存器放到 CMD 线上响应类型是 R2共 136 位起始位 传输位 6 位保留 120 位 CID CRC7 结束位。uboot 把 R2 响应的 128 位有效数据复制到 mmc-cid 数组int mmc_all_send_cid(struct mmc *mmc, u32 *cid) { struct mmc_cmd cmd; cmd.cmdidx MMC_CMD_ALL_SEND_CID; cmd.resp_type MMC_RSP_R2; cmd.cmdarg 0; int err mmc_send_cmd(mmc, cmd, NULL); if (err) return err; memcpy(cid, cmd.response, sizeof(u32) * 4); return 0; }CID 里包含厂商 ID、产品名、序列号和制造日期。调试时通过 mmc info 看到这些信息能确认卡有没有被正确识别。CMD3SEND_RELATIVE_ADDR是 SD 卡初始化里很有意思的一步主机不知道卡的地址卡的 RCA 是它自己生成并返回给主机的。R6 响应的高 16 位就是 RCA。uboot 里取出来存到 mmc-rcaint mmc_send_relative_addr(struct mmc *mmc) { struct mmc_cmd cmd; cmd.cmdidx SD_CMD_SEND_RELATIVE_ADDR; cmd.resp_type MMC_RSP_R6; cmd.cmdarg 0; int err mmc_send_cmd(mmc, cmd, NULL); if (err) return err; mmc-rca cmd.response[0] 16; return 0; }拿到 RCA 之后CMD7SELECT_CARD把指定地址的卡从 stby 状态拉进 tran 状态。参数是 RCA 左移 16 位所以 u-boot 里这句 cmd.cmdarg mmc-rca 16;。从这一步开始主机和卡才真正建立会话。需要提一下CMD3 之后总线上的所有带 RCA 参数的普通命令都要携带正确的 RCA否则卡不认。调试时看到 CMD7 之后卡返回 error 状态多半是前面 RCA 解析错了或者某个结构体没更新。2.4 CMD9/ACMD6/ACMD51读 CSD、切位宽、读 SCR进入 tran 状态后主机需要读 CSD 寄存器来解析容量和速度参数。CMD9SEND_CSD参数同样是 RCA 16响应 R2。CSD 的解析是容量计算的核心这里给出两种典型情况SDSC 卡CSD 1.0 版本容量计算公式BLOCK_LEN 2^READ_BL_LEN MULT 2^(C_SIZE_MULT 2) BLOCKNR (C_SIZE 1) * MULT 容量 BLOCK_LEN * BLOCKNRSDHC/SDXC 卡CSD 2.0 版本公式简单很多容量 (C_SIZE 1) * 512KB举例一张 32GB SDHC 卡的 C_SIZE 字段为 0xFFFF容量 (65535 1) × 512KB 32 GiB。uboot 里 mmc_read_capacity 就是按这个逻辑解析的判断依据是之前 ACMD41 响应里的 CCS 位。ACMD6SET_BUS_WIDTH用于把数据线从 1-bit 切换到 4-bit。参数 0b00 表示 1-bit0b10 表示 4-bit。uboot 的 sd_set_bus_width 先发 CMD55再发 CMD6索引 6参数 2成功后再调 mmc_set_bus_width 让主机控制器也切到 4-bit 模式。注意 ACMD6 的 CMD55 参数要带 RCAcmd.cmdidx MMC_CMD_APP_CMD; cmd.cmdarg mmc-rca 16; cmd.resp_type MMC_RSP_R1;ACMD51SEND_SCR读取 8 字节的 SCR 寄存器通过它判断卡支持的 SD spec 版本、是否支持 4-bit、是否支持安全命令。uboot 里 sd_send_scr 在 mmc_startup 中被调用。这块信息对正常启动影响不大但如果卡是 SD 4.x 以上的新卡涉及 UHS 模式切换读 SCR 是判断能力集的前提。3. uboot 源码走读从 mmc_init 到 mmc_startup 的执行链路3.1 入口mmc_init 的主机控制器准备与 400kHz 时钟以 uboot 2021.x~2023.x 的 drivers/mmc/mmc.c 为例。系统启动过程中mmc_initialize() 会遍历注册过的 MMC 主机控制器逐个调用 mmc_probe最终进入 mmc_init。这个函数是整个初始化流程的总入口。mmc_init 的第一件事是 mmc_set_ios主机控制器驱动会在这里把时钟设置到 400kHz 识别频率。这一步如果没做对后面所有命令都会失败。我看过一份 i.MX6ULL 的板级代码时钟树配置错误导致 SD 卡时钟根本没有输出现象就是逻辑分析仪上 CLK 纹丝不动uboot 卡在 mmc_init 超时。3.2 识别阶段三个关键函数在 uboot 里的实现mmc_init 里真正和协议相关的调用顺序是/* Reset the card */ err mmc_go_idle(mmc); /* Test for SD version 2 */ err mmc_send_if_cond(mmc); if (IS_SD(mmc)) { err sd_send_op_cond(mmc, mmc-ocr, mmc-ocr); ... } err mmc_startup(mmc);三个函数分别对应 CMD0、CMD8、ACMD41前面已经贴了代码。这里补充一个阅读源码的技巧mmc_init 里对 mmc-version 的判断要理清楚。如果 CMD8 成功version 被设为 SD_VERSION_2如果 CMD8 失败老卡sd_send_op_cond 会把 version 设为 SD_VERSION_1_0。后面对卡能力的判断是否支持 4-bit、是否高容量都会走不同的分支。很多移植 uboot 的人喜欢直接改 mmc.c我建议不要动协议层代码板级差异应该通过 mmc-cfg 的 voltages、host_caps、f_max 等配置项表达。协议层代码经过大量平台验证改坏的风险远大于收益。3.3 mmc_startup 里做了哪些收尾动作mmc_startup 承担 ACMD41 之后的所有识别动作。核心流程如下static int mmc_startup(struct mmc *mmc) { int err; /* 1. 读取 CID */ err mmc_all_send_cid(mmc, mmc-cid); if (err) return err; /* 2. 获取 RCA仅 SD 卡需要MMC 卡 RCA 固定为 0 */ err mmc_send_relative_addr(mmc, mmc-rca); if (err) return err; /* 3. 选中卡进入 tran 状态 */ err mmc_select_card(mmc); if (err) return err; /* 4. 读取 CSD解析容量和速度 */ err mmc_send_csd(mmc, mmc-csd); if (err) return err; if (IS_SD(mmc)) { /* 5. 读 SCR 确认能力集 */ err sd_send_scr(mmc, mmc-scr); if (err) return err; /* 6. 切换 4-bit 总线 */ err sd_set_bus_width(mmc, mmc-card_caps); if (err) return err; } /* 7. 提高时钟到卡支持的传输速度 */ err mmc_set_clock(mmc, mmc-tran_speed); ... }注意到一个容易踩的细节mmc_select_card 发送 CMD7 之后卡的状态才真正能被后续的读写命令接受。如果 mmc_startup 中途失败比如 CMD9 响应 CRC 错误整个 mmc_init 返回错误uboot 会认为卡不存在。这时候别一上来怀疑 uboot 代码先用逻辑分析仪确认 CMD7 之后 CMD9 的响应内容里 CSD 数据有没有被正确传输。3.4 数据读命令 CMD17 到块设备的调用路径初始化完成后读写数据就走另一套路径。uboot 命令行执行 mmc read 0x82000000 0x100 1最终会走到 mmc_bread再进入 mmc_read_blocks。数据读的核心命令是 CMD17READ_SINGLE_BLOCK和 CMD18READ_MULTIPLE_BLOCK。static ulong mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) { struct mmc_cmd cmd; struct mmc_data data; int err; /* CMD17 读单块CMD18 读多块 */ cmd.cmdidx blkcnt 1 ? MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK; cmd.resp_type MMC_RSP_R1; /* 注意SDSC 用字节地址SDHC/SDXC 用块地址 */ cmd.cmdarg mmc-high_capacity ? start : start * mmc-read_bl_len; data.dest dst; data.blocks blkcnt; data.blocksize mmc-read_bl_len; data.flags MMC_DATA_READ; err mmc_send_cmd(mmc, cmd, data); ... }这里最关键的是 cmdarg 的计算。SDSC 卡 CMD17 参数是字节地址block number × 512而 SDHC/SDXC 卡直接用块地址。很多自行实现 SD 驱动的朋友在 SDHC 卡上把参数乘了 512导致读到完全错误的位置——uboot 通过 high_capacity 标志区分这个标志正是来自 ACMD41 响应里的 CCS 位。数据块本身不在 CMD 线上传输而是在 DAT0 上1-bit 模式或 DAT0~DAT3 上4-bit 模式传输。每块数据后有 16 位 CRC 校验。所以当你看到 CMD17 有响应但数据读不出来时问题往往出在 DAT 线上而不是命令线上。4. 实测波形把一次完整初始化过程按命令逐段还原4.1 抓波形要准备什么探针和采样率怎么选逻辑分析仪是调 SD 卡初始化最趁手的工具。我用的方案是 Saleae Logic 16采样率设 50MS/s同时抓 CLK、CMD、DAT0 三根线。50MS/s 对 400kHz 识别频率绰绰有余等初始化完成后时钟升到 25MHz50MS/s 也还能看清关键沿变化。探针连接注意几点夹头要稳避免在 CMD/DAT 上引入过长裸露导线所有探针共地接在板子的 GND 测试点如果板子空间允许CLK 和 CMD 分开走线尽量避免两个探针的线缆互相缠绕否则高频下串扰会干扰波形判读。抓触发条件上推荐把触发设在 CMD 线的下降沿。CMD 空闲是高电平命令帧一出现就会拉低这个下降沿是最干净的触发点。不需要等上电瞬间的触发uboot 反复 mmc rescan 的时候你随时可以手动触发抓一帧完整序列。4.2 从上电到进入传输模式的波形特征逐段解读一次完整初始化的波形在逻辑分析仪上可以分成几个肉眼可辨的段落第一段是空白时钟段。CLK 以 400kHz 连续翻转CMD 线维持高电平持续至少 74 个时钟周期。这是规范要求的卡上电稳定时间。如果这段时钟缺失或者频率远高于 400kHz卡后续不会响应任何命令。第二段是 CMD0 单独出现。帧内容是 0x40 00 00 00 00 95发送后 CMD 线回到高电平没有响应帧。有的卡需要重复两三次 CMD0 才能稳定进入 idle这是正常的不必紧张。第三段是 CMD8 加上 R7 响应。CMD8 帧 0x48 00 00 01 AA 87 发送后大约 8 个时钟周期CMD 线上出现一个方向反转的帧——起始位 0、传输位 0、回显的命令索引 8、参数 0x1AA最后是 CRC7 和结束位。这个方向反转在波形上表现为主机发送时 CMD 线先低后高走完 48 位响应帧同样 48 位但内容由卡驱动。第四段是 CMD55/ACMD41 的重复循环。波形上你会看到成对的帧CMD550x77 开头带 R1 响应紧接着 ACMD410x69 开头带 R3 响应。R3 的响应内容是 OCR一开始可能是 0x00FF8000busy 位bit31为 0循环若干次后变成 0xC0FF8000SDHC 卡busy 置位 CCS 置位。看到 busy 从 0 变 1初始化就完成了。第五段是 CMD2 的 R2 长响应。R2 有 136 位在波形上是所有响应里最长的帧。Saleae 的 SD 协议解析器能直接把 CID 内容解出来包括厂商 ID、产品名、序列号。第六段是 CMD3 和 CMD7。CMD3 的 R6 响应里能看到 RCA 值比如 0x0001 对应响应 0x00010000。CMD7 参数是 RCA 16响应 R1。到这里卡进入 tran 状态。最后一段是位宽和时钟切换。ACMD6 切换 4-bit 模式后主机把 CLK 从 400kHz 升到 25MHz同时 DAT0~DAT3 上的数据开始出现。在逻辑分析仪屏幕上CLK 频率的跳变非常显眼。4.3 三种异常波形的识别方法异常波形基本可以通过有没有命令帧、有没有响应帧、响应内容对不对来归类。第一种有 CLK 和 CMD0但没有响应帧出现。CMD8 之后一直看不到 R7ACMD41 也看不到 R3。这种情况往往是卡没进入工作状态优先查电源、上拉和卡座接触。第二种命令帧发出来了响应帧也有但响应内容错误。比如 R7 返回的不是 0x1AA或者 R1 的卡状态位带着 error 标志。多数是 CRC 计算错或者信号质量差导致响应位被采样错。我会把采样率调到最大重新抓看响应帧的边沿是否存在振铃或过冲如果有就在 CMD 线上串联 22Ω~33Ω 电阻改善信号质量。第三种ACMD41 的 R3 响应一直在 0x00FF8000 打转busy 永远不置位。这种我见过最典型的场景是卡电源电压偏低卡内部 LDO 还没起来其次是主机把电压窗口参数写错只声明了某个窄区间卡认为电压不兼容。前者用示波器看 VDD 波形后者检查 OCR_VOLTAGE 的定义。再强调一次波形上的命令帧内容可以对照 2.1 节的帧格式手工解析。0x40 00 00 00 00 95 这个序列值得背下来它是判断uboot 有没有真的把命令发出去的黄金标准。5. 初始化失败排查手册按现象反推根因5.1 卡对 CMD0 完全无响应时的排查顺序如果逻辑分析仪上看到 CMD0 已经发出去但卡没有后续反应按这个顺序排查第一确认卡供电。用示波器量卡座的 VDD 引脚3.3V 是否稳定有没有在初始化瞬间跌落。卡在 ACMD41 上电过程中电流可能突然拉高如果电源带载能力不足电压跌落超过几百毫伏就会让初始化失败。我遇到过一次问题出在卡座旁的 10μF 电容没焊补上就好了。第二确认 CMD 线上拉。规范要求 CMD 线在主机不驱动时为高电平。如果你看到 CMD 线在帧与帧之间出现长时间低电平或者高阻态极可能是上拉电阻缺失。可以用万用表量 CMD 对地电阻正常应该在 10k~100k 范围。第三确认时钟频率。初始化阶段 CLK 必须在 100kHz~400kHz超过这个范围很多卡直接忽略。uboot 里如果 f_max 配置错误导致 400kHz 分频计算异常CLK 会跑到几兆甚至十几兆卡当然没反应。第四换一张卡。听起来像废话但卡损坏的概率比想象中高特别是二手拆机卡。uboot 的协议实现经过全世界上亿设备验证大概率不是 uboot 的问题。5.2 ACMD41 一直拉不高的常见原因ACMD41 循环超时基本上集中在三种原因。电压窗口不匹配最常见。记得检查 sd_send_op_cond 里 cmdarg 的电压位。如果你自定义板子时覆盖了 mmc-cfg-voltages只声明了 3.0~3.1Vbit[18]而卡需要 2.7~3.6V 全范围ACMD41 就会一直不置 busy。uboot 默认的 0x00FF8000 恰好覆盖 2.7~3.6V不要轻易改小。卡进了 inactive 状态也常见。如果之前一次初始化因为电压不匹配失败卡可能已经进入 inactiveCMD0 都叫不醒必须断电重新上电。这解释了为什么第一次失败后不管怎么重试都不行断电再试就好了。第三种是 HCS 位和卡实际容量不匹配。主机声明支持高容量HCS1卡响应没有 CCS 位说明是 SDSC 卡或者反过来主机没置 HCS卡却是 SDHC——后一种情况下 SDHC 卡虽然能完成初始化但后续容量解析会出问题。uboot 的 mmc-high_capacity 是由响应里的 CCS 位决定的注意确认最终赋值是在 ACMD41 响应之后。5.3 能识别、读写失败时的 DAT 线与位宽问题初始化能过、读写失败问题基本都在数据通道上和命令通道无关。这时候把逻辑分析仪的探针从 CMD 挪到 DAT0~DAT3 上。最常见的问题是位宽不一致初始化阶段永远是 1-bit 模式只用 DAT0ACMD6 切换成 4-bit 后如果硬件上 DAT1~DAT3 没接、焊错、或者虚焊数据就整个乱掉。表现是 mmc read 返回成功但读出来的数据全是 0xFF 或者 CRC 错误。排查方法是先在设备树或板级配置里强制只走 1-bit 模式host_caps 去掉 MMC_CAP_4BIT如果读写恢复正常问题就是 4-bit 通路硬件。第二个常见问题是 DAT1 被复用作卡检测。某些板子的 CD 信号和 DAT1 共用了一个引脚4-bit 模式下会因为信号冲突导致读写失败。解决办法是换一个空闲 GPIO 做卡检测或者直接用 DAT3 做卡检测很多卡规范推荐的做法。第三个是信号完整性问题。面包板或者飞线调试时DAT 线太长、CLK 串扰过强都会导致数据 CRC 错。表现是偶发读写失败重试可能成功。这个时候给 CLK 和 DAT 线加串联电阻22Ω~47Ω把线缩短问题基本能解决。5.4 uboot 命令行下快速定位问题的几条命令uboot 命令行本身提供了很好的调试手段。卡初始化失败时先在提示符下跑这几条mmc list mmc rescan mmc infommc list 能看到当前注册的 MMC 主机控制器mmc rescan 强制重新执行 mmc_init 流程mmc info 打印卡信息。如果 mmc info 能输出 CID、CSD、容量这些说明初始化已经通了问题在后面的 boot 流程。读数据测试用mmc dev 0 mmc read 0x82000000 0 1 md 0x82000000 0x10mmc read 把 block 0 读到内存md 查看内存内容。如果读到的是 0xFF 或者一堆无规律数据对照 5.3 节查数据通道。还有个容易被忽略的点uboot 编译时如果开了 CONFIG_MMC_DEBUGmmc 驱动会打印大量调试信息包括每条命令的参数和响应值。这个开关在 drivers/mmc/Kconfig 里遇到难啃的初始化问题可以打开重编配合逻辑分析仪双管齐下。我自己的习惯是先在 uboot 命令行确认 mmc info 正常再回到 Linux 内核验证这样能把问题边界划得很清楚。如果 uboot 正常而 Linux 不正常优先怀疑设备树里的 SD 卡节点配置——pinctrl、bus-width、max-frequency 这几个属性是重灾区。最后再说一个比较隐蔽的坑SD 卡对命令时序有一定容忍度但不同的卡宽容度差异很大。有些杂牌卡对 CMD8 之后的等待时间很敏感uboot 默认的延时不够时个别卡就会初始化失败。如果你在 uboot 下反复失败、换一张大牌卡立刻通过不要急着改代码先确认是不是卡兼容性问题——这类问题通常通过调整 mmc_go_idle 里的 udelay 参数就能缓解。嵌入式开发里换卡试试从来不是偷懒而是最便宜的二分定位法。