STM32F4X SDIO(九) 例程讲解-SD卡擦除、读写

news2024/10/6 12:26:41

STM32F4X SDIO (九) 例程讲解-SD卡擦除、读写

  • 例程讲解-SD卡擦除、读写
    • SD卡擦除
      • CMD32:ERASE_WR_BLK_START
        • 命令发送
        • 命令响应
      • CMD33:ERASE_WR_BLK_END
        • 命令发送
        • 命令响应
        • CMD38:ERASE
        • 命令响应
      • CMD13:SD_CMD_SEND_STATUS
        • 命令发送
        • 命令回应
    • SD卡读数据
      • CMD16:SET_BLOCKLEN
        • 命令发送
        • 命令响应
      • 设置SDIO控制传输数据类型
      • CMD18:READ_MULTIPLE_BLOCK
        • 命令发送
        • 命令响应
      • DMA配置
        • DMA接收配置
      • CMD12:STOP_TRANSMISSION
        • 命令发送
        • 命令响应
      • 等待SD卡读取完毕
        • 命令发送
        • 命令响应
      • 数据读取波形
    • SD卡写
      • CMD16:SET_BLOCKLEN
        • 命令发送
        • 命令响应
      • 设置SDIO控制传输数据类型
      • CMD24:WRITE_BLOCK
        • 命令发送
        • 命令响应
      • DMA配置
        • DMA发送配置
      • 等待SD卡写入完成
        • 命令发送
        • 命令响应
      • SD卡写数据波形

本节例程基于 野火电子的STM32F407的SD卡读写例程进行讲解。上一节中讲解了SD卡设置成4下模式的步骤,本节将会讲解SD卡的 擦除和读写操作

例程讲解-SD卡擦除、读写

SD卡擦除

针对SD卡这种存储设备,在写入数据之前都需要将数据进行擦除。对于SDHC容量的SD卡来说,最小的擦除单位扇区,一个扇区对应的大小是512字节

CMD32:ERASE_WR_BLK_START

在进行SD卡的擦除操作前,需要设置SD卡的擦除地址,擦除的地址有两个,分别是擦除的起始地址擦除的结束地址。对于标准SD卡来说,地址以字节为单位,对于标准SDHC来说,地址以块为单位。设置擦除起始地址的命令是CMD32
在这里插入图片描述

命令发送

CMD32命令需要的参数要擦除块的起始地址

 SDIO_CmdInitStructure.SDIO_Argument =(uint32_t)startaddr; // 起始地址
 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START; // CMD32 
 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);

这里面的起始地址设置为0,也就是擦除第一个扇区。
在这里插入图片描述

命令响应

