STM32F4X SDIO(六) 例程讲解-SD_PowerON

STM32F4X SDIO(六) 例程讲解-SD_PowerON

从本节开始将会结合实际的例程讲解SD卡使用,包括SDIO控制器初始化,SD卡初始化,SD卡擦除、SD卡读写等。本例程将会使用野火电子的STM32F407的SD卡读写例程进行讲解。

例程讲解-SD_PowerON

SD_PowerON函数主要是配置SDIO的引脚、询问SD卡的工作电压和配置SD卡时钟

SDIO引脚初始化和时钟初始化

在使用STM32F4X的SDIO控制器前,需要先初始化SDIO的GPIO引脚。

STM32F4X的SDIO需要用到6个引脚

  • PC8:SDIO的数据线0引脚
  • PC9:SDIO的数据线1引脚
  • PC10:SDIO的数据线2引脚
  • PC11:SDIO的数据线3引脚
  • PC12:SDIO的时钟引脚
  • PD12:SDIO的命令引脚
c 复制代码
void SD_LowLevel_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;

  /* 使能GPIOC和GPIOD时钟 */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
  /* 将GPIOC8、GPIOC9、GPIOC10、GPIOC11、GPIOC12和GPIOD2 复用为SDIO   */
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);

  /*  配置GPIOC8、GPIOC9、GPIOC10、GPIOC11、GPIOC12和GPIOD2引脚属性  */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOC, &GPIO_InitStructure);


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_Init(GPIOD, &GPIO_InitStructure);


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* 使能SDIO时钟 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);

  /*使能DMA时钟 */
  RCC_AHB1PeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE);
}

在SD_LowLevel_Init函数中做了SDIO引脚和时钟的初始化操作。

SDIO初始化(单线模式)

在SD卡刚上电的初始化的时候,默认的总线宽度为1位总线宽度,其通信频率在400KHz左右,所以在SD卡上电的时候,也需要配置SDIO的总线宽度和工作频率。

c 复制代码
SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV; // SDIOCLK分频系数
SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising; // SDIO_CK的采样模式
SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;// SDIO_CK的使能
SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;// SDIO_CK是否使用节能模式
SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;// SDIO总线宽度
SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable; // 是否使用硬件流控
SDIO_Init(&SDIO_InitStructure);

这里需要注意的SDIO_CK的频率计算

根据数据手册可以知道SDIO_CK的计算公式。STM32F4X的SDIOCLK的时钟频率是48MHz,SDIO_INIT_CLK_DIV值为0x76,那么SDIO_CK频率计算如下
SDIO_CK = SDIOCLK / (0x76 + 2) = 400KHz

刚好满足SD卡上电时的频率要求。

CMD0:GO_IDLE_STATE

SD卡初始化的第一步是发送CMD0命令复位SD卡,让SD卡进入IDLE状态。

命令发送程序

c 复制代码
  SDIO_CmdInitStructure.SDIO_Argument = 0x0; // 没有参数
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; // CMD0
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No; // 没有响应
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; // 不等待
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; // 使能CPSM状态机
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdError(); // 判断错误状态

调用SDIO_SendCommand函数发送CMD0命令到SD卡

命令响应程序

发送完CMD0之后需要判断发送是否有误,由于CMD0没有响应数据,所以只需判断SDIO控制器状态即可

c 复制代码
static SD_Error CmdError(void)
{
  SD_Error errorstatus = SD_OK;
  uint32_t timeout;

  timeout = SDIO_CMD0TIMEOUT; /*!< 10000 */

  while ((timeout > 0) && (SDIO_GetFlagStatus(SDIO_FLAG_CMDSENT) == RESET))
  {
    timeout--;
  }

  if (timeout == 0)
  {
    errorstatus = SD_CMD_RSP_TIMEOUT;
    return(errorstatus);
  }

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

  return(errorstatus);
}

CMD8:SEND_IF_COND

CMD8命令的作用有两个,分别是电压校验和扩展现有的命令和响应。

在SD卡进入IDLE模式后,下一步就是通过CMD8给SD卡发送电压校验命令。

CMD8参数


