1. 前言
本篇文章主要是介绍stm324x9i_eval_sdio_sd.c
里面SD_Init()函数完整的过程。它主要是实现了SDIO的初始化、SD卡的Power UP、SD卡的初始化和获取SD卡的相关信息等,下面会详细介绍SDIO的初始化和SD卡的Power UP的分析。
2. SD_LowLevel_Init()
SD_LowLevel_Init()
主要功能是初始化使用的IO和相关的Clock,具体如下:
- 将PC.08, PC.09, PC.10, PC.11配置为SDIO模式的DATA0, DATA1, DATA2, DATA3功能。
- 将PD.02, PC.12配置为SDIO模式的SDIO_CMD, SDIO_CLK功能。
- 配置SDIO接口的Clock使能。
- 配置SDIO传输时使用DMA2,并且使能DMA2的Clock。
void SD_LowLevel_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOC and GPIOD Periph clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
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);
/* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
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);
/* Configure PD.02 CMD line */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure PC.12 pin: CLK pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Enable the SDIO APB2 Clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);
/* Enable the DMA2 Clock */
RCC_AHB1PeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE);
}
3. SD_PowerON()
SD_PowerON()
主要功能是询问SD卡的工作电压和配置工作时钟。主要涉及到的函数如下:
- SDIO_Init()
- SDIO_SetPowerState()
- SDIO_ClockCmd()
- CMD0: GO_IDLE_STATE
3.1 SDIO_Init()
SDIO_Init()
主要是配置SDIO时钟控制寄存器(SDIO_CLKCR)。下面通过Code、Register Map和Table来介绍对SDIO_CLKCR寄存器的设置如下:
/**
* @brief SDIO Intialization Frequency (400KHz max)
*/
#define SDIO_INIT_CLK_DIV ((uint8_t)0x76)
/*!< 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);
名称 | 描述 | Value | 备注 |
HWFC_EN | 硬件流控制使能 (HW Flow Control enable) 0:禁止硬件流控制 1:使能硬件流控制 |
0 | SDIO_HardwareFlowControl_Disable |
NEGEDGE | SDIO_CK 移相选择位 (SDIO_CK dephasing selection bit) 0:在主时钟 SDIOCLK 的上升沿产生 SDIO_CK 1:在主时钟 SDIOCLK 的下降沿产生 SDIO_CK |
0 | SDIO_ClockEdge_Rising |
WIDBUS | 宽总线模式使能位 (Wide bus mode enable bit) 00:默认总线模式:使用 SDIO_D0 01:4 位宽总线模式:使用 SDIO_D[3:0] 10:8 位宽总线模式:使用 SDIO_D[7:0] |
0 | SDIO_BusWide_1b //在SD卡初始化阶段只用到CLK和CMD, SDIO_D0作为指示SD卡的繁忙状态,所以这里被设置0。 |
BYPASS | 时钟分频器旁路使能位 (Clock divider bypass enable bit) 0:禁止旁路:在驱动 SDIO_CK 输出信号前,根据 CLKDIV 值对 SDIOCLK 进行分频。 1:使能旁路:SDIOCLK 直接驱动 SDIO_CK 输出信号。 |
0 | SDIO_ClockBypass_Disable |
PWRSAV | 节能模式配置位 (Power saving configuration bit) 0:始终使能 SDIO_CK 时钟 1:仅在总线激活时使能 SDIO_CK |
0 | SDIO_ClockPowerSave_Disable |
CLKEN | 时钟使能位 (Clock enable bit) 0:禁止 SDIO_CK 1:使能 SDIO_CK |
0 | |
CLKDIV | 时钟分频系数 (Clock divide factor) 该字段定义输入时钟 (SDIOCLK) 与输出时钟 (SDIO_CK) 之间的分频系数: SDIO_CK 频率 = SDIOCLK / [CLKDIV + 2] |
0x76 | SDIO_CK 频率 = SDIOCLK / [CLKDIV + 2] 400K = 48M / [0x76 + 2] |
3.2 SDIO_SetPowerState()
SDIO_SetPowerState()
主要是配置SDIO电源控制寄存器 (SDIO_POWER)为ON。下面通过Code、Register Map和Table来介绍对SDIO_POWER寄存器的设置如下:
/** @defgroup SDIO_Power_State
* @{
*/
#define SDIO_PowerState_ON ((uint32_t)0x00000003)
/*!< Set Power State to ON */
SDIO_SetPowerState(SDIO_PowerState_ON);
名称 | 描述 | Value | 备注 |
PWRCTRL | 电源控制位 (Power supply control bits)。 00:掉电:停止为卡提供时钟 01:保留 10:保留,上电 11:通电:为卡提供时钟。 |
3 | SDIO_PowerState_ON |
3.3 SDIO_ClockCmd()
SDIO_ClockCmd()
主要是配置SDIO时钟控制寄存器(SDIO_CLKCR)的CLKEN为使能 SDIO_CK。下面通过Code、Register Map和Table来介绍对SDIO_POWER寄存器的设置如下:
/*!< Enable SDIO Clock */
SDIO_ClockCmd(ENABLE);
-------------------------------------------->
void SDIO_ClockCmd(FunctionalState NewState)
{
*(__IO uint32_t *) CLKCR_CLKEN_BB = (uint32_t)NewState;
}
-------------------------------------------->
/* ------------ SDIO registers bit address in the alias region ----------- */
#define SDIO_OFFSET (SDIO_BASE - PERIPH_BASE) // (0x40012C00 - 0x40000000) = 0x12C00
/* --- CLKCR Register ---*/
/* Alias word address of CLKEN bit */
#define CLKCR_OFFSET (SDIO_OFFSET + 0x04) // (0x12C00 + 0x04) = 0x12C04
#define CLKEN_BitNumber 0x08
#define CLKCR_CLKEN_BB (PERIPH_BB_BASE + (CLKCR_OFFSET * 32) + (CLKEN_BitNumber * 4))
//(0x42000000 + ( 0x12C04 * 32) + (0x08 * 4)) = 0x422580A0
这里会有一个疑问:为什么CLKCR_CLKEN_BB(0x422580A0)这个地址可以操作到SDIO_CLKCR(0x40012C04)寄存器的CLKEN时钟使能位?
解释这个问题我们需要了解一下Cortex-M4内核的Memory Map(如下图),Cortex-M4的存储器系统支持所谓的“位带”(bit-band)操作。通过它,实现了对单一Bit的原子操作。
下面的公式显示别名区域( the alias region)如何映射到位带区域(the bit-band region):bit_word_offset = (byte_offset x 32) + (bit_number x 4)
bit_word_addr = bit_band_base + bit_word_offset
- bit_word_offset:目标Bit在位带区域(the bit-band region)中的位置。
- bit_word_addr:目标Bit 映射到 别名区域( the alias region)中 字的地址 (就是32Bit地址)。
- bit_band_base:别名区域( the alias region)的起始地址。// Peripheral: 0x42000000 SRAM: 0x22000000
- byte_offset:目标Bit在位带区域(the bit-band region)中的字节数。
- bit_number:目标Bit的位置,0-7。
例如:SRAM的寄存器地址0x200FFFFF的bit[7]映射到别名区域( the alias region)的0x23FFFFFC,计算公式如下:0x23FFFFFC = 0x22000000 + (0xFFFFF*32) + (7*4).
3.4 CMD0: GO_IDLE_STATE
在上电的阶段,发送的第一个命令是CMD0,使SD卡进入到IDLE状态。如下图所示:
/*!< 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);
使用SDIO_SendCommand
发送CMD,涉及到2个寄存器:SDIO 参数寄存器 (SDIO_ARG) 和 SDIO 命令寄存器 (SDIO_CMD) 。
名称 | 描述 | Value | 备注 |
CMDARG | 命令参数 (Command argument) 作为命令消息的一部分发送给卡的命令参数。如果命令包含参数,则在将命令写入到命令寄存器之前,必须将参数加载到此寄存器中。 |
0 |
名称 | 描述 | Value | 备注 |
ATACMD | CE-ATA 命令 (CE-ATA command) 如果 ATACMD 置 1,则 CPSM 将传输 CMD61。 |
0 | |
nIEN | 非中断使能 (not Interrupt Enable) 如果该位为 0,则使能 CE-ATA 设备中的中断。 |
0 | |
ENCMDcompl | 使能 CMD 完成 (Enable CMD completion) 如果此位置 1,则使能命令完成信号。 |
0 | |
SDIOSuspend | SD I/O 挂起命令 (SD I/O suspend command) 如果此位置 1,则要发送的命令为挂起命令(仅用于 SDIO 卡)。 |
0 | |
CPSMEN | 命令路径状态机 (CPSM) 使能位 (Command path state machine (CPSM) Enable bit) 如果此位置 1,则使能 CPSM。 |
1 | SDIO_CPSM_Enable |
WAITPEND | CPSM 等待数据传输结束(CmdPend 内部信号) (CPSM Waits for ends of data transfer (CmdPend internal signal))。 如果此位置 1,则 CPSM 将等到数据传输结束后才开始发送命令。 |
0 | SDIO_Wait_No |
WAITINT | CPSM 等待中断请求 (CPSM waits for interrupt request) 如果此位置 1,则 CPSM 禁止命令超时并等待中断请求。 |
0 | SDIO_Wait_No |
WAITRESP | 等待响应位 (Wait for response bits) 00:无响应,但 CMDSENT 标志除外 01:短响应,但 CMDREND 或 CCRCFAIL 标志除外 10:无响应,但 CMDSENT 标志除外 11:长响应,但 CMDREND 或 CCRCFAIL 标志除外 |
0x00000000 | SDIO_Response_No |
CMDINDEX | 命令索引 (Command index) 命令索引作为命令消息的一部分发送给卡。 |
0 | SD_CMD_GO_IDLE_STATE |
使用逻辑分析仪抓取实际发送出来的波形如下:
发送CMD0后,还需要判断是否发送成功。由于CMD0是无需响应的CMD,所以这里只需要判断SDIO 状态寄存器 (SDIO_STA)是否发送完成。
#define SDIO_FLAG_CMDSENT ((uint32_t)0x00000080)
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);
}
名称 | 描述 | Value | 备注 |
CEATAEND | 针对 CMD61 收到了 CE-ATA 命令完成信号 (CE-ATA command completion signal received for CMD61) |
||
SDIOIT | 收到了 SDIO 中断 (SDIO interrupt received) | ||
RXDAVL | 接收 FIFO 中有数据可用 (Data available in receive FIFO) | ||
TXDAVL | 传输 FIFO 中有数据可用 (Data available in transmit FIFO) | ||
RXFIFOE | 接收 FIFO 为空 (Receive FIFO empty) | ||
TXFIFOE | 发送 FIFO 为空 (Transmit FIFO empty) 如果使能了硬件流控制,则 TXFIFOE 信号在 FIFO 包含 2 个字时激活。 |
||
RXFIFOF | 接收 FIFO 已满 (Receive FIFO full) 如果使能了硬件流控制,则 RXFIFOF 信号在 FIFO 差 2 个字便变满之前激活。 |
||
TXFIFOF | 传输 FIFO 已满 (Transmit FIFO full) | ||
RXFIFOHF | 接收 FIFO 半满:FIFO 中至少有 8 个字 (Receive FIFO half full: there are at least 8 words in the FIFO) |
||
TXFIFOHE | 传输 FIFO 半空:至少可以写入 8 个字到 FIFO (Transmit FIFO half empty: at least 8 words can be written into the FIFO) |
||
RXACT | 数据接收正在进行中 (Data receive in progress) | ||
TXACT | 数据传输正在进行中 (Data transmit in progress) | ||
CMDACT | 命令传输正在进行中 (Command transfer in progress) | ||
DBCKEND | 已发送/ 接收数据块(CRC 校验通过) (Data block sent/received (CRC check passed)) |
||
STBITERR | 在宽总线模式下,并非在所有数据信号上都检测到了起始位 (Start bit not detected on all data signals in wide bus mode) |
||
DATAEND | 数据结束(数据计数器 SDIDCOUNT 为零) (Data end (data counter, SDIDCOUNT, is zero)) |
||
CMDSENT | 命令已发送(不需要响应)(Command sent (no response required)) | 0x00000080 | SDIO_FLAG_CMDSENT |
CMDREND | 已接收命令响应(CRC 校验通过)(Command response received (CRC check passed)) | ||
RXOVERR | 收到了 FIFO 上溢错误 (Received FIFO overrun error) | ||
TXUNDERR | 传输 FIFO 下溢错误 (Transmit FIFO underrun error) | ||
DTIMEOUT | 数据超时 (Data timeout) | ||
CTIMEOUT | 命令响应超时 (Command response timeout) 命令超时周期为固定值 64 个 SDIO_CK 时钟周期。 |
||
DCRCFAIL | 已发送/ 接收数据块(CRC 校验失败) (Data block sent/received (CRC check failed)) |
||
CCRCFAIL | 已接收命令响应(CRC 校验失败) (Command response received (CRC check failed)) |
3.5 CMD8: SEND_IF_COND
CMD8: SEND_IF_COND
这个命令定义在《Physical Specification Version 2.00》以上版本,它有2个作用:
- 电压检测:检查卡是否能在主机供电电压下工作。
- 扩展现有命令:启用CMD8可以扩展一些现有命令保留位的新功能。ACMD41被扩展以支持高容量SD存储卡。
#define SD_CHECK_PATTERN ((uint32_t)0x000001AA)
#define SDIO_SEND_IF_COND ((uint32_t)0x00000008)
/*!< 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);
通过下面CMD8命令格式和SD_CHECK_PATTERN(0x000001AA)可以了解到这里选择的操作电压是2.7~3.6V。
实际使用逻辑分析仪抓取主机发送CMD8时的波形如下:
CMD8命令发送成功后,我们需要检测SD卡是否有响应。通过它来判断SD卡是否支持SD2.0版本以上的协议。所以,从代码上来看这里只是判断了SDIO 状态寄存器 (SDIO_STA)是否已接收命令响应(CMDREND)。因为STM32F429支持SD2.0,所以只能支持大容量SD存储卡(32GB)。
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);
}
为什么判断的是Response7?因为在SD2.0协议是已经定义好的,无论是host还是SD卡都必须按照这个协议来。
从逻辑分析仪抓取的波形来看,SD卡有回复,说明它支持SD2.0以上协议,并且支持主机提供的电压2.7~3.6V。波形如下:
3.6 CMD55: SD_CMD_APP_CMD
CMD55: SD_CMD_APP_CMD
指定下个命令为特定应用命令,不是标准命令。SD卡主机模块系统旨在为各种应用程序类型提供一个标准接口。在此环境中,需要有特定的客户/应用程序功能。为实现这些功能,在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD)。要使用 SD卡制造商特定的 ACMD命令如ACMD41,需要在发送该命令之前发送 CMD55 命令,告知 SD卡接下来的命令为特定应用命令。CMD55 命令只对紧接的第一个命令有效,SD卡如果检测到 CMD55 之后的第一条命令为 ACMD 则执行其特定应用功能,如果检测发现不是 ACMD 命令,则执行标准命令。
#define SD_CMD_APP_CMD ((uint8_t)55)
/*!< 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 状态寄存器 (SDIO_STA)来判断命令响应是否已经正确被接收。然后,通过函数SDIO_GetCommandResponse获取SDIO 命令响应寄存器 (SDIO_RESPCMD) Value来判断Host接收到的响应命令是否是刚刚发送的命令。最后,通过函数SDIO_GetResponse获取SDIO 响应 1寄存器 (SDIO_RESP1) SD卡的状态。
static SD_Error CmdResp1Error(uint8_t cmd)
{
SD_Error errorstatus = SD_OK;
uint32_t status;
uint32_t response_r1;
status = SDIO->STA;
while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
{
status = SDIO->STA;
}
...
/*!< 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 for analysis */
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);
}
...
return(errorstatus);
}
从SD2.0协议里面可以了解到CMD55的response是R1,R1的主要获取的Card Status。
这里只是展示了一部分的Card Status,需要找参考完整的可以参考《Physical Specification Version 2.00》,我在下面的参考资料里面有发相关的链接。
通过逻辑分析仪抓取的波形,Card Status为SD卡已经准备好接收ACMD命令。如下:
3.7 ACMD41: SD_CMD_SD_APP_OP_COND
ACMD41: SD_CMD_SD_APP_OP_COND
被设计为为主机提供一种机制来识别和拒绝与主机所提供的VDD范围不匹配的卡。不能在指定范围内进行数据传输的SD卡,应放弃总线操作,进入Inactive 状态。OCR寄存器定义了相关的电压等级。
#define SD_VOLTAGE_WINDOW_SD ((uint32_t)0x80100000)
#define SD_HIGH_CAPACITY ((uint32_t)0x40000000)
#define SD_CMD_SD_APP_OP_COND ((uint8_t)41) /*!< For SD Card only */
SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SD_HIGH_CAPACITY;
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++;
ACMD41
主要设置的是OCR寄存器,希望设置的电压范围在3.2-3.3和高容量的SD卡(参考OCR寄存器),实际发送的波形如下:
发送ACMD41
后,主要是通过Response R3来获取OCR寄存器的状态,判断Card power up status bit (31bit)是否为高电平,如果为低电平那么SD卡还在power up阶段未完成。
重复发送ACMD41
获取OCR寄存器Power up status bit的状态,一直发送到该bit为高电平为止,此时说明power on这个过程已经完成。
4. 参考资料
SDIO参考的资料如下:
下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/14975835
移植成功的完整代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/15265756