CMD32的响应类型是R1,所以需要判断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;
  }

  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);
  }

  /*!< 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);
  }

  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);
}

在这里插入图片描述
CMD32返回的卡状态是0x900,根据SD卡状态表可知,当前SD卡已经转准备就绪,并且处于传输模式下。
在这里插入图片描述

CMD33:ERASE_WR_BLK_END

擦除操作中,除了需要设置擦除的起始地址外,还需要设置擦除的结束地址
在这里插入图片描述

命令发送

CMD33命令需要的参数要擦除块的结束地址

 SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)endaddr;// 起始地址 
 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;// CMD33
 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);

在这里插入图片描述
从波形图可以可以看到,程序中设置的擦除结束地址是51200,也就是擦除100个扇区。

命令响应

CMD33的响应类型是R1,所以需要判断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;
  }

  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);
  }

  /*!< 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);
  }

  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);
}

在这里插入图片描述
CMD33返回的卡状态是0x900,根据SD卡状态表可知,当前SD卡已经转准备就绪,并且处于传输模式下。
在这里插入图片描述

CMD38:ERASE

设置好需要擦除的起始地址和结束地址后,就可以调用CMD38命令擦除扇区。
在这里插入图片描述

 SDIO_CmdInitStructure.SDIO_Argument = 0; // 参数为0
 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE; // CMD38
 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);

在这里插入图片描述

命令响应

CMD38的响应类型是R1b,所以需要判断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;
  }

  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);
  }

  /*!< 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);
  }

  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);
}

在这里插入图片描述

CMD38返回的卡状态是0x800,根据SD卡状态表可知,当前SD卡处于"not ready"状态。也就是还在擦除扇区中
在这里插入图片描述

CMD13:SD_CMD_SEND_STATUS

CMD13的作用是查询SD的状态,判断SD卡是否擦除完成。
在这里插入图片描述

命令发送

CMD13命令发送是需要带参数,参数是需要查询的SD卡的RCA地址

 SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
 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);

在这里插入图片描述

命令回应

在这里插入图片描述
由波形图可知,CMD13返回的SD卡状态是0x900,也就是当前SD卡已经擦除完成,准备就绪。

SD卡读数据

CMD16:SET_BLOCKLEN

在开始读数据之前,需要设置需要读取的数据块大小,对于SDHC容量的SD卡来说,数据块大小为512字节
在这里插入图片描述

命令发送
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize; // 数据块大小 SDHC为512字节
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN; // 命令索引 CMD16
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);

在这里插入图片描述

命令响应

CMD16的命令响应是R1,需要判断SD卡状态

在这里插入图片描述
从波形图可知,SD卡总线已经准备就绪,并且进入tran状态。
在这里插入图片描述

设置SDIO控制传输数据类型

设置完SD卡块大小之后,还需要设置SDIO控制器的数据传输类型,在例程中我们需要读取100个扇区的数据,也就是需要读取512 * 100总共51200字节的数据。

SDIO_DataInitStruct.SDIO_DataTimeOut = SD_DATATIMEOUT; // 传输超时时间
SDIO_DataInitStruct.SDIO_DataLength = NumberOfBlocks * BlockSize; // 传输数据大小 512 * 100
SDIO_DataInitStruct.SDIO_DataBlockSize = (uint32_t) 9 << 4; // 数据块大小 512字节
SDIO_DataInitStruct.SDIO_TransferDir = SDIO_TransferDir_ToSDIO; //传输方向 从SD卡到SDIO控制器
SDIO_DataInitStruct.SDIO_TransferMode = SDIO_TransferMode_Block; // 传输类型 块传输
SDIO_DataInitStruct.SDIO_DPSM = SDIO_DPSM_Enable; // 使能DPSM状态机
SDIO_DataConfig(&SDIO_DataInitStruct);

CMD18:READ_MULTIPLE_BLOCK

CMD18作用是读取多个扇区数据,CMD18需要传入一个数据的起始地址
在这里插入图片描述

命令发送
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr; // 数据读取的起始地址
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_MULT_BLOCK; // 命令索引 CMD18
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);

在这里插入图片描述

命令响应

CMD18的命令响应是R1,需要判断SD卡状态
在这里插入图片描述
从波形图可知,SD卡总线已经准备就绪,并且进入tran状态。
在这里插入图片描述

DMA配置

一般情况下,在进行SD卡数据传输时,因为传输的数据量都比较大,所以一般都会使用DMA进行传输。

DMA接收配置

以下是例程中的接收DMA配置。

void SD_LowLevel_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize)
{
  DMA_InitTypeDef SDDMA_InitStructure;

  DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_FEIF | SD_SDIO_DMA_FLAG_DMEIF | SD_SDIO_DMA_FLAG_TEIF | SD_SDIO_DMA_FLAG_HTIF | SD_SDIO_DMA_FLAG_TCIF);

  /* DMA2 Stream3  or Stream6 disable */
  DMA_Cmd(SD_SDIO_DMA_STREAM, DISABLE);

  /* DMA2 Stream3 or Stream6 Config */
  DMA_DeInit(SD_SDIO_DMA_STREAM);

  SDDMA_InitStructure.DMA_Channel = SD_SDIO_DMA_CHANNEL;
  SDDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
  SDDMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)BufferDST;
  SDDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  SDDMA_InitStructure.DMA_BufferSize = 1;
  SDDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  SDDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  SDDMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  SDDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  SDDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  SDDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  SDDMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  SDDMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  SDDMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
  SDDMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;
  DMA_Init(SD_SDIO_DMA_STREAM, &SDDMA_InitStructure);
  DMA_ITConfig(SD_SDIO_DMA_STREAM, DMA_IT_TC, ENABLE);
  DMA_FlowControllerConfig(SD_SDIO_DMA_STREAM, DMA_FlowCtrl_Peripheral);

  /* DMA2 Stream3 or Stream6 enable */
  DMA_Cmd(SD_SDIO_DMA_STREAM, ENABLE);
}