bit[39:8]是CMD8的参数。

  • bit[39:20]:保留位,为0
  • bit[19:16]:支持的电压范围,从表中可以知道这里我们写1
  • bit[15:8]:根据手册,bit[15:8]要为0xAA。
    所以CMD8的参数的输入参数就为0x1AA

命令发送程序

c 复制代码
  SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN; // 参数,为0x1AA
  SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND; // 命令编号 CMD8
  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);

命令响应程序

CMD8的响应类型是R7,如果SD卡接受提供的电压范围就会返回R7响应,否则不会返回R7响应。

c 复制代码
/* 检查R7响应 */
static SD_Error CmdResp7Error(void)
{
  SD_Error errorstatus = SD_OK;
  uint32_t status;
  uint32_t timeout = SDIO_CMD0TIMEOUT;

  status = SDIO->STA;

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

  if ((timeout == 0) || (status & SDIO_FLAG_CTIMEOUT))
  {
    /*!< Card is not V2.0 complient or card does not support the set voltage range */
    errorstatus = SD_CMD_RSP_TIMEOUT;
    SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
    return(errorstatus);
  }

  if (status & SDIO_FLAG_CMDREND)
  {
    /*!< Card is SD V2.0 compliant */
    errorstatus = SD_OK;
    SDIO_ClearFlag(SDIO_FLAG_CMDREND);
    return(errorstatus);
  }
  return(errorstatus);
}

如何SD卡响应CMD8命令,则代表该SD卡为SD2.0以上的卡。

c 复制代码
SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
  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 = CmdResp7Error();

  if (errorstatus == SD_OK)
  {
    CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */
    SDType = SD_HIGH_CAPACITY;
  }

CMD55:APP_CMD

在主机发完CMD8命令后,如果SD卡响应CMD8命令,那么接下来主机就需要发送CMD55命令,告诉SD卡在CMD55命令后的是特殊应用命令

CMD55命令参数

  • bit[31:16]:RCA,SD卡的地址,这里因为还没有获取到SD卡地址,所以设为0
  • bit[15:0]:一般为0

命令发送

c 复制代码
    SDIO_CmdInitStructure.SDIO_Argument = 0x00; // CMD55参数
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD; // 命令索引 CMD55
    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);

命令响应

CMD55的命令响应是R1,R1命令会返回SD卡的状态,所以在判断R1响应时需要判断SD卡的状态是否正常。

R1响应返回的卡状态如下

c 复制代码
static SD_Error CmdResp1Error(uint8_t cmd)
{
  SD_Error errorstatus = SD_OK;
  uint32_t status;
  uint32_t response_r1;

  status = SDIO->STA;

 /* 判断SDIO控制器状态 */
  while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
  {
    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);
  }

  /*!< 命令号是否为发送的命令号 */
  if (SDIO_GetCommandResponse() != cmd)
  {
    errorstatus = SD_ILLEGAL_CMD;
    return(errorstatus);
  }

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

  /*!< 依次判断SD卡状态位  */
  response_r1 = SDIO_GetResponse(SDIO_RESP1);

  if ((response_r1 & SD_OCR_ERRORBITS) == SD_ALLZERO)
  {
    return(errorstatus);
  }

  if (response_r1 & SD_OCR_ADDR_OUT_OF_RANGE)
  {
    return(SD_ADDR_OUT_OF_RANGE);
  }

  if (response_r1 & SD_OCR_ADDR_MISALIGNED)
  {
    return(SD_ADDR_MISALIGNED);
  }

  if (response_r1 & SD_OCR_BLOCK_LEN_ERR)
  {
    return(SD_BLOCK_LEN_ERR);
  }

  if (response_r1 & SD_OCR_ERASE_SEQ_ERR)
  {
    return(SD_ERASE_SEQ_ERR);
  }

  if (response_r1 & SD_OCR_BAD_ERASE_PARAM)
  {
    return(SD_BAD_ERASE_PARAM);
  }

  if (response_r1 & SD_OCR_WRITE_PROT_VIOLATION)
  {
    return(SD_WRITE_PROT_VIOLATION);
  }

  if (response_r1 & SD_OCR_LOCK_UNLOCK_FAILED)
  {
    return(SD_LOCK_UNLOCK_FAILED);
  }

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

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

  if (response_r1 & SD_OCR_CARD_ECC_FAILED)
  {
    return(SD_CARD_ECC_FAILED);
  }

  if (response_r1 & SD_OCR_CC_ERROR)
  {
    return(SD_CC_ERROR);
  }

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

  if (response_r1 & SD_OCR_STREAM_READ_UNDERRUN)
  {
    return(SD_STREAM_READ_UNDERRUN);
  }

  if (response_r1 & SD_OCR_STREAM_WRITE_OVERRUN)
  {
    return(SD_STREAM_WRITE_OVERRUN);
  }

  if (response_r1 & SD_OCR_CID_CSD_OVERWRIETE)
  {
    return(SD_CID_CSD_OVERWRITE);
  }

  if (response_r1 & SD_OCR_WP_ERASE_SKIP)
  {
    return(SD_WP_ERASE_SKIP);
  }

  if (response_r1 & SD_OCR_CARD_ECC_DISABLED)
  {
    return(SD_CARD_ECC_DISABLED);
  }

  if (response_r1 & SD_OCR_ERASE_RESET)
  {
    return(SD_ERASE_RESET);
  }

  if (response_r1 & SD_OCR_AKE_SEQ_ERROR)
  {
    return(SD_AKE_SEQ_ERROR);
  }
  return(errorstatus);
}

