STM32F4X SDIO(七) 例程讲解-SD_InitializeCards & SD_GetCardInfo


本节例程基于 野火电子的STM32F407的SD卡读写例程进行讲解。上一节中讲解了SD卡上电过程,这节将会讲解一下SD卡的初始化过程,包括 获取SD卡的CID、CSD和SD卡RCA地址

例程讲解-SD_InitializeCards & SD_GetCardInfo

CMD2:ALL_SEND_CID

CMD2的作用是通知所有卡通过CMD线返回CID值,CID值包括SD卡的识别号、制造商ID、OEMID、产品名称、版本号、序列号等信息,相当于是SD卡的身份证。
在这里插入图片描述

命令发送程序

CMD2命令无需带参数,所以SDIO控制器可以直接发送CMD2命令

 SDIO_CmdInitStructure.SDIO_Argument = 0x0; // 不带参数
 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID; // 命令所以为CMD2
 SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long; // 长响应
 SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; // 不等待
 SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; // 使能CPSM状态机
 SDIO_SendCommand(&SDIO_CmdInitStructure);

在这里插入图片描述

命令响应程序

CMD2的响应是R2,其响应类型为长响应。其中bit[127:1]是其响应的数据。
在这里插入图片描述

首先需要先判断SD卡的响应是否正常,如果响应正常,下一步就需要把128位的CID读出来。

static SD_Error CmdResp2Error(void)
{
  SD_Error errorstatus = SD_OK;
  uint32_t status;

  status = SDIO->STA;

  while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
  {
    status = SDIO->STA;
  }

  if (status & SDIO_FLAG_CTIMEOUT)
  {
    errorstatus = SD_CMD_RSP_TIMEOUT;
    SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
    return(errorstatus);
  }
  else if (status & SDIO_FLAG_CCRCFAIL)
  {
    errorstatus = SD_CMD_CRC_FAIL;
    SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
    return(errorstatus);
  }

  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);

  return(errorstatus);
}

// 读取128位CID
    CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
    CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
    CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
    CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);

根据STM32F4X的数据手册可知,如果是长响应,高位的数据保存在SDIO_RESP1寄存器,低位保存在SDIO_RESP4寄存器
在这里插入图片描述

CID数据解析

在这里插入图片描述
根据波形图可知SD卡的CID响应数据为0x9f54495344333247614af80704017158,CID的数据解析表如下

在这里插入图片描述

下面就来对SD卡读取到CID数据进行解析

NameFieldWidthCID-sliceValue
Manufacturer IDMID8[127:120]0x9F
OEM/Application IDOID16[119:104]0x5449
Product namePNM40[103:64]0x5344333247
"SD32G"
Product revisionPRV8[63:56]0x61
Product serial numberPSN32[55:24]0x4af80704
reservedX4[23:20]0
Manufacturing dateMDT12[19:8]0x171
CRC7 checksumCRC7[7:1]0x2C
not used, always 1x1[0:0]0x1

CMD3:SEND_RELATIVE_ADDR

CMD3的作用是请求SD卡发布一个RCA,RCA的作用相当于是一个地址,这个RCA后续在读写SD卡中用于对SD卡进行寻址。

在这里插入图片描述

命令发送程序

 SDIO_CmdInitStructure.SDIO_Argument = 0x00; // 不带参数
 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR; // 命令索引 CMD3
 SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; // 短响应
 SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; // 不等待
 SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; // 使能 CPSM状态机
 SDIO_SendCommand(&SDIO_CmdInitStructure);

在这里插入图片描述

命令响应程序

CMD3的响应是R6,其中SD卡的RCA在响应数据的bit[31:16]
在这里插入图片描述
在程序中会先判断SD卡的响应是否正常,如果响应正常就通过SDIO_RESP1寄存器读取R6响应的数据,然后保存高16位的RCA地址。