CMD12:STOP_TRANSMISSION

在进行SD卡的多数据块传输时,当所有数据块都读取完成后,我们需要发送CMD12命令,告诉SD卡停止传输
在这里插入图片描述

命令发送
SDIO_CmdInitStructure.SDIO_Argument = 0; 
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_STOP_TRANSMISSION; // 命令索引 CMD12
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);

在这里插入图片描述

命令响应

CMD12的命令响应是R1b,需要判断SD卡状态
在这里插入图片描述

等待SD卡读取完毕

当我们向SD卡发送完CMD12命令后,需要发送CMD13不断读取SD卡状态,判断SD卡是否读取完成
在这里插入图片描述

命令发送
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS; // 命令索引 CMD13
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);

在这里插入图片描述

命令响应

在这里插入图片描述
从波形图可知,SD卡总线已经准备就绪,并且进入tran状态。
在这里插入图片描述

数据读取波形

在这里插入图片描述

SD卡写

SD卡的写过程跟SD卡的读过程差不多。只不过数据传输方向相反

CMD16:SET_BLOCKLEN

在开始写数据之前,需要设置需要读取的数据块大小,对于SDHC容量的SD卡来说,数据块大小为512字节
在这里插入图片描述

命令发送
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize; // 数据块大小 SDHC为512字节
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN; // 命令索引 CMD16
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);

在这里插入图片描述

命令响应

CMD16的命令响应是R1,需要判断SD卡状态

在这里插入图片描述
从波形图可知,SD卡总线已经准备就绪,并且进入tran状态。
在这里插入图片描述

设置SDIO控制传输数据类型

设置完SD卡块大小之后,还需要设置SDIO控制器的数据传输类型,在例程中我们需要读取1个扇区的数据,也就是需要读取512总共51200字节的数据。

SDIO_DataInitStruct.SDIO_DataTimeOut = SD_DATATIMEOUT; // 传输超时时间
SDIO_DataInitStruct.SDIO_DataLength = BlockSize; // 传输数据大小 512字节
SDIO_DataInitStruct.SDIO_DataBlockSize = (uint32_t) 9 << 4; // 数据块大小 512字节
SDIO_DataInitStruct.SDIO_TransferDir = SDIO_TransferDir_ToCard; //传输方向 从SDIO控制器到SD卡
SDIO_DataInitStruct.SDIO_TransferMode = SDIO_TransferMode_Block; // 传输类型 块传输
SDIO_DataInitStruct.SDIO_DPSM = SDIO_DPSM_Enable; // 使能DPSM状态机
SDIO_DataConfig(&SDIO_DataInitStruct);

CMD24:WRITE_BLOCK

CMD24的作用是写单个块。CMD24需要带参数,参数是写入的扇区地址
在这里插入图片描述

命令发送
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)WriteAddr; // 扇区地址 0地址
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN; // 命令索引 CMD24
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);

在这里插入图片描述

命令响应

CMD24的命令响应是R1,需要判断SD卡状态
在这里插入图片描述
从波形图可知,SD卡总线已经准备就绪,并且进入tran状态。
在这里插入图片描述

DMA配置

DMA发送配置

以下是例程中的DMA发送配置。

