【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(四)-介绍库函数,获取一些SD卡的信息
其他链接
【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卡读取的事情