嵌入式SD/TF卡通用协议-SDIO协议

SD卡(SecureDigital MemoryCard)即:安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡。

一、SD卡内部结构-内部寄存器

|-----|-------|----------------------------------------------|
| 名称 | bit宽度 | 描述 |
| OCR | 32 | OCR记录了电压范围、卡的工作模式(如是否支持高容量模式)以及卡是否准备好进行数据传输。 |
| CID | 128 | 通常用于获取卡的制造商ID和产品型号等。 |
| RCA | 16 | 用于标识SD卡的相对地址。在初始化阶段,SD卡会通过RCA与主机系统建立连接。 |
| DSR | 16 | 用于SD卡驱动阶段的控制,主要控制SD卡的电气特性和速率。 |
| CSD | 128 | 包含有关SD卡具体规格的详细信息,包括卡的容量、访问模式、速度等级等。 |
| SCR | 64 | 包含SD卡的配置和特性,如支持的功能、卡的速度等级等。 |
| SSR | 512 | 它包含卡的工作状态、错误状态、命令结果等信息。 |
| CSR | 32 | 显示SD卡当前的状态,包括是否准备好进行读写操作、卡的健康状态等。 |

二、SD卡硬件接线

为了驱动SD卡,首先的第一步就是观察SD卡引脚接线情况,通常我们在画原理图时,都是使用的SD卡卡槽与MCU进行接线。SD卡可以使用SDIO协议和SPI协议进行通信,这里我们主要介绍SDIO协议。下图中的TF卡只是容量小一点的SD卡。

|-----|-------|-------|
| 引脚号 | SD卡 | TF卡 |
| 1 | data3 | data2 |
| 2 | cmd | data3 |
| 3 | vss | cmd |
| 4 | vdd | vdd |
| 5 | clk | clk |
| 6 | vss | vss |
| 7 | data0 | data0 |
| 8 | data1 | data1 |
| 9 | data2 | |

SD卡硬件接线图:

其中


TF卡硬件接线图:

以F1C100S为例,接线图如下所示:

SDIO总线和USB总线类似,SDIO总线也有两端,其中一端是主机(HOST)端,另一端是设备端(DEVICE),采用HOST- DEVICE这样的设计是为了简化DEVICE的设计,所有的通信都是由HOST端发出命令开始的。在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了,SDIO的HOST可以连接多个DEVICE。

我们发现上图中的TF卡使用的协议为SDIO协议,主要有4根数据线,1根命令线,1根时钟线组合而成。为了驱动这个SD/TF卡我们就需要去了解SDIO协议的时序。

三、SD卡命令及其配置流程

1. 常用CMD命令

