STM32F103ZET6 — SDIO (Mirco SDCard)

1. SDIO 接口简述

SDIO 接口用于访问 MMC 卡,SD 存储卡或者 SDI/O 卡的接口标准。

这里要做的是 Micro SDCard,也就是 TF Card(存储卡)的操作。属于第二种。

本实验中使用的Micro SD卡属于SDSC(标准容量,最大两G)卡。介绍卡的种类是因为SD协议中的命令也支持这三种类型的卡,因此对STM32中的SDIO接口进行初始化后,上电后就要对接入的卡进行检测、分类,这个过程是通过向卡发送一系列不同的命令,根据卡不同的响应来进行分类。

 

2. Micro SD 卡

Micro SD 卡就是 TF Card 的意思,是小的 SD Card。一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;电源检测单元保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;卡及接口控制单元控制SD卡的运行状态,它包括有8个寄存器;接口驱动器控制SD卡引脚的输入输出。

可以看到,卡对外交互的引脚有:

  • CLK时钟线,由SDIO主机产生,即由STM32控制器输出;
  • CMD命令控制线,SDIO主机通过该线发送命令控制SD卡,如果命令要求SD卡提供应答(响应),SD卡也是通过该线传输应答信息;
  • D[0-3]数据线,传输读写数据;SD卡可将D0拉低表示忙状态;
  • VDD、VSS1、VSS2电源和地信号。

上述引脚定义了整个卡的传输模式:即,支持4线并行传输,数据同步传输,支持命令的交互

虽然可以共用总线,但不推荐多卡槽共用总线信号,要求一个单独SD总线应该连接一个单独的SD卡,总线的结构为:

具体的硬件原理图如下所示:

测试用卡:某东上买的卡,品牌:Kingston,Micro SDHC,Class 10, 大小 16GB,直接插上卡座即可

 

3. SD 协议

关于 SD Card 的所有协议定义,都遵循标准的 SD 组织定义的协议,参考:

《Physical Layer Simplified Specification V2.0》

协议中规定了物理层数据传送,Timing,频率,状态,寄存器等等。

 

3.1 传输特性

SD 总线通信是基于命令数据传输的。通讯由一个起始位("0"),由一个停止位("1")终止。

数据传送 MSB,上升沿有效。

传输的时候,发送命令,仅仅使用 CMD 线,能够支持开启 4 线进行数据的发送和接收。

使用4数据线传输时,每次传输4bit数据,每根数据线都必须有起始位、终止位以及CRC位,CRC位每根数据线都要分别检查,并把检查结果汇总然后在数据传输完后通过D0线反馈给主机。

SD卡数据包有两种格式,一种是常规数据(8bit宽),它先发低字节再发高字节,而每个字节则是先发高位再发低位

4线同步发送,每根线发送一个字节的其中两个位,数据位在四线顺序排列发送,DAT3数据线发较高位,DAT0数据线发较低位。

另外一种数据包发送格式是宽位数据包格式,对SD卡而言宽位数据包发送方式是针对SD卡SSR(SD状态)寄存器内容发送的,SSR寄存器总共有512bit,在主机发出ACMD13命令后SD卡将SSR寄存器内容通过DAT线发送给主机。

 

3.2 CMD&RSP 命令以及响应

3.2.1 CMD 命令包格式

SD 命令格式固定为 48bit,都是通过 CMD 线连续传输的(数据线不参与):

SD命令的组成如下:

    起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为0,终止位为1。

    传输标志:用于区分传输方向,该位为1时表示命令,方向为主机传输到SD卡,该位为0时表示响应,方向为SD卡传输到主机。

命令主体内容包括命令、地址信息/参数和CRC校验三个部分。

    命令号:它固定占用6bit,所以总共有64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于SD卡操作,只是专门用于MMC卡或者SD I/O卡。

    地址/参数:每个命令有32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这32bit用于指定参数,而寻址命令这32bit用于指定目标SD卡的地址。

    CRC7校验:长度为7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

 

3.2.2 CMD 命令类型

SD命令有4种类型:

    无响应广播命令(bc),发送到所有卡,不返回任务响应;

    带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;

    寻址命令(ac),发送到选定卡,DAT线无数据传输;

    寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。

另外,SD卡主机模块系统旨在为各种应用程序类型提供一个标准接口。在此环境中,需要有特定的客户/应用程序功能。为实现这些功能,在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD)。要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。

任何的 ACMD 命令之前,都需要先发送 CMD55 命令,来标记此处将会发送一个 ACMD 命令。

 

3.2.3 CMD 命令交互

SD总线的基本交互是命令与响应交互,即,使用 CMD 线发送命令,然后接收来自 CMD 的 RSP(如果有响应的话):

SD数据是以块(Black)形式传输的,SDHC卡数据块长度一般为512字节,数据可以从主机到卡,也可以是从卡到主机。数据块需要CRC位来保证数据传输成功。CRC位由SD卡系统硬件生成。STM32控制器可以控制使用单线或4线传输,本开发板设计使用4线传输。

SD数据传输支持单块和多块读写,它们分别对应不同的操作命令,多块写入还需要使用命令来停止整个写入操作。数据写入前需要检测SD卡忙状态,因为SD卡在接收到数据后编程到存储区过程需要一定操作时间。SD卡忙状态通过把D0线拉低表示。数据块读操作与之类似,只是无需忙状态检测。命令格式

 

3.2.4 CMD 命令描述

SD卡系统的命令被分为多个类,每个类支持一种"卡的功能设置"。下面列举了SD卡部分命令信息,更多详细信息可以参考SD简易规格文件说明,表中填充位和保留位都必须被设置为0。

命令序号

类型

参数

响应

缩写

描述

基本命令(Class 0)

CMD0

bc

[31:0]填充位

-

GO_IDLE_STATE

复位所有的卡到idle状态。

CMD2

bcr

[31:0]填充位

R2

ALL_SEND_CID

通知所有卡通过CMD线返回CID值。

CMD3

bcr

[31:0]填充位

R6

SEND_RELATIVE_ADDR

通知所有卡发布新RCA。

CMD4

bc

[31:16]DSR[15:0]填充位

-

SET_DSR

编程所有卡的DSR。

CMD7

ac

[31:16]RCA[15:0]填充位

R1b

SELECT/DESELECT_CARD

选择/取消选择RCA地址卡。

CMD8

bcr

[31:12]保留位[11:8]VHS[7:0]检查模式

R7

SEND_IF_COND

发送SD卡接口条件,包含主机支持的电压信息,并询问卡是否支持。

CMD9

ac

[31:16]RCA[15:0]填充位

R2

SEND_CSD

选定卡通过CMD线发送CSD内容

CMD10

ac

[31:16]RCA[15:0]填充位

R2

SEND_CID

选定卡通过CMD线发送CID内容

CMD12

ac

[31:0]填充位

R1b

STOP_TRANSMISSION

强制卡停止传输

CMD13

ac

[31:16]RCA[15:0]填充位

R1

SEND_STATUS

选定卡通过CMD线发送它状态寄存器

CMD15

ac

[31:16]RCA[15:0]填充位

-

GO_INACTIVE_STATE

使选定卡进入"inactive"状态

面向块的读操作(Class 2)

CMD16

ac

[31:0]块长度

R1

SET_BLOCK_LEN

对于标准SD卡,设置块命令的长度,对于SDHC卡块命令长度固定为512字节。

CMD17

adtc

[31:0]数据地址

R1

READ_SINGLE_BLOCK

对于标准卡,读取SEL_BLOCK_LEN长度字节的块;对于SDHC卡,读取512字节的块。

CMD18

adtc

[31:0]数据地址

R1

READ_MULTIPLE_BLOCK

连续从SD卡读取数据块,直到被CMD12中断。块长度同CMD17。

