其他链接

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(三)-SD卡的操作流程

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(四)-介绍库函数,获取一些SD卡的信息

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(五)-文件管理初步介绍

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(六)-FatFs使用的思路介绍

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(七)-准备移植FatFs

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(八)-认识内存管理

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(终)-配合内存管理来遍历SD卡

(2020.11.24)最近要重启这个项目,看了下自己的博客,想看看有没有什么地方可以精简的,意外发现以前放的库函数文档缺失一些档案。。。抱歉

这里补上新的链接,另外,我找时间把FatFs补完,当初我写,也只是加深自己印象,只是没想到浏览次数有点多,那我更要对自己写出来的东西负责,谢谢大家了

(2021.02.02)终篇写完了,以后在研究W25Qxx + FatFs,有时间就回头优化一下自己的博客吧

分享官方的库函数文档:https://pan.baidu.com/s/1tSXSx1Q2sB3l7OWo2Zwx6Q

提取码:1234

库函数的位置如下,以及一些库函数的介绍

写程序前,先来厘清思路,免得等等混乱了。我想对SD卡做相关操作,那么,具体的流程如下:

【1】STM32上电后

【2】对几个SDIO相关的引脚初始化(查芯片手册,哪几个引脚对应SDIO,还有时钟也要设置)

【3】设置中断(SDIO中断,还有优先级之类的)

【4】复位SDIO外设寄存器(将SDIO外设寄存器,初始化为它们的默认复位值)

【5】调用库函数,SDIO初始化(库函数名:SDIO_Init,一些初始化的东西,例如频率,卡识别时要先设400K,后续数据传输时可以调高些,另外还有设置几根数据线之类的)

【6】调用库函数,设置SDIO上电(库函数名:SDIO_SetPowerState,要使用SD卡,就要把电源打开)

【7】调用库函数,使能SDIO时钟(库函数名:SDIO_ClockCmd,和SD卡交互数据需要时钟)

【8】调用发送命令的库函数,开始发送命令(CMD)(我第三章博客讲的【SD卡操作流程】,卡识别(卡识别模式)的时候,先发CMD0这个命令(命令的介绍在第二章博客),然后等待响应之类的)

开始一系列识别SD卡的操作(我第一章博客说明了SD卡的种类,通过命令,获得响应和没获得响应,都有对应的情况,请参考我第三章博客)

所以第8步骤的命令发送顺序是:CMD0 -> CMD8 -> CMD55 -> CMD41 -> CMD2 -> CMD3(由于CMD41是特定应用命令,在CMD41之前,必须发送CMD55)

【9】第8步骤最后发送了CMD3,进入数据传输模式,首先来获取SD的容量,卡的块大小之类的信息(发送CMD9命令)

【10】前9个步骤算是初始化流程,然后就可以做你想要的操作了,例如读取SD卡,或是写入数据,擦除SD卡等等

以上就是大致的流程

我分了好几篇博客,没办法,SD卡的操作相当繁琐,如果一篇博客要讲完所有东西,看的人一定头晕死了,分类一下比较清楚

有了思路,就可以开始写代码了,直接从第二步开始

【2】对几个SDIO相关的引脚初始化

先来看一张引脚图

和SDIO有关的引脚就这些

但是,SD卡只支持4位数据传输,所以只占SDIO_D0 ~ SDIO_D3,再加上SDIO_CK、SDIO_CMD,就是图上绿色框框内的这些

SDIO_D4、SDIO_D5、SDIO_D6、SDIO_D7,MMC卡应该会用的到(我第一章博客有介绍到),此刻无需理会

接下来对PC8、PC9、PC10、PC11、PC12,还有PD2做一些配置和使能时钟的动作,我们可以写一个函数方便调用

void mySD_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; /* GPIOC and GPIOD Periph clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD , ENABLE); // 使能两个时钟,因为SDIO在两个端口上,上面图片有说明了,一个在PC,另一个在PD GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO); // PC8使用SDIO功能
GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO); // PC9使用SDIO功能
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO); // PC10使用SDIO功能
GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO); // PC11使用SDIO功能
GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO); // PC12使用SDIO功能
GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO); // PD2使用SDIO功能 /* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */ // 配置PC8、PC9、PC10、PC11引脚
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 */ // 配置PD2引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure); /* Configure PC.12 pin: CLK pin */ // 配置PC12引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure); /*!< Configure SD_SPI_DETECT_PIN pin: SD Card detect pin */ // 如果你想用SPI来操作SD卡?
// GPIO_InitStructure.GPIO_Pin = SD_DETECT_PIN;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
// GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStructure); /* Enable the SDIO APB2 Clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE); // 使能SDIO时钟,SDIO是挂在APB2总线上的 /* Enable the DMA2 Clock */
RCC_AHB1PeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE); // 如果要使用DMA的话,使能DMA时钟
}

  

