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,禁止片选。

驱动代码:

//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 hspi1extern 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       0xFFenum _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

 

//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);	//发送CMD55r1=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位是否为1if(buff[0]&0x40){SD_TYPE=V2HC;}else {SD_TYPE=V2;}						}}}else//SD V1.x/ MMC	V3{SD_sendcmd(CMD55,0,0X01);			//发送CMD55r1=SD_sendcmd(CMD41,0,0X01);	//发送CMD41if(r1<=1){		SD_TYPE=V1;retry=0XFFFE;do //等待退出IDLE模式{SD_sendcmd(CMD55,0,0X01);	//发送CMD55r1=SD_sendcmd(CMD41,0,0X01);//发送CMD41}while(r1&&retry--);}else//MMC卡不支持CMD55+CMD41识别{SD_TYPE=MMC;//MMC V3retry=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);//忽略crcspi_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信息,如果期间出错,返回0if(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);            //关闭SPISD_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博客

还有正点原子的讲解视频

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/498985.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出 Spring (二)| 依赖注入(DI)、自动装配

3. 依赖注入&#xff08;DI&#xff09; 3.1 概念 依赖注入&#xff08;Dependency Injection,DI&#xff09;。 依赖&#xff1a;bean对象的创建依赖于容器&#xff01;注入&#xff1a;bean对象中的所有属性&#xff0c;由容器来注入&#xff01; 创建子项目 spring04-DI…

线性代数期末复习 [基础篇]

关于第六点: AXB 在期末考试中一般A都是可逆的 我们可以先把A的逆求出来,X A − 1 B A^-1B A−1B,或者 (A,B) -> r (E, A − 1 B A^-1B A−1B) 如果A矩阵不可逆,转变为方程组求解问题,假设都是二维矩阵 A(x1,x2) (b1,b2) Ax1 b1,Ax2 b2 XAB 如果A可逆,直接XB A − 1 A^-…

V90伺服PN版组态配置<一>

1、添加PLC之后&#xff0c;继续博图中网络视图中添加新设备&#xff0c;添加伺服驱动器组态设备 2、SINAMICS V90 PN V1.0 3、修改驱动器的IP地址。 【注意】 在项目中提前做好项目规划&#xff0c;如PLC设备从192.168.0.1开始&#xff0c;顺序递增------个位数都是CPU设备…

深度学习笔记(12)——深度学习概论

深度学习概论 深度学习关系&#xff1a; 为什么机器人有一部分不在人工智能里面&#xff1a;机器人技术是一个跨学科的领域&#xff0c;它结合了机械工程、电子工程、计算机科学以及人工智能&#xff08;AI&#xff09;等多个领域的知识。 并不是所有的机器人都依赖于人工智能…

TCP-UDP调试工具推荐:Socket通信测试教程(附详细图解)

前言 在网络编程与应用开发中&#xff0c;调试始终是一项不可忽视的重要环节。尤其是在涉及TCP/IP、UDP等底层网络通信协议时&#xff0c;如何确保数据能够准确无误地在不同节点间传输&#xff0c;是许多开发者关注的核心问题。 调试的难点不仅在于定位连接建立、数据流控制及…

使用three.js 实现vr全景图展示,复制即可用