面向块的写操作(Class 4)

CMD24

adtc

[31:0]数据地址

R1

WRITE_BLOCK

对于标准卡,写入SEL_BLOCK_LEN长度字节的块;对于SDHC卡,写入512字节的块。

CMD25

adtc

[31:0]数据地址

R1

WRITE_MILTIPLE_BLOCK

连续向SD卡写入数据块,直到被CMD12中断。每块长度同CMD17。

CMD27

adtc

[31:0]填充位

R1

PROGRAM_CSD

对CSD的可编程位进行编程

擦除命令(Class 5)

CMD32

ac

[31:0]数据地址

R1

ERASE_WR_BLK_START

设置擦除的起始块地址

CMD33

ac

[31:0]数据地址

R1

ERASE_WR_BLK_END

设置擦除的结束块地址

CMD38

ac

[31:0]填充位

R1b

ERASE

擦除预先选定的块

加锁命令(Class 7)

CMD42

adtc

[31:0]保留

R1

LOCK_UNLOCK

加锁/解锁SD卡

特定应用命令(Class 8)

CMD55

ac

[31:16]RCA[15:0]填充位

R1

APP_CMD

指定下个命令为特定应用命令,不是标准命令

CMD56

adtc

[31:1]填充位[0]读/写

R1

GEN_CMD

通用命令,或者特定应用命令中,用于传输一个数据块,最低位为1表示读数据,为0表示写数据

SD卡特定应用命令

ACMD6

ac

[31:2]填充位[1:0]总线宽度

R1

SET_BUS_WIDTH

定义数据总线宽度('00'=1bit,'10'=4bit)。

ACMD13

adtc

[31:0]填充位

R1

SD_STATUS

发送SD状态

ACMD41

Bcr

[32]保留位[30]HCS(OCR[30]) [29:24]保留位[23:0]VDD电压(OCR[23:0])

R3

SD_SEND_OP_COND

主机要求卡发送它的支持信息(HCS)和OCR寄存器内容。

ACMD51

adtc

[31:0]填充位

R1

SEND_SCR

读取配置寄存器SCR

 

3.2.5 RSP 命令的响应

响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。SDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是 48bit 长度,只有R2类型是长响应,其长度为136bit。各个类型响应具体情况。

特别注意:除了R3类型之外,其他响应都使用CRC7校验来校验,对于R2类型是使用CID和CSD寄存器内部CRC7

R1(正常响应命令)

描述

起始位

传输位

命令号

卡状态(Card Status)

CRC7

终止位

Bit

47

46

[45:40]

[39:8]

[7:1]

0

位宽

1

1

6

32

7

1

"0"

"0"

x

x

x

"1"

备注

如果有传输到卡的数据,那么在数据线可能有busy信号

R2(CID,CSD寄存器)

描述

起始位

传输位

保留

[127:1]

终止位

Bit

135

134

[133:128]

127

0

位宽

1

1

6

x

1

"0"

"0"

"111111"

CID或者CSD寄存器[127:1]位的值

"1"

备注

CID寄存器内容作为CMD2和CMD10响应,CSD寄存器内容作为CMD9响应。

R3(OCR寄存器)

描述

起始位

传输位

保留

OCR寄存器

保留

终止位

Bit

47

46

[45:40]

[39:8]

[7:1]

0

位宽

1

1

6

32

7

1

"0"

"0"

"111111"

x

"1111111"

"1"

备注

OCR寄存器的值作为ACMD41的响应

R6(发布的RCA寄存器响应)

描述

起始位

传输位

CMD3

RCA寄存器

卡状态位

CRC7

终止位

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的响应

R7(发布的RCA寄存器响应)

描述

起始位

传输位

CMD8

保留

接收电压

检测模式

CRC7

终止位

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的响应,返回卡支持电压范围和检测模式

Spec 中的描述为:

 

3.3 SD卡的模式

3.3.1 SD卡的操作模式

SD卡有多个版本,STM32控制器目前最高支持《Physical Layer Simplified Specification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。

SD卡系统(包括主机和SD卡)定义了两种操作模式:卡识别模式和数据传输模式。在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。在每个操作模式下,SD卡都有几种状态,通过命令控制实现卡状态的切换。

操作模式

SD卡状态

无效模式(Inactive)

无效状态(Inactive State)

卡识别模式(Card identification mode)

空闲状态(Idle State)

准备状态(Ready State)

识别状态(Identification State)

数据传输模式(Data transfer mode)

待机状态(Stand-by State)

传输状态(Transfer State)

发送数据状态(Sending-data State)

接收数据状态(Receive-data State)

编程状态(Programming State)

断开连接状态(Disconnect State)

 

3.3.2 卡识别模式

在卡识别模式下,主机会复位所有处于"卡识别模式"的SD卡,确认其工作电压范围,识别SD卡类型,并且获取SD卡的相对地址(卡相对地址较短,便于寻址)。在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下。卡识别模式下SD卡状态转换:

主机上电后,所有卡处于空闲状态,包括当前处于无效状态的卡。主机也可以发送GO_IDLE_STATE(CMD0)让所有卡软复位从而进入空闲状态,但当前处于无效状态的卡并不会复位。

主机在开始与卡通信前,需要先确定双方在互相支持的电压范围内。SD卡有一个电压支持范围,主机当前电压必须在该范围可能才能与卡正常通信。SEND_IF_COND(CMD8)命令就是用于验证卡接口操作条件的(主要是电压支持)。卡会根据命令的参数来检测操作条件匹配性,如果卡支持主机电压就产生响应,否则不响应。而主机则根据响应内容确定卡的电压匹配性。CMD8是SD卡标准V2.0版本才有的新命令,所以如果主机有接收到响应,可以判断卡为V2.0或更高版本SD卡。

SD_SEND_OP_COND(ACMD41)命令可以识别或拒绝不匹配它的电压范围的卡。ACMD41命令的VDD电压参数用于设置主机支持电压范围,卡响应会返回卡支持的电压范围。对于对CMD8有响应的卡,把ACMD41命令的HCS位设置为1,可以测试卡的容量类型,如果卡响应的CCS位为1说明为高容量SD卡,否则为标准卡。卡在响应ACMD41之后进入准备状态,不响应ACMD41的卡为不可用卡,进入无效状态。ACMD41是应用特定命令,发送该命令之前必须先发CMD55

ALL_SEND_CID(CMD2)用来控制所有卡返回它们的卡识别号(CID),处于准备状态的卡在发送CID之后就进入识别状态。之后主机就发送SEND_RELATIVE_ADDR(CMD3)命令,让卡自己推荐一个相对地址(RCA)并响应命令。这个RCA是16bit地址,而CID是128bit地址,使用RCA简化通信。卡在接收到CMD3并发出响应后就进入数据传输模式,并处于待机状态,主机在获取所有卡RCA之后也进入数据传输模式。

 

3.3.3 数据传输模式

只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。数据传输模式下,SD卡状态转换过程:

CMD7用来选定和取消指定的卡,卡在待机状态下还不能进行数据通信,因为总线上可能有多个卡都是出于待机状态,必须选择一个RCA地址目标卡使其进入传输状态才可以进行数据通信。同时通过CMD7命令也可以让已经被选择的目标卡返回到待机状态。

数据传输模式下的数据通信都是主机和目标卡之间通过寻址命令点对点进行的。卡处于传输状态下可以使用表 362中面向块的读写以及擦除命令对卡进行数据读写、擦除。CMD12可以中断正在进行的数据通信,让卡返回到传输状态。CMD0和CMD15会中止任何数据编程操作,返回卡识别模式,这可能导致卡数据被损坏。

 

3.4 SD卡时钟频率

识别模式下的SD时钟需要在 400KHz,识别模式转成传送模式后,可以直接将频率提高到 24MHz

 

