历史上的今天
返回首页

历史上的今天

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

正在发生

2021年11月25日 | 解决STM32 SD卡官方库移植时 获取不到SD Status寄存器值的问题

2021-11-25 来源:eefocus

前言

SD卡内部的SD Status寄存器和Card Status寄存器从名字上来看好像都是状态寄存器,但实际上所要呈现的工程是不同的,这一点要注意;

本博文主要说内容是怎样正确的获取SD Status寄存器的值;

如有不足之处,还请多多指教;

问题

SD Status寄存器表如下,我想获取其中的AU_SIZE的值

在这里插入图片描述

([400:0]这段位域内没有信息,我这里忽略了)

如下图,AU_SIZE的值本身应该在0x01~0x09之间,但是我用串口打印出来的值却是0;

在这里插入图片描述

获取SD Status寄存器的函数代码如下:


/** 

  * @brief SD Card Status 

  */

typedef struct

{

  __IO uint8_t DAT_BUS_WIDTH;

  __IO uint8_t SECURED_MODE;

  __IO uint16_t SD_CARD_TYPE;

  __IO uint32_t SIZE_OF_PROTECTED_AREA;

  __IO uint8_t SPEED_CLASS;

  __IO uint8_t PERFORMANCE_MOVE;

  __IO uint8_t AU_SIZE;

  __IO uint16_t ERASE_SIZE;

  __IO uint8_t ERASE_TIMEOUT;

  __IO uint8_t ERASE_OFFSET;

} SD_CardStatus;


static uint8_t SDSTATUS_Tab[16];                            //SD状态表

SD_CardStatus SDCardStatus;


/**

  * @brief  Enables wide bus opeartion for the requeseted card if supported by 

  *         card.

  * @param  WideMode: Specifies the SD card wide bus mode. 

  *   This parameter can be one of the following values:

  *     @arg SDIO_BusWide_8b: 8-bit data transfer (Only for MMC)

  *     @arg SDIO_BusWide_4b: 4-bit data transfer

  *     @arg SDIO_BusWide_1b: 1-bit data transfer

  * @retval SD_Error: SD Card Error code.

  */

SD_Error SD_GetCardStatus(SD_CardStatus *cardstatus)

{

  SD_Error errorstatus = SD_OK;

  uint8_t tmp = 0;

  


  errorstatus = SD_SendSDStatus((uint32_t *)SDSTATUS_Tab); //程序卡死在这个函数里; 


  if (errorstatus  != SD_OK)

  {

    return(errorstatus);

  }


  /*!< Byte 0 */

  tmp = (uint8_t)((SDSTATUS_Tab[0] & 0xC0) >> 6);

  cardstatus->DAT_BUS_WIDTH = tmp;


  /*!< Byte 0 */

  tmp = (uint8_t)((SDSTATUS_Tab[0] & 0x20) >> 5);

  cardstatus->SECURED_MODE = tmp;


  /*!< Byte 2 */

  tmp = (uint8_t)((SDSTATUS_Tab[2] & 0xFF));

  cardstatus->SD_CARD_TYPE = tmp << 8;


  /*!< Byte 3 */

  tmp = (uint8_t)((SDSTATUS_Tab[3] & 0xFF));

  cardstatus->SD_CARD_TYPE |= tmp;


  /*!< Byte 4 */

  tmp = (uint8_t)(SDSTATUS_Tab[4] & 0xFF);

  cardstatus->SIZE_OF_PROTECTED_AREA = tmp << 24;


  /*!< Byte 5 */

  tmp = (uint8_t)(SDSTATUS_Tab[5] & 0xFF);

  cardstatus->SIZE_OF_PROTECTED_AREA |= tmp << 16;


  /*!< Byte 6 */

  tmp = (uint8_t)(SDSTATUS_Tab[6] & 0xFF);

  cardstatus->SIZE_OF_PROTECTED_AREA |= tmp << 8;


  /*!< Byte 7 */

  tmp = (uint8_t)(SDSTATUS_Tab[7] & 0xFF);

  cardstatus->SIZE_OF_PROTECTED_AREA |= tmp;


  /*!< Byte 8 */

  tmp = (uint8_t)((SDSTATUS_Tab[8] & 0xFF));

  cardstatus->SPEED_CLASS = tmp;


  /*!< Byte 9 */

  tmp = (uint8_t)((SDSTATUS_Tab[9] & 0xFF));

  cardstatus->PERFORMANCE_MOVE = tmp;


  /*!< Byte 10 */

  tmp = (uint8_t)((SDSTATUS_Tab[10] & 0xF0) >> 4);

  cardstatus->AU_SIZE = tmp;


  /*!< Byte 11 */

  tmp = (uint8_t)(SDSTATUS_Tab[11] & 0xFF);

  cardstatus->ERASE_SIZE = tmp << 8;


  /*!< Byte 12 */

  tmp = (uint8_t)(SDSTATUS_Tab[12] & 0xFF);

  cardstatus->ERASE_SIZE |= tmp;


  /*!< Byte 13 */

  tmp = (uint8_t)((SDSTATUS_Tab[13] & 0xFC) >> 2);

  cardstatus->ERASE_TIMEOUT = tmp;


  /*!< Byte 13 */

  tmp = (uint8_t)((SDSTATUS_Tab[13] & 0x3));

  cardstatus->ERASE_OFFSET = tmp;

 

  return(errorstatus);

}



