目录
学习目标
我们接下来要讲解的是有关SD卡的一些知识,因为单片机内部的存储容量比较小,一些数据什么的也无法保存,所以这个时候就需要我们使用SD卡来解决这个问题。目前SD卡还在路上,就无法做实验,等SD卡到了再把实验补上。
内容
SDIO,全称: Secure Digital Input and Output ,即安全数字输入输出接口。它是在SD卡接口的基础上发展而来,它可以兼容之前的SD卡,并可以连接SDIO接口设备,比如:蓝牙、WIFI、照相机等。
SDIO和SD卡规范间的一个重要区别是增加了低速标准。低速卡的目标应用是以最小的硬件开支支持低速I/O能力。低速卡支持类似调制解调器、条码扫描仪和GPS接收器等应用。 STM32的SDIO控制器支持多媒体卡(MMC卡)、SD存储卡、SD I/O卡和CE-ATA设备。
介绍
- 与多媒体卡系统规格书版本4.2全兼容。支持三种不同的数据总线模式:1位(默认)、4位和8位。
- 与较早的多媒体卡系统规格版本全兼容(向前兼容)。
- 与SD存储卡规格版本2.0全兼容。
- 与SD I/O卡规格版本2.0全兼容:支持两种不同的数据总线模式:1位(默认)和4位。
- 完全支持CE-ATA功能(与CE-ATA数字协议版本1.1全兼容)。 8位总线模式下数据传输速率可达48MHz。
- 数据和命令输出使能信号,用于控制外部双向驱动器。
复位后默认情况下 SDIO_D0 用于数据传输。初始化后主机可以改变数据总线的宽度(通过 ACMD6 命令设置)。
如果一个多媒体卡接到了总线上,则 SDIO_D0、SDIO_D[3:0]或 SDIO_D[7:0]可以用于数据传输。MMC 版本 V3.31 和之前版本的协议只支持 1 位数据线,所以只能用 SDIO_D0(为了通用性考虑,在程序里面我们只要检测到是 MMC 卡就设置为 1 位总线数据)。
如果一个 SD 或 SD I/O 卡接到了总线上,可以通过主机配置数据传输使用 SDIO_D0 或 SDIO_D[3:0]。所有的数据线都工作在推挽模式。
卡时钟(SDIO_CK)
每个时钟周期在命令和数据线上传输 1 位命令或数据。对于多媒体卡 V3.31 协议,时钟频率可以在 0MHz 至 20MHz 间变化;对于多媒体卡 V4.0/4.2 协议,时钟频率可以在 0MHz 至 48MHz 间变化;对于 SD 或 SD I/O 卡,时钟频率可以在 0MHz 至 25MHz间变化。
SDIO_CK=SDIOCLK/(2+CLKDIV),CLKDIV 是分配系数
SDIO适配器时钟(SDIOCLK)
该时钟用于驱动 SDIO 适配器,来自 PLL48CK,一般为 48Mhz,并用于产生 SDIO_CK 时钟。
APB2总线接口时钟(PCLK2)
该时钟用于驱动SDIO的APB2总线接口,其频率为HCLK/2, 一般为 84Mhz。
注:其时钟频率(SDIO_CK)是不能超过 400Khz 的,否则可能无法完成初始化。在初始化以后,就可以设置时钟频率到最大了(但不可超过 SD卡的最大操作时钟频率)。
命令
SDIO的命令分为:应用相关命令(ACMD)和通用命令(CMD)两部分。发送ACMD时,需先发送CMD55。
SDIO所有的命令和响应都是在SDIO_CMD引脚上面传输的,命令长度固定为48位,SDIO命令格式如下表所示:
除了命令索引和参数需要我们设置,其他都是由SDIO硬件自动控制。命令索引(如CMD0,CMD1之类)由SDIO_CMD寄存器设置,命令参数则由SDIO_ARG寄存器设置。
响应
一般情况下,选中的 SD 卡在接收到命令之后,都会回复一个应答(注意 CMD0 是无应答的),这个应答我们称之为响应,响应也是在 CMD 线上串行传输的。
STM32F4 的 SDIO 控制器支持 2 种响应类型,即:短响应(48 位)和长响应(136 位),这两种响应类型都带 CRC 错误检测(注意不带 CRC 的响应应该忽略 CRC 错误标志,如 CMD1 的响应)。
短响应
长响应
不论是短响应还是长响应,硬件都会自动滤除了起始位、传输位、CRC7以及结束位等信息,对于短响应,命令索引存放在SDIO_RESPCMD寄存器,参数则存放在SDIO_RESP1寄存器里面。对于长响应,则仅留CID/CSD位域,存放在SDIO_RESP1~SDIO_RESP4等4个寄存器。
SD卡总共有6类响应(R1、R1b、R2、R3、R6、R7),我们这里以R1为例简单介绍一下。R1(普通响应命令)响应属于短响应,其长度为48位,如下表所示:
在收到 R1 响应后,我们可以从 SDIO_RESPCMD 寄存器和 SDIO_RESP1 寄存器分别读出命令索引和卡状态信息。
传输
读
这个传输的过程,可以类比成我们上班的过程,首先老板叫你来上班(发送命令),然后来上班(响应),之后完成今天的工作(传输数据块),就可以下班了(停止)。但是如果老板叫你加班(连续传输),你就得一直干,干到什么时候呢?就是老板让你下班的时候((Stop)命令 ),然后你才可以下班(响应)。
从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带有 CRC 校验值(CRC 由 SDIO 硬件自动处理),单个数据块读的时候,在收到 1 个数据块以后即可以停止了,不需要发送停止命令(CMD12)。但是多块数据读的时候,SD 卡将一直发送数据给主机,直到接到主机发送的 STOP 命令(CMD12)。
写
这个写呢,其实就是老板给你下任务,每次给你分配任务的时候会礼貌的问一下你忙不忙(繁忙询问),当你忙完了才会给你下任务。
数据块写操作同数据块读操作基本类似,只是数据块写的时候,多了一个繁忙判断,新的数据块必须在 SD 卡非繁忙的时候发送。这里的繁忙信号由 SD 卡拉低 SDIO_D0,以表示繁忙, SDIO 硬件自动控制,不需要我们软件处理。
寄存器
寄存器部分和之前一样,就不详细介绍了,手册都有。但是寄存器部分是非常重要的,千万不能忽视了。
SD卡
SD卡(Secure Digital MemoryCard)即:安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
SD卡按容量分类,可以分为3类: SD卡、SDHC卡、SDXC卡,如下表所示:
SD卡由9个引脚与外部通信,支持SPI和SDIO 两种模式,不同模式下,SD卡引脚功能描述如下表所示:
寄存器
初始化
首先需要发送CMD0指令,进行软复位。之后是CMD8,带一个主机供电电压的参数。如果是2.0以后的卡而且电压在范围内,他就会响应,然后我们就可以知道这个SD卡的供电范围。之后我们发送ACMD41指令,然后会带一个参数来判断是不是高容量卡,如果是的话,CCS返回1,否则返回0。但是如果是1.0的卡或者电压范围不对,他就会走左边那条分支,我们也发送ACMD41指令,有响应就说明是1.0的卡,否者就是电压范围不对,如果压根没反应,就有可能是MMC卡了,之后发送一个CMD1的指令就行。
不管什么卡(这里我们将卡分为 4 类:SD2.0 高容量卡(SDHC,最大32G),SD2.0 标准容量卡(SDSC,最大 2G),SD1.x 卡和 MMC 卡),首先我们要执行的是卡上电(需要设置 SDIO_POWER[1:0]=11),上电后发送 CMD0,对卡进行软复位,之后发送 CMD8 命令,用于区分 SD 卡 2.0,只有 2.0 及以后的卡才支持 CMD8 命令,MMC 卡和 V1.x 的卡,是不支持该命令的。
我们需要在发送 CMD8 的时候,通过其带的参数我们可以设置 VHS 位,以告诉 SD卡,主机的供电情况。
这里我们使用参数 0X1AA,即告诉 SD 卡,主机供电为 2.7~3.6V 之间,如果 SD 卡支持CMD8,且支持该电压范围,则会通过 CMD8 的响应(R7)将参数部分原本返回给主机,如果支持 CMD8,或者不支持这个电压范围,则不响应。
在发送 CMD8 后,发送 ACMD41(注意发送 ACMD41 之前要先发送 CMD55),来进一步确认卡的操作电压范围,并通过 HCS 位来告诉 SD 卡,主机是不是支持高容量卡(SDHC)。ACMD41 的命令格式如下所示:
ACMD41 得到的响应(R3)包含 SD 卡 OCR 寄存器内容,OCR 寄存器内容定义如表 所示:
CMD2,用于获得 CID 寄存器的数据,CID 寄存器数据各位定义如下所示:
SD 卡在收到 CMD2 后,将返回 R2 长响应(136 位),其中包含 128 位有效数据(CID 寄存器内容),存放在 SDIO_RESP1~4 等 4 个寄存器里面。通过读取这四个寄存器,就可以获得SD 卡的 CID 信息。
CMD3,用于设置卡相对地址(RCA,必须为非 0),对于 SD 卡(非 MMC 卡),在收到 CMD3 后,将返回一个新的 RCA 给主机,方便主机寻址。RCA 的存在允许一个 SDIO 接口挂多个 SD 卡,通过 RCA 来区分主机要操作的是哪个卡。而对于 MMC 卡,则不是由 SD 卡自动返回 RCA,而是主机主动设置 MMC 卡的 RCA,即通过 CMD3 带参数(高 16 位用于 RCA 设置),实现 RCA 设置。同样 MMC 卡也支持一个 SDIO 接口挂多个 MMC 卡,不同于 SD 卡的是所有的 RCA 都是由主机主动设置的,而 SD 卡的 RCA 则是 SD 卡发给主机的。
在获得卡 RCA 之后,我们便可以发送 CMD9(带 RCA 参数),获得 SD 卡的 CSD 寄存器内容,从 CSD 寄存器,我们可以得到 SD 卡的容量和扇区大小等十分重要的信息。
读数据
单块
多块
接下来,我们对各个CMD指令进行一个简单的介绍,我就放一下到这,等下好找。
CMD16
CMD17
CMD18
CMD12
写数据
单块
多块
CMD13
CMD24
CMD23
CMD55
CMD25
硬件连接
硬件连接就是这样子诺,没什么可讲的。
代码
就放一下初始化代码。
//初始化SD卡
//返回值:错误代码;(0,无错误)
SD_Error SD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SD_Error errorstatus=SD_OK;
u8 clkdiv=0;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_DMA2, ENABLE);//使能GPIOC,GPIOD DMA2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);//SDIO时钟使能
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, ENABLE);//SDIO复位
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12; //PC8,9,10,11,12复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100M
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);// PC8,9,10,11,12复用功能输出
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure);//PD2复用功能输出
//引脚复用映射设置
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_SDIO); //PC8,AF12
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource12,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource2,GPIO_AF_SDIO);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, DISABLE);//SDIO结束复位
//SDIO外设寄存器设置为默认值
SDIO_Register_Deinit();
NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
errorstatus=SD_PowerON(); //SD卡上电
if(errorstatus==SD_OK)errorstatus=SD_InitializeCards(); //初始化SD卡
if(errorstatus==SD_OK)errorstatus=SD_GetCardInfo(&SDCardInfo); //获取卡信息
if(errorstatus==SD_OK)errorstatus=SD_SelectDeselect((u32)(SDCardInfo.RCA<<16));//选中SD卡
if(errorstatus==SD_OK)errorstatus=SD_EnableWideBusOperation(SDIO_BusWide_4b); //4位宽度,如果是MMC卡,则不能用4位模式
if((errorstatus==SD_OK)||(SDIO_MULTIMEDIA_CARD==CardType))
{
if(SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V1_1||SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V2_0)
{
clkdiv=SDIO_TRANSFER_CLK_DIV+2; //V1.1/V2.0卡,设置最高48/4=12Mhz
}else clkdiv=SDIO_TRANSFER_CLK_DIV; //SDHC等其他卡,设置最高48/2=24Mhz
SDIO_Clock_Set(clkdiv); //设置时钟频率,SDIO时钟计算公式:SDIO_CK时钟=SDIOCLK/[clkdiv+2];其中,SDIOCLK固定为48Mhz
//errorstatus=SD_SetDeviceMode(SD_DMA_MODE); //设置为DMA模式
errorstatus=SD_SetDeviceMode(SD_POLLING_MODE);//设置为查询模式
}
return errorstatus;
}
总结
虽然SD卡内容比较多,但是却特别重要。所以我们需要好好去理解这些内容。实验就等SD卡到了再做。