3.5 SD卡宽总线选择

启动上电默认进入识别模式,默认使用总线宽度为 1 bit,在进入传输模式后,可以先获取到卡的 SCR 寄存器,再在寄存器中找到对应的是否支持 4 bit 模式的标志位,最后使用 CMD55 + ACMD6 来告诉 SD Card 开启 4bit 模式传输。

 

3.6 SD 卡内部寄存器描述

名称

bit宽度

描述

CID

128

卡识别号(Card identification number):用来识别的卡的个体号码(唯一的)

RCA

16

相对地址(Relative card address):卡的本地系统地址,初始化时,动态地由卡建议,主机核准。

DSR

16

驱动级寄存器(Driver Stage Register):配置卡的输出驱动

CSD

128

卡的特定数据(Card Specific Data):卡的操作条件信息

SCR

64

SD配置寄存器(SD Configuration Register):SD 卡特殊特性信息

OCR

32

操作条件寄存器(Operation conditions register)

SSR

512

SD状态(SD Status):SD卡专有特征的信息

CSR

32

卡状态(Card Status):卡状态信息

上述寄存器都是通过发送特定的指令进行访问的。

其中, CSD 寄存器表征 SD Card 的大小,Block Size,以及如果是 V2 一下的卡的话,是否支持 Block 内进行读写。

CSR 状态寄存器,返回的是卡当前的状态,能够用于区分当前卡是否正在 Programing 等等。

 

3.7 SD 卡读/写/擦除

3.7.1 SD 卡读

SD 卡的读,分为两种方式的读:

  • 读一个 block:发送 CMD17 命令
  • 读多个 block:发送 CMD18 命令,需要主动发送 CMD12 停止传送命令才会停止动作。

 

3.7.2 SD 卡写

  • 写一个 block:发送 CMD24 命令
  • 写多个 block:发送 CMD25 命令,需要主动发送 CMD12 停止传送命令才会停止动作。

与读不一样,写的话,数据到达 SD Card 侧的硬件 Buffer 后,需要一些时间进行内部的 Programme,此刻,对外就是一个 busy的状态:

这里需要注意的是,在 Spec 中描述:

在多块连续写入的时候,需要先去 pre-erase,即使用 CMD55+ACMD23 命令进行预擦除

 

3.7.3 SD 卡擦除

卡的擦除操作,顺序发送 CMD32(擦除的起始地址), CMD33(擦除的结束地址), CMD38(开始擦除)。

 

4. STM32F103ZTE6 的 SD 功能

SDIO包含2个部分:

● SDIO适配器模块:实现所有MMC/SD/SD I/O卡的相关功能,如时钟的产生、命令和数据的传送。
● AHB总线接口:操作SDIO适配器模块中的寄存器,并产生中断和 DMA 请求信号。

复位后默认情况下SDIO_D0用于数据传输。初始化后主机可以改变数据总线的宽度。

 

4.1 SDIO 适配器

 

4.2 SDIO 控制单元

控制单元负责电源管理和时钟分频管理。时钟部分,来自 AHB 的时钟。

 

4.3 SDIO 命令通道

命令通道单元向卡发送命令并从卡接收响应。

命令通道有对应的硬件状态机实现:

命令通道状态机(CPSM):当写入命令寄存器并设置了使能位,开始发送命令。命令发送完成时,命令通道状态机(CPSM)设置状态标志并在不需要响应时进入空闲状态(见下图)。当收到响应后,接收到的CRC码将会与内部产生的CRC码比较,然后设置相应的状态标志。

当进入等待(Wait)状态时,命令定时器开始运行;当CPSM进入接收(Receive)状态之前,产生了超时,则设置超时标志并进入空闲(Idle)状态。

注意:命令超时固定为 64 个 SDIO_CK 时钟周期。

注意:CPSM 保持在空闲状态至少 8 个 SDIO_CK 周期,以满足 NCC 和 NRC 时序限制。 NCC 是两个主机命令间的最小间隔; NRC 是主机命令与卡响应之间的最小间隔。(这个是 Spec 的规定)

状态机如下:

 

4.4 SDIO 数据通道

数据通道子单元在主机与卡之间传输数据。下图是数据通道的框图。

在时钟控制寄存器中可以配置卡的数据总线宽度。如果选择了4位总线模式,则每个时钟周期四条数据信号线(SDIO_D[3:0])上将传输4位数据;如果选择了8位总线模式,则每个时钟周期八条数据信号线(SDIO_D[7:0])上将传输8位数据;如果没有选择宽总线模式,则每个时钟周期只在SDIO_D0上传输1位数据。
根据传输的方向(发送或接收),使能时数据通道状态机(DPSM)将进入Wait_S或Wait_R状态:
● 发送:DPSM进入Wait_S状态。如果发送FIFO中有数据,则DPSM进入发送状态,同时数据通道子单元开始向卡发送数据。
● 接收:DPSM进入Wait_R状态并等待开始位;当收到开始位时,DPSM进入接收状态,同时数据通道子单元开始从卡接收数据。

数据通道状态机(DPSM):DPSM工作在SDIO_CK频率,卡总线信号与SDIO_CK的上升沿同步。DPSM有6个状态,

 

4.5 数据FIFO

数据FIFO(先进先出)部件是一个数据缓冲器,带发送和接收单元。控制器的FIFO包含宽度为32bit、深度为32字的数据缓冲器和发送/接收逻辑。其中SDIO状态寄存器(SDIO_STA)的TXACT位用于指示当前正在发送数据,RXACT位指示当前正在接收数据,这两个位不可能同时为1。

    当TXACT为1时,可以通过APB2接口将数据写入到传输FIFO。

    当RXACT为1时,接收FIFO存放从数据路径部件接收到的数据。

根据FIFO空或满状态会把SDIO_STA寄存器位值1,并可以产生中断和DMA请求。

 

5. 代码流程

1. 配置 NVIC 控制器,开启 SDIO 中断

2. 配置 GPIO 口,复用推挽输出

3. 开启 SDIO/DMA2 模块时钟

4. 进入卡上电流程

5. 进入卡的初始化流程(识别),并转换到 Transfer 模式

6. 提高 SDCLK 的频率

7. 选择卡

8. 开启 4 bit 模式

 

5.1 识别模式代码

NVIC 和 GPIO ,SDIO 和 DMA2 的基础配置如下:

static void SK_SD_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    /* Configure the NVIC Preemption Priority Bits */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

static void SK_SD_GPIOConfig(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    /*!< GPIOC and GPIOD Periph clock enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD , ENABLE);
    
    /*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    
    /*!< Configure PD.02 CMD line */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOD, &GPIO_InitStructure); 
}