例程中判断R1响应的步骤如下

  • 先判断SDIO控制的状态是否有错
  • 判断R1响应的命令号是否为CMD55
  • 最后再根据R1响应的SD卡状态位依次进行判断

    根据波形图可知,返回的SD卡状态位0x120,也就是bit5和bit8置1

ACMD41:SD_SEND_OP_COND

发送ACMD41的作用是告诉SD卡,主机是否支持大容量卡,并且判断SD卡是否上电完成。

ACMD41参数

ACMD41参数是根据SD卡的OCR寄存器进行定义,以下为OCR寄存器的定义表

在这里我们需要注意以下几个bit

  • bit[30]:卡容量位,如果是高容量卡,设置为 1,如果是标准卡,设置为 0。
  • bit[31]:卡上电状态位,这个状态位在卡的上电流程完成后设置
    在程序中,我们需要将bit30和bit31都设置为1,卡的电压设置为3.2-3.3,也就是bit20为1,则AMCD41的输入参数就为0xC0100000

命令发送

c 复制代码
      SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType; // 参数 0xC0100000
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND; // 命令索引 ACMD41
      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);

命令响应

ACMD41的响应是R3响应

例程中需要判断R3响应的OCR寄存器中的bit[31]是否为1,如果不为1,则需要循环发送CMD55和ACMD41,一直等到OCR寄存器总bit31为1

c 复制代码
while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
    {

      /*!< SEND CMD55 APP_CMD with RCA as 0 */
      SDIO_CmdInitStructure.SDIO_Argument = 0x00;
      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_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
      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 = CmdResp3Error();
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }

      response = SDIO_GetResponse(SDIO_RESP1);
      validvoltage = (((response >> 31) == 1) ? 1 : 0); // 判断bit31是否为1,如果为1则代表SD卡上电完成
      count++;
    }
    if (count >= SD_MAX_VOLT_TRIAL)
    {
      errorstatus = SD_INVALID_VOLTRANGE;
      return(errorstatus);
    }

    if (response &= SD_HIGH_CAPACITY)
    {
      CardType = SDIO_HIGH_CAPACITY_SD_CARD;
    }

至此,SD卡上电部分就完成了,下面来简介梳理一下SD卡上电的流程

SD卡上电流程

  1. 初始化SDIO引脚和时钟
  2. 初始化SDIO控制器,设置总线宽度为1,SDIO_CK频率不高于400KHz
  3. 发生CMD0命令,让SD卡进入IDLE模式
  4. 发生CMD8命令,判断SD卡是否支持设置的电压范围
  5. 发送CMD55命令
  6. 发送ACMD41命令,并判断SD卡是否上电完成,如果没有上电完成就重复5和6步骤
  7. SD卡上电完成

野火电子SD卡上电程序

