33. SDHI——SD卡读写测试

33.1. SDHI 模块简介

SD存储卡(Secure Digital Memory Card),是由SD协会(SDA)开发的专有非易失性闪存卡格式,用于便携式设备。 该标准于1999年8月由闪迪、松下和东芝共同推出,作为对多媒体卡(MMC卡)的改进,并已成为行业标准。

SD卡在我们生活中已经非常普遍了,控制器对SD卡进行读写通信操作一般有两种通信接口可选, 一种是SPI接口,另外一种就是SDIO接口。SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。 MMC卡可以说是SD卡的前身,现阶段已经用得很少。SD I/O卡本身不是用于存储的卡,它是指利用SDIO传输协议的一种外设。比如Wi-Fi Card, 它主要是提供Wi-Fi功能,有些Wi-Fi模块是使用串口或者SPI接口进行通信的,但Wi-Fi SDIO Card是使用SDIO接口进行通信的。 并且一般设计SD I/O卡是可以插入到SD的插槽。CE-ATA是专为轻薄笔记本硬盘设计的硬盘高速通讯接口。

随之科技发展,SD卡容量需求越来越大,SD卡发展到现在也是有几个版本的, 关于SDIO接口的设备整体概括见下图。

SDIO接口的设备

SDHI支持1位和4位总线,用于连接支持SD、SDHC和SDXC格式的不同存储卡。 开发符合SD规范的主机设备时,您必须遵守SD主机/辅助产品许可协议(SD HALA)。 MMC接口支持提供e.MMC 4.51(JEDEC Standard JESD 84- B451)设备访问的1位、4位和8位MMC总线。 该接口还提供向后兼容性,并支持高速SDR传输模式。

关于SD卡和SD I/O部分内容可以在SD协会网站获取到详细的介绍,比如各种SD卡尺寸规则、读写速度标示方法、应用扩展等等信息。 SD卡协会(SDA)规定了各种涉及SD卡的协议,相关规范文件可以在SDA官网 https://www.sdcard.org/downloads/pls/中下载查看。 JEDEC是微电子产业的领导标准机构,JEDEC官网 https://www.jedec.org/中有多媒体卡的相关规范文件。

图

瑞萨的 SDHI 支持以下存储设备:

  • SDSC(SD标准容量)、SDHC(SD高容量)、SDXC(SD扩展容量)和e.MMC(嵌入式多媒体卡)

  • 支持读取、写入和擦除 SD 存储设备

  • 支持 1、4 或 8 位数据总线(仅 e.MMC 支持 8 位总线)

  • 支持检测设备写保护(仅限 SD 卡)

  • 支持高速模式

  • 自动将时钟配置为主机(MCU)和设备支持的最大时钟速率

  • 支持使用 DMAC 或 DTC 进行硬件加速

  • 支持操作完成或发生错误时的回调通知

SDHI时钟频率由 PCLKB分频而来,可选2n(n=0到9)分频

33.2. SD卡介绍

参考文档:
  • 《Physical Layer Simplified Specification 6.00 Aug. 29, 2018》

  • 《SD Host Controller Simplified Specification 4.20 Jul. 25, 2018》

33.2.1. SD卡分类

33.2.1.1. 按容量分类

SD卡按容量(Capacity)分类,可以分为标准SD卡、高容量SD卡、扩展容量SD卡、超大容量SD卡,详细规格如下:

  1. Standard Capacity SD Memory Card (SDSC):这种卡容量小于等于2GB

  2. High Capacity SD Memory Card (SDHC):这种卡容量大于2GB,小于等于32GB

  3. Extended Capacity SD Memory Card (SDXC):这种卡容量大于32GB, 小于等于2TB

  4. Ultra Capacity SD Memory Card (SDUC):这种卡容量大于2TB, 小于等于128TB

如果一张SD卡,上面标着 “HC” ,说明这是张SDHC卡。

33.2.1.2. 按总线速度