/**

  * @brief  Returns the current SD card's status.

  * @param  psdstatus: pointer to the buffer that will contain the SD card status 

  *         (SD Status register).

  * @retval SD_Error: SD Card Error code.

  */

SD_Error SD_SendSDStatus(uint32_t *psdstatus)

{

  SD_Error errorstatus = SD_OK;

  uint32_t count = 0;


  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)

  {

    errorstatus = SD_LOCK_UNLOCK_FAILED;

    return(errorstatus);

  }


//CMD7 这个是我自己加的,在获取SD_Status寄存器值之前,卡必须处于传输状态“tran_state”

SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;

  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEL_DESEL_CARD;

  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;   //r1

  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  SDIO_SendCommand(&SDIO_CmdInitStructure);


  errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);


  if (SD_OK != errorstatus)

  {

    return(errorstatus);

  }


/*!< Set Block Size for Card,cmd16,若是sdsc卡,可以用来设置块大小,若是sdhc卡,块大小为512字节,不受cmd16影响 */

  SDIO_CmdInitStructure.SDIO_Argument = 512;

  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;

  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;   //r1

  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  SDIO_SendCommand(&SDIO_CmdInitStructure);


  errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);


  if (SD_OK != errorstatus)

  {

    return(errorstatus);

  }

  

  /*!< CMD55 */

  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;

  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;

  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;

  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_APP_CMD);


  if (errorstatus != SD_OK)

  {

    return(errorstatus);

  }


  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;

  SDIO_DataInitStructure.SDIO_DataLength = 64;

  SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_64b;

  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;

  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;

  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;

  SDIO_DataConfig(&SDIO_DataInitStructure);


  /*!< Send ACMD13 SD_APP_STAUS  with argument as card's RCA.*/

  SDIO_CmdInitStructure.SDIO_Argument = 0;

  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_STAUS;

  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;

  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_SD_APP_STAUS);


  if (errorstatus != SD_OK)

  {

    return(errorstatus);

  }


  while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))

  {

    if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)

    {

      for (count = 0; count < 8; count++)

      {

        *(psdstatus + count) = SDIO_ReadData();

      }

      psdstatus += 8;

    }

  }

  if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)

  {

    SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);

    errorstatus = SD_DATA_TIMEOUT;

    return(errorstatus);

  }

  else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)

  {

    SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);

    errorstatus = SD_DATA_CRC_FAIL;

    return(errorstatus);

  }

  else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)

  {

    SDIO_ClearFlag(SDIO_FLAG_RXOVERR);

    errorstatus = SD_RX_OVERRUN;

    return(errorstatus);

  }

  else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)

  {

    SDIO_ClearFlag(SDIO_FLAG_STBITERR);

    errorstatus = SD_START_BIT_ERR;

    return(errorstatus);

  }


  while (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)

  {

    *psdstatus = SDIO_ReadData();

    psdstatus++;

  }


  /*!< Clear all the static status flags*/

  SDIO_ClearFlag(SDIO_STATIC_FLAGS);


  return(errorstatus);

}


我用JLink调试时候,发现函数在下面函数中执行死循环;


  while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))

  {

    if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)

    {

      for (count = 0; count < 8; count++)

      {

        *(psdstatus + count) = SDIO_ReadData();

      }

      psdstatus += 8;

    }

  }


但是问题并不是出在这里,而是在上面SD_Error SD_SendSDStatus(uint32_t *psdstatus)函数中,少了一个命令;

看下列图片中的红线标注意思是,要想获取SD Status寄存器的值,必须要使SD卡处于“传输状态”,也就是“tran_state”

在这里插入图片描述

也就是主机向SD卡发送CMD55+ACMD13之前,必须先判断当前SD卡目前的状态,可以用 SD_GetState()函数,函数会返回当前状态,我获取的是处于待机状态 SD_CARD_STANDBY,查阅装填装换机制如下图:

在这里插入图片描述

需要发送一个CMD7即可从SD_CARD_STANDBY模式转化为SD_CARD_TRANSFER模式,最终即可最终即可获得;CMD7代码我已经在上面的例程中写出来了;上面的代码是正确代码;实现的结果如下:

在这里插入图片描述

AU_SIZE = 0x09;正确;

推荐阅读

史海拾趣

FORYARD公司的发展小趣事