c 复制代码
SD_Error SD_PowerON(void)
{
  __IO SD_Error errorstatus = SD_OK;
  uint32_t response = 0, count = 0, validvoltage = 0;
  uint32_t SDType = SD_STD_CAPACITY;

  /*!< Power ON Sequence -----------------------------------------------------*/
  /*!< Configure the SDIO peripheral */
  /*!< SDIO_CK = SDIOCLK / (SDIO_INIT_CLK_DIV + 2) */
  /*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */
  /*!< SDIO_CK for initialization should not exceed 400 KHz */  
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  SDIO_Init(&SDIO_InitStructure);

  /*!< Set Power State to ON */
  SDIO_SetPowerState(SDIO_PowerState_ON);

  /*!< Enable SDIO Clock */
  SDIO_ClockCmd(ENABLE);

  /*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*/
  /*!< No CMD response required */
  SDIO_CmdInitStructure.SDIO_Argument = 0x0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdError();

  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);
  }

  /*!< CMD8: SEND_IF_COND ----------------------------------------------------*/
  /*!< Send CMD8 to verify SD card interface operating condition */
  /*!< Argument: - [31:12]: Reserved (shall be set to '0')
               - [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
               - [7:0]: Check Pattern (recommended 0xAA) */
  /*!< CMD Response: R7 */
  SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
  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 = CmdResp7Error();

  if (errorstatus == SD_OK)
  {
    CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */
    SDType = SD_HIGH_CAPACITY;
  }
  else
  {
    /*!< CMD55 */
    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
    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);
  }
  /*!< CMD55 */
  SDIO_CmdInitStructure.SDIO_Argument = 0x00;
  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 is Command TimeOut, it is a MMC card */
  /*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)
     or SD card 1.x */
  if (errorstatus == SD_OK)
  {
    /*!< SD CARD */
    /*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
    while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
    {

      /*!< SEND CMD55 APP_CMD with RCA as 0 */
      SDIO_CmdInitStructure.SDIO_Argument = 0x00;
      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_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
      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 = CmdResp3Error();
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }

      response = SDIO_GetResponse(SDIO_RESP1);
      validvoltage = (((response >> 31) == 1) ? 1 : 0);
      count++;
    }
    if (count >= SD_MAX_VOLT_TRIAL)
    {
      errorstatus = SD_INVALID_VOLTRANGE;
      return(errorstatus);
    }

    if (response &= SD_HIGH_CAPACITY)
    {
      CardType = SDIO_HIGH_CAPACITY_SD_CARD;
    }

  }/*!< else MMC Card */

  return(errorstatus);
}
相关推荐
Mortal_hhh22 分钟前
VScode的C/C++点击转到定义,不是跳转定义而是跳转声明怎么办?(内附详细做法)
ide·vscode·stm32·编辑器
深圳市青牛科技实业有限公司35 分钟前
【青牛科技】应用方案|D2587A高压大电流DC-DC
人工智能·科技·单片机·嵌入式硬件·机器人·安防监控
Mr.谢尔比2 小时前
电赛入门之软件stm32keil+cubemx
stm32·单片机·嵌入式硬件·mcu·信息与通信·信号处理
LightningJie2 小时前
STM32中ARR(自动重装寄存器)为什么要减1
stm32·单片机·嵌入式硬件
鹿屿二向箔2 小时前
STM32外设之SPI的介绍
stm32
西瓜籽@2 小时前
STM32——毕设基于单片机的多功能节能窗控制系统
stm32·单片机·课程设计
远翔调光芯片^138287988725 小时前
远翔升压恒流芯片FP7209X与FP7209M什么区别?做以下应用市场摄影补光灯、便携灯、智能家居(调光)市场、太阳能、车灯、洗墙灯、舞台灯必看!
科技·单片机·智能家居·能源
极客小张5 小时前
基于STM32的智能充电桩:集成RTOS、MQTT与SQLite的先进管理系统设计思路
stm32·单片机·嵌入式硬件·mqtt·sqlite·毕业设计·智能充电桩
m0_739312878 小时前
【STM32】项目实战——OV7725/OV2604摄像头颜色识别检测(开源)
stm32·单片机·嵌入式硬件
嵌入式小章8 小时前
基于STM32的实时时钟(RTC)教学
stm32·嵌入式硬件·实时音视频