【STM32】STM32 SDIO SD卡读写测试(二)-- SD_Init之Power On阶段

相关文章

《【SDIO】SDIO、SD卡、FatFs文件系统相关文章索引》

1. 前言

本篇文章主要是介绍stm324x9i_eval_sdio_sd.c里面SD_Init()函数完整的过程。它主要是实现了SDIO的初始化SD卡的Power UPSD卡的初始化获取SD卡的相关信息等,下面会详细介绍SDIO的初始化SD卡的Power UP的分析。
在这里插入图片描述

2. SD_LowLevel_Init()

SD_LowLevel_Init()主要功能是初始化使用的IO相关的Clock,具体如下:

  1. PC.08, PC.09, PC.10, PC.11配置为SDIO模式DATA0, DATA1, DATA2, DATA3功能。
  2. PD.02, PC.12配置为SDIO模式SDIO_CMD, SDIO_CLK功能。
  3. 配置SDIO接口的Clock使能。
  4. 配置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
  • CMD8: SEND_IF_COND
  • CMD55: SD_CMD_APP_CMD
  • ACMD41: SD_CMD_SD_APP_OP_COND

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 */

while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
{
    
    
  ...

  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

猜你喜欢

转载自blog.csdn.net/ZHONGCAI0901/article/details/113809318