一 概述
在嵌入式小系统领域,SD卡存储是一个非常重要的功能。可从难度上,它又是非常难的。因为它涉及到两个大的功能点,一个是文件系统,这个难度非一般。另外一个是sd卡的底层驱动。涉及到的接口多,所以也是一个难度高的地方。两个混合在一起,非常容易出问题。笔者在这块花费了很多时间。也遇到了很多问题。这里需要做一个总结了。
二 源码解析
通过使用Cube来生成的sd卡读写源码,大概率是不成功的,这就要逐步的定位了,到底是哪儿出了问题呢?
面对纷繁复杂的局势,第一步就是要学会拆解,逐步击破。接下来,就让我们来逐步的分析一下。
步骤一,首先排查硬件十分ok?
这个很简单,写一个GPIO拉高拉低的源码,来逐个验证一下这些IO口是否都是通的。代码如下所示:
通过万用表来测量这些IO,假如是不通的,那就要检查一下IO硬件了。
步骤二,接下来,就要抛开文件系统,来查看一下sd卡是否能识别了。
这部分源码比较复杂,需要自己整理的,这里给出笔者整理出来的源码。
#define SD_TIMEOUT ((uint32_t)0x00100000U) //等待时间 #define BLOCK_SIZE 512 //块的数目 #define NUMBER_OF_BLOCKS 50 //块的数据大小 #define MULTI_BUFFER_SIZE (BLOCK_SIZE * NUMBER_OF_BLOCKS) /** * @brief 数组匹配检测函数 * @param pBuffer1:发送数组;pBuffer2:接受数组;BufferLength:数组长度 * @retval HAL_OK:匹配;HAL_ERROR:不匹配 */ static HAL_StatusTypeDef Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength) { while (BufferLength--) { if (*pBuffer1 != *pBuffer2) { return HAL_ERROR; } else{ pBuffer1++; pBuffer2++; } } return HAL_OK; } uint8_t Buffer_Block_Tx[512]={0};//写入数组 uint8_t Buffer_Block_Rx[512];//读取数组 uint8_t SD_save_ok=0; uint32_t sd_status_one = 0; uint32_t time_sd_1=0; uint32_t time_sd_2=0; uint16_t sd_test_ii=0; void SD_SingleBlockTest_easy(void) { for(sd_test_ii=0;sd_test_ii<512;sd_test_ii++) { //对写入数组进行赋值 Buffer_Block_Tx[sd_test_ii]=sd_test_ii%216; } sd_status_one =HAL_SD_WriteBlocks(&hsd1,(uint8_t *)Buffer_Block_Tx,0,1,0xfff); //将写入数组写入SD卡中,0表示写入地址为0,1表示为写入1个扇区的数据 if(sd_status_one == HAL_OK) { mprintf("write success \r\n"); } else { mprintf("write failed status is:%d \r\n",sd_status_one); } for(sd_test_ii=0;sd_test_ii<512;sd_test_ii++) { //对读取数组赋值 Buffer_Block_Rx[sd_test_ii]=0; } sd_status_one =HAL_SD_ReadBlocks(&hsd1,(uint8_t *)Buffer_Block_Rx,0,1,0xfff);; //读取SD卡数据,将数据存在读取数组中 sd_status_one=Buffercmp(Buffer_Block_Tx,Buffer_Block_Rx,512); //写入数组和读取数组进行对比 if(sd_status_one == HAL_OK) { mprintf("SD test ok!!\r\n"); } else { mprintf("SD_test fail!\r\n " ); } } /** * @brief SD卡等待擦除完成函数 * @param 无 * @retval HAL_OK:擦除成功;HAL_ERROR:擦除失败 */ static HAL_StatusTypeDef Wait_SDCARD_Ready(void) { uint32_t loop = SD_TIMEOUT; while(loop > 0) { loop--; if(HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_TRANSFER) { return HAL_OK; } } return HAL_ERROR; } /* USER CODE BEGIN 1 */ /** * @brief SD卡擦除测试 * @param 无 * @retval 无 */ static void SD_EraseTest(void) { HAL_StatusTypeDef Status = HAL_OK; HAL_StatusTypeDef EraseStatus = HAL_OK; if (Status == HAL_OK) { Status = HAL_SD_Erase(&hsd1, 0x00, (BLOCK_SIZE * NUMBER_OF_BLOCKS));// SD卡外设句柄、擦除的起始地址、擦除的结束地址 //等待擦除完成 if(Wait_SDCARD_Ready() != HAL_OK) { EraseStatus = HAL_ERROR; } } if(EraseStatus == HAL_OK) { mprintf("SD card efuse success \r\n"); } else { mprintf("SD card efuse failed \n"); } } /** * @brief SDMMC1 Initialization Function * @param None * @retval None */ static void MX_SDMMC1_SD_Init(void) { /* USER CODE BEGIN SDMMC1_Init 0 */ /* USER CODE END SDMMC1_Init 0 */ /* USER CODE BEGIN SDMMC1_Init 1 */ /* USER CODE END SDMMC1_Init 1 */ hsd1.Instance = SDMMC1; hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B; hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; hsd1.Init.ClockDiv = 4; /* USER CODE BEGIN SDMMC1_Init 2 */ if (HAL_SD_Init(&hsd1) != HAL_OK) { Error_Handler(); } if(HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_TRANSFER) { mprintf("SD card init ok!\r\n\r\n"); SD_EraseTest(); //SD卡擦除测试 SD_SingleBlockTest_easy();//SD卡读写测试 } else { mprintf("SD card init fail!\r\n" ); } /* USER CODE END SDMMC1_Init 2 */ }
这部分是来检查sd卡能否读写的,绕过文件系统的。
通过测试,笔者发现是可以正常读写的,如下所示:
步骤三,接下来就要剑指文件系统了。肯定是这哪儿出了问题,具体怎么定位呢?由于文件系统比较复杂,接下来一章节继续分析吧。
三 总结
做bug定位,能做到化繁为简,这就成功了一半了。接下来,就是对知识点的掌握和对问题的深究能力了。
作者:虚生 出处:https://www.cnblogs.com/dylancao/ 以音频和传感器算法为核心的智能可穿戴产品解决方案提供商 ,提供可穿戴智能软硬件解决方案的设计,开发和咨询服务。 勾搭热线:邮箱:1173496664@qq.com weixin:18019245820 市场技术对接群:347609188 |