1.实现效果 2.代码 1.npm安装three.js npm install three 2.引入three.js import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls 3.初始化模型 init(val) {this.container document.querySelector(.container)// 初始…

1月第二讲:WxPython跨平台开发框架之图标选择界面

1、图标分类介绍 这里图标我们分为两类&#xff0c;一类是wxPython内置的图标资源&#xff0c;以wx.Art_开始。wx.ART_ 是 wxPython 提供的艺术资源&#xff08;Art Resource&#xff09;常量&#xff0c;用于在界面中快速访问通用的图标或位图资源。这些资源可以通过 wx.ArtP…

C语言优化技巧--达夫设备(Duff‘s Device)解析

1983年&#xff0c;一位程序员TomDuff&#xff08;曾在贝尔实验室和星球大战母公司卢卡斯影业就职过&#xff09;&#xff0c;在参与图形渲染的软件中尝试优化图像数据传输到帧缓冲区的过程中遇到了性能瓶颈&#xff0c;写下了一段奇怪的代码&#xff1a; void send( int * to…

[硬件] DELL BIOS 相关注意事项

前言 前段时间重装系统. DELL BIOS属实资料少, 又难用. 这里给出相关的注意事项, 并且配上图片. BIOS相关注意事项 进入BIOS ESC/F2/ F12. 都可以进入BIOS, 当进U盘的入Win PE系统时, 使用F12 效果更佳. 关闭安全模式 切换到Boot Configuration选项,将Secure Boot选项off选…

一个完美的自动化测试框架应该怎么写?

一、什么是自动化测试框架&#xff1f; 自动化测试框架是为自动化测试用例或者脚本提供执行环境而搭建的基础设施。自动化测试框架有助于有效地开发、执行和报告自动化测试用例。 优点&#xff1a; 代码复用 提高测试效率 更高的测试覆盖率 维护成本低 更早发现和记…

从0开始的docker镜像制作-ubuntu22.04

从0开始的docker镜像制作-ubuntu22.04 一、拉取基础ubuntu22.04镜像二、进入拉取的docker镜像中&#xff0c;下载自己需要的安装包三、安装需要的系统软件四、打包现有镜像为一个新的镜像五、推送打包的镜像到私有docker服务器1.编辑docker文件&#xff0c;使其允许http传输和对…

使用exe4j将jar转成exe、java打包exe

1、maven打包 需要配置以下插件&#xff0c;注意skip为false 插件配置中设置 <skip>true</skip> 时&#xff0c;实际上是告诉 Maven 在构建过程中跳过 spring-boot-maven-plugin 插件的执行。也就是说&#xff0c;Maven 在打包时不会将项目打包成可执行的 JAR 文…

微信小程序调用 WebAssembly 烹饪指南

我们都是在夜里崩溃过的俗人&#xff0c;所幸终会天亮。明天就是新的开始&#xff0c;我们会变得与昨天不同。 一、Rust 导出 wasm 参考 wasm-bindgen 官方指南 https://wasm.rust-lang.net.cn/wasm-bindgen/introduction.html wasm-bindgen&#xff0c;这是一个 Rust 库和 CLI…

Cherno C++学习笔记 P48 如何让vector运行的更快

这一篇文章当中我们会讲到有关于vector的使用优化问题&#xff0c;如何能够让vector运行得更快。C优化是一个非常非常复杂的问题&#xff0c;最重要的是要了解环境&#xff0c;要知道事情是如何发生的&#xff0c;以及我们应该怎么做。当然我们今天会做的事情其实只是触及表面。…

MyBatis执行一条sql语句的流程(源码解析)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 MyBatis执行一条sql语句的流程&#xff08;源码解析&#xff09; MyBatis执行sql语句的流程加载配置文件加载配置文件的流程 创建sqlsessionFactory对象解析Mapper创建sqlses…

淺談Cocos2djs逆向

前言 簡單聊一下cocos2djs手遊的逆向&#xff0c;有任何相關想法歡迎和我討論^^ 一些概念 列出一些個人認為比較有用的概念&#xff1a; Cocos遊戲的兩大開發工具分別是CocosCreator和CocosStudio&#xff0c;區別是前者是cocos2djs專用的開發工具&#xff0c;後者則是coco…

吊舱激光测距核心技术详解!

一、核心技术 吊舱激光测距的核心技术主要体现在激光发射与接收、信号处理与距离计算、以及数据校正与优化等方面。 激光发射与接收&#xff1a; 激光发射器&#xff1a;产生经过调制的激光束&#xff0c;该激光束具有特定的频率和波形。这些激光束被投射到目标物体上。 光…

SpringAI从入门到熟练

学习SpringAI的记录情况 文章目录 前言 因公司需要故而学习SpringAI文档&#xff0c;故将自己所见所想写成文章&#xff0c;供大佬们参考 主要是为什么这么写呢&#xff0c;为何不抽出来呢&#xff0c;还是希望可以用的时候更加方便一点&#xff0c;如果大家有需求可以自行去…

如何使用大语言模型进行事件抽取与关系抽取

诸神缄默不语-个人CSDN博文目录 文章目录 1. 什么是事件抽取与关系抽取&#xff1f;2. 示例&#xff1a;使用大语言模型进行事件抽取与关系抽取 1. 什么是事件抽取与关系抽取&#xff1f; 事件抽取是指从文本中识别出与某些“事件”相关的信息。这些事件通常包括动作、参与者、…

GoldenDB组件及对应的用户和进程

1. GoldenDB组件及对应的用户和进程 GoldenDB数据库由管理节点、全局事务节点GTM、计算节点CN、数据节点DN等组成。 1.1. 管理节点 管理节点分为集群管理、Insight运维管理平台&#xff08;InsightServer、RDB、ZK&#xff09;。 1.1.1. 集群管理 1. 集群管理包括Metadatas…