static void SK_SDIO_Config(void)
{
    /*!< Enable the SDIO AHB Clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE);
    SDIO_DeInit();
}

static void SK_SDIO_DMA2Config(void)
{
    /*!< Enable the DMA2 Clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
}

 

这里我将所有的命令CMD 和 回应 RSP的 检查全部封装成为函数的形式:

/************************** SD CMD Defination Start *********************************/
void SK_SdioSendCmd_0(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = 0x0;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; //cmd0
    stSdioCmdInit.SDIO_Response = SDIO_Response_No;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_8(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = SD_CHECK_PATTERN;
    stSdioCmdInit.SDIO_CmdIndex = SDIO_SEND_IF_COND;  // cmd8
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;// Rsp = r7
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_55(uint32_t rca)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = (uint32_t) rca << 16;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_APP_CMD;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short; //r1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendAppCmd_41(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SD_HIGH_CAPACITY;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;  //r3
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendAppCmd_51(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Send ACMD51 SD_APP_SEND_SCR with argument as 0 */
    stSdioCmdInit.SDIO_Argument = 0x0;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SD_APP_SEND_SCR;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;  //r1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendAppCmd_6(uint32_t bit_wide)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = bit_wide;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_APP_SD_SET_BUSWIDTH;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendAppCmd_23(uint32_t num_of_blk)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = num_of_blk;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SET_BLOCK_COUNT;  //cmd23
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_2(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = 0x0;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Long;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_3(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = 0x00;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;  //cmd3
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short; //r6
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_9(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = (uint32_t)(g_stSDCARD.RCA << 16);
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SEND_CSD;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Long;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

// Erase Start Command
void SK_SdioSendCmd_32(uint32_t start_addr)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Send CMD32 SD_ERASE_GRP_START with argument as addr  */
    stSdioCmdInit.SDIO_Argument = start_addr;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;  //R1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

// Erase End Command
void SK_SdioSendCmd_33(uint32_t end_addr)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Send CMD33 SD_ERASE_GRP_END with argument as addr  */
    stSdioCmdInit.SDIO_Argument = end_addr;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;  //R1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

// Erase Command
void SK_SdioSendCmd_38(void)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = 0;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_ERASE;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;  //R1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

// Request the Card Status Registers
void SK_SdioSendCmd_13(uint32_t rca)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    stSdioCmdInit.SDIO_Argument = (uint32_t) rca << 16;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_16(uint16_t block_size)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Set Block Size for Card,cmd16,if sdhc card the block size is 512, this will not effect */
    stSdioCmdInit.SDIO_Argument = (uint32_t) block_size;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;   //r1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_17(uint32_t addr)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Send CMD17 READ_SINGLE_BLOCK */
    stSdioCmdInit.SDIO_Argument = (uint32_t)addr;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_18(uint32_t addr)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Send CMD18 READ_MULT_BLOCK with argument data address */
    stSdioCmdInit.SDIO_Argument = (uint32_t)addr; //起始地址
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_READ_MULT_BLOCK;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short; //r1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_24(uint32_t WriteAddr)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Send CMD24 WRITE_SINGLE_BLOCK */
    stSdioCmdInit.SDIO_Argument = WriteAddr;    //写入地址
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;     //r1
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_25(uint32_t WriteAddr)
{
    SDIO_CmdInitTypeDef stSdioCmdInit;

    /*!< Send CMD25 WRITE_MULT_BLOCK with argument data address */
    stSdioCmdInit.SDIO_Argument = (uint32_t)WriteAddr;
    stSdioCmdInit.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK;
    stSdioCmdInit.SDIO_Response = SDIO_Response_Short;
    stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
    stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&stSdioCmdInit);
}

void SK_SdioSendCmd_12(void)
{
  /*!< Send CMD12 STOP_TRANSMISSION  */
  SDIO->ARG = 0x0;
  SDIO->CMD = 0x44C;
}

/************************** SD CMD Defination End *********************************/

当发送命令后,STM32 的 SDIO 进入了等待状态机(如果有RSP),等待一个SDIO 状态寄存器被置位,可能会出现的有:

  • CMDREND:已接收到响应(CRC检测成功)
  • CTIMEOUT:命令响应超时 (Command response timeout)。固定的值,为64个SDIO_CK时钟周期
  • CCRCFAIL:已收到命令响应(CRC检测失败) (Command response received)  
static SD_Error SK_CmdError(void)
{
    SD_Error errorstatus = SD_OK;
    uint32_t timeout;

    timeout = SDIO_CMD0TIMEOUT; /*!< 10000 */

    /* Check If CMD have been Send out */
    while ((timeout > 0) && (SDIO_GetFlagStatus(SDIO_FLAG_CMDSENT) == RESET))
    {
        timeout--;
    }

    if (timeout == 0)
    {
        errorstatus = SD_CMD_RSP_TIMEOUT;
        return (errorstatus);
    }

    /*!< Clear all the static flags */
    SDIO_ClearFlag(SDIO_STATIC_FLAGS);

    return(errorstatus);
}

static SD_Error SK_CheckCmdRsp_1(uint8_t cmd)
{

  while (!(SDIO->STA & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)));

  SDIO->ICR = SDIO_STATIC_FLAGS;

  return (SD_Error)(SDIO->RESP1 & SD_OCR_ERRORBITS);
}

static SD_Error SK_CheckCmdRsp_2(void)
{
    SD_Error errorstatus = SD_OK;
    uint32_t status;

    status = SDIO->STA;

    while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
    {
        status = SDIO->STA;
    }

    if (status & SDIO_FLAG_CTIMEOUT)
    {
        errorstatus = SD_CMD_RSP_TIMEOUT;
        SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
        return(errorstatus);
    }
    else if (status & SDIO_FLAG_CCRCFAIL)
    {
        errorstatus = SD_CMD_CRC_FAIL;
        SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
        return(errorstatus);
    }

    /*!< Clear all the static flags */
    SDIO_ClearFlag(SDIO_STATIC_FLAGS);

    return(errorstatus);

}

static SD_Error SK_CheckCmdRsp_3(void)
{
    SD_Error errorstatus = SD_OK;
    uint32_t status;

    status = SDIO->STA;

    while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
    {
        status = SDIO->STA;
    }

    if (status & SDIO_FLAG_CTIMEOUT)
    {
        errorstatus = SD_CMD_RSP_TIMEOUT;
        SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
        return(errorstatus);
    }
    /*!< Clear all the static flags */
    SDIO_ClearFlag(SDIO_STATIC_FLAGS);
    return(errorstatus);
}

static SD_Error SK_CheckCmdRsp_6(void)
{
    SD_Error errorstatus = SD_OK;
    uint32_t response;

    do {
        errorstatus = SK_CheckCmdRsp_3();
        SK_SD_RETURN_CHECK(errorstatus);

        if (SD_CMD_SET_REL_ADDR != SDIO_GetCommandResponse())
        {
            errorstatus = SD_ILLEGAL_CMD;
            break;
        }

        response = SDIO_GetResponse(SDIO_RESP1);

        if (response & SD_R6_GENERAL_UNKNOWN_ERROR)
        {
            errorstatus = SD_GENERAL_UNKNOWN_ERROR;
            break;
        }

        if (response & SD_R6_ILLEGAL_CMD)
        {
            errorstatus = SD_ILLEGAL_CMD;
            break;
        }

        if (response & SD_R6_COM_CRC_FAILED)
        {
            errorstatus = SD_COM_CRC_FAILED;
            break;
        }

        g_stSDCARD.RCA = (uint16_t)(response >> 16);

    } while (0);

    return errorstatus;
}

static SD_Error SK_CheckCmdRsp_7(void)
{
    SD_Error errorstatus = SD_OK;
    uint32_t crc_fail_status, timeout_status, cmd_rsp_status;
    uint32_t timeout = 0;

    for (timeout = 0; timeout < SDIO_CMD0TIMEOUT; timeout++)
    {
        crc_fail_status = SDIO_GetFlagStatus(SDIO_FLAG_CCRCFAIL);
        timeout_status = SDIO_GetFlagStatus(SDIO_FLAG_CTIMEOUT);
        cmd_rsp_status = SDIO_GetFlagStatus(SDIO_FLAG_CMDREND);

        /*!< CRC Fail check */
        if (crc_fail_status)
        {
            errorstatus = SD_CMD_CRC_FAIL;
            SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
            break;
        }

        /*!< Card is not V2.0 complient or card does not support the set voltage range */
        if (timeout_status)
        {
            errorstatus = SD_CMD_RSP_TIMEOUT;
            SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
            break;
        }

        /*!< Rsp Fail check */
        if (cmd_rsp_status)
        {
            errorstatus = SD_OK;
            SDIO_ClearFlag(SDIO_FLAG_CMDREND);
            break;
        }
    }

    if (SDIO_CMD0TIMEOUT == timeout)
        errorstatus = SD_CMD_RSP_TIMEOUT;

    return errorstatus;
}

细心的观众可能看到了,在 SK_CheckCmdRsp_3 不会去返回 CRC 错误的码,是因为这个 RSP 3 不带 CRC 位。

 

SD Card 总体的初始化流程如下,分为了上电阶段,初始化阶段,提速阶段,选择卡阶段,和设置 4 bit 阶段:

void SK_SDIO_SDCARD_Init(void)
{
    SD_Error errorstatus = SD_OK;

    SK_SD_NVIC_Config();
    SK_SD_GPIOConfig();
    SK_SDIO_Config();
    SK_SDIO_DMA2Config();

    do {
        errorstatus = SK_SdcardPowerOn();
        SK_SD_RETURN_CHECK(errorstatus);

        errorstatus = SK_SD_InitializeCards();
        SK_SD_RETURN_CHECK(errorstatus);

        // Now in Transfer Mode, just change to 24Mhz Clock
        SK_SetSDIOFreqToHighSpeed();

        errorstatus = SK_SD_SelectDeselect((uint32_t)(g_stSDCARD.RCA << 16));
        SK_SD_RETURN_CHECK(errorstatus);

        errorstatus = SK_Enable_4Bit_WideBus();
        SK_SD_RETURN_CHECK(errorstatus);
    } while (0);
}

 

上电阶段的代码如下:

/* Set SDIO_CK to 400KHz in Identifacation Mode */
static void SK_SetSDIOFreqToLowSpeed(void)
{
    SDIO_InitTypeDef stSdioInit;

    /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */
    /*!< SDIO_CK for Initialization should not exceed 400 KHz */
    /* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */
    stSdioInit.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
    stSdioInit.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
    stSdioInit.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
    stSdioInit.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
    stSdioInit.SDIO_BusWide = SDIO_BusWide_1b;
    stSdioInit.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
    SDIO_Init(&stSdioInit);

}

static SD_Error SK_SdcardPowerOn(void)
{
    SD_Error errorstatus = SD_OK;
    uint32_t response = 0, count = 0, validvoltage = 0;

    g_stSDCARD.CardType = SDIO_STD_CAPACITY_SD_CARD_V1_1;

    /*!< Power ON Sequence -----------------------------------------------------*/
    /*!< Configure the SDIO peripheral */
    /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */
    /*!< SDIO_CK for Initialization should not exceed 400 KHz */
    /* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */
    SK_SetSDIOFreqToLowSpeed();

    /// STM32 SDIO CARD Clock Power On
    SDIO_SetPowerState(SDIO_PowerState_ON);

    SDIO_ClockCmd(ENABLE);

    do {

        /*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*/
        SK_SdioSendCmd_0();
        errorstatus = SK_CmdError();
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< CMD8: SEND_IF_COND ----------------------------------------------------*/
        SK_SdioSendCmd_8();
        errorstatus = SK_CheckCmdRsp_7();
        SK_SD_RETURN_CHECK(errorstatus);

        // Support Card V2.0
        g_stSDCARD.CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0;

        /*!< CMD55: SD_CMD_APP_CMD ------------------------------------------------ */
        SK_SdioSendCmd_55(0x00);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_APP_CMD);
        SK_SD_RETURN_CHECK(errorstatus);

        while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
        {
            /*!< CMD55: SD_CMD_APP_CMD ------------------------------------------------ */
            SK_SdioSendCmd_55(0x00);
            errorstatus = SK_CheckCmdRsp_1(SD_CMD_APP_CMD);
            if (errorstatus != SD_OK)
                return errorstatus;

            /*!< ACMD41: SD_CMD_APP_CMD ------------------------------------------------ */
            SK_SdioSendAppCmd_41();
            errorstatus = SK_CheckCmdRsp_3();
            if (errorstatus != SD_OK)
                return errorstatus;

            response = SDIO_GetResponse(SDIO_RESP1);
            validvoltage = (((response >> 31) == 1) ? 1 : 0);

            count++;
        }

        if (count >= SD_MAX_VOLT_TRIAL)
        {
            errorstatus = SD_INVALID_VOLTRANGE;
            break;
        }

        if (response & SD_HIGH_CAPACITY)
        {
            g_stSDCARD.CardType = SDIO_HIGH_CAPACITY_SD_CARD;
        }

    } while (0);

    return errorstatus;
}

 

初始化部分代码,主要是获取到 CID, CSD 结构,和 RCA 地址,并对 CID 和 CSD 结构做解析:

static SD_Error SK_SD_InitializeCards(void)
{
    SD_Error errorstatus = SD_OK;
    uint32_t CID_RAW_DATA[4] = {0};
    uint32_t CSD_RAW_DATA[4] = {0};

    do {
        if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
        {
            errorstatus = SD_REQUEST_NOT_APPLICABLE;
            break;
        }

        if (SDIO_SECURE_DIGITAL_IO_CARD != g_stSDCARD.CardType)
        {
            /*!< CMD2: ALL_SEND_CID ------------------------------------------------ */
            SK_SdioSendCmd_2();
            errorstatus = SK_CheckCmdRsp_2();
            SK_SD_RETURN_CHECK(errorstatus);

            CID_RAW_DATA[0] = SDIO_GetResponse(SDIO_RESP1);
            CID_RAW_DATA[1] = SDIO_GetResponse(SDIO_RESP2);
            CID_RAW_DATA[2] = SDIO_GetResponse(SDIO_RESP3);
            CID_RAW_DATA[3] = SDIO_GetResponse(SDIO_RESP4);

            /*!< CMD3: SD_CMD_SET_REL_ADDR ------------------------------------------ */
            SK_SdioSendCmd_3();
            errorstatus = SK_CheckCmdRsp_6();
            SK_SD_RETURN_CHECK(errorstatus);

            /*!< CMD9: SD_CMD_SEND_CSD ------------------------------------------ */
            SK_SdioSendCmd_9();
            errorstatus = SK_CheckCmdRsp_2();
            SK_SD_RETURN_CHECK(errorstatus);

            CSD_RAW_DATA[0] = SDIO_GetResponse(SDIO_RESP1);
            CSD_RAW_DATA[1] = SDIO_GetResponse(SDIO_RESP2);
            CSD_RAW_DATA[2] = SDIO_GetResponse(SDIO_RESP3);
            CSD_RAW_DATA[3] = SDIO_GetResponse(SDIO_RESP4);

            _parseCID(CID_RAW_DATA);
            _parseCSD(CSD_RAW_DATA);
        }
    } while (0);

    return errorstatus;
}

 

接着切换到了 24MHz 的高频:

/* Set SDIO_CK to 24MHz in Transfer Mode */
static void SK_SetSDIOFreqToHighSpeed(void)
{
    SDIO_InitTypeDef stSdioInit;

    /*SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) = 24MHz */
    stSdioInit.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;   
    stSdioInit.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
    stSdioInit.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
    stSdioInit.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
    stSdioInit.SDIO_BusWide = SDIO_BusWide_1b;
    stSdioInit.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
    SDIO_Init(&stSdioInit);
}

选中卡,发送 cmd7:

static SD_Error SK_SD_SelectDeselect(uint32_t addr)
{
  SD_Error errorstatus = SD_OK;
  SDIO_CmdInitTypeDef stSdioCmdInit;

  /*!< Send CMD7 SDIO_SEL_DESEL_CARD */
  stSdioCmdInit.SDIO_Argument =  addr;
  stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SEL_DESEL_CARD;
  stSdioCmdInit.SDIO_Response = SDIO_Response_Short;
  stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
  stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&stSdioCmdInit);

  errorstatus = SK_CheckCmdRsp_1(SD_CMD_SEL_DESEL_CARD);

  return(errorstatus);
}

切换到 4 bit wide:

static SD_Error SK_Get_SCR(uint16_t rca, uint32_t *pscr)
{
    SD_Error errorstatus = SD_OK;
    SDIO_DataInitTypeDef SDIO_DataInitStructure;
    SDIO_CmdInitTypeDef stSdioCmdInit;
    uint16_t delay_time;
    uint32_t index = 0;
    uint32_t tempscr[2] = {0, 0};

    do {
        // No need to send the CMD16 to change the Block length, SDHC is fixed in 512-Bytes
        /*!< Set Block Size To 8 Bytes */ 
        stSdioCmdInit.SDIO_Argument = (uint32_t)8;
        stSdioCmdInit.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN; //  cmd16
        stSdioCmdInit.SDIO_Response = SDIO_Response_Short;  //r1
        stSdioCmdInit.SDIO_Wait = SDIO_Wait_No;
        stSdioCmdInit.SDIO_CPSM = SDIO_CPSM_Enable;
        SDIO_SendCommand(&stSdioCmdInit);

        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SET_BLOCKLEN);
        SK_SD_RETURN_CHECK(errorstatus);

        SK_SdioSendCmd_55(g_stSDCARD.RCA);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_APP_CMD);
        SK_SD_RETURN_CHECK(errorstatus);

        SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
        SDIO_DataInitStructure.SDIO_DataLength = 8; //8-bytes
        SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_8b  ;// Set Block Size 8-bytes 
        SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
        SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
        SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
        SDIO_DataConfig(&SDIO_DataInitStructure);

        for(delay_time = 0; delay_time < 20; delay_time++)
            __nop();

        /*!< Send ACMD51 SD_APP_SEND_SCR with argument as 0 */
        SK_SdioSendAppCmd_51();
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SD_APP_SEND_SCR);
        SK_SD_RETURN_CHECK(errorstatus);

        // Wait for Status.    FIFO OverFlow         Data CRC ERR         Data Timeout          Recv Data         No Start bit
        while (!(SDIO->STA & (SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
        {            
            if (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)
            {   
                *(tempscr + index) = SDIO_ReadData();
                    index++; 
        
                if(index > 1 ) 
                    break;
            }
        }

        if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
        {
            SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
            errorstatus = SD_DATA_TIMEOUT;
            break;
        }
        else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
        {
            SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
            errorstatus = SD_DATA_CRC_FAIL;
            break;
        }
        else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
        {
            SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
            errorstatus = SD_RX_OVERRUN;
            break;
        }
        else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
        {
            SDIO_ClearFlag(SDIO_FLAG_STBITERR);
            errorstatus = SD_START_BIT_ERR;
            break;
        }

        /*!< Clear all the static flags */
        SDIO_ClearFlag(SDIO_STATIC_FLAGS);

        *(pscr + 1) = ((tempscr[0] & SD_0TO7BITS) << 24) | ((tempscr[0] & SD_8TO15BITS) << 8) | ((tempscr[0] & SD_16TO23BITS) >> 8) | ((tempscr[0] & SD_24TO31BITS) >> 24);
        *(pscr) = ((tempscr[1] & SD_0TO7BITS) << 24) | ((tempscr[1] & SD_8TO15BITS) << 8) | ((tempscr[1] & SD_16TO23BITS) >> 8) | ((tempscr[1] & SD_24TO31BITS) >> 24);
    } while (0);

    return errorstatus;
}

static void SK_SetSDIO4BitWide(void)
{
    SDIO_InitTypeDef stSdioInit;

    /*!< Configure the SDIO peripheral */
    stSdioInit.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV; 
    stSdioInit.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
    stSdioInit.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
    stSdioInit.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
    stSdioInit.SDIO_BusWide = SDIO_BusWide_4b;
    stSdioInit.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
    SDIO_Init(&stSdioInit);
}

static SD_Error SK_Enable_4Bit_WideBus(void)
{
    SD_Error errorstatus = SD_OK;

    do {
        /// First get the SCR to ensure the card support the 4 bit data transfer
        errorstatus = SK_Get_SCR(g_stSDCARD.RCA, g_stSDCARD.SD_scr_raw_data);
        SK_SD_RETURN_CHECK(errorstatus);

        if (g_stSDCARD.SD_scr_raw_data[1] & SD_WIDE_BUS_SUPPORT)
        {
             /*!< Send CMD55 APP_CMD with argument as card's RCA.*/
            SK_SdioSendCmd_55(g_stSDCARD.RCA);
            errorstatus = SK_CheckCmdRsp_1(SD_CMD_APP_CMD);
            SK_SD_RETURN_CHECK(errorstatus);

            /*!< Send ACMD6 APP_CMD with argument as 2 for wide bus mode */
            SK_SdioSendAppCmd_6(SD_4_BIT_WIDE);
            errorstatus = SK_CheckCmdRsp_1(SD_CMD_APP_SD_SET_BUSWIDTH);
            SK_SD_RETURN_CHECK(errorstatus);

            SK_SetSDIO4BitWide();
        }
    } while (0);

    return errorstatus;
}

 

5.2 传输模式代码

5.2.1 擦除

擦除部分,除了发送指令后,需要发送 cmd13 轮询当前的状态,只有不为 busy 了,才算擦除完成:

static SD_Error SK_IsCardProgramming(uint8_t *pstatus)
{
    SD_Error errorstatus = SD_OK;
    __IO uint32_t respR1 = 0, status = 0;

    SK_SdioSendCmd_13(g_stSDCARD.RCA);

    status = SDIO->STA;

    while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
    {
        status = SDIO->STA;
    }

    do {
        if (status & SDIO_FLAG_CTIMEOUT)
        {
            errorstatus = SD_CMD_RSP_TIMEOUT;
            SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
            break;
        }

        if (status & SDIO_FLAG_CCRCFAIL)
        {
            errorstatus = SD_CMD_CRC_FAIL;
            SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
            break;
        }

        status = (uint32_t)SDIO_GetCommandResponse();

        if (status != SD_CMD_SEND_STATUS)
        {
            errorstatus = SD_ILLEGAL_CMD;
            break;
        }

        /*!< Clear all the static flags */
        SDIO_ClearFlag(SDIO_STATIC_FLAGS);

        /*!< We have received response, retrieve it for analysis  */
        respR1 = SDIO_GetResponse(SDIO_RESP1);

        /*!< Find out card status */
        *pstatus = (uint8_t) ((respR1 >> 9) & 0x0000000F);   //status[12:9] :cardstate 

        if ((respR1 & SD_OCR_ERRORBITS) == 0x00)
        {
            return(errorstatus);
        }

        if (respR1 & SD_OCR_ADDR_OUT_OF_RANGE)
        {
            return(SD_ADDR_OUT_OF_RANGE);
        }

        if (respR1 & SD_OCR_ADDR_MISALIGNED)
        {
            return(SD_ADDR_MISALIGNED);
        }

        if (respR1 & SD_OCR_BLOCK_LEN_ERR)
        {
            return(SD_BLOCK_LEN_ERR);
        }

        if (respR1 & SD_OCR_ERASE_SEQ_ERR)
        {
            return(SD_ERASE_SEQ_ERR);
        }

        if (respR1 & SD_OCR_BAD_ERASE_PARAM)
        {
            return(SD_BAD_ERASE_PARAM);
        }

        if (respR1 & SD_OCR_WRITE_PROT_VIOLATION)
        {
            return(SD_WRITE_PROT_VIOLATION);
        }

        if (respR1 & SD_OCR_LOCK_UNLOCK_FAILED)
        {
            return(SD_LOCK_UNLOCK_FAILED);
        }

        if (respR1 & SD_OCR_COM_CRC_FAILED)
        {
            return(SD_COM_CRC_FAILED);
        }

        if (respR1 & SD_OCR_ILLEGAL_CMD)
        {
            return(SD_ILLEGAL_CMD);
        }

        if (respR1 & SD_OCR_CARD_ECC_FAILED)
        {
            return(SD_CARD_ECC_FAILED);
        }

        if (respR1 & SD_OCR_CC_ERROR)
        {
            return(SD_CC_ERROR);
        }

        if (respR1 & SD_OCR_GENERAL_UNKNOWN_ERROR)
        {
            return(SD_GENERAL_UNKNOWN_ERROR);
        }

        if (respR1 & SD_OCR_STREAM_READ_UNDERRUN)
        {
            return(SD_STREAM_READ_UNDERRUN);
        }

        if (respR1 & SD_OCR_STREAM_WRITE_OVERRUN)
        {
            return(SD_STREAM_WRITE_OVERRUN);
        }

        if (respR1 & SD_OCR_CID_CSD_OVERWRIETE)
        {
            return(SD_CID_CSD_OVERWRITE);
        }

        if (respR1 & SD_OCR_WP_ERASE_SKIP)
        {
            return(SD_WP_ERASE_SKIP);
        }

        if (respR1 & SD_OCR_CARD_ECC_DISABLED)
        {
            return(SD_CARD_ECC_DISABLED);
        }

        if (respR1 & SD_OCR_ERASE_RESET)
        {
            return(SD_ERASE_RESET);
        }

        if (respR1 & SD_OCR_AKE_SEQ_ERROR)
        {
            return(SD_AKE_SEQ_ERROR);
        }

    } while (0);

    return errorstatus;
}

SD_Error SK_SD_Erase(uint32_t start_addr, uint32_t end_addr)
{
    SD_Error errorstatus = SD_OK;
    uint32_t delay = 0;
    __IO uint32_t maxdelay = 0;
    uint8_t cardstate = 0;

    maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);

    if (g_stSDCARD.CardType == SDIO_HIGH_CAPACITY_SD_CARD)
    {
        start_addr /= SD_BLOCK_SIZE_512;
        end_addr   /= SD_BLOCK_SIZE_512;
    }

    do {
        /*!< Send CMD32 SD_ERASE_GRP_START with argument as addr  */
        SK_SdioSendCmd_32(start_addr);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SD_ERASE_GRP_START);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Send CMD33 SD_ERASE_GRP_END with argument as addr  */
        SK_SdioSendCmd_33(end_addr);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SD_ERASE_GRP_END);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Send CMD38 ERASE */
        SK_SdioSendCmd_38();
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_ERASE);
        SK_SD_RETURN_CHECK(errorstatus);

        for (delay = 0; delay < maxdelay; delay++);

        /*!< Wait till the card is in programming state */
        errorstatus = SK_IsCardProgramming(&cardstate);
        while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
        {
            errorstatus = SK_IsCardProgramming(&cardstate);
        }
    } while (0);

    return errorstatus;
}

 

