步骤:
获取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的位宽是不一样的)代码如下:
//代码来自上面历程 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; //最终计算的结果,以字节为单位;代码如下:
//代码来自上面历程 cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 512 * 1024; //最终计算的结果,以字节为单位; cardinfo->CardBlockSize = 512;特别注意:尤其是V2.0的SD卡最大可以达到32G类型,防止溢出;
问题:在对SD卡进行FatFS系统移植的时候,我在实验中发现STM32官方提供的SD卡程序只能支持0~4G以内的SD卡(其实不能说是BUG,严格的说是一个移植不兼容的问题);详细问题情况如下:
我的内存卡是 16GB(标签)== 1610001000*1000=16000000000字节 = 16000000000/1024/1024/1024 = 14.9GB(实际); 但是实际上厂家并不会这么精准(唉),所最终我的内存卡的容量最终如下图(我们暂且取14.6GB): 下面对这个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,强迫症表示接受不了!!!)