static SD_Error CmdResp6Error(uint8_t cmd, uint16_t *prca)
{
  SD_Error errorstatus = SD_OK;
  uint32_t status;
  uint32_t response_r1;

  status = SDIO->STA;

  while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
  {
    status = SDIO->STA;
  }

  if (status & SDIO_FLAG_CTIMEOUT)
  {
    errorstatus = SD_CMD_RSP_TIMEOUT;
    SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
    return(errorstatus);
  }
  else if (status & SDIO_FLAG_CCRCFAIL)
  {
    errorstatus = SD_CMD_CRC_FAIL;
    SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
    return(errorstatus);
  }

  /*!< Check response received is of desired command */
  if (SDIO_GetCommandResponse() != cmd)
  {
    errorstatus = SD_ILLEGAL_CMD;
    return(errorstatus);
  }

  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);

  /*!< We have received response, retrieve it.  */
  response_r1 = SDIO_GetResponse(SDIO_RESP1);

  if (SD_ALLZERO == (response_r1 & (SD_R6_GENERAL_UNKNOWN_ERROR | SD_R6_ILLEGAL_CMD | SD_R6_COM_CRC_FAILED)))
  {
    *prca = (uint16_t) (response_r1 >> 16);
    return(errorstatus);
  }

  if (response_r1 & SD_R6_GENERAL_UNKNOWN_ERROR)
  {
    return(SD_GENERAL_UNKNOWN_ERROR);
  }

  if (response_r1 & SD_R6_ILLEGAL_CMD)
  {
    return(SD_ILLEGAL_CMD);
  }

  if (response_r1 & SD_R6_COM_CRC_FAILED)
  {
    return(SD_COM_CRC_FAILED);
  }

  return(errorstatus);
}

在这里插入图片描述

CMD9:SEND_CSD

CMD9的作用是获取SD卡的CSD数据,CSD数据主要包含SD卡一些基本状态信息,包括SD的块大小、SD卡传输速率、SD卡的擦除扇区大小等信息。CMD9命令需要带参数,其参数是SD卡的RCA地址
在这里插入图片描述

命令发送程序

 SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16); // 参数RCA地址
 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD; // 命令索引 CMD9
 SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long; // 长响应
 SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; // 不等待
 SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; // 使能CPSM状态机
 SDIO_SendCommand(&SDIO_CmdInitStructure);

在这里插入图片描述

命令响应程序

CMD9的响应是R2,长响应,CSD的返回数据是128位。
在这里插入图片描述

static SD_Error CmdResp2Error(void)
{
  SD_Error errorstatus = SD_OK;
  uint32_t status;

  status = SDIO->STA;

  while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
  {
    status = SDIO->STA;
  }

  if (status & SDIO_FLAG_CTIMEOUT)
  {
    errorstatus = SD_CMD_RSP_TIMEOUT;
    SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
    return(errorstatus);
  }
  else if (status & SDIO_FLAG_CCRCFAIL)
  {
    errorstatus = SD_CMD_CRC_FAIL;
    SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
    return(errorstatus);
  }

  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);

  return(errorstatus);
}
CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);

CSD的数据读取跟CID的数据读取是一样,不同的是两个数据的解析方法不一样

CSD数据解析

在这里插入图片描述

从波形图可知CSD的数据为0x400e00325b590000e68f7f800a400018,下面就对读取到的CSD数据进行解析。
在这里插入图片描述

NameFieldWidthCID-sliceValue
CSD structureCSD_STRUCTURE2[127:126]0x1
reservedX6[125:120]0
data read access-time-1TAAC8[119:112]0xE
"SD32G"
data read access-time-2 in CLK cycles (NSAC*100)NSAC8[111:104]0x0
max. data transfer rateTRAN_SPEED8[103:96]0x32
card command classesCCC12[95:84]0x5b5
max. read data block lengthREAD_BL_LEN4[83:80]0x9
partial blocks for read allowedREAD_BL_PARTIAL1[79:79]0x0
write block misalignmentWRITE_BLK_MISALIGN1[78:78]0x0
read block misalignmentREAD_BLK_MISALIGN1[77:77]0x0
DSR implementedDSR_IMP1[76:76]0x0
reservedX6[75:70]0x0
device sizeC_SIZE22[69:48]0xE68F
reservedX1[47:47]0x0
erase single block enableERASE_BLK_EN1[46:46]0x1
erase sector sizeSECTOR_SIZE7[45:39]0x7F
write protect group sizeWP_GRP_SIZE7[39:32]0x0
write protect group enableWP_GRP_ENABLE1[31:31]0x0
reservedX2[30:29]0x0
write speed factorR2W_FACTOR2[28:26]0x2
max. write data block lengthWRITE_BL_LEN4[25:22]0x9
partial blocks for write allowedWRITE_BL_PARTIAL1[21:21]0x0
reservedX5[20:16]0x0
File format groupFILE_FORMAT_GRP1[15:15]0x0
copy flagCOPY1[14:14]0x0
permanent write protectionPERM_WRITE_PROTECT1[13:13]0x0
temporary write protectionTMP_WRITE_PROTECT1[12:12]0x0
File formatFILE_FORMAT2[11:10]0x0
reservedX2[9:8]0x0
CRCCRC7[7:1]0xC
not used, always'1'X1[0:0]0x1
CSD结构版本

