4. SDIO命令集
说到SDIO协议,核心就是命令的交互。没有命令,主机和卡就是两个哑巴。我刚开始接触SDIO时,被那堆命令号搞得有点懵。其实没那么复杂。
所有SDIO命令都是48位宽。你想想看,这48位里包含了起始位、方向、命令索引、参数,再加上CRC校验和结束位。结构很规整。
命令格式(48位)
- Bit 47: 起始位(总是0)
- Bit 46: 方向位(1=主机→卡)
- Bit [45:40]: 命令索引(6位,支持64个命令)
- Bit [39:8]: 参数(32位,具体含义看命令)
- Bit [7:1]: CRC7校验
- Bit 0: 结束位(总是1)
4.1 基本命令:SDIO操作的基石
基本命令是所有SD/O/SDIO卡共用的。它们定义了卡的生命周期管理。从识别、初始化到状态切换,都靠这些命令。
我个人习惯把基本命令分成三组:控制类、状态类、配置类。
| 分类 | 命令 | 功能简述 | 我的使用心得 |
|---|---|---|---|
| 控制类 | CMD0 (GO_IDLE_STATE) | 复位卡到空闲状态 | 硬件复位后必须发,但注意CMD0后别急着发其他命令,给卡留点反应时间 |
| 控制类 | CMD15 (GO_INACTIVE_STATE) | 将指定卡置于非激活态 | 多卡应用时用它断掉不用的卡,省电 |
| 状态类 | CMD13 (SEND_STATUS) | 获取卡当前状态 | 我调试时最常用的命令,没有之一 |
| 配置类 | CMD52 (IO_RW_DIRECT) | 读写SDIO卡内部寄存器(1字节) | SDIO的灵魂命令,后面细说 |
个人经验:CMD13这玩意儿太重要了。每次执行耗时的操作(比如擦除、切换电压)之后,我习惯先发CMD13确认卡的状态,再发下一步操作。卡没准备好你就发读写,结果就是超时。
4.2 读写命令:数据吞吐的关键
读写命令是Benchmark测试的核心。吞吐跑不上去,八成是读写命令配置有问题。
SDIO的读写命令分两种:单块读写和多块读写。单块用于小数据量,多块用于大数据量传输。
- CMD53 (IO_RW_EXTENDED) — SDIO的扩展读写命令,支持多字节和多块传输。这个命令是SDIO吞吐测试的主角。
- CMD17/CMD18 — 用于存储卡的单块/多块读取。
- CMD24/CMD25 — 用于存储卡的单块/多块写入。
讲个例子。CMD53的参数结构是这样的:
Bit [31]: 读写方向(1=读,0=写)
Bit [30:28]: 功能号(0~6,对应SDIO卡的7个功能)
Bit [27]: 块模式(1=按块传输,0=按字节传输)
Bit [26]: 操作码(1=多块,0=单块)
Bit [25:9]: 寄存器地址(17位)
Bit [8:0]: 字节数或块数(9位)
注意!CMD53的数据长度上限是512字节(或512块)。如果单次传输超过这个限制,必须分多次进行。
我见过有人踩坑:一次CMD53想传2KB数据,结果卡直接返回超时。后来发现是因为没有理解块模式下的块数限制。
4.3 ACMD:隐藏在标准命令后的秘密
ACMD是什么?字面意思是Application-Specific Command。其实就是私有命令,但需要通过CMD55(APP_CMD)来引导。
为什么要这么设计?说白了,标准命令集只有64个,不够用。于是SD协会留了个后门:先用CMD55告诉卡“接下来我要发私有命令了”,然后再发ACMD。
ACMD的操作流程是这样的:
- 主机发送CMD55,带上目标卡RCA。
- 卡返回R1响应,如果响应中的APP_CMD位(bit 5)为1,表示卡准备好接收ACMD。
- 主机再发送ACMDx(注意ACMD的命令号映射到普通命令索引上)。
- 卡执行并返回响应。
我曾经踩过一个坑:发CMD55后,不等响应位就立刻发ACMD。结果卡报错。后来养成习惯:发完CMD55,一定要检查响应中的APP_CMD位,确认置1后再发ACMD。
常用的ACMD有:
- ACMD6 (SET_BUS_WIDTH) — 设置总线宽度(1位或4位)。想跑高吞吐?记得设置成4位模式。
- ACMD51 (SEND_SCR) — 读取卡的配置寄存器(SCR),获取卡支持的特性。
- ACMD42 (SET_CLR_CARD_DETECT) — 控制卡检测引脚。
我这里有一个常用的ACMD结构图,帮你理清关系:
4.4 命令响应类型:主机知道卡在说什么
命令发出去,卡得回话。SDIO定义了5种响应类型:R1、R2、R3、R4、R5。但SDIO卡最常用的是R1和R5。
| 响应类型 | 长度 | 使用场景 | 关键位说明 |
|---|---|---|---|
| R1 | 48位 | 大多数命令(CMD13、CMD55等) | 包含卡当前状态和错误标志 |
| R3 | 48位 | CMD5(SDIO SEND_OP_COND) | 返回OCR寄存器的内容 |
| R5 | 48位 | CMD52、CMD53(SDIO特定命令) | 包含功能号、读写标志、数据位 |
| R2 | 136位 | CMD2、CMD9(CID、CSD读取) | 长响应,包含128位寄存器数据 |
避坑:好多人在处理R5响应时会忽略最高两位。CMD52的R5响应在高位包含了读写标志和功能号。如果不校验这俩位,你可能会读到错误的数据而浑然不知。
我有个同事,就因为没检查R5的读写标志,把读出来的数据当寄存器内容分析了两天,才发现读反了。
4.5 实战建议:如何用命令集测吞吐
回到我们的主题——吞吐测试。基于上面的命令集知识,我一般会分几步走:
- 初始化阶段:发CMD0复位,CMD5获取OCR,CMD3获取RCA,CMD7选中卡。
- 功能选择:CMD52写入功能接口的I/O使能寄存器,唤醒具体的功能。
- 配置总线:CMD55 + ACMD6设置4位总线宽度。
- 吞吐测试:大量使用CMD53(扩展读写),选择合适的块大小和块数。
- 状态监控:每发一批数据,插几个CMD13看看卡的状态有没有异常。
你想想看,这一步一步下来,节奏就掌握在你手里了。吞吐测试不是蛮力发数据,而是精细控制的过程。
小技巧:我用CMD52读取卡的功能寄存器和中断状态寄存器,来判断卡是否准备好接受新数据。这样比单纯依赖超时机制靠谱得多。超时是最后一道防线,不是常规手段。