void SD_LowLevel_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize)
{
  DMA_InitTypeDef SDDMA_InitStructure;

  DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_FEIF | SD_SDIO_DMA_FLAG_DMEIF | SD_SDIO_DMA_FLAG_TEIF | SD_SDIO_DMA_FLAG_HTIF | SD_SDIO_DMA_FLAG_TCIF);

  /* DMA2 Stream3  or Stream6 disable */
  DMA_Cmd(SD_SDIO_DMA_STREAM, DISABLE);

  /* DMA2 Stream3  or Stream6 Config */
  DMA_DeInit(SD_SDIO_DMA_STREAM);

  SDDMA_InitStructure.DMA_Channel = SD_SDIO_DMA_CHANNEL;
  SDDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
  SDDMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)BufferSRC;
  SDDMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
  SDDMA_InitStructure.DMA_BufferSize = 1;
  SDDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  SDDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  SDDMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  SDDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  SDDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  SDDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  SDDMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  SDDMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  SDDMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
  SDDMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;
  DMA_Init(SD_SDIO_DMA_STREAM, &SDDMA_InitStructure);
  DMA_ITConfig(SD_SDIO_DMA_STREAM, DMA_IT_TC, ENABLE);
  DMA_FlowControllerConfig(SD_SDIO_DMA_STREAM, DMA_FlowCtrl_Peripheral);

  /* DMA2 Stream3  or Stream6 enable */
  DMA_Cmd(SD_SDIO_DMA_STREAM, ENABLE);
    
}

等待SD卡写入完成

由于例程中是只写一个扇区,所以不用发送CMD12强制停止传输,但是我们需要发送CMD13不断读取SD卡状态,判断SD卡是否读取完成
在这里插入图片描述

命令发送
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS; // 命令索引 CMD13
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);

在这里插入图片描述

命令响应

在这里插入图片描述
从波形图可知,SD卡总线已经准备就绪,并且进入tran状态。
在这里插入图片描述

SD卡写数据波形

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1196703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 配合图形库实现画线效果

#include<stdio.h> #include <conio.h> #include<math.h> #include <graphics.h> // 引用图形库头文件 #define N 12 int List[N][N];void draw() {for (int i 0; i < N; i) {int x 200 * cos(2 * 3.14 * i / N);int y 200 * sin(2 * 3.1…

SwiftUI 如何保证 Text 中字符数量相等的字符串显示宽度一定相同?

0. 问题现象 在 SwiftUI 中我们往往需要将内容相似的字符串展列出来给用户比较,这些字符串内容各有不同但字符数量始终是相等的,我们希望它们的显示宽度始终保持一致: 如上图所示:即使是等宽字符组成的字符串在字符数量相等时它们的显示宽度仍然可能不一致。但演示中最底部…

抖音短视频账号矩阵系统、短视频矩阵源码+无人直播源码开发可打包

抖音短视频账号矩阵系统、短视频矩阵源码无人直播源码开发可打包 矩阵系统源码主要有三种框架&#xff1a;Spring、Struts和Hibernate。Spring框架是一个全栈式的Java应用程序开发框架&#xff0c;提供了IOC容器、AOP、事务管理等功能。Struts框架是一个MVC架构的Web应用程序框…

Raft分布式一致性算法

拜占庭将军 假设多位拜占庭将军中没有叛军&#xff0c;信使的信息可靠但有可能被暗杀的情况下&#xff0c;将军们如何达成是否要进攻的一致性决定&#xff1f;解决问题的思路是&#xff0c;从多位处于平等地位的将军中选举出一位大将军&#xff0c;所有作战指令由大将军发出。…

python升级pip的时候一直失败

如图,一直提示使用 python.exe -m pip install --upgrade pip 进行升级pip,但是执行这句命令又不成功.然后综合了几篇文章以后使用了下面的命令可以升级了 python -m pip install --upgrade pip --user -i https://mirrors.aliyun.com/pypi/simple/ 主要是在推荐的语句上使用…

解决内存泄漏问题,Profiler工具的使用介绍

什么是内存泄漏 内存泄漏&#xff08;Memory Leak&#xff09;是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放&#xff0c;造成系统内存的浪费&#xff0c;导致程序运行速度减慢甚至系统崩溃等严重后果。 以上是官方针对内存泄漏的说法。说的通俗一点&#x…

高频SQL50题(基础版)-3

文章目录 主要内容一.SQL练习题1.1174-即时食物配送代码如下&#xff08;示例&#xff09;: 2.550-游戏玩法分析代码如下&#xff08;示例&#xff09;: 3.2356-每位教师所教授的科目种类的数量代码如下&#xff08;示例&#xff09;: 4.1141-查询近30天活跃用户数代码如下&…

【FPGA】Verilog:计数器 | 异步计数器 | 同步计数器 | 2位二进制计数器的实现 | 4位十进制计数器的实现

