单片机驱动大容量sd卡_51单片机驱动sd卡复位错误 - CSDN
  • SD卡驱动

    2019-11-21 10:09:47
    SD 主要分为两个版本,有1.x版和现在普遍使用的2.0 版,2.0 版是为了适应大容量SD 提出的标准,这个标准把SD 分为SDSC 和SDHC 。其中SDSC 为标准容量,容量最大不超过2G ;超过2G 的都属于高容量的SDHC...

    注:文中提及的寄存器都是STM32H7的寄存器。其他单片机操作同理。

     

    一、介绍

    1、SD卡种类

            SD 卡主要分为两个版本,有1.x版和现在普遍使用的2.0 版,2.0 版是为了适应大容量SD 卡提出的标准,这个标准把SD
    卡分为SDSC 卡和SDHC 卡。其中SDSC 卡为标准容量,容量最大不超过2G ;超过2G 的都属于高容量的SDHC 卡。

    注:SDHC卡最大只能到32G,所以又出现了SDXC卡,最大支持到2T,SDXC可以支持300MB/S的速度,现在支持设备并不多,而且SDXC不可向下兼容,不支持普通的SD和SDHC卡槽和读卡器,故本文不做介绍。

    2、SDIO接口

            SDIO接口包含CLK、CMD 及4 条DAT[3:0]信号线。这6 条信号线都是共用的总线,即新加入的设备可以并联接入到SDIO 接口。SDIO 主机是通过命令和SD 从设备的响应来寻址的,所以不需要片选信号线。
    1) 、CLK 是卡的时钟信号线,由主机产生时钟信号,SD 卡和SDIO 卡的时钟频率可为0~25MHz。在命令和数据线上,每个时钟周期在传输1 位命令或数据。
    2) 、CMD为命令信号线,SDIO 的所有由主机发出的命令及从机对命令的响应,都是在这个信号线上传输的。
    3) 、DAT[3:0]表示4 条数据线,主机和从机的数据信号在这4 条线上传输。

     

    二、SDIO的命令与响应

    1、SDIO命令

            SDIO的命令分为应用相关命令(ACMD)和通用命令(CMD)两部分,应用相关命令(ACMD)的发送,必须先发送通用命令(CMD55),然后才能发送应用相关命令(ACMD)。
            SDIO的所有命令和响应都是通过CMD引脚传输的,任何命令的长度都是固定为48位,命令格式如下:

            所有的命令都是由主机发出,其中起始位、传输位、CRC7和结束位由SDIO硬件控制,我们需要设置的就只有命令索引和参数部分。命令索引是命令的ID号,比如CMD0、CMD1之类的。有些命令会包含参数,如读命令就包含要读数据的地址,这些命令参数就存放在参数[39:8]。

    2、SDIO响应

            一般情况下,选中的SD卡在接收到主机命令后,都会回复一个应答(注意CMD0是无应答的),这个应答我们称之为响应,响应也是在CMD线上串行传输的。对于不同的命令,会有不同的响应格式,共有7种,简称为R1-R7。按照响应的字节长度不同又可以把这7种分为两类,即:短响应(48位)和长响应(136位)。这两类响应类型都带CRC错误检测(注意不带CRC的响应应该忽略CRC错误标志,如CMD1的响应)。

           短响应的格式:

            长响应的格式:

    3、响应格式的例子

            拿R1(属于短响应)来举例,格式为:

             命令索引的内容为它响应的命令的编码,如当我们向SD卡发送CMD55(编码:110111)命令时,它返回的响应R1的命令索引内容即为110111。
            [39:8]的内容为命令响应参数,如这个响应R1的内容即为卡状态。

            再拿R6(属于短响应)来举例,格式为:

            command index 的内容为它响应的命令的编码,如当我们向SD卡发送CMD3(编码:000011)命令时,它返回的响应R6的command index 内容即为000011。
            Argument field 的内容为命令响应参数,如这个响应R6的内容即为RCA(卡的相对地址)及card status (卡的状态)。

            所以当我们需要知道RCA 和卡状态时,我们可以向卡发送CMD3 命令,然后等待SD卡对命令的响应。SDIO接口通过CMD信号线接收到响应后,由硬件去除响应的头尾信息,把command index 保存到SDMMC_RESPCMD寄存器,把Argument field 内容保存存储到SDMMC_RESP1寄存器中。然后软件读取这两个寄存器即可获得所需的信息。

    4、数据的读取和写入

            SD卡的数据写入、读取的最小单位是块,每块的大小为512 字节。下图为多个数据块的写入过程。首先软件通过SDIO 接口的CMD 信号线发送多块写入的命令,接收到正常的响应后,要写入的数据线从4 根DAT 信号线传输出去,每块结束后是CRC校验码。接着要检测忙状态,数据传输到SD卡后,SD卡启动内部时序保存数据,这时SD卡会把DAT0信号线拉低,表示于“忙”状态,忙状态结束后,主机才能发送下一个数据块的数据。

            下图是读的过程,从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带有CRC校验值(CRC由SDMMC硬件自动处理),单个数据块读的时候,在收到1个数据块以后即可以停止了,不需要发送停止命令(CMD12)。但是多块数据读的时候,SD卡将一直发送数据给主机,直到接到主机发送的STOP命令(CMD12)。

     

    三、SD卡初始化流程

            从上图可以看到,不管什么卡(这里我们将卡分为4类:SD2.0高容量卡(SDHC,最大32G),SD2.0 标准容量卡(SDSC,最大2G),SD1x 卡和MMC卡),首先我们要执行的是卡上电(需要设置SDMMC_POWER[1:0]=11),上电后发送CMD0,对卡进行软复位,之后发送CMD8命令,用于区分SD卡2.0,只有2.0及以后的卡才支持CMD8命令,MMC卡和V1.x卡都不支持该命令。CMD8的格式如下:

            我们在发送CMD8的时候,可以顺便通过其所带的参数(VHS)告诉SD卡主机的供电情况,VHS定义如下:

            这里我们使用参数0x1AA,即告诉SD卡,主机供电为2.7~3.6V之间,如果SD卡支持CMD8,且支持该电压范围,则会通过CMD8的响应(R7)将参数部分原本返回给主机,如果不支持CMD8,或者不支持这个电压范围,则不响应。
            在发送CMD8后,发送ACMD41(注意发送ACMD41之前要先发送CMD55),来进一步确认卡的操作电压范围,并通过HCS位来告诉SD卡,主机是不是支持高容量卡(SDHC)。ACMD41的命令格式如下:

            ACMD41得到的响应(R3)包含SD卡OCR寄存器内容,OCR寄存器内容定义如下:

            对于支持CMD8指令(有响应)的卡,主机通过ACMD41的参数设置HCS位为1,来告诉SD卡主机支持SDHC卡,如果设置为0,则表示主机不支持SDHC卡。SDHC卡如果接收到HCS为0,则永远不会返回卡就绪状态,就会被主机判断为不支持的卡。对于不支持CMD8的卡,HCS位设置为0即可。
            SD卡在接收到ACMD41后,返回OCR寄存器内容,如果是2.0的卡,主机可以通过判断OCR的CCS位来判断是SDHC还是SDSC。如果是1.x的卡,则忽略该位。OCR寄存器的最后一个位用于告诉主机SD卡是否上电完成,如果上电完成,该位将会被置1。
            对于MMC卡,则不支持ACMD41,不响应CMD55,对MMC卡,我们只需要在发送CMD0后,在发送CMD1(作用同ACMD41),检查MMC卡的OCR寄存器,实现MMC卡的初始化。

            至此,我们完成了SD卡类型的区分。最后发送CMD2和CMD3命令,用于获得卡CID寄存器数据和卡相对地址(RCA)。
            CMD2,用于获得CID寄存器的数据,CID 寄存器数据各位定义如下:

            SD卡在收到CMD2后,将返回R2长响应(136位),其中包含128位有效数据(CID寄存器内容),存放在SDMMC_RESP1~4等4个寄存器里面。通过读取这四个寄存器,就可以获得SD卡的CID信息。
            CMD3,用于设置卡相对地址(RCA,必须为非0),对于SD卡(非MMC卡),在收到CMD3后,将返回一个新的RCA给主机,方便主机寻址。RCA的存在允许一个SDMMC接口挂多个SD卡,通过RCA来区分主机要操作的是哪个卡。而对于MMC卡,则不是由SD卡自动返回RCA,而是主机主动设置MMC卡的RCA,即通过CMD3参数(高16位用于RCA设置),实现RCA设置。同样MMC卡也支持一个SDMMC接口挂多个MMC卡,不同于SD卡的是所有的RCA都是由主机主动设置的,而SD卡的RCA则是SD卡发给主机的。
            在获得卡RCA之后,我们便可以发送CMD9(带RCA参数),获得SD卡的CSD寄存器内容,从CSD寄存器,我们可以得到SD卡的容量和扇区大小等十分重要的信息。
            至此,我们的SD卡初始化基本就结束了,最后通过CMD7命令,选中我们要操作的SD卡,即可开始对SD卡的读写操作了。

     

    四、开发经验

    1、发送ACMD41前要先发送CMD55,有些卡第一次发送CMD55会返回卡状态未就绪,需要多发送几次,直到卡返回就绪状态。所以一定要接收CMD55的响应,判断卡状态,否则初始化不成功。笔者使用256MB的卡时,遇到过一次。

     

    五、简要总结

            首先给卡上电,然后发送CMD0让卡进入IDLE STAGE模式。

            接着发送CMD8,如果有响应,卡就是2.0卡,继续发送ACMD41,接收响应,如果响应中的CCS位为1,卡就是SDHC,如果CCS为0,卡就是SDSC。如果CMD8无响应,那么卡就是1.0卡,发送ACMD41,接收响应,根据响应中的31位判断卡是否进入准备状态。

            卡分类完成后,发送CMD2来获取卡信息,发送CMD3来设置相对地址,卡的初始化结束。

     

    六、初始化代码示例

            代码来自正点原子的STM32H7的SD卡例程。建议复制到notepad++等地方看。

            主要关注 SD_PowerON() 和SD_InitializeCards() 函数,这两个函数包含初始化流程,和上面的流程图是完全对应的,可以帮助理解。

    #include "sdmmc_sdcard.h"
    #include "string.h"  
    #include "usart.h"	 
    //	 
    //本程序只供学习使用,未经作者许可,不得用于其它任何用途
    //ALIENTEK STM32H7开发板
    //SDMMC 驱动代码	(仅提供查询模式驱动代码)
    //正点原子@ALIENTEK
    //技术论坛:www.openedv.com
    //创建日期:2018/7/31
    //版本:V1.0
    //版权所有,盗版必究。
    //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
    //All rights reserved		 
    //********************************************************************************
    //升级说明
    //无
    // 	 
    static u8 CardType=STD_CAPACITY_SD_CARD_V1_1;			//SD卡类型(默认为1.x卡)
    static u32 CSD_Tab[4],CID_Tab[4],RCA=0;					//SD卡CSD,CID以及相对地址(RCA)数据
    SD_CardInfo SDCardInfo;									//SD卡信息
    
    //SD_ReadDisk/SD_WriteDisk函数专用buf,当这两个函数的数据缓存区地址不是4字节对齐的时候,
    //需要用到该数组,确保数据缓存区地址是4字节对齐的.
    __align(4) u8 SDMMC_DATA_BUFFER[512];						  
     
    
    //初始化SD卡
    //返回值:错误代码;(0,无错误)
    SD_Error SD_Init(void)
    {
    	SD_Error errorstatus=SD_OK;	  
    	u8 clkdiv=0;
    	//SDMMC1 IO口初始化   	 
        GPIO_InitTypeDef GPIO_Initure;
    
        __HAL_RCC_SDMMC1_CLK_ENABLE();  //使能SDMMC1时钟
        __HAL_RCC_GPIOC_CLK_ENABLE();   //使能GPIOC时钟
        __HAL_RCC_GPIOD_CLK_ENABLE();   //使能GPIOD时钟
        
        //PC8,9,10,11,12
        GPIO_Initure.Pin=GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
        GPIO_Initure.Mode=GPIO_MODE_AF_PP;      //推挽复用
        GPIO_Initure.Pull=GPIO_NOPULL;          //无上下拉
        GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;     //高速
        GPIO_Initure.Alternate=GPIO_AF12_SDIO1; //复用为SDIO
        HAL_GPIO_Init(GPIOC,&GPIO_Initure);     //初始化
        
        //PD2
        GPIO_Initure.Pin=GPIO_PIN_2;            
        HAL_GPIO_Init(GPIOD,&GPIO_Initure);     //初始化
        
     	//SDMMC外设寄存器设置为默认值 			   
    	SDMMC1->POWER=0x00000000;
    	SDMMC1->CLKCR=0x00000000;
    	SDMMC1->ARG=0x00000000;
    	SDMMC1->CMD=0x00000000;
    	SDMMC1->DTIMER=0x00000000;
    	SDMMC1->DLEN=0x00000000;
    	SDMMC1->DCTRL=0x00000000;
    	SDMMC1->ICR=0X1FE00FFF;
    	SDMMC1->MASK=0x00000000;	  
    
    //    HAL_NVIC_SetPriority(SDMMC1_IRQn,2,0);  //配置SDMMC1中断,抢占优先级2,子优先级0
    //    HAL_NVIC_EnableIRQ(SDMMC1_IRQn);        //使能SDMMC1中断
        
       	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(1);	//4位宽度,如果是MMC卡,则不能用4位模式 
      	if((errorstatus==SD_OK)||(MULTIMEDIA_CARD==CardType))
    	{  		    
    		if(SDCardInfo.CardType==STD_CAPACITY_SD_CARD_V1_1||SDCardInfo.CardType==STD_CAPACITY_SD_CARD_V2_0)
    		{
    			clkdiv=SDMMC_TRANSFER_CLK_DIV+2;	//V1.1/V2.0卡,设置最高48/4=12Mhz
    		}else clkdiv=SDMMC_TRANSFER_CLK_DIV;	//SDHC等其他卡,设置最高48/2=24Mhz
    		SDMMC_Clock_Set(clkdiv);	//设置时钟频率,SDMMC时钟计算公式:SDMMC_CK时钟=SDMMCCLK/[clkdiv+2];其中,SDMMCCLK固定为48Mhz 
      	}
    	return errorstatus;		 
    }
    //SDMMC时钟初始化设置
    //clkdiv:时钟分频系数
    //CK时钟=sdmmc_ker_ck/[2*clkdiv];(sdmmc_ker_ck钟固定为400Mhz)
    void SDMMC_Clock_Set(u16 clkdiv)
    {
    	u32 tmpreg=SDMMC1->CLKCR; 
      	tmpreg&=0XFFFFFC00; 
     	tmpreg|=clkdiv;   
    	SDMMC1->CLKCR=tmpreg;
    } 
    //SDMMC发送命令函数
    //cmdindex:命令索引,低六位有效
    //waitrsp:期待的相应.00/10,无响应;01,短响应;11,长响应
    //arg:参数
    void SDMMC_Send_Cmd(u8 cmdindex,u8 waitrsp,u32 arg)
    {			
    	u32 tmpreg=0;
    	SDMMC1->ARG=arg;  
    	tmpreg|=cmdindex&0X3F;	//设置新的index			 
    	tmpreg|=(u32)waitrsp<<8;//设置新的wait rsp 
    	tmpreg|=0<<10;			//无等待
      	tmpreg|=1<<12;			//命令通道状态机使能
    	SDMMC1->CMD=tmpreg;
    }
    //SDMMC发送数据配置函数
    //datatimeout:超时时间设置
    //datalen:传输数据长度,低25位有效,必须为块大小的整数倍
    //blksize:块大小.实际大小为:2^blksize字节
    //dir:数据传输方向:0,控制器到卡;1,卡到控制器;
    void SDMMC_Send_Data_Cfg(u32 datatimeout,u32 datalen,u8 blksize,u8 dir)
    {
    	u32 tmpreg;
    	SDMMC1->DTIMER=datatimeout;
      	SDMMC1->DLEN=datalen&0X1FFFFFF;	//低25位有效
    	tmpreg=SDMMC1->DCTRL; 
    	tmpreg&=0xFFFFFF00;		//清除之前的设置.
    	tmpreg|=blksize<<4;		//设置块大小
    	tmpreg|=0<<2;			//块数据传输
    	tmpreg|=(dir&0X01)<<1;	//方向控制
    	tmpreg|=1<<0;			//数据传输使能,DPSM状态机
    	SDMMC1->DCTRL=tmpreg;		
    }  
    
    //卡上电
    //查询所有SDMMC接口上的卡设备,并查询其电压和配置时钟
    //返回值:错误代码;(0,无错误)
    SD_Error SD_PowerON(void)
    {
     	u8 i=0;
    	u32 tempreg=0;
    	SD_Error errorstatus=SD_OK;
    	u32 response=0,count=0,validvoltage=0;
    	u32 SDType=SD_STD_CAPACITY;
    	//配置CLKCR寄存器  
    	tempreg|=0<<12;				//PWRSAV=0,非省电模式 
    	tempreg|=0<<14;				//WIDBUS[1:0]=0,1位数据宽度
    	tempreg|=0<<16;				//NEGEDGE=0,SDMMCCK下降沿更改命令和数据
    	tempreg|=0<<17;				//HWFC_EN=0,关闭硬件流控制    
    	SDMMC1->CLKCR=tempreg; 
    	SDMMC_Clock_Set(SDMMC_INIT_CLK_DIV);//设置时钟频率(初始化的时候,不能超过400Khz)		
    
    
    
    	
     	SDMMC1->POWER=0X03;			//上电状态,开启卡时钟     
       	for(i=0;i<74;i++)
    	{
    		SDMMC_Send_Cmd(SD_CMD_GO_IDLE_STATE,0,0);//发送CMD0进入IDLE STAGE模式命令.												  
    		errorstatus=CmdError();
    		if(errorstatus==SD_OK)break;
     	}
     	if(errorstatus)return errorstatus;//返回错误状态
    	SDMMC_Send_Cmd(SD_SDMMC_SEND_IF_COND,1,SD_CHECK_PATTERN);//发送CMD8,短响应,检查SD卡接口特性.
     														//arg[11:8]:01,支持电压范围,2.7~3.6V
    														//arg[7:0]:默认0XAA
    														//返回响应7
      	errorstatus=CmdResp7Error();						//等待R7响应
     	if(errorstatus==SD_OK) 								//R7响应正常
    	{
    		CardType=STD_CAPACITY_SD_CARD_V2_0;				//SD 2.0卡
    		SDType=SD_HIGH_CAPACITY;			   			//高容量卡
    	}
    	SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,0);					//发送CMD55,短响应	 
    	errorstatus=CmdResp1Error(SD_CMD_APP_CMD); 		 	//等待R1响应   
    	if(errorstatus==SD_OK)//SD2.0/SD 1.1,否则为MMC卡
    	{																  
    		//SD卡,发送ACMD41 SD_APP_OP_COND,参数为:0x80100000 
    		while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL))
    		{	   										   
    			SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,0);				//发送CMD55,短响应	 
    			errorstatus=CmdResp1Error(SD_CMD_APP_CMD); 	 	//等待R1响应   
     			if(errorstatus!=SD_OK)return errorstatus;   	//响应错误
    			SDMMC_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=SDMMC1->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=HIGH_CAPACITY_SD_CARD;
    		}
     	}else//MMC卡
    	{
    		//MMC卡,发送CMD1 SDMMC_SEND_OP_COND,参数为:0x80FF8000 
    		while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL))
    		{	   										   				   
    			SDMMC_Send_Cmd(SD_CMD_SEND_OP_COND,1,SD_VOLTAGE_WINDOW_MMC);//发送CMD1,短响应	 
    			errorstatus=CmdResp3Error(); 					//等待R3响应   
     			if(errorstatus!=SD_OK)return errorstatus;   	//响应错误  
    			response=SDMMC1->RESP1;;			   				//得到响应
    			validvoltage=(((response>>31)==1)?1:0);
    			count++;
    		}
    		if(count>=SD_MAX_VOLT_TRIAL)
    		{
    			errorstatus=SD_INVALID_VOLTRANGE;
    			return errorstatus;
    		}	 			    
    		CardType=MULTIMEDIA_CARD;	  
      	}  
      	return(errorstatus);		
    }
    //SD卡 Power OFF
    //返回值:错误代码;(0,无错误)
    SD_Error SD_PowerOFF(void)
    {
      	SDMMC1->POWER&=~(3<<0);//SDMMC电源关闭,时钟停止	
    	return SD_OK;		  
    }   
    //初始化所有的卡,并让卡进入就绪状态
    //返回值:错误代码
    SD_Error SD_InitializeCards(void)
    {
     	SD_Error errorstatus=SD_OK;
    	u16 rca = 0x01;
     	if((SDMMC1->POWER&0X03)==0)return SD_REQUEST_NOT_APPLICABLE;//检查电源状态,确保为上电状态
     	if(SECURE_DIGITAL_IO_CARD!=CardType)				//非SECURE_DIGITAL_IO_CARD
    	{
    		SDMMC_Send_Cmd(SD_CMD_ALL_SEND_CID,3,0);		//发送CMD2,取得CID,长响应	 
    		errorstatus=CmdResp2Error(); 					//等待R2响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误		    
     		CID_Tab[0]=SDMMC1->RESP1;
    		CID_Tab[1]=SDMMC1->RESP2;
    		CID_Tab[2]=SDMMC1->RESP3;
    		CID_Tab[3]=SDMMC1->RESP4;
    	}
    	if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(SECURE_DIGITAL_IO_COMBO_CARD==CardType)||(HIGH_CAPACITY_SD_CARD==CardType))//判断卡类型
    	{
    		SDMMC_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 (MULTIMEDIA_CARD==CardType)
        {
     		SDMMC_Send_Cmd(SD_CMD_SET_REL_ADDR,1,(u32)(rca<<16));//发送CMD3,短响应 	   
    		errorstatus=CmdResp2Error(); 					//等待R2响应   
    		if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	 
        }
    	if (SECURE_DIGITAL_IO_CARD!=CardType)			//非SECURE_DIGITAL_IO_CARD
    	{
    		RCA = rca;
    		SDMMC_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]=SDMMC1->RESP1;
    		CSD_Tab[1]=SDMMC1->RESP2;
    		CSD_Tab[2]=SDMMC1->RESP3;						
    		CSD_Tab[3]=SDMMC1->RESP4;					    
    	}
    	return SD_OK;//卡初始化成功
    } 
    //得到卡信息
    //cardinfo:卡信息存储区
    //返回值:错误状态
    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; 					//保留
     	if((CardType==STD_CAPACITY_SD_CARD_V1_1)||(CardType==STD_CAPACITY_SD_CARD_V2_0)||(MULTIMEDIA_CARD==CardType))//标准1.1/2.0卡/MMC卡
    	{
    		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<<(cardinfo->SD_csd.DeviceSizeMul+2));
    		cardinfo->CardBlockSize=1<<(cardinfo->SD_csd.RdBlockLen);//块大小
    		cardinfo->CardCapacity*=cardinfo->CardBlockSize;
    	}else if(CardType==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;
    }
    //设置SDMMC总线宽度(MMC卡不支持4bit模式)
    //wmode:位宽模式.0,1位数据宽度;1,4位数据宽度;2,8位数据宽度
    //返回值:SD卡错误状态
    SD_Error SD_EnableWideBusOperation(u32 wmode)
    {
      	SD_Error errorstatus=SD_OK;
    	u16 clkcr=0;
      	if(MULTIMEDIA_CARD==CardType)return SD_UNSUPPORTED_FEATURE;//MMC卡不支持
     	else if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType))
    	{
    		if(wmode>=2)return SD_UNSUPPORTED_FEATURE;//不支持8位模式
     		else   
    		{
    			errorstatus=SDEnWideBus(wmode);
     			if(SD_OK==errorstatus)
    			{
    				clkcr=SDMMC1->CLKCR;	//读取CLKCR的值
    				clkcr&=~(3<<14);		//清除之前的位宽设置    
    				clkcr|=(u32)wmode<<14;	//1位/4位总线宽度 
    				clkcr|=0<<17;			//不开启硬件流控制
    				SDMMC1->CLKCR=clkcr;	//重新设置CLKCR值 
    			}
    		}  
    	} 
    	return errorstatus; 
    } 
    //选卡
    //发送CMD7,选择相对地址(rca)为addr的卡,取消其他卡.如果为0,则都不选择.
    //addr:卡的RCA地址
    SD_Error SD_SelectDeselect(u32 addr)
    {
     	SDMMC_Send_Cmd(SD_CMD_SEL_DESEL_CARD,1,addr);	//发送CMD7,选择卡,短响应	 	   
       	return CmdResp1Error(SD_CMD_SEL_DESEL_CARD);	  
    }  
    //SD卡读取单个/多个块 
    //buf:读数据缓存区
    //addr:读取地址
    //blksize:块大小
    //nblks:要读取的块数,1,表示读取单个块
    //返回值:错误状态 
    SD_Error SD_ReadBlocks(u8 *buf,long long addr,u16 blksize,u32 nblks)
    {
      	SD_Error errorstatus=SD_OK; 
       	u32 count=0;
    	u32 timeout=SDMMC_DATATIMEOUT;  
    	u32 *tempbuff=(u32*)buf;	//转换为u32指针 
        SDMMC1->DCTRL=0x0;			//数据控制寄存器清零(关DMA)   
    	if(CardType==HIGH_CAPACITY_SD_CARD)//大容量卡
    	{
    		blksize=512;
    		addr>>=9;
    	}     
    	SDMMC_Send_Cmd(SD_CMD_SET_BLOCKLEN,1,blksize);			//发送CMD16+设置数据长度为blksize,短响应 	   
    	errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN);			//等待R1响应   
    	if(errorstatus!=SD_OK)
        {
            //printf("SDMMC_Send_Cmd=%d\r\n",errorstatus);
            return errorstatus;   			//响应错误
        }
    	SDMMC_Send_Data_Cfg(SD_DATATIMEOUT,nblks*blksize,9,1);	//nblks*blksize,块大小恒为512,卡到控制器	 
    	SDMMC1->CMD|=1<<6;										//CMDTRANS=1,产生一个数据传输命令
    	if(nblks>1)												//多块读  
    	{									    
    		SDMMC_Send_Cmd(SD_CMD_READ_MULT_BLOCK,1,addr);		//发送CMD18+从addr地址出读取数据,短响应 	   
    		errorstatus=CmdResp1Error(SD_CMD_READ_MULT_BLOCK);	//等待R1响应   
    		if(errorstatus!=SD_OK)
    		{	
    			//printf("SD_CMD_READ_MULT_BLOCK Error\r\n");
    			return errorstatus;   		//响应错误	 
    		}
    	}else													//单块读
    	{ 
    		SDMMC_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;   		//响应错误	 
    	} 
    	INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDMMC读写操作!!!)
    	while(!(SDMMC1->STA&((1<<5)|(1<<1)|(1<<3)|(1<<8))))//无上溢/CRC/超时/完成(标志)
    	{
    		if(SDMMC1->STA&(1<<15))						//接收区半满,表示至少存了8个字
    		{
    			for(count=0;count<8;count++)			//循环读取数据
    			{
    				*(tempbuff+count)=SDMMC1->FIFO;
    			}
    			tempbuff+=8;	 
    			timeout=0X7FFFFF; 	//读数据溢出时间
    		}else 	//处理超时
    		{
    			if(timeout==0)return SD_DATA_TIMEOUT;
    			timeout--;
    		}
    	} 
    	SDMMC1->CMD&=~(1<<6);		//CMDTRANS=0,结束数据传输
    	INTX_ENABLE();				//开启总中断
    	if(SDMMC1->STA&(1<<3))		//数据超时错误
    	{										   
    		SDMMC1->ICR|=1<<3; 		//清错误标志
    		return SD_DATA_TIMEOUT;
    	}else if(SDMMC1->STA&(1<<1))//数据块CRC错误
    	{
    		SDMMC1->ICR|=1<<1; 		//清错误标志
    		if(nblks>1)				//针对可能出现的CRC错误,如果是多块读取,必须发送结束传输命令!
    		{
    			SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0);		//发送CMD12+结束传输 	   
    			errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应   
    		}				
    		return SD_DATA_CRC_FAIL;		   
    	}else if(SDMMC1->STA&(1<<5))//接收fifo上溢错误
    	{
    		SDMMC1->ICR|=1<<5; 		//清错误标志
    		return SD_RX_OVERRUN;		 
    	}  
    	if((SDMMC1->STA&(1<<8))&&(nblks>1))//多块接收结束,发送结束指令
    	{
    		if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType))
    		{
    			SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0);		//发送CMD12+结束传输 	   
    			errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应   
    			if(errorstatus!=SD_OK)return errorstatus;	 
    		}
    	}
    	SDMMC1->ICR=0X1FE00FFF;	 		//清除所有标记 
    	return errorstatus;
    }	
     		    																  
    //SD卡写单个/多个块 
    //buf:数据缓存区
    //addr:写地址
    //blksize:块大小
    //nblks:要读取的块数,1,表示读取单个块
    //返回值:错误状态												   
    SD_Error SD_WriteBlocks(u8 *buf,long long addr,u16 blksize,u32 nblks)
    {
    	SD_Error errorstatus = SD_OK;
    	u8  cardstate=0;
    	u32 timeout=0,bytestransferred=0;
    	u32 cardstatus=0,count=0,restwords=0;
    	u32 tlen=nblks*blksize;						//总长度(字节)
    	u32*tempbuff=(u32*)buf;								 
     	if(buf==NULL)return SD_INVALID_PARAMETER;	//参数错误   
      	SDMMC1->DCTRL=0x0;							//数据控制寄存器清零(关DMA)    
     	if(CardType==HIGH_CAPACITY_SD_CARD)			//大容量卡
    	{
    		blksize=512;
    		addr>>=9;
    	}     
    	SDMMC_Send_Cmd(SD_CMD_SET_BLOCKLEN,1,blksize);			//发送CMD16+设置数据长度为blksize,短响应 	   
    	errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN);			//等待R1响应   
    	if(errorstatus!=SD_OK)return errorstatus;   			//响应错误  
    	if(nblks>1)												//多块写
    	{									     
    		if(nblks*blksize>SD_MAX_DATA_LENGTH)return SD_INVALID_PARAMETER;   
         	if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType))
        	{
    			//提高性能
    	 	   	SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,(u32)RCA<<16);	//发送ACMD55,短响应 	   
    			errorstatus=CmdResp1Error(SD_CMD_APP_CMD);		//等待R1响应   		   
    			if(errorstatus!=SD_OK)return errorstatus;				    
    	 	   	SDMMC_Send_Cmd(SD_CMD_SET_BLOCK_COUNT,1,nblks);	//发送CMD23,设置块数量,短响应 	   
    			errorstatus=CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);//等待R1响应   		   
    			if(errorstatus!=SD_OK)return errorstatus;				    
    		} 
    		SDMMC_Send_Cmd(SD_CMD_WRITE_MULT_BLOCK,1,addr);		//发送CMD25,多块写指令,短响应 	   
    		errorstatus=CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK);	//等待R1响应    
    	}else													//单块写		
    	{ 
    		SDMMC_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=SDMMC1->RESP1;													  
    		timeout=SD_DATATIMEOUT;
    		while(((cardstatus&0x00000100)==0)&&(timeout>0)) 	//检查READY_FOR_DATA位是否置位
    		{
    			timeout--;
    			SDMMC_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=SDMMC1->RESP1;													  
    		}
    		if(timeout==0)return SD_ERROR;  
    		SDMMC_Send_Cmd(SD_CMD_WRITE_SINGLE_BLOCK,1,addr);	//发送CMD24,写单块指令,短响应 	   
    		errorstatus=CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);//等待R1响应   	
    	}	   
    	if(errorstatus!=SD_OK)return errorstatus;   	   
     	SDMMC_Send_Data_Cfg(SD_DATATIMEOUT,nblks*blksize,9,0);	//blksize,块大小恒为512字节,控制器到卡	  
    	SDMMC1->CMD|=1<<6;										//CMDTRANS=1,产生一个数据传输命令
    	timeout=SDMMC_DATATIMEOUT; 
    	INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDMMC读写操作!!!)
    	while(!(SDMMC1->STA&((1<<4)|(1<<1)|(1<<8)|(1<<3))))//下溢/CRC/数据结束/超时
    	{
    		if(SDMMC1->STA&(1<<14))							//发送区半空,表示至少存了8字(32字节)
    		{	  
    			if((tlen-bytestransferred)<SD_HALFFIFOBYTES)//不够32字节了
    			{
    				restwords=((tlen-bytestransferred)%4==0)?((tlen-bytestransferred)/4):((tlen-bytestransferred)/4+1);
    				for(count=0;count<restwords;count++,tempbuff++,bytestransferred+=4)
    				{
    					SDMMC1->FIFO=*tempbuff;
    				}
    			}else 										//发送区半空,可以发送至少8字(32字节)数据
    			{
    				for(count=0;count<SD_HALFFIFO;count++)
    				{
    					SDMMC1->FIFO=*(tempbuff+count);
    				}
    				tempbuff+=SD_HALFFIFO;
    				bytestransferred+=SD_HALFFIFOBYTES;
    			}
    			timeout=0X3FFFFFFF;		//写数据溢出时间
    		}else
    		{
    			if(timeout==0)return SD_DATA_TIMEOUT; 
    			timeout--;
    		}
    	} 
    	SDMMC1->CMD&=~(1<<6);			//CMDTRANS=0,结束数据传输
    	INTX_ENABLE();					//开启总中断
    	if(SDMMC1->STA&(1<<3))			//数据超时错误
    	{										   
    		SDMMC1->ICR|=1<<3; 			//清错误标志
    		return SD_DATA_TIMEOUT;
    	}else if(SDMMC1->STA&(1<<1))	//数据块CRC错误
    	{
    		SDMMC1->ICR|=1<<1; 			//清错误标志
    		if(nblks>1)					//针对可能出现的CRC错误,如果是多块读取,必须发送结束传输命令!
    		{
    			SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0);		//发送CMD12+结束传输 	   
    			errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应   
    		}	
    		return SD_DATA_CRC_FAIL;		   
    	}else if(SDMMC1->STA&(1<<4)) 	//接收fifo下溢错误
    	{
    		SDMMC1->ICR|=1<<4; 			//清错误标志
    		return SD_TX_UNDERRUN;		 
    	}
    	if((SDMMC1->STA&(1<<8))&&(nblks>1))//多块发送结束,发送结束指令
    	{
    		if((STD_CAPACITY_SD_CARD_V1_1==CardType)||(STD_CAPACITY_SD_CARD_V2_0==CardType)||(HIGH_CAPACITY_SD_CARD==CardType))
    		{
    			SDMMC_Send_Cmd(SD_CMD_STOP_TRANSMISSION,1,0);		//发送CMD12+结束传输 	   
    			errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应   
    			if(errorstatus!=SD_OK)return errorstatus;	 
    		}
    	}
    	SDMMC1->ICR=0X1FE00FFF;		//清除所有标记	  
     	errorstatus=IsCardProgramming(&cardstate);
     	while((errorstatus==SD_OK)&&((cardstate==SD_CARD_PROGRAMMING)||(cardstate==SD_CARD_RECEIVING)))
    	{
    		errorstatus=IsCardProgramming(&cardstate);
    	}   
    	return errorstatus;
    }  
    //检查CMD0的执行状态
    //返回值:sd卡错误码
    SD_Error CmdError(void)
    {
    	SD_Error errorstatus = SD_OK;
    	u32 timeout=SDMMC_CMD0TIMEOUT;	   
    	while(timeout--)
    	{
    		if(SDMMC1->STA&(1<<7))break;//命令已发送(无需响应)	 
    	}	    
    	if(timeout==0)return SD_CMD_RSP_TIMEOUT;  
    	SDMMC1->ICR=0X1FE00FFF;			//清除标记
    	return errorstatus;
    }	 
    //检查R7响应的错误状态
    //返回值:sd卡错误码
    SD_Error CmdResp7Error(void)
    {
    	SD_Error errorstatus=SD_OK;
    	u32 status;
    	u32 timeout=SDMMC_CMD0TIMEOUT;
     	while(timeout--)
    	{
    		status=SDMMC1->STA;
    		if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)	
    	}
     	if((timeout==0)||(status&(1<<2)))	//响应超时
    	{																				    
    		errorstatus=SD_CMD_RSP_TIMEOUT;	//当前卡不是2.0兼容卡,或者不支持设定的电压范围
    		SDMMC1->ICR|=1<<2;				//清除命令响应超时标志
    		return errorstatus;
    	}	 
    	if(status&1<<6)						//成功接收到响应
    	{								   
    		errorstatus=SD_OK;
    		SDMMC1->ICR|=1<<6;				//清除响应标志
     	}
    	return errorstatus;
    }	   
    //检查R1响应的错误状态
    //cmd:当前命令
    //返回值:sd卡错误码
    SD_Error CmdResp1Error(u8 cmd)
    {	  
       	u32 status; 
    	while(1)
    	{
    		status=SDMMC1->STA;
    		if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)
    	} 
    	if(status&(1<<2))					//响应超时
    	{																				    
     		SDMMC1->ICR=1<<2;				//清除命令响应超时标志
    		SDMMC1->ICR=0X1FE00FFF;				//清除标记
    		return SD_CMD_RSP_TIMEOUT;
    	}	
     	if(status&(1<<0))					//CRC错误
    	{																				    
     		SDMMC1->ICR=1<<0;				//清除标志
    		return SD_CMD_CRC_FAIL;
    	}		
    	if(SDMMC1->RESPCMD!=cmd)return SD_ILLEGAL_CMD;//命令不匹配 
      	SDMMC1->ICR=0X1FE00FFF;				//清除标记
    	return (SD_Error)(SDMMC1->RESP1&SD_OCR_ERRORBITS);//返回卡响应
    }
    //检查R3响应的错误状态
    //返回值:错误状态
    SD_Error CmdResp3Error(void)
    {
    	u32 status;						 
     	while(1)
    	{
    		status=SDMMC1->STA;
    		if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)	
    	}
     	if(status&(1<<2))					//响应超时
    	{											 
    		SDMMC1->ICR|=1<<2;				//清除命令响应超时标志
    		return SD_CMD_RSP_TIMEOUT;
    	}	 
       	SDMMC1->ICR=0X1FE00FFF;				//清除标记
     	return SD_OK;								  
    }
    //检查R2响应的错误状态
    //返回值:错误状态
    SD_Error CmdResp2Error(void)
    {
    	SD_Error errorstatus=SD_OK;
    	u32 status;
    	u32 timeout=SDMMC_CMD0TIMEOUT;
     	while(timeout--)
    	{
    		status=SDMMC1->STA;
    		if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)	
    	}
      	if((timeout==0)||(status&(1<<2)))	//响应超时
    	{																				    
    		errorstatus=SD_CMD_RSP_TIMEOUT; 
    		SDMMC1->ICR|=1<<2;				//清除命令响应超时标志
    		return errorstatus;
    	}	 
    	if(status&1<<0)						//CRC错误
    	{								   
    		errorstatus=SD_CMD_CRC_FAIL;
    		SDMMC1->ICR|=1<<0;				//清除响应标志
     	}
    	SDMMC1->ICR=0X1FE00FFF;				//清除标记
     	return errorstatus;								    		 
    } 
    //检查R6响应的错误状态
    //cmd:之前发送的命令
    //prca:卡返回的RCA地址
    //返回值:错误状态
    SD_Error CmdResp6Error(u8 cmd,u16*prca)
    {
    	SD_Error errorstatus=SD_OK;
    	u32 status;					    
    	u32 rspr1;
     	while(1)
    	{
    		status=SDMMC1->STA;
    		if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)	
    	}
    	if(status&(1<<2))					//响应超时
    	{																				    
     		SDMMC1->ICR|=1<<2;				//清除命令响应超时标志
    		return SD_CMD_RSP_TIMEOUT;
    	}	 	 
    	if(status&1<<0)						//CRC错误
    	{								   
    		SDMMC1->ICR|=1<<0;				//清除响应标志
     		return SD_CMD_CRC_FAIL;
    	}
    	if(SDMMC1->RESPCMD!=cmd)			//判断是否响应cmd命令
    	{
     		return SD_ILLEGAL_CMD; 		
    	}	    
    	SDMMC1->ICR=0X1FE00FFF;				//清除所有标记
    	rspr1=SDMMC1->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;
    }
    
    //SDMMC使能宽总线模式
    //enx:0,不使能;1,使能;
    //返回值:错误状态
    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;
     	if(SDMMC1->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//SD卡处于LOCKED状态		    
     	errorstatus=FindSCR(RCA,scr);						//得到SCR寄存器数据
     	if(errorstatus!=SD_OK)return errorstatus;
    	if((scr[1]&SD_WIDE_BUS_SUPPORT)!=SD_ALLZERO)		//支持宽总线
    	{
    	 	SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,(u32)RCA<<16);	//发送CMD55+RCA,短响应											  
    	 	errorstatus=CmdResp1Error(SD_CMD_APP_CMD);
    	 	if(errorstatus!=SD_OK)return errorstatus; 
    	 	SDMMC_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;				//不支持宽总线设置 	 
    }												   
    //检查卡是否正在执行写操作
    //pstatus:当前状态.
    //返回值:错误代码
    SD_Error IsCardProgramming(u8 *pstatus)
    {
     	vu32 respR1 = 0, status = 0; 
      	SDMMC_Send_Cmd(SD_CMD_SEND_STATUS,1,(u32)RCA<<16);		//发送CMD13 	   
      	status=SDMMC1->STA;
    	while(!(status&((1<<0)|(1<<6)|(1<<2))))status=SDMMC1->STA;//等待操作完成
       	if(status&(1<<0))			//CRC检测失败
    	{
    		SDMMC1->ICR|=1<<0;		//清除错误标记
    		return SD_CMD_CRC_FAIL;
    	}
       	if(status&(1<<2))			//命令超时 
    	{
    		SDMMC1->ICR|=1<<2;		//清除错误标记
    		return SD_CMD_RSP_TIMEOUT;
    	}
     	if(SDMMC1->RESPCMD!=SD_CMD_SEND_STATUS)return SD_ILLEGAL_CMD;
    	SDMMC1->ICR=0X1FE00FFF;		//清除所有标记
    	respR1=SDMMC1->RESP1;
    	*pstatus=(u8)((respR1>>9)&0x0000000F);
    	return SD_OK;
    }
    //读取当前卡状态
    //pcardstatus:卡状态
    //返回值:错误代码
    SD_Error SD_SendStatus(uint32_t *pcardstatus)
    {
    	SD_Error errorstatus = SD_OK;
    	if(pcardstatus==NULL)
    	{
    		errorstatus=SD_INVALID_PARAMETER;
    		return errorstatus;
    	}
     	SDMMC_Send_Cmd(SD_CMD_SEND_STATUS,1,RCA<<16);	//发送CMD13,短响应		 
    	errorstatus=CmdResp1Error(SD_CMD_SEND_STATUS);	//查询响应状态 
    	if(errorstatus!=SD_OK)return errorstatus;
    	*pcardstatus=SDMMC1->RESP1;//读取响应值
    	return errorstatus;
    } 
    //返回SD卡的状态
    //返回值:SD卡状态
    SDCardState SD_GetState(void)
    {
    	u32 resp1=0;
    	if(SD_SendStatus(&resp1)!=SD_OK)return SD_CARD_ERROR;
    	else return (SDCardState)((resp1>>9) & 0x0F);
    }
    //查找SD卡的SCR寄存器值
    //rca:卡相对地址
    //pscr:数据缓存区(存储SCR内容)
    //返回值:错误状态		   
    SD_Error FindSCR(u16 rca,u32 *pscr)
    {  
    	SD_Error errorstatus = SD_OK;
    	u32 tempscr[2]={0,0};  
     	SDMMC_Send_Cmd(SD_CMD_SET_BLOCKLEN,1,8);			//发送CMD16,短响应,设置Block Size为8字节											  
     	errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN);
     	if(errorstatus!=SD_OK)return errorstatus;	    
      	SDMMC_Send_Cmd(SD_CMD_APP_CMD,1,(u32)rca<<16);	//发送CMD55,短响应 									  
     	errorstatus=CmdResp1Error(SD_CMD_APP_CMD);
     	if(errorstatus!=SD_OK)return errorstatus;
    	SDMMC_Send_Data_Cfg(SD_DATATIMEOUT,8,3,1);		//8个字节长度,block为8字节,SD卡到SDMMC.
       	SDMMC_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(!(SDMMC1->STA&(SDMMC_STA_RXOVERR|SDMMC_STA_DCRCFAIL|SDMMC_STA_DTIMEOUT|SDMMC_STA_DBCKEND|SDMMC_STA_DATAEND)))
    	{ 
    		if(!(SDMMC1->STA&(1<<19)))		//接收FIFO数据可用
    		{
    			tempscr[0]=SDMMC1->FIFO;	//读取FIFO内容 
    			tempscr[1]=SDMMC1->FIFO;	//读取FIFO内容 
    			break;
    		}
    	}
     	if(SDMMC1->STA&(1<<3))		//接收数据超时
    	{										 
     		SDMMC1->ICR|=1<<3;		//清除标记
    		return SD_DATA_TIMEOUT;
    	}else if(SDMMC1->STA&(1<<1))//已发送/接收的数据块CRC校验错误
    	{
     		SDMMC1->ICR|=1<<1;		//清除标记
    		return SD_DATA_CRC_FAIL;   
    	}else if(SDMMC1->STA&(1<<5))//接收FIFO溢出
    	{
     		SDMMC1->ICR|=1<<5;		//清除标记
    		return SD_RX_OVERRUN;   	   
    	} 
       	SDMMC1->ICR=0X1FE00FFF;		//清除标记	 
    	//把数据顺序按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;
    }  
    //读SD卡
    //buf:读数据缓存区
    //sector:扇区地址
    //cnt:扇区个数	
    //返回值:错误状态;0,正常;其他,错误代码;				  				 
    u8 SD_ReadDisk(u8*buf,u32 sector,u32 cnt)
    {
    	u8 sta=SD_OK;
    	long long lsector=sector;
    	u32 n;
    	if(CardType!=STD_CAPACITY_SD_CARD_V1_1)lsector<<=9;
    	if((u32)buf%4!=0)
    	{
    	 	for(n=0;n<cnt;n++)
    		{
    		 	sta=SD_ReadBlocks(SDMMC_DATA_BUFFER,lsector+512*n,512,1);//单个sector的读操作
    			memcpy(buf,SDMMC_DATA_BUFFER,512);
    			buf+=512;
    		} 
    	}else sta=SD_ReadBlocks(buf,lsector,512,cnt);	//单个/多个sector   
    	return sta;
    }
    //写SD卡
    //buf:写数据缓存区
    //sector:扇区地址
    //cnt:扇区个数	
    //返回值:错误状态;0,正常;其他,错误代码;	
    u8 SD_WriteDisk(u8*buf,u32 sector,u32 cnt)
    {
    	u8 sta=SD_OK;
    	u32 n;
    	long long lsector=sector;
    	if(CardType!=STD_CAPACITY_SD_CARD_V1_1)lsector<<=9;
    	if((u32)buf%4!=0)
    	{
    	 	for(n=0;n<cnt;n++)
    		{
    			memcpy(SDMMC_DATA_BUFFER,buf,512);
    		 	sta=SD_WriteBlocks(SDMMC_DATA_BUFFER,lsector+512*n,512,1);//单个sector的写操作
    			buf+=512;
    		} 
    	}else sta=SD_WriteBlocks(buf,lsector,512,cnt);	//单个/多个sector   
    	return sta;
    }
    
    
    
    
    
    
    
    

     

    参考:

    野火 《零死角玩转STM32》

    正点原子《STM32H7开发指南-HAL库版本_V1.0》

    展开全文
  • 单片机读取SD卡数据解决方案

    千次阅读 2009-10-19 17:39:00
    随着电子技术的发展,SD卡作为大容量数据存储器越来越受到广大电子爱好者和客户的衷爱。但是由于SD卡FAT操作的编程复杂,很多电子爱好者望而却步,现在好了,我们推出了这块小巧玲珑的SD卡读卡模块,将复杂的FAT操作...

        随着电子技术的发展,SD卡作为大容量数据存储器越来越受到广大电子爱好者和客户的衷爱。但是由于SD卡FAT操作的编程复杂,很多电子爱好者望而却步,现在好了,我们推出了这块小巧玲珑的SD卡读卡模块,将复杂的FAT操作集成其内,单片机只需简单的串口操作就能将数据存到SD卡中去,而且支持FAT16和FAT32。
        单片机不限,51、avr、msp430、stc等单片机都能连接使用。
       ---------------------------------------------------------------
       稳定:工作稳定,不挑卡,不s机
       方便:串口UART操作(直接接任何带串口单片机),指令少,还可以顺序存
       ----------------------------------------------------------------


    功能简介:
    1. 电源输入范围宽:4~9V,支持3.3V和5V的UART接口。
    2. 和单片机的接口简单 UART(串口)接口 可以直接和单片机的UART接口连接
    3. 集成FAT32和FAT16格式系统
    4. 文件的读写支持两种模式,一种顺序读写,用以连续的大量数据的读写.一种为给定起始地址的读写,用来随即读写的少量数据的情况,这个功能是的文件的读写非常灵活
    5. 波特率可以用拨码开关选择,支持从1200~115200bps
    6. 一次性发送数据贞可以长达256字节,有效字节数250
    7. 支持<=8G容量的SD卡
    8. 读写SD卡模块的速度快
    9. 命令结构简单,方便发送
    10.能动态监测SD状态有错就会提示
    11.模块尺寸小,便于安装与使用。

    SDV600 单片机串口读写sd卡模块资料

    说明书下载地址:http://www.prog430.com/files/SDV6.pdf

    测试软件下载地址:http://www.prog430.com/files/SDV6_test.rar

    测试辅助模块驱动下载地址:http://www.prog430.com/files/CP2102.rar

    产品购买方式:http://item.taobao.com/auction/item_detail-0db1-671f8a1a78bb55f6c79e2ab03a34bf69.htm

    展开全文
  • 单片机spi驱动SD卡

    2012-11-08 11:44:13
    1. 单片机spi驱动SD卡SD卡相关资料】  SD卡在现在的日常生活与工作中使用非常广泛,时下已经成为最为通用的数据存储卡。在诸如MP3、数码相机等设备上也都采用SD卡作为其存储设备。SD卡之所以得到如此广泛的...

    1. 单片机spi驱动SD卡【SD卡相关资料】

       SD卡在现在的日常生活与工作中使用非常广泛,时下已经成为最为通用的数据存储卡。在诸如MP3、数码相机等设备上也都采用SD卡作为其存储设备。SD卡之所以得到如此广泛的使用,是因为它价格低廉、存储容量大、使用方便、通用性与安全性强等优点。既然它有着这么多优点,那么如果将它加入到单片机应用开发系统中来,将使系统变得更加出色。这就要求对SD卡的硬件与读写时序进行研究。对于SD卡的硬件结构,在官方的文档上有很详细的介绍,如SD卡内的存储器结构、存储单元组织方式等内容。要实现对它的读写,最核心的是它的时序,笔者在经过了实际的测试后,使用51单片机成功实现了对SD卡的扇区读写,并对其读写速度进行了评估。下面先来讲解SD卡的读写时序。

    1       SD卡的引脚定义:

              SD卡引脚功能详述:

    引脚

    编号

    SD模式

            SPI模式

    名称

    类型

    描述

    名称

    类型

    描述

    1

    CD/DAT3

    IOPP

    卡检测/

    数据线3

    #CS

    I

    片选

    2

    CMD

    PP

    命令/

    回应

    DI

    I

    数据输入

    3

    VSS1

    S

    电源地

    VSS

    S

    电源地

    4

    VDD

    S

    电源

    VDD

    S

    电源

    5

    CLK

    I

    时钟

    SCLK

    I

    时钟

    6

    VSS2

    S

    电源地

    VSS2

    S

    电源地

    7

    DAT0

    IOPP

    数据线0

    DO

    OPP

    数据输出

    8

    DAT1

    IOPP

    数据线1

    RSV

     

     

    9

    DAT2

    IOPP

    数据线2

    RSV

     

     

           注:S:电源供给  I:输入 O:采用推拉驱动的输出          PP:采用推拉驱动的输入输出

    SD卡支持两种总线方式:SD方式与SPI方式。其中SD方式采用6线制,使用CLKCMDDAT0~DAT3进行数据通信。而SPI方式采用4线制,使用CSCLKDataInDataOut进行数据通信。SD方式时的数据传输速度与SPI方式要快,采用单片机对SD卡进行读写时一般都采用SPI模式。采用不同的初始化方式可以使SD卡工作于SD方式或SPI方式。这里只对其SPI方式进行介绍。

    2       SPI方式驱动SD卡的方法

         SD卡的SPI通信接口使其可以通过SPI通道进行数据读写。从应用的角度来看,采用SPI接口的好处在于,很多单片机内部自带SPI控制器,不光给开发上带来方便,同时也见降低了开发成本。然而,它也有不好的地方,如失去了SD卡的性能优势,要解决这一问题,就要用SD方式,因为它提供更大的总线数据带宽。SPI接口的选用是在上电初始时向其写入第一个命令时进行的。以下介绍SD卡的驱动方法,只实现简单的扇区读写。

    1  命令与数据传输

    1.       命令传输

    SD卡自身有完备的命令系统,以实现各项操作。命令格式如下:

          命令的传输过程采用发送应答机制,过程如下:

    每一个命令都有自己命令应答格式。在SPI模式中定义了三种应答格式,如下表所示:

    字节

    含义

     

     

     

    1

    7

    开始位,始终为0

    6

    参数错误

    5

    地址错误

    4

    擦除序列错误

    3

    CRC错误

    2

    非法命令

    1

    擦除复位

    0

    闲置状态

    字节

    含义

     

     

     

    1

    7

    开始位,始终为0

    6

    参数错误

    5

    地址错误

    4

    擦除序列错误

    3

    CRC错误

    2

    非法命令

    1

    擦除复位

    0

    闲置状态

     

     

     

    2

    7

    溢出,CSD覆盖

    6

    擦除参数

    5

    写保护非法

    4

    ECC失败

    3

    卡控制器错误

    2

    未知错误

    1

    写保护擦除跳过,锁/解锁失败

    0

    锁卡

     

    字节

    含义

     

     

     

    1

    7

    开始位,始终为0

    6

    参数错误

    5

    地址错误

    4

    擦除序列错误

    3

    CRC错误

    2

    非法命令

    1

    擦除复位

    0

    闲置状态

    25

    全部

    操作条件寄存器,高位在前

              写命令的例程:

    //-----------------------------------------------------------------------------------------------

      向SD卡中写入命令,并返回回应的第二个字节

    //-----------------------------------------------------------------------------------------------

    unsigned char Write_Command_SD(unsigned char *CMD)

    {

       unsigned char tmp;

       unsigned char retry=0;

       unsigned char i;

     

       //禁止SD卡片选

       SPI_CS=1;

       //发送8个时钟信号

       Write_Byte_SD(0xFF);

       //使能SD卡片选

       SPI_CS=0;

     

       //SD卡发送6字节命令

       for (i=0;i<0x06;i++)

       {

          Write_Byte_SD(*CMD++);

       }

      

       //获得16位的回应

       Read_Byte_SD(); //read the first byte,ignore it.

       do

       {  //读取后8

          tmp = Read_Byte_SD();

          retry++;

       }

       while((tmp==0xff)&&(retry<100));

       return(tmp);

    }

     

    2  初始化

    SD卡的初始化是非常重要的,只有进行了正确的初始化,才能进行后面的各项操作。在初始化过程中,SPI的时钟不能太快,否则会造初始化失败。在初始化成功后,应尽量提高SPI的速率。在刚开始要先发送至少74个时钟信号,这是必须的。在很多读者的实验中,很多是因为疏忽了这一点,而使初始化不成功。随后就是写入两个命令CMD0CMD1,使SD卡进入SPI模式

               初始化时序图:

               初始化例程:

    //--------------------------------------------------------------------------

        初始化SD卡到SPI模式

    //--------------------------------------------------------------------------

    unsigned char SD_Init()

       unsigned char retry,temp;

       unsigned char i;

       unsigned char CMD[] = {0x40,0x00,0x00,0x00,0x00,0x95};

       SD_Port_Init(); //初始化驱动端口

      

       Init_Flag=1; //将初始化标志置1

     

       for (i=0;i<0x0f;i++)

       {

          Write_Byte_SD(0xff); //发送至少74个时钟信号

       }

       //SD卡发送CMD0

       retry=0;

       do

       { //为了能够成功写入CMD0,在这里写200

         temp=Write_Command_SD(CMD);

         retry++;

         if(retry==200)

         { //超过200

           return(INIT_CMD0_ERROR);//CMD0 Error!

         }

       }

       while(temp!=1);  //回应01h,停止写入

      

       //发送CMD1SD

       CMD[0] = 0x41; //CMD1

       CMD[5] = 0xFF;

       retry=0;

       do

       { //为了能成功写入CMD1,100

         temp=Write_Command_SD(CMD);

         retry++;

         if(retry==100)

         { //超过100

           return(INIT_CMD1_ERROR);//CMD1 Error!

         }

       }

       while(temp!=0);//回应00h停止写入

      

       Init_Flag=0; //初始化完毕,初始化标志清零

      

       SPI_CS=1;  //片选无效

       return(0); //初始化成功

    }

    3  读取CID

    CID寄存器存储了SD卡的标识码。每一个卡都有唯一的标识码。

    CID寄存器长度为128位。它的寄存器结构如下:

    名称

    数据宽度

    CID划分

    生产标识号

    MID

    8

    [127:120]

    OEM/应用标识

    OID

    16

    [119:104]

    产品名称

    PNM

    40

    [103:64]

    产品版本

    PRV

    8

    [63:56]

    产品序列号

    PSN

    32

    [55:24]

    保留

    4

    [23:20]

    生产日期

    MDT

    12

    [19:8]

    CRC7校验合

    CRC

    7

    [7:1]

    未使用,始终为1

    1

    [0:0]

    它的读取时序如下:

            与此时序相对应的程序如下:

    //------------------------------------------------------------------------------------

        读取SD卡的CID寄存器   16字节   成功返回0

    //-------------------------------------------------------------------------------------

    unsigned char Read_CID_SD(unsigned char *Buffer)

    {

       //读取CID寄存器的命令

       unsigned char CMD[] = {0x4A,0x00,0x00,0x00,0x00,0xFF};

       unsigned char temp;

       temp=SD_Read_Block(CMD,Buffer,16); //read 16 bytes

       return(temp);

    }

           4)读取CSD

                  CSDCard-Specific Data)寄存器提供了读写SD卡的一些信息。其中的一些单元可以由用户重新编程。具体的CSD结构如下:

    名称

    数据宽度

    单元类型

    CSD划分

    CSD结构

    CSD_STRUCTURE

    2

    R

    [127:126]

    保留

    -

    6

    R

    [125:120]

    数据读取时间1

    TAAC

    8

    R

    [119:112]

    数据在CLK周期内读取时间2(NSAC*100)

    NSAC

    8

    R

    [111:104]

    最大数据传输率

    TRAN_SPEED

    8

    R

    [103:96]

    卡命令集合

    CCC

    12

    R

    [95:84]

    最大读取数据块长

    READ_BL_LEN

    4

    R

    [83:80]

    允许读的部分块

    READ_BL_PARTIAL

    1

    R

    [79:79]

    非线写块

    WRITE_BLK_MISALIGN

    1

    R

    [78:78]

    非线读块

    READ_BLK_MISALIGN

    1

    R

    [77:77]

    DSR条件

    DSR_IMP

    1

    R

    [76:76]

    保留

    -

    2

    R

    [75:74]

    设备容量

    C_SIZE

    12

    R

    [73:62]

    最大读取电流@VDD min

    VDD_R_CURR_MIN

    3

    R

    [61:59]

    最大读取电流@VDDmax

    VDD_R_CURR_MAX

    3

    R

    [58:56]

    最大写电流@VDD min

    VDD_W_CURR_MIN

    3

    R

    [55:53]

    最大写电流@VDD max

    VDD_W_CURR_MAX

    3

    R

    [52:50]

    设备容量乘子

    C_SIZE_MULT

    3

    R

    [49:47]

    擦除单块使能

    ERASE_BLK_EN

    1

    R

    [46:46]

    擦除扇区大小

    SECTOR_SIZE

    7

    R

    [45:39]

    写保护群大小

    WP_GRP_SIZE

    7

    R

    [38:32]

    写保护群使能

    WP_GRP_ENABLE

    1

    R

    [31:31]

    保留

    -

    2

    R

    [30:29]

    写速度因子

    R2W_FACTOR

    3

    R

    [28:26]

    最大写数据块长度

    WRITE_BL_LEN

    4

    R

    [25:22]

    允许写的部分部

    WRITE_BL_PARTIAL

    1

    R

    [21:21]

    保留

    -

    5

    R

    [20:16]

    文件系统群

    FILE_OFRMAT_GRP

    1

    R/W

    [15:15]

    拷贝标志

    COPY

    1

    R/W

    [14:14]

    永久写保护

    PERM_WRITE_PROTECT

    1

    R/W

    [13:13]

    暂时写保护

    TMP_WRITE_PROTECT

    1

    R/W

    [12:12]

    文件系统

    FIL_FORMAT

    2

    R/W

    [11:10]

    保留

    -

    2

    R/W

    [9:8]

    CRC

    CRC

    7

    R/W

    [7:1]

    未用,始终为1

    -

    1

     

    [0:0]

               读取CSD 的时序:

               相应的程序例程如下:

    //-----------------------------------------------------------------------------------------

        读SD卡的CSD寄存器   共16字节    返回0说明读取成功

    //-----------------------------------------------------------------------------------------

    unsigned char Read_CSD_SD(unsigned char *Buffer)

    {  

       //读取CSD寄存器的命令

       unsigned char CMD[] = {0x49,0x00,0x00,0x00,0x00,0xFF};

       unsigned char temp;

       temp=SD_Read_Block(CMD,Buffer,16); //read 16 bytes

       return(temp);

    }


    4  读取SD卡信息

    综合上面对CIDCSD寄存器的读取,可以知道很多关于SD卡的信息,以下程序可以获取这些信息。如下:

    //-----------------------------------------------------------------------------------------------

    //返回

    // SD卡的容量,单位为M

    // sector count and multiplier MB are in

    u08 == C_SIZE / (2^(9-C_SIZE_MULT))

    // SD卡的名称

    //-----------------------------------------------------------------------------------------------

    void SD_get_volume_info()

    {  

        unsigned char i;

        unsigned char c_temp[5];

        VOLUME_INFO_TYPE SD_volume_Info,*vinf;

        vinf=&SD_volume_Info; //Init the pointoer;

    /读取CSD寄存器

        Read_CSD_SD(sectorBuffer.dat);

    //获取总扇区数

    vinf->sector_count = sectorBuffer.dat[6] & 0x03;

    vinf->sector_count <<= 8;

    vinf->sector_count += sectorBuffer.dat[7];

    vinf->sector_count <<= 2;

    vinf->sector_count += (sectorBuffer.dat[8] & 0xc0) >> 6;

    // 获取multiplier

    vinf->sector_multiply = sectorBuffer.dat[9] & 0x03;

    vinf->sector_multiply <<= 1;

    vinf->sector_multiply += (sectorBuffer.dat[10] & 0x80) >> 7;

    //获取SD卡的容量

    vinf->size_MB = vinf->sector_count >> (9-vinf->sector_multiply);

    // get the name of the card

    Read_CID_SD(sectorBuffer.dat);

    vinf->name[0] = sectorBuffer.dat[3];

    vinf->name[1] = sectorBuffer.dat[4];

    vinf->name[2] = sectorBuffer.dat[5];

    vinf->name[3] = sectorBuffer.dat[6];

    vinf->name[4] = sectorBuffer.dat[7];

    vinf->name[5] = 0x00; //end flag  

    }

             以上程序将信息装载到一个结构体中,这个结构体的定义如下:

    typedef struct SD_VOLUME_INFO

    { //SD/SD Card info

      unsigned int  size_MB;

      unsigned char sector_multiply;

      unsigned int  sector_count;

      unsigned char name[6];

    } VOLUME_INFO_TYPE;

    5  扇区读

    扇区读是对SD卡驱动的目的之一。SD卡的每一个扇区中有512个字节,一次扇区读操作将把某一个扇区内的512个字节全部读出。过程很简单,先写入命令,在得到相应的回应后,开始数据读取。

    扇区读的时序:

     

                 扇区读的程序例程:

    unsigned char SD_Read_Sector(unsigned long sector,unsigned char *buffer)

       unsigned char retry;

       //命令16

       unsigned char CMD[] = {0x51,0x00,0x00,0x00,0x00,0xFF};

       unsigned char temp;

      

       //地址变换   由逻辑块地址转为字节地址

       sector = sector << 9; //sector = sector * 512

     

       CMD[1] = ((sector & 0xFF000000) >>24 );

       CMD[2] = ((sector & 0x00FF0000) >>16 );

       CMD[3] = ((sector & 0x0000FF00) >>8 );

     

       //将命令16写入SD

       retry=0;

       do

       {  //为了保证写入命令  一共写100

          temp=Write_Command_MMC(CMD);

          retry++;

          if(retry==100)

          {

            return(READ_BLOCK_ERROR); //block write Error!

          }

       }

       while(temp!=0);

              

       //Read Start Byte form MMC/SD-Card (FEh/Start Byte)

       //Now data is ready,you can read it out.

       while (Read_Byte_MMC() != 0xfe);

       readPos=0;

      SD_get_data(512,buffer) ;  //512字节被读出到buffer

     return 0;

    }

    其中SD_get_data函数如下:

    //----------------------------------------------------------------------------

        获取数据到buffer

    //----------------------------------------------------------------------------

    void SD_get_data(unsigned int Bytes,unsigned char *buffer)

    {

       unsigned int j;

       for (j=0;j<Bytes;j++)

          *buffer++ = Read_Byte_SD();

    }

    6  扇区写

    扇区写是SD卡驱动的另一目的。每次扇区写操作将向SD卡的某个扇区中写入512个字节。过程与扇区读相似,只是数据的方向相反与写入命令不同而已。

        扇区写的时序:

     

     

     

    扇区写的程序例程:

    //--------------------------------------------------------------------------------------------

        写512个字节到SD卡的某一个扇区中去   返回0说明写入成功

    //--------------------------------------------------------------------------------------------

    unsigned char SD_write_sector(unsigned long addr,unsigned char *Buffer)

       unsigned char tmp,retry;

       unsigned int i;

       //, 命令24

       unsigned char CMD[] = {0x58,0x00,0x00,0x00,0x00,0xFF};

       addr = addr << 9; //addr = addr * 512

     

       CMD[1] = ((addr & 0xFF000000) >>24 );

       CMD[2] = ((addr & 0x00FF0000) >>16 );

       CMD[3] = ((addr & 0x0000FF00) >>8 );

     

       //写命令24SD卡中去

       retry=0;

       do

       {  //为了可靠写入,写100

          tmp=Write_Command_SD(CMD);

          retry++;

          if(retry==100)

          {

            return(tmp); //send commamd Error!

          }

       }

       while(tmp!=0);

      

     

       //在写之前先产生100个时钟信号

       for (i=0;i<100;i++)

       {

          Read_Byte_SD();

       }

     

       //写入开始字节

       Write_Byte_MMC(0xFE);  

     

       //现在可以写入512个字节

       for (i=0;i<512;i++)

       {

          Write_Byte_MMC(*Buffer++);

       }

     

       //CRC-Byte

       Write_Byte_MMC(0xFF); //Dummy CRC

       Write_Byte_MMC(0xFF); //CRC Code

      

       

       tmp=Read_Byte_MMC();   // read response

       if((tmp & 0x1F)!=0x05) // 写入的512个字节是未被接受

       {

         SPI_CS=1;

         return(WRITE_BLOCK_ERROR); //Error!

       }

       //等到SD卡不忙为止

    //因为数据被接受后,SD卡在向储存阵列中编程数据

       while (Read_Byte_MMC()!=0xff){};

     

       //禁止SD

       SPI_CS=1;

       return(0);//写入成功

    }

        此上内容在笔者的实验中都已调试通过。单片机采用STC89LE单片机(SD卡的初始化电压为2.0V~3.6V,操作电压为3.1V~3.5V,因此不能用5V单片机,或进行分压处理),工作于22.1184M的时钟下,由于所采用的单片机中没硬件SPI,采用软件模拟SPI,因此读写速率都较慢。如果要半SD卡应用于音频、视频等要求高速场合,则需要选用有硬件SPI的控制器,或使用SD模式,当然这就需要各位读者对SD模式加以研究,有了SPI模式的基础,SD模式应该不是什么难事。

    摘自:

    http://blog.csdn.net/topchao/article/details/4218630



    展开全文
  • SD卡驱动分析

    2018-04-03 11:04:51
    重点强调一遍,SD卡的最高工作电压是3.6V,如果采用5V单片机一定要加电平转换芯片,建议还是用3.3V单片机进行操作。首先我们来了解一下SD卡的发展过程。到目前为止(2016年7月)SD卡一共有4个版本,我们直接看一下这...
    重点强调一遍,SD卡的最高工作电压是3.6V,如果采用5V单片机一定要加电平转换芯片,建议还是用3.3V单片机进行操作。

    首先我们来了解一下SD卡的发展过程。
    到目前为止(2016年7月)SD卡一共有4个版本,我们直接看一下这个来自SD卡官网(www.sdcard.org)的表格:


    当然高版本是向下兼容的。

    接下来了解一下SD卡的引脚,SD卡的引脚和MMC卡是兼容,目前看来在生活中很难见到MMC卡,因此我们主要讨论SD卡,MMC卡有兴趣的可以自己了解一下。

    我们再来看一张来自SD卡官网的图:


    右图中第二排引脚是设计给UHS-II总线使用的,第一排的引脚和原来的引脚是一样的。

    下面给出SD卡引脚的定义:


    这张图来自网上,上面给出了标准SD卡和MicroSD卡在SD模式及SPI模式下的引脚定义,至于MMC卡的引脚定义就忽略吧。特别说明一句,CS线是片选线,低电平有效。

    SD卡的通信方式有两种,一种是SPI模式,它的传输方式是单比特传输,另一种是SDIO模式(也叫SD模式),它的传输方式是4比特传输。SDIO模式传输时需要加CRC校验,如果单片机不自带CRC校验功能就需要自己写一下CRC校验,这是有点困难的;而在SPI方式工作时,除了初始化的指令需要CRC,发送其余的指令以及传输数据时都可以关闭CRC,当然如果需要的话可以使用指令打开CRC。SDIO模式传输的速度肯定比SPI模式快,但程序较为复杂,SPI模式相对较为简单,大多数单片机都有硬件SPI,即使没有也可以用软件模拟SPI,因此我们主要讨论如何使用SPI模式驱动SD卡。下文中的内容如果没有特殊说明,都是指SPI模式。

    SD卡上电后默认工作在SD模式,如果在发送复位指令(CMD0)时将CS拉低,SD卡将进入SPI模式,否则SD卡会保持在SD模式不变。想要从SPI模式切换为SD模式的唯一方式就是重新上电,电源断开的时间必须大于1ms。

    这里给出以SPI方式初始化SD卡的流程图:
    这个图是我按照官方手册中给出的流程图翻译的,对部分内容进行了适当取舍。



    上电后在发送CMD0之前要先发送至少74个时钟。其中前64个时钟是让SD卡达到正常工作电压,后10个时钟是为了同步。

    在初始化阶段时钟频率不要超过400KHz,初始化完成后可以提高频率以加快读写速度。

    接下来我们再来说一下SD卡的通信时序。

    我们知道SPI一般有以下几种设置:
    高位最先发送、低位最先发送;时钟空闲为高电平、时钟空闲为低电平;上升沿采样、下降沿采样等。这里采取的设置是高位优先发送、时钟空闲为低电平、上升沿采样。

    SD卡发送指令的格式如下:


    参数一般为unsigned int,高字节优先发送。

    SD卡的指令分两种,一种是CMD,另一种是ACMD。按照手册中的说法,CMD是标准指令,ACMD是特殊应用指令。下面我将手册中的指令翻译出来,共大家参考。这里只翻译了能用的指令,保留指令还有预留指令都没有翻译。个人能力有限,我尽量翻译得准确一些,有不恰当的地方欢迎大家指出。






    在这里说明一点,SD模式和SPI模式下的指令数量是相同的,但是指令的格式有一定不同,网上许多文章将这两者混为一谈。

    SD卡在每接收到一条指令后都会有一个应答,具体是哪种应答可以从上面的表中查到,下面给出应答的格式。

    R1
    长度是一字节,最高位永远为0,其余位是错误指示位,出错时被置一,具体含义如下:
    第0位,处于空闲状态:SD卡处于空闲状态,且正在运行初始化程序
    第1位,擦除重置:在执行擦除前,一个擦除指令序列被清除,因为收到了一个超出擦除序列的指令。(这个我实在翻译不好,大家凑合看)
    第2位,非法指令:就是字面意思
    第3位,CRC错误:就是字面意思
    第4位,擦除序列错误:在擦除指令序列中发现错误
    第5位,地址错误:就是字面意思
    第6位,特定错误:指令的参数超出范围
    第7位,永远为0



    R1b
    R1b是在R1的基础上增加了一个忙碌状态指示,当R1的值为0时SD卡处于忙碌状态,而当R1为任何不为0的值时,SD卡才能开始接收下一条指令。

    R2
    一共两字节,格式如下:
    第15位:永远为0
    第14位:参数错误
    第13位:地址错误
    第12位:擦除序列错误
    第11位:CRC错误
    第10位:非法指令
    第9位:擦除重置
    第8位:处于空闲状态
    第7位:超出范围或CSD寄存器被覆盖
    第6位:擦除参数
    第5位:写入预擦除违规
    第4位:ECC错误
    第3位:CC错位
    第2位:错误
    第1位:跳过写入预擦除或锁定、解锁指令失败
    第0位:SD卡锁定

    R3
    1字节的R1+4字节的OCR寄存器值

    R7
    1字节的R1+4字节的应答,详细定义这里就不说了,只要知道这个值一般是0x000001AA就行,有兴趣的自己去看手册。

    除此之外,SD卡还有控制标志,下面给出这些控制标志。

    数据应答标志
    每向SD卡写入一个扇区的数据后,SD卡都会返回一个字节的应答,格式如下:


    状态位
    010:接受本次传输的数据
    101:因为CRC错误拒绝本次传输的数据
    110:因为写入错误拒绝本次传输的数据
    如果在多扇区写入时发生错误,主机应当使用CMD12停止传输。如果错误代码是110,主机可以使用CMD13查看错误原因,使用ACMD22找到写入成功的扇区。

    开始传送标志和停止传送标志
    单扇区读写和多扇区读的格式是:1字节的开始传输标志+512字节的数据+2字节CRC。

    单扇区读写和多扇区读的开始传送标志是:


    即0xFE

    多扇区写的开始传送标志是:


    即0xFC

    多扇区写的停止传送标志是:


    即0xFD

    数据错误标志
    如果读取操作失败SD卡不能发送数据,SD卡就会发出这个标志,格式如下:


    错误类型
    0001:错误
    0010:卡片内部控制器错误
    0100:ECC错误
    1000:超出范围


    终于说完了这些数据格式,下面结合代码来看一看,SD卡的初始化、读写单个数据块(扇区)是如何操作的。

    1. #include "spi.h"//这是SPI的驱动
    2. #define WAIT_COUNT 10//等待SD卡进行某些操作的计数器,超过这个次数认为等待超时,高速单片机(如STM32)可以将这个数值适当扩大
    3. sbit SPI_SS = P1^6;//SPI从机选择口, 连接到其它MCU的SS口
    4. //SD卡读写扇区的缓存,小容量卡的扇区大小设置成512字节,大容量卡的扇区就是512字节不可更改
    5. unsigned char xdata SDBlockBuffer[512] = {0};
    6. //初始化完成后可获得SD卡的类型,方便其他程序使用
    7. unsigned char SD_TYPE = 0;
    8. //返回值
    9. //0,初始化失败,电压不正确,SD卡无法使用,或是MMC卡
    10. //1,初始化成功,SD1.0的卡
    11. //2,初始化成功,SD2.0的标准卡(2GB以内)
    12. //3,初始化成功,SD2.0的大容量卡(32GB以内)
    13. unsigned char SDInit()
    14. {
    15.     unsigned char i = 0, r = 0;//i是计数器,r用于保存其他函数的返回值
    16.     char sd_type;
    17.     unsigned char buf[4] = {0};
    18.     
    19.     //用低速初始化SPI,初始化时SPI总线时钟频率不要超过400KHz
    20.     InitSPI(3);
    21.     //使能
    22.     SPI_SS = 0;
    23.     //至少74个时钟信号
    24.     for (i=0; i<10; i++)
    25.     {
    26.         SPI_RW(0xFF);
    27.     }
    28.     //一定次数之内如果没有得到SD卡的返回值,说明通信失败,这个次数可以进行适当更改
    29.     i = WAIT_COUNT;
    30.     do
    31.     {
    32.         r = SDSendCmd(0, 0, 0x95);
    33.     }while(i-- && (r & 0x80));
    34.     //注意:在SPI模式下CRC是默认关闭的,但是要保证CMD0的CRC正确,之后的指令的CRC可以是任意值,CMD0的CRC是0x95,已经计算好了,直接用就行。
    35.     //如有需要可以使用CMD59打开或关闭CRC,详情自行查看手册
    36.     
    37.     //r=0x01说明SD卡正常,可以继续进行下一步操作
    38.     if(r == 0x01)
    39.     {
    40.         //CMD8是SD 2.0新添加的指令,如果响应说明是SD2.0或更高版本
    41.         r = SDSendCmd(8, 0x1AA, 0x87);
    42.         //返回0x01是SD2.0
    43.         if (r == 0x01)
    44.         {
    45.             //对于SPI模式的R7,只有5个字节,首先返回的是R1,然后的4个字节,一般情况下是0x0000 01AA
    46.             SPI_SS = 0;
    47.             for (i=0; i<4; i++)
    48.             {
    49.                 buf[i] = SPI_RW(0xFF);
    50.             }
    51.             SPI_SS = 1;
    52.             if (buf[2] == 0x01 && buf[3] == 0xAA)
    53.             {
    54.                 //支持电压范围2.7-3.6
    55.                 //备注[1]
    56.                 //等待SD卡准备好,这个阶段时间可能较长,需要多等一段时间,但不应该超过一秒,超过一秒应重新初始化
    57.                 i = 254;
    58.                 do
    59.                 {
    60.                     SDSendCmd(55, 0, 0);//这是发送CMD55
    61.                     r = SDSendCmd(41, 0x40000000, 0);//发送ACMD41
    62.                     i--;
    63.                 }while(r && i);//如果r的最低位为0说明SD卡准备好了
    64.                 if (i)
    65.                 {
    66.                     //使用CMD58检查SD卡版本和电压,这里只检查版本,不检查电压
    67.                     r = SDSendCmd(58, 0, 0);
    68.                     SPI_SS = 0;
    69.                     for (i=0; i<4; i++)
    70.                     {
    71.                         buf[i] = SPI_RW(0xFF);
    72.                     }
    73.                     SPI_SS = 1;
    74.                     
    75.                     if (buf[0] & 0x40)
    76.                     {
    77.                         //是2.0或更高版本的大容量卡,即SDHC
    78.                         sd_type = 3;
    79.                     }
    80.                     else
    81.                     {
    82.                         //是2.0或更高版本的标准卡
    83.                         sd_type = 2;
    84.                     }
    85.                 }
    86.                 else
    87.                 {
    88.                     //SD卡准备时间过长
    89.                     sd_type = 0;
    90.                 }
    91.             }
    92.             else
    93.             {
    94.                 //失败,电压不正常或此卡无法使用
    95.                 sd_type = 0;
    96.             }
    97.         }
    98.         //返回0x05是SD1.0
    99.         else if (r == 0x05)
    100.         {
    101.             //等待SD卡准备好,同上
    102.             i = 254;
    103.             do
    104.             {
    105.                 SDSendCmd(55, 0, 0);//这是发送CMD55,告诉SD卡下一条指令是ACMD
    106.                 r = SDSendCmd(41, 0, 0);//发送ACMD41,这里的参数是0,与2.0不同
    107.                 i--;
    108.             }while(r && i);//如果r的最低位为0说明SD卡准备好了
    109.             
    110.             if (i)
    111.             {
    112.                 //上电初始化完成
    113.                 sd_type = 1;
    114.             }
    115.             else
    116.             {
    117.                 //初始化超时,可能是MMC卡或SD卡无法使用
    118.                 sd_type = 0;
    119.             }
    120.             
    121.         }
    122.         //其他情况
    123.         else
    124.         {
    125.             //失败,不是SD卡或SD卡无法使用
    126.             sd_type = 0;
    127.         }
    128.     }
    129.     else
    130.     {
    131.         //SD卡初始化失败,或者是其他问题
    132.         sd_type = 0;
    133.     }
    134.     
    135.     //额外8个时钟,让SD卡完成其他操作
    136.     SPI_RW(0xFF);
    137.     //先取消片选,再更改通信速度
    138.     SPI_SS = 1;
    139.     //SD卡初始化完成后可以进行高速通信
    140.     InitSPI(0);
    141.     return sd_type;
    142. }
    143. //发送SD卡指令,这三个参数分别是指令编号,指令参数,crc
    144. unsigned char SDSendCmd(unsigned char CmdNum, unsigned long int dat, unsigned char crc)
    145. {
    146.     unsigned char i = 0, r = 0;
    147.     SPI_RW(0x40 | CmdNum);
    148.     SPI_RW(dat >> 24);
    149.     SPI_RW(dat >> 16);
    150.     SPI_RW(dat >> 8);
    151.     SPI_RW(dat);
    152.     SPI_RW(crc);
    153.     //一定次数之内如果没有得到SD卡的返回值,说明通信失败,这个次数可以进行适当更改
    154.     i = WAIT_COUNT;
    155.     do
    156.     {
    157.         r = SPI_RW(0xFF);
    158.     }while(i-- && (r & 0x80));
    159.     //SPI模式下大多数指令的应答都是R1,R7的格式是一个R1+4字节的参数,只有CMD13的应答是R2,在这里不考虑应答R2的情况
    160.     return r;
    161. }
    162. //等待传输开始的标识0xFE
    163. unsigned char SDWaitStartToken()
    164. {
    165.     unsigned char i = 254, r;
    166.     do
    167.     {
    168.         r = SPI_RW(0xFF);
    169.         i--;
    170.     }while(i && (r != 0xFE));
    171.     if (i == 0)
    172.     {
    173.         //等待超时
    174.         return 1;
    175.     }
    176.     else
    177.     {
    178.         //得到响应
    179.         return 0;
    180.     }
    181. }
    182. unsigned char SDReadSingleBlock(unsigned char *buf, unsigned long int address)
    183. {
    184.     unsigned int i;
    185.     unsigned char r;
    186.     SPI_SS = 0;
    187.     //普通SD卡使用字节寻址,SDHC使用块寻址(就是以扇区为单位进行寻址)
    188.     if (SD_TYPE < 3)
    189.     {
    190.         address <<= 9;
    191.     }
    192.     r = SDSendCmd(17, address, 0);
    193.     SPI_SS = 0;
    194.     
    195.     if (r == 0)
    196.     {    
    197.         if (SDWaitStartToken())
    198.         {
    199.             //等待超时
    200.             return 1;
    201.         }
    202.         i = 512;
    203.         while (i)
    204.         {
    205.             *buf = SPI_RW(0xFF);//接收的数据保存到buf数组中
    206.             buf++;
    207.             i--;
    208.         }
    209.         //抛弃两个字节的CRC
    210.         SPI_RW(0xFF);
    211.         SPI_RW(0xFF);
    212.     }
    213.     SPI_SS = 1;
    214.     return r;
    215. }
    216. unsigned char SDWriteSingleBlock(unsigned char *buf, unsigned long int address)
    217. {
    218.     unsigned int i;
    219.     unsigned char r;
    220.     SPI_SS = 0;
    221.     //普通SD卡使用字节寻址,SDHC使用块寻址
    222.     if (SD_TYPE < 3)
    223.     {
    224.         address <<= 9;
    225.     }
    226.     
    227.     r = SDSendCmd(24, address, 0);
    228.     SPI_SS = 0;
    229.     
    230.     //r == 0说明SD卡准备好了,可以开始写入
    231.     if (r == 0)
    232.     {    
    233.         //得到r==0的响应后,无论发送什么SD卡都返回0xFF
    234.         //发送开始信号
    235.         SPI_RW(0xFE);
    236.         i = 512;
    237.         while (i)
    238.         {
    239.             SPI_RW(*buf);
    240.             buf++;
    241.             i--;
    242.         }
    243.         //发送两个字节的CRC
    244.         SPI_RW(0xFF);
    245.         SPI_RW(0xFF);
    246.         //接收返回值,检查写入是否成功
    247.         r = SPI_RW(0xFF);
    248.         if ((r & 0x1F) != 0x05)
    249.         {
    250.             return 2;//写入失败
    251.         }
    252.     }
    253.     else
    254.     {
    255.         return 1;//等待SD卡准备超时
    256.     }
    257.     SPI_SS = 1;
    258.     return 0;
    259. }


    像代码中这样操作SD卡实质就是把SD卡当成了一个24XX或是25QXX的EEPROM使用。而我们知道,当你把SD卡插在电脑上时,你会看到SD卡内的各种文件和文件夹,这就是文件系统。文件系统的知识又能再写一篇文章了,这里就不详细介绍了,有兴趣的可以去看《数据重现——文件系统原理精解与数据恢复最佳实践》一书的“第三章FAT文件系统”,这本书怎么弄,大家都懂。看完这部分你会对FAT文件系统有一个很好的理解,如果你的能力比较强,完全可以自己写一个简单的FAT32驱动。能力有限的话可以直接移植Petit Fatfs,目前(2016年8月)最新版本是R0.03,官网是http://elm-chan.org/fsw/ff/00index_p.html,下载地址是http://elm-chan.org/fsw/ff/pff3.zip
    展开全文
  • SD卡引脚 电路图及工作原理介绍

    千次阅读 2019-06-15 10:54:38
    对于SD卡的硬件结构,在官方的文档上有很详细的介绍,如SD卡内的存储器结构、存储单元组织方式等内容。要实现对它的读写,最核心的是它的时序,笔者在经过了实际的测试后,使用51单片机成功实现了对SD卡的扇区读写,...
  • STM32下SD卡驱动详解

    千次阅读 2015-07-15 15:00:54
    自己在这几个月中为了毕业设计放下了对S3C2440的学习,从而着手在...我用STM32做的是一个有界面的MP3所以不得不用到SD卡,(当然nandflash也可以不过pcb难画不说,最主要容易坏掉)。SD卡方便快捷。 说道SD卡
  • 最近看了32的sd卡和FATFS文件系统,现在就自己的理解总结一下。 一、SD卡模块 SD 卡:安全数码卡, 它是在 MMC 的基础上发展而来, 是一种基于半导体快闪记忆器的新一代记忆设备。按容量分类,可以将SD 卡分为 3 ...
  • 双卡+Micro+SD与TF读写模块+SPISDIO双模式驱动+3.3V5V
  • 驱动SD卡

    2016-10-07 21:41:42
    1. 单片机spi驱动SD卡SD卡相关资料】  SD卡在现在的日常生活与工作中使用非常广泛,时下已经成为最为通用的数据存储卡。在诸如MP3、数码相机等设备上也都采用SD卡作为其存储设备。SD卡之所以得到如此广泛...
  • SPI方式驱动SD卡的方法

    千次阅读 2010-07-01 13:25:00
    SD卡驱动主要有初始化、读、写、擦除等。 1、初始化步骤: (1) 延时至少74clock (2) 发送CMD0,需要返回0x01,进入Idle状态 (3) 循环发送CMD55+ACMD41,直到返回0x00,进入...
  • SD卡及STM32的SDIO接口相关原理

    万次阅读 2018-08-28 14:09:55
    SD卡  SD卡(Secure Digital Memory Card)即:安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式... SD卡容量分类,可以分为3类:SD卡、SDHC卡、SDXC...
  • 很多单片机系统都需要大容量存储设备,以存储数据。目前常用的有U盘,FLASH芯片,SD卡等。他们各有优点,综合比较,最适合单片机系统的莫过于SD卡了,它不仅容量可以做到很大(32GB以上),支持SPI/SDIO驱动,...
  • SD卡的读取分析设计

    2018-05-13 16:25:01
    SD卡的读取分析设计很多单片机都需要大容量存储设备,用来存储数据。目前常用的有U盘,FLASH芯片,SD卡等。它不仅用量可以做到最大到(32GB以上),支持SPI/SDIO驱动,而且有多重体积的尺寸可供支持,能够满足不同...
  • QNX驱动开发——SD卡SD模式开发实录

    千次阅读 2012-07-27 21:06:50
    和大多数朋友一样以前只用过SD卡的SPI模式,因为这种模式简单,无需专门的控制器,在大多数单片机上都可以实现,且无需CRC校验,控制起来也比较方便。而SD模式可以实现4条数据线同时传输,在速度上,比SPI模式有很...
  • 设备: STM32F103VE-M3,板载SD卡读写模块。 源码: 野火的SD卡扇区读写驱动,振南FAT的STM32版本。 另有1G-16G SD卡若干张。 移植过程: SD卡扇区读写驱动测试,SD卡单扇区读写,多扇区读写,多扇区擦除成功。 ...
  • micro SD(TF)详解

    万次阅读 2016-09-18 19:37:56
    SD卡(Secure Digital Memory Card)是一种基于半导体闪存工艺的存储卡。为了满足数码产品不断缩小存储卡体积的要求,SD卡逐渐演变出了Mini SD,Micro SD两种规格。  SD卡背面共有9个引脚,包含4根数据线,支持...
  • 在调试SD卡时,大家都喜欢使用扇区进行验证。也就是说,一般都是通过读写扇区数据进行比较,或者读取第0扇区的数据,在通过WinHex...这种方法对于1G的SD卡或部分2G的SD卡可能是对的,但是对于有些2G的SD卡和大于2G...
1 2 3 4 5 ... 20
收藏数 1,025
精华内容 410
关键字:

单片机驱动大容量sd卡