当前位置: 首页 新闻资讯 技术问答

SPI模式下MCU对SD卡的控制及操作命令

SD NAND-贴片式TF卡-贴片式SD卡-免费测试2024-01-121766

一、前言

        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口数据,检查卡是否写保护和完全插入。


image

三、SD卡物理接口

我们看到的SD卡一包如下所示,包含9个引脚和一个写保护开关:

其引脚定义如下:

image

注: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××究竟是什么样的命令,本文最后会附上)

image

在复位成功之后可以通过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卡流程

image

(2)读SD卡流程

image

五、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

SPI方式驱动SD卡介绍

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卡版本: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,因为这个值不太大,占用内存不太多,适合单片机使用。

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,设置多扇区写入前预先擦除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信息

向SD卡发送命令的步骤介绍(SendSDCardCmd)

取消选中SD卡(SDCardCancelCS)

发送新的命令之前,需要取消之前的片选,额外发多 8个 CLK (发送0xFF无效数据),结束之前的操作。 (1). 取消片选 (2). 最多发送 8个 CLK


时序图

选中SD卡(SDCardSelectCS)

选中片选 (2).等待SD卡忙状态

说明: 向SD卡发送0xFF,如果SD卡也返回0xFF就表示SD卡已经准备好,如果返回不是0xFF就表示SD卡还没有准备好,需要等待。


时序图

向SD卡发送操作命令cmd

将要发送的命令 |0x40 发给SD卡。 示例: cmd | 0x40

命令是8位数据。


时序图

向SD卡发送命令参数

命令参数是32位数据,SPI每次发送8位,需要发送4次,先发送最高8位,依次再发送低位。


时序图

发送CRC校验

CRC是8位数据。

注意: 如果发送的是CMD12命令(停止数据传输),在发送CRC校验之后,需要再发送一个0xFF数据。


时序图


时序图

等待SD卡响应

向SD卡发送0xFF数据,如果SD卡返回的数据最高位为0,就是表示SD卡响应完成,否则就继续发送0xFF,再判断,直到SD卡响应成功。

SD卡响应之后,完成一次命令发送,并将返回的数据当做返回值返回去

推荐: 使用do{…}while()循环结构,更加方便判断。


时序图

SD卡的寄存器与操作命令介绍

SDCard_CMD0:卡复位命令


时序图

SDCard_CMD8:检测是否是2.0版本的SD卡

发送只有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 寄存器的结构描述:


时序图

 SDCard_CMD9: 获取SD卡的CSD信息

CSD包括容量和速度信息,存放CID的内存,至少16Byte

CMD9的命令:


SDCard_CMD17: 设置单个读取的扇区


SDCard_CMD18: 设置读扇区(连续读扇区使用)


SDCard_CMD12: 停止数据传输


SDCard_CMD24: 设置写单个扇区


SDCard_CMD55


SDCard_CMD23: 多扇区写入前预先擦除块数量


SDCard_CMD25: 设置写多个扇区


SDCard_CMD41


SDCard_CMD58


SD卡SPI接口命令

SPI接口时序


数据先发/先收高位

从图上得知(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;}

SPI模式下: SD卡初始化步骤(SDCardDeviceInit)

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个时钟信号,结束本次操作。

SPI模式下: 向SD卡发送数据包步骤(SDCardSendData)

每次发送数据包默认为512字节。

1. 等待SD卡忙状态

向SD卡发送一个0xFF数据,如果SD卡也原路返回0xFF就表示SD卡处于闲置状态。

2. 发送(开始传输)/(结束传输)的标志

写一个扇区的情况下发送0xFE开始传输数据。

写多个扇区的情况下发送0xFC开始传输数据。

写多个扇区的情况下,连续发送数据完成之后,发送0xFD结束数据发送。

3. 如果不是结束标志(0xFD),就是表示发送的是正常的数据包,就进行循环发送512字节的数据。

注意: 每次发送数据包的单位是按扇区为单位的,也就是512字节,一包数据长度固定为512字节。

4. 数据发送完之后,再接着发送0xFF忽略CRC校验(连续发送3个0xFF)。

 SPI模式下: 从SD卡读取数据包步骤(SDCardRecvData)

等待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,完成写入。


取消片选

 SPI模式下: 从SD卡读取指定扇区数据(SDCardReadData)

读取一个扇区的步骤:

1、 发送CM17命令,设置读取的扇区

2、 接着进行接收SD卡返回的数据包。(参考5.4小节)

每次固定接收512字节,以扇区为单位

3、 取消片选,完成数据读取

说明: 取消片选之后,需要再额外发送8个时钟信号,结束本次操作

读取多个扇区的步骤:

1、 发送CMD18命令,设置读取的扇区(连续读多个扇区使用)

2、 接着循环接收SD卡返回的数据包。(参考5.4小节)

每次固定接收512字节,以扇区为单位

3、 发送CMD12指令,停止数据传输

4、 取消片选,完成数据读取

说明: 取消片选之后,需要再额外发送8个时钟信号,结束本次操作

5.7 SPI模式下: 获取SD卡的总扇区数(GetSDCardSectorCount)

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;//得到总扇区数

示例代码

sdcard.c文件

#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个时钟

sdcard.h文件

#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

热门标签:SDNAND贴片式TF卡贴片式SD卡SD FLASHSLC NANDNAND FLASH


SD NAND-贴片式TF卡-贴片式SD卡-免费测试

深圳市芯存者科技有限公司

售前咨询
售前咨询
售后服务
售后服务
联系我们

电话:176-6539-0767

Q Q:135-0379-986

邮箱:1350379986@qq.com

地址:深圳市南山区蛇口街道后海大道1021号B C座C422W8

在线客服 在线客服 QQ客服 微信客服 淘宝店铺 联系我们 返回顶部