STM32 SPI读取SD卡

七个响应类型:

R1 Response (Normal Response): R1响应是最基本的响应,包含一个字节的状态位,用于指示命令是否成功执行。常用。最高位为0。最低位为1表示是空闲状态。其他位是各种错误提示。

R1b Response (Normal with Busy): 类似于R1,但在命令执行完成后,卡会持续将忙位(busy)发送给主机,直到卡准备好执行下一个命令。

R2 Response (CID or CSD Register Response): 包含两个字节的数据,用于读取CID(Card ID)或CSD(Card Specific Data)寄存器内容。

R3 Response (OCR Register Response): 包含四个字节的数据,用于读取OCR(Operating Conditions Register)寄存器内容。第一个字节就是R1响应,然后是OCR寄存器。OCR中主要看CCS位(标志v2.0高容量和标准容量)和电压支持范围

R6 Response (Published RCA Response): 包含一个字节的状态位和一个字节的相对卡地址(RCA),用于获取卡的相对地址。

R7 Response (Card Interface Condition Response): 包含一个字节的状态位和一个字节的回应信息,用于卡初始化阶段。常用返回状态。命令8专属响应

Data Response (for Data Transfer Commands): 在读/写数据时,卡会响应数据响应,用于指示数据是否成功接收。

数据传输控制:

数据块传输时,存在Token控制传输。

1.数据响应Token:

一个字节,格式如下

XXX Status 1

其中Status三个bit,010数据正常接收,101 CRC校验失败。110写入错误

2.数据块开始和停止Token

单块读写和多块读中数据块开头的Token是固定0xFE表示数据块开始

多块写使用0xFC开始,0xFD结束。

命令表:

初始化流程图:

这个流程图实际上是SDIO模式的,SPI在识别V1卡上有区别,然后并没有进入识别状态和请求RCA的过程。

1.上电后将SPI模式设置为低速模式要求小于400Kb/s。然后片选

2.等待至少47个CLK时间,一般发送8个无效字节即可

3.发送CMD0直至卡进入空闲状态(r1==0x01)

4.检查卡类型,发送CMD8,若有回应则是V2.0卡需进行后续判别,V1卡的识别是发送CMD8失败后,继续发送CMD55+ACMD41然后根据r1返回值小于等于1则是V1.0卡,不响应就要判断MMC和其他不受支持的卡。判断MMC卡是继续发送CMD1,如果响应是MMC卡,否则不受支持,均结束初始化。SPI模式中没有获取RCA步骤。

5.如果确定是V2.0卡,为获得R3回复需再发送4个字节,第3第4字节分别为0x01和0xAA则支持2.7-3.6V。

6.再循环发送CMD55+ACMD41,直到返回0x00,进入Ready状态

7.发送CMD58,获得R1回复后再发送4字节获得OCR值(R3回复)。buff[0]&0x40就是V2HC,否则就是V2标准容量。

结束初始化。

数据传输:

SD卡单块读取流程:

发送CMD17开始读取数据块并等待R1响应,后继续等待数据起始令牌0xFE,然后接收数据,禁止片选即可完成读取。

对于标准容量卡数据块大小由CMD16设置,SDHC卡就是512字节

多块读取:

发送CMD18,等待R1响应,等待起始数据令牌0XFE,读第一块,等待数据令牌0xFE,读第二块

...

发送CMD12后等待R1

禁止片选

SD单块写入:

发CMD24,等待响应,发送数据起始令牌0xFE,写入第一数据块,检测MISO是否为低(低表示SD卡忙),MISO为高后发送两字节伪CRC 0xFF,禁止片选

多块写:

发ACMD23预擦除,发ACMD25开始写,发送起始令牌0xFC,写入第一个数据块,等待不忙了再写2字节伪CRC 0xFF,继续发起始令牌,第二块数据.....,发送结束令牌0xFD,禁止片选。

驱动代码:

cpp 复制代码
//mmc_sd.h
/*
GPIO初始化要求 片选线需要推挽输出默认输出高电平,高速。片选线低电平有效
SPI选用CPOL=1,CPHA=1,全双工,256分频,Motorola模式,软件NSS,不使用CRC,禁用TI模式
*/
#ifndef __SD_H
#define __SD_H