如果SD卡按总线速度模式来分,有下面几种:

  1. 默认速度模式:3.3V供电模式,频率上限25MHz,速度上限 12.5MB/sec

  2. 高速模式:3.3V供电模式,频率上限50MHz,速度上限 25MB/sec

  3. SDR12:UHS-I卡,1.8V供电模式,频率上限25MHz,速度上限 12.5MB/sec

  4. SDR25:UHS-I卡,1.8V供电模式,频率上限50MHz,速度上限 25MB/sec

  5. SDR50:UHS-I卡,1.8V供电模式,频率上限100MHz,速度上限 50MB/sec

  6. SDR104:UHS-I卡,1.8V供电模式,频率上限208MHz,速度上限 104MB/sec

  7. DDR50:UHS-I卡,1.8V供电模式,频率上限50MHz,性能上限 50MB/sec

  8. UHS156:UHS-II卡,频率范围26 MHz-52 MHz,性能上限 1.56Gbps

  9. UHS624:UHS-II卡,频率范围26 MHz-52 MHz,性能上限 6.24Gbps

SDR(Single Date Rate), 一个周期只能采集一次数据,即一个bit,由于SD卡是4条数据线并行传输,所以一个周期能传输4bit, 如果频率是50MHz(即1秒传输次数为50 000 000),那么1秒能传输的数据量为25MB(这里1MB为1 000 000 Byte)。 所以这就是为什么各种SDR模式里面,频率上限是速度上限的两倍。 而对于DDR(Double Data Rate),在时钟上升沿和下降沿都可以采集数据,也就是单一周期内可读取或写入2次,因此4条并行数据线在一个周期内能传输8bit的数据。

33.2.1.3. 按读写性能

如果按照读写数据性能分类有下面几种(每种规格后面的数字象征最小的读写速度):

  1. Class 0: 这种卡没有性能要求,包含了物理层规范层2.0版本之前的旧卡

  2. Class 2: 在默认速度模式下,性能至少要达到(大于等于)2MB/sec

  3. Class 4: 在默认速度模式下,性能至少要达到 4MB/sec

  4. Class 6: 在默认速度模式下,性能至少要达到 6MB/sec

  5. Class 10: 在高速模式下,性能至少要达到 10MB/sec

注解

上面所说的性能单位[MB/sec]表示1000x1000[Byte/sec], 而数据大小单位[MB]表示1024x1024[Byte]。 这是因为最大SD总线速度由最大SD时钟频率决定(在50 MHz时,为25[MB/sec]=25000000[Byte/sec]), 在频率计算中,MHz转换为Hz是 1MHz = 1000x1000Hz; 而数据大小基于内存边界(2的幂)。

../../_images/note.png

注解

实际书写规范中 B 应表示 Byte (字节), b 应表示 bit (比特), Mb 和 MB 是有区别的。

33.2.2. SD 卡物理结构

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

../../_images/SD_card.png

SD卡总共有8个寄存器,用于设定或表示SD卡信息,参考下方的SD卡寄存器表。这些寄存器只能通过对应的命令访问, 对SD卡进行控制操作并不是像操作控制器GPIO相关寄存器那样一次读写一个寄存器的,它是通过命令来控制, SDIO定义了64个命令,每个命令都有特殊意义,可以实现某一特定功能,SD卡接收到命令后, 根据命令要求对SD卡内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

SD卡寄存器表

寄存器名称

bit宽度

描述

CID(Card IDentification Register)寄存器

128

卡识别码寄存器,存储SD卡唯一标识号,该号在卡生产厂家编程后无法修改

RCA(Relative Card Address)寄存器

16

卡相对地址寄存器是一个16位可写的地址寄存器,控制器可通过地址选择对应地址的SD卡

DSR(Driver Stage Register)寄存器

16

驱动级寄存器,属于可选寄存器,用于配置卡的驱动输出

CSD(Card-Specific Data Register)寄存器

128

卡特性数据寄存器,包含了访问该卡数据时的必要配置信息

SCR(SD Card Configuration Register)寄存器

64

SD卡配置寄存器(SCR),提供了SD卡的一些特殊特性在这张卡内,这个寄存器内容由制造商在生产厂内设置

OCR(Operating Conditions Register)寄存器

32

操作条件寄存器主要存储了VDD电压范围,SD卡操作电压范围为2~3.6V

SSR(SD Status Register)寄存器

512

SD卡状态;SD卡卡专有特征信息

CSR(Card Status Register)寄存器

32

卡状态信息

每个寄存器位的含义可以参考SD简易规格文件《Part1_Physical_Layer_Simplified_Specification_Ver6.00》第5章内容。

SD卡主要引脚和功能描述如下:

  • CLK: 时钟信号,控制器或者SD卡在每个时钟周期传输一个命令位或数据位,在SD总线的默认速度模式下频率可在0~25MHz之间变化, SD卡的总线管理器可以不受任何限制的自由产生0~25MHz的频率,在UHS-I速度模式下,时钟频率最高可达208M。

  • CMD: 命令和响应复用引脚,命令是由控制器发给SD卡,可以是从控制器到单个SD卡,也可以是到SD总线上所有卡;响应是存储卡对控制器发送的命令应答,应答可以来自单卡或所有卡。

  • DAT0 ~ DAT3: 数据线,数据可以从卡传向控制器也可以从控制器传向卡。

33.2.3. SDIO总线

SDIO总线通信是基于命令和数据传输的。通讯由一个起始位(“0”),由一个停止位(“1”)终止。SD通信一般是主机发送一个命令(Command), 从设备在接收到命令后作出响应(Response),如有需要会有数据(Data)传输参与。

  • Command 命令: 一个命令代表着一个操作的开始。命令通过CMD线传输,方向从主机到SD卡。

  • Response 响应: 响应是SD卡对前一次主机发送的命令的执行情况的反馈。也是通过CMD线传输,方向从SD卡到主机。

  • Data 数据: 数据是通过4条DATA线传输的,方向可以从主机到SD卡,也可以从SD卡到主机。

SD总线的基本交互是命令与响应交互,见下图。

../../_images/bus1.png

SD数据是以块(Block)形式传输的,SDHC卡数据块长度一般为512字节,数据可以从主机到卡,也可以是从卡到主机。 数据块总是以CRC位为后缀,来保证数据传输成功。CRC位由SD卡系统硬件生成。 当CMD线上跟随停止命令时,多块传输终止。主机可以配置数据传输使用单条或多条数据线。 主机向SD卡写入数据块操作示意图如下。

../../_images/bus2.png

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

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

SD卡数据包有两种格式,一种是常规数据(8bit宽),它先发低字节(最低有效位 LSB)再发高字节(最高有效位 MSB), 而每个字节则是先发高位(最高有效位 MSB)再发低位(最低有效位 LSB),4线传输示意如图所示。

../../_images/8data_transmission.png

4线同步发送,每根线发送一个字节的其中两个位,数据位在四线顺序排列发送,DAT3数据线发送较高位,DAT0数据线发送较低位。 另外一种数据包发送格式是宽位数据包格式,对SD卡而言宽位数据包发送方式是针对SD卡SSR(SD状态)寄存器内容发送的, SSR寄存器总共有512bit,在主机发出ACMD13命令后,SD卡将SSR寄存器内容通过DAT线发送给主机。宽位数据包格式示意见图 宽位数据包传输

宽位数据包传输

33.2.3.1. SD命令

SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。

33.2.3.1.1. 命令格式

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

SD命令格式

SD命令的组成如下:

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

  • 传输标志:用于区分传输方向,该位为1时表示命令,方向为主机传输到SD卡,该位为0时表示响应,方向为SD卡传输到主机。 命令主体内容包括命令、地址信息/参数和CRC校验三个部分。

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

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

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

33.2.3.1.2. 命令类型

SD命令有4种类型:

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

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

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

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

所有命令和响应都通过SD存储卡的CMD线发送。 命令传输总是从对应于命令字的最左边开始。

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

33.2.3.1.3. 命令描述

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

虽然没有必须完全记住每个命令详细信息,但熟悉命令对后面编程理解非常有帮助。

SD部分命令描述

33.2.3.2. 响应

所有响应都通过命令行CMD发送,响应从左到右传输。 响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。SDIO总共有7个响应类型(代号:R1~R7), 其中SD卡没有R4、R5类型响应,SDIO卡支持名为R4和R5的其他响应类型。 特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。 与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。 短响应是48bit长度,只有R2类型是长响应,其长度为136bit。 除了R3类型之外,其他响应都使用CRC7校验来校验,对于R2类型是使用CID和CSD寄存器内部CRC7。各个类型响应具体情况如下图。

SD卡响应类型