目录 Ⅰ. 实践说明 0x00 计数器&#xff08;Counter&#xff09; 0x01 异步计数器&#xff08;Asynchronous Counter&#xff09; 0x02 同步计数器&#xff08;Synchronous Counter&#xff09; Ⅱ. 实践&#xff1a;2位二进制计数器 0x00 实践说明 0x01 输出表 0x02 代…

由于找不到dll无法执行代码的解决方法,深度解析5个dll修复方法

在使用计算机的过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到dll无法执行代码”。这个错误通常发生在程序运行时&#xff0c;系统无法找到所需的动态链接库&#xff08;DLL&#xff09;文件。这个问题可能由多种原因引起&#xff0c;包括缺少…

前端如何结合mock模拟假数据

由于某人不想写后端接口&#xff0c;不想用真数据对接vue-element-admin框架&#xff0c;用以前的接口&#xff0c;改token有点点麻烦&#xff0c;所以咱试试mock.js

《算法通关村——透彻理解二叉树中序遍历的应用》

《算法通关村——透彻理解二叉树中序遍历的应用》 直接上题 108. 将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高…

【电工基础】

电工基础 11.1 简介1.2 电路作用1.3 电路模型1.4 电流定义1.5 电压定义1.6 电动势1.7 电阻元件1.7.1 电阻元件定义1.7.2 电阻原件的特性1.7.31.7.4 1.81.91.10 345 1 1.1 简介 电源外部&#xff0c;正电荷移动的方向是由电源正极向电源负极方向&#xff0c;负电荷移动的方向是…

GEE:遥感影像二值化

作者:CSDN @ _养乐多_ 在Google Earth Engine(GEE)中,图像二值化是将图像中的像素值转换为二进制(0或1)的过程。这通常用于将连续的遥感图像转换为只有两个值的二值图像,以突出图像中的特定特征或区域。 结果如下图所示,将NDVI图像中,大于0.3的值设置为1(黑色),小…

VBA_MF系列技术资料1-217

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04属于定…

超详细的性能测试流程

一、性能测试概念 我们经常看到的性能测试概念&#xff0c;有人或称之为性能策略&#xff0c;或称之为性能方法&#xff0c;或称之为性能场景分类&#xff0c;大概可以看到性能测试、负载测试、压力测试、强度测试等一堆专有名词的解释。 针对这些概念&#xff0c;我不知道你…

Harbor了解及部署

Harbor 无论是使用Docker-distribution去自建仓库&#xff0c;还是通过官方镜像跑容器的方式去自建仓库&#xff0c;通过前面的演示我们可以发现其是非常的简陋的&#xff0c;还不如直接使用官方的Docker Hub去管理镜像来得方便&#xff0c;至少官方的Docker Hub能够通过web界…

认识继承和多态

1 继承 1.1 为什么需要继承 Java 中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程序里就需要考虑 比如&a…

赶紧收藏!阿里内部使用的127页k8s实战手册,不能再详细了!

2022 年 12 月 8 号Kubernetes 发布了1.26新版本&#xff0c;此版本中有两个突出的新功能&#xff0c;它们有可能改变用户与 Kubernetes 交互的方式&#xff0c;此外&#xff0c;其他功能将使集群管理员的工作更轻松。 说起来&#xff0c;Kubernetes从诞生初期就广泛受到业界的…

英国访问学者类型签证详解

近年来&#xff0c;英国作为世界上优秀的学术研究中心之一&#xff0c;吸引了众多国际学者的目光。为了促进国际学术交流&#xff0c;英国设立了多种类型的访问学者签证&#xff0c;以满足不同学者的需求。在本文中&#xff0c;我们将深入探讨英国访问学者类型签证及其特点。 1…

金字塔原理小节

目录 第1章 为什么要用金字塔结构 一、归类分组&#xff0c;将思想组织成金字塔 二、奇妙的数字“7” 三、归类分组搭建金字塔 四、找出逻辑关系&#xff0c;抽象概括 五、自上而下表达&#xff0c;结论先行 第1章 为什么要用金字塔结构 如果受众希望通过阅读你的文章、听…