#include "main.h"
#include "spi.h"
#include <stdio.h>
//片选线宏定义,CubeMX初始化后还需要在这里设置一下宏定义
#define SD_CS_GPIO_Port GPIOA
#define SD_CS_Pin GPIO_PIN_3

#define SD_SPI hspi1

extern uint8_t SD_TYPE;

//SD卡类型
#define ERR     	0x00
#define MMC				0x01
#define V1				0x02
#define V2				0x04
#define V2HC			0x06

#define DUMMY_BYTE				 0xFF 
#define MSD_BLOCKSIZE			 512


//CMD定义
#define CMD0    0       //卡复位
#define CMD1    1
#define CMD8    8       //命令8 ,SEND_IF_COND
#define CMD9    9       //命令9 ,读CSD数据
#define CMD10   10      //命令10,读CID数据
#define CMD12   12      //命令12,停止数据传输
#define CMD16   16      //命令16,设置SectorSize 应返回0x00
#define CMD17   17      //命令17,读sector
#define CMD18   18      //命令18,读Multi sector
#define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
#define CMD24   24      //命令24,写sector
#define CMD25   25      //命令25,写Multi sector
#define CMD41   41      //命令41,应返回0x00
#define CMD55   55      //命令55,应返回0x01
#define CMD58   58      //命令58,读OCR信息
#define CMD59   59      //命令59,使能/禁止CRC,应返回0x00

//数据写入回应字意义
#define MSD_DATA_OK                0x05
#define MSD_DATA_CRC_ERROR         0x0B
#define MSD_DATA_WRITE_ERROR       0x0D
#define MSD_DATA_OTHER_ERROR       0xFF
//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR      0x00
#define MSD_IN_IDLE_STATE          0x01
#define MSD_ERASE_RESET            0x02
#define MSD_ILLEGAL_COMMAND        0x04
#define MSD_COM_CRC_ERROR          0x08
#define MSD_ERASE_SEQUENCE_ERROR   0x10
#define MSD_ADDRESS_ERROR          0x20
#define MSD_PARAMETER_ERROR        0x40
#define MSD_RESPONSE_FAILURE       0xFF


enum _CD_HOLD
{
	HOLD = 0,
	RELEASE = 1,
};

typedef struct               /* Card Specific Data */
{
  uint8_t  CSDStruct;            /* CSD structure */
  uint8_t  SysSpecVersion;       /* System specification version */
  uint8_t  Reserved1;            /* Reserved */
  uint8_t  TAAC;                 /* Data read access-time 1 */
  uint8_t  NSAC;                 /* Data read access-time 2 in CLK cycles */
  uint8_t  MaxBusClkFrec;        /* Max. bus clock frequency */
  uint16_t CardComdClasses;      /* Card command classes */
  uint8_t  RdBlockLen;           /* Max. read data block length */
  uint8_t  PartBlockRead;        /* Partial blocks for read allowed */
  uint8_t  WrBlockMisalign;      /* Write block misalignment */
  uint8_t  RdBlockMisalign;      /* Read block misalignment */
  uint8_t  DSRImpl;              /* DSR implemented */
  uint8_t  Reserved2;            /* Reserved */
  uint32_t DeviceSize;           /* Device Size */
  uint8_t  MaxRdCurrentVDDMin;   /* Max. read current @ VDD min */
  uint8_t  MaxRdCurrentVDDMax;   /* Max. read current @ VDD max */
  uint8_t  MaxWrCurrentVDDMin;   /* Max. write current @ VDD min */
  uint8_t  MaxWrCurrentVDDMax;   /* Max. write current @ VDD max */
  uint8_t  DeviceSizeMul;        /* Device size multiplier */
  uint8_t  EraseGrSize;          /* Erase group size */
  uint8_t  EraseGrMul;           /* Erase group size multiplier */
  uint8_t  WrProtectGrSize;      /* Write protect group size */
  uint8_t  WrProtectGrEnable;    /* Write protect group enable */
  uint8_t  ManDeflECC;           /* Manufacturer default ECC */
  uint8_t  WrSpeedFact;          /* Write speed factor */
  uint8_t  MaxWrBlockLen;        /* Max. write data block length */
  uint8_t  WriteBlockPaPartial;  /* Partial blocks for write allowed */
  uint8_t  Reserved3;            /* Reserded */
  uint8_t  ContentProtectAppli;  /* Content protection application */
  uint8_t  FileFormatGrouop;     /* File format group */
  uint8_t  CopyFlag;             /* Copy flag (OTP) */
  uint8_t  PermWrProtect;        /* Permanent write protection */
  uint8_t  TempWrProtect;        /* Temporary write protection */
  uint8_t  FileFormat;           /* File Format */
  uint8_t  ECC;                  /* ECC code */
  uint8_t  CSD_CRC;              /* CSD CRC */
  uint8_t  Reserved4;            /* always 1*/
}
MSD_CSD;