|------------------|--------|---------|----------|-------------------------------------------------------------------------|
| 命令类型 | 命令 | 命令号 | 响应类型 | 功能描述 |
| 基本命令(class0) | CMD0 | 0x00 | - | 复位所有卡的状态为空闲状态。 |
| 基本命令(class0) | CMD2 | 0x02 | R2 | 获取卡的CID。 |
| 基本命令(class0) | CMD3 | 0x03 | R6 | 获取卡的相对地址(RCA)。 |
| 基本命令(class0) | CMD7 | 0x07 | R1b | 选择卡并切换到传输状态。(OCR) |
| 基本命令(class0) | CMD8 | 0x08 | R7 | 询问卡是否支持主机支持电压。 |
| 基本命令(class0) | CMD9 | 0x09 | R2 | 获取卡的CSD。 |
| 基本命令(class0) | CMD12 | 0x0C | R1b | 停止传输命令(用于终止数据传输)。 |
| 基本命令(class0) | CMD13 | 0x0D | R1 | 读取 SD 卡的 Card Status Register(卡状态寄存器)。 |
| 基本命令(class0) | CMD15 | 0x0F | - | 释放卡的访问控制,解除卡锁定。 |
| 基本命令(class0) | CMD16 | 0x10 | R1 | 用于指定卡的数据块大小,通常是512字节或者更大的块。 |
| 读取命令(class2) | CMD17 | 0x11 | R1 | 读取单个数据块。标准 SD读取数据块长度受 CMD16 控制。对于 SDHC、SDXC卡,读取数据块为固定的 512字节,不受CMD16 控制 |
| 读取命令(class2) | CMD18 | 0x12 | R1 | 读取多个数据块。块大小由 CMD16 控制。 |
| 擦除命令(class3) | CMD32 | 0x20 | R1 | 设置要擦除的第一个块的地址(擦除起始地址)。 |
| 擦除命令(class3) | CMD33 | 0x21 | R1 | 设置要擦除的最后一个块的地址(擦除结束地址)。 |
| 擦除命令(class3) | CMD38 | 0x26 | R1b | 从块的起始位置到结束位置执行擦除操作的命令。 |
| 写入命令(class4) | CMD24 | 0x18 | R1 | 写入单块数据。块大小由 CMD16 控制。 |
| 写入命令(class4) | CMD25 | 0x19 | R1 | 写入多个数据块。块大小由 CMD16 控制。 |
| 特殊命令(class8) | CMD55 | 0x37 | R1 | 发送前置命令,告知卡后续要执行ACMD命令。 |
| 特殊命令(class8) | ACMD6 | 0x06 | R1b | 配置卡的总线宽度。 |
| 特殊命令(class8) | ACMD23 | 0x17 | R1 | 告诉SD卡后续要写入多少个数据块。 |
| 特殊命令(class8) | ACMD41 | 0x29 | R3 | 启动卡并查询OCR响应,卡上电完成。 |
| 特殊命令(class8) | ACMD51 | 0x33 | R1 | 读取SD卡的SD 状态寄存器。 |

2. 响应类型

在SD卡命令中,响应类型是指SD卡在接收到命令后返回的响应格式,所有响应都是通过CMD信号线发送。不同命令会有不同的响应类型,这些响应类型决定了卡返回数据的结构。响应类型的关键是根据命令和数据位数来判断的。根据响应类型,可以知道要解析多少个字节,以及如何解读响应内容。

SDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。

根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

而有些响应后面会带有 ' b' 标志,表示 " 忙 "标志。用于命令执行期间卡可能需要一些时间来完成操作。响应中会有一个"忙"位,表示卡是否仍在执行某些操作。

(1)短响应

R1响应(正常响应命令):R1响应会返回32位的卡状态。

|------|-----|-----|-----------|----------|---------|-----|
| Bit位 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
| 位宽 | 1 | 1 | 6 | 32 | 7 | 1 |
| 值 | "0" | "0" | x | x | x | "1" |
| 描述 | 开始位 | 传输位 | 命令号 | 卡状态 | CRC7 | 结束位 |

R1b响应:和R1响应差不多,但是R1b响应在数据线上会有busy信号,主机在发送完数据后,应该检查 busy 信号。

|------|-----|-----|-----------|----------|---------|-----|
| Bit位 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
| 位宽 | 1 | 1 | 6 | 32 | 7 | 1 |
| 值 | "0" | "0" | x | x | x | "1" |
| 描述 | 开始位 | 传输位 | 命令号 | 卡状态 | CRC7 | 结束位 |

**R3响应(OCR 寄存器):**R3响应会返回32位的OCR寄存器的值作为ACMD41的响应。

|------|-----|-----|-----------|----------|----------|-----|
| Bit位 | 47 | 46 | [45:40] | [39:8] | [7:1] | 0 |
| 位宽 | 1 | 1 | 6 | 32 | 7 | 1 |
| 值 | "0" | "0" | "111111" | x | "111111" | "1" |
| 描述 | 开始位 | 传输位 | 保留位 | OCR寄存器 | 保留位 | 结束位 |

R6响应(CMD3专用响应):

