SD卡存储容量的计算过程(附带修正STM32官方库里SD卡例程的一个BUG)

30 篇文章 11 订阅
10 篇文章 2 订阅
前言
  1. SD卡底层驱动代码量不小,功能稍微有点复杂,其他的功能不说了;本博文主要介绍SD卡V1.0和V2.0版本的SD卡的容量结算;
  2. 在对SD卡进行FATFS文件系统(最新R0.13c版本)移植时,接口函数DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void *buff )会获取SD卡的三个重要信息作为f_fdiskf_mkfs函数为整个SD卡分区挂载文件系统的依据;
  3. 下面的代码来自于STM32官方提供的固件库的SD卡例程,但是:例程里边有错误的地方需要修改,如果不修改有可能会影响到FatFS系统移植时分区的问题,在博文的最后有特别指出;
  4. CSD寄存器(Card-Specific Data register 卡特性数据寄存器)
  5. 看懂本文需要有SD卡开发经验和FATFS文件系统移植经验基础的同志;
1.获取并处理CSD寄存器数据

步骤:

  • 获取SD卡CSD寄存器数据。执行SD_GetCardInfo函数,将CSD寄存器的值全部赋值到CSD结构体;要获取的数据如下表(来自SD卡V2.0协议)
    对于V1.0的卡来说,要获取下面几组数据:
    在这里插入图片描述
    对于V2.0的卡来说,只需获取一个数据:
    在这里插入图片描述
    所对应的代码下面列出来,但是注意我们所要用到的V1.0和V2.0的参数本身不多,所有我对结构体做出适当的修剪:
/** 
  * @brief  Card Specific Data: CSD Register   
  */ 
typedef struct
{
  /***省略若干行***/
  __IO uint8_t  RdBlockLen;           /*!< Max. read data block length V1.0计算公式要用到*/   
  /***省略若干行***/
  __IO uint32_t DeviceSize;           /*!< Device Size V1.0和V2.0计算公式都要用到*/   
  /***省略若干行***/
  __IO uint8_t  DeviceSizeMul;        /*!< Device size multiplier */
  /***省略若干行***/
} SD_CSD;

/** 
  * @brief SD Card information 
  * 这个结构体包含了SD卡的CSD寄存器和CID寄存器,这里我们只讨论CSD寄存器
  */
typedef struct
{
  SD_CSD SD_csd;			
  SD_CID SD_cid;					
  uint32_t CardCapacity;  /*!< Card Capacity */   
  uint32_t CardBlockSize; /*!< Card Block Size */
  uint16_t RCA;
  uint8_t CardType;
} SD_CardInfo;  //此结构体将上面两个结构体都包含了;


/**
  * @brief  Returns information about specific card.
  * @param  cardinfo: pointer to a SD_CardInfo structure that contains all SD card 
  *         information.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo)
{
  SD_Error errorstatus = SD_OK;
  uint8_t tmp = 0;

/*************************************************************/
/*****************SD_CardInfo结构体填充****************/
/*************************************************************/	

  cardinfo->CardType = (uint8_t)CardType;
  cardinfo->RCA = (uint16_t)RCA;