typedef struct				 /*Card Identification Data*/
{
  uint8_t  ManufacturerID;       /* ManufacturerID */
  uint16_t OEM_AppliID;          /* OEM/Application ID */
  uint32_t ProdName1;            /* Product Name part1 */
  uint8_t  ProdName2;            /* Product Name part2*/
  uint8_t  ProdRev;              /* Product Revision */
  uint32_t ProdSN;               /* Product Serial Number */
  uint8_t  Reserved1;            /* Reserved1 */
  uint16_t ManufactDate;         /* Manufacturing Date */
  uint8_t  CID_CRC;              /* CID CRC */
  uint8_t  Reserved2;            /* always 1 */
}
MSD_CID;

typedef struct
{
  MSD_CSD CSD;
  MSD_CID CID;
  uint32_t Capacity;              /* Card Capacity */
  uint32_t BlockSize;             /* Card Block Size */
  uint16_t RCA;
  uint8_t CardType;
  uint32_t SpaceTotal;            /* Total space size in file system */
  uint32_t SpaceFree;      	     /* Free space size in file system */
}
MSD_CARDINFO, *PMSD_CARDINFO;

extern MSD_CARDINFO SD0_CardInfo;

//SD_Init需要在CubeMX提前配置好SPI和CS线(GPIO High Level,High Speed,Push Up,PushPull)
uint8_t		 	SD_init(void);
void 				SD_CS(uint8_t p);
//得到的值右移11位即为MB
uint32_t  	SD_GetSectorCount(void);
uint8_t 		SD_GETCID (uint8_t *cid_data);
uint8_t 		SD_GETCSD(uint8_t *csd_data);
int 				MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo);
uint8_t			SD_ReceiveData(uint8_t *data, uint16_t len);
uint8_t 		SD_SendBlock(uint8_t*buf,uint8_t cmd);
//主要导出函数
uint8_t 		SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);
uint8_t 		SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);


void SPI_setspeed(uint8_t speed);
uint8_t spi_readwrite(uint8_t Txdata);
void Get_SDCard_Capacity(void);
//void WritetoSD(char filename[], uint8_t write_buff[], uint8_t bufSize);

#endif
cpp 复制代码
//mmc_sd.c
#include "mmc_sd.h"


uint8_t DFF=0xFF;
uint8_t test;
uint8_t SD_TYPE=0x00;

MSD_CARDINFO SD0_CardInfo;

//
//片选
//
void SD_CS(uint8_t p){
	if(p==0)
		{	
			HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_SET);
		}else
		{
			HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_RESET);
		}
}
///
//发送命令,发完释放
//
int SD_sendcmd(uint8_t cmd,uint32_t arg,uint8_t crc){
	uint8_t r1;
  uint8_t retry;
	printf("\r\nCMD:0x%2X\r\n",cmd);
  SD_CS(0);
	HAL_Delay(20);
  SD_CS(1);
	do{
		retry=spi_readwrite(DFF);
	}while(retry!=0xFF);
	uint8_t Txdata[6]={cmd|0x40,arg>>24,arg>>16,arg>>8,arg,crc};
	HAL_SPI_Transmit(&SD_SPI,Txdata,6,100);  
  if(cmd==CMD12)
	{
		spi_readwrite(DFF);
	}
  do
	{
		HAL_SPI_Receive(&SD_SPI,&r1,1,0xFF);
	}while(r1&0X80);
	
	return r1;
}


//SD卡初始化

