4. MMC/SD/eMMC协议详解:命令、响应、数据传输与总线速度模式
MMC、SD与eMMC协议虽然分属不同代际与市场定位,但其底层通信模型高度一致:主机(Host)通过命令线(CMD)发送命令,从设备(Card/Device)在指定窗口内返回响应(Response),数据线(DAT0-DAT7)按约定的时序传输块数据,所有信号线均受CRC校验保护。理解这套协议体系,是驱动开发中正确初始化设备、配置总线时序、实现高效读写以及排查通信故障的前提。本章将从命令格式与分类、响应类型、数据线传输模式、CRC算法以及总线速度模式五个维度展开,并结合实际驱动代码与调试案例进行深度拆解。
4.1 命令格式与分类
MMC/SD/eMMC协议中,所有命令均为48位长度,由主机通过CMD线发送。命令格式固定为:1位起始位(恒为0)、1位方向位(主机发送为1)、6位命令索引(CMD0-CMD63)、32位命令参数(内容因命令而异)、7位CRC校验码(覆盖命令索引与参数)以及1位结束位(恒为1)。驱动开发中,命令索引与参数的正确构造是基础,而CRC7的硬件自动生成通常由控制器完成,软件层只需关注命令语义。
命令按功能可分为四大类:广播命令(如CMD0、CMD2),不指定特定设备,所有设备同时接收;点对点命令(如CMD7、CMD17),通过RCA(相对设备地址)或地址参数指定目标设备;带数据命令(如CMD24、CMD18),命令本身仅触发数据传输,实际数据在DAT线上完成;应用特定命令(ACMD),需先发送CMD55告知设备后续命令为应用命令。例如,SD卡初始化时,ACMD41用于查询/设置工作电压,而eMMC则使用CMD1完成类似功能。
mmc_wait_for_cmd()函数负责发送命令并等待响应。驱动开发者需注意,CMD0(复位)与CMD15(中断)等命令不要求设备返回响应,而CMD6(切换功能)在eMMC中用于模式切换,其参数需按位域精确构造,错误位将导致设备无响应或进入未知状态。
4.2 响应类型详解:R1/R2/R3/R6/R7
设备对命令的响应同样通过CMD线返回,长度分为48位(R1/R3/R6/R7)和136位(R2)两种。响应类型由命令规范决定,驱动需根据命令索引解析对应响应结构。
| 响应类型 | 长度 | 关键字段 | 典型命令 | 驱动关注点 |
|---|---|---|---|---|
| R1 | 48位 | 32位设备状态 | CMD7, CMD17, CMD24 | 检查错误位(如ADDRESS_ERROR、WP_VIOLATION) |
| R2 | 136位 | CID或CSD寄存器(120位) | CMD2, CMD9, CMD10 | 解析设备容量、最大时钟频率、读写块长度 |
| R3 | 48位 | OCR寄存器(32位) | CMD1, ACMD41 | 检查电压窗口与忙标志(BUSY) |
| R6 | 48位 | RCA(16位)+ 状态位 | CMD3 | 获取设备地址,用于后续点对点命令 |
| R7 | 48位 | 回声字段(参数原样返回) | CMD8 | 验证SD卡版本与电压匹配 |
R1响应是驱动中最常处理的类型,其32位状态字段包含23个状态位,涵盖设备当前状态(如tran、data)、错误标志(如ECC错误、CCRC错误)以及锁定状态等。驱动在每次命令后应检查关键错误位,避免在设备异常时继续后续操作。R2响应用于读取CID(设备ID)和CSD(设备特定数据),其中CSD中的c_size、c_size_mult和read_bl_len字段共同决定设备容量,驱动需按规范进行移位计算。
4.3 数据线传输模式与CRC校验
数据在DAT0-DAT7线上传输,支持1位、4位和8位(仅eMMC)三种模式。数据传输以块为单位,块长度由CSD或CMD16设定,常见为512字节。读操作(CMD17/CMD18)中,设备在命令响应后拉低DAT0表示忙,随后在CLK上升沿输出数据块;写操作(CMD24/CMD25)中,主机在命令响应后直接发送数据,设备通过CRC状态令牌(在DAT0上返回)告知写入结果。
CRC校验覆盖命令、响应和数据三个层面:命令与响应使用CRC7多项式x^7+x^3+1,数据块使用CRC16多项式x^16+x^12+x^5+1。硬件控制器通常自动完成CRC生成与校验,但驱动需理解CRC失败时的处理流程:若命令响应CRC错误,主机应重发命令;若数据CRC错误,读操作应重读,写操作需查询设备状态并可能重写。
// 数据块CRC校验示例(伪代码)
uint16_t crc16_data(const uint8_t *data, size_t len) {
uint16_t crc = 0;
for (size_t i = 0; i < len; i++) {
crc ^= (data[i] << 8);
for (int j = 0; j < 8; j++) {
if (crc & 0x8000) crc = (crc << 1) ^ 0x1021;
else crc <<= 1;
}
}
return crc;
}
4.4 总线速度模式:DS/HS/SDR/DDR/HS200/HS400
总线速度模式决定了CLK频率、数据线宽度和信号电平,直接影响吞吐量。驱动需在初始化阶段通过命令序列逐步提升速度模式,每一步都需验证设备能力与稳定性。
| 模式 | CLK频率 | 数据线宽度 | 信号电平 | 理论带宽 | 适用设备 |
|---|---|---|---|---|---|
| DS (Default Speed) | 25MHz | 1/4/8位 | 3.3V | 12.5/25/50 MB/s | 所有MMC/SD/eMMC |
| HS (High Speed) | 50MHz | 1/4/8位 | 3.3V | 25/50/100 MB/s | SD 2.0+, eMMC 4.x |
| SDR104 | 208MHz | 4位 | 1.8V | 104 MB/s | SD 3.0+ |
| DDR50 | 50MHz DDR | 4位 | 1.8V | 50 MB/s | SD 3.0+ |
| HS200 | 200MHz | 8位 | 1.8V | 200 MB/s | eMMC 5.0+ |
| HS400 | 200MHz DDR | 8位 | 1.8V | 400 MB/s | eMMC 5.1+ |
速度模式切换流程遵循“先配置设备,再切换主机”的原则。以eMMC从HS切换至HS200为例:首先通过CMD6将设备切换到HS模式并验证;然后通过CMD6的HS_TIMING字段选择HS200,同时将信号电平切换至1.8V(通过CMD6的VOLTAGE_SWITCH字段);最后主机侧将CLK频率提升至200MHz,并将数据线宽度配置为8位。整个过程需在设备返回的忙信号结束后进行,否则可能导致设备状态错乱。
背景:基于i.MX8M Plus平台,eMMC为三星KLMBG4JETD-B041,设计目标为HS400模式。
现象:初始化过程中,CMD6切换至HS400后,设备无响应,CMD线卡死。
分析:通过逻辑分析仪抓取波形,发现主机在发送CMD6后立即提升CLK频率至200MHz,但设备仍在处理内部切换,DAT0忙信号尚未释放。进一步检查代码,发现驱动未等待设备忙信号结束。
解决:在CMD6响应后增加忙信号轮询,等待DAT0拉高后再提升CLK频率。同时,将信号电平切换与HS200模式切换分离,先切电平再切模式,避免时序竞争。
结果:修改后HS400模式稳定运行,顺序读速度从HS200的180MB/s提升至350MB/s。
复盘:高速模式切换必须严格遵循设备规范中的时序要求,尤其是忙信号等待和电平切换顺序,驱动中应增加超时保护与重试机制。
4.5 常见误区与总结复盘
- 误区一:认为所有设备都支持最高速度模式。实际需通过CSD/EXT_CSD寄存器读取设备能力,如eMMC的EXT_CSD第196字节(
DEVICE_TYPE)指示支持的HS200/HS400模式。 - 误区二:忽略CRC错误的重试策略。在高速模式下,偶发CRC错误是正常的,驱动应具备重试机制,而非直接报错。
- 误区三:混淆SD与eMMC的初始化流程。SD卡使用ACMD41与CMD8,eMMC使用CMD1与CMD6,两者不兼容。
- 误区四:在DDR模式下未正确配置数据选通(DS)信号。HS400模式依赖DS信号进行数据采样,若未启用或时序错误,将导致数据错位。
总结而言,MMC/SD/eMMC协议的命令-响应-数据模型是驱动开发的核心骨架。驱动开发者需熟练掌握命令构造、响应解析、CRC校验逻辑以及速度模式切换的完整流程。在实际项目中,建议使用逻辑分析仪或协议分析仪验证初始化时序,并建立完善的错误处理与降速机制,以确保在各种硬件环境下稳定运行。