/**********************************************/
/*****************CSD结构体填充****************/
/**********************************************/	
	
  /*!< Byte 0 */
  tmp = (uint8_t)((CSD_Tab[0] & 0xFF000000) >> 24);
  cardinfo->SD_csd.CSDStruct = (tmp & 0xC0) >> 6;
  cardinfo->SD_csd.SysSpecVersion = (tmp & 0x3C) >> 2;
  cardinfo->SD_csd.Reserved1 = tmp & 0x03;

  /*!< Byte 1 */
  tmp = (uint8_t)((CSD_Tab[0] & 0x00FF0000) >> 16);
  cardinfo->SD_csd.TAAC = tmp;

  /*!< Byte 2 */
  tmp = (uint8_t)((CSD_Tab[0] & 0x0000FF00) >> 8);
  cardinfo->SD_csd.NSAC = tmp;

  /*!< Byte 3 */
  tmp = (uint8_t)(CSD_Tab[0] & 0x000000FF);
  cardinfo->SD_csd.MaxBusClkFrec = tmp;

  /*!< Byte 4 */
  tmp = (uint8_t)((CSD_Tab[1] & 0xFF000000) >> 24);
  cardinfo->SD_csd.CardComdClasses = tmp << 4;

  /*!< Byte 5 */
  tmp = (uint8_t)((CSD_Tab[1] & 0x00FF0000) >> 16);
  cardinfo->SD_csd.CardComdClasses |= (tmp & 0xF0) >> 4;
  cardinfo->SD_csd.RdBlockLen = tmp & 0x0F;

  /*!< Byte 6 */
  tmp = (uint8_t)((CSD_Tab[1] & 0x0000FF00) >> 8);
  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; /*!< Reserved */

  if ((CardType == SDIO_STD_CAPACITY_SD_CARD_V1_1) || (CardType == SDIO_STD_CAPACITY_SD_CARD_V2_0))
  {
    cardinfo->SD_csd.DeviceSize = (tmp & 0x03) << 10;

    /*!< Byte 7 */
    tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
    cardinfo->SD_csd.DeviceSize |= (tmp) << 2;

    /*!< Byte 8 */
    tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);
    cardinfo->SD_csd.DeviceSize |= (tmp & 0xC0) >> 6;

    cardinfo->SD_csd.MaxRdCurrentVDDMin = (tmp & 0x38) >> 3;
    cardinfo->SD_csd.MaxRdCurrentVDDMax = (tmp & 0x07);

    /*!< Byte 9 */
    tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);
    cardinfo->SD_csd.MaxWrCurrentVDDMin = (tmp & 0xE0) >> 5;
    cardinfo->SD_csd.MaxWrCurrentVDDMax = (tmp & 0x1C) >> 2;
    cardinfo->SD_csd.DeviceSizeMul = (tmp & 0x03) << 1;
    /*!< Byte 10 */
    tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
    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 == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    /*!< Byte 7 */
    tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
    cardinfo->SD_csd.DeviceSize = (tmp & 0x3F) << 16;

    /*!< Byte 8 */
    tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);

    cardinfo->SD_csd.DeviceSize |= (tmp << 8);

    /*!< Byte 9 */
    tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);

    cardinfo->SD_csd.DeviceSize |= (tmp);

    /*!< Byte 10 */
    tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
    
    cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 512 * 1024;
    cardinfo->CardBlockSize = 512;    
  }


  cardinfo->SD_csd.EraseGrSize = (tmp & 0x40) >> 6;
  cardinfo->SD_csd.EraseGrMul = (tmp & 0x3F) << 1;

  /*!< Byte 11 */
  tmp = (uint8_t)(CSD_Tab[2] & 0x000000FF);
  cardinfo->SD_csd.EraseGrMul |= (tmp & 0x80) >> 7;
  cardinfo->SD_csd.WrProtectGrSize = (tmp & 0x7F);

  /*!< Byte 12 */
  tmp = (uint8_t)((CSD_Tab[3] & 0xFF000000) >> 24);
  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;

  /*!< Byte 13 */
  tmp = (uint8_t)((CSD_Tab[3] & 0x00FF0000) >> 16);
  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);

  /*!< Byte 14 */
  tmp = (uint8_t)((CSD_Tab[3] & 0x0000FF00) >> 8);
  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);

  /*!< Byte 15 */
  tmp = (uint8_t)(CSD_Tab[3] & 0x000000FF);
  cardinfo->SD_csd.CSD_CRC = (tmp & 0xFE) >> 1;
  cardinfo->SD_csd.Reserved4 = 1;

  /***省略若干行***/

  return(errorstatus);
}

  • 通过上面一顿操作我们将想要的信息整理了出来,分别是:
    用于V1.0计算的:C_SIZE,C_SIZE_MULT,READ_BL_LEN
    用于V2.0计算的:C_SIZE;(注意两个C_SIZE的位宽是不一样的)
2. V1.0计算公式

在这里插入图片描述
代码如下:

//代码来自上面历程
	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;  //最终计算的结果,以字节为单位;
3. V2.0计算公式

在这里插入图片描述
代码如下:

	//代码来自上面历程
	cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 512 * 1024; //最终计算的结果,以字节为单位;
    cardinfo->CardBlockSize = 512;  

特别注意:尤其是V2.0的SD卡最大可以达到32G类型,防止溢出;

4. 但是这里有一个问题(内存卡小于等于4GB的请忽略下面)