uint8_t SD_init(void)
{
	uint8_t r1;	
	uint8_t buff[6] = {0};
	uint16_t retry; 
	uint8_t i;
	uint8_t data=0xff;
	SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
	SD_CS(0);
	//等待至少74个低速时钟周期
	for(retry=0;retry<10;retry++){
			HAL_SPI_Transmit(&SD_SPI,&data,1,0xFF);
	}
	//SD卡进入IDLE状态
	do{
		r1 = SD_sendcmd(CMD0 ,0, 0x95);	
	}while(r1!=0x01);
	
	//查看SD卡的类型
	SD_TYPE=0;
	r1 = SD_sendcmd(CMD8, 0x1AA, 0x87);
	if(r1==0x01)//SD V2.0
	{
		for(i=0;i<4;i++)
		{
			buff[i]=spi_readwrite(DFF);	//Get trailing return value of R7 resp
		}
		if(buff[2]==0X01 && buff[3]==0XAA)//卡是否支持2.7~3.6V
		{
			retry=0XFFFE;
			//等待卡进入Ready状态
			do
			{
				SD_sendcmd(CMD55,0,0X01);	//发送CMD55
				r1=SD_sendcmd(CMD41,0x40000000,0X01);//发送CMD41
			}while(r1&&retry--);
			
			if(retry&&SD_sendcmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
			{
				for(i=0;i<4;i++)buff[i]=spi_readwrite(0XFF);//得到OCR值
				//检查CCS位是否为1
				if(buff[0]&0x40)
				{
					SD_TYPE=V2HC;
				}
				else 
				{
					SD_TYPE=V2;
				}						
			}
		}
	}
	else//SD V1.x/ MMC	V3
	{
			SD_sendcmd(CMD55,0,0X01);			//发送CMD55
			r1=SD_sendcmd(CMD41,0,0X01);	//发送CMD41
			if(r1<=1)
			{		
				SD_TYPE=V1;
				retry=0XFFFE;
				do //等待退出IDLE模式
				{
					SD_sendcmd(CMD55,0,0X01);	//发送CMD55
					r1=SD_sendcmd(CMD41,0,0X01);//发送CMD41
				}while(r1&&retry--);
			}
			else//MMC卡不支持CMD55+CMD41识别
			{
				SD_TYPE=MMC;//MMC V3
				retry=0XFFFE;
				do //等待退出IDLE模式
				{											    
					r1=SD_sendcmd(CMD1,0,0X01);//发送CMD1
				}while(r1&&retry--);  
			}
			if(retry==0||SD_sendcmd(CMD16,512,0X01)!=0)SD_TYPE=ERR;//错误的卡
	}
	SD_CS(0);
	SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
	if(SD_TYPE)return 0;
	else return r1;
}


//读取指定长度数据
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
{

   uint8_t r1;
   SD_CS(1);									   
   do
   { 
      r1 = spi_readwrite(0xFF);	
      HAL_Delay(100);
		}while(r1 != 0xFE);	
  while(len--)
  {
   *data = spi_readwrite(0xFF);
   data++;
  }
	//接收两个无效的CRC和停止位
  spi_readwrite(0xFF);
  spi_readwrite(0xFF); 										  		
  return 0;
}
//向sd卡写入一个数据包的内容 512字节
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{	
	uint16_t t;	
uint8_t r1;	
	do{
		r1=spi_readwrite(0xFF);
	}while(r1!=0xFF);
	
	spi_readwrite(cmd);
	if(cmd!=0XFD)//不是结束指令
	{
		for(t=0;t<512;t++)spi_readwrite(buf[t]);//提高速度,减少函数传参时间
	    spi_readwrite(0xFF);//忽略crc
	    spi_readwrite(0xFF);
		t=spi_readwrite(0xFF);//接收响应
		if((t&0x1F)!=0x05)return 2;//响应错误									  					    
	}						 									  					    
    return 0;//写入成功
}

//获取CID信息
uint8_t SD_GETCID (uint8_t *cid_data)
{
		uint8_t r1;
	  r1=SD_sendcmd(CMD10,0,0x01); //读取CID寄存器
		if(r1==0x00){
			r1=SD_ReceiveData(cid_data,16);
		}
		SD_CS(0);
		if(r1)return 1;
		else return 0;
}
//获取CSD信息
uint8_t SD_GETCSD(uint8_t *csd_data){
		uint8_t r1;	 
    r1=SD_sendcmd(CMD9,0,0x01);//发CMD9命令,读CSD寄存器
    if(r1==0)
	{
    	r1=SD_ReceiveData(csd_data, 16);//接收16个字节的数据 
    }
	SD_CS(0);//取消片选
	if(r1)return 1;
	else return 0;
}
//获取SD卡的总扇区数,右移11位就是MB单位
uint32_t SD_GetSectorCount(void)
{
    uint8_t csd[16];
    uint32_t Capacity;  
    uint8_t n;
		uint16_t csize;  					    
	//取CSD信息,如果期间出错,返回0
    if(SD_GETCSD(csd)!=0) return 0;	    
    //如果为SDHC卡,按照下面方式计算
    if((csd[0]&0xC0)==0x40)	 //V2.00的卡
    {	
		csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
		Capacity = (uint32_t)csize << 10;//得到扇区数	 		   
    }else//V1.XX的卡
    {	
		n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
		csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
		Capacity= (uint32_t)csize << (n - 9);//得到扇区数   
    }
    return Capacity;
}
int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)
{
  uint8_t r1;
  uint8_t CSD_Tab[16];
  uint8_t CID_Tab[16];

  /* Send CMD9, Read CSD */
  r1 = SD_sendcmd(CMD9, 0, 0xFF);
  if(r1 != 0x00)
  {
    return r1;
  }

  if(SD_ReceiveData(CSD_Tab, 16))
  {
	return 1;
  }

  /* Send CMD10, Read CID */
  r1 = SD_sendcmd(CMD10, 0, 0xFF);
  if(r1 != 0x00)
  {
    return r1;
  }

  if(SD_ReceiveData(CID_Tab, 16))
  {
	return 2;
  }  

  /* Byte 0 */
  SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
  SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
  SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;
  /* Byte 1 */
  SD0_CardInfo->CSD.TAAC = CSD_Tab[1] ;
  /* Byte 2 */
  SD0_CardInfo->CSD.NSAC = CSD_Tab[2];
  /* Byte 3 */
  SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];
  /* Byte 4 */
  SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;
  /* Byte 5 */
  SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
  SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
  /* Byte 6 */
  SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
  SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
  SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
  SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
  SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */
  SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
  /* Byte 7 */
  SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
  /* Byte 8 */
  SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
  SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
  SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
  /* Byte 9 */
  SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
  SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
  SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
  /* Byte 10 */
  SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
  SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
  SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
  /* Byte 11 */
  SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
  SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
  /* Byte 12 */
  SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
  SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
  SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
  SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
  /* Byte 13 */
  SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
  SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
  SD0_CardInfo->CSD.Reserved3 = 0;
  SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
  /* Byte 14 */
  SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
  SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
  SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
  SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
  SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
  SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);
  /* Byte 15 */
  SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
  SD0_CardInfo->CSD.Reserved4 = 1;

  if(SD0_CardInfo->CardType == V2HC)
  {
	 /* Byte 7 */
	 SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) *256;
	 /* Byte 8 */
	 SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9] ;
  }

  SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;
  SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;

  /* Byte 0 */
  SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];
  /* Byte 1 */
  SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;
  /* Byte 2 */
  SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];
  /* Byte 3 */
  SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;
  /* Byte 4 */
  SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;
  /* Byte 5 */
  SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;
  /* Byte 6 */
  SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];
  /* Byte 7 */
  SD0_CardInfo->CID.ProdName2 = CID_Tab[7];
  /* Byte 8 */
  SD0_CardInfo->CID.ProdRev = CID_Tab[8];
  /* Byte 9 */
  SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;
  /* Byte 10 */
  SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;
  /* Byte 11 */
  SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;
  /* Byte 12 */
  SD0_CardInfo->CID.ProdSN |= CID_Tab[12];
  /* Byte 13 */
  SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
  /* Byte 14 */
  SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
  /* Byte 15 */
  SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];
  /* Byte 16 */
  SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
  SD0_CardInfo->CID.Reserved2 = 1;

  return 0;  
}


