STM32F4X SDIO(九) 例程讲解-SD卡擦除、读写
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;// CMD33SDIO_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; // 参数为0SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE; // CMD38SDIO_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卡写数据波形

相关文章:
STM32F4X SDIO(九) 例程讲解-SD卡擦除、读写
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_…...
【机器学习范式】监督学习,无监督学习,强化学习, 半监督学习,自监督学习,迁移学习,对比分析+详解与示例代码
目录 1. 监督学习 (Supervised Learning): 2. 无监督学习 (Unsupervised Learning): 3. 强化学习 (Reinforcement Learning): 4. 半监督学习 (Semi-Supervised Learning): 5. 自监督学习 (Self-Supervised Learning): 6. 迁移学习 (Transfer Learning): 7 机器学习范式应…...
JUC包下面的四大天王+线程池部分知识
一)Semphore:限流器用我就对了 Java中信号量Semphore是把操作系统原生的信号量封装了一下,本质就是一个计数器,描述了 可用资源的个数,主要涉及到两个操作 如果计数器为0了,继续Р操作,就会出现阻塞等待的情况 P操作:申…...
AGV系统控制位置管理功能
# ファイル: agv_locattion.py # 説明: AGV (Automated Guided Vehicle) の位置情報を管理し、UDPサーバーとして動作するGUIアプリケーションです。 # 必要なライブラリをインポート import tkinter as tk import socket import threading def AGV_handle_submit(canvas, st…...
JavaScript从入门到精通系列第三十三篇:详解正则表达式语法(二)
文章目录 一:正则表达式 1: 检查一个字符串中是否有. 2:第二种关键表达 3:第三种关键表达 编辑4:第四种关键表达 5:第五种关键表达 6:第六种关键表达 二:核心表达二 1&am…...
由于找不到 d3dx9_43.dll,无法继续执行代码。重新安装程序可能会解决此问题
电脑出现d3dx9_43.dll缺失的问题,通常是由于DirectX组件未安装或损坏导致的。为了解决这个问题,我为您提供了以下四个解决方法: d3dx9_43.dll解决方法1. 使用dll修复程序修复 首先,使用系统文件程序dll进行修复操作非常简单&…...
AI全栈大模型工程师(二十一)LangChain和SemanticKernel怎么选
LangChain 和 Semantic Kernel 怎么选? #%% md 划重点: 两者都值得学C#、JavaScript 和 Java 现在没得选做原型,首选 LangChain。功能多,开发快做产品,还是 SK 长期更可依赖建议只用 SK 的 Connectors 和 Plugins 能力…...
npm install 报错 chromedriver 安装失败的解决办法
npm install chromedriver --chromedriver_cdnurlhttp://cdn.npm.taobao.org/dist/chromedriver...
C语言--每日五道选择题--Day6
第一题 1、声明以下变量,则表达式: ch/i (f*d – i) 的结果类型为( ) char ch; int i; float f; double d; A: char B: int C: float D: double 答案及解析 D 基本数据类型的等级从低到高如下:char-> int-> long-> f…...
element-ui 封装 表格
一、封装表格组件 <template><el-table :data"list" :default-sort"{ prop: date }" style"width: 100%"><template v-for"item in tableColumn"><el-table-columnv-if"item.filters":prop"item…...
数据的使用、表关系的创建、Django框架的请求生命周期流程图
目录 一、数据的增删改查 1. 用户列表的展示 2. 修改数据的逻辑分析 3. 删除功能的分析 二、如何创建表关系 三、Django的请求生命周期流程图 一、数据的增删改查 1. 用户列表的展示 把数据表中得用户数据都给查询出来展示在页面上 查询数据 def userlist(request):&qu…...
Python基础教程:类--继承和方法的重写
嗨喽,大家好呀~这里是爱看美女的茜茜呐 什么是继承 继承就是让类与类之间产生父子关系,子类可以拥有父类的静态属性和方法 继承就是可以获取到另一个类中的静态属性和普通方法(并非所有成员) 在python中,新建的类可…...
Three.js提供了多种类型的灯光
Three.js提供了多种类型的灯光,包括环境光、点光源、平行光源和聚光灯。这些灯光可以用来照亮场景中的物体,使其看起来更加真实。 环境光(AmbientLight):环境光会均匀地照亮场景中的所有物体,没有方向,不能用来投射阴…...
精通Nginx(10)-负载均衡
负载均衡就是将前端过来的负载分发到两台或多台应用服务器。Nginx支持多种协议的负载均衡,包括http(s)、TCP、UDP(关于TCP、UDP负载均衡另文讲述)等。 目录 HTTP负载均衡 负载均衡策略 轮询 least_conn(最少连接) hash(通用哈希) ip_hash(IP 哈希) random(随…...
Hls学习(一)
1:CPU、DSP、GPU都算软件可编程的硬件 2:dsp在递归方面有所减弱,在递归方面有所增强,比如递归啊等,GPU可以同时处理多个进程,对于大块数据,流处理比较适用 3:为了提高运算量处理更多…...
Maven打包引入本地依赖包
Maven打包引入本地依赖包 SpringBoot 工程,Maven 在构建项目时,如何引入本地 Jar 包? 适合场景: 引用的依赖不在 Maven 仓库第三方公司提供的 SDK 包Maven 内网离线开发引入被定制改动过的 Jar 包 解决方法: 在 I…...
Docker常用命令及部署微服务项目
Docker常用命令及部署微服务项目 1、Docker常用命令 1、设置Yum源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 2、安装docker yum -y install docker-ce 3、启动docker service docker start 4、验证 docker version 5…...
okhttp添加公共参数
在项目开发中很多时候后台都会给一些全局的公共入参,比如携带手机信息或者时间戳等字段。而我们在使用okhttp时,就需要我们单独就行二次封装处理了,对于请求全局参数,每次请求都要去写一次,那是肯定不行的。 所以就要我…...
基于SpringBoot的SSMP整合案例(开启日志与分页查询条件查询功能实现)
开启事务 导入Mybatis-Plus框架后,我们可以使用Mybatis-Plus自带的事务,只需要在配置文件中配置即可 使用配置方式开启日志,设置日志输出方式为标准输出mybatis-plus:global-config:db-config:table-prefix: tb_id-type: autoconfiguration:…...
android studio 修改图标
Android Studio 修改图标 简介 Android Studio 是一款由谷歌推出的用于开发 Android 应用程序的集成开发环境(IDE)。在开发过程中,我们可以根据自己的需求修改 Android Studio 的图标,以个性化我们的开发环境。 本文将介绍如何在…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