|------|-----|-----|-----------|----------------|--------------|---------|-----|
| Bit位 | 47 | 46 | [45:40] | [39:8] || [7:1] | 0 |
| 位宽 | 1 | 1 | 6 | 16 | 16 | 7 | 1 |
| 值 | "0" | "0" | "000011" | x | x | x | "1" |
| 描述 | 开始位 | 传输位 | 命令号CMD3 | [31:16]卡的RCA | [15:0]卡的状态 | CRC7 | 结束位 |

**R7响应(CMD8专用响应):**Bit[19:16]表明卡支持的电压范围。卡接受提供的电压范围就返回 R7 响应。卡会在响应的参数中返回电压范围和检查模式。

|------|-----|-----|-----------|-----------|-----------|----------|---------|-----|
| Bit位 | 47 | 46 | [45:40] | [39:20] | [19:16] | [15:8] | [7:1] | 0 |
| 位宽 | 1 | 1 | 6 | 20 | 4 | 8 | 7 | 1 |
| 值 | "0" | "0" | "001000" | "00000h" | x | x | x | "1" |
| 描述 | 开始位 | 传输位 | 命令号CMD8 | 保留位 | 接受电压 | 检查模式 | CRC7 | 结束位 |

(2)长响应

**R2响应(CID,CSD 寄存器):**R2响应会返回SD卡中CID或者CSD寄存器的值。其中CID寄存器的值用于CMD2和CMD10响应,CSD寄存器的值用于CMD9响应。

|------|-----|-----|-------------|----------------------|-----|
| Bit位 | 135 | 134 | [133:128] | [127:1] | 0 |
| 位宽 | 1 | 1 | 6 | 127 | 1 |
| 值 | "0" | "0" | "111111" | x | "1" |
| 描述 | 开始位 | 传输位 | 保留位 | CID/CSD寄存器的值(内部CRC7) | 结束位 |

(2)命令和响应使用举例

以F1C100s举例,该芯片中有4个响应寄存器,如下所示。当我们发送命令后需要接收响应时,要根据不同的响应类型来获取响应寄存器的值。

例如短响应通常只需要 1 个响应寄存器**,** 响应数据会被放入响应寄存器0(通常是 SD0_RESP0),因此,获取响应时只需要读取 SD0_RESP0 即可。

cpp 复制代码
// 假设响应数据保存在 sd_command.response 数组中
sd_command.cmdidx    = 3;   //CMD3
sd_command.cmdarg    = 0;   // 根据具体命令的参数设置
sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_PRESENT;
//SD_RESPONSE_PRESENT:设置需要响应。 SD_RESPONSE_CRC :CRC校验。
result = sd_card_send_command(&sd_command, 0);

// 读取四个响应寄存器的值
uint32 response= sd_command.response[0];  //获取响应寄存器0的值

对于长响应,通常需要读取 4 个响应寄存器(如 SD0_RESP0, SD0_RESP1, SD0_RESP2, SD0_RESP3),SD0_RESP0 存储高位部分。

cpp 复制代码
// 假设响应数据保存在 sd_command.response 数组中
sd_command.cmdidx    = 2;   //CMD2
sd_command.cmdarg    = 0;   // 根据具体命令的参数设置
sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_PRESENT | SD_RESPONSE_136;
//SD_RESPONSE_PRESENT:设置需要响应。 SD_RESPONSE_CRC :CRC校验。SD_RESPONSE_136:长响应。
result = sd_card_send_command(&sd_command, 0);

// 读取四个响应寄存器的值
uint32 response0 = sd_command.response[0];  //获取响应寄存器0的值
uint32 response1 = sd_command.response[1];  //获取响应寄存器1的值
uint32 response2 = sd_command.response[2];  //获取响应寄存器2的值
uint32 response3 = sd_command.response[3];  //获取响应寄存器3的值

3. SD卡初始化流程

(1)初始化GPIO功能,SDIO时钟不超过400kHz。

(2)开启SDIO电源,为卡提供时钟,并延时两毫秒。

(3)发送CMD0复位命令,让卡到空闲状态。

