平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线
工程介绍:主要文件在USER组中,bsp_sdio_sdcard.c,bsp_sdio_sdcard.h和main.c,另外FatFs是用来后面移植文件系统使用的,对于本节内容暂时不需要。bsp_sdio_sdcard.c和bsp_sdio_sdcard.h文件主要参考教材《STM32库开发实战指南——基于STM32F03》。教材中部分省略内容来自于其他网友总结,在此表示感谢。
本文想大概讲一下SD卡读写实验的具体步骤,以及最重要的是一个细节问题,它使我在这个问题上耗了两天的时间。
大概流程如下:
1.硬件设计:
因为在该平台的板子上已经存在了一个SD卡接口,因此,不需要额外的连线,只需要一条串口连接线连接到电脑上输出调试信息即可。
2.软件设计:
操作的大致流程:
1》初始化相关的GPIO及SDIO外设;
2》配置SDIO基本通信环境进入卡识别模式,通过几个命令处理得到卡类型;
3》如果是可用卡就进入数据传输模式,接下来可以进行读写和擦差操作。
具体的步骤:
因为官方固件库有实现的案例,所以我们不妨”拿来主义“。其实bsp_sdio_sdcard.c和bsp_sdio_sdcard.h就是对stm32_eval_sdio_sd.c和stm32_eval_sdio_sd.h文件的修改和借鉴。我是直接把名字改了,然后在源文件的基础上做了修改。
2.1 GPIO的初始化和DMA配置(以下函数位于bsp_sdio_sdcard.c和bsp_sdio_sdcard.h文件中,其大部分内容来自于上面固件库的例程stm32_eval_sdio_sd.c和stm32_eval_sdio_sd.h的修改和添加)
//宏定义 //SDIO_FIOF地址=SDIO地址+0x80至SDIO地址+0xfc #define SDIO_FIFO_ADDRESS ((uint32_t) 0x40018080) //SDIO初始化时钟频率,最大400kHz #define SDIO_INIT_CLK_DIV ((uint8_t) 0xB2) //数据传输时钟频率,最大25MHz #define SDIO_TRANSFER_CLK_DIV ((uint8_t) 0x02)//数据读写速率 //SDIO传输使用的模式,可选DMA模式或者普通模式 #define SD_DMA_MODE ((uint32_t) 0x00000000) //#define SD_POLLING_MODE ((uint32_t) 0x00000002) //SD卡检测GPIO引脚 #define SD_DETECT_GPIO_PORT GPIOC #define SD_DETECT_PIN GPIO_Pin_13
//GPIO初始化,开启时钟 static void GPIO_Configuration() { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);//开启时钟 //配置PC8/9/10/11/12引脚(D0/1/2/3和CLK) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置复用模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); //配置PD2为CMD引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); //使能SDIO AHB时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE); //使能DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); }
//DMA传输配置,接收数据 void SD_DMA_RxConfig(uint32_t* BufferDST, uint32_t BufferSize) { DMA_InitTypeDef DMA_InitStructure; //清除DMA标志位 DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4); //配置前禁止DMA DMA_Cmd(DMA2_Channel4, DISABLE); //DMA传输配置 //外设地址 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS; //目标地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST; //外设为源地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //除以4,把字转换成字节单位 DMA_InitStructure.DMA_BufferSize = BufferSize / 4; //外设地址不增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //存储目标地址 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //外设数据大小为字 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //存储数据大小 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //不循环,一次 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //通道优先级为高 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //非存储器至存储器模式 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //初始化DMA DMA_Init(DMA2_Channel4, &DMA_InitStructure); //使能DMA通道 DMA_Cmd(DMA2_Channel4, ENABLE); }
//为SDIO发送数据配置DMA2的通道2的请求 void SD_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize) { DMA_InitTypeDef DMA_InitStructure; //清除DMA标志位 DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4); //配置前禁止DMA DMA_Cmd(DMA2_Channel4, DISABLE); //DMA传输配置 //外设地址 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS; //存储地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferSRC; //外设为目的地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //除以4,把字转换成字节单位 DMA_InitStructure.DMA_BufferSize = BufferSize / 4; //外设地址不增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //存储目标地址 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //外设数据大小为字 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //存储数据大小 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //不循环,一次 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //通道优先级为高 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //非存储器至存储器模式 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //初始化DMA DMA_Init(DMA2_Channel4, &DMA_InitStructure); //使能DMA通道 DMA_Cmd(DMA2_Channel4, ENABLE); }
//配置中断控制器 void SDIO_NVIC_Configuration() { NVIC_InitTypeDef NVIC_InitStructure; //优先级组选择 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //选择USART的中断源 NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn; //配置抢占优先级 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //配置子优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //使能中断 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //初始化配置 NVIC_Init(&NVIC_InitStructure); }2.2 SDIO初始化
/** * @brief Initializes the SD Card and put it into StandBy State (Ready for data * transfer). * @param None * @retval SD_Error: SD Card Error code. */ SD_Error SD_Init(void) { SD_Error errorstatus = SD_OK; //配置SDIO中断优先级 SDIO_NVIC_Configuration(); //SDIO外设底层引脚初始化 GPIO_Configuration(); //对SDIO所有的寄存器进行复位 SDIO_DeInit(); //上电,并进行卡识别流程,确认卡的操作电压 errorstatus = SD_PowerON(); //识别不成功 if (errorstatus != SD_OK) { /*!< CMD Response TimeOut (wait for CMDSENT flag) */ return(errorstatus); } //识别成功,对SD卡进行初始化 errorstatus = SD_InitializeCards(); //初始化失败 if (errorstatus != SD_OK) { /*!< CMD Response TimeOut (wait for CMDSENT flag) */ return(errorstatus); } //进入数据传输模式,提高读写速度 /*!< Configure the SDIO peripheral */ /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */ /*!< on STM32F2xx 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); if (errorstatus == SD_OK) { /*----------------- 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); } return(errorstatus); }与之相对的还有一个SD_DeInit()函数,复位寄存器。
void SD_LowLevel_DeInit(void) { GPIO_InitTypeDef GPIO_InitStructure; /*!< Disable SDIO Clock */ SDIO_ClockCmd(DISABLE); /*!< Set Power State to OFF */ SDIO_SetPowerState(SDIO_PowerState_OFF); /*!< DeInitializes the SDIO peripheral */ SDIO_DeInit(); /*!< Disable the SDIO AHB Clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, DISABLE); /*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOC, &GPIO_InitStructure); /*!< Configure PD.02 CMD line */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); }
//对SDIO所有的寄存器进行复位 void SD_DeInit(void) { SD_LowLevel_DeInit(); }SD_PowerOn()函数用于查询卡的工作电压和时钟控制配置,没有修改。可以看一下。
/** * @brief Enquires cards about their operating voltage and configures * clock controls. * @param None * @retval SD_Error: SD Card Error code. */ SD_Error SD_PowerON(void) { 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 */ /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */ /*!< on STM32F2xx 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); /*!< Set Power State to ON */ SDIO_SetPowerState(SDIO_PowerState_ON); /*!< Enable SDIO Clock */ SDIO_ClockCmd(ENABLE); /*!< CMD0: 复位所有的SD卡,使其进入空闲状态 ---------------------------------------------------*/ /*!< No CMD response required */ SDIO_CmdInitStructure.SDIO_Argument = 0x0; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;//关闭等待中断 SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;//CPSM在发送数据之前等待数据传输结束 SDIO_SendCommand(&SDIO_CmdInitStructure); //检测是否正确接收到Cmd0 errorstatus = CmdError(); if (errorstatus != SD_OK) { /*!< CMD Response TimeOut (wait for CMDSENT flag) */ return(errorstatus); } /*!< CMD8: SEND_IF_COND ----------------------------------------------------*/ /*!< 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; 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(); if (errorstatus == SD_OK) { CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */ SDType = SD_HIGH_CAPACITY; } else { //有相应就是SD CARD 2.0,此时无响应,代表是1.x或MMC /*!< CMD55 */ 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); } //检查是SD卡,还是MMC卡,或者是不支持的卡 /*!< CMD55 */ 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); /*!< 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 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); 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; 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(); 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; } }/*!< else MMC Card */ return(errorstatus); }初始化所有的卡或者单个卡进入就绪状态,没有修改。
/** * @brief Intialises all cards or single card as the case may be Card(s) come * into standby state. * @param None * @retval SD_Error: SD Card Error code. */ SD_Error SD_InitializeCards(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; 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; 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); } } 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); }2.3 数据操作
SD卡数据擦除操作,基本没有修改。
/** * @brief Allows to erase memory area specified for the given card. * @param startaddr: the start address. * @param endaddr: the end address. * @retval SD_Error: SD Card Error code. */ SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr) { SD_Error errorstatus = SD_OK; uint32_t delay = 0; __IO uint32_t maxdelay = 0; uint8_t cardstate = 0; /*!< Check if the card coomnd class supports erase command */ if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0) { errorstatus = SD_REQUEST_NOT_APPLICABLE; return(errorstatus); } //延时,根据时钟分频设置来计算 maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2); if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED) { errorstatus = SD_LOCK_UNLOCK_FAILED; return(errorstatus); } if (CardType == SDIO_HIGH_CAPACITY_SD_CARD) { startaddr /= 512; endaddr /= 512; } /*!< According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */ if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType)) { /*!< Send CMD32 SD_ERASE_GRP_START with argument as addr */ SDIO_CmdInitStructure.SDIO_Argument = startaddr; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START; 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_SD_ERASE_GRP_START); if (errorstatus != SD_OK) { return(errorstatus); } /*!< Send CMD33 SD_ERASE_GRP_END with argument as addr */ SDIO_CmdInitStructure.SDIO_Argument = endaddr; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END; 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_SD_ERASE_GRP_END); if (errorstatus != SD_OK) { return(errorstatus); } } /*!< Send CMD38 ERASE */ SDIO_CmdInitStructure.SDIO_Argument = 0; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE; 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_ERASE); if (errorstatus != SD_OK) { return(errorstatus); } for (delay = 0; delay < maxdelay; delay++) {} /*!< Wait till the card is in programming state */ errorstatus = IsCardProgramming(&cardstate); while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate))) { errorstatus = IsCardProgramming(&cardstate); } return(errorstatus); }数据写入操作,需要修改,关键,否则可能出错。
/** * @brief Allows to write one block starting from a specified address in a card. * The Data transfer can be managed by DMA mode or Polling mode. * @note This operation should be followed by two functions to check if the * DMA Controller and SD Card status. * - SD_ReadWaitOperation(): this function insure that the DMA * controller has finished all data transfer. * - SD_GetStatus(): to check that the SD Card has finished the * data transfer and it is ready for data. * @param writebuff: pointer to the buffer that contain the data to be transferred. * @param WriteAddr: Address from where data are to be read. * @param BlockSize: the SD card Data block size. The Block size should be 512. * @retval SD_Error: SD Card Error code. */ SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize) { SD_Error errorstatus = SD_OK; TransferError = SD_OK; TransferEnd = 0; StopCondition = 0; //复位SDIO配置 SDIO->DCTRL = 0x0; if (CardType == SDIO_HIGH_CAPACITY_SD_CARD) { BlockSize = 512; WriteAddr /= 512; } //----------------------在库的基础上添加的,没有此段将容易卡死在DMA检测中-------------------------- /*!< Send CMD16 设置块大小 */ SDIO_CmdInitStructure.SDIO_Argument = BlockSize; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN; 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_SET_BLOCKLEN); if (errorstatus != SD_OK) { return(errorstatus); } //---------------------------------------------------------------------------------------------- /*!< Send CMD24 WRITE_SINGLE_BLOCK */ SDIO_CmdInitStructure.SDIO_Argument = WriteAddr; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK; 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_WRITE_SINGLE_BLOCK); if (errorstatus != SD_OK) { return(errorstatus); } //配置SDIO写数据寄存器 SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT; SDIO_DataInitStructure.SDIO_DataLength = BlockSize; SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4; SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard; SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block; SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;//开启数据通道状态机 SDIO_DataConfig(&SDIO_DataInitStructure); SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);//开启数据传输结束中断 SD_DMA_TxConfig((uint32_t *)writebuff, BlockSize);//配置DMA,跟Rx类似 SDIO_DMACmd(ENABLE);//使能SDIO的DMA请求 return(errorstatus); }
/** * @brief This function waits until the SDIO DMA data transfer is finished. 写操作等待函数 * This function should be called after SDIO_WriteBlock() and * SDIO_WriteMultiBlocks() function to insure that all data sent by the * card are already transferred by the DMA controller. * @param None. * @retval SD_Error: SD Card Error code. */ SD_Error SD_WaitWriteOperation(void) { SD_Error errorstatus = SD_OK; while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK)) {} if (TransferError != SD_OK) { return(TransferError); } /*!< Clear all the static flags */ SDIO_ClearFlag(SDIO_STATIC_FLAGS); return(errorstatus); }数据读取操作,需要修改,关键,否则可能出错。
/** * @brief Allows to read one block from a specified address in a card. The Data * transfer can be managed by DMA mode or Polling mode. * @note This operation should be followed by two functions to check if the * DMA Controller and SD Card status. * - SD_ReadWaitOperation(): this function insure that the DMA * controller has finished all data transfer. * - SD_GetStatus(): to check that the SD Card has finished the * data transfer and it is ready for data. * @param readbuff: pointer to the buffer that will contain the received data * @param ReadAddr: Address from where data are to be read. * @param BlockSize: the SD card Data block size. The Block size should be 512. * @retval SD_Error: SD Card Error code. */ SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize) { SD_Error errorstatus = SD_OK; TransferError = SD_OK; TransferEnd = 0;//清除传输结束标志位,在中断服务中置一 StopCondition = 0; SDIO->DCTRL = 0x0; if (CardType == SDIO_HIGH_CAPACITY_SD_CARD) { BlockSize = 512; ReadAddr /= 512; } //----------------------在库的基础上添加的,没有此段将容易卡死在DMA检测中-------------------------- /*!< Send CMD16 设置块大小 */ SDIO_CmdInitStructure.SDIO_Argument = BlockSize; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN; 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_SET_BLOCKLEN); if (errorstatus != SD_OK) { return(errorstatus); } //---------------------------------------------------------------------------------------------- SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT; SDIO_DataInitStructure.SDIO_DataLength = BlockSize; SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4; SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO; SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block; SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable; SDIO_DataConfig(&SDIO_DataInitStructure); /*!< Send CMD17 READ_SINGLE_BLOCK */ SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr; SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK; 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_READ_SINGLE_BLOCK); if (errorstatus != SD_OK) { return(errorstatus); } SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE); SDIO_DMACmd(ENABLE); SD_DMA_RxConfig((uint32_t *)readbuff, BlockSize); return(errorstatus); }
/** * @brief This function waits until the SDIO DMA data transfer is finished. * This function should be called after SDIO_ReadMultiBlocks() function * to insure that all data sent by the card are already transferred by * the DMA controller. * @param None. * @retval SD_Error: SD Card Error code. */ SD_Error SD_WaitReadOperation(void) { SD_Error errorstatus = SD_OK; while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK)) {} if (TransferError != SD_OK) { return(TransferError); } return(errorstatus); }2.4 中断服务函数,此处需要提醒的是,这个中断处理函数的位置推荐也放在 bsp_sdio_sdcard.c文件中,因为这样的话可以防止忘记,Keil会自动地根据它的名称识别出它是一个中断函数,无论该函数位于哪个文件中,这个请放心。
//SDIO中断服务函数 void SDIO_IRQHandler() { //中断相关的处理 SD_ProcessIRQSrc(); }
/** * @brief Allows to process all the interrupts that are high. 由中断服务程序调用,负责中断相关的处理 * @param None * @retval SD_Error: SD Card Error code. */ SD_Error SD_ProcessIRQSrc(void) { if (StopCondition == 1)//发送读取/多块读写命令时置一 { SDIO->ARG = 0x0;//命令参数寄存器 SDIO->CMD = 0x44C;//命令寄存器 TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//开启命令状态机短响应 } else { TransferError = SD_OK; } SDIO_ClearITPendingBit(SDIO_IT_DATAEND);//清中断 SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE);//关闭SDIO中断使能 TransferEnd = 1; return(TransferError); }2.5 测试函数
#include "sdio_test.h" #include "stdio.h" #include "bsp_sdio_sdcard.h" #include "stm32f10x.h" #define BLOCK_SIZE 512 #define NUMBER_OF_BLOCKS 32 #define MULTI_BUFFER_SIZE (BLOCK_SIZE * NUMBER_OF_BLOCKS)//缓冲区大小 uint8_t Buffer_Block_Tx[BLOCK_SIZE]; uint8_t Buffer_Block_Rx[BLOCK_SIZE]; uint8_t Buffer_MultiBlock_Tx[MULTI_BUFFER_SIZE]; uint8_t Buffer_MultiBlock_Rx[MULTI_BUFFER_SIZE]; SD_Error Status; //测试状态 typedef enum { FAILED=0, PASSED=!FAILED } TestStatus; volatile TestStatus EraseStatus = FAILED, TransferStatus1 = FAILED, TransferStatus2 = FAILED; //声明外部变量 extern SD_CardInfo SDCardInfo; //是否擦除成功 TestStatus eBuffercmp(uint8_t* pBuffer,uint32_t BufferLength) { while(BufferLength--) { if((*pBuffer!=0xFF) && (*pBuffer!=0x00))//擦除后是0xff或0x00 { return FAILED; } pBuffer++; } return PASSED; } /* *函数名:Buffercmp *描述:比较两个缓冲区中的数据是否相等 *输入:-pBuffer1,-pBuffer2:要比较的缓冲区的指针*-BufferLength缓冲区长度*输出:-PASSED相等*-FAILED不等*/ TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2,uint32_t BufferLength) { while(BufferLength--) { if(*pBuffer1 != *pBuffer2) { return FAILED; } pBuffer1++; pBuffer2++; } return PASSED; } /* *函数名:Fill_Buffer *描述:在缓冲区中填写数据 *输入:-pBuffer要填充的缓冲区 * -BufferLength要填充的大小 * -Offset填在缓冲区的第一个值 *输出:无 */ void Fill_Buffer(uint8_t* pBuffer,uint32_t BufferLength,uint32_t Offset) { uint16_t index=0; /*Putin global buffer some values*/ for(index=0;index<BufferLength;index++) { pBuffer[index] = index + Offset; } } /* *函数名:SD_EraseTest *描述:擦除数据测试 *输入:无 *输出:无 */ void SD_EraseTest(void) { /*-------------------BlockErase------------------------------------------*/ if(Status==SD_OK) { Status=SD_Erase(0x00,(BLOCK_SIZE*NUMBER_OF_BLOCKS));//第一个参数为擦除起始地址,第二个参数为擦除结束地址 } if(Status==SD_OK) { //读取刚刚擦除的区域 Status=SD_ReadMultiBlocks(Buffer_MultiBlock_Rx,0x00,BLOCK_SIZE,NUMBER_OF_BLOCKS); /*Check if the Transfer is finished*/ Status=SD_WaitReadOperation();//循环查询dma传输是否结束 /*Wait until end of DMA transfer*/ while(SD_GetStatus()!=SD_TRANSFER_OK); } /*Check the correctness of erased blocks*/ if(Status==SD_OK) { //把擦除区域读出来对比 EraseStatus=eBuffercmp(Buffer_MultiBlock_Rx,MULTI_BUFFER_SIZE); } if(EraseStatus==PASSED) { printf("擦除测试成功!\r\n"); } else { printf("擦除测试失败!\r\n"); } } //单块读写测试 void SD_SingleBlockTest() { //向数组填充要写入的数据 Fill_Buffer(Buffer_Block_Tx, BLOCK_SIZE, 0x320F); if(Status == SD_OK) { //将BLOCK_SIZE个字节写入SD卡的0地址 Status = SD_WriteBlock(Buffer_Block_Tx, 0x00, BLOCK_SIZE); //检查传输 Status = SD_WaitWriteOperation(); while(SD_GetStatus() != SD_TRANSFER_OK); } if(Status == SD_OK) { //从SD卡的0地址读取512个字节的数据 Status = SD_ReadBlock(Buffer_Block_Rx, 0x00, BLOCK_SIZE); //检查传输 Status = SD_WaitWriteOperation(); while(SD_GetStatus() != SD_TRANSFER_OK); } //校验数据是否一致 if(Status == SD_OK) { TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, BLOCK_SIZE); } if(TransferStatus1 == PASSED) { printf("单块读写测试成功!\r\n"); } else { printf("单块读写测试失败!\r\n"); } } //SD卡测试函数 void SD_Test() { //SD卡使用SDIO中断及DMA中断接收数据,中断服务程序位于"bsp_sdio_sdcard.c"中 Status = SD_Init(); if(Status != SD_OK) { printf("SD卡初始化失败!请确保SD卡插在开发板上,或者换一张SD卡测试!\r\n"); } else { printf("SD卡初始化成功!\r\n"); printf("SD_cid:%d \r\n", (SDCardInfo.SD_cid.ManufacturerID)); printf("CardBlockSize:%d \r\n", SDCardInfo.CardBlockSize); printf("CardCapacity:%d \r\n", SDCardInfo.CardCapacity); } if(Status == SD_OK) { //擦除测试 SD_EraseTest(); //单块读写测试 SD_SingleBlockTest(); } }2 .6 重要提醒
因为检测SD卡是否正确插入的宏SD_PRESENT和SD_NOT_PRESENT是ST官方的原SD驱动程序以一个输入引脚电平判断SD卡是否插入的,但是我们硬件中没有使用到该引脚,所以我们应该把ST驱动中原来用来进行引脚检测部分的代码删除掉,但代码中,我们还是保留了宏SD_PRESENT和SD_NOT_PRESENT的定义。
用于代码检测的具体位置:
修改方案:
其实就是不做检查,利用初始化是否成功来代替检查过程,就可以了。
删除步骤:
/** * @brief Detect if SD card is correctly plugged in the memory slot. * @param None * @retval Return if SD is detected or not */ uint8_t SD_Detect(void) { __IO uint8_t status = SD_PRESENT; /*!< Check GPIO to detect SD */ if (GPIO_ReadInputDataBit(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != Bit_RESET) { status = SD_NOT_PRESENT; } return status; } //来自于测试文件中的外部全局变量 extern SD_Error Status; /** * @brief Returns the current card's state. * @param None * @retval SDCardState: SD Card Error or SD Card Current State. */ SDCardState SD_GetState(void) { uint32_t resp1 = 0; //原SD卡驱动程序以一个输入引脚的电平判断SD卡是否正确插入,由于我们硬件中没有使用该引脚,所以我们的程序需要在这里屏蔽掉SD驱动中原来引脚电平检测部分的代码 //if(SD_Detect()== SD_PRESENT) if(Status == SD_OK) { if (SD_SendStatus(&resp1) != SD_OK) { return SD_CARD_ERROR; } else { return (SDCardState)((resp1 >> 9) & 0x0F); } } else { return SD_CARD_ERROR; } }你可能会缺少的代码:
/**
* @brief Gets the cuurent sd card data transfer status.
* @param None
* @retval SDTransferState: Data Transfer state.
* This value can be:
* - SD_TRANSFER_OK: No data transfer is acting
* - SD_TRANSFER_BUSY: Data transfer is acting
*/
SDTransferState SD_GetStatus(void)
{
SDCardState cardstate = SD_CARD_TRANSFER;
cardstate = SD_GetState();
if (cardstate == SD_CARD_TRANSFER)
{
return(SD_TRANSFER_OK);
}
else if(cardstate == SD_CARD_ERROR)
{
return (SD_TRANSFER_ERROR);
}
else
{
return(SD_TRANSFER_BUSY);
}
}
就是这些,下一节将介绍向SD卡移植FatFs。