随着业务的不断扩展,FORYARD意识到全球化布局的重要性。1995年,公司决定在中国设立研发中心和生产基地,以利用当地丰富的人才资源和成本优势。这一战略决策极大地提升了FORYARD的产能和研发效率,同时也使其能够更好地服务全球客户。此后,FORYARD还陆续在欧洲、亚洲等地建立了多个分支机构,形成了覆盖全球的研发、生产和销售网络。通过全球化布局,FORYARD不仅增强了自身的市场竞争力,还促进了全球电子产业的交流与合作。

ELESTA GmbH公司的发展小趣事

1997年,ELESTA继电器有限公司正式成立,专注于制造符合IEC 61810-3标准的强制导向触点继电器。这一战略举措进一步巩固了ELESTA在电子继电器领域的领先地位,并为公司的长期发展奠定了坚实的基础。

Gazelle Microcircuits Inc公司的发展小趣事
+12V电压是电脑主板中非常重要的电压之一,它主要用于给硬盘驱动器、光驱、风扇等大功率设备供电。这些设备通常需要较高的电压来驱动其内部的电机或机械结构。
巴丁微公司的发展小趣事

巴丁微积极参与各类电子行业的展会和交流活动,以此展示公司的技术实力和产品优势。在CITE2023第十一届中国电子信息博览会上,巴丁微向大家展示了其在电机驱动芯片在智能锁、气表、水表、个人护理及智能垃圾桶等领域的应用案例,吸引了大量专业人士和行业客户的关注。

通过展会,巴丁微不仅成功展示了公司的最新技术和产品,还加强了与业界同仁的交流与合作,进一步提升了公司在行业内的知名度和影响力。

意普(ESPE)公司的发展小趣事

随着工业自动化和智能化的不断发展,意普(ESPE)公司开始将光电保护技术与智能制造相结合,推出了多款智能光电保护产品。这些产品具有更高的检测精度和更快的反应速度,能够更好地保护操作人员的安全。同时,公司还积极参与工业互联网和物联网的建设,推动光电保护技术的智能化发展。

EDSYN公司的发展小趣事

Econais公司自创立之初,就致力于研发超低功耗(ULP)的无线模块。随着物联网(IoT)和机器对机器(M2M)通信技术的兴起,市场对低功耗嵌入式模块的需求日益增长。Econais工程师团队凭借几十年的行业经验,成功研发出了一系列超低功耗Wi-Fi模块,这些模块不仅性能卓越,而且易于集成,为各种物联网应用提供了强大的技术支持。

问答坊 | AI 解惑

无线传感器网络的研究与应用

无线传感器网络的研究与应用…

查看全部问答>

m430f1121time_a 作uart应用怎么设置?

m430f1121time_a 作uart应用怎么设置?…

查看全部问答>

无线调制解调器低成本供电方案设计

  周敏捷   随着移动上网需求的不断增加和3G无线网络时代的来临, 越来越多的最终用户开始选择基于无线手机网络的数据接入业务,这些业务主要是通过GPRS, CDMA 以及即将启用的WCDMA, TD-SCDMA及EVDO等3G通信标准接入互联网络。无线网 ...…

查看全部问答>

跟我学上位机系列1--仿串口调试小助手

一直说搞一个上位机编程方面的教程,一直没有空,明天去合肥出差,今天准备休息一下,突然想到还有答应大家的上位机教程没有编写,下午抽时间根据大家的反馈写了一个仿串口调试小助手,功能基本完善了,大家在实际工作中也可以用,只是功能还不完善 ...…

查看全部问答>

关于stm8s的VCAP上的电容的疑问

手册上推荐470nF的低ESR的电容. 这个不太好找,大家都用什么厂家,型号 另用无极性的CBB是否可以???…

查看全部问答>

【为C2000做贡献】TMS320F2812中断系统分析及其C语言编程

中断不论在什么芯片上都是核心,看看2812是怎么设置中断的。 [ 本帖最后由 fxw451 于 2011-4-7 09:29 编辑 ]…

查看全部问答>

FPGA与SOPC设计教程

FPGA与SOPC设计教程,很好的学习SOPC和NIOSS资料。…

查看全部问答>

50M的时钟能50M分频么

50M的时钟能50M分频么?  我做数字钟的时候,想得到1秒,所以我对50M时钟做了50M的分频;但是结果很不理想,没有输出;我给时钟做5M的分频却能得到0.1秒;不知道为什么,哪个大侠帮我解答一下。…

查看全部问答>

关于CCS3.3 代码自动提示功能

关于CCS3.3 的那个 代码自动提示功能 我消失了  重新编译也不能。。甚至重新新建工程都不行。。。有哪位兄弟教我一下。。。。现在没有了。。。写代码很不方便。。。。谢谢啊…

查看全部问答>

2530-2.4/2.5协议栈

ZStack-2530-2.4.0     要求IAR7.5及以上版本编译器ZStack-2530-2.5.0     要求IAR8.0及以上版本编译器…

查看全部问答>