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

    

目录

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

1.CSD寄存器

2.OCR寄存器

3.CID寄存器

4.SCR寄存器

二、SD卡拓扑结构

三、SD卡硬件接线

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

1. 常用CMD命令

2. 响应类型

(1)短响应

(2)长响应

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

3. SD卡初始化流程

4. SD卡写操作流程

5. SD卡读操作流程

6. SD卡擦除流程

五、SDIO协议数据包格式

1. 单线传输模式

2. 多线传输模式

六、SD卡相关时序分析

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

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

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

4. SD卡读时序

(1)单块读

(2)多块读

5. 停止命令时序

七、实践一F1C100s使用SD卡


    

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

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

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

1.CSD寄存器

2.OCR寄存器

3.CID寄存器

4.SCR寄存器

二、SD卡拓扑结构

        SD卡总线拓扑参考图 SD卡总线拓扑。虽然可以共用总线, 但不推荐多卡槽共用总线信号,要求一个单独SD总线应该连接一个单独的SD卡。当一个主机SD总线上有多个SD卡时,就会通过SD卡的RCA地址来区分每个卡。

三、SD卡硬件接线

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

引脚号SD卡TF卡
1data3data2
2cmddata3
3vsscmd
4vddvdd
5clkclk
6vssvss
7data0data0
8data1data1
9data2

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)CMD00x00-复位所有卡的状态为空闲状态。
CMD20x02R2获取卡的CID。
CMD30x03R6获取卡的相对地址(RCA)。
CMD70x07R1b选择卡并切换到传输状态。(OCR)
CMD80x08R7询问卡是否支持主机支持电压。
CMD90x09R2获取卡的CSD。
CMD120x0CR1b停止传输命令(用于终止数据传输)。
CMD130x0DR1读取 SD 卡的 Card Status Register(卡状态寄存器)。
CMD150x0F-释放卡的访问控制,解除卡锁定。
CMD160x10R1用于指定卡的数据块大小,通常是512字节或者更大的块。
读取命令(class2)CMD170x11R1读取单个数据块。标准 SD读取数据块长度受 CMD16 控制。对于 SDHC、SDXC卡,读取数据块为固定的 512字节,不受CMD16 控制
CMD180x12R1读取多个数据块。块大小由 CMD16 控制。
擦除命令(class3)CMD320x20R1设置要擦除的第一个块的地址(擦除起始地址)。
CMD330x21R1设置要擦除的最后一个块的地址(擦除结束地址)。
CMD380x26R1b从块的起始位置到结束位置执行擦除操作的命令。
写入命令(class4)CMD240x18R1写入单块数据。块大小由 CMD16 控制。
CMD250x19R1写入多个数据块。块大小由 CMD16 控制。
CMD270x1BR1对 CSD 的可编程位进行编程
特殊命令(class8)CMD550x37R1发送前置命令,告知卡后续要执行ACMD命令。
ACMD60x06R1b配置卡的总线宽度。
ACMD230x17R1告诉SD卡后续要写入多少个数据块。
ACMD410x29R3启动卡并查询OCR响应,卡上电完成。
ACMD510x33R1读取SD卡的SCR寄存器。

2. 响应类型

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

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

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

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

(1)短响应

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

Bit位4746[45:40][39:8][7:1]0
位宽1163271
"0""0"xxx"1"
描述开始位传输位命令号卡状态CRC7结束位

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

Bit位4746[45:40][39:8][7:1]0
位宽1163271
"0""0"xxx"1"
描述开始位传输位命令号卡状态CRC7结束位

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

Bit位4746[45:40][39:8][7:1]0
位宽1163271
"0""0""111111"x"111111""1"
描述开始位传输位保留位OCR寄存器保留位结束位

R6响应(CMD3专用响应):

Bit位4746[45:40][39:8][7:1]0
位宽116161671
"0""0""000011"xxx"1"
描述开始位传输位命令号CMD3[31:16]卡的RCA[15:0]卡的状态CRC7结束位

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

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

(2)长响应

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

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

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

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

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

// 假设响应数据保存在 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= *SD0_RESP0;   //获取响应寄存器0的值

        对于长响应,通常需要读取 4 个响应寄存器(如 SD0_RESP0, SD0_RESP1, SD0_RESP2, SD0_RESP3),SD0_RESP0 存储低位部分[31:0],依次类推。

