STM32F4X SDIO(六) 例程讲解-SD_PowerON
- 例程讲解-SD_PowerON
-
- SDIO引脚初始化和时钟初始化
- SDIO初始化(单线模式)
- CMD0:GO_IDLE_STATE
-
- 命令发送程序
- 命令响应程序
- CMD8:SEND_IF_COND
-
- CMD8参数
- 命令发送程序
- 命令响应程序
- CMD55:APP_CMD
-
- CMD55命令参数
- 命令发送
- 命令响应
- ACMD41:SD_SEND_OP_COND
-
- ACMD41参数
- 命令发送
- 命令响应
- SD卡上电流程
- 野火电子SD卡上电程序
从本节开始将会结合实际的例程讲解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的命令引脚
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的总线宽度和工作频率。
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状态。
命令发送程序
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控制器状态即可
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
命令发送程序
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响应。
/* 检查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以上的卡。
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
命令发送
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响应返回的卡状态如下
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
命令发送
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
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卡上电流程
- 初始化SDIO引脚和时钟
- 初始化SDIO控制器,设置总线宽度为1,SDIO_CK频率不高于400KHz
- 发生CMD0命令,让SD卡进入IDLE模式
- 发生CMD8命令,判断SD卡是否支持设置的电压范围
- 发送CMD55命令
- 发送ACMD41命令,并判断SD卡是否上电完成,如果没有上电完成就重复5和6步骤
- SD卡上电完成
野火电子SD卡上电程序
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); }