嵌入式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. 停止命令时序

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

相关推荐
pandyele5 小时前
单片机死机问题处理
单片机·嵌入式
JaneZJW1 天前
嵌入式岗位面试八股文(篇三 操作系统(下))
linux·stm32·面试·嵌入式·c
JaneZJW2 天前
嵌入式岗位面试八股文(篇三 操作系统(上))
linux·单片机·面试·操作系统·嵌入式
Bull-man2 天前
LS1046 XFI网口接近10Gbps
linux·arm开发·嵌入式
xachary4 天前
Arduino 小白的 DIY 空气质量检测仪(5)- OLED显示模块、按钮模块
物联网·嵌入式·arduino
番茄大杀手5 天前
使用kendryte官方的C SDK 和toolchain 对k210进行开发
嵌入式·k210
委员7 天前
Arduino中借助LU-ASR01实现语音识别
单片机·嵌入式·语音识别·arduino·nodemcu·lu-asr01·天问block
憧憬一下8 天前
RK3399 PCIe 中断处理与映射分析(INTx中断机制源码分析)
arm开发·嵌入式硬件·嵌入式·linux驱动开发·pci/pcie
7yewh13 天前
Linux驱动开发 IIC I2C驱动 编写APP访问EEPROM AT24C02
linux·arm开发·驱动开发·嵌入式硬件·嵌入式