CSD_STRUCTURE区域代表的是CSD寄存器结构使用的解析版本号,在SD卡的发展中使用了不同的CSD版本,在上面的例程中CSD_STRUCTURE的值是0x01,则代表使用的CSD V2.0的版本
在这里插入图片描述

READ_BL_LEN

最大的数据块读取长度,数值为9则代表是512字节
在这里插入图片描述

SD卡容量

根据上面的CSD数据我们可以计算出SD卡的容量大小,其计算公式如下memory capacity = (C_SIZE+1) * 512KByte
memory capacity = (0xE68F + 1) * 512 = 30,220,288(Byte) = 29512(M) = 28G

获取SD卡信息

MCU读取到SD卡的CSD和CID数据后,就可以根据上面的表格进行数据的解析

SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo)
{
  SD_Error errorstatus = SD_OK;
  uint8_t tmp = 0;

  cardinfo->CardType = (uint8_t)CardType;
  cardinfo->RCA = (uint16_t)RCA;

  /*!< 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 = ((uint64_t)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;


  /*!< Byte 0 */
  tmp = (uint8_t)((CID_Tab[0] & 0xFF000000) >> 24);
  cardinfo->SD_cid.ManufacturerID = tmp;

  /*!< Byte 1 */
  tmp = (uint8_t)((CID_Tab[0] & 0x00FF0000) >> 16);
  cardinfo->SD_cid.OEM_AppliID = tmp << 8;

  /*!< Byte 2 */
  tmp = (uint8_t)((CID_Tab[0] & 0x000000FF00) >> 8);
  cardinfo->SD_cid.OEM_AppliID |= tmp;

  /*!< Byte 3 */
  tmp = (uint8_t)(CID_Tab[0] & 0x000000FF);
  cardinfo->SD_cid.ProdName1 = tmp << 24;

  /*!< Byte 4 */
  tmp = (uint8_t)((CID_Tab[1] & 0xFF000000) >> 24);
  cardinfo->SD_cid.ProdName1 |= tmp << 16;

  /*!< Byte 5 */
  tmp = (uint8_t)((CID_Tab[1] & 0x00FF0000) >> 16);
  cardinfo->SD_cid.ProdName1 |= tmp << 8;

  /*!< Byte 6 */
  tmp = (uint8_t)((CID_Tab[1] & 0x0000FF00) >> 8);
  cardinfo->SD_cid.ProdName1 |= tmp;

  /*!< Byte 7 */
  tmp = (uint8_t)(CID_Tab[1] & 0x000000FF);
  cardinfo->SD_cid.ProdName2 = tmp;

  /*!< Byte 8 */
  tmp = (uint8_t)((CID_Tab[2] & 0xFF000000) >> 24);
  cardinfo->SD_cid.ProdRev = tmp;

  /*!< Byte 9 */
  tmp = (uint8_t)((CID_Tab[2] & 0x00FF0000) >> 16);
  cardinfo->SD_cid.ProdSN = tmp << 24;

  /*!< Byte 10 */
  tmp = (uint8_t)((CID_Tab[2] & 0x0000FF00) >> 8);
  cardinfo->SD_cid.ProdSN |= tmp << 16;

  /*!< Byte 11 */
  tmp = (uint8_t)(CID_Tab[2] & 0x000000FF);
  cardinfo->SD_cid.ProdSN |= tmp << 8;

  /*!< Byte 12 */
  tmp = (uint8_t)((CID_Tab[3] & 0xFF000000) >> 24);
  cardinfo->SD_cid.ProdSN |= tmp;

  /*!< Byte 13 */
  tmp = (uint8_t)((CID_Tab[3] & 0x00FF0000) >> 16);
  cardinfo->SD_cid.Reserved1 |= (tmp & 0xF0) >> 4;
  cardinfo->SD_cid.ManufactDate = (tmp & 0x0F) << 8;

  /*!< Byte 14 */
  tmp = (uint8_t)((CID_Tab[3] & 0x0000FF00) >> 8);
  cardinfo->SD_cid.ManufactDate |= tmp;

  /*!< Byte 15 */
  tmp = (uint8_t)(CID_Tab[3] & 0x000000FF);
  cardinfo->SD_cid.CID_CRC = (tmp & 0xFE) >> 1;
  cardinfo->SD_cid.Reserved2 = 1;
  
  return(errorstatus);
}

SD卡初始化流程

  1. 发送CMD2获取CID数据
  2. 发送CMD3获取SD卡的RCA地址
  3. 发送CMD9获取SD卡的CSD数据
  4. 根据CID和CSD表格解析数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值