按顺序来,接下来是【3】设置中断

static void myNVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure; /* Configure the NVIC Preemption Priority Bits */ // 嵌套向量中断控制器组选择
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn; // 配置SDIO中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢断优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure); // 初始化配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = SD_SDIO_DMA_IRQn; // 使能SDIO DMA中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢断优先级为1
NVIC_Init(&NVIC_InitStructure); // 初始化配置NVIC
}

按顺序来,接下来是【4】复位SDIO外设寄存器

void mySDIO_DeInit(void)
{
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, DISABLE);
}

  

按顺序来,接下来是【5】调用库函数,SDIO初始化,由于刚开始是卡识别模式,所以时钟最高设置400KHz,后续数据传输模式时,可以再调高

void mySDIO_init(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);
}

  

按顺序来,接下来是【6】调用库函数,设置SDIO上电

void mySDIO_PowerOn(void)
{
/*!< Set Power State to ON */
SDIO_SetPowerState(SDIO_PowerState_ON);
}

  

按顺序来,接下来是【7】调用库函数,使能SDIO时钟

void mySDIO_EnableClock(void)
{
/*!< Enable SDIO Clock */
SDIO_ClockCmd(ENABLE);
}

  

按顺序来,接下来是【8】调用发送命令的库函数,开始发送命令(CMD)

按照一开始的思路,我们要发送CMD0 -> CMD8 -> CMD55 -> CMD41 -> CMD2 -> CMD3

上代码前,先解释一下SDIO_SendCommand这个库函数,这是发命令(CMD)用的

后续进行卡识别时,要发送CMD0、CMD8、CMD41等等(CMD41是特定应用命令,在它之前必须先发CMD55)

在卡识别结束后,对SD读、写、擦除等相关的操作,一律都是用这个库函数来操作的

我们先来发送CMD0、CMD8、CMD55、CMD41这几个命令,先判别此卡是什么类型

SD_Error mySD_Identify(void)
{
__IO SD_Error errorstatus = SD_OK;
uint32_t response = 0, count = 0, validvoltage = 0;
uint32_t SDType = SD_STD_CAPACITY; /*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*/ // 准备发送CMD0,开始一系列的卡识别
/*!< No CMD response required */
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;
SDIO_SendCommand(&SDIO_CmdInitStructure); // 发送命令 errorstatus = CmdError(); // CmdError等待发送完成函数,下面另一个代码块有函数的内容 if (errorstatus != SD_OK)
{
/*!< CMD Response TimeOut (wait for CMDSENT flag) */ // 响应超时
return(errorstatus);
} /*!< CMD8: SEND_IF_COND ----------------------------------------------------*/ // 准备发送CMD8
/*!< 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; // 设置命令为MD8
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(); // 等待R7响应,我第二章博客有说明,另外下方另一个代码块有这个函数的内容 if (errorstatus == SD_OK)
{
CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */ // ************ 此SD卡是2.0协议的卡 ************
SDType = SD_HIGH_CAPACITY;
}
else
{
/*!< CMD55 */
SDIO_CmdInitStructure.SDIO_Argument = 0x00;
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;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_APP_CMD); // R1响应
}
/*!< CMD55 */
SDIO_CmdInitStructure.SDIO_Argument = 0x00;
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;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_APP_CMD); // R1响应 /*!< 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_OK,那么此SD卡是1.0协议,否则就是MMC卡
{
/*!< 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); // R1响应 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; // 设置命令为CMD41
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(); // R3响应
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; // ************ 此SD卡是1.0协议的卡 ************
} }/*!< else MMC Card */ return(errorstatus);
}  

以下是等待函数和响应函数,对照上方的代码块,例如卡识别的时候,发送CMD0,会等待发送完成(执行CmdError等待函数),发送CMD8时,会需要一个响应(执行CmdResp7Error函数,等待R7响应)等等