// 假设响应数据保存在 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 = *SD0_RESP0;  //获取响应寄存器0的值
uint32 response1 = *SD0_RESP1;   //获取响应寄存器1的值
uint32 response2 = *SD0_RESP2;   //获取响应寄存器2的值
uint32 response3 = *SD0_RESP3;   //获取响应寄存器3的值

3. SD卡初始化流程

(1)SD 卡初始化时,SDIO_CK时钟不可超过 400KHz;初始化完成后,可设为最大(不可超过 SD 卡最大操作频率 25MHz))并可更改数据总线宽度(默认只用 SDIO_D0数据线进行初始化)。

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

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

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

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

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

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

(8)发送CMD9将获取到的SD卡的RCA地址作为参数传入SD参数寄存器中,获得卡的数据(CSD)。包含卡的规格、容量、访问模式等信息。

(9)发送CMD7将获取到的SD卡的RCA地址作为参数传入SD参数寄存器中,用于选择该卡,切换到传输状态。此时,SD卡已经准备好进行数据的读写操作。

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

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

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

补充知识点:如何设置SDIO的频率呢?以F1C100s为例。

        答:通过配置F1C100S的CCU寄存器。CCU 是 Clock Control Unit(时钟控制单元) 的简称,它是一个用于管理和控制芯片内部时钟源的硬件模块。通过 CCU 寄存器,系统可以动态地设置各个模块的时钟频率、启用或禁用特定的时钟,以及选择时钟源等。

        假设我们需要设置SDIO的时钟频率为400khz,我们可以选择OSC24M的时钟源,然后进行选择预分频和主分频比。

答:可以设置预分频为4,主分频为14。频率=24M /(4*(14+1))= 400khz。即:

 *CCU_SDMMC0_CLK |= ( 0x80000000 | (0x00 << 24)| (0x00 << 20) | (0x4 << 16) | (0x00 << 8) | (15 - 1));

4. SD卡写操作流程

(1)发送CMD7命令,将初始化获取到的SD卡的RCA地址作为参数传入SD参数寄存器中,用于选择对应的SD卡,确保卡处于“传输状态”。

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

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

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

(4)轮询读取SDIO状态寄存器(查看F1C100S手册):监控传输过程,等待数据完成写入FIFO。

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

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

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

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

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

单块写:

多块写: 

5. SD卡读操作流程

(1)发送CMD7命令,将初始化获取到的SD卡的RCA地址作为参数传入SD参数寄存器中,用于选择对应的SD卡,确保卡处于“传输状态”。

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

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

(4)轮询读取SDIO状态寄存器(查看F1C100S手册),读取 FIFO 中的数据,直到数据接收完毕。

(5)数据传输结束后,发送 CMD12 强制停止传输命令。单块读时不需要发送,多块读时需要发送。

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

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

单块读:

多块读:

6. SD卡擦除流程

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

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

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

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

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

五、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. 停止命令时序

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

七、实践一F1C100s使用SD卡

       F1C100s芯片中有很多个专属SD卡的寄存器(详情查看手册即可),在发送命令或者数据时,我们只需要将命令或数据存放到寄存器即可,不需要手动发送。例如命令存放 F1c100的SD命令寄存器中,SD卡的控制器会自动通过CMD线将命令发送到SD卡。也就是说,不需要额外的手动操作通过CMD线发送命令。控制器本身会根据命令寄存器中的数据,控制数据总线(CMD线)来发送命令。

1. 定义相关结构体

struct tagSD_CARD_COMMAND
{
  uint16 cmdidx;       //命令
  uint32 resp_type;    //响应类型
  uint32 cmdarg;   //命令参数
  uint32 response[4];  //响应
};

struct tagSD_CARD_DATA
{
  uint8  *data;  //数据
  uint32  flags;  //标志
  uint32  blocks; //块数
  uint32  blocksize; //大小
};

typedef struct tagSD_CARD_COMMAND   SD_CARD_COMMAND;
typedef struct tagSD_CARD_DATA      SD_CARD_DATA;

SD_CARD_COMMAND sd_command;
SD_CARD_DATA    sd_data;

2. 通过寄存器设置基础配置

        例如将端口设置为SDIO模式、打开始终、设置频率等等。

3. 发送命令

