前言
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中;
上一篇:STM32 HardFault_Handler 硬件错误 解决办法
下一篇:解决MDK(Keil)Download图标灰色不能下载的问题
推荐阅读
史海拾趣
为了响应国家振兴制造业的号召,培养优秀的数字化设计与制造技术人才,Extech公司在近年来启动了“PLM China”计划。该计划面向全国高校提供价值1000万人民币的软件支持,旨在与教育行业携手合作,共同提升中国制造业的整体设计研发水平。通过这一计划的实施,Extech公司不仅展现了自己的社会责任感,也为中国制造业的发展做出了积极的贡献。
以上五个故事分别从不同角度展现了Extech公司在电子行业中的发展历程和成就。通过持续的创新和努力,Extech公司已经成为了一家在制造业信息化领域具有重要影响力的企业。
为了响应国家振兴制造业的号召,培养优秀的数字化设计与制造技术人才,Extech公司在近年来启动了“PLM China”计划。该计划面向全国高校提供价值1000万人民币的软件支持,旨在与教育行业携手合作,共同提升中国制造业的整体设计研发水平。通过这一计划的实施,Extech公司不仅展现了自己的社会责任感,也为中国制造业的发展做出了积极的贡献。
以上五个故事分别从不同角度展现了Extech公司在电子行业中的发展历程和成就。通过持续的创新和努力,Extech公司已经成为了一家在制造业信息化领域具有重要影响力的企业。
随着全球对环保和可持续发展的重视,ASI也积极响应这一趋势,致力于研发更加环保、节能的半导体产品。公司不断投入资源研发低功耗、高性能的芯片技术,为推动绿色电子产业的发展贡献力量。同时,ASI也对未来充满信心,将继续加大在新技术、新应用领域的投入,为半导体行业的未来发展贡献更多的智慧和力量。
这五个故事展示了Advanced Semiconductor, Inc.在电子行业中的发展历程和取得的成就。从创立初期的艰难探索到如今的行业领先地位,ASI凭借其独特的技术、创新的精神和敏锐的市场洞察力,不断突破自我、超越自我,成为了半导体行业的一颗璀璨明星。
为了保证产品质量,Frequency Devices公司建立了完善的质量管理体系。公司引入了先进的生产设备和检测技术,对原材料采购、生产加工、成品检验等各个环节进行严格把控。同时,公司还建立了完善的售后服务体系,为客户提供及时、专业的技术支持和解决方案。这些措施有效保障了产品的质量和稳定性,赢得了客户的信赖和好评。
作为一家有社会责任感的企业,中汇瑞德一直积极履行社会责任。公司不仅关注员工的成长和福利,还积极参与社会公益事业。例如,公司定期组织员工参与环保活动,推广节能减排理念;同时,公司还设立了奖学金和助学金,支持贫困地区的教育事业。这些举措不仅提升了公司的社会形象,也增强了员工的凝聚力和归属感。
以上五个故事是根据中汇瑞德电子股份有限公司的发展历程和公开信息进行的合理推测和构建。虽然并非公司实际的发展历程,但可以从中看到一家企业在电子行业中的成长轨迹和发展脉络。
为了保证产品质量和客户满意度,中汇瑞德一直将质量管理体系的建设放在重要位置。公司引入了国际先进的质量管理理念和方法,建立了完善的质量管理体系,并通过了多项国际认证。同时,公司还加强了对供应商的管理和评估,确保原材料的质量符合公司要求。这些措施有效地提高了产品的质量稳定性和可靠性,赢得了客户的信任和认可。
三菱Q系列编程语言主要有以下几种,梯形图(LAD)、指令表(LI)、顺序功能图(SFC)、结构化编程语言(ST)、功能块(FB),这五种编程语言都是符合IEC61131-3标准的编程语言,而且都可以在GX Developer里面实现。 一、SFC(顺序功能图) SFC(Se ...… 查看全部问答∨ |
|
前言 人生约有三分之一的时间在睡眠中度过,良好的睡眠是保证身体健康的必要条件。睡眠呼吸暂停症(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协议栈问题不大, ...… 查看全部问答∨ |
eboot 中设置好了 IP 地址和 MAC 地址,选择 D 开始下载。串口输出一系列 Sent BOOTME to 255.255.255.255,但是在PB下面始终找不到设备,这个大概是什么原因? … 查看全部问答∨ |
|
请高手帮忙: 问下tornado的安装过程中,如果从开始菜单直接运行tornado,会提示错误,这是因为安装还没有完成,在C盘Tornado2.2文件夹下你会发现一个setup.log文件,用ultraedit或vim打开,在最后一行你会发现一个注册表键值,进入注册表,找到这 ...… 查看全部问答∨ |
我想问一下,单片机项目开发,是从硬件开始,然后是软件,比如我做一个简单的流水灯试验,要买哪些零件啊,并且如何焊接啊, 如何设计原理图啊,我真的不知道从何下手,高手教教我啊,谢谢了… 查看全部问答∨ |