33.3. SD卡的操作模式及切换

33.3.1. SD卡的操作模式

主机和卡之间的一般通信由主机(master)控制。 主机发送两种类型的命令:广播和寻址(点对点)命令。

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

33.3.2. 卡识别模式

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

../../_images/card_state.png

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

上电或CMD0后,所有卡的CMD线都处于输入模式,等待下一个命令的起始位。 这些卡使用默认的卡相对地址(RCA=0x0000)和默认400kHz时钟频率进行初始化。

主机在开始与卡通信前,需要先确定双方在互相支持的电压范围内。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之后也进入数据传输模式。

33.3.3. 数据传输模式

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

数据传输模式卡状态转换

CMD7用于选择一张卡,并将其设置为传输状态,在给定时间内,只能有一张卡处于传输状态。 如果之前有卡处于传输状态,则会将其与主机连接释放,同时该卡会回到待机状态。

必须选择一个RCA地址目标卡使其进入传输状态才可以进行数据通信。 同时通过CMD7命令也可以让已经被选择的目标卡返回到待机状态。 但传输卡地址为“0x0000”时候,可以将所有卡设置为待机状态, 当有卡的RCA为0时,可以通过CMD3来更改卡的RCA号。

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

33.4. SDHI功能详解

33.4.1. DMA传输请求

SD/MMC主机接口有两种DMA传输请求。 在介绍DMA请求时,我们要先了解以下几个寄存器。

33.4.1.1. SDIH 寄存器

SD_INFO2 (SD Card Interrupt Flag Register 2) : SD卡中断标志寄存器2

SD_INFO2 寄存器

标志

功能

[0]

CMDE (R/W)

命令错误检测标志

0:未检测到命令错误

1:检测到命令错误

[1]

CRCE (R/W)

CRC错误检测标志

0:未检测到CRC错误

1:检测到CRC错误

[2]

ENDE (R/W)

结束误码检测标志

0:未检测到结束位错误

1:检测到结束位错误

[3]

DTO (R/W)

数据超时检测标志

0:未检测到数据超时

1:检测到数据超时

[4]

ILW (R/W)

SD_BUF0非法写访问检测标志

0:未检测到对SD_BUF0寄存器的非法写入访问

1:检测到对SD_BUF0寄存器的非法写入访问

[5]

ILR (R/W)

SD_BUF0非法读访问检测标志

0:未检测到对SD_BUF0寄存器的非法读访问

1:检测到对SD_BUF0寄存器的非法读访问

[6]

RSPTO (R/W)

响应超时检测标志

0:未检测到响应超时

1:检测到响应超时

[7]

SDD0MON (R)

SDHI_D0引脚状态标志

0:SDnDAT0引脚低

1:SDnDAT0引脚为高

[8]

BRE (R/W)

SD_BUF0读取启用标志

0:禁用对SD_BUF0寄存器的读访问

1:启用对SD_BUF0寄存器的读访问

[9]

BWE (R/W)

SD_BUF0写启用标志

0:禁用对SD_BUF0寄存器的写入访问

1:启用对SD_BUF0寄存器的写入访问

[10]

— (R/W)

R/W = 0

[11]

— (R/W)

读取的值未定义,写入值应为1

[12]

— (R/W)

R/W = 0

[13]

SD_CLK_CTRLEN (R)

SD_CLK_CTRL写启用标志

0:SD/MMC总线(CMD和DAT线路)繁忙,因此禁用对SD_CLK_CTRL.CLKEN和CLKSEL[7:0]位的写入访问

1:SD/MMC总线(CMD和DAT线路)不忙,因此启用对SD_CLK_CTRL.CLKEN和CLKSEL[7:0]位的写入访问

[14]

CBSY (R)

命令序列状态标志

0:命令序列完成

1:命令序列正在进行(忙)

[15]

ILA (R/W)

非法访问错误检测标志

0:未检测到非法访问错误

1:检测到非法访问错误

[31:16]

— (R/W)

R/W = 0

SD_INFO2 寄存器指示SD缓冲器的状态和SD卡/MMC的状态。 将要清除的标志设置为0。 将未清除的标志设置为1。

SD_DMAEN (DMA Mode Enable Register) : DMA模式启用寄存器

SD_DMAEN 寄存器

