历史上的今天
返回首页

历史上的今天

今天是:2024年11月25日(星期一)

正在发生

2021年11月25日 | SD卡存储容量的计算过程

2021-11-25 来源:eefocus

前言

SD卡底层驱动代码量不小,功能稍微有点复杂,其他的功能不说了;本博文主要介绍SD卡V1.0和V2.0版本的SD卡的容量结算;

在对SD卡进行FATFS文件系统(最新R0.13c版本)移植时,接口函数DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void *buff )会获取SD卡的三个重要信息作为f_fdisk和f_mkfs函数为整个SD卡分区和挂载文件系统的依据;

下面的代码来自于STM32官方提供的固件库的SD卡例程,但是:例程里边有错误的地方需要修改,如果不修改有可能会影响到FatFS系统移植时分区的问题,在博文的最后有特别指出;

CSD寄存器(Card-Specific Data register 卡特性数据寄存器)

看懂本文需要有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,严格的说是一个移植不兼容的问题);详细问题情况如下:


我的内存卡是

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中;

推荐阅读

史海拾趣

GeneSiC公司的发展小趣事

FWBELL公司在电子行业中的五个发展故事

故事一:创立与早期发展

FWBELL公司,全称F.W. Bell Inc.,由Floyd Bell于1944年在美国创立。创立之初,公司专注于设计和制造霍尔发电机,这一创新性的产品迅速在市场中获得了认可。在随后的几十年里,FWBELL始终保持着在测量电流和磁场仪器领域的领先地位。从第一批配备固态电子设备的高斯计,到后续不断迭代的产品线,FWBELL的技术革新推动了整个行业的发展。这段早期的发展历程奠定了FWBELL在电子行业中的坚实基础。

故事二:产品线扩展与全球影响力

随着技术的不断进步和市场需求的扩大,FWBELL的产品线逐渐扩展,涵盖了高斯计、特斯拉计、霍尔元件、霍尔电流传感器以及标准磁铁等多个领域。这些产品凭借其卓越的性能和极高的性价比,在全球范围内赢得了广泛的客户群体。FWBELL的产品不仅被用于商业运输、军事、公务和通用航空等高端领域,还深入到了石油和天然气、工业、医疗以及空间探索等多个行业。这种广泛的行业覆盖进一步提升了FWBELL在全球电子行业中的影响力。

故事三:技术革新与领导地位

FWBELL一直致力于技术革新,不断推出具有领先技术水平的新产品。例如,其高斯计和特斯拉计采用了特有的探头动态补偿技术,确保了量程内的基本精度,并内置软件消除了复杂的校准过程。这些技术创新使得FWBELL的产品在性能上始终保持领先地位。同时,FWBELL还积极参与国际标准的制定和推广工作,为整个行业的发展贡献了自己的力量。

故事四:加入Meggitt集团与资源整合

2010年,FWBELL成为了Meggitt PLC.的全资子公司OECO LLC的一部分。这一战略性的整合为FWBELL带来了更多的资源和支持。Meggitt是一家全球工程集团,专注于为航空航天、国防和能源市场提供极端环境组件和智能子系统。加入Meggitt后,FWBELL得以与其他行业先驱如Securaplane、TFE Electronics和Artus等共同合作,进一步提升了其在电子行业中的竞争力。

故事五:中国市场拓展与本地化服务

近年来,FWBELL积极拓展中国市场,并在中国设立了分支机构以提供更便捷的本地化服务。这些分支机构不仅负责产品的销售和推广工作,还为客户提供专业的技术支持和售后服务。通过与中国本土企业的合作与交流,FWBELL得以更好地了解中国市场的需求变化和技术趋势,从而不断优化其产品和服务以满足客户的实际需求。这一系列的举措不仅加深了FWBELL在中国市场的影响力,也为其在全球范围内的持续发展奠定了坚实的基础。

芯旺微电子(ChipON)公司的发展小趣事

在汽车电子领域,AEC-Q100品质认证是衡量MCU产品质量的重要标准。芯旺微电子通过不懈的努力和技术创新,成功研发出满足AEC-Q100品质认证的MCU产品,并广泛应用于汽车前装市场。这一突破不仅证明了芯旺微电子的技术实力,也为其赢得了国内外众多知名汽车厂商的信赖和合作。

Cyrustek公司的发展小趣事

Cyrustek公司自创立之初,就注重技术研发和创新。在电子行业的激烈竞争中,Cyrustek凭借其独特的芯片设计理念和先进的生产工艺,成功推出了一系列高性能、低功耗的芯片产品。这些产品不仅满足了市场对于高品质电子产品的需求,也帮助Cyrustek在市场上建立了良好的口碑。随着技术的不断进步和产品的不断升级,Cyrustek逐渐在电子行业中崭露头角。