(4)发送CMD8命令,询问卡是否支持主机支持电压。根据电压值判断SD卡支持版本。

(5)反复发送ACMD41(先发送CMD55,再发送ACMD41),等待OCR回应,卡上电完成和卡容量类型。如果正常则进入准备状态。

(6)发送CMD2,进入识别状态,获得卡信息(CID)。包含卡的制造商、产品ID等信息。

(7)发送CMD3,获得卡的相对地址(RCA)。这个地址用于区分不同的卡,并在后续的操作中标识当前使用的卡。进入待机状态(下一步就要进行数据传输)。

(8)发送CMD9,获得卡的数据(CSD)。包含卡的规格、容量、访问模式等信息。

(9)发送CMD7,选择卡,切换到传输状态。此时,SD卡已经准备好进行数据的读写操作。

(10)检查卡是否被锁定,卡被锁定时不能进行写操作,通常用于保护卡内的数据不被修改。我们在写操作时,SD卡要处于非锁定模式。

(11)通过读取SD卡的SCR寄存器,判断卡是否支持宽总线(4位数据线宽度)。如果支持宽总线,则可以提高数据传输速率。

(12)发送ACMD6(先发送CMD55,再发送ACMD6),修改总线宽度,(如从1位宽设置为4位宽)。

4. SD卡写操作流程

(1)发送**CMD7**命令选择SD卡,确保卡处于"传输状态"。

(2)设置块大小,通过**CMD16**命令设置块大小(通常是512字节),如果不设置,则默认为512字节。

(3)配置SDIO的数据路径状态机DPSM(这个通常是硬件配置步骤,不需要通过命令发送)。在配置块大小后,需要配置SDIO(SD卡接口)的数据路径状态机(DPSM)来处理数据的传输。DPSM 控制数据传输的流动,它会确保数据能够被正确地写入到SD卡中。这个步骤通常是在硬件层面配置的,与命令流程关系不大,但非常重要。

(3)SD卡使用 CMD24 (单块写命令)或 CMD25(多块写命令)来执行数据写操作。命令需要包含块地址和要写入的数据。

(4)发送命令 CMD13轮询SDIO状态寄存器:监控传输过程,等待数据完成写入FIFO。

TXFIFO_EMPTY:表示发送 FIFO(数据缓冲区)为空,意味着数据已经传输完成。

RXFIFO_READY:表示接收 FIFO 准备好接收数据。

CMD_DONE:表示命令已经执行完成。

ERROR:表示传输过程中出现了错误。

(5)发送停止传输命令(CMD12):当数据传输完成后,在需要停止传输时使用。
单块写:

多块写:

5. SD卡读操作流程

(1)发送**CMD16**设置块大小,并配置 SDIO 的数据路径状态机。

(2)发送 CMD18连续读取命令,读取多个数据块直到完成。如果是单块读取,发送CMD17 单块读取命令。

(3)发送命令 CMD13 轮询 SDIO 状态寄存器,读取 FIFO 中的数据,直到数据接收完毕。

(4)数据传输结束后,发送 CMD12 强制停止传输命令。

(5)检查 FIFO 是否为空,如果不为空,则继续读取剩余数据。

(6)检查错误状态,确保数据读取无误,如果出现错误则处理。
单块读:

多块读:

6. SD卡擦除流程

(1)SD状态检查,参数检査、命令支持检查、检查卡类型:在进行任何写入操作之前,需要检查 SD 卡的状态,以确保卡处于正常工作状态。这通常包括检查卡是否处于准备状态、卡是否支持擦除操作等。在擦除操作前,需要确保所选参数(如要擦除的起始块和结束块的地址)是有效的。确认 SD 卡是否支持擦除命令。

(2)发送 **CMD32**设置要擦除的起始块的地址:在执行该命令后,卡会将起始地址加载到擦除缓冲区,并准备好擦除操作。

(3)发送**CMD33**设置要擦除的最后一个块的地址:该命令告诉卡擦除的结束地址,范围包括从起始块到结束块。

