一、前言
SD 卡有两个可选的通讯协议:SD 模式和 SPI模式 SD 模式是SD 卡标准的读写方式,但是在选用SD 模式时,往往需要选择带有SD 卡控制器接口的 MCU,或者必须加入额外的SD卡控制单元以支持SD 卡的读写 然而,大多数MCU都没有集成SD 卡控制器接口,若选用SD 模式通讯就无形中增加了产品的硬件成本。在SD卡数据读写时间要求不是很严格的情况下, 选用 SPI模式可以说是一种最佳的解决方案 因为在 SPI模式下,通过四条线就可以完成所有的数据交换,并且目前市场上很多MCU都集成 有现成的SPI接口电路,采用 SPI模式对 SD卡进行读写操作可大大简化硬件电路的设计
二、硬件电路实现
以NXP的LPC2210 ARM7MCU为例,下图是周立功开发的实现板电路
这里,将LPC2210MCU的SPI0用于SD卡的控制和数据读写。对SPI0的两个数据线加了上拉电阻以便于MMC卡兼容。
卡供电采用了可控方式,通过GPIO口控制MOS管对其进行供电。
卡检测电路也使用GPIO口实现。通过读GPIO口数据,检查卡是否写保护和完全插入。
三、SD卡物理接口
我们看到的SD卡一包如下所示,包含9个引脚和一个写保护开关:
其引脚定义如下:
注:1. S:电源;I:输入;O:推挽输出;PP:推挽I/O。
2. 扩展的DAT线(DAT1 ~ DAT3)在上电后处于输入状态。它们在执行SET_BUS_WIDTH命令后作为DAT线操作。当不使用DAT1 ~ DAT3 线时,主机应使自己的DAT1~DAT3线处于输入模式。这样定义是为了与MMC卡保持兼容。
3. 上电后,这条线为带 50KΩ上拉电阻的输入线(可以用于检测卡是否存在或选择 SPI 模式) 。用户可以在正常的数据传输中用 SET_CLR_CARD_DETECT(ACMD42)命令断开上拉电阻的连接。MMC卡的该引脚在SD模式下为保留引脚,在SD模式下无任何作用。
4. MMC卡在SD模式下为:I/O/PP/OD。
5. MMC卡在SPI模式下为:I/PP。
四、对SD卡的控制流程
1、SD卡的SPI工作模式
SD 卡在上电初期自动进入SD 总线模式,在此模式下向 SD 卡发送复位命令CMD0 。如果SD卡在接收复位命令过程中CS低电平有效,则进入SPI模式,否则工作在SD 总线模式。
下边是插入SD卡,并初始化为SPI模式的流程图:(至于CMD××究竟是什么样的命令,本文最后会附上)
在复位成功之后可以通过CMD55和ACMD41 判断当前电压是否在工作范围内 主机还可以继续通过CMD10读取SD 卡的CID寄存器,通过CMD16 设置数据 Block长度,通过CMD9 读取卡的 CSD寄存器 从CSD 寄存器中,主机可获知卡容量,支持的命令集等重要参数。
2、数据块的读写
完成SD 卡的初始化之后即可进行它的读写操作 SD卡的读写操作都是通过发送 SD 卡命令完成的SPI总线模式支持单块(CMD24)和多块(CMD25)写操作,多块操作是指从指定位置开始写下去,直到SD 卡收到一个停止命令CMD12才停止 单块写操作的数据块长度只能是512 字节 单块写入时,命令为CMD24,当应答为0时说明可以写入数据,大小为512 字节 SD 卡对每个发送给自己的数据块都通过一个应答命令确认,它为1个字节长,当低 5位为00101 时,表明数据块被正确写入SD 卡 在需要读取SD 卡中数据的时候,读SD卡的命令字为CMD17,接收正确的第一个响应命令字节为0xFE,随后是512 个字节的用户数据块,最后为2 个字节的CRC验证码 可见,读写SD 卡的操作都是在初始化后基于 SD 卡命令和响应完成操作的,写、读 SD 卡的程序流程图如下所示 :
(1)写SD卡流程
(2)读SD卡流程
五、SD卡的操作命令集合
对SD卡的操作就靠这些命令来实现的。一下命令来自周立功的SD/MMC中间件。我查了好多地方都只显示CMD0、CMD1之类的东西,而没人说这些东西是什么。就贴到这里供参考,这也是我写这个博客的目的,因为这些命令我找了好久的。关于SD,我一句操作代码都没贴。。。
/* 命令响应定义 define command's response */
#define R1 1
#define R1B 2
#define R2 3
#define R3 4
/**********************************************
SD卡SPI模式下命令集
**********************************************/
/******************************** 基本命令集 Basic command set **************************/
/* 复位SD 卡 Reset cards to idle state */
#define CMD0 0
#define CMD0_R R1
/* 读OCR寄存器 Read the OCR (MMC mode, do not use for SD cards) */
#define CMD1 1
#define CMD1_R R1
/* 读CSD寄存器 Card sends the CSD */
#define CMD9 9
#define CMD9_R R1
/* 读CID寄存器 Card sends CID */
#define CMD10 10
#define CMD10_R R1
/* 停止读多块时的数据传输 Stop a multiple block (stream) read/write operation */
#define CMD12 12
#define CMD12_R R1B
/* 读 Card_Status 寄存器 Get the addressed card's status register */
#define CMD13 13
#define CMD13_R R2
/***************************** 块读命令集 Block read commands **************************/
/* 设置块的长度 Set the block length */
#define CMD16 16
#define CMD16_R R1
/* 读单块 Read a single block */
#define CMD17 17
#define CMD17_R R1
/* 读多块,直至主机发送CMD12为止 Read multiple blocks until a CMD12 */
#define CMD18 18
#define CMD18_R R1
/***************************** 块写命令集 Block write commands *************************/
/* 写单块 Write a block of the size selected with CMD16 */
#define CMD24 24
#define CMD24_R R1
/* 写多块 Multiple block write until a CMD12 */
#define CMD25 25
#define CMD25_R R1
/* 写CSD寄存器 Program the programmable bits of the CSD */
#define CMD27 27
#define CMD27_R R1
/***************************** 写保护 Write protection *****************************/
/* Set the write protection bit of the addressed group */
#define CMD28 28
#define CMD28_R R1B
/* Clear the write protection bit of the addressed group */
#define CMD29 29
#define CMD29_R R1B
/* Ask the card for the status of the write protection bits */
#define CMD30 30
#define CMD30_R R1
/***************************** 擦除命令 Erase commands *******************************/
/* 设置擦除块的起始地址(只用于SD卡) Set the address of the first write block to be erased(only for SD) */
#define CMD32 32
#define CMD32_R R1
/* 设置擦除块的终止地址(只用于SD卡) Set the address of the last write block to be erased(only for SD) */
#define CMD33 33
#define CMD33_R R1
/* 设置擦除块的起始地址(只用于MMC卡) Set the address of the first write block to be erased(only for MMC) */
#define CMD35 35
#define CMD35_R R1
/* 设置擦除块的终止地址(只用于MMC卡) Set the address of the last write block to be erased(only for MMC) */
#define CMD36 36
#define CMD36_R R1
/* 擦除所选择的块 Erase the selected write blocks */
#define CMD38 38
#define CMD38_R R1B
/***************************** 锁卡命令 Lock Card commands ***************************/
/* 设置/复位密码或上锁/解锁卡 Set/reset the password or lock/unlock the card */
#define CMD42 42
#define CMD42_R R1B
/* Commands from 42 to 54, not defined here */
/***************************** 应用命令 Application-specific commands ****************/
/* 禁止下一个命令为应用命令 Flag that the next command is application-specific */
#define CMD55 55
#define CMD55_R R1
/* 应用命令的通用I/O General purpose I/O for application-specific commands */
#define CMD56 56
#define CMD56_R R1
/* 读OCR寄存器 Read the OCR (SPI mode only) */
#define CMD58 58
#define CMD58_R R3
/* 使能或禁止 CRC Turn CRC on or off */
#define CMD59 59
#define CMD59_R R1
/***************************** 应用命令 Application-specific commands ***************/
/* 获取 SD Status寄存器 Get the SD card's status */
#define ACMD13 13
#define ACMD13_R R2
/* 得到已写入卡中的块的个数 Get the number of written write blocks (Minus errors ) */
#define ACMD22 22
#define ACMD22_R R1
/* 在写之前,设置预先擦除的块的个数 Set the number of write blocks to be pre-erased before writing */
#define ACMD23 23
#define ACMD23_R R1
/* 读取OCR寄存器 Get the card's OCR (SD mode) */
#define ACMD41 41
#define ACMD41_R R1
/* 连接/断开CD/DATA[3]引脚上的上拉电阻 Connect or disconnect the 50kOhm internal pull-up on CD/DAT[3] */
#define ACMD42 42
#define ACMD42_R R1
/* 读取SCR寄存器 Get the SD configuration register */
#define ACMD51 51
#define ACMD51_R R1
SD卡的SPI通信接口使其可以通过SPI通道进行数据读写。
从应用的角度来看,采用SPI接口的好处在于,很多单片机内部自带SPI控制器,不光给开发上带来方便,同时也见降低了开发成本。然而,它也有不好的地方,如失去了SD卡的性能优势,要解决这一问题,就要用SD方式,因为它提供更大的总线数据带宽。SPI接口的选用是在上电初始时向其写入第一个命令时进行的。以下介绍SD卡的驱动方法,只实现简单的扇区读写。
根据当前所用开发板原理图为例,SD卡卡槽的接口与STM32 IO口对应如下:
PC11 片选 SDCardCS PC12 时钟 SDCardSCLK PD2 输出 SPI_MOSI--主机输出从机输入 PC8 输入 SPI_MISO--主机输入从机输出 SD卡与开发板的SPI方式接线关系如下:DATA0---PC8-----OUT---MISO---主机输入从机输出 DATA1---PC9 DATA2---PC10 DATA3---PC11----CS CLK-----PC12-------SCLK CMD-----PD2------INPUT--MOSI--主机输出从机输入
SD卡版本:SD V1.X(即SD标准卡)最大容量2GB
SD V2.0 2.0版本的标准卡,最多2GB
SD V2.0HC 2.0高容量卡,最多32GB
说明: 本程序主要针对SD卡2.0 HC 2.0高容量卡协议进行说明。
SD卡默认操作的扇区大小是512字节。扇区大小,可以通过指令设置。就算不是512,也可以通过指令设置成512,因为这个值不太大,占用内存不太多,适合单片机使用。
#define SDCard_CMD0 0 //卡复位 #define SDCard_CMD8 8 //命令8 ,SEND_IF_COND #define SDCard_CMD9 9 //命令9 ,读CSD数据 #define SDCard_CMD12 12 //命令12,停止数据传输 #define SDCard_CMD13 16 //命令16,设置扇区大小 应返回0x00#define SDCard_CMD17 17 //命令17,读扇区 #define SDCard_CMD18 18 //命令18,读多个扇区 #define SDCard_CMD23 23 //命令23,设置多扇区写入前预先擦除block#define SDCard_CMD24 24 //命令24,写扇区#define SDCard_CMD25 25 //命令25,写多个扇区 #define SDCard_CMD41 41 //命令41,应返回0x00#define SDCard_CMD55 55 //命令55,应返回0x01 #define SDCard_CMD58 58 //命令58,读OCR信息
发送新的命令之前,需要取消之前的片选,额外发多 8个 CLK (发送0xFF无效数据),结束之前的操作。 (1). 取消片选 (2). 最多发送 8个 CLK
时序图
选中片选 (2).等待SD卡忙状态
说明: 向SD卡发送0xFF,如果SD卡也返回0xFF就表示SD卡已经准备好,如果返回不是0xFF就表示SD卡还没有准备好,需要等待。
时序图
将要发送的命令 |0x40 发给SD卡。 示例: cmd | 0x40
命令是8位数据。
时序图
命令参数是32位数据,SPI每次发送8位,需要发送4次,先发送最高8位,依次再发送低位。
时序图
CRC是8位数据。
注意: 如果发送的是CMD12命令(停止数据传输),在发送CRC校验之后,需要再发送一个0xFF数据。
时序图
时序图
向SD卡发送0xFF数据,如果SD卡返回的数据最高位为0,就是表示SD卡响应完成,否则就继续发送0xFF,再判断,直到SD卡响应成功。
SD卡响应之后,完成一次命令发送,并将返回的数据当做返回值返回去。
推荐: 使用do{…}while()循环结构,更加方便判断。
时序图
时序图
发送只有V2.0版的SD卡才具有的命令CMD8,然后检测返回值: 返回值若是0x01,则表示此卡为V2.0卡,然后再发送循环命令CMD55+CMD41,直到返回0x00,确定SD2.0卡初始化成功;
然后再发送CMD58命令,接收返回的OCR寄存器的数据,其中第31位用于判断V2.0的卡是否为SDHC类型。
若返回值不为0x01,则进一步判断是V1.0卡还是MMC卡:先发送循环命令CMD55+CMD41进行复位,如果复位不成功则考虑是MMC卡,如果复位成功,则为V1.0卡。在复位不成功的情况下,再使用CMD1进行复位,如果复位成功,则表明是MMC卡,如果复位不成功,则表示是无法识别的卡。
时序图
时序图
时序图
时序图
鉴别到v2.0版本之后,可以读取OCR 寄存器的值,继续判断是否是V2.0高速卡。
SD卡响应命令成功,可以继续接收4字节的OCR寄存器值;
OCR寄存器的第30位(CCS)指示了卡的类型是否为SDHC,此位为1则为SDHC,为0则为SDSC。
OCR 寄存器,储存了卡的 VDD 电压轮廓图。任何标准的 SD 卡主控制器可以使用 2V 至 3.6V 的工作电压来让 SD 卡能执行这个电压识别操作(CMD1)。而访问存储器的阵列操作无论如何都需要 2.7V 至 3.6V 的工作电压。OCR 寄存器显示了在访问卡的数据时所需要的电压范围。
OCR 寄存器的结构描述:
时序图
CSD包括容量和速度信息,存放CID的内存,至少16Byte
CMD9的命令:
数据先发/先收高位
从图上得知(SD卡在SPI模式下): 下降沿写数据、时钟的上升沿读数据。
示例:
u8 SDCardReadWriteOneByte(u8 DataTx){ u8 i; u8 data=0; for(i=0;i<8;i++) { SDCARD_SCK=0; if(DataTx&0x80)SDCARD_MOSI=1; else SDCARD_MOSI=0; SDCARD_SCK=1; DataTx<<=1; data<<=1; if(SDCARD_MISO)data|=0x01; } return data;}
SD卡的初始化是非常重要的,只有进行了正确的初始化,才能进行后面的各项操作。在初始化过程中,SPI的时钟不能太快,否则会造初始化失败。在初始化成功后,应尽量提高SPI的速率。在刚开始要先发送至少74个时钟信号,这是必须的。如果接到复位命令(CMD0)时,CS信号有效(低电平),SPI模式启用。
详细步骤:
1. 初始化与 SD卡连接的硬件条件(MCU的 SPI配置,IO口配置等等) 2. 向总线最少发送74个脉冲,为了让SD卡正常启动 (唤醒SD卡) (解释: 就是时钟线至少需要74个跳变,向MOSI发送0xFF数据即可,这是无效数据) 3. 复位卡(CMD0),进入 IDLE(闲置)状态。 说明: 最后的返回值等于0x01就表示复位成功。 4. 发送 CMD8,检查是否支持 2.0协议,因为这个命令是在2.0的协议里面才添加的 说明: 发送 CMD8命令之后,返回值等于0x01表示就是2.0版本的SD卡。 5. 如果是2.0版本的SD卡,就需要循环发送CMD55+ CMD41命令等待2.0卡初始化成功,如果CMD41命令的返回值等于0就表示卡复位成功。(先发CMD55,再发CMD41) 6. 2.0卡初始化成功后,再发送CMD58命令,继续判断是否是高速卡。 说明: CMD58命令返回值等于0,表示执行成功。然后就可以读取4字节的OCR 寄存器的值。OCR寄存器的第30位(CCS)指示了卡的类型是否为SDHC,此位为1则为SDHC,为0则为SDSC。 如果只是为了判断是否是高速卡,可以只读取1个字节数据即可,因为SD返回的数据先返回的是高位数据(24~31),后面的数据可以不读取。 7. 取消片选,结束初始化。 说明: 取消片选之后,需要再额外发送8个时钟信号,结束本次操作。
每次发送数据包默认为512字节。
1. 等待SD卡忙状态 向SD卡发送一个0xFF数据,如果SD卡也原路返回0xFF就表示SD卡处于闲置状态。 2. 发送(开始传输)/(结束传输)的标志 写一个扇区的情况下发送0xFE开始传输数据。 写多个扇区的情况下发送0xFC开始传输数据。 写多个扇区的情况下,连续发送数据完成之后,发送0xFD结束数据发送。 3. 如果不是结束标志(0xFD),就是表示发送的是正常的数据包,就进行循环发送512字节的数据。 注意: 每次发送数据包的单位是按扇区为单位的,也就是512字节,一包数据长度固定为512字节。 4. 数据发送完之后,再接着发送0xFF忽略CRC校验(连续发送3个0xFF)。
等待SD卡发回数据起始令牌0xFE向SD卡发送0xFF,如果SD卡返回0xFE就表示等待成功。
收到返回的数据起始令牌之后就可以连续读取数据了(接收的数量以传入的cnt为准),读完数据发送两个伪CRC
SPI模式下: 向SD卡指定扇区写数据(SDCardWriteData)
封装的函数原型: SDCardWriteData(u8*buf,u32 sector,u32 cnt)
写一个扇区步骤:
1、发送CMD24命令,设置要写的扇区。(写单个扇区使用CMD24命令)
2、接着向SD卡写数据包(参考5.3小节)。
写多个扇区的步骤:
1、发送CMD55命令(正常应返回0x01)
2、 发送CMD23命令(设置多扇区写入前预先擦除N个block)---写入的数量
3、 发送CMD25命令,设置写入的扇区位置。(设置写多个扇区)
4、 接着向SD卡写数据包(参考5.3小节)。
写结束指令0xFD,完成写入。
取消片选
读取一个扇区的步骤:
1、 发送CM17命令,设置读取的扇区
2、 接着进行接收SD卡返回的数据包。(参考5.4小节)
每次固定接收512字节,以扇区为单位。
3、 取消片选,完成数据读取
说明: 取消片选之后,需要再额外发送8个时钟信号,结束本次操作。
读取多个扇区的步骤:
1、 发送CMD18命令,设置读取的扇区(连续读多个扇区使用)
2、 接着循环接收SD卡返回的数据包。(参考5.4小节)
每次固定接收512字节,以扇区为单位。
3、 发送CMD12指令,停止数据传输
4、 取消片选,完成数据读取
说明: 取消片选之后,需要再额外发送8个时钟信号,结束本次操作。
1、 发送CMD9命令,读取CSD信息
2、 连续接收16个字节数据包。(参考5.4小节)
3、 取消片选,完成读取
4、 判断是否是v2.0 SDHC高速卡。
使用读取的第一个字节数据csd[0]&0xC0判断是否等于0x40,如果等于0x40就是v2.0高速卡。
5、 如果是v2.0 SDHC高速卡就按照以下公式计算得到扇区数量
csize=csd[9]+(csd[8]<<8)+1; Capacity=csize<<10;//得到总扇区数
#include "sdcard.h" /* 函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节 函数参数:data是要写入的数据 返 回 值:读到的数据 */u8 SDCardReadWriteOneByte(u8 DataTx){ u8 i; u8 data=0; for(i=0;i<8;i++) { SDCARD_SCK=0; if(DataTx&0x80)SDCARD_MOSI=1; else SDCARD_MOSI=0; SDCARD_SCK=1; DataTx<<=1; data<<=1; if(SDCARD_MISO)data|=0x01; } return data;}//4种: 边沿两种、电平是两种/* 函数功能:底层SD卡接口初始化 本程序SPI接口如下: PC11 片选 SDCardCS PC12 时钟 SDCardSCLK PD2 输出 SPI_MOSI--主机输出从机输入 PC8 输入 SPI_MISO--主机输入从机输出 */void SDCardSpiInit(void){ /*1. 开启时钟*/ RCC->APB2ENR|=1<<5; //使能PORTD时钟 RCC->APB2ENR|=1<<4; //使能PORTC时钟 /*2. 配置GPIO口模式*/ GPIOC->CRH&=0xFFF00FF0; GPIOC->CRH|=0x00033008; GPIOD->CRL&=0xFFFFF0FF; GPIOD->CRL|=0x00000300; /*3. 上拉*/ GPIOC->ODR|=1<<8; GPIOC->ODR|=1<<11; GPIOC->ODR|=1<<12; GPIOD->ODR|=1<<2;}/* 函数功能:从sd卡读取一个数据包的内容 函数参数: buf:数据缓存区 len:要读取的数据长度. 返回值: 0,成功;其他,失败; */u8 SDCardRecvData(u8*buf,u16 len){ while(SDCardReadWriteOneByte(0xFF)!=0xFE){}//等待SD卡发回数据起始令牌0xFE while(len--)//开始接收数据 { *buf=SDCardReadWriteOneByte(0xFF); buf++; } //下面是2个伪CRC(dummy CRC) SDCardReadWriteOneByte(0xFF); SDCardReadWriteOneByte(0xFF); return 0;//读取成功}/* 函数功能:向sd卡写入一个数据包的内容 512字节 函数参数: buf 数据缓存区 cmd 指令 返 回 值:0表示成功;其他值表示失败; */u8 SDCardSendData(u8*buf,u8 cmd){ u16 t; while(SDCardReadWriteOneByte(0xFF)!=0xFF){} //等待忙状态 SDCardReadWriteOneByte(cmd); if(cmd!=0xFD)//不是结束指令 { for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,减少函数传参时间 SDCardReadWriteOneByte(0xFF); //忽略crc SDCardReadWriteOneByte(0xFF); SDCardReadWriteOneByte(0xFF); //接收响应 } return 0;//写入成功}/* 函数功能:向SD卡发送一个命令 函数参数: u8 cmd 命令 u32 arg 命令参数 u8 crc crc校验值 返回值:SD卡返回的响应 */ u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc){ u8 r1; SDCARD_CS=1; //取消上次片选 SDCardReadWriteOneByte(0xff);//提供额外的8个时钟 SDCARD_CS=0; //选中SD卡 while(SDCardReadWriteOneByte(0xFF)!=0xFF){};//等待成功 //发送数据 SDCardReadWriteOneByte(cmd | 0x40);//分别写入命令 SDCardReadWriteOneByte(arg >> 24); SDCardReadWriteOneByte(arg >> 16); SDCardReadWriteOneByte(arg >> 8); SDCardReadWriteOneByte(arg); SDCardReadWriteOneByte(crc); if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading do { r1=SDCardReadWriteOneByte(0xFF); }while(r1&0x80); //等待响应,或超时退出 return r1;}/* 函数功能:获取SD卡的总扇区数(扇区数) 返 回 值: 0表示容量检测出错,其他值表示SD卡的容量(扇区数/512字节) 说 明: 每扇区的字节数必为512字节,如果不是512字节,则初始化不能通过. */u32 GetSDCardSectorCount(void){ u8 csd[16]; u32 Capacity=0; u16 csize; //获取SD卡的CSD信息,包括容量和速度信息,存放CID的内存,至少16Byte SendSDCardCmd(SDCard_CMD9,0,0x01);//发SDCard_CMD9命令,读CSD SDCardRecvData(csd,16);//接收16个字节的数据 SDCARD_CS=1;//取消片选 SDCardReadWriteOneByte(0xff);//提供额外的8个时钟 if((csd[0]&0xC0)==0x40) //SDHC卡,按照下面方式计算 { csize=csd[9]+(csd[8]<<8)+1; Capacity=csize<<10;//得到扇区数 } return Capacity;}/* 函数功能: 初始化SD卡 返 回 值: 非0表示初始化失败! */u8 SDCardDeviceInit(void){ u8 r1=0; // 存放SD卡的返回值 u8 data; u16 i; /*1. 初始化底层IO口*/ SDCardSpiInit(); /*2. 发送最少74个脉冲*/ for(i=0;i<10;i++)SDCardReadWriteOneByte(0xFF); /*3. 进入闲置状态*/ do { r1=SendSDCardCmd(SDCard_CMD0,0,0x95); }while(r1!=0x01); /*4. 鉴别是否是2.0版本的SD卡*/ if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==0x01) { do { SendSDCardCmd(SDCard_CMD55,0,0x01); //发送SDCard_CMD55 r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0x01);//发送SDCard_CMD41 }while(r1); if(SendSDCardCmd(SDCard_CMD58,0,0x01)==0)//鉴别SD2.0卡版本开始 { data=SDCardReadWriteOneByte(0xFF);//得到OCR值 if(data&0x40) { r1=0; //高速卡 } } } SDCARD_CS=1;//取消片选 SDCardReadWriteOneByte(0xff);//提供额外的8个时钟 return r1; //其他错误}/* 函数功能:读SD卡 函数参数: buf:数据缓存区 sector:扇区 cnt:扇区数 返回值: 0,ok;其他,失败. 说 明: SD卡一个扇区大小512字节 */void SDCardReadData(u8*buf,u32 sector,u32 cnt){ u32 i=0; if(cnt==1) { SendSDCardCmd(SDCard_CMD17,sector,0x01);//读扇区 SDCardRecvData(buf,512); //接收512个字节 } else { SendSDCardCmd(SDCard_CMD18,sector,0x01);//连续读命令 for(i=0;i<cnt;i++) { SDCardRecvData(buf,512);//接收512个字节 buf+=512; } SendSDCardCmd(SDCard_CMD12,0,0x01); //停止数据传输 } SDCARD_CS=1;//取消片选 SDCardReadWriteOneByte(0xff);//提供额外的8个时钟();}/* 函数功能:向SD卡写数据 函数参数: buf:数据缓存区 sector:起始扇区 cnt:扇区数 说 明: SD卡一个扇区大小512字节 */void SDCardWriteData(u8*buf,u32 sector,u32 cnt){ u32 i=0; if(cnt==1) { SendSDCardCmd(SDCard_CMD24,sector,0x01);//写单个扇区 SDCardSendData(buf,0xFE);//写512个字节 } else { SendSDCardCmd(SDCard_CMD55,0,0x01); SendSDCardCmd(SDCard_CMD23,cnt,0x01); //设置多扇区写入前预先擦除N个block SendSDCardCmd(SDCard_CMD25,sector,0x01);//写多个扇区 for(i=0;i<cnt;i++) { SDCardSendData(buf,0xFC);//写512个字节 buf+=512; } SDCardSendData(0,0xFD);//写结束指令 } SDCARD_CS=1; SDCardReadWriteOneByte(0xff);//提供额外的8个时钟
#ifndef SD_H_#define SD_H_ #include "stm32f10x.h"#include "led.h" #include "key.h" #include "usart.h"/*---------------------------------------------- 本程序SPI接口如下: PC11 片选 SDCardCS PC12 时钟 SDCardSCLK PD2 输出 SPI_MOSI--主机输出从机输入 PC8 输入 SPI_MISO--主机输入从机输出 ------------------------------------------------*/ #define SDCARD_CS PCout(11) #define SDCARD_SCK PCout(12) #define SDCARD_MOSI PDout(2) #define SDCARD_MISO PCin(8)// SD卡指令表 #define SDCard_CMD0 0 //卡复位 #define SDCard_CMD8 8 //命令8 ,SEND_IF_COND #define SDCard_CMD9 9 //命令9 ,读CSD数据 #define SDCard_CMD12 12 //命令12,停止数据传输// #define SDCard_CMD13 16 //命令16,设置扇区大小 应返回0x00 #define SDCard_CMD17 17 //命令17,读扇区 #define SDCard_CMD18 18 //命令18,读多个扇区 #define SDCard_CMD23 23 //命令23,设置多扇区写入前预先擦除N个block #define SDCard_CMD24 24 //命令24,写扇区 #define SDCard_CMD25 25 //命令25,写多个扇区 #define SDCard_CMD41 41 //命令41,应返回0x00 #define SDCard_CMD55 55 //命令55,应返回0x01 #define SDCard_CMD58 58 //命令58,读OCR信息 //函数声明 u8 SDCardReadWriteOneByte(u8 data);//底层接口,SPI读写字节函数 u8 SDCardDeviceInit(void);//初始化void SDCardReadData(u8*buf,u32 sector,u32 cnt);//读块(扇区) void SDCardWriteData(u8*buf,u32 sector,u32 cnt);//写块(扇区) u8 SDCardSendData(u8*buf,u8 cmd);//发送数据包 u8 SDCardRecvData(u8*buf,u16 len);//接收数据包 u32 GetSDCardSectorCount(void);//读扇区数 #endif
上一篇:SD卡引脚说明
下一篇:关于SD卡挂载失败解决方法