标志

功能

[0]

R/W = 0

[1]

DMAEN

DMA传输启用。

0:禁用使用DMA传输访问SD_BUF0寄存器。

1:启用DMA传输以访问SD_BUF0寄存器。

[3:2]

R/W = 0

[4]

R/W = 1

[11:5]

R/W = 0

[12]

R/W = 1

[31:13]

R/W = 0

当 SD_INFO2.CBSY 位为1时,不可重写此位。 当 SD_INFO2_MASK.BWEM 位为0时 或 SD_INFO2_MASK.BREM 位为0时,将 SD_DMAEN.DMAEN 位设置为0。 当 SD_DMAEN.DMAEN 位为1时, SD_INFO2_MASK.BWEM 位设为1, SD_INFO2_MASK.BREM 位设为1。 SD_DMAEN 寄存器使能或禁用DMA传输。

SD_SIZE (Transfer Data Length Register) : 传输数据长度寄存器

SD_SIZE 寄存器

标志

功能

[9:0]

LEN[9:0]

传输数据大小设置。

这些位指定传输数据大小。

[31:10]

R/W = 0

当SD_INFO2.CBSY标志为1时,不可写SD_SIZE寄存器。 SD_SIZE寄存器用于设置传输数据大小。

LEN[9:0]位(传输数据大小设置)使用单块传输时,传输数据大小可以在LEN[9:0]位中从1字节到512字节进行设置。 当在多块传输序列(CMD18和CMD25)期间自动发出CMD12时,传输数据大小只能设置为512字节。 当在多数据块传输序列期间未自动发出CMD12时,传输数据大小可设置为32、64、128、256或512字节。 但是,32、64、128或256字节的多数据块读取传输只能在SDIO多数据块传输(CMD53)期间执行。 使用包含数据传输的命令时,请勿将这些位设置为0。

SD_BUF0 (SD Buffer Register) : SD 缓冲寄存器

写入SD卡时,写入数据写入该寄存器。 从SD卡读取时,从该寄存器读取读取的数据。 该寄存器内部连接到两个512字节的缓冲区。 如果在执行多块读取时两个缓冲区都不为空,则SD卡/MMC时钟停止以暂停接收数据。 当其中一个缓冲器为空时,提供SD卡/MMC时钟以恢复接收数据。

SOFT_RST (Software Reset Register) : 软件重置寄存器

SOFT_RST 寄存器

标志

功能

[0]

SDRST

软件重置控制。

0:重置SD/MMC主机接口软件。

1:取消SD/MMC主机接口软件重置

[2:1]

R/W = 0

[31:3]

R/W = 0

软件复位会使下面的位和标志恢复初始化

图

SD_STOP (Data Stop Register) : 数据停止寄存器

SD_STOP 寄存器

标志

功能

[0]

STP

传输停止当该位设置为1时,数据传输停止。

[7:1]

R/W = 0

[8]

SEC

块计数寄存器值选择。 0:禁用SD_SECCNT寄存器值 1:启用SD_SECCNT寄存器值

[31:9]

R/W = 0

该值通过重置和由SOFT_RST.SDRST标志触发的重置进行初始化。 当SD_INFO2.CBSY标志为1时,请勿重写此位。 SD_STOP寄存器停止数据传输。 在多块传输序列期间,可通过设置SD_STOP寄存器将SD_SECCNT寄存器值(要传输的块数)设置为有效或无效。

SDIO_MODE (SDIO Mode Control Register) : SDIO模式控制寄存器

SDIO_MODE 寄存器

标志

功能

[0]

INTEN

SDIO中断接受启用

0:禁用SDIO中断接受

1:启用SDIO中断接受

[1]

R/W = 0

[2]

RWREQ

读取等待请求

0:允许SD/MMC退出读取等待状态

1:请求SD/MMC进入读取等待状态

[7:3]

R/W = 0

[8]

IOABT

SDIO中止

在由CMD53触发的多块传输期间,如果此位设置为1,则立即发出CMD52,并中止命令序列

[9]

C52PUB

SDIO无中止

在由CMD53触发的多块传输期间,如果此位设置为1,则在传输过程完成且命令序列完成后发出CMD52

[31:10]

R/W = 0

