历史上的今天

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

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

发布者:脑力驿站 来源: eefocus关键字:SD卡  存储容量  STM32  官方库  BUG 手机看文章 扫描二维码
随时随地手机看文章

前言

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

[1] [2]
关键字:SD卡  存储容量  STM32  官方库  BUG 引用地址:SD卡存储容量的计算过程

上一篇:STM32 HardFault_Handler 硬件错误 解决办法
下一篇:解决MDK(Keil)Download图标灰色不能下载的问题

推荐阅读

2年一度的德国慕尼黑电子展近期盛大开幕,来自全球50多个国家3000余家企业围绕工业4.0、汽车、物联网等热门主题同台竞技,秀出当今最先进的技术和最热门的应用。 全球知名半导体制造商罗姆亮相展会,展示了在汽车电子和工业设备等领域的丰富产品、解决方案以及模拟技术和功率元器件等技术亮点。罗姆欧洲市场总监Gunter Richard也向eeworld记者解读了罗姆...
移动应用、基础设施与国防应用中核心技术与RF解决方案的领先供应商Qorvo®, Inc.(纳斯达克代码:QRVO)今日宣布,推出市场上性能最强大的MCU和集成电机控制和驱动控制产品---新型PAC5527电源应用控制器。Qorvo在单个片上系统(SoC)控制器可实现高效率、高性能和较长的电池寿命,采用无刷直流(BLDC)电机供电工具。 Qorvo电源管理和电机驱动产品总监Dav...
新浪数码讯 11月24日消息,据每日互动个推大数据对国内折叠屏手机市场进行数据分析显示,折叠屏手机市场正在迅猛增长,男性用户占比近8成。折叠屏手机的发展情况  每日互动个推大数据展示了折叠屏手机的发展情况。2018年10月,柔宇科技发布全球首款折叠屏手机——FlexPai(柔派);2019年,国内折叠屏手机概念随着三星和华为两家大厂的新品发...
上海市消保委对市面上常用的12款手机App进行调查,研究这些App的自动续费规则以及扣费期限,结果发现个别App居然提前3天就扣费,消费者还没察觉会员到期就自动续订下个月会员。据了解,本次调查的App涵盖在线视频、外卖点餐、音乐电台等几大类目,其中绝大多数App的自动续费扣费时间设置在到期前1天,消费者只要在当天手动取消就能停止续订。部分App的扣费...

史海拾趣

问答坊 | AI 解惑

三菱编程语言的介绍(Q系列)——SFC

  三菱Q系列编程语言主要有以下几种,梯形图(LAD)、指令表(LI)、顺序功能图(SFC)、结构化编程语言(ST)、功能块(FB),这五种编程语言都是符合IEC61131-3标准的编程语言,而且都可以在GX Developer里面实现。 一、SFC(顺序功能图) SFC(Se ...…

查看全部问答∨

GENESYS 2007.08 License

安捷伦科技有限公司畅销的射频EDA软件。…

查看全部问答∨

嵌入式系统

嵌入式系统的核心是嵌入式微处理器。嵌入式微处理器一般就具备以下4个特点: 1)对实时多任务有很强的支持能力,能完成多任务并且有较短的中断响应时间,从而使内部的代码和实时内核心的执行时间减少到最低限度。 2)具有功能很强的存储区保护 ...…

查看全部问答∨

合作伙伴

职位要求 1.        本科以上学历,电子、自动化等相关专业 2.        熟练掌握 C 语言,熟悉汇编语言。 3.        熟练掌握 DSP 的开发环境 4 ...…

查看全部问答∨

无线型睡眠呼吸暂停症监视系统浅析

           前言 人生约有三分之一的时间在睡眠中度过,良好的睡眠是保证身体健康的必要条件。睡眠呼吸暂停症(sleep apnea)是一种很常见的睡眠呼吸疾病,据调查,在美国约有24%的成年男性及9%成年女 ...…

查看全部问答∨

以太网控制器KSZ8851, ping timeout的问题

实验板使用KSZ8851-16MLL以太网控制器 使用PC连续ping实验板直到timeout,当ping 153次、384次或者537次后,timeout; 我测试了20组,ping 153次后timeout出现了9次,384后出现7次,537出现4次。 这个似乎太规律了吧? TCP/IP协议栈问题不大, ...…

查看全部问答∨

WinCE 5.0 EBoot 和 PB 连接问题。

eboot 中设置好了 IP 地址和 MAC 地址,选择 D 开始下载。串口输出一系列 Sent BOOTME to 255.255.255.255,但是在PB下面始终找不到设备,这个大概是什么原因? …

查看全部问答∨

今天高兴,散分100!(取之于民,用之于民)

今天高兴,散分100!(取之于民,用之于民)…

查看全部问答∨

tornado安装问题

请高手帮忙: 问下tornado的安装过程中,如果从开始菜单直接运行tornado,会提示错误,这是因为安装还没有完成,在C盘Tornado2.2文件夹下你会发现一个setup.log文件,用ultraedit或vim打开,在最后一行你会发现一个注册表键值,进入注册表,找到这 ...…

查看全部问答∨

新手请教大家一个问题啊,谢谢

我想问一下,单片机项目开发,是从硬件开始,然后是软件,比如我做一个简单的流水灯试验,要买哪些零件啊,并且如何焊接啊, 如何设计原理图啊,我真的不知道从何下手,高手教教我啊,谢谢了…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved