其他链接
【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卡读取的事情