当SD_INFO2.CBSY标志为1时,不可写SD_STOP寄存器。 SDIO_MODE寄存器控制SDIO中断的接收、多块传输期间的CMD52发送和读取等待请求。 请勿同时将位 C52PUB位 和 IOABT位 设置为1。

更多寄存器相关描述请查看《RA6M5 Group User’s Manual: Hardware》第39章内容。

33.4.1.2. (1) SD_BUF写 + DMA传输请求

SD_BUF_DMA写操作示例:

图

当SD_INFO2寄存器的BWE位设置为1,同时SD_DMAEN寄存器的DMAEN位设置为1时,SD_BUF写入DMA传输请求被置位。

当一个块中的最后一个数据(基于在SD_SIZE中设置的传输数据大小)传输完成时,SD_BUF写入DMA传输请求被拒绝。 通过将SOFT_RST寄存器的SDRST位清0或将SD_STOP寄存器的STP位设置为1,也可以拒绝SD_BUF写入DMA传输请求。 但是,如果在DMA传输时发生通信错误或超时,则不会拒绝SD_BUF写入DMA传输请求。

在通过DMA传输请求写入SD_BUF,并传输一个块中的最后一个数据后,SD_INFO2寄存器的BWE位被清除。

DMA传输的数量必须为 ( n × one block)。 (n = 整数,one block = (块)在SD_SIZE中设置的传输数据大小)

当SDIO_MODE寄存器的IOABT位设置为1时,SD_BUF写入DMA传输请求被拒绝。 通过将SD_DMAEN.DMAEN位清0,也可以拒绝DMA传输请求。 但是,在写入SD_CMD之前,如果SD_DMAEN.DMAEN位设置为1,则DMA传输请求再次置位。

由于SD_INFO2.BWE位不会在设置STP/IOABT位、通信错误或超时时清零,因此在发出下一个命令之前,应将该位清0。 设置SD_INFO2.BWE位时,不会发出通过DMA传输写入SD_BUF的下一个请求。

33.4.1.3. (2) SD_BUF读 + DMA传输请求

SD_BUF_DMA读操作示例:

图

当SD_INFO2寄存器的BRE位设置为1,同时SD_DMAEN寄存器的DMAEN位设置为1时,SD_BUF读取DMA传输请求被置位。

当传输一个块中的最后一个数据(基于SD_SIZE中设置的传输数据大小)时,SD_BUF读取DMA传输请求被拒绝。 通过将SOFT_RST寄存器的SDRST位清0或将SD_STOP寄存器的STP位设置为1,SD_BUF读取DMA传输请求也会被拒绝。 但是,如果在DMA传输时发生通信错误或超时,则SD_BUF读取DMA传输请求不会被否定。 在通过DMA传输向SD_BUF写入请求之后,传输一个块中的最后一个数据后,SD_INFO2寄存器的BRE位被清除。

DMA传输的数量必须为(n × one block)。 其中n为整数,one block为块(在SD_SIZE中设置的传输数据大小)。 当SDIO_MODE寄存器的IOABT位设置为1时,SD_BUF读取DMA传输请求被拒绝。

通过将SD_DMAEN.DMAEN位清0,也可以拒绝DMA传输请求。 但是,在写入SD_CMD之前,如果SD_DMAEN.DMAEN位设置为1,则DMA传输请求再次置位。

由于SD_INFO2.BRE位不会在设置STP/IOABT位、通信错误或超时时清零,因此在发出下一个命令之前,应将该位清0。 设置SD_INFO2.BRE位时,不会发出通过DMA传输写入SD_BUF的下一个请求。

33.5. SD 卡读写测试实验

33.5.1. 硬件设计

SD卡接口使用4条数据线(SD_DATA0 ~ SD_DATA3),1条时钟线(SD_CLK), 1个命令和响应复用引脚(SD_CMD_A),以及1个卡检测引脚(SD_CD)。

启明6M5开发板的SD卡接口部分的原理图如图所示:

图

启明4M2开发板的SD卡接口部分的原理图如图所示:

图

33.5.2. 软件设计

33.5.2.1. 新建工程

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “32_SDHI_SDCard_RW”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “32_SDHI_SDCard_RW”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “sdhi_sdcard” 文件夹, 再进入 “sdhi_sdcard” 文件夹里面新建源文件和头文件:“bsp_sdhi_sdcard.c” 和 “bsp_sdhi_sdcard.h”。 工程文件结构如下。