Exclara Inc公司的发展小趣事

随着LED技术的不断发展和市场需求的不断变化,Exclara看到了新的市场机遇。公司凭借在LED领域的技术积累和市场经验,成功进军了智能家居、智能照明等新兴市场。在这些新兴市场中,公司凭借其独特的技术优势和创新能力,迅速获得了市场份额和客户的认可。面对新的市场挑战和机遇,Exclara将继续保持创新精神和技术实力,为电子行业的发展贡献更多的力量。

请注意,以上故事框架仅供参考,具体内容和细节需要根据实际情况进行补充和完善。

Crocus公司的发展小趣事

随着产品性能的不断提升和市场需求的持续增长,Crocus开始积极拓展市场。公司加强与全球知名企业的合作,共同推动TMR传感器技术在各个领域的应用。同时,Crocus还注重品牌建设,通过参加国际电子展览、发布技术论文等方式提升品牌知名度和影响力。

ERGOBAHCO公司的发展小趣事

ERGOBAHCO公司成立于20世纪90年代初,当时正值电子行业快速发展的黄金时期。公司创始人李明(化名)看准了市场对于高质量电子配件的需求,决定从电子连接器这一细分领域入手。然而,初创时期公司面临着资金短缺、技术落后等诸多挑战。李明凭借对市场敏锐的洞察力,成功争取到了几笔关键的投资,并带领团队攻克了一系列技术难关。通过不懈努力,ERGOBAHCO公司逐渐在电子连接器领域站稳了脚跟。

问答坊 | AI 解惑

arm

ARM入门笔记…

查看全部问答>

FPGA可综合性对初学着的一些建议

FPGA可综合性对初学着的一些建议一、HDL不是硬件设计语言 过去笔者曾碰到过不少VHDL或Verilog HDL的初学者问一些相似的问题,诸如如何实现除法、开根号,如何写循环语句等等。在这个论坛上,也时常能看到一些网友提出这一类的问题。 对于这些问 ...…

查看全部问答>

用dds9854线性扫频,幅度变化的问题

dds9854 FM CHIRP模式线性扫频,从20M到40M,幅度下降很明显,用反sinc滤波器效果也不好,有什么好方法,或者我想做个功放,让增益从20M到40M增加,补偿一下,不知道怎么做,大侠们帮帮小弟,在此谢过!…

查看全部问答>

IC (es56031)时序问题

有哪位弄过ES56031混响吗? 我按照datasheet的时序,采用UCOM模式,依次发送D4,D3,D2,D1,SHEEP = 10110,但是就是得不到正确的延时啊,总是得到最小的那个延时12MS,我怀疑是一个初始值,也就是说我没有设置成功。…

查看全部问答>

多线程实质是什么?

多线程实质是什么? …

查看全部问答>

首家外包网络服务平台问世

  “外包在线”网络技术有限公司CEO喻烜为大家讲述了她鲜为人知的创业经历,从初识“外包”到立志创业,从寻求投资到初有成就,借由这朵铿锵玫瑰坚韧不拔的毅力和非凡的智慧,国内首家外包服务网络平台终于问世,自此“外包”服务更加平民化,从 ...…

查看全部问答>

嵌入式开发经典网站集锦

国内站点: 华恒公司的主页,里面有很多的相关资料,有待大家去发现 http://www.hhcn.com/chinese/embedlinux-res.html SkyEye嵌入式硬件仿真项目 www.skyeye.org http://gro.clinux.org/projects/skyeye/ 公社的SkyEye项目专栏 http://www.linuxfa ...…

查看全部问答>

士大夫

                                 士大夫…

查看全部问答>

想问一下传感器断线检测的问题,求助大侠!!!

如题。对于输出电压信号的传感器,如热电偶等,我们一般先采用运放对微弱信号进行放大,然后再用内置AD的单片机进行采样。那么我想问一下,如何对热电偶进行断线检测呢? 另外,若是PT100,或是热敏电阻,那么又是如何进行断线检测呢…

查看全部问答>

(转)树莓派购买建议

先要普及一下,正版树莓派目前世面上,从颜色来分,有绿色板子(UK和国产)、红色板子(国产)、蓝色板子(国外),分别简称绿版、红板、蓝版;从性能来说目前(2013.3.23)最高内存为512M,CPU为700ma,其余的都是山寨或者仿制的,或者打着树莓派 ...…

查看全部问答>