问题:在对SD卡进行FatFS系统移植的时候,我在实验中发现STM32官方提供的SD卡程序只能支持0~4G以内的SD卡(其实不能说是BUG,严格的说是一个移植不兼容的问题);详细问题情况如下:

  1. 我的内存卡是
    16GB(标签)== 1610001000*1000=16000000000字节 = 16000000000/1024/1024/1024 = 14.9GB(实际);
    但是实际上厂家并不会这么精准(唉),所最终我的内存卡的容量最终如下图(我们暂且取14.6GB):
    在这里插入图片描述
  2. 下面对这个SD卡直接用上面移植来的SD卡例程和FATFS文件系统对其进行平均4分区,分区后如下:
    在这里插入图片描述
    会发现:有4个相等的有效分区,和一个12G的未分配区间,为什么会产生这个问题?14.6GB的内存不应该是都是3.65GB吗?
    原因:看下面这段代码,这段代码是FATFS文件系统中一个命令控制函数,FATFS文件系统用它来获取分区的依据;
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res = RES_PARERR;

	switch (pdrv) {
	case DEV_SPI_FLASH :

		return res;
	case DEV_SDHC :
		
		switch(cmd)
		{
			
				/* Generic command (Used by FatFs) */
			case CTRL_SYNC:					 res = RES_OK;break;		//确保设备完成了等待写过程,也就是设备数据缓冲区内的数据写入了存储介质;
			case GET_SECTOR_COUNT:	 												//获取存储设备中可用扇区数值返回到buff所指向的DWORD中;
						*(DWORD*)buff = SDCardInfo.CardCapacity/SDCardInfo.CardBlockSize;    
						res = RES_OK;break;
			case GET_SECTOR_SIZE:		 												//获取存储设备中扇区大小返回到buff所指向的DWORD中;
						*(DWORD*)buff = SDCardInfo.CardBlockSize;//SDCardInfo.CardBlockSize;	//扇区大小等于块大小;
						res = RES_OK;
						break;
			
			case GET_BLOCK_SIZE:					//GET_BLOCK_SIZE:以扇区为单位,将擦除块大小返回到buff指向的DWORD变量;
						*(DWORD*)buff = 1;
						res = RES_OK;
						break;
			case CTRL_TRIM:					 			//告诉设备此扇区块(cmd中包含的地址)不再需要,可以擦除;

						res = RES_OK;
						break;

			default:;break;
		}

		return res;
	}

	return RES_PARERR;
}

(DWORD)buff = SDCardInfo.CardCapacity/SDCardInfo.CardBlockSize; 是问题的原因,在准确点说是: SDCardInfo.CardCapacity这个结构体成员的值发生了错误,然而导致这个问题的原因是如下代码:

	cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 512 * 1024; //最终计算的结果,以字节为单位;
    cardinfo->CardBlockSize = 512;  

对,就是V2.0的计算公式;然而再准确的说,原因是:cardinfo->SD_csd.DeviceSize这个成员变量的数据类型,如下定义:

__IO uint32_t DeviceSize;           /*!< Device Size V1.0和V2.0计算公式都要用到*/   

通过计算可知:uint32_t的最大表示值为0xFFFF FFFF = (4GB-1Byte),如果超出4G,则势必会发生溢出,从而导致数据错误,再引起分区错误;