5.2.2 读数据(单 Block 和 多 Block)

开启 DMA2,并且使能 DMA,由于 DMA 取以 32bit 的方式,所以 DMA_BufferSize = BufferSize / 4;

void SD_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize)
{
  DMA_InitTypeDef DMA_InitStructure;

  DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);

  /*!< DMA2 Channel-4 disable */
  DMA_Cmd(DMA2_Channel4, DISABLE);

  /*!< DMA2 Channel4 Config */
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA2_Channel4, &DMA_InitStructure);

  /*!< DMA2 Channel4 enable */
  DMA_Cmd(DMA2_Channel4, ENABLE); 
}

SD_Error SK_SD_ReadBlock(uint8_t *read_buf, uint32_t read_addr, uint16_t blk_size)
{
    SD_Error errorstatus = SD_OK;
    SDIO_DataInitTypeDef SDIO_DataInitStructure;

    TransferError = SD_OK;
    TransferEnd = 0;
    StopCondition = 0;

    SDIO->DCTRL = 0x0;

    if (g_stSDCARD.CardType == SDIO_HIGH_CAPACITY_SD_CARD)
    {
        blk_size   = SD_BLOCK_SIZE_512;
        read_addr /= SD_BLOCK_SIZE_512;
    }

    do {
        /*!< Send CMD16 SD_CMD_SET_BLOCKLEN with argument as block size  */
        SK_SdioSendCmd_16(blk_size);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SET_BLOCKLEN);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Configure the Data and make the state machine go into the WAIT_R State */
        SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
        SDIO_DataInitStructure.SDIO_DataLength = blk_size;
        SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_512b;
        SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
        SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
        SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
        SDIO_DataConfig(&SDIO_DataInitStructure);

        /*!< Send CMD17 READ_SINGLE_BLOCK */
        SK_SdioSendCmd_17(read_addr);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_READ_SINGLE_BLOCK);
        SK_SD_RETURN_CHECK(errorstatus);

        // Use DMA2
        SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
        SDIO_DMACmd(ENABLE);
        SD_DMA_RxConfig((uint32_t *)read_buf, blk_size);

    } while (0);

    return errorstatus;
}