文件结构
32_SDHI_SDCard_RW
├─ ......
└─ src
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ sdhi_sdcard
   │  ├─ bsp_sdhi_sdcard.c
   │  └─ bsp_sdhi_sdcard.h
   └─ hal_entry.c

33.5.2.2. FSP配置

打开工程的 FSP 配置界面,按照一般步骤,首先配置SDHI用到的引脚,再配置SDHI的各项属性参数。

在配置界面底部点击 Pins,根据自己所使用的开发板原理图参考下图来配置 SDHI 引脚,需要注意这里设置的引脚组是混合的,选择 “Mixed” 才不会报错。

图

在配置界面底部点击 Stacks,按如下图所示步骤加入 SDHI 模块。

图

加入 SDHI 模块后如下图所示。

图

它这里给出报错,查找原因后发现,原来它要求我们添加一个底层的传输驱动,而这个底层传输模块可以选用 DMAC 或 DTC。

我们选择使用 DMAC,因此继续按如下图所示步骤在 SDHI 模块下方添加一个 DMAC 模块,用于SD卡的后台数据传输。

图

下面对 SDHI 模块属性和用于传输SD卡数据的 DMAC 模块属性进行配置。

首先点到 SDHI 模块,然后在右下角属性配置窗口进行 SDHI 属性的配置。

SDHI 模块属性

属性

描述

Unaligned Access Support

未对齐的访问支持

SD Support

SD支持

eMMC Support

e.MMC支持

Name

名称

Channel

通道号

Bus Width

总线宽度

Block Size

块大小

Card Detection

卡检测

Write Protection

写保护

Callback

回调函数

Access Interrupt Priority

访问中断优先级

Card Interrupt Priority

卡中断优先级

DTC Interrupt Priority

DTC中断优先级

然后,点到 DMAC 模块,继续配置 DMAC 属性。

DMAC 模块属性

属性

描述

Name

设置为 g_transfer_sdhi

Channel

0

Transfer End Interrupt Priority

传输结束中断优先级。这里设置为 Priority 10

完成上述配置即可保存FSP配置,并生成代码。然后进入下一步的代码编写工作。

33.5.2.3. SD卡初始化函数

该函数十分简单,仅仅调用 R_SDHI_Open 函数初始化了 SDHI 外设模块。 下一步即可按照 SD 卡协议规范对 SD 卡进行操作。

代码 32‑1 SD卡初始化函数
 /* SDHI SD卡初始化函数 */
 void SDCard_Init(void)
 {
     fsp_err_t err;

     /* 打开SDHI */
     err = R_SDHI_Open(&g_sdmmc0_ctrl, &g_sdmmc0_cfg);
     assert(FSP_SUCCESS == err);
 }

33.5.2.4. SD卡中断回调函数

下面是卡中断回调函数,用来判断我们进行的各种事件,并通过一些标志位及时通知应用程序进行处理。

代码 32‑2 SD卡中断回调函数
 __IO uint32_t g_transfer_complete = 0;
 __IO uint32_t g_card_erase_complete = 0;
 __IO bool g_card_inserted = false;


 /* 如果启用了卡检测中断,则在发生卡检测事件时调用回调。 */
 void g_sdmmc0_callback(sdmmc_callback_args_t *p_args)
 {
     if (SDMMC_EVENT_TRANSFER_COMPLETE == p_args->event)  //读取或写入完成
     {
         g_transfer_complete = 1;
     }
     if (SDMMC_EVENT_CARD_INSERTED == p_args->event)  //卡插入中断
     {
         g_card_inserted = true;
     }
     if (SDMMC_EVENT_CARD_REMOVED == p_args->event)   //卡拔出中断
     {
         g_card_inserted = false;
     }
     if (SDMMC_EVENT_ERASE_COMPLETE == p_args->event)  //擦除完成
     {
         g_card_erase_complete = 1;
     }
     if (SDMMC_EVENT_ERASE_BUSY == p_args->event)  //擦除超时
     {
         g_card_erase_complete = 2;
     }
 }

33.5.2.5. SD卡操作函数