解决办法:修改如图两个地方即可:
在这里插入图片描述
在这里插入图片描述
修改后再分区:
在这里插入图片描述
基本上完成了4平分,(最后的13M,强迫症表示接受不了!!!)

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是STM32H750 SD卡SPI模式的例程,供参考: ```c #include "stm32h7xx_hal.h" #include "fatfs.h" /* SPI1 init function */ static void MX_SPI1_Init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } } /* GPIO init function */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin : PA4 */ GPIO_InitStruct.Pin = GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /*Configure GPIO pins : PA5 PA6 PA7 */ GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void SD_SPI_Init(void) { /* SPI1 parameter configuration*/ MX_SPI1_Init(); /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); /*Configure GPIO pin : PA4 */ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /*Configure GPIO pins : PA5 PA6 PA7 */ GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void SD_SPI_DeInit(void) { HAL_SPI_DeInit(&hspi1); } void SD_SPI_SetSpeed(uint8_t speed) { hspi1.Init.BaudRatePrescaler = speed; HAL_SPI_Init(&hspi1); } uint8_t SPI_RW(uint8_t data) { uint8_t tmp; HAL_SPI_TransmitReceive(&hspi1, &data, &tmp, 1, 1000); return tmp; } uint8_t SD_SPI_ReadWriteByte(uint8_t byte) { uint8_t res; HAL_SPI_TransmitReceive(&hspi1,&byte,&res,1,HAL_MAX_DELAY); return res; } void SD_SPI_WriteByte(uint8_t byte) { HAL_SPI_Transmit(&hspi1,&byte,1,HAL_MAX_DELAY); } uint8_t SD_SPI_ReadByte(void) { uint8_t byte = 0xFF; HAL_SPI_TransmitReceive(&hspi1,&byte,&byte,1,HAL_MAX_DELAY); return byte; } uint8_t SD_SPI_WaitReady(void) { uint8_t res; uint32_t retry=0; do { res = SD_SPI_ReadByte(); retry++; if(retry > 0x1FFFFFFF) return 0x01; }while(res!=0xFF); return 0; } uint8_t SD_SPI_Deselect(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); SD_SPI_ReadByte(); return 0; } uint8_t SD_SPI_Select(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); if(SD_SPI_WaitReady()) { SD_SPI_Deselect(); return 1; } return 0; } uint8_t SD_SPI_SendCommand(uint8_t cmd, uint32_t arg, uint8_t crc) { uint8_t r1; uint8_t retry = 0; SD_SPI_Deselect(); if(SD_SPI_Select()) return 0x01; SD_SPI_WriteByte(cmd | 0x40); SD_SPI_WriteByte(arg >> 24); SD_SPI_WriteByte(arg >> 16); SD_SPI_WriteByte(arg >> 8); SD_SPI_WriteByte(arg); SD_SPI_WriteByte(crc); while((r1 = SD_SPI_ReadByte()) == 0xFF) { retry++; if(retry > 0xFE) { SD_SPI_Deselect(); return 0x01; } } if(r1 != 0x00) { SD_SPI_Deselect(); return r1; } return 0; } uint8_t SD_SPI_SendCommand_NoDeassert(uint8_t cmd, uint32_t arg, uint8_t crc) { uint8_t r1; uint8_t retry = 0; if(SD_SPI_Select()) return 0x01; SD_SPI_WriteByte(cmd | 0x40); SD_SPI_WriteByte(arg >> 24); SD_SPI_WriteByte(arg >> 16); SD_SPI_WriteByte(arg >> 8); SD_SPI_WriteByte(arg); SD_SPI_WriteByte(crc); while((r1 = SD_SPI_ReadByte()) == 0xFF) { retry++; if(retry > 0xFE) { SD_SPI_Deselect(); return 0x01; } } if(r1 != 0x00) { SD_SPI_Deselect(); return r1; } return 0; } uint8_t SD_SPI_ReceiveData(uint8_t *data, uint16_t len, uint8_t release) { uint8_t retry = 0x1F; do { *data = SD_SPI_ReadByte(); data++; *data = SD_SPI_ReadByte(); data++; len -= 2; } while(len && retry--); if(release) SD_SPI_Deselect(); return len ? 0x01 : 0; } uint8_t SD_SPI_SendData(const uint8_t *data, uint8_t token) { uint8_t r1; if(!SD_SPI_Select()) return 0x01; SD_SPI_WriteByte(token); if(token != 0xFD) { while(SD_SPI_ReadByte() == 0xFF); do { SD_SPI_WriteByte(*data++); SD_SPI_WriteByte(*data++); } while(--token); SD_SPI_ReadByte(); SD_SPI_ReadByte(); r1 = SD_SPI_ReadByte() & 0x1F; if(r1 == 0x05) { r1 = 0; if(SD_SPI_WaitReady()) return 0x01; } if(SD_SPI_Deselect()) return 0x01; return r1; } else { SD_SPI_Deselect(); return 0x00; } } uint8_t SD_SPI_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc, uint8_t *resp, uint8_t resplen) { uint8_t retry = 0x1F; uint8_t ret; if(cmd & 0x80) { ret = SD_SPI_SendCommand(cmd, arg, crc); if(ret) return ret; } if(resp) { do { *resp = SD_SPI_ReadByte(); resp++; resplen--; } while(resplen && (*resp == 0xFF) && retry--); if(resplen == 0) return 0x01; } return 0x00; } uint8_t SD_SPI_Initialize(void) { uint8_t r1; uint32_t retry; SD_SPI_SetSpeed(SPI_BAUDRATEPRESCALER_256); for(retry = 0; retry < 0x100; retry++) SD_SPI_ReadByte(); HAL_Delay(100); retry = 0x1FFF; do { r1 = SD_SPI_SendCommand_NoDeassert(SD_CMD_GO_IDLE_STATE, 0, 0x95); retry--; } while((r1 != 0x01) && retry); if(!retry) return 0x01; retry = 0xFFFF; do { r1 = SD_SPI_SendCommand_NoDeassert(SD_CMD_SEND_IF_COND, 0x000001aa, 0x87); retry--; } while((r1 != 0x01) && retry); if(retry == 0) return 0x01; if(SD_SPI_SendCmd(SD_CMD_APP_CMD, 0, 0, NULL, 0x00) == 0x01) { r1 = SD_SPI_SendCmd(SD_CMD_SD_SEND_OP_COND, 0x40300000, 0, NULL, 0x00); if(r1 == 0x00) { r1 = SD_SPI_SendCmd(SD_CMD_READ_OCR, 0, 0, NULL, 0x00); if(r1 == 0x00) { return 0x00; } } } return 0x01; } ``` 这是一个简单的SD卡SPI模式的例程,包括了SD卡的初始化、命令发送、数据读写等操作。需要注意的是,这个例程是基于HAL库编写的,如果你使用的是其他的库,可能需要进行一些适当的修改。另外,这个例程只是一个参考,具体的实现方式还需要根据自己的实际需求进行调整和完善。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值