SD_Error SK_SD_ReadMultiBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{
    SD_Error errorstatus = SD_OK;
    SDIO_DataInitTypeDef SDIO_DataInitStructure;

    TransferError = SD_OK;
    TransferEnd = 0;
    StopCondition = 1;

    SDIO->DCTRL = 0x0;

    if (g_stSDCARD.CardType == SDIO_HIGH_CAPACITY_SD_CARD)
    {
        BlockSize = SD_BLOCK_SIZE_512;
        ReadAddr /= SD_BLOCK_SIZE_512;
    }

    do {
        /*!< Send CMD16 SD_CMD_SET_BLOCKLEN with argument as block size  */
        SK_SdioSendCmd_16(BlockSize);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SET_BLOCKLEN);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Configure the Data and make the state machine go into the WAIT_R State */
        SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
        SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
        SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_512b;
        SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
        SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
        SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
        SDIO_DataConfig(&SDIO_DataInitStructure);

        /*!< Send CMD18 READ_MULT_BLOCK with argument data address */
        SK_SdioSendCmd_18(ReadAddr);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SET_BLOCKLEN);
        SK_SD_RETURN_CHECK(errorstatus);

        SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
        SDIO_DMACmd(ENABLE);
        SD_DMA_RxConfig((uint32_t *)readbuff, (NumberOfBlocks * BlockSize));

    } while (0);

    return errorstatus;
}

 