//可跨block写SD卡,写的数量是sector*cnt,如果buf不够长则继续向下写入乱码
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
	uint8_t r1;
	if(SD_TYPE!=V2HC)sector *= 512;//转换为字节地址
	if(cnt==1)
	{
		r1=SD_sendcmd(CMD24,sector,0X01);//标准卡是写SEL_BLOCK_LEN字节,SDHC写入512字节块命令
		if(r1==0)//指令发送成功
		{
			r1=SD_SendBlock(buf,0xFE);//写512个字节	   
		}
	}
	else
	{
		if(SD_TYPE!=MMC)
		{
			SD_sendcmd(CMD55,0,0X01);	
			SD_sendcmd(CMD23,cnt,0X01);//发送指令	
		}
 		r1=SD_sendcmd(CMD25,sector,0X01);//连续写SD卡命令
		if(r1==0)
		{
			do
			{
				r1=SD_SendBlock(buf,0xFC);//接收512个字节	 
				buf+=512;  
			}while(--cnt && r1==0);
			r1=SD_SendBlock(0,0xFD);//接收512个字节 
		}
	}   
	SD_CS(0);//取消片选
	return r1;//
}	
//读SD卡
//buf:数据缓存区
//sector:扇区
//cnt:扇区数
//返回值:0,ok;其他,失败. 2是缓冲区溢出告警
uint8_t SD_ReadDisk(uint8_t *buf,uint32_t sector,uint8_t cnt)
{
	if(sector*cnt > sizeof(buf))return 2;
	uint8_t r1;
	if(SD_TYPE!=V2HC)sector <<= 9;//转换为字节地址
	if(cnt==1)
	{
		r1=SD_sendcmd(CMD17,sector,0X01);//读命令
		if(r1==0)//指令发送成功
		{
			r1=SD_ReceiveData(buf,512);//接收512个字节	   
		}
	}else
	{
		r1=SD_sendcmd(CMD18,sector,0X01);//连续读命令
		do
		{
			r1=SD_ReceiveData(buf,512);//接收512个字节	 
			buf+=512;  
		}while(--cnt && r1==0); 	
		SD_sendcmd(CMD12,0,0X01);	//发送停止命令
	}   
	SD_CS(0);//取消片选
	return r1;//
}