#define SD_RESPONSE_NONE                 0x00000000
#define SD_RESPONSE_PRESENT              0x00000001
#define SD_RESPONSE_136                  0x00000002
#define SD_RESPONSE_CRC                  0x00000004
#define SD_RESPONSE_BUSY                 0x00000008
 
//Send reset command to the card
  sd_command.cmdidx    = 0;  //发送cmd0
  sd_command.cmdarg    = 0;
  sd_command.resp_type = SD_RESPONSE_NONE;
  sd_card_send_command(&sd_command, 0);
  //Wait a while for the card to reset
  sd_card_delay(50);
  //Send card interface condition command to the card
  sd_command.cmdidx    = 8;  //发送CMD8
  sd_command.cmdarg    = 0x00000155;        //31:12 reserved (0x00000), 11:8 supply voltage (0x1 = 2.7 - 3.6V), 7:0 check pattern (0x55)
  sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_PRESENT;
  result = sd_card_send_command(&sd_command, 0);
#define SD_CMD_START                     0x80000000
#define SD_CMD_SEND_INIT_SEQ             0x00008000
#define SD_CMD_RESP_EXPIRE               0x00000040
#define SD_CMD_LONG_RESPONSE             0x00000080
#define SD_CMD_CHK_RESPONSE_CRC          0x00000100
#define SD_CMD_DATA_EXPIRE               0x00000200
#define SD_CMD_WRITE                     0x00000400
#define SD_CMD_AUTO_STOP                 0x00001000
#define SD_CMD_WAIT_PRE_OVER             0x00002000
#define SD_CMD_UPCLK_ONLY                0x00200000
#define SD_STATUS_CARD_DATA_BUSY         0x00000200
#define SD_STATUS_FIFO_EMPTY             0x00000004
#define SD_STATUS_FIFO_FULL              0x00000008

int32 sd_card_send_command(PSD_CARD_COMMAND command, PSD_CARD_DATA data)
{
  uint32 cmdval = SD_CMD_START; //用于合成命令值
  int32  error = SD_OK;  //错误类型
  uint32 timeout;   //超时时间
  //判断命令是否为空
  if(command == 0) return(SD_ERROR);
  //判断命令号
  if(command->cmdidx == 12)     return(SD_OK);
  if(command->cmdidx == 0)      cmdval |= SD_CMD_SEND_INIT_SEQ;
  //判断响应类型
  if(command->resp_type & SD_RESPONSE_PRESENT)   cmdval |= SD_CMD_RESP_EXPIRE;  //
  if(command->resp_type & SD_RESPONSE_136)       cmdval |= SD_CMD_LONG_RESPONSE;  //长响应
  if(command->resp_type & SD_RESPONSE_CRC)       cmdval |= SD_CMD_CHK_RESPONSE_CRC; //CRC校验
  //如果有数据需要传递
  if(data){
      cmdval |=(SD_CMD_DATA_EXPIRE | SD_CMD_WAIT_PRE_OVER);
      if(data->flags & SD_DATA_WRITE)       cmdval |= SD_CMD_WRITE;
      if(data->blocks > 1)                  cmdval |= SD_CMD_AUTO_STOP;
      *SD0_BKSR = data->blocksize;
      *SD0_BYCR = data->blocks * data->blocksize;
  }
  //将命令参数加载到命令参数寄存器,将合成命令值加载到命令寄存器。
  *SD0_CAGR = command->cmdarg;
  *SD0_CMDR = command->cmdidx | cmdval;
  //如果有数据
  if(data){
    if( (error = sd_send_data(data)) )   goto out;
  }
  //等待命令完成。
  if((error = sd_rint_wait(1000, SD_RINT_COMMAND_DONE)))    goto out;
  //如果有数据
  if(data){
            if(data->blocks > 1)    cmdval = SD_RINT_AUTO_COMMAND_DONE;
            else                    cmdval = SD_RINT_DATA_OVER;
            //等待命令完成
            if((error = sd_rint_wait(120, cmdval)))     goto out;
  }
  //检查是否允许卡发送忙
  if(command->resp_type & SD_RESPONSE_BUSY){
        timeout = timer0_get_ticks() + 2000;  //设置2超时时间
        while(*SD0_STAR & SD_STATUS_CARD_DATA_BUSY){  //等待sd卡发送完毕
            if(timer0_get_ticks() > timeout){   //检查是否超时
                error = SD_ERROR_TIMEOUT;
                goto out;
            }
        }
  }
  //获取短响应
  command->response[0] = *SD0_RESP0;
  //获取长响应
	if(command->resp_type & SD_RESPONSE_136){
	    command->response[1] = *SD0_RESP1;
		command->response[2] = *SD0_RESP2;
		command->response[3] = *SD0_RESP3;
	}
out:
  if(error < 0){
    //重置 DMA、FIFO 和控制器
    *SD0_GCTL |= (SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST | SD_GCTL_SOFT_RST);
    sd_card_update_clock();  //更新时钟
  }
  *SD0_RISR = 0xFFFFFFFF;   //清除所有原始中断
  *SD0_GCTL |= SD_GCTL_FIFO_RST; //重置FIFO
  return(error);  
}