5.2.3 写数据(单 Block 和 多 Block)

void SD_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize)
{

  DMA_InitTypeDef DMA_InitStructure;

  DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);

  /*!< DMA2 Channel4 disable */
  DMA_Cmd(DMA2_Channel4, DISABLE);

  /*!< DMA2 Channel4 Config */
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferSRC;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA2_Channel4, &DMA_InitStructure);

  /*!< DMA2 Channel4 enable */
  DMA_Cmd(DMA2_Channel4, ENABLE);  
}

SD_Error SK_SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize)
{
    SD_Error errorstatus = SD_OK;
    SDIO_DataInitTypeDef SDIO_DataInitStructure;

    TransferError = SD_OK;
    TransferEnd = 0;
    StopCondition = 0;

    SDIO->DCTRL = 0x0;

    if (g_stSDCARD.CardType == SDIO_HIGH_CAPACITY_SD_CARD)
    {
        BlockSize  = SD_BLOCK_SIZE_512;
        WriteAddr /= SD_BLOCK_SIZE_512;
    }

    do {
        /*!< Send CMD16 SD_CMD_SET_BLOCKLEN with argument as block size  */
        SK_SdioSendCmd_16(BlockSize);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SET_BLOCKLEN);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Send CMD24 WRITE_SINGLE_BLOCK */
        SK_SdioSendCmd_24(WriteAddr);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_WRITE_SINGLE_BLOCK);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Configure the Data and make the state machine go into the WAIT_S State */
        SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
        SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
        SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_512b;
        SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
        SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
        SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
        SDIO_DataConfig(&SDIO_DataInitStructure);

        // DMA2 Mode
        SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
        SD_DMA_TxConfig((uint32_t *)writebuff, BlockSize);
        SDIO_DMACmd(ENABLE);

    } while (0);

    return errorstatus;
}

