第3章 总线协议基础:命令、响应、数据与CRC

咱们今天聊聊eMMC通信最底层的规矩——总线协议。说白了,就是Host和Device之间怎么“说话”、怎么“回话”、怎么“传东西”,以及怎么保证传的东西没出错。

我刚开始接触eMMC时,总觉得协议就是一堆枯燥的时序图。后来在一个项目里,因为没搞懂命令格式,导致初始化流程卡死,调了整整两天……从那以后,我就老老实实把这块啃透了。你想想看,总线协议就像交通规则,不搞清楚,车跑得再快也得翻。

3.1 命令格式:Host怎么说

Host给Device发命令,用的是CMD线(双向)。每一帧命令都是48位固定长度。嗯,这一点很关键:不管你发什么命令,长度都一样。

位域 位置 描述
起始位 Bit 47 固定为'0',表示命令开始
传输位 Bit 46 '1'表示Host→Device方向
命令索引 Bit 45 ~ 40 6位,范围0~63,如CMD0、CMD8
参数 Bit 39 ~ 8 32位,具体含义由命令定义
CRC7 Bit 7 ~ 1 7位CRC,保护命令索引+参数
结束位 Bit 0 固定为'1',表示命令结束

我个人习惯把命令格式看作一个“小信封”:起始位是信封开口,传输位标明寄信人,命令索引是收件人编号,参数是正文,CRC7是防伪封条,结束位是信封底。任何一个位错了,Device都不会认。

经验之谈: 调试早期时,我喜欢用逻辑分析仪抓CMD线,直接看48位原始数据。如果你看到Device没响应,先检查命令的CRC7算没算对。我吃过这个亏——代码里一个多项式查表写错了,发出去的CMD0一直带CRC错误,Device直接无视。

3.2 响应类型:Device怎么回

Device收到命令后,通过CMD线回响应。响应长度分两种:48位(短响应)和136位(长响应)。

为什么要分长短?你想,有些响应只需要传个状态(比如R1、R6),48位就够了;但有些响应要传CID、CSD这种一大串寄存器内容,48位装不下,就得用136位。

类型 长度 典型命令 说明
R1 48位 大多数命令 标准状态响应,32位状态标志
R2 136位 CMD2、CMD9、CMD10 CID/CSD寄存器内容
R3 48位 CMD5、CMD8 OCR寄存器内容
R4 48位 CMD6(切换功能) 模式切换状态
R5 48位 中断请求相关 R5比较特殊,项目用得少
重点关注:HS400模式下,我们打交道最多的是R1和R2。R1里的32位状态字,藏着Device的健康状况。比如bit 19是切换错误(SWITCH_ERROR),我曾经在一个板子上发现Device频繁报告这个错误,最后查出是电压不稳导致的。

3.3 数据块传输机制:怎么传大批量数据

说到数据块传输,HS400模式很特殊。它用的是8位并行总线(也就是DATA0~DATA7),并且是双沿采样——时钟上升沿和下降沿都采样数据。这意味着数据传输率直接翻倍。

说白了,HS400在物理层就是一个“双倍速”的并行通道。Host和Device之间传数据,都是以块为单位。HS400支持的块大小默认是512字节,但也可以配置成1024字节等。

// 数据块传输的典型流程(伪代码)
// 1. Host发命令,告诉Device要读写
send_command(CMD18, start_block_address);  // 多块读

// 2. Device返回响应
resp = wait_response(R1);

// 3. 检查响应状态
if (resp.status & CARD_STATUS_READY_FOR_DATA) {
    // 4. 开始数据传输(HS400模式下)
    // 每个时钟沿传输8位,所以一个块512字节需要256个时钟周期
    for (int i = 0; i < block_count; i++) {
        read_data_block(buffer + i * 512, 512);  // 包含CRC16验证
    }
} else {
    // 设备没准备好,重试或报错
    handle_error();
}

这个过程里有个细节:每一个数据块传输完,Device会追加2字节的CRC16校验码。Host读到后,要用相同的多项式再算一遍,如果对不上,就得重传这个块。

我记得有一次在产品量产阶段,发现个别板子在大批量读写时会偶发CRC16校验失败。排查后发现是PCB走线过长导致信号完整性问题。所以你看,协议层面有校验保护,但物理层的问题还是要靠硬件设计来解决。

3.4 CRC校验:防错机制

eMMC用了两种CRC:

  • CRC7:用于命令和短响应,保护49位数据(命令索引+参数,或响应内容)
  • CRC16:用于数据块传输,保护整个数据块

CRC7的多项式是:x^7 + x^3 + 1(对应二进制0x89)。CRC16的多项式是:x^16 + x^12 + x^5 + 1(对应0x1021)。

这里有个常见坑:CRC7算的是“左对齐”的,也就是要先把数据左移7位再除多项式。我第一次实现时抄了一个网上查表法,结果跟eMMC规范对不上,折腾了老半天才发现是初始值和输出差异的问题。

避坑指南:我曾经在实现CRC7软件计算时,发现Device老是提示CRC错误。后来仔细看规范才知道,eMMC的CRC7算法要求初始值为0x00,而不是常见的0x7F。这一字之差,让我浪费了整整一天。记住:严格按规范,不要想当然地复用其他协议的代码。

3.5 本章知识总览

为了让你一眼看清这三大部分的关系,我画了这张图:

eMMC总线协议基础:命令 → 响应 → 数据传输 Host(主机) Device(设备) CMD线:命令 CMD线:响应 DATA0~7:数据块传输(HS400双沿采样) CRC7:命令/响应保护 CRC16:数据块保护 关键交互时序 1. Host通过CMD线发送48位命令(含CRC7) 2. Device通过CMD线返回响应(48或136位,含CRC7) 3. 通过DATA线传输数据块(每块后附CRC16)

3.6 实战小结

这一章我们拆解了协议最核心的三部分:命令格式、响应类型、数据块传输与CRC。你不一定要背下每个位域的值,但一定要理解它们的“职责”:

  1. 命令是Host发出的48位“指令信封”,Device必须解码并校验CRC7后才能执行。
  2. 响应是Device的回执,R1最常用,R2用来获取寄存器内容。
  3. 数据块传输在HS400模式下利用8位并行双沿采样,每块带CRC16校验,保证数据完整性。
  4. CRC7和CRC16是eMMC最底层的防错机制,实现时一定要对照规范,否则“差之毫厘,谬以千里”。
工程师小贴士:实际调试时,用逻辑分析仪抓CMD线,配合Protocol Analyzer解析出命令和响应的细节,是最快定位问题的方法。别闷头看代码,让波形说话。