概述
20.1 概述
SD卡是嵌入式系统中最常见的存储器,不仅容量可以做的很大,并且接口通用,支持SPI/SDIO驱动,尺寸可供选择,能满足不同应用的要求。STM32F1系列自带了标准的4位SDIO接口,最高通信速度可达24MHz,最高每秒能传输12M字节的数据。
20.1.1 SDIO框图
STM32F1的SDIO控制器包括2部分,SDIO适配器模块和AHB总线接口,功能框图如下图所示。
其中SDIO适配器模块主要用于实现所有MMC/SD卡的相关功能,如时钟的产生,命令和数据的传输,AHB总线接口则用于操作SDIO适配器模块中的寄存器并产生中断和DMA请求信号。复位后默认情况下SDIO_D0用于数据传输。初始化后主机可以改变数据总线的宽度。
如果一个多媒体卡接到了总线上,则SDIO_D0、SDIO_D[3:0]或SDIO_D[7:0]可以用于数据传输。MMC版本V3.31和之前版本的协议只支持1位数据线,所以只能用SDIO_D0(为了通用性考虑,在程序里面我们只要检测到是MMC卡就设置为1位总线数据)。
如果一个SD卡接到了总线上,可以通过主机配置数据传输使用SDIO_D0或SDIO_D[3:0]。所有的数据线都工作在推挽模式。
SDIO_CMD有两种操作模式:
(1)用于初始化时的开路模式(仅用于MMC版本V3.31或之前版本)
(2)用于命令传输的推挽模式(SD卡和MMCV4.2在初始化时也使用推挽驱动)
20.1.2 SDIO时钟
从SDIO框图我们可以看到SDIO总共有3个时钟,分别是:
(1)卡时钟SDIO_CK:每个时钟周期在命令和数据线上传输1位命令或数据。对于多媒体卡V3.31协议,时钟频率可以在0MHz至20MHz间变化;对于多媒体卡V4.0/4.2协议,时钟频率可以在0MHz至48MHz间变化;对于SD卡,时钟频率可以在0MHz至25MHz间变化。
(2)SDIO适配器时钟SDIOCLK:该时钟用于驱动SDIO适配器,其频率等于AHB总线频率HCLK,并用于产生SDIO_CK时钟
(3)AHB总线接口时钟HCLK/2:该时钟用于驱动SDIO的AHB总线接口,其频率为HCLK/2。
我们的SD卡时钟SDIO_CK,根据卡的不同,可能有好几个区间,这就涉及到时钟频率的设置,SDIO_CK与SDIOCLK的关系为:
SDIO_CK=SDIOCLK/(2+CLKDIV)
其中,SDIOCLK为HCLK,一般是72MHz,而CLKDIV则是分配系数,可以通过SDIO的SDIO_CLKCR寄存器进行设置,确保SDIO_CK不超过卡的最大操作频率。
注:在SD卡刚刚初始化的时候,其时钟频率SDIO_CK不能超过400KHz,否则可能无法完成初始化。在初始化以后,就可以设置时钟频率到最大了,但不可超过SD卡的最大操作时钟频率。
20.1.3 SDIO的命令与响应
SDIO的命令分为应用相关命令ACMD和通用命令CMD两部分,应用相关命令ACMD的发送,必须先发送通用命令CMD55,然后才能发送应用相关命令ACMD。SDIO的所有命令和响应都是通过SDIO_CMD引脚传输的,任何命令的长度都是固定为48位,SDIO的命令格式如下表所示。
Bit位 | 宽度 | 值 | 说明 |
47 | 1 | 0 | 起始位 |
46 | 1 | 1 | 传输位 |
45:40 | 6 | - | 命令索引 |
39:8 | 32 | - | 参数 |
7:1 | 7 | - | CRC7 |
0 | 1 | 1 | 结束位 |
所有的命令都是由STM32F1发出,其中开始位、传输位、CRC7和结束位由SDIO硬件控制,我们需要设置的就只有命令索引和参数部分。其中命令索引在SDIO_CMD寄存器里面设置,命令参数则由寄存器SDIO_ARG设置。一般情况下,选中的SD卡在接收到命令之后,都会回复一个应答(但是CMD0无应答),这个应答我们称之为响应,响应也是在CMD线上串行传输的。STM32F1的SDIO控制器支持2种响应类型,48位的短响应和136位的长响应,这两种响应类型都带CRC错误检测,不带CRC的响应应该忽略CRC错误标志,如CMD1的响应。
短响应的格式如下表所示。
Bit位 | 宽度 | 值 | 说明 |
47 | 1 | 0 | 起始位 |
46 | 1 | 0 | 传输位 |
45:40 | 6 | - | 命令索引 |
39:8 | 32 | - | 参数 |
7:1 | 7 | - | CRC7或者1111111 |
0 | 1 | 1 | 结束位 |
长响应的格式如下表所示。
Bit位 | 宽度 | 值 | 说明 |
135 | 1 | 0 | 起始位 |
134 | 1 | 0 | 传输位 |
133:128 | 6 | 111111 | 保留 |
127:1 | 127 | - | CID或CSD(包括内部CRC7) |
0 | 1 | 1 | 结束位 |
硬件为我们滤除了开始位、传输位、CRC7以及结束位等信息,对于短响应,命令索引存放在SDIO_RESPCMD寄存器,参数则存放在SDIO_RESP1寄存器里面。对于长响应,则仅留CID/CSD位域,存放在SDIO_RESP1到SDIO_RESP4等4个寄存器。SD卡总共有5类响应(R1、R2、R3、R6、R7),这里以R1为例简单介绍一下。R1(普通响应命令)响应属于短响应,其长度为48位,R1响应的格式如下表所示。
Bit位 | 宽度 | 值 | 说明 |
47 | 1 | 0 | 起始位 |
46 | 1 | 1 | 传输位 |
45:40 | 6 | X | 命令索引 |
39:8 | 32 | X | 参数 |
7:1 | 7 | X | CRC7 |
0 | 1 | 1 | 结束位 |
在收到R1响应后,我们可以从SDIO_RESPCMD寄存器和SDIO_RESP1寄存器分别读出命令索引和卡状态信息。
20.1.4 数据块读操作
对于SD卡,数据是以数据块的形式传输的,我们常用的卡就是SD卡,所以不考虑MMC形式的读写操作,因为MMC卡数据以数据块或者数据流的形式传输。
从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带有CRC校验值,CRC由SDIO硬件自动处理,单个数据块读的时候,在收到1个数据块以后即可以停止了,不需要发送停止命令CMD12。但是多块数据读的时候,SD卡将一直发送数据给主机,直到接到主机发送的STOP命令CMD12。
SDIO多数据块的读操作如下图所示。
20.1.5 数据块写操作
数据块写操作同数据块读操作基本类似,只是数据块写的时候,多了一个忙判断,新的数据块必须在SD卡非忙的时候发送。这里的忙信号由SD卡拉低SDIO_D0,以表示忙,SDIO硬件自动控制,不需要我们软件处理。
20.2 SDIO相关寄存器
20.2.1 SDIO电源控制寄存器:SDIO_POWER
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
- | CTRL |
Bit 1~Bit 0:电源控制位
00:电源关闭,卡的时钟停止
01:保留
10:保留的上电状态
11:上电状态,卡的时钟开启
20.2.2 SDIO时钟控制寄存器:SDIO_CLKCR
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
- | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
- | HWFC_EN | NEGEDGE | WIDBUS | BYPASS | PWRSAV | CLKEN | CLKDIV |
Bit 14:硬件流控制使能
0:关闭硬件流控制
1:使能硬件流控制
Bit 13:SDIO_CK相位选择位
0:在主时钟SDIOCLK的上升沿产生SDIO_CK
1:在主时钟SDIOCLK的下降沿产生SDIO_CK
Bit 12~Bit 11:宽总线模式使能位
00:默认总线模式,使用SDIO_D0
01:4位总线模式,使用SDIO_D[3:0]
10:8位总线模式,使用SDIO_D[7:0]
Bit 10:旁路时钟分频器
0:关闭旁路:驱动SDIO_CK输出信号之前,依据CLKDIV数值对SDIOCLK分频
1:使能旁路:SDIOCLK直接驱动SDIO_CK输出信号
Bit 9:省电配置位(为了省电,当总线为空闲时,设置PWRSAV位可以关闭SDIO_CK时钟输出)
0:始终输出SDIO_CK
1:仅在有总线活动时才输出SDIO_CK
Bit 8:时钟使能位
0:SDIO_CK关闭
1:SDIO_CK使能
Bit 7~Bit 0:时钟分频系数
这个域定义了输入时钟(SDIOCLK)与输出时钟(SDIO_CK)间的分频系数:SDIO_CK频率=SDIOCLK/[CLKDIV+2]
20.2.3 SDIO参数寄存器:SDIO_ARG
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
CMDARG[31:16] | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
CMDARG[16:0] |
Bit 31~Bit 0:命令参数
属于发送到卡中命令的一部分,如果一个命令包含一个参数,必须在写命令到命令寄存器之前加载这个寄存器
20.2.4 SDIO命令响应寄存器:SDIO_RESPCMD
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
- | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
- | RESPCMD |
Bit 5~Bit 0:响应的命令索引
只读位,包含最后收到的命令响应中的命令索引
20.2.5 SDIO相应寄存器组:SDIO_RESP1~SDIO_RESP4
寄存器 | 短响应 | 长响应 |
SDIO_RESP1 | 卡状态[31:0] | 卡状态[127:96] |
SDIO_RESP2 | 未使用 | 卡状态[95:64] |
SDIO_RESP3 | 未使用 | 卡状态[31:0] |
SDIO_RESP4 | 未使用 | 卡状态[31:0] |
注:总是先收到卡状态的最高位,SDIO_RESP3寄存器的最低位始终为0。
20.2.6 SDIO命令寄存器:SDIO_CMD
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
- | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
- | CE_ATACMD | nIEN | ENCMDcomp1 | SDIOSuspend | CPSMEN | WAITPEND | WAITINT | WAITRESP | CMDINDEX |
Bit 14:CE-ATA命令
如果设置该位,CPSM转至CMD61
Bit 13:不使能中断
如果未设置该位,则使能CE-ATA设备的中断
Bit 12:使能CMD完成
如果设置该位,则使能命令完成信号
Bit 11:SDIO暂停命令
如果设置该位,则将要发送的命令是一个暂停命令(只能用于SDIO卡)
Bit 10:命令通道状态机使能位
如果设置该位,则使能CPSM
Bit 9:CPSM等待数据传输结束(CmdPend内部信号)
如果设置该位,则CPSM在开始发送一个命令之前等待数据传输结束
Bit 8:CPSM等待中断请求
如果设置该位,则CPSM关闭命令超时控制并等待中断请求
Bit 7~Bit 6:等待响应位
00:无响应,期待CMDSENT标志
01:短响应,期待CMDREND或CCRCFAIL标志
10:无响应,期待CMDSENT标志
11:长响应,期待CMDREND或CCRCFAIL标志
Bit 5~Bit 0:命令索引,作为命令的一部分发送到卡中
20.2.7 SDIO数据定时器:SDIO_DTIMER
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
DATATIME[31:16] | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
DATATIME[15:0] |
Bit 31~Bit 0:数据超时时间,以卡总线时钟周期为单位的数据超时时间
20.2.8 SDIO数据长度寄存器:SDIO_DLEN
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
- | DATALENGTH[24:16] | ||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
DATALENGTH[15:0] |
Bit 24~Bit 0:数据长度,要传输的数据字节数目
20.2.9 SDIO数据控制寄存器:SDIO_DCTRL
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
- | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
- | SDIOEN | RWMOD | RWSTOP | RWSTART | DBLOCKSIZE | DMAEN | DTMODE | DTDIR | DTEN |
Bit 11:SDIO使能功能
如果设置了该位,则DPSM执行SDIO卡特定的操作
Bit 10:读等待模式
0:停止SDIO_CK控制读等待
1:使用SDIO_D2控制读等待
Bit 9:读等待停止
0:如果设置了RWSTART,执行读等待
1:如果设置了RWSTART,停止读等待
Bit 8:读等待开始
设置该位开始读等待操作
Bit 7~Bit 4:数据块长度,当选择了块数据传输模式,该域定义数据块长度
0000:块长度=1字节
0001:块长度=2字节
0010:块长度=4字节
0011:块长度=8字节
0100:块长度=16字节
0101:块长度=32字节
0110:块长度=64字节
0111:块长度=128字节
1000:块长度=256字节
1001:块长度=512字节
1010:块长度=1024字节
1011:块长度=2048字节
1100:块长度=4096字节
1101:块长度=8192字节
1110:块长度=16384字节
1111:保留
Bit 3:DMA使能位
0:关闭DMA
1:使能DMA
Bit 2:数据传输模式
0:块数据传输
1:流数据传输
Bit 1:数据传输方向
0:控制器至卡
1:卡至控制器
Bit 0:数据传输使能位
如果设置该位为1,则开始数据传输。根据DTSIR方向位,DPSM进入Wait_S或Wait_R状态,如果在传输的一开始就设置了RWSTART位,则DPSM进入读等待状态。不需要在数据传输结束后清除使能位,但必须更改SDIO_DCTRL以允许新的数据传输。
20.2.10 SDIO状态寄存器:SDIO_STA
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
- | CEATAEND | SDIOIT | RXDAVL | TXDAVL | RXFIFOE | TXFIFOE | RXFIFOF | TXFIFOF | |||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
RXFIFOHF | TXFIFOHF | RXACT | TXACT | CMDACT | DBCKEND | STBITERR | DATAEND | CMDSENT | CMDREND | RXOVERR | TXUNDERR | DTMEOUT | CTIMEOUT | DCRCFAIL | CCRCFAIL |
Bit 23:在CMD61接收到CE-ATA命令完成信号
Bit 22:收到SDIO中断
Bit 21:在接收FIFO中的数据可用
Bit 20:在发送FIFO中的数据可用
Bit 19:接收FIFO空
Bit 18:发送FIFO空
若使用了硬件流控制,当FIFO包含2个字时,TXFIFOE信号变为有效。
Bit 17:接收FIFO满
若使用了硬件流控制,当FIFO还差2个字满时,RXFIFOF信号变为有效。
Bit 16:发送FIFO满
Bit 15:接收FIFO半满,FIFO中至少还有8个字
Bit 14:发送FIFO半空,FIFO中至少还可以写入8个字。
Bit 13:正在接收数据
Bit 12:正在发送数据
Bit 11:正在传输命令
Bit 10:已发送/接收数据块(CRC检测成功)
Bit 9:在宽总线模式,没有在所有数据信号上检测到起始位
Bit 8:数据结束(数据计数器,SDIO_DCOUNT=0)
Bit 7:命令已发送(不需要响应)
Bit 6:已接收到响应(CRC检测成功)
Bit 5:接收FIFO上溢错误
Bit 4:发送FIFO下溢错误
Bit 3:数据超时
Bit 2:命令响应超时
命令超时时间是一个固定的值,为64个SDIO_CK时钟周期。
Bit 1:已发送/接收数据块(CRC检测失败)
Bit 0:已收到命令响应(CRC检测失败)
注:状态寄存器可以用来查询SDIO控制器的当前状态。比如SDIO_STA的位2表示命令响应超时,说明SDIO的命令响应出了问题。我们通过设置SDIO_ICR的位2则可以清除这个超时标志,而设置SDIO_MASK的位2,则可以开启命令响应超时中断,设置为0关闭。
20.2.11 SDIO数据FIFO寄存器:SDIO_FIFO
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
FIFODATA[31:16] | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
FIFODATA[15:0] |
Bit 31~Bit 0:接收或发送FIFO数据
FIFO数据占据32个32位的字,地址为:SDIO基址+0x80至SDIO基址+0xFC
数据FIFO寄存器包括接收和发送FIFO,他们由一组连续的32个地址上的32个寄存器组成,CPU可以使用FIFO读写多个操作数。例如我们要从SD卡读数据,就必须读SDIO_FIFO寄存器,要写数据到SD卡,则要写SDIO_FIFO寄存器。SDIO将这32个地址分为16个一组,发送接收各占一半。而我们每次读写的时候,最多就是读取发送FIFO或写入接收FIFO的一半大小的数据,也就是8个字(32个字节),在操作SDIO_FIFO(不论读出还是写入)必须是以4字节对齐的内存进行操作,否则将导致出错。
20.3 SD卡初始化
20.3.1 卡上电流程
20.3.2 卡初始化流程
20.4 实验例程
功能:利用STM32F1的SDIO模块读取SD的容量并通过TFTLCD显示。
(1)创建sdio_sdcard.h文件,并输入以下代码。
#ifndef _SDIO_SDCARD_H_#define _SDIO_SDCARD_H_#include "sys.h"/********************************************************************************************************* SD 卡 数 据 定 义*********************************************************************************************************///用户配置区#define SDIO_INIT_CLK_DIV 0x76 //SDIO初始化频率,最大400Kh#define SDIO_TRANSFER_CLK_DIV 0x00 //SDIO传输频率,该值太小可能会导致读写文件出错//SDIO工作模式定义,通过SD_SetDeviceMode函数设置#define SD_POLLING_MODE 0 //查询模式,该模式下,如果读写有问题,建议增大SDIO_TRANSFER_CLK_DIV的设置#define SD_DMA_MODE 1 //DMA模式,该模式下,如果读写有问题,建议增大SDIO_TRANSFER_CLK_DIV的设置//SDIO 各种错误枚举定义typedef enum{ //特殊错误定义 SD_CMD_CRC_FAIL = 1, //收到命令响应(CRC校验失败) SD_DATA_CRC_FAIL = 2, //发送/接收数据块(CRC校验失败) SD_CMD_RSP_TIMEOUT = 3, //命令响应超时 SD_DATA_TIMEOUT = 4, //数据超时 SD_TX_UNDERRUN = 5, //传输FIFO运行 SD_RX_OVERRUN = 6, //接收FIFO运行 SD_START_BIT_ERR = 7, //在宽总线模式下的所有数据信号上未检测到起始位 SD_CMD_OUT_OF_RANGE = 8, //CMD的参数超出范围 SD_ADDR_MISALIGNED = 9, //地址没有对齐 SD_BLOCK_LEN_ERR = 10, //卡不允许传输的块长度,或者传输的字节数与块长度不匹配 SD_ERASE_SEQ_ERR = 11, //擦除命令序列中发生错误 SD_BAD_ERASE_PARAM = 12, //删除组的无效选择 SD_WRITE_PROT_VIOLATION = 13, //试图对写保护块进行编程 SD_LOCK_UNLOCK_FAILED = 14, //在unlock命令中检测到序列或密码错误,或者试图访问锁定的卡 SD_COM_CRC_FAILED = 15, //前一个命令的CRC校验失败 SD_ILLEGAL_CMD = 16, //命令对卡状态无效 SD_CARD_ECC_FAILED = 17, //已应用卡内部ECC,但未能更正数据 SD_CC_ERROR = 18, //内部卡控制错误 SD_GENERAL_UNKNOWN_ERROR = 19, //通用或未知错误 SD_STREAM_READ_UNDERRUN = 20, //卡无法在流读取操作中维持数据传输 SD_STREAM_WRITE_OVERRUN = 21, //卡无法维持流模式下的数据编程 SD_CID_CSD_OVERWRITE = 22, //CID/CSD覆盖错误 SD_WP_ERASE_SKIP = 23, //只删除了部分地址空间 SD_CARD_ECC_DISABLED = 24, //命令已在不使用内部ECC的情况下执行 SD_ERASE_RESET = 25, //在执行之前清除了擦除序列,因为收到了一个超出擦除序列的命令 SD_AKE_SEQ_ERROR = 26, //身份验证顺序错误 SD_INVALID_VOLTRANGE = 27, SD_ADDR_OUT_OF_RANGE = 28, SD_SWITCH_ERROR = 29, SD_SDIO_DISABLED = 30, SD_SDIO_FUNCTION_BUSY = 31, SD_SDIO_FUNCTION_FAILED = 32, SD_SDIO_UNKNOWN_FUNCTION = 33, //标准错误定义 SD_INTERNAL_ERROR, SD_NOT_CONFIGURED, SD_REQUEST_PENDING, SD_REQUEST_NOT_APPLICABLE, SD_INVALID_PARAMETER, SD_UNSUPPORTED_FEATURE, SD_UNSUPPORTED_HW, SD_ERROR, SD_OK = 0 } SD_Error; //SD卡CSD寄存器数据 typedef struct{ u8 CSDStruct; //CSD结构 u8 SysSpecVersion; //系统版本 u8 Reserved1; //保留 u8 TAAC; //数据读取访问时间1 u8 NSAC; //在CLK期间的数据读取访问时间2 u8 MaxBusClkFrec; //总线最高频率 u16 CardComdClasses; //卡命令类 u8 RdBlockLen; //最大读取数据块长度 u8 PartBlockRead; //允许读取的部分块 u8 WrBlockMisalign; //写入块未对准 u8 RdBlockMisalign; //读取块未对准 u8 DSRImpl; //DSR生效 u8 Reserved2; //保留 u32 DeviceSize; //驱动大小 u8 MaxRdCurrentVDDMin; //最大读取流@ VDD最小 u8 MaxRdCurrentVDDMax; //最大读取流@ VDD最大 u8 MaxWrCurrentVDDMin; //最大写入流@ VDD最小 u8 MaxWrCurrentVDDMax; //最大写入流@ VDD最大 u8 DeviceSizeMul; //驱动大小 u8 EraseGrSize; //擦除组大小 u8 EraseGrMul; //擦除组大小乘数 u8 WrProtectGrSize; //写保护组大小 u8 WrProtectGrEnable; //写保护组使能 u8 ManDeflECC; //制造商默认ECC u8 WrSpeedFact; //写入速度因子 u8 MaxWrBlockLen; //最大写入数据长度 u8 WriteBlockPaPartial; //允许写入的部分块 u8 Reserved3; //保留 u8 ContentProtectAppli; //内容保护程序 u8 FileFormatGrouop; //文件格式组 u8 CopyFlag; //复制标志OTP u8 PermWrProtect; //永久写保护 u8 TempWrProtect; //临时写保护 u8 FileFormat; //文件格式 u8 ECC; //ECC 代码 u8 CSD_CRC; //CSD CRC u8 Reserved4; //总是1} SD_CSD; //SD卡CID寄存器数据typedef struct{ u8 ManufacturerID; //制造商 u16 OEM_AppliID; //OEM/应用程序ID u32 ProdName1; //名称部分1 u8 ProdName2; //名称部分2 u8 ProdRev; //版本 u32 ProdSN; //序列号 u8 Reserved1; //保留1 u16 ManufactDate; //出厂日期 u8 CID_CRC; //CID CRC u8 Reserved2; //总是1} SD_CID; //SD卡状态typedef enum{ SD_CARD_READY = 0x00000001, SD_CARD_IDENTIFICATION = 0x00000002, SD_CARD_STANDBY = 0x00000003, SD_CARD_TRANSFER = 0x00000004, SD_CARD_SENDING = 0x00000005, SD_CARD_RECEIVING = 0x00000006, SD_CARD_PROGRAMMING = 0x00000007, SD_CARD_DISCONNECTED = 0x00000008, SD_CARD_ERROR = 0x000000FF}SDCardState;//SD卡信息,包括CSD,CID等数据typedef struct{ SD_CSD SD_csd; SD_CID SD_cid; long long CardCapacity; //SD卡容量,单位:字节,最大支持2^64字节大小的卡 u32 CardBlockSize; //SD卡块大小 u16 RCA; //卡相对地址 u8 CardType; //卡类型} SD_CardInfo;extern SD_CardInfo SDCardInfo; //SD卡信息//SDIO相关标志位,拷贝自:stm32f4xx_sdio.h#define SDIO_FLAG_CCRCFAIL 0x00000001#define SDIO_FLAG_DCRCFAIL 0x00000002#define SDIO_FLAG_CTIMEOUT 0x00000004#define SDIO_FLAG_DTIMEOUT 0x00000008#define SDIO_FLAG_TXUNDERR 0x00000010#define SDIO_FLAG_RXOVERR 0x00000020#define SDIO_FLAG_CMDREND 0x00000040#define SDIO_FLAG_CMDSENT 0x00000080#define SDIO_FLAG_DATAEND 0x00000100#define SDIO_FLAG_STBITERR 0x00000200#define SDIO_FLAG_DBCKEND 0x00000400#define SDIO_FLAG_CMDACT 0x00000800#define SDIO_FLAG_TXACT 0x00001000#define SDIO_FLAG_RXACT 0x00002000#define SDIO_FLAG_TXFIFOHE 0x00004000#define SDIO_FLAG_RXFIFOHF 0x00008000#define SDIO_FLAG_TXFIFOF 0x00010000#define SDIO_FLAG_RXFIFOF 0x00020000#define SDIO_FLAG_TXFIFOE 0x00040000#define SDIO_FLAG_RXFIFOE 0x00080000#define SDIO_FLAG_TXDAVL 0x00100000#define SDIO_FLAG_RXDAVL 0x00200000#define SDIO_FLAG_SDIOIT 0x00400000#define SDIO_FLAG_CEATAEND 0x00800000//SDIO 指令集#define SD_CMD_GO_IDLE_STATE 0#define SD_CMD_SEND_OP_COND 1#define SD_CMD_ALL_SEND_CID 2#define SD_CMD_SET_REL_ADDR 3 //SD卡的SDIO_SEND_REL_ADDR#define SD_CMD_SET_DSR 4#define SD_CMD_SDIO_SEN_OP_COND 5#define SD_CMD_HS_SWITCH 6#define SD_CMD_SEL_DESEL_CARD 7#define SD_CMD_HS_SEND_EXT_CSD 8#define SD_CMD_SEND_CSD 9#define SD_CMD_SEND_CID 10#define SD_CMD_READ_DAT_UNTIL_STOP 11 //SD卡不支持#define SD_CMD_STOP_TRANSMISSION 12#define SD_CMD_SEND_STATUS 13#define SD_CMD_HS_BUSTEST_READ 14#define SD_CMD_GO_INACTIVE_STATE 15#define SD_CMD_SET_BLOCKLEN 16#define SD_CMD_READ_SINGLE_BLOCK 17#define SD_CMD_READ_MULT_BLOCK 18#define SD_CMD_HS_BUSTEST_WRITE 19#define SD_CMD_WRITE_DAT_UNTIL_STOP 20 #define SD_CMD_SET_BLOCK_COUNT 23 #define SD_CMD_WRITE_SINGLE_BLOCK 24#define SD_CMD_WRITE_MULT_BLOCK 25#define SD_CMD_PROG_CID 26#define SD_CMD_PROG_CSD 27#define SD_CMD_SET_WRITE_PROT 28#define SD_CMD_CLR_WRITE_PROT 29#define SD_CMD_SEND_WRITE_PROT 30#define SD_CMD_SD_ERASE_GRP_START 32 //设置要擦除的第一个写块的地址SD卡独有#define SD_CMD_SD_ERASE_GRP_END 33 //设置要擦除的连续范围的最后一个写块的地址SD卡独有#define SD_CMD_ERASE_GRP_START 35 //设置要擦除的第一个写块的地址MMC card 3.31#define SD_CMD_ERASE_GRP_END 36 //设置要擦除的连续范围的最后一个写块的地址MMC card 3.31#define SD_CMD_ERASE 38#define SD_CMD_FAST_IO 39 //SD卡不支持#define SD_CMD_GO_IRQ_STATE 40 //SD卡不支持#define SD_CMD_LOCK_UNLOCK 42#define SD_CMD_APP_CMD 55#define SD_CMD_GEN_CMD 56#define SD_CMD_NO_CMD 64#define SD_CMD_APP_SD_SET_BUSWIDTH 6 //SD卡独有#define SD_CMD_SD_APP_STAUS 13 //SD卡独有#define SD_CMD_SD_APP_SEND_NUM_WRITE_BLOCKS 22 //SD卡独有#define SD_CMD_SD_APP_OP_COND 41 //SD卡独有#define SD_CMD_SD_APP_SET_CLR_CARD_DETECT 42 //SD卡独有#define SD_CMD_SD_APP_SEND_SCR 51 //SD卡独有#define SD_CMD_SDIO_RW_DIRECT 52 //SD卡IO独有#define SD_CMD_SDIO_RW_EXTENDED 53 //SD卡IO独有#define SD_CMD_SD_APP_GET_MKB 43 //SD卡独有#define SD_CMD_SD_APP_GET_MID 44 //SD卡独有#define SD_CMD_SD_APP_SET_CER_RN1 45 //SD卡独有#define SD_CMD_SD_APP_GET_CER_RN2 46 //SD卡独有#define SD_CMD_SD_APP_SET_CER_RES2 47 //SD卡独有#define SD_CMD_SD_APP_GET_CER_RES1 48 //SD卡独有#define SD_CMD_SD_APP_SECURE_READ_MULTIPLE_BLOCK 18 //SD卡独有#define SD_CMD_SD_APP_SECURE_WRITE_MULTIPLE_BLOCK 25 //SD卡独有#define SD_CMD_SD_APP_SECURE_ERASE 38 //SD卡独有#define SD_CMD_SD_APP_CHANGE_SECURE_AREA 49 //SD卡独有#define SD_CMD_SD_APP_SECURE_WRITE_MKB 48 //SD卡独有//支持的SD卡定义#define SDIO_STD_CAPACITY_SD_CARD_V1_1 0x00000000#define SDIO_STD_CAPACITY_SD_CARD_V2_0 0x00000001#define SDIO_HIGH_CAPACITY_SD_CARD 0x00000002#define SDIO_MULTIMEDIA_CARD 0x00000003#define SDIO_SECURE_DIGITAL_IO_CARD 0x00000004#define SDIO_HIGH_SPEED_MULTIMEDIA_CARD 0x00000005#define SDIO_SECURE_DIGITAL_IO_COMBO_CARD 0x00000006#define SDIO_HIGH_CAPACITY_MMC_CARD 0x00000007//SDIO相关参数定义#define NULL 0#define SDIO_STATIC_FLAGS 0x000005FF#define SDIO_CMD0TIMEOUT 0x00010000 #define SDIO_DATATIMEOUT 0xFFFFFFFF #define SDIO_FIFO_Address 0x40018080//Mask for errors Card Status R1 OCR Register #define SD_OCR_ADDR_OUT_OF_RANGE 0x80000000#define SD_OCR_ADDR_MISALIGNED 0x40000000#define SD_OCR_BLOCK_LEN_ERR 0x20000000#define SD_OCR_ERASE_SEQ_ERR 0x10000000#define SD_OCR_BAD_ERASE_PARAM 0x08000000#define SD_OCR_WRITE_PROT_VIOLATION 0x04000000#define SD_OCR_LOCK_UNLOCK_FAILED 0x01000000#define SD_OCR_COM_CRC_FAILED 0x00800000#define SD_OCR_ILLEGAL_CMD 0x00400000#define SD_OCR_CARD_ECC_FAILED 0x00200000#define SD_OCR_CC_ERROR 0x00100000#define SD_OCR_GENERAL_UNKNOWN_ERROR 0x00080000#define SD_OCR_STREAM_READ_UNDERRUN 0x00040000#define SD_OCR_STREAM_WRITE_OVERRUN 0x00020000#define SD_OCR_CID_CSD_OVERWRIETE 0x00010000#define SD_OCR_WP_ERASE_SKIP 0x00008000#define SD_OCR_CARD_ECC_DISABLED 0x00004000#define SD_OCR_ERASE_RESET 0x00002000#define SD_OCR_AKE_SEQ_ERROR 0x00000008#define SD_OCR_ERRORBITS 0xFDFFE008//Masks for R6 Response #define SD_R6_GENERAL_UNKNOWN_ERROR 0x00002000#define SD_R6_ILLEGAL_CMD 0x00004000#define SD_R6_COM_CRC_FAILED 0x00008000#define SD_VOLTAGE_WINDOW_SD 0x80100000#define SD_HIGH_CAPACITY 0x40000000#define SD_STD_CAPACITY 0x00000000#define SD_CHECK_PATTERN 0x000001AA#define SD_VOLTAGE_WINDOW_MMC 0x80FF8000#define SD_MAX_VOLT_TRIAL 0x0000FFFF#define SD_ALLZERO 0x00000000#define SD_WIDE_BUS_SUPPORT 0x00040000#define SD_SINGLE_BUS_SUPPORT 0x00010000#define SD_CARD_LOCKED 0x02000000#define SD_CARD_PROGRAMMING 0x00000007#define SD_CARD_RECEIVING 0x00000006#define SD_DATATIMEOUT 0xFFFFFFFF#define SD_0TO7BITS 0x000000FF#define SD_8TO15BITS 0x0000FF00#define SD_16TO23BITS 0x00FF0000#define SD_24TO31BITS 0xFF000000#define SD_MAX_DATA_LENGTH 0x01FFFFFF#define SD_HALFFIFO 0x00000008#define SD_HALFFIFOBYTES 0x00000020//Command Class Supported #define SD_CCCC_LOCK_UNLOCK 0x00000080#define SD_CCCC_WRITE_PROT 0x00000040#define SD_CCCC_ERASE 0x00000020 //CMD8指令#define SDIO_SEND_IF_COND 0x00000008/********************************************************************************************************* 函 数 列 表*********************************************************************************************************/SD_Error SD_Init( void ) ; //初始化SD卡u8 SD_ReadDisk( u8*buf, u32 sector, u8 cnt ) ; //读SD卡,fatfs/usb调用u8 SD_WriteDisk( u8*buf, u32 sector, u8 cnt ) ; //写SD卡,fatfs/usb调用#endif
(2)创建sdio_sdcard.c文件,并输入以下代码。
#include "sdio_sdcard.h"#include "string.h"SD_CardInfo SDCardInfo ; //SD卡信息u8 CardType=SDIO_STD_CAPACITY_SD_CARD_V1_1 ; //SD卡类型/***************************************************Name :SDIO_Clock_SetFunction :SDIO发送命令函数Paramater : cmdindex:命令索引,低六位有效 waitrsp:期待的相应 00/10:无响应 01:短响应 11:长响应 arg:参数Return :错误代码***************************************************/void SDIO_Send_Cmd( u8 cmdindex, u8 waitrsp, u32 arg ){ u32 tmpreg ; SDIO->ARG = arg ; tmpreg = SDIO->CMD ; tmpreg &= 0xFFFFF800 ; //清除index和waitrsp tmpreg |= cmdindex&0X3F ; //设置新的index tmpreg |= waitrsp<<6 ; //设置新的wait rsp tmpreg |= 0<<8 ; //无等待 tmpreg |= 1<<10 ; //命令通道状态机使能 SDIO->CMD = tmpreg ;}/***************************************************Name :SDIO_Send_Data_CfgFunction :SDIO发送数据配置函数Paramater : datatimeout:超时时间设置 datalen:传输数据长度,低25位有效,必须为块大小的整数倍 blksize:块大小.实际大小为:2^blksize字节 dir:数据传输方向 0:控制器到卡 1:卡到控制器Return :错误代码***************************************************/void SDIO_Send_Data_Cfg( u32 datatimeout, u32 datalen, u8 blksize, u8 dir ){ u32 tmpreg; SDIO->DTIMER = datatimeout ; SDIO->DLEN = datalen&0x1FFFFFF ; //低25位有效 tmpreg = SDIO->DCTRL ; tmpreg &= 0xFFFFFF08 ; //清除之前的设置 tmpreg |= blksize<<4 ; //设置块大小 tmpreg |= 0<<2 ; //块数据传输 tmpreg |= ( dir&0x01 )<<1 ; //方向控制 tmpreg |= 1<<0 ; //数据传输使能,DPSM状态机 SDIO->DCTRL = tmpreg ;}/***************************************************Name :SDIO_Clock_SetFunction :SDIO时钟初始化设置Paramater : clkdiv:时钟分频系数Return :错误代码***************************************************/void SDIO_Clock_Set( u8 clkdiv ){ u32 tmpreg = SDIO->CLKCR ; tmpreg &= 0xFFFFFF00 ; tmpreg |= clkdiv; SDIO->CLKCR = tmpreg ;}/***************************************************Name :CmdErrorFunction :检查CMD0的执行状态Paramater :NoneReturn :错误代码***************************************************/SD_Error CmdError(){ SD_Error errorstatus = SD_OK ; u32 timeout=SDIO_CMD0TIMEOUT ; while( timeout-- ) { //命令已发送(无需响应) if( SDIO->STA&( 1<<7 ) ) break ; } if( timeout==0 ) return SD_CMD_RSP_TIMEOUT ; SDIO->ICR = 0x5FF ; //清除标记 return errorstatus ;}/***************************************************Name :CmdResp7ErrorFunction :检查R7响应的错误状态Paramater :NoneReturn :错误代码***************************************************/SD_Error CmdResp7Error(){ u32 status ; u32 timeout = 0x00010000 ; while( timeout-- ) { status = SDIO->STA ; if( ( status&0x45 )!=0 ) //等待接收到应答 break ; } //响应超时 if( ( timeout==0 )||( ( status&0x04 )==0x04 ) ) { //当前卡不是2.0兼容卡,或者不支持设定的电压范围 SDIO->ICR |= 1<<2 ; //清除命令响应超时标志 return SD_CMD_RSP_TIMEOUT ; } //成功接收到响应 if( ( status&0x40 )==0x40 ) SDIO->ICR |= 1<<6 ; //清除响应标志 return SD_OK ;}/***************************************************Name :CmdResp1ErrorFunction :检查R1响应的错误状态Paramater : cmd:当前命令Return :错误代码***************************************************/SD_Error CmdResp1Error( u8 cmd ){ u32 status ; while( 1 ) { status = SDIO->STA ; if( ( status&0x45 )!=0 ) //等待接收到应答 break ; } //响应超时 if( ( status&0x04 )==0x04 ) { SDIO->ICR |= 1<<2 ; //清除命令响应超时标志 return SD_CMD_RSP_TIMEOUT ; } //CRC错误 if( ( status&0x01 )==0x01 ) { SDIO->ICR |= 1<<0; //清除标志 return SD_CMD_CRC_FAIL; } if( SDIO->RESPCMD!=cmd ) return SD_ILLEGAL_CMD ; //命令不匹配 SDIO->ICR = 0x5FF ; //清除标记 return ( SD_Error )( SDIO->RESP1&0xFDFFE008 ) ; //返回卡响应}/***************************************************Name :CmdResp3ErrorFunction :检查R3响应的错误状态Paramater :NoneReturn :错误代码***************************************************/SD_Error CmdResp3Error(){ u32 status ; while( 1 ) { status = SDIO->STA ; if( ( status&0x45 )!=0 ) //等待接收到应答 break ; } //响应超时 if( ( status&0x04 )==0x04 ) { SDIO->ICR |= 1<<2 ; //清除命令响应超时标志 return SD_CMD_RSP_TIMEOUT ; } SDIO->ICR = 0x5FF ; //清除标记 return SD_OK ;}/***************************************************Name :CmdResp2ErrorFunction :检查R2响应的错误状态Paramater :NoneReturn :错误代码***************************************************/SD_Error CmdResp2Error(){ u32 status ; u32 timeout = 0x00010000 ; while( timeout-- ) { status = SDIO->STA ; if( ( status&0x45 )!=0 ) //接收到R2响应 break ; } //响应超时 if( ( timeout==0 )||( ( status&0x04 )==0x04 ) ) { SDIO->ICR |= 1<<2 ; //清除命令响应超时标志 return SD_CMD_RSP_TIMEOUT ; } //CRC错误 if( ( status&0x01 )==0x01 ) { SDIO->ICR |= 1<<0 ; //清除响应标志 return SD_CMD_CRC_FAIL ; } SDIO->ICR = 0x5FF ; //清除标记 return SD_OK ;}/***************************************************Name :CmdResp6ErrorFunction :检查R6响应的错误状态Paramater : cmd:之前发送的命令 prca:卡返回的RCA地址Return :错误代码***************************************************/SD_Error CmdResp6Error( u8 cmd, u16 *prca ){ SD_Error errorstatus=SD_OK ; u32 rspr1, status; while(1) { status = SDIO->STA ; //CRC错误/命令响应超时/已经收到响应(CRC校验成功) if( status&( (1<<0)|(1<<2)|(1<<6) ) ) break ; } //响应超时 if( status&( 1<<2 ) ) { SDIO->ICR |= 1<<2 ; //清除命令响应超时标志 return SD_CMD_RSP_TIMEOUT ; } //CRC错误 if( status&1<<0 ) { SDIO->ICR |= 1<<0 ; //清除响应标志 return SD_CMD_CRC_FAIL ; } //判断是否响应cmd命令 if( SDIO->RESPCMD!=cmd ) return SD_ILLEGAL_CMD ; SDIO->ICR = 0x5FF ; //清除所有标记 rspr1 = SDIO->RESP1 ; //得到响应 if( SD_ALLZERO==( rspr1&( SD_R6_GENERAL_UNKNOWN_ERROR|SD_R6_ILLEGAL_CMD|SD_R6_COM_CRC_FAILED ) ) ) { *prca = ( u16 )( rspr1>>16 ) ; //右移16位得到,rca return errorstatus ; } if( rspr1&SD_R6_GENERAL_UNKNOWN_ERROR ) return SD_GENERAL_UNKNOWN_ERROR ; if( rspr1&SD_R6_ILLEGAL_CMD ) return SD_ILLEGAL_CMD ; if( rspr1&SD_R6_COM_CRC_FAILED ) return SD_COM_CRC_FAILED ; return errorstatus ;}/***************************************************Name :SDIO_IRQHandlerFunction :SDIO中断服务函数Paramater :NoneReturn :错误代码***************************************************/void SDIO_IRQHandler(){ //接收完成中断 if( SDIO->STA&( 1<<8 ) ) { SDIO_Send_Cmd( SD_CMD_STOP_TRANSMISSION, 1, 0 ) ; //发送CMD12,结束传输 SDIO->ICR |= 1<<8 ; //清除完成中断标记 SDIO->MASK &= ~( ( 1<<1 )|( 1<<3 )|( 1<<8 )|( 1<<14 )|( 1<<15 )|( 1<<4 )|( 1<<5 )|( 1<<9 ) ) ; //关闭相关中断 } //数据CRC错误 if( SDIO->STA&( 1<<1 ) ) { SDIO->ICR |= 1<<1 ; //清除中断标记 SDIO->MASK &= ~( ( 1<<1 )|( 1<<3 )|( 1<<8 )|( 1<<14 )|( 1<<15 )|( 1<<4 )|( 1<<5 )|( 1<<9 ) ) ; //关闭相关中断 } //数据超时错误 if( SDIO->STA&( 1<<3 ) ) { SDIO->ICR |= 1<<3 ; //清除中断标记 SDIO->MASK &= ~( ( 1<<1 )|( 1<<3 )|( 1<<8 )|( 1<<14 )|( 1<<15 )|( 1<<4 )|( 1<<5 )|( 1<<9 ) ) ; //关闭相关中断 } //FIFO上溢错误 if( SDIO->STA&( 1<<5 ) ) { SDIO->ICR |= 1<<5 ; //清除中断标记 SDIO->MASK &= ~( ( 1<<1 )|( 1<<3 )|( 1<<8 )|( 1<<14 )|( 1<<15 )|( 1<<4 )|( 1<<5 )|( 1<<9 ) ) ; //关闭相关中断 } //FIFO下溢错误 if( SDIO->STA&( 1<<4 ) ) { SDIO->ICR |= 1<<4 ; //清除中断标记 SDIO->MASK &= ~( ( 1<<1 )|( 1<<3 )|( 1<<8 )|( 1<<14 )|( 1<<15 )|( 1<<4 )|( 1<<5 )|( 1<<9 ) ) ; //关闭相关中断 } //起始位错误 if( SDIO->STA&( 1<<9 ) ) { SDIO->ICR |= 1<<9 ; //清除中断标记 SDIO->MASK &= ~( ( 1<<1 )|( 1<<3 )|( 1<<8 )|( 1<<14 )|( 1<<15 )|( 1<<4 )|( 1<<5 )|( 1<<9 ) ) ; //关闭相关中断 }}/***************************************************Name :SD_PowerONFunction :卡上电Paramater :NoneReturn :错误代码***************************************************/SD_Error SD_PowerON(){ u8 i=0 ; SD_Error errorstatus=SD_OK ; u32 response=0, count=0, validvoltage=0 ; u32 SDType=SD_STD_CAPACITY ; //配置CLKCR寄存器 SDIO->CLKCR = 0 ; //清空CLKCR之前的设置 SDIO->CLKCR |= 0<<9 ; //非省电模式 SDIO->CLKCR |= 0<<10 ; //关闭旁路,CK根据分频设置输出 SDIO->CLKCR |= 0<<11 ; //1位数据宽度 SDIO->CLKCR |= 0<<13 ; //SDIOCLK上升沿产生SDIOCK SDIO->CLKCR |= 0<<14 ; //关闭硬件流控制 SDIO_Clock_Set( SDIO_INIT_CLK_DIV ) ; //设置时钟频率(初始化的时候,不能超过400Khz) SDIO->POWER = 0x03 ; //上电状态,开启卡时钟 SDIO->CLKCR |= 1<<8 ; //SDIOCK使能 for( i=0; i<74; i++ ) { SDIO_Send_Cmd( SD_CMD_GO_IDLE_STATE, 0, 0 ) ; //发送CMD0进入IDLE STAGE模式命令 errorstatus = CmdError() ; if( errorstatus==SD_OK ) break ; } //返回错误状态 if( errorstatus ) return errorstatus ; SDIO_Send_Cmd( SDIO_SEND_IF_COND, 1, SD_CHECK_PATTERN ) ; //发送CMD8,短响应,检查SD卡接口特性 errorstatus = CmdResp7Error() ; //等待R7响应 //R7响应正常 if( errorstatus==SD_OK ) { CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0 ; //SD 2.0卡 SDType = SD_HIGH_CAPACITY ; //高容量卡 } SDIO_Send_Cmd( SD_CMD_APP_CMD, 1, 0 ) ; //发送CMD55,短响应 errorstatus = CmdResp1Error( SD_CMD_APP_CMD ) ; //等待R1响应 //SD2.0/SD 1.1,否则为MMC卡 if( errorstatus==SD_OK ) { //SD卡,发送ACMD41 SD_APP_OP_COND,参数为:0x80100000 while( ( !validvoltage )&&( count { SDIO_Send_Cmd( SD_CMD_APP_CMD, 1, 0 ) ; //发送CMD55,短响应 errorstatus = CmdResp1Error( SD_CMD_APP_CMD ) ; //等待R1响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; SDIO_Send_Cmd( SD_CMD_SD_APP_OP_COND, 1, SD_VOLTAGE_WINDOW_SD|SDType ); //发送ACMD41,短响应 errorstatus = CmdResp3Error() ; //等待R3响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; response = SDIO->RESP1 ; //得到响应 validvoltage = ( ( ( response>>31 )==1 )?1:0 ) ; //判断SD卡上电是否完成 count ++ ; } if( count>=SD_MAX_VOLT_TRIAL ) { errorstatus = SD_INVALID_VOLTRANGE ; return errorstatus ; } if( response&=SD_HIGH_CAPACITY ) CardType = SDIO_HIGH_CAPACITY_SD_CARD ; } return errorstatus ;}/***************************************************Name :SD_InitializeCardsFunction :初始化所有的卡Paramater :NoneReturn :错误代码***************************************************/u32 CSD_Tab[ 4 ], CID_Tab[ 4 ], RCA=0 ; //SD卡CSD,CID以及相对地址(RCA)数据SD_Error SD_InitializeCards(){ SD_Error errorstatus=SD_OK ; u16 rca = 0x01 ; //检查电源状态,确保为上电状态 if( ( SDIO->POWER&0x03 )==0 ) return SD_REQUEST_NOT_APPLICABLE ; //非SECURE_DIGITAL_IO_CARD if( SDIO_SECURE_DIGITAL_IO_CARD!=CardType ) { SDIO_Send_Cmd( SD_CMD_ALL_SEND_CID, 3, 0 ) ; //发送CMD2,取得CID,长响应 errorstatus = CmdResp2Error() ; //等待R2响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus; CID_Tab[ 0 ] = SDIO->RESP1 ; CID_Tab[ 1 ] = SDIO->RESP2 ; CID_Tab[ 2 ] = SDIO->RESP3 ; CID_Tab[ 3 ] = SDIO->RESP4 ; } //判断卡类型 if( ( SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType )||( SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType )||( SDIO_SECURE_DIGITAL_IO_COMBO_CARD==CardType )||( SDIO_HIGH_CAPACITY_SD_CARD==CardType ) ) { SDIO_Send_Cmd( SD_CMD_SET_REL_ADDR, 1, 0 ) ; //发送CMD3,短响应 errorstatus = CmdResp6Error( SD_CMD_SET_REL_ADDR, &rca ) ; //等待R6响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; } if( SDIO_MULTIMEDIA_CARD==CardType ) { SDIO_Send_Cmd( SD_CMD_SET_REL_ADDR, 1, ( u32 )( rca<<16 ) ) ; //发送CMD3,短响应 errorstatus = CmdResp2Error() ; //等待R2响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; } //非SECURE_DIGITAL_IO_CARD if( SDIO_SECURE_DIGITAL_IO_CARD!=CardType ) { RCA = rca ; SDIO_Send_Cmd( SD_CMD_SEND_CSD, 3, ( u32 )( rca<<16 ) ) ; //发送CMD9+卡RCA,取得CSD,长响应 errorstatus = CmdResp2Error() ; //等待R2响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; CSD_Tab[ 0 ] = SDIO->RESP1 ; CSD_Tab[ 1 ] = SDIO->RESP2 ; CSD_Tab[ 2 ] = SDIO->RESP3 ; CSD_Tab[ 3 ] = SDIO->RESP4 ; } return SD_OK ; //卡初始化成功}/***************************************************Name :SD_GetCardInfoFunction :得到卡信息Paramater : cardinfo:卡信息存储区Return :错误代码***************************************************/SD_Error SD_GetCardInfo( SD_CardInfo *cardinfo ){ SD_Error errorstatus = SD_OK ; u8 tmp = 0 ; cardinfo->CardType = ( u8 )CardType ; //卡类型 cardinfo->RCA = ( u16 )RCA ; //卡RCA值 tmp = ( u8 )( ( CSD_Tab[ 0 ]&0xFF000000 )>>24 ) ; cardinfo->SD_csd.CSDStruct = ( tmp&0xC0 )>>6 ; //CSD结构 cardinfo->SD_csd.SysSpecVersion = ( tmp&0x3C )>>2 ; //2.0协议还没定义这部分(为保留),应该是后续协议定义的 cardinfo->SD_csd.Reserved1 = tmp&0x03 ; //2个保留位 tmp = ( u8 )( ( CSD_Tab[ 0 ]&0x00FF0000 )>>16 ) ; //第1个字节 cardinfo->SD_csd.TAAC = tmp ; //数据读时间1 tmp = ( u8 )( ( CSD_Tab[ 0 ]&0x0000FF00 )>>8 ) ; //第2个字节 cardinfo->SD_csd.NSAC = tmp ; //数据读时间2 tmp = ( u8 )( CSD_Tab[ 0 ]&0x000000FF ) ; //第3个字节 cardinfo->SD_csd.MaxBusClkFrec = tmp ; //传输速度 tmp = ( u8 )( ( CSD_Tab[ 1 ]&0xFF000000 )>>24 ) ; //第4个字节 cardinfo->SD_csd.CardComdClasses = tmp<<4 ; //卡指令类高四位 tmp = ( u8 )( ( CSD_Tab[ 1 ]&0x00FF0000 )>>16 ) ; //第5个字节 cardinfo->SD_csd.CardComdClasses |= ( tmp&0xF0 )>>4 ; //卡指令类低四位 cardinfo->SD_csd.RdBlockLen = tmp&0x0F ; //最大读取数据长度 tmp = ( u8 )( ( CSD_Tab[ 1 ]&0x0000FF00 )>>8 ) ; //第6个字节 cardinfo->SD_csd.PartBlockRead = ( tmp&0x80 )>>7 ; //允许分块读 cardinfo->SD_csd.WrBlockMisalign = ( tmp&0x40 )>>6 ; //写块错位 cardinfo->SD_csd.RdBlockMisalign = ( tmp&0x20 )>>5 ; //读块错位 cardinfo->SD_csd.DSRImpl = ( tmp&0x10 )>>4 ; cardinfo->SD_csd.Reserved2 = 0 ; //保留 //标准1.1/2.0卡/MMC卡 if( ( CardType==SDIO_STD_CAPACITY_SD_CARD_V1_1 )||( CardType==SDIO_STD_CAPACITY_SD_CARD_V2_0 )||( SDIO_MULTIMEDIA_CARD==CardType ) ) { cardinfo->SD_csd.DeviceSize = ( tmp&0x03 )<<10 ; //C_SIZE(12位) tmp = ( u8 )( CSD_Tab[ 1 ]&0x000000FF ) ; //第7个字节 cardinfo->SD_csd.DeviceSize |= tmp<<2 ; tmp = ( u8 )( ( CSD_Tab[ 2 ]&0xFF000000 )>>24 ); //第8个字节 cardinfo->SD_csd.DeviceSize |= ( tmp&0xC0 )>>6 ; cardinfo->SD_csd.MaxRdCurrentVDDMin = ( tmp&0x38 )>>3 ; cardinfo->SD_csd.MaxRdCurrentVDDMax = ( tmp&0x07 ) ; tmp = ( u8 )( ( CSD_Tab[ 2 ]&0x00FF0000 )>>16 ) ; //第9个字节 cardinfo->SD_csd.MaxWrCurrentVDDMin = ( tmp&0xE0 )>>5 ; cardinfo->SD_csd.MaxWrCurrentVDDMax = ( tmp&0x1C )>>2 ; cardinfo->SD_csd.DeviceSizeMul = ( tmp&0x03 )<<1 ; //C_SIZE_MULT tmp = ( u8 )( ( CSD_Tab[ 2 ]&0x0000FF00 )>>8 ) ; //第10个字节 cardinfo->SD_csd.DeviceSizeMul |= ( tmp&0x80 )>>7 ; cardinfo->CardCapacity = ( cardinfo->SD_csd.DeviceSize+1 ) ; //计算卡容量 cardinfo->CardCapacity *= ( 1<SD_csd.DeviceSizeMul+2 ) ) ; cardinfo->CardBlockSize = 1<SD_csd.RdBlockLen ) ; //块大小 cardinfo->CardCapacity *= cardinfo->CardBlockSize ; } //高容量卡 else if( CardType==SDIO_HIGH_CAPACITY_SD_CARD ) { tmp = ( u8 )( CSD_Tab[ 1 ]&0x000000FF ); //第7个字节 cardinfo->SD_csd.DeviceSize = ( tmp&0x3F )<<16 ; //C_SIZE tmp = ( u8 )( ( CSD_Tab[ 2 ]&0xFF000000 )>>24 ) ; //第8个字节 cardinfo->SD_csd.DeviceSize |= ( tmp<<8 ) ; tmp = ( u8 )( ( CSD_Tab[ 2 ]&0x00FF0000 )>>16 ) ; //第9个字节 cardinfo->SD_csd.DeviceSize |= tmp ; tmp = ( u8 )( ( CSD_Tab[ 2 ]&0x0000FF00 )>>8 ) ; //第10个字节 cardinfo->CardCapacity = ( long long )( cardinfo->SD_csd.DeviceSize+1 )*512*1024 ; //计算卡容量 cardinfo->CardBlockSize = 512 ; //块大小固定为512字节 } cardinfo->SD_csd.EraseGrSize = ( tmp&0x40 )>>6 ; cardinfo->SD_csd.EraseGrMul = ( tmp&0x3F )<<1 ; tmp = ( u8 )( CSD_Tab[ 2 ]&0x000000FF ) ; //第11个字节 cardinfo->SD_csd.EraseGrMul |= ( tmp&0x80 )>>7 ; cardinfo->SD_csd.WrProtectGrSize=( tmp&0x7F ) ; tmp = ( u8 )( ( CSD_Tab[ 3 ]&0xFF000000 )>>24 ) ; //第12个字节 cardinfo->SD_csd.WrProtectGrEnable = ( tmp&0x80 )>>7 ; cardinfo->SD_csd.ManDeflECC = ( tmp&0x60 )>>5 ; cardinfo->SD_csd.WrSpeedFact = ( tmp&0x1C )>>2 ; cardinfo->SD_csd.MaxWrBlockLen=( tmp&0x03 )<<2 ; tmp = ( u8 )( ( CSD_Tab[ 3 ]&0x00FF0000 )>>16 ) ; //第13个字节 cardinfo->SD_csd.MaxWrBlockLen |= ( tmp&0xC0 )>>6 ; cardinfo->SD_csd.WriteBlockPaPartial = ( tmp&0x20 )>>5 ; cardinfo->SD_csd.Reserved3 = 0 ; cardinfo->SD_csd.ContentProtectAppli=(tmp&0x01); tmp = ( u8 )( ( CSD_Tab[ 3 ]&0x0000FF00 )>>8 ) ; //第14个字节 cardinfo->SD_csd.FileFormatGrouop = ( tmp&0x80 )>>7 ; cardinfo->SD_csd.CopyFlag = ( tmp&0x40 )>>6 ; cardinfo->SD_csd.PermWrProtect = ( tmp&0x20 )>>5 ; cardinfo->SD_csd.TempWrProtect = ( tmp&0x10 )>>4 ; cardinfo->SD_csd.FileFormat = ( tmp&0x0C )>>2 ; cardinfo->SD_csd.ECC = ( tmp&0x03 ) ; tmp = ( u8 )( CSD_Tab[ 3 ]&0x000000FF ) ; //第15个字节 cardinfo->SD_csd.CSD_CRC = ( tmp&0xFE )>>1 ; cardinfo->SD_csd.Reserved4 = 1 ; tmp = ( u8 )( ( CID_Tab[ 0 ]&0xFF000000 )>>24 ) ; //第0个字节 cardinfo->SD_cid.ManufacturerID = tmp ; tmp = ( u8 )( ( CID_Tab[ 0 ]&0x00FF0000 )>>16 ) ; //第1个字节 cardinfo->SD_cid.OEM_AppliID=tmp<<8; tmp = ( u8 )( ( CID_Tab[ 0 ]&0x000000FF00 )>>8 ) ; //第2个字节 cardinfo->SD_cid.OEM_AppliID |= tmp ; tmp = ( u8 )( CID_Tab[ 0 ]&0x000000FF ) ; //第3个字节 cardinfo->SD_cid.ProdName1 = tmp<<24 ; tmp = ( u8 )( ( CID_Tab[ 1 ]&0xFF000000 )>>24 ) ; //第4个字节 cardinfo->SD_cid.ProdName1 |= tmp<<16 ; tmp = ( u8 )( ( CID_Tab[ 1 ]&0x00FF0000 )>>16 ) ; //第5个字节 cardinfo->SD_cid.ProdName1 |= tmp<<8 ; tmp = ( u8 )( ( CID_Tab[ 1 ]&0x0000FF00 )>>8 ) ; //第6个字节 cardinfo->SD_cid.ProdName1 |= tmp ; tmp = ( u8 )( CID_Tab[ 1 ]&0x000000FF ) ; //第7个字节 cardinfo->SD_cid.ProdName2 = tmp ; tmp = ( u8 )( ( CID_Tab[ 2 ]&0xFF000000 )>>24 ) ; //第8个字节 cardinfo->SD_cid.ProdRev = tmp ; tmp = ( u8 )( ( CID_Tab[ 2 ]&0x00FF0000 )>>16 ) ; //第9个字节 cardinfo->SD_cid.ProdSN = tmp<<24 ; tmp = ( u8 )( ( CID_Tab[ 2 ]&0x0000FF00 )>>8 ) ; //第10个字节 cardinfo->SD_cid.ProdSN |= tmp<<16 ; tmp = ( u8 )( CID_Tab[ 2 ]&0x000000FF ); //第11个字节 cardinfo->SD_cid.ProdSN |= tmp<<8 ; tmp = ( u8 )( ( CID_Tab[ 3 ]&0xFF000000 )>>24 ) ; //第12个字节 cardinfo->SD_cid.ProdSN |= tmp ; tmp = ( u8 )( ( CID_Tab[ 3 ]&0x00FF0000 )>>16 ) ; //第13个字节 cardinfo->SD_cid.Reserved1 |= ( tmp&0xF0 )>>4 ; cardinfo->SD_cid.ManufactDate = ( tmp&0x0F )<<8 ; tmp = ( u8 )( ( CID_Tab[ 3 ]&0x0000FF00 )>>8 ) ; //第14个字节 cardinfo->SD_cid.ManufactDate |= tmp ; tmp = ( u8 )( CID_Tab[ 3 ]&0x000000FF ) ; //第15个字节 cardinfo->SD_cid.CID_CRC = ( tmp&0xFE )>>1 ; cardinfo->SD_cid.Reserved2 = 1 ; return errorstatus ;}/***************************************************Name :FindSCRFunction :查找SD卡的SCR寄存器值Paramater : rca:卡相对地址 pscr:数据缓存区(存储SCR内容)Return :错误代码***************************************************/ SD_Error FindSCR( u16 rca, u32 *pscr ){ u32 index = 0 ; SD_Error errorstatus = SD_OK ; u32 tempscr[ 2 ] = { 0, 0 } ; SDIO_Send_Cmd( SD_CMD_SET_BLOCKLEN, 1, 8 ) ; //发送CMD16,短响应,设置Block Size为8字节 errorstatus = CmdResp1Error( SD_CMD_SET_BLOCKLEN ) ; if( errorstatus!=SD_OK ) return errorstatus ; SDIO_Send_Cmd( SD_CMD_APP_CMD, 1, ( u32 )rca<<16 ) ; //发送CMD55,短响应 errorstatus = CmdResp1Error( SD_CMD_APP_CMD ) ; if( errorstatus!=SD_OK ) return errorstatus ; SDIO_Send_Data_Cfg( SD_DATATIMEOUT, 8, 3, 1 ) ; //8个字节长度,block为8字节,SD卡到SDIO SDIO_Send_Cmd( SD_CMD_SD_APP_SEND_SCR, 1, 0 ) ; //发送ACMD51,短响应,参数为0 errorstatus = CmdResp1Error( SD_CMD_SD_APP_SEND_SCR ) ; if( errorstatus!=SD_OK ) return errorstatus ; while( !( SDIO->STA&( SDIO_FLAG_RXOVERR|SDIO_FLAG_DCRCFAIL|SDIO_FLAG_DTIMEOUT|SDIO_FLAG_DBCKEND|SDIO_FLAG_STBITERR ) ) ) { //接收FIFO数据可用 if( SDIO->STA&( 1<<21 ) ) { *( tempscr+index ) = SDIO->FIFO ; //读取FIFO内容 index ++ ; if( index>=2 ) break ; } } //接收数据超时 if( SDIO->STA&( 1<<3 ) ) { SDIO->ICR |= 1<<3 ; //清除标记 return SD_DATA_TIMEOUT ; } //已发送/接收的数据块CRC校验错误 else if( SDIO->STA&( 1<<1 ) ) { SDIO->ICR |= 1<<1 ; //清除标记 return SD_DATA_CRC_FAIL ; } //接收FIFO溢出 else if( SDIO->STA&( 1<<5 ) ) { SDIO->ICR |= 1<<5 ; //清除标记 return SD_RX_OVERRUN ; } //起始位检测错误 else if( SDIO->STA&( 1<<9 ) ) { SDIO->ICR |= 1<<9 ; //清除标记 return SD_START_BIT_ERR ; } SDIO->ICR = 0X5FF ; //清除标记 //把数据顺序按8位为单位倒过来. *( 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 ) ; return errorstatus ;}/***************************************************Name :SDEnWideBusFunction :SDIO使能宽总线模式Paramater : enx:使能开关 0:不使能 1:使能Return :错误代码***************************************************/SD_Error SDEnWideBus( u8 enx ){ SD_Error errorstatus = SD_OK ; u32 scr[ 2 ] = { 0, 0 } ; u8 arg = 0x00 ; if( enx ) arg = 0x02 ; else arg = 0x00 ; //SD卡处于LOCKED状态 if( SDIO->RESP1&SD_CARD_LOCKED ) return SD_LOCK_UNLOCK_FAILED ; errorstatus = FindSCR( RCA, scr ); //得到SCR寄存器数据 if( errorstatus!=SD_OK ) return errorstatus ; //支持宽总线 if( ( scr[ 1 ]&SD_WIDE_BUS_SUPPORT )!=SD_ALLZERO ) { SDIO_Send_Cmd( SD_CMD_APP_CMD, 1, ( u32 )RCA<<16 ); //发送CMD55+RCA,短响应 errorstatus = CmdResp1Error( SD_CMD_APP_CMD ) ; if( errorstatus!=SD_OK ) return errorstatus ; SDIO_Send_Cmd( SD_CMD_APP_SD_SET_BUSWIDTH, 1, arg ) ; //发送ACMD6,短响应,参数:10,4位;00,1位 errorstatus = CmdResp1Error( SD_CMD_APP_SD_SET_BUSWIDTH ) ; return errorstatus ; } else return SD_REQUEST_NOT_APPLICABLE ; //不支持宽总线设置}/***************************************************Name :SD_EnableWideBusOperationFunction :设置SDIO总线宽度Paramater : wmode:位宽模式 0->1位数据宽度 1->4位数据宽度 2->8位数据宽度Return :错误代码***************************************************/SD_Error SD_EnableWideBusOperation( u32 wmode ){ SD_Error errorstatus = SD_OK ; if( SDIO_MULTIMEDIA_CARD==CardType ) return SD_UNSUPPORTED_FEATURE ; //MMC卡不支持 else if( ( SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType )||( SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType )||( SDIO_HIGH_CAPACITY_SD_CARD==CardType ) ) { if( wmode>=2 ) return SD_UNSUPPORTED_FEATURE ; //不支持8位模式 else { errorstatus = SDEnWideBus( wmode ) ; if( SD_OK==errorstatus ) { SDIO->CLKCR &= ~( 3<<11 ) ; //清除之前的位宽设置 SDIO->CLKCR |= ( u16 )wmode<<11 ; //1位/4位总线宽度 SDIO->CLKCR |= 0<<14 ; //不开启硬件流控制 } } } return errorstatus ;}/***************************************************Name :SD_InitFunction :初始化SD卡Paramater :NoneReturn :错误代码***************************************************/SD_Error SD_Init(){ u8 clkdiv=0 ; SD_Error errorstatus=SD_OK ; //SDIO IO口初始化 RCC->APB2ENR |= 1<<4 ; //使能PC时钟 RCC->APB2ENR |= 1<<5 ; //使能PD时钟 RCC->AHBENR |= 1<<10 ; //使能SDIO时钟 RCC->AHBENR |= 1<<1 ; //使能DMA2时钟 GPIOC->CRH &= 0xFFF00000 ; GPIOC->CRH |= 0x000BBBBB ; //PC.8~12 复用输出 GPIOD->CRL &= 0xFFFFF0FF ; GPIOD->CRL |= 0x00000B00 ; //PD2复用输出 //SDIO外设寄存器设置为默认值 SDIO->POWER = 0x00000000 ; SDIO->CLKCR = 0x00000000 ; SDIO->ARG = 0x00000000 ; SDIO->CMD = 0x00000000 ; SDIO->DTIMER = 0x00000000 ; SDIO->DLEN = 0x00000000 ; SDIO->DCTRL = 0x00000000 ; SDIO->ICR = 0x00C007FF ; SDIO->MASK = 0x00000000 ; NVIC_Init( 0, 0, SDIO_IRQn, 2 ) ; //SDIO中断配置 errorstatus = SD_PowerON() ; //SD卡上电 if( errorstatus==SD_OK ) errorstatus=SD_InitializeCards() ; //初始化SD卡 if( errorstatus==SD_OK ) errorstatus=SD_GetCardInfo( &SDCardInfo ) ; //获取卡信息 if( errorstatus==SD_OK ) { SDIO_Send_Cmd( SD_CMD_SEL_DESEL_CARD, 1, SDCardInfo.RCA<<16 ) ; //发送CMD7,选择卡,短响应 errorstatus = CmdResp1Error( SD_CMD_SEL_DESEL_CARD ) ; } if( errorstatus==SD_OK ) errorstatus=SD_EnableWideBusOperation( 1 ) ; //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+6 ; //V1.1/V2.0卡,设置最高72/12=6Mhz else clkdiv = SDIO_TRANSFER_CLK_DIV ; //SDHC等其他卡,设置最高72/6=12Mhz SDIO_Clock_Set( clkdiv ); //设置时钟频率 } return errorstatus ;}/***************************************************Name :convert_from_bytes_to_power_of_twoFunction :得到NumberOfBytes以2为底的指数Paramater : NumberOfBytes:字节数Return :以2为底的指数值***************************************************/u8 convert_from_bytes_to_power_of_two( u16 NumberOfBytes ){ u8 count=0 ; while( NumberOfBytes!=1 ) { NumberOfBytes >>= 1 ; count ++ ; } return count ;}/***************************************************Name :SD_ReadBlockFunction :读取一个块Paramater : buf:读数据缓存区(必须4字节对齐) addr:读取地址 blksize:块大小Return :错误代码***************************************************/SD_Error SD_ReadBlock( u8 *buf, long long addr, u16 blksize ){ SD_Error errorstatus = SD_OK ; u8 power ; u32 count=0, *tempbuff=( u32* )buf ; //转换为u32指针 u32 timeout = SDIO_DATATIMEOUT ; if( NULL==buf ) return SD_INVALID_PARAMETER ; SDIO->DCTRL = 0x0 ; //数据控制寄存器清零(关DMA) //大容量卡 if( CardType==SDIO_HIGH_CAPACITY_SD_CARD ) { blksize = 512 ; addr >>= 9 ; } SDIO_Send_Data_Cfg( SD_DATATIMEOUT, 0, 0, 0 ) ; //清除DPSM状态机配置 //卡锁了 if( SDIO->RESP1&SD_CARD_LOCKED ) return SD_LOCK_UNLOCK_FAILED ; if( ( blksize>0 )&&( blksize<=2048 )&&( ( blksize&( blksize-1 ) )==0 ) ) { power = convert_from_bytes_to_power_of_two( blksize ) ; SDIO_Send_Cmd( SD_CMD_SET_BLOCKLEN, 1, blksize ) ; //发送CMD16+设置数据长度为blksize,短响应 errorstatus = CmdResp1Error( SD_CMD_SET_BLOCKLEN ) ; //等待R1响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; } else return SD_INVALID_PARAMETER ; SDIO_Send_Data_Cfg( SD_DATATIMEOUT, blksize, power, 1 ) ; //blksize,卡到控制器 SDIO_Send_Cmd( SD_CMD_READ_SINGLE_BLOCK, 1, addr ) ; //发送CMD17+从addr地址出读取数据,短响应 errorstatus = CmdResp1Error( SD_CMD_READ_SINGLE_BLOCK ) ; //等待R1响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; __asm volatile( "cpsid i" ) ; //关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) //无上溢/CRC/超时/完成(标志)/起始位错误 while( !( SDIO->STA&( ( 1<<5 )|( 1<<1 )|( 1<<3 )|( 1<<10 )|( 1<<9 ) ) ) ) { //接收区半满,表示至少存了8个字 if( SDIO->STA&( 1<<15 ) ) { for( count=0; count<8; count++ ) //循环读取数据 *( tempbuff+count ) = SDIO->FIFO ; tempbuff += 8 ; timeout = 0x7FFFFF ; //读数据溢出时间 } //处理超时 else { if( timeout==0 ) return SD_DATA_TIMEOUT ; timeout -- ; } } //数据超时错误 if( SDIO->STA&( 1<<3 ) ) { SDIO->ICR |= 1<<3 ; //清错误标志 return SD_DATA_TIMEOUT ; } //数据块CRC错误 else if( SDIO->STA&( 1<<1 ) ) { SDIO->ICR |= 1<<1 ; //清错误标志 return SD_DATA_CRC_FAIL ; } //接收FIFO上溢错误 else if( SDIO->STA&( 1<<5 ) ) { SDIO->ICR |= 1<<5 ; //清错误标志 return SD_RX_OVERRUN ; } //接收起始位错误 else if( SDIO->STA&( 1<<9 ) ) { SDIO->ICR |= 1<<9 ; //清错误标志 return SD_START_BIT_ERR ; } //FIFO里面,还存在可用数据 while( SDIO->STA&( 1<<21 ) ) { *tempbuff = SDIO->FIFO ; //循环读取数据 tempbuff ++ ; } __asm volatile( "cpsie i" ) ; //开启总中断 SDIO->ICR = 0x5FF ; //清除所有标记 return errorstatus ;}/***************************************************Name :SD_ReadMultiBlocksFunction :读取多个块Paramater : buf:读数据缓存区(必须4字节对齐) addr:读取地址 blksize:块大小 nblks:要读取的块数Return :错误代码***************************************************/__align(4) u32 *tempbuff ;SD_Error SD_ReadMultiBlocks( u8 *buf, long long addr, u16 blksize, u32 nblks ){ SD_Error errorstatus = SD_OK ; u8 power ; u32 count = 0 ; u32 timeout = SDIO_DATATIMEOUT ; tempbuff = ( u32* )buf ; //转换为u32指针 SDIO->DCTRL = 0x0 ; //数据控制寄存器清零(关DMA) //大容量卡 if( CardType==SDIO_HIGH_CAPACITY_SD_CARD ) { blksize = 512 ; addr >>= 9 ; } SDIO_Send_Data_Cfg( SD_DATATIMEOUT, 0, 0, 0 ) ; //清除DPSM状态机配置 //卡锁了 if( SDIO->RESP1&SD_CARD_LOCKED ) return SD_LOCK_UNLOCK_FAILED ; if( ( blksize>0 )&&( blksize<=2048 )&&( ( blksize&( blksize-1 ) )==0 ) ) { power = convert_from_bytes_to_power_of_two( blksize ) ; SDIO_Send_Cmd( SD_CMD_SET_BLOCKLEN, 1, blksize ) ; //发送CMD16+设置数据长度为blksize,短响应 errorstatus = CmdResp1Error( SD_CMD_SET_BLOCKLEN ) ; //等待R1响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; } else return SD_INVALID_PARAMETER ; //多块读 if( nblks>1 ) { //判断是否超过最大接收长度 if( nblks*blksize>SD_MAX_DATA_LENGTH ) return SD_INVALID_PARAMETER ; SDIO_Send_Data_Cfg( SD_DATATIMEOUT, nblks*blksize, power, 1 ) ; //nblks*blksize,512块大小,卡到控制器 SDIO_Send_Cmd( SD_CMD_READ_MULT_BLOCK, 1, addr ) ; //发送CMD18+从addr地址出读取数据,短响应 errorstatus = CmdResp1Error( SD_CMD_READ_MULT_BLOCK ) ; //等待R1响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; __asm volatile( "cpsid i" ) ; //关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) //无上溢/CRC/超时/完成(标志)/起始位错误 while( !( SDIO->STA&( ( 1<<5 )|( 1<<1 )|( 1<<3 )|( 1<<8 )|( 1<<9 ) ) ) ) { //接收区半满,表示至少存了8个字 if( SDIO->STA&( 1<<15 ) ) { //循环读取数据 for( count=0; count<8; count++ ) *( tempbuff+count ) = SDIO->FIFO ; tempbuff += 8 ; timeout = 0X7FFFFF ; //读数据溢出时间 } //处理超时 else { if( timeout==0 ) return SD_DATA_TIMEOUT ; timeout -- ; } } //数据超时错误 if( SDIO->STA&( 1<<3 ) ) { SDIO->ICR |= 1<<3 ; //清错误标志 return SD_DATA_TIMEOUT; } //数据块CRC错误 else if( SDIO->STA&( 1<<1 ) ) { SDIO->ICR |= 1<<1 ; //清错误标志 return SD_DATA_CRC_FAIL ; } //接收fifo上溢错误 else if( SDIO->STA&( 1<<5 ) ) { SDIO->ICR |= 1<<5 ; //清错误标志 return SD_RX_OVERRUN; } //接收起始位错误 else if( SDIO->STA&( 1<<9 ) ) { SDIO->ICR |= 1<<9 ; //清错误标志 return SD_START_BIT_ERR ; } //FIFO里面,还存在可用数据 while( SDIO->STA&( 1<<21 ) ) { *tempbuff = SDIO->FIFO ; //循环读取数据 tempbuff ++ ; } //接收结束 if( SDIO->STA&( 1<<8 ) ) { if( ( SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType )||( SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType )||( SDIO_HIGH_CAPACITY_SD_CARD==CardType ) ) { SDIO_Send_Cmd( SD_CMD_STOP_TRANSMISSION, 1, 0 ) ; //发送CMD12+结束传输 errorstatus = CmdResp1Error( SD_CMD_STOP_TRANSMISSION ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; } } __asm volatile( "cpsie i" ) ; //开启总中断 SDIO->ICR = 0x5FF ; //清除所有标记 } return errorstatus ;}/***************************************************Name :IsCardProgrammingFunction :检查卡是否正在执行写操作Paramater : pstatus:当前状态Return :错误代码***************************************************/SD_Error IsCardProgramming( u8 *pstatus ){ volatile u32 respR1=0, status=0 ; SDIO_Send_Cmd( SD_CMD_SEND_STATUS, 1, ( u32 )RCA<<16 ) ; //发送CMD13 status = SDIO->STA ; while( !( status&( ( 1<<0 )|( 1<<6 )|( 1<<2 ) ) ) ) status = SDIO->STA ; //等待操作完成 //CRC检测失败 if( status&( 1<<0 ) ) { SDIO->ICR |= 1<<0 ; //清除错误标记 return SD_CMD_CRC_FAIL ; } //命令超时 if( status&( 1<<2 ) ) { SDIO->ICR |= 1<<2 ; //清除错误标记 return SD_CMD_RSP_TIMEOUT ; } if( SDIO->RESPCMD!=SD_CMD_SEND_STATUS ) return SD_ILLEGAL_CMD ; SDIO->ICR = 0X5FF ; //清除所有标记 respR1 = SDIO->RESP1 ; *pstatus = ( u8 )( ( respR1>>9 )&0x0000000F ) ; return SD_OK ;}/***************************************************Name :SD_WriteBlockFunction :写1个块Paramater : buf:数据缓存区 addr:写地址 blksize:块大小Return :错误代码***************************************************/SD_Error SD_WriteBlock( u8 *buf, long long addr, u16 blksize ){ SD_Error errorstatus = SD_OK ; u8 power=0, cardstate=0 ; u32 timeout=0, bytestransferred=0 ; u32 cardstatus=0, count=0, restwords=0 ; u32 tlen = blksize ; //总长度(字节) u32*tempbuff = ( u32* )buf ; //参数错误 if( buf==NULL ) return SD_INVALID_PARAMETER ; SDIO->DCTRL = 0x0 ; //数据控制寄存器清零(关DMA) SDIO_Send_Data_Cfg( SD_DATATIMEOUT, 0, 0, 0 ) ; //清除DPSM状态机配置 //卡锁了 if( SDIO->RESP1&SD_CARD_LOCKED ) return SD_LOCK_UNLOCK_FAILED ; //大容量卡 if( CardType==SDIO_HIGH_CAPACITY_SD_CARD ) { blksize = 512 ; addr >>= 9 ; } if( ( blksize>0 )&&( blksize<=2048 )&&( ( blksize&( blksize-1 ) )==0 ) ) { power = convert_from_bytes_to_power_of_two( blksize ) ; SDIO_Send_Cmd( SD_CMD_SET_BLOCKLEN, 1, blksize ) ; //发送CMD16+设置数据长度为blksize,短响应 errorstatus = CmdResp1Error( SD_CMD_SET_BLOCKLEN ) ; //等待R1响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; } else return SD_INVALID_PARAMETER ; SDIO_Send_Cmd( SD_CMD_SEND_STATUS, 1, ( u32 )RCA<<16 ) ; //发送CMD13,查询卡的状态,短响应 errorstatus = CmdResp1Error( SD_CMD_SEND_STATUS ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; cardstatus = SDIO->RESP1 ; timeout = SD_DATATIMEOUT ; //检查READY_FOR_DATA位是否置位 while( ( ( cardstatus&0x00000100 )==0 )&&( timeout>0 ) ) { timeout -- ; SDIO_Send_Cmd( SD_CMD_SEND_STATUS, 1, ( u32 )RCA<<16 ) ; //发送CMD13,查询卡的状态,短响应 errorstatus = CmdResp1Error( SD_CMD_SEND_STATUS ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; cardstatus = SDIO->RESP1 ; } if( timeout==0 ) return SD_ERROR ; SDIO_Send_Cmd( SD_CMD_WRITE_SINGLE_BLOCK, 1, addr ) ; //发送CMD24,写单块指令,短响应 errorstatus = CmdResp1Error( SD_CMD_WRITE_SINGLE_BLOCK ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; SDIO_Send_Data_Cfg( SD_DATATIMEOUT, blksize, power, 0 ) ; //blksize, 控制器到卡 timeout = SDIO_DATATIMEOUT ; __asm volatile( "cpsid i" ) ; //关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) //数据块发送成功/下溢/CRC/超时/起始位错误 while( !( SDIO->STA&( ( 1<<10 )|( 1<<4 )|( 1<<1 )|( 1<<3 )|( 1<<9 ) ) ) ) { //发送区半空,表示至少存了8个字 if( SDIO->STA&( 1<<14 ) ) { //不够32字节了 if( ( tlen-bytestransferred ) { restwords=( ( tlen-bytestransferred )%4==0 )?( ( tlen-bytestransferred )/4 ):( ( tlen-bytestransferred )/4+1 ) ; for( count=0; count4 ) SDIO->FIFO = *tempbuff ; } else { for( count=0; count<8; count++ ) SDIO->FIFO = *( tempbuff+count ) ; tempbuff += 8 ; bytestransferred += 32 ; } timeout = 0x3FFFFFFF ; //写数据溢出时间 } else { if( timeout==0 ) return SD_DATA_TIMEOUT ; timeout -- ; } } //数据超时错误 if( SDIO->STA&( 1<<3 ) ) { SDIO->ICR |= 1<<3 ; //清错误标志 return SD_DATA_TIMEOUT ; } //数据块CRC错误 else if( SDIO->STA&( 1<<1 ) ) { SDIO->ICR |= 1<<1 ; //清错误标志 return SD_DATA_CRC_FAIL ; } //接收fifo下溢错误 else if( SDIO->STA&( 1<<4 ) ) { SDIO->ICR |= 1<<4 ; //清错误标志 return SD_TX_UNDERRUN ; } //接收起始位错误 else if( SDIO->STA&( 1<<9 ) ) { SDIO->ICR |= 1<<9 ; //清错误标志 return SD_START_BIT_ERR ; } __asm volatile( "cpsie i" ) ; //开启总中断 SDIO->ICR = 0x5FF ; //清除所有标记 SDIO->ICR = 0x5FF ; //清除所有标记 errorstatus = IsCardProgramming( &cardstate ) ; while( ( errorstatus==SD_OK )&&( ( cardstate==SD_CARD_PROGRAMMING )||( cardstate==SD_CARD_RECEIVING ) ) ) errorstatus = IsCardProgramming( &cardstate ) ; return errorstatus ;}/***************************************************Name :SD_WriteMultiBlocksFunction :写多个块Paramater : buf:数据缓存区 addr:写地址 blksize:块大小 nblks:要写入的块数Return :错误代码***************************************************/SD_Error SD_WriteMultiBlocks( u8 *buf, long long addr, u16 blksize, u32 nblks ){ SD_Error errorstatus = SD_OK ; u8 power = 0, cardstate = 0 ; u32 timeout=0, bytestransferred=0 ; u32 count = 0, restwords = 0 ; u32 tlen= nblks*blksize ; //总长度(字节) u32 *tempbuff = ( u32* )buf ; //参数错误 if( buf==NULL ) return SD_INVALID_PARAMETER ; SDIO->DCTRL = 0x0 ; //数据控制寄存器清零(关DMA) SDIO_Send_Data_Cfg( SD_DATATIMEOUT, 0, 0, 0 ); //清除DPSM状态机配置 //卡锁了 if( SDIO->RESP1&SD_CARD_LOCKED ) return SD_LOCK_UNLOCK_FAILED ; //大容量卡 if( CardType==SDIO_HIGH_CAPACITY_SD_CARD ) { blksize = 512 ; addr >>= 9 ; } if( ( blksize>0 )&&( blksize<=2048 )&&( ( blksize&( blksize-1 ) )==0 ) ) { power = convert_from_bytes_to_power_of_two( blksize ) ; SDIO_Send_Cmd( SD_CMD_SET_BLOCKLEN, 1, blksize ) ; //发送CMD16+设置数据长度为blksize,短响应 errorstatus = CmdResp1Error( SD_CMD_SET_BLOCKLEN ) ; //等待R1响应 //响应错误 if( errorstatus!=SD_OK ) return errorstatus ; } else return SD_INVALID_PARAMETER ; if( nblks>1 ) { if( nblks*blksize>SD_MAX_DATA_LENGTH ) return SD_INVALID_PARAMETER ; if( ( SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType )||( SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType )||( SDIO_HIGH_CAPACITY_SD_CARD==CardType ) ) { //提高性能 SDIO_Send_Cmd( SD_CMD_APP_CMD, 1, ( u32 )RCA<<16 ); //发送ACMD55,短响应 errorstatus = CmdResp1Error( SD_CMD_APP_CMD ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; SDIO_Send_Cmd( SD_CMD_SET_BLOCK_COUNT, 1, nblks ) ; //发送CMD23,设置块数量,短响应 errorstatus = CmdResp1Error( SD_CMD_SET_BLOCK_COUNT ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; } SDIO_Send_Cmd( SD_CMD_WRITE_MULT_BLOCK, 1, addr ) ; //发送CMD25,多块写指令,短响应 errorstatus = CmdResp1Error( SD_CMD_WRITE_MULT_BLOCK ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; SDIO_Send_Data_Cfg( SD_DATATIMEOUT, nblks*blksize, power, 0 ) ; //blksize控制器到卡 timeout = SDIO_DATATIMEOUT ; __asm volatile( "cpsid i" ) ; //关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) //下溢/CRC/数据结束/超时/起始位错误 while( !( SDIO->STA&( ( 1<<4 )|( 1<<1) |( 1<<8 )|( 1<<3 )|( 1<<9 ) ) ) ) { //发送区半空,表示至少存了8字(32字节) if( SDIO->STA&( 1<<14 ) ) { //不够32字节了 if( ( tlen-bytestransferred ) { restwords = ( ( tlen-bytestransferred )%4==0 )?( ( tlen-bytestransferred )/4 ):( ( tlen-bytestransferred )/4+1 ); for( count=0; count4 ) SDIO->FIFO = *tempbuff ; } //发送区半空,可以发送至少8字(32字节)数据 else { for( count=0; count SDIO->FIFO = *( tempbuff+count ) ; tempbuff += SD_HALFFIFO ; bytestransferred += SD_HALFFIFOBYTES ; } timeout = 0x3FFFFFFF ; //写数据溢出时间 } else { if( timeout==0 ) return SD_DATA_TIMEOUT ; timeout -- ; } } //数据超时错误 if(SDIO->STA&(1<<3)) { SDIO->ICR |= 1<<3 ; //清错误标志 return SD_DATA_TIMEOUT ; } //数据块CRC错误 else if( SDIO->STA&( 1<<1 ) ) { SDIO->ICR |= 1<<1 ; //清错误标志 return SD_DATA_CRC_FAIL ; } //接收fifo下溢错误 else if( SDIO->STA&( 1<<4 ) ) { SDIO->ICR |= 1<<4 ; //清错误标志 return SD_TX_UNDERRUN ; } //接收起始位错误 else if( SDIO->STA&( 1<<9 ) ) { SDIO->ICR |= 1<<9 ; //清错误标志 return SD_START_BIT_ERR ; } //发送结束 if( SDIO->STA&( 1<<8 ) ) { if( ( SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType )||( SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType )||( SDIO_HIGH_CAPACITY_SD_CARD==CardType ) ) { SDIO_Send_Cmd( SD_CMD_STOP_TRANSMISSION, 1, 0 ) ; //发送CMD12+结束传输 errorstatus = CmdResp1Error( SD_CMD_STOP_TRANSMISSION ) ; //等待R1响应 if( errorstatus!=SD_OK ) return errorstatus ; } } __asm volatile( "cpsie i" ) ; //开启总中断 SDIO->ICR=0x5FF ; //清除所有标记 } SDIO->ICR = 0x5FF ; //清除所有标记 errorstatus = IsCardProgramming( &cardstate ) ; while( ( errorstatus==SD_OK )&&( ( cardstate==SD_CARD_PROGRAMMING )||( cardstate==SD_CARD_RECEIVING ) ) ) errorstatus = IsCardProgramming( &cardstate ) ; return errorstatus ; }/***************************************************Name :SD_ReadDiskFunction :读SD卡Paramater : buf:数据缓存区 sector:扇区地址 cnt:扇区个数Return :错误代码***************************************************/__align(4) u8 SDIO_DATA_BUFFER[ 512 ] ;u8 SD_ReadDisk( u8*buf, u32 sector, u8 cnt ){ u8 sta = SD_OK ; long long lsector = sector ; u8 n ; if( CardType!=SDIO_STD_CAPACITY_SD_CARD_V1_1 ) lsector <<= 9 ; if( ( ( u32 )buf%4 )!=0 ) { for( n=0; n { sta = SD_ReadBlock( SDIO_DATA_BUFFER, lsector+512*n, 512 ) ; //单个sector的读操作 memcpy( buf , SDIO_DATA_BUFFER , 512 ) ; buf += 512 ; } } else { if( cnt==1 ) sta = SD_ReadBlock( buf, lsector, 512 ) ; //单个sector的读操作 else sta = SD_ReadMultiBlocks( buf, lsector, 512, cnt ) ; //多个sector } return sta ;}/***************************************************Name :SD_WriteDiskFunction :写SD卡Paramater : buf:数据缓存区 sector:扇区地址 cnt:扇区个数Return :错误代码***************************************************/u8 SD_WriteDisk( u8*buf, u32 sector, u8 cnt ){ u8 sta = SD_OK ; u8 n ; long long lsector = sector ; if( CardType!=SDIO_STD_CAPACITY_SD_CARD_V1_1 ) lsector <<= 9 ; if( ( ( u32 )buf%4 )!=0 ) { for( n=0; n { memcpy( SDIO_DATA_BUFFER, buf, 512 ) ; sta = SD_WriteBlock( SDIO_DATA_BUFFER, lsector+512*n, 512 ) ; //单个sector的写操作 buf += 512 ; } } else { if( cnt==1 ) sta = SD_WriteBlock( buf, lsector, 512 ) ; //单个sector的写操作 else sta = SD_WriteMultiBlocks( buf, lsector, 512, cnt ) ; //多个sector } return sta ;}
(3)创建1.c文件,并输入以下代码。
#include "sys.h"#include "delay.h"#include "usart1.h"#include "lcd.h"#include "sdio_sdcard.h"int main(){ u8 Str[ 30 ] ; u16 temp ; STM32_Clock_Init( 9 ) ; //系统时钟设置 SysTick_Init( 72 ) ; //延时初始化 USART1_Init( 72, 115200 ) ; //串口初始化为115200 LCD_Init() ; //初始化LCD while( SD_Init() ) ; //初始化SD卡 temp = SDCardInfo.CardCapacity>>20 ; //单位换算 sprintf( ( char * )Str, "SD Size: %4d MB", temp ) ; LCD_ShowString( 30, 170, Str ) ; //显示SD卡容量 while(1) { }}
最后
以上就是灵巧帅哥为你收集整理的获取当前sd卡的总线宽度_STM32学习笔记20—SD卡基础读写实验的全部内容,希望文章能够帮你解决获取当前sd卡的总线宽度_STM32学习笔记20—SD卡基础读写实验所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复