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参数


bit39:8是CMD8的参数。

  • bit39:20:保留位,为0
  • bit19:16:支持的电压范围,从表中可以知道这里我们写1
  • bit15:8:根据手册,bit15: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命令参数

  • bit31:16:RCA,SD卡的地址,这里因为还没有获取到SD卡地址,所以设为0
  • bit15: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

  • bit30:卡容量位,如果是高容量卡,设置为 1,如果是标准卡,设置为 0。
  • bit31:卡上电状态位,这个状态位在卡的上电流程完成后设置
    在程序中,我们需要将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寄存器中的bit31是否为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);
}
相关推荐
电子科技圈6 小时前
Arm电机控制开发工具哪家强?
arm开发·单片机·嵌入式硬件·设计模式·软件构建
JasmineX-16 小时前
下位机直接控制57步进电机匀速转动
stm32·单片机·嵌入式硬件
聆思科技AI芯片7 小时前
【AI硬件开发上手教程】怎么使用AI编程实现小聆AI语音控制舵机
人工智能·嵌入式硬件·开源·ai编程·智能硬件
abc light7 小时前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发
吠品17 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery114018 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
小李不想当小白21 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
LCG米21 小时前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip
尼喃1 天前
662K/65Z5三脚稳压芯片规格书—PW6566B33/B30引脚功能
嵌入式硬件
创界工坊工作室1 天前
63-基于STC89C52霍尔传感器的智能化电能表设计
stm32·单片机·嵌入式硬件·51单片机·proteus