/**
* @brief Checks for error conditions for CMD0.
* @param None
* @retval SD_Error: SD Card Error code.
*/
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);
} /**
* @brief Checks for error conditions for R1 response.
* @param cmd: The sent command index.
* @retval SD_Error: SD Card Error code.
*/
static SD_Error CmdResp1Error(uint8_t cmd) // ------------ R1响应 ------------
{
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);
} /**
* @brief Checks for error conditions for R2 (CID or CSD) response.
* @param None
* @retval SD_Error: SD Card Error code.
*/
static SD_Error CmdResp2Error(void) // ------------ R2响应 ------------
{
SD_Error errorstatus = SD_OK;
uint32_t status; status = SDIO->STA; while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
{
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);
} /*!< Clear all the static flags */
SDIO_ClearFlag(SDIO_STATIC_FLAGS); return(errorstatus);
} /**
* @brief Checks for error conditions for R3 (OCR) response.
* @param None
* @retval SD_Error: SD Card Error code.
*/
static SD_Error CmdResp3Error(void) // ------------ R3响应 ------------
{
SD_Error errorstatus = SD_OK;
uint32_t status; 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);
}
/*!< Clear all the static flags */
SDIO_ClearFlag(SDIO_STATIC_FLAGS);
return(errorstatus);
} /**
* @brief Checks for error conditions for R6 (RCA) response.
* @param cmd: The sent command index.
* @param prca: pointer to the variable that will contain the SD card relative
* address RCA.
* @retval SD_Error: SD Card Error code.
*/
static SD_Error CmdResp6Error(uint8_t cmd, uint16_t *prca) // ------------ R6响应 ------------
{
SD_Error errorstatus = SD_OK;
uint32_t status;
uint32_t response_r1; status = SDIO->STA; while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
{
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. */
response_r1 = SDIO_GetResponse(SDIO_RESP1); if (SD_ALLZERO == (response_r1 & (SD_R6_GENERAL_UNKNOWN_ERROR | SD_R6_ILLEGAL_CMD | SD_R6_COM_CRC_FAILED)))
{
*prca = (uint16_t) (response_r1 >> 16);
return(errorstatus);
} if (response_r1 & SD_R6_GENERAL_UNKNOWN_ERROR)
{
return(SD_GENERAL_UNKNOWN_ERROR);
} if (response_r1 & SD_R6_ILLEGAL_CMD)
{
return(SD_ILLEGAL_CMD);
} if (response_r1 & SD_R6_COM_CRC_FAILED)
{
return(SD_COM_CRC_FAILED);
} return(errorstatus);
} /**
* @brief Checks for error conditions for R7 response.
* @param None
* @retval SD_Error: SD Card Error code.
*/
static SD_Error CmdResp7Error(void) // ------------ R7响应 ------------
{
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);
}

最终,由CardType得知,此卡是哪一种类型的卡(SD卡1.0协议、SD卡2.0协议、MMC卡等等)

但是还没完成,按照卡识别流程图,最后还需要发送CMD2(要求卡返回它的CID)和CMD3(要求卡返回它的RCA地址),一切都完成,就可以进入数据传输模式

【题外话】其实我一直在想,CMD2、CMD3要不要和第9步骤的CMD9合并,后来想想还是算了,CMD9已经是数据传输模式,不要和流程图搞混了

所以我用两个函数【mySD_InitializeCards1】(CMD2、CMD3)、【mySD_InitializeCards2】(CMD9)来区分

SD_Error SD_InitializeCards1(void)
{
SD_Error errorstatus = SD_OK;
uint16_t rca = 0x01; if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
{
errorstatus = SD_REQUEST_NOT_APPLICABLE;
return(errorstatus);
} if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
{
/*!< Send CMD2 ALL_SEND_CID */
SDIO_CmdInitStructure.SDIO_Argument = 0x0;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID; // 设置命令为CMD2
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp2Error(); if (SD_OK != errorstatus)
{
return(errorstatus);
} CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
}
if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)
|| (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
{
/*!< Send CMD3 SET_REL_ADDR with argument 0 */
/*!< SD Card publishes its RCA. */
SDIO_CmdInitStructure.SDIO_Argument = 0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR; // 设置命令为CMD3
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 = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca); if (SD_OK != errorstatus)
{
return(errorstatus);
}
}
return(errorstatus);

  