4. 完整代码(大概逻辑)

int32 sd_card_init(void)
{
  int32 result;
  //清空数据结构
  memset(&sd_command, 0, sizeof(SD_CARD_COMMAND));
  memset(&sd_data, 0, sizeof(SD_CARD_DATA));
  *PORT_F_CFG_REG = 0x222222;  //配置SDIO引脚
  *CCU_BUS_SOFT_RST0 |= CCU_BSRR0_SD0_RST;   //De-assert SPI0 reset
  *CCU_BUS_CLK_GATE0 |= CCU_BCGR0_SD0_EN;  //Open the SPI0 bus gate
  sd_card_delay(10);  //Wait a short wile for the device to become active
  sd_card_clk_init(400000);   //Setup the SD card clock at 400KHz
  *SD0_HWRST &= ~SD_HWRST_ACTIVE;   //Reset the SD card interface
  sd_card_delay(50);   //Wait a while for the device to be reset
  *SD0_HWRST |= SD_HWRST_ACTIVE;   //Make the SD card interface active again
  sd_card_delay(50); //Wait a while for the device to become active again
  *SD0_GCTL |= (SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST | SD_GCTL_SOFT_RST);   //Reset the DMA, FIFO and controller
  sd_card_delay(50); //Wait a while for the system to be done resetting
  *SD0_GCTL &= ~SD_GCTL_CD_DBC_ENB;   //Disable card detect de-bounce
  *SD0_CKCR |= SD_CKCR_CCLK_CTRL; //Turn card clock of when FSM in idle state
  *SD0_BWDR = SD_BWDR_1_BIT_WIDTH;   //Set the hardware to use only a single bit for data transfer
  *SD0_TMOR = 0xFFFFFFFF;   //Set max timeout for data and response.
  *SD0_CKCR |= SD_CKCR_CCLK_ENB;   //Turn the card clock on
  sd_card_update_clock();   //Update the card clock
  sd_card_delay(50);   //Wait a while for the clock to stabilize

  //发送命令0
  sd_command.cmdidx    = 0;
  sd_command.cmdarg    = 0;
  sd_command.resp_type = SD_RESPONSE_NONE;
  sd_card_send_command(&sd_command, 0);
  sd_card_delay(50);
  //发送命令8
  sd_command.cmdidx    = 8;
  sd_command.cmdarg    = 0x00000155;        //31:12 reserved (0x00000), 11:8 supply voltage (0x1 = 2.7 - 3.6V), 7:0 check pattern (0x55)
  sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_PRESENT;
  result = sd_card_send_command(&sd_command, 0);
  rt_kprintf("result:%d\n",result);
  sd_card_delay(100);
 // rt_kprintf("CMD8_reg:0x%08x\n",*SD0_CMDR);
  rt_kprintf("CMD8_val:0x%08x\n",*SD0_RESP0);
  if(result == SD_OK)
  {
    do
    {
      //发送特殊命令
      sd_command.cmdidx    = 55;
      sd_command.cmdarg    = 0;
      sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_PRESENT;
      sd_card_send_command(&sd_command, 0);
      sd_card_delay(10);
      //Send host capacity support information command
      sd_command.cmdidx    = 41;
      sd_command.cmdarg    = 0x40FF8000;                      //Need to figure out these settings
      sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_PRESENT;
      result = sd_card_send_command(&sd_command, 0);
      sd_card_delay(500);
     // rt_kprintf("CMD41_reg:0x%08x\n",*SD0_CMDR);
      rt_kprintf("CMD41_val:0x%08x\n",*SD0_RESP0);
    //0 means still initializing
    } while((sd_command.response[0] & 0x80000000) == 0);

    //Check card capacity status. 1 means SDHC or SDXC. 0 means SDSC
    if(sd_command.response[0] & 0x40000000){
      cardtype = SD_CARD_TYPE_SDHC;
    }else{
      cardtype = SD_CARD_TYPE_SDSC;  //Signal TYPE_SD_LOW...
    }
    rt_kprintf("cardtype:%d\n",cardtype);
  }
  sd_card_delay(50);
  //Only if a card has been detected
  if(cardtype)
  {
    //Send get CID numbers command to the card
    sd_command.cmdidx    = 2;
    sd_command.cmdarg    = 0;
    sd_command.resp_type =  SD_RESPONSE_CRC|SD_RESPONSE_136 | SD_RESPONSE_PRESENT;
    sd_card_send_command(&sd_command, 0);
    sd_card_delay(50);
    rt_kprintf("CMD2_reg:0x%08x\n",*SD0_CMDR);
    //Save the numbers
    cardcid[0] = sd_command.response[3];
    cardcid[1] = sd_command.response[2];
    cardcid[2] = sd_command.response[1];
    cardcid[3] = sd_command.response[0];
    sd_card_delay(50);

    //Send publish RCA command to the card
    sd_command.cmdidx    = 3;
    sd_command.cmdarg    = 0;
    sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_136 | SD_RESPONSE_PRESENT;
    result = sd_card_send_command(&sd_command, 0);
    if(result){
        return(SD_ERROR);
    }
    cardrca= *SD0_RESP0 >>16;
    rt_kprintf("cardrca:0x%08x\n",cardrca);
 }


  //Send get card specific data command to the card
  sd_command.cmdidx    = 9;
  sd_command.cmdarg    = cardrca;
  sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_136 | SD_RESPONSE_PRESENT;
  result = sd_card_send_command(&sd_command, 0);
  if(result){
    return(result);
  }
  
  //Save the CSD
  cardcsd[0] = sd_command.response[3];
  cardcsd[1] = sd_command.response[2];
  cardcsd[2] = sd_command.response[1];
  cardcsd[3] = sd_command.response[0];
  
  //Check the version of the CSD_STRUCTURE
  if(((cardcsd[0] & 0xC0000000) == 0) || (cardtype == SD_CARD_TYPE_MMC)){
    cardsectors = ((((cardcsd[1] & 0x03FF) << 2) | (cardcsd[2] >> 30)) + 1) << (((cardcsd[2] >> 15) & 0x07) + 2) << ((cardcsd[1] >> 16) & 0x0F) >> 9;
    cardsize = cardsectors >> 2;
  }
  else{
    cardsize = ((((cardcsd[1] & 0x03F) << 16) | (cardcsd[2] >> 16)) + 1) * 512;
    cardsectors = cardsize * 2;
 }
  rt_kprintf("cardsectors:0x%08x\n",cardsectors); //0x00ed8000 正确

  //Send get card identification data command to the card
  sd_command.cmdidx    = 10;
  sd_command.cmdarg    = cardrca;
  sd_command.resp_type = SD_RESPONSE_CRC | SD_RESPONSE_136 | SD_RESPONSE_PRESENT;
  result = sd_card_send_command(&sd_command, 0);
  if(result){
    return(result);
  }
  //Save the CID
  cardcid[0] = sd_command.response[3];
  cardcid[1] = sd_command.response[2];
  cardcid[2] = sd_command.response[1];
  cardcid[3] = sd_command.response[0];
  //Decode the CID
  //Get the manufacturer ID
  cardmid = cardcid[0] >> 24;
  
  //Get the product name
  cardpnm[0] = cardcid[0] & 0xFF;
  cardpnm[1] = (cardcid[1] >> 24) & 0xFF;
  cardpnm[2] = (cardcid[1] >> 16) & 0xFF;
  cardpnm[3] = (cardcid[1] >> 8) & 0xFF;
  cardpnm[4] = cardcid[1] & 0xFF;
  //Get the product serial number
  cardpsn = (cardcid[2] << 8) | ((cardcid[3] >> 24) & 0xFF);


//Set the card clock to 48MHz and use the 4 bit bus if supported
  if(sd_card_set_clock_and_bus(1)){
      //Signal error on failure
       return(SD_ERROR);
  }
  return(SD_OK);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值