uint8_t spi_readwrite(uint8_t Txdata){
	uint8_t Rxdata;	
	HAL_SPI_TransmitReceive(&hspi1,&Txdata,&Rxdata,1,100);
	return Rxdata;
}
//SPI1波特率设置
void SPI_setspeed(uint8_t speed){
	 assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    __HAL_SPI_DISABLE(&SD_SPI);            //关闭SPI
    SD_SPI.Instance->CR1&=0XFFC7;          //位3-5清零,用来设置波特率
    SD_SPI.Instance->CR1|=speed;//设置SPI速度
    __HAL_SPI_ENABLE(&SD_SPI);             //使能SPI
}


void Get_SDCard_Capacity(void)
{
	uint8_t res;
	
	res = SD_init();		//SD卡初始化
	if(res == 1)
	{
		printf("SD卡初始化失败! \r\n");		
	}
	else
	{
		printf("SD卡初始化成功! \r\n");		
	}
		
} 

///END//

参考文献:

36. SD卡---读写测试(SPI模式) --- [野火]STM32库开发实战指南------基于野火MINI开发板 文档

stm32中的SDIO_stm32 sdio-CSDN博客

stm32读写SD卡(SPI模式)_spi sd卡-CSDN博客

还有正点原子的讲解视频

相关推荐
天庭鸡腿哥1 小时前
国外软件,安装即时专业版!
stm32·microsoft·macos·everything
214实验室1 小时前
STM32串口打印使用printf乱码问题
stm32·单片机·嵌入式硬件
沐欣工作室_lvyiyi2 小时前
基于单片机的电厂烟道粉尘浓度检测系统(论文+源码)
单片机·嵌入式硬件·毕业设计
Groundwork Explorer3 小时前
异步框架+POLL混合方案应对ESP32 MPY多任务+TCP多连接
python·单片机
d111111111d4 小时前
什么是内存对齐?在STM32上面如何通过编辑器指令来实现内存对齐。
笔记·stm32·单片机·嵌入式硬件·学习·编辑器
bai5459365 小时前
STM32 CuberIDE 中断
stm32·单片机·嵌入式硬件
小叶子来了啊5 小时前
5Arduino 程序结构
单片机·嵌入式硬件
小叶子来了啊5 小时前
1Arduino 简介
单片机·嵌入式硬件
雾岛听风眠6 小时前
电路板维修
单片机·嵌入式硬件
少一倍的优雅6 小时前
hi3863(WS63) 智能小车 (一) 简单介绍
单片机·嵌入式硬件·harmonyos·hi3863