(4)发送 CMD38 擦除选定的块:CMD38 命令会要求卡擦除从起始块到结束块之间的所有数据块。

(5)擦除完成后的检查:发送命令 CMD13轮询 SDIO 状态寄存器,检查是否存在错误。如果没有错误,擦除操作成功;如果有错误,系统需要根据错误码采取适当的恢复措施。

四、SDIO协议数据包格式

1. 单线传输模式

单线模式下,只有一根数据线 DAT0 传输数据。所以必须要有传输方向位来明确数据的流向。

传输方向位:1:主机传输到SD卡, 0:SD卡传输到主机。

2. 多线传输模式

在多线传输模式下,有4 条数据线(DAT0DAT3)并行完成的,传输方向由 命令类型 和 数据线的硬件状态 决定,不需要额外的传输方向位。主机通过命令明确告诉 SD 卡当前的操作是读还是写(例如 CMD17 是读单块,CMD24 是写单块),因此数据的方向是由命令预先确定的。多线模式强调高效并行传输,省去了传输方向位这一单线模式中的冗余信息。

五、SD卡相关时序分析

1. SD卡识别和卡启动时序(CMD2/ACMD41命令时序)

CMD2 和 ACMD41 的时序如下。命令后跟着两个 Z bit(允许总线切换方向的时间),接着是响应卡发出的 P bit。卡响应主机命令的起始在 Nid 时钟周期后。

2. SD卡发送相对地址时序(CMD3命令时序)

3. SD卡两个命令发送间隔时序(命令时序)

SD卡两个命令发送之间的时序,通常称为命令间隔时序,用来确保主机和卡之间的通信稳定,同时为卡提供充足的时间完成当前命令的处理。

主机在接收到卡的响应后,必须等待 至少 8 个时钟周期,确保 CMD 线为高电平空闲状态,才可发送下一条命令。

4. SD卡读时序

在SD卡的操作中,数据读取(尤其是单块读取)需要通过特定的命令来控制和配置。

(1)单块读

主机通过 CMD7 来选择一个卡进行数据读取操作,通过 CMD16 来设置需要传送数据的有效块长度。读操作的基本总线时序见图4-18。序列以单块读命令(CMD17)开始,CMD17 在参数中指出了起始地址。响应也通过CMD 线发送。

(2)多块读

多块读模式中,在主机读命令之后,卡发送一个连续的数据块流。通过CMD12来中止。

5. 停止命令时序

数据传输在停止命令后的两个时钟周期停止。

相关推荐
乔碧萝成都分萝15 小时前
二十四、Linux如何处理中断
linux·驱动开发·嵌入式
程序员老舅17 小时前
【无标题】
c++·嵌入式·八股文·c++八股文·八股文面试题·c++面经·c++面试题
charlie11451419118 小时前
现代嵌入式C++教程:对象池(Object Pool)模式
开发语言·c++·学习·算法·嵌入式·现代c++·工程实践
π同学2 天前
基于RT-Thread的STM32开发第十二讲SD卡篇——DFS文件系统
stm32·dfs·rtthread·sdio
我是海飞2 天前
杰理 AC792N 使用 WebSocket 连接百度语音大模型,实现 AI 对话
c语言·单片机·嵌入式·ai对话·杰理·websockey
不凉帅2 天前
NO.2计算机基础
网络·嵌入式·硬件·软件·计算机基础
时光の尘2 天前
【STM32】两万字详解SD卡移植最新版本FatFs文件系统(ff16)
stm32·mcu·dma·sd·fatfs·sdio·ff16
PinoLio3 天前
鲁班猫烧录镜像win10平台
嵌入式·鲁班猫
不脱发的程序猿4 天前
使用Python高效对比多个相似的CAN DBC数据
python·单片机·嵌入式硬件·嵌入式
皮蛋sol周4 天前
嵌入式学习数据结构(二)双向链表 内核链表
linux·数据结构·学习·嵌入式·arm·双向链表