SD_Error SK_SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{
    SD_Error errorstatus = SD_OK;
    SDIO_DataInitTypeDef SDIO_DataInitStructure;
    __IO uint32_t count = 0;
    
    TransferError = SD_OK;
    TransferEnd = 0;
    StopCondition = 1;
    
    SDIO->DCTRL = 0x0;
    
    if (g_stSDCARD.CardType == SDIO_HIGH_CAPACITY_SD_CARD)
    {
      BlockSize  = SD_BLOCK_SIZE_512;
      WriteAddr /= SD_BLOCK_SIZE_512;
    }

    do {
        /*!< Send CMD16 SD_CMD_SET_BLOCKLEN with argument as block size  */
        SK_SdioSendCmd_16(BlockSize);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SET_BLOCKLEN);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Pre-Erased : To Improve Performance. First CMD55 */
        SK_SdioSendCmd_55(g_stSDCARD.RCA);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_APP_CMD);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Pre-Erased : To Improve Performance. Second ACMD23 */
        SK_SdioSendAppCmd_23(NumberOfBlocks);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_SET_BLOCK_COUNT);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Send CMD25 WRITE_MULT_BLOCK with argument data address */
        SK_SdioSendCmd_25(WriteAddr);
        errorstatus = SK_CheckCmdRsp_1(SD_CMD_WRITE_MULT_BLOCK);
        SK_SD_RETURN_CHECK(errorstatus);

        /*!< Configure the Data and make the state machine go into the WAIT_S State */
        SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
        SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
        SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_512b;
        SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
        SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
        SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
        SDIO_DataConfig(&SDIO_DataInitStructure);

        SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
        SDIO_DMACmd(ENABLE);
        SD_DMA_TxConfig((uint32_t *)writebuff, (NumberOfBlocks * BlockSize));

    } while (0);

    return errorstatus;
}

 

5.2.4 中断处理

SD_Error SD_ProcessIRQSrc(void)
{
    if (StopCondition == 1)
    {
        /*!< Send CMD12 STOP_TRANSMISSION to stop the data transfer */
        SK_SdioSendCmd_12();
        TransferError = SK_CheckCmdRsp_1(SD_CMD_STOP_TRANSMISSION);
    }
    else
    {
        TransferError = SD_OK;
    }

    SDIO_ClearITPendingBit(SDIO_IT_DATAEND);
    SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE);

    TransferEnd = 1;

    return(TransferError);
}

void SDIO_IRQHandler(void) 
{
    /* Process All SDIO Interrupt Sources */
    SD_ProcessIRQSrc();
}

 

5.2.5 测试代码

在读和写的时候,都需要去确认当前 DMA 已经传送完毕,同时写数据的时候,也需要确认当前的卡已经出于 Transfer 的基础状态,而没有在 Programming 状态。

static SDTransferState SK_GetCurrentCardState()
{
    SDTransferState ret;
    SD_Error errorstatus = SD_OK;
    __IO uint32_t respR1 = 0;
    SDCardState state;

    SK_SdioSendCmd_13(g_stSDCARD.RCA);
    errorstatus = SK_CheckCmdRsp_1(SD_CMD_SEND_STATUS);

    if (errorstatus != SD_OK)
        return SD_TRANSFER_ERROR;

    respR1 = SDIO_GetResponse(SDIO_RESP1);
    state = (SDCardState)((respR1 >> 9) & 0x0F);

    if (SD_CARD_TRANSFER == state)
        ret = SD_TRANSFER_OK;
    else if (SD_CARD_ERROR == state)
        ret = SD_TRANSFER_ERROR;
    else
        ret = SD_TRANSFER_BUSY;

    return ret;
}

SD_Error SD_WaitReadOperation(void)
{
    SD_Error errorstatus = SD_OK;

    // Wait for Transfer ok
    while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
    {}

    if (TransferError != SD_OK)
    {
        return(TransferError);
    }

    return(errorstatus);
}

SD_Error SD_WaitWriteOperation(void)
{
    SD_Error errorstatus = SD_OK;

    // Wait for Transfer ok
    while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
    {}

    if (TransferError != SD_OK)
    {
    return(TransferError);
    }

    /*!< Clear all the static flags */
    SDIO_ClearFlag(SDIO_STATIC_FLAGS);

    return(errorstatus);
}

void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset)
{
    uint16_t index = 0;

    /* Put in global buffer same values */
    for (index = 0; index < BufferLength; index++ )
    {
        pBuffer[index] = index + Offset;
    }
}

TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength)
{
    while (BufferLength--)
    {
        if (*pBuffer1 != *pBuffer2)
        {
            return FAILED;
        }
        pBuffer1++;
        pBuffer2++;
    }

    return PASSED;
}

uint8_t Buffer_MultiBlock_Tx[MULTI_BUFFER_SIZE] = {0};
uint8_t Buffer_MultiBlock_Rx[MULTI_BUFFER_SIZE] = {0};
uint8_t Buffer_Block_Tx[MULTI_BUFFER_SIZE] = {0};
uint8_t Buffer_Block_Rx[MULTI_BUFFER_SIZE] = {0};

volatile TestStatus EraseStatus = FAILED, TransferStatus1 = FAILED, TransferStatus2 = FAILED;

/* Test Interface */
SD_Error SD_EraseTest(void)
{
    /* Erase NumberOfBlocks Blocks of WRITE_BL_LEN(512 Bytes) */
    SD_Error Status = SD_OK;
    // Erase start at 0x00, length
    Status = SK_SD_Erase(0x00, MULTI_BUFFER_SIZE);

    if (SD_OK == Status)
    {
        Status = SK_SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, SD_BLOCK_SIZE_512, NUMBER_OF_BLOCKS);
        Status = SD_WaitReadOperation();
        while(SK_GetCurrentCardState() != SD_TRANSFER_OK);
    }

    return Status;
}

SD_Error SD_SingleBlockTest(void)
{
    SD_Error Status = SD_OK;

    Fill_Buffer(Buffer_Block_Tx, SD_BLOCK_SIZE_512, 0x320F);

    Status = SK_SD_WriteBlock(Buffer_Block_Tx, 0x00, SD_BLOCK_SIZE_512);
    Status = SD_WaitWriteOperation();
    while(SK_GetCurrentCardState() != SD_TRANSFER_OK);

    if (Status == SD_OK)
    {
        Status = SK_SD_ReadBlock(Buffer_Block_Rx, 0x00, SD_BLOCK_SIZE_512);
        Status = SD_WaitReadOperation();
        while(SK_GetCurrentCardState() != SD_TRANSFER_OK);
    }

    if (Status == SD_OK)
    {
        TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, SD_BLOCK_SIZE_512);
    }

    return Status;
}

SD_Error SD_MultiBlockTest(void)
{
    SD_Error Status = SD_OK;

    Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0);

    Status = SK_SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, SD_BLOCK_SIZE_512, NUMBER_OF_BLOCKS);
    Status = SD_WaitWriteOperation();
    while(SK_GetCurrentCardState() != SD_TRANSFER_OK);

    if (Status == SD_OK)
    {
        Status = SK_SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, SD_BLOCK_SIZE_512, NUMBER_OF_BLOCKS);
        /* Check if the Transfer is finished */
        Status = SD_WaitReadOperation();
        while(SK_GetCurrentCardState() != SD_TRANSFER_OK);
    }

    if (Status == SD_OK)
    {
        TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
    }

    return Status;
}

 

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页