按照顺序,接下来是【9】第8步骤最后发送了CMD3,进入数据传输模式,首先来获取SD的容量,卡的块大小之类的信息(发送CMD9命令)

SD_Error SD_InitializeCards2(void)
{
if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
{
RCA = rca; /*!< Send CMD9 SEND_CSD with argument as card's RCA */
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp2Error(); if (SD_OK != errorstatus)
{
return(errorstatus);
} CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
} errorstatus = SD_OK; /*!< All cards get intialized */ return(errorstatus);
}

一切都完成后,可以再次设置时钟,已经不再是卡识别模式了,可以把速度调快些

并且做一些处理,后续想要知道一些信息,访问SDCardInfo这个结构体就可以了

最终,选中SD卡,后续才可以做一些读、写、擦除操作

  /*!< Configure the SDIO peripheral */
/*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */
/*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */
SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_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); /*----------------- Read CSD/CID MSD registers ------------------*/
errorstatus = SD_GetCardInfo(&SDCardInfo); if (errorstatus == SD_OK)
{
/*----------------- Select Card --------------------------------*/
errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
} if (errorstatus == SD_OK)
{
errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
}

  

  

最后是步骤【10】,要尝试读取SD卡,不过,这篇博客我想在这里结束了

因为我必须要另外研究一个新的东西,叫FATFS,这是一个文件管理系统

想象一下,你的硬盘里面可能会有分C盘、D盘、E盘。。。

每个盘下面可能会用文件夹区分档案

例如

D:\音乐\中文歌...

D:\音乐\英文歌...

SD卡里面也有这种文件管理,等一切都研究完成,再来处理SD卡读取的事情

【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(四)-介绍库函数,获取一些SD卡的信息的更多相关文章

  1. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡

    由于一张SD卡要能读写,涉及到的技术有些多,我打算分以下几篇博客 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含 ...

  2. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(终)-配合内存管理来遍历SD卡

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  3. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(八)-认识内存管理

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  4. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(七)-准备移植FatFs

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  5. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(六)-FatFs使用的思路介绍

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  6. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(五)-文件管理初步介绍

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  7. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  8. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(三)-SD卡的操作流程

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  9. 第36章 SDIO—SD卡读写测试

    第36章     SDIO—SD卡读写测试 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/f ...

随机推荐

  1. 网关服务spring cloud zuul

    Zuul例子配置文件 spring.application.name=switch-gateway server.port=5555 请求路由 传统路由方式 zuul.routes.api-a-url ...

  2. PE节表详细分析

    目录 PE节表详细分析 0x00 前言 0x01 PE节表分析 节表结构 节表数量 节表名字 节表大小 节位置 节表属性 0x02 代码编写 PE节表详细分析 0x00 前言 上一篇文章我们学习了PE ...

  3. ELK集群之kibana(4)

    kibane安装及基础使用 Kibana的安装 Kibana包含前端展示.es操作简化 yum localinstall kibana-7.6.2-x86_64.rpm -y Kibana配置修改ki ...

  4. 【Python+postman接口自动化测试】(3)什么是接口测试?

    什么是接口测试? 接口测试是测试系统组件间接口的一种测试.接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点.测试的重点是要检查数据的交换.传递和控制管理过程,以及系统间的相互逻辑依 ...

  5. ubuntu install redis

    ubuntu install redis apt-get update apt-get install redis-server redis-server --daemonize yes

  6. building sasl.wrapper extention

    yum install gcc-c++ python-devel.x86_64 cyrus-sasl-devel.x86_64 pip install pyhs2 ref: https://www.o ...

  7. 微信小程序(七)

    组件: 组件是视图层的基本组成单元 组件自带一些功能与微信风格的样式 一个组件通常包括:开始标签和结束标签,属性用来修饰这个组件,内容在两个标签之内. 媒体组件 地图 开放能力 画布 视图容器 vie ...

  8. IDEA Plugin,写一个看股票指数和K线的插件

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 没招了,不写点刺激的,你总是不好好看! 以前,我不懂.写的技术就是技术内容,写的场景 ...

  9. UDP端口检查告警SHELL脚本(企业微信版机器人版)

    脚本准备 0Batch_Check.sh 1port_check.sh 2wechat_bot_alert.sh CheckList CheckList #支持大/小写 10.1.1.5 Udp 53 ...

  10. Spring Cloud Gateway的断路器(CircuitBreaker)功能

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...