本帖最后由 正点原子运营 于 2023-8-30 18:05 编辑
五十章 SD卡实验
1)实验平台:正点原子探索者STM32F407开发板
2) 章节摘自【正点原子】STM32F407开发指南 V1.1
6)STM32技术交流QQ群:151941872
很多单片机系统都需要大容量存储设备,以存储数据。目前常用的有U盘,FLASH芯片,SD卡等。他们各有优点,综合比较,最适合单片机系统的莫过于SD卡了,它不仅容量可以做到很大(32GB以上),支持SPI/SDIO驱动,而且有多种体积的尺寸可供选择(标准的SD卡尺寸及Micor SD卡尺寸等),能满足不同应用的要求。
只需要少数几个IO口即可外扩一个高达32GB或以上的外部存储器,容量从几十M到几十G选择范围很大,更换也很方便,编程也简单,是单片机大容量外部存储器的首选。
探索者F407开发板V3版本以后,使用的接口是Micro SD卡接口,卡座带自锁功能,使用STM32F4自带的SDIO接口驱动,4位模式,最高通信速度可达48Mhz(分频器旁路时),最高每秒可传输数据24M字节,对于一般应用足够了。在本章中,我们将向大家介绍,如何在开发板上实现MicorSD卡的读取。
本章分为如下几个部分: 50.1 SD卡简介 50.2 SDIO接口简介 50.3 SD卡初始化流程 50.4 硬件设计 50.5 程序设计 50.6 下载验证
50.1 SD卡简介
50.1.1 SD物理结构SD卡的规范由SD卡协会明确,可以访问https://www.sdcard.org查阅更多标准。SD卡主要有SD、Mini SD和microSD(原名TF卡,2004年正式更名为Micro SD Card,为方便本文用microSD表示)三种类型,MiniSD已经被microSD取代,使用得不多,根据最新的SD卡规格列出的参数如表50.1.1.1所示: 上述表格的“脚位数”,对应于实卡上的“金手指”数,不同类型的卡的触点数量不同,访问的速度也不相同。SD卡允许了不同的接口来访问它的内部存储单元。最常见的是SDIO模式和SPI模式,根据这两种接口模式,我们也列出SD卡引脚对应于这两种不同的电路模式的引脚功能定义,如表50.1.1.2 所示。 表50.1.1.2 SD卡引脚编号(注:S:电源 I:输入 O:推挽输出 PP:推挽) 对比的,我们来看一下microSD引脚,可见只比SD卡少了一个电源引脚VSS2,其它的引脚功能类似。 表50.1.1.3 microSD卡引脚编号(注:S:电源 I:输入 O:推挽输出 PP:推挽) SD卡和micorSD只有引脚和形状大小不同,内部结构类似,操作时序完全相同,可以使用完全相同的代码驱动,下面以9Pin SD卡的内部结构为为例,展示SD卡的存储结构。 图50.1.1.1 SD卡内部物理结构(RCA 寄存器在SPI模式下不可访问) SD卡有自己的寄存器,但它不能直接进行读写操作,需要通过命令来控制,SDIO协议定义了一些命令用于实现某一特定功能,SD卡根据收到的命令要求对内部寄存器进行修改。图50.1.1.4中描述的SD卡的寄存器是我们和SD卡进行数据通讯的主要通道,如下: 关于SD卡的更多信息和硬件设计规范可以参考SD卡协议《Physical Layer SimplifiedSpecification Version 2.00》的相关章节(注:因为STM32的SDIO匹配的是SD协议2.0版本,后续版本也兼容此旧协议版本,故本章仍以2.0版本为介绍对象)。
50.1.2 命令和响应一个完整的SD卡操作过程是:主机(单片机等)发起“命令”,SD卡根据命令的内容决定是否发送响应信息及数据等,如果是数据读/写操作,主机还需要发送停止读/写数据的命令来结束本次操作,这意味着主机发起命令指令后,SD卡可以没有响应、数据等过程,这取决于命令的含义。这一过程如图50.1.2.1所示。 SD卡有多种命令和响应,它们的格式定义及含义在《SD卡协议V2.0》的第三和第四章有详细介绍,发送命令时主机只能通过CMD引脚发送给SD卡,串行逐位发送时先发送最高位(MSB),然后是次高位这样类推……接下来,我们看看SD卡的命令格式,如表 50.1.2.1所示: SD卡的命令固定为48位,由6个字节组成,字节1的最高2位固定为01,低6位为命令号(比如CMD16,为10000B即16进制的0X10,完整的CMD16,第一个字节为01010000,即0X10+0X40)。字节2~5为命令参数,有些命令是没有参数的。字节6的高七位为CRC值,最低位恒定为1。
SD卡的命令总共有12类,分为Class0~Class11,本章,我们仅介绍几个比较重要的命令,如表50.1.2.2所示: 上表中,大部分的命令是初始化的时候用的。表中的R1、R3和R7等是SD卡的应答信号,每个响应也有规定好的格式,如图50.1.2.2所示: 在规定为有响应的命令下,每发送一个命令,SD卡都会给出一个应答,以告知主机该命令的执行情况,或者返回主机需要获取的数据,应答可以是R1~R7,R1的应答,各位描述如表50.1.2.3所示: R2~R7的响应,限于篇幅,我们就不介绍了,但需要注意的是除了R2响应是128位外,其它的响应都是48位,请大家参考SD卡2.0协议。
50.1.3 卡模式SD卡系统(包括主机和SD卡)定义了SD卡的工作模式,在每个操作模式下,SD卡都有几种状态,参考表50.1.3.1,状态之间通过命令控制实现卡状态的切换。 对于我们来说两种有效操作模式:卡识别模式和数据传输模式。在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备,对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡在卡识别状态接收到CMD3 (SEND_RCA)命令后,SD卡就进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。
在卡识别模式下,主机会复位所有处于“卡识别模式”的SD卡,确认其工作电压范围,识别SD卡类型,并且获取SD卡的相对地址(卡相对地址较短,便于寻址)。在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下。卡识别模式下SD卡状态转换如图50.1.3.1。
主机上电后,所有卡处于空闲状态,包括当前处于无效状态的卡。主机也可以发送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之后也进入数据传输模式。
50.1.4数据模式在数据模式下我们可以对SD卡的存储块进行读写访问操作。SD卡上电后默认以一位数据总线访问,可以通过指令设置为宽总线模式,可以同时使有4位总线并行读写数据,这样对于支持宽总线模式的接口(如:SDIO和SPI等)都能加快数据操作速度。 图50.1.4.1 1位数据线传输8bit的数据流格式 SD卡有两种数据模式,一种是常规的8位宽,即一次按一字节传输,另一种是一次按512字节传输,我们只介绍前面一种。当按8-bit连续传输时,每次传输从最低字节开始,每字节从最高位(MSB)开始发送,当使用一条数据线时,只能通过DAT0进行数据传输,那它的数据传输结构如图50.1.4.1 1所示。
当使用4线模式传输8-bit结构的数据时,数据仍按MSB先发送的原则,DAT[3:0]的高位发送高数据位,低位发送低数据位。硬件支持的情况下,使用4线传输可以提升传输速率。 图50.1.4.2 4位数据线传输8bit格式的数据流格式 只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。数据传输模式下,SD卡状态转换过程见图50.1.4.3。 CMD7用来选定和取消指定的卡,卡在待机状态下还不能进行数据通信,因为总线上可能有多个卡都是出于待机状态,必须选择一个RCA地址目标卡使其进入传输状态才可以进行数据通信。同时通过CMD7命令也可以让已经被选择的目标卡返回到待机状态。
数据传输模式下的数据通信都是主机和目标卡之间通过寻址命令点对点进行的。卡处于传输状态下可以通过命令对卡进行数据读写、擦除。CMD12可以中断正在进行的数据通信,让卡返回到传输状态。CMD0和CMD15会中止任何数据编程操作,返回卡识别模式,注意谨慎使用,不当操作可能导致卡数据被损坏。
至此,我们已经介绍了SD卡操作的一些知识,并知道了SD卡操作的命令、响应和数据传输等状态,接下来我们来分析实际的硬件接口如何向SD卡发送我们需要的数据。
50.2 SDIO接口简介前面提到SD卡的驱动方式之一是用SDIO接口通讯,正点原子探索者STM32F407自带SDIO接口,本节,我们将简单介绍STM32F4的SDIO接口,包括:主要功能及框图、时钟、命令与响应和相关寄存器简介等。
50.2.1 SDIO主要功能及框图SDIO于2001年推出,透过SD总线连接多样设备的特性使得SDIO逐渐被用于连接各种嵌入式I/O设备上。由于SD总线简单的连接特性与支持更高的总线速度模式,SDIO也变得越来越普及。嵌入式解决方案让主机能在任何时间存取SDIO装置而SD卡插槽则可让用户使用SD存储卡。
SDIO本来是记忆卡的标准,由于SD卡方便即插即用的特性,现在也可以把SDIO拿来插上一些外围接口使用,如SDIO的WIFI卡、Bluetooth卡、Radio/TVcard等。这些卡使用的SDIO命令略有差异。根据SDIO的类别,SDIO的家族大概是这样: 图50.2.1.1STM32F4的SDIO支持的功能 STM32F4的SDIO控制器支持多媒体卡(MMC卡)、SD存储卡、SDI/O卡和CE-ATA设备等。SDIO的主要功能如下: Ø 与多媒体卡系统规格书版本4.2全兼容。支持三种不同的数据总线模式:1位(默认)、4位和8位。 Ø 与较早的多媒体卡系统规格版本全兼容(向前兼容)。 Ø 与SD存储卡规格版本2.0全兼容。SD卡规范版本2.0,包括SD和高容量SDHC标准卡,故不支持超大容量SDXC/SDUC标准卡,所以STM32F4xx的SDIO可以支持的最高卡容量是32GB。 Ø 与SDI/O卡规格版本2.0全兼容:支持良种不同的数据总线模式:1位(默认)和4位。 Ø 完全支持CE-ATA功能(与CE-ATA数字协议版本1.1全兼容)。8位总线模式下数据传输速率可达48MHz。 Ø 数据和命令输出使能信号,用于控制外部双向驱动器。 Ø SDIO不具备兼容SPI的通信模式。
STM32F4的SDIO控制器包含2个部分:SDIO适配器模块和APB2总线接口,其功能框图如图50.1.1.1所示: 图50.2.1.2 STM32F4的SDIO控制器功能框图 复位后默认情况下SDIO_D0用于数据传输。初始化后主机可以改变数据总线的宽度(通过ACMD6命令设置)。如果一个多媒体卡接到了总线上,则SDIO_D0、SDIO_D[3:0]或SDIO_D[7:0]可以用于数据传输。MMC版本V3.31和之前版本的协议只支持1位数据线,所以只能用SDIO_D0(为了通用性考虑,在程序里面我们只要检测到是MMC卡就设置为1位总线数据)。
如果一个SD或SDI/O卡接到了总线上,可以通过主机配置数据传输使用SDIO_D0或SDIO_D[3:0]。所有的数据线都工作在推挽模式。SDIO_CMD有两种操作模式: ①用于初始化时的开路模式(仅用于MMC版本V3.31或之前版本) ②用于命令传输的推挽模式(SD/SDI/O卡和MMCV4.2在初始化时也使用推挽驱动)
50.2.2 SDIO的时钟从图50.2.1.1我们可以看到SDIO总共有3个时钟,分别是: ① 卡时钟(SDIO_CK):每个时钟周期在命令和数据线上传输1位命令或数据。对于多媒体卡V3.31协议,时钟频率可以在0MHz至20MHz间变化;对于多媒体卡V4.0/4.2协议,时钟频率可以在0MHz至48MHz间变化;对于SD或SDI/O卡,时钟频率可以在0MHz至25MHz间变化。 ② SDIO适配器时钟(SDIOCLK):该时钟用于驱动SDIO适配器,来自OLL48CK,其频率一般为48Mhz,并用于产生SDIO_CK时钟。 ③ APB2总线接口时钟(PCLK2):该时钟用于驱动SDIO的APB2总线接口,其频率为HCLK/2,一般为84Mhz。
前面提到,我们的SD卡时钟(SDIO_CK),根据卡的不同,可能有好几个区间,这就涉及到时钟频率的设置,SDIO_CK与SDIOCLK的关系为: 其中,SDIOCLK为PLL48CK,一般是48Mhz,而CLKDIV则是分配系数,可以通过SDIO的SDIO_CLKCR寄存器进行设置(确保SDIO_CK不超过卡的最大操作频率)。
这里要提醒大家,在SD卡刚刚初始化的时候,其时钟频率(SDIO_CK)是不能超过400Khz的,否则可能无法完成初始化。在初始化以后,就可以设置时钟频率到最大了(但不可超过SD卡的最大操作时钟频率)。
50.2.3 SDIO的命令与响应前机介绍SD卡寄器时已经说过SD卡是需要通过命令控制的,下面我们将介绍一些主要操作命令和响应过程,没介绍完的部分,大家可以对照光盘《SD卡2.0协议.pdf》或《STM32F4xx参考手册_V4(中文版).pdf》第28章进行更深入的学习。
SDIO的命令分为应用相关命令(ACMD)和通用命令(CMD)两部分,应用相关命令(ACMD)的发送,必须先发送通用命令(CMD55),然后才能发送应用相关命令(ACMD)。
SDIO的所有命令和响应都只通过SDIO_CMD引脚传输的,任何命令的长度都是固定为48位,SDIO的命令格式如表50.2.3.1所示: 所有的命令都是由STM32F4发出,其中开始位、传输位、CRC7和结束位由SDIO硬件控制,我们需要设置的就只有命令索引和参数部分。其中命令索引(如CMD0,CMD1之类的)在SDIO_CMD寄存器里面设置,命令参数则由寄存器SDIO_ARG设置。
一般情况下,选中的SD卡在接收到命令之后,都会回复一个应答(注意CMD0是无应答的),这个应答我们称之为响应,响应也是在CMD线上串行传输的。STM32F4的SDIO控制器支持2种响应类型,即:短响应(48位)和长响应(136位),这两种响应类型都带CRC错误检测(注意不带CRC的响应应该忽略CRC错误标志,如CMD1的响应)。短响应的格式如表50.2.3.2所示: 长响应的格式如表50.2.3.3所示: 同样,硬件为我们滤除了开始位、传输位、CRC7以及结束位等信息,对于短响应,命令索引存放在SDIO_RESPCMD寄存器,参数则存放在SDIO_RESP1寄存器里面。对于长响应,则仅留CID/CSD位域,存放在SDIO_RESP1~SDIO_RESP4等4个寄存器。
SD存储卡总共有5类响应(R1、R2、R3、R6、R7),我们这里以R1为例简单介绍一下。R1(普通响应命令)响应属于短响应,其长度为48位,R1响应的格式如表50.2.3.4所示: 在收到R1响应后,我们可以从SDIO_RESPCMD寄存器和SDIO_RESP1寄存器分别读出命令索引和卡状态信息。
主要的响应就介绍到这,最后,我们看看数据在SDIO控制器与SD卡之间的传输。对于SDI/SDIO存储器,数据是以数据块的形式传输的,而对于MMC卡,数据是以数据块或者数据流的形式传输。本节我们只考虑数据块形式的数据传输。SDIO(多)数据块读操作,如图50.2.3.1所示: 从上图,我们可以看出,从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带有CRC校验值(CRC由SDIO硬件自动处理),单个数据块读的时候,在收到1个数据块以后即可以停止了,不需要发送停止命令(CMD12)。但是多块数据读的时候,SD卡将一直发送数据给主机,直到接到主机发送的STOP命令(CMD12)。SDIO(多)数据块写操作,如图50.2.3.2所示: 数据块写操作同数据块读操作基本类似,只是数据块写的时候,多了一个繁忙判断,新的数据块必须在SD卡非繁忙的时候发送。这里的繁忙信号由SD卡拉低SDIO_D0,以表示繁忙,SDIO硬件自动控制,不需要我们软件处理。
SDIO的命令与响应就为大家介绍到这里。
50.2.4 SDIO相关寄存器介绍这部分将结合《STM32F4xx参考手册_V4(中文版).pdf》的内容和大家一起分析使用SDIO时我们主要用的一些寄存器的情况。
l SDIO电源控制寄存器(SDIO_POWER) SDIO电源控制寄存器(SDIO_POWER)寄存器复位值为0,所以SDIO的电源是关闭的,我们要启用SDIO,第一步就是要设置该寄存器最低2个位均为1,让SDIO上电,开启卡时钟。该寄存器定义如图50.2.4.1所示: 图50.2.4.1 SDIO_POWER寄存器位定义 l SDIO时钟控制寄存器(SDIO_CLKCR) SDIO时钟控制寄存器(SDIO_CLKCR),该寄存器主要用于设置SDIO_CK的分配系数,开关等,并可以设置SDIO的数据位宽,该寄存器的定义如图50.2.4.2所示: 图50.2.4.2 SDIO_CLKCR寄存器位定义 上图仅列出了部分我们要用到的位设置,WIDBUS用于设置SDIO总线位宽,正常使用的时候,设置为1,即4位宽度。BYPASS用于设置分频器是否旁路,我们一般要使用分频器,所以这里设置为0,禁止旁路。CLKEN则用于设置是否使能SDIO_CK,我们设置为1。最后,CLKDIV则用于控制SDIO_CK的分频,设置为1,即可得到24Mhz的SDIO_CK频率。
l SDIO参数寄存器(SDIO_ARG) SDIO参数寄存器(SDIO_ARG),该寄存器包含一个32位命令参数,该参数作为命令消息的一部分发送到卡,如果命令包含参数,则在将命令写入到命令寄存器之前,必须将参数加载到此寄存器中。
l SDIO命令响应寄存器(SDIO_RESPCMD) SDIO命令响应寄存器(SDIO_RESPCMD),该寄存器为32位,但只有低6位有效,比较简单,用于存储最后收到的命令响应中的命令索引。如果传输的命令响应不包含命令索引(长响应或OCR响应),则该寄存器的内容不可预知。
l SDIO响应寄存器组(SDIO_RESP1~SDIO_RESP4) SDIO响应寄存器组(SDIO_RESP1~SDIO_RESP4),该寄存器组总共由4个32位寄存器组成,用于存放接收到的卡响应部分信息。如果收到短响应,则数据存放在SDIO_RESP1寄存器里面,其他三个寄存器没有用到。而如果收到长响应,则依次存放在SDIO_RESP1~SDIO_RESP4里面,如表50.2.4.3所示: 表50.2.4.3 响应类型和SDIO_RESPx寄存器 l SDIO命令寄存器(SDIO_CMD) SDIO命令寄存器(SDIO_CMD)各位定义如图50.2.4.3所示: 图中只列出了部分位的描述,其中低6位为命令索引,也就是我们要发送的命令索引号(比如发送CMD1,其值为1,索引就设置为1)。位[7:6],用于设置等待响应位,用于指示CPSM是否需要等待,以及等待类型等。这里的CPSM,即命令通道状态机,我们就不详细介绍了,请参阅《STM32F4xx参考手册_V4(中文版).pdf》第807页,有详细介绍。命令通道状态机我们一般都是开启的,所以位10要设置为1。
l SDIO数据定时器寄存器(SDIO_DTIMER) SDIO数据定时器寄存器(SDIO_DTIMER)用于存储以卡总线时钟(SDIO_CK)为周期的数据超时时间,一个计数器将从SDIO_DTIMER寄存器加载数值,并在数据通道状态机(DPSM)进入Wait_R或繁忙状态时进行递减计数,当DPSM处在这些状态时,如果计数器减为0,则设置超时标志。这里的DPSM,即数据通道状态机,类似CPSM,详细请参考《STM32F4xx参考手册_V4(中文版).pdf》第809页。注意:在写入数据控制寄存器,进行数据传输之前,必须先写入该寄存器(SDIO_DTIMER)和数据长度寄存器(SDIO_DLEN)!
l SDIO数据长度寄存器(SDIO_DLEN) SDIO数据长度寄存器(SDIO_DLEN)低25位有效,用于设置需要传输的数据字节长度。对于块数据传输,该寄存器的数值,必须是数据块长度(通过SDIO_DCTRL设置)的倍数。
l SDIO数据控制寄存器(SDIO_DCTRL) SDIO数据控制寄存器(SDIO_DCTRL)各位定义如图50.2.4.4所示: 图50.2.4.4 SDIO_DCTRL寄存器位定义 该寄存器,用于控制数据通道状态机(DPSM),包括数据传输使能、传输方向、传输模式、DMA使能、数据块长度等信息,都是通过该寄存器设置。我们需要根据自己的实际情况,来配置该寄存器,才可正常实现数据收发。
接下来,我们介绍几个位定义十分类似的寄存器,他们是:状态寄存器(SDIO_STA)、清除中断寄存器(SDIO_ICR)和中断屏蔽寄存器(SDIO_MASK),这三个寄存器每个位的定义都相同,只是功能各有不同。所以可以一起介绍,以状态寄存器(SDIO_STA)为例,该寄存器各位定义如图50.2.4.5所示: 状态寄存器可以用来查询SDIO控制器的当前状态,以便处理各种事务。比如SDIO_STA的位2表示命令响应超时,说明SDIO的命令响应出了问题。我们通过设置SDIO_ICR的位2则可以清除这个超时标志,而设置SDIO_MASK的位2,则可以开启命令响应超时中断,设置为0关闭。其他位我们就不一一介绍了,请大家自行学习。
最后,我们向大家介绍SDIO的数据FIFO寄存器(SDIO_FIFO),数据FIFO寄存器包括接收和发送FIFO,他们由一组连续的32个地址上的32个寄存器组成,CPU可以使用FIFO读写多个操作数。例如我们要从SD卡读数据,就必须读SDIO_FIFO寄存器,要写数据到SD卡,则要写SDIO_FIFO寄存器。SDIO将这32个地址分为16个一组,发送接收各占一半。而我们每次读写的时候,最多就是读取发送FIFO或写入接收FIFO的一半大小的数据,也就是8个字(32个字节),这里特别提醒,我们操作SDIO_FIFO(不论读出还是写入)必须是以4字节对齐的内存进行操作,否则将导致出错! 至此,SDIO的相关寄存器介绍,我们就介绍完了。还有几个不常用的寄存器,我们没有介绍到,请大家参考《STM32F4xx参考手册_V4(中文版).pdf》第28章相关章节。 |