代码 32‑3 SD卡操作函数
 uint8_t g_dest[SDHI_MAX_BLOCK_SIZE] BSP_ALIGN_VARIABLE(4);  //4字节对齐
 uint8_t g_src[SDHI_MAX_BLOCK_SIZE]  BSP_ALIGN_VARIABLE(4);


 void SDCard_Operation(void)
 {
     fsp_err_t err;
     sdmmc_status_t status;
     sdmmc_device_t my_sdmmc_device = {0};
     uint32_t i;


     /* 初始化要传输到SD卡内的源数组 */
     for (i = 0; i < SDHI_MAX_BLOCK_SIZE; i++)
     {
         g_src[i] = (uint8_t)('A' + (uint8_t)(i % 26));
     }

     /* 检查卡是否插入 */
     err = R_SDHI_StatusGet(&g_sdmmc0_ctrl, &status);
     assert(FSP_SUCCESS == err);
     if (!status.card_inserted)
     {
         /* 等待卡插入中断 */
         while (!g_card_inserted)
         {
             printf("\r\n请插入SD卡\r\n");
             R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);
         }
         printf("\r\n检测到卡已插入\r\n");
     }

     /* 设备应在检测到VDD最小值后1ms内准备好接受第一个命令。
       参考SD物理层简化规范6.00版第6.4.1.1节“卡的通电时间”。
     */
     R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);

     /* 初始化SD卡。在为SD设备插入卡之前,不应执行此操作。 */
     err = R_SDHI_MediaInit(&g_sdmmc0_ctrl, &my_sdmmc_device);
     assert(FSP_SUCCESS == err);

     /* 写入数据 */
     err = R_SDHI_Write(&g_sdmmc0_ctrl, g_src, 1, 1);
     assert(FSP_SUCCESS == err);
     while (g_transfer_complete == 0);
     g_transfer_complete = 0;

     /* 读出数据 */
     err = R_SDHI_Read(&g_sdmmc0_ctrl, g_dest, 1, 1);
     assert(FSP_SUCCESS == err);
     while (g_transfer_complete == 0);
     g_transfer_complete = 0;

     /* 打印数据 */
     for (i = 0; i < SDHI_MAX_BLOCK_SIZE; i++)
     {
         if (i % 26 == 0)
             printf(" ");
         printf("%c", g_dest[i]);
     }

     /* 对比数据 */
     if (strncmp((char*)&g_dest[0], (char*)&g_src[0], SDHI_MAX_BLOCK_SIZE) == 0)
     {
         printf("\r\nSD卡读写数据成功!\r\n");
     }
     else
     {
         printf("\r\nSD卡读写数据错误!\r\n");
     }
 }

33.5.2.6. hal_entry入口函数

首先是hal_entry函数,关于LED以及串口调试部分不再赘述,在本代码中只讲解重要部分。 此处的代码是以启明6M5板子的为例,启明4M2板子的串口初始化函数名(Debug_UART0_Init)不同,读者需要注意。

代码 32‑4 sd卡读写测试 hal_entry 函数
 /* 用户头文件包含 */
 #include "led/bsp_led.h"
 #include "debug_uart/bsp_debug_uart.h"
 #include "sdhi_sdcard/bsp_sdhi_sdcard.h"


 void hal_entry(void)
 {
     /* TODO: add your own code here */

     LED_Init();         // LED 初始化
     Debug_UART4_Init(); // SCI4 UART 调试串口初始化

     SDCard_Init();      // SD卡初始化

     printf("这是一个 SD 卡读写测试例程\r\n");
     printf("打开串口助手,查看程序运行过程中打印的提示信息\r\n");

     /* 对SD卡进行读写等操作 */
     SDCard_Operation();

     while(1)
     {
         LED1_ON;
         R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
         LED1_OFF;
         R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
     }


 #if BSP_TZ_SECURE_BUILD
     /* Enter non-secure code */
     R_BSP_NonSecureEnter();
 #endif
 }

33.5.3. 下载验证

保证开发板相关硬件连接正确,用USB线连接开发板“USB TO UART”接口跟电脑,然后打开串口助手查看串口的打印信息。 复位开发板后插入SD卡,可以在串口助手上看到如下的程序运行结果。

图