一 概述
在嵌入式小系统领域,SD卡存储是一个非常重要的功能。可从难度上,它又是非常难的。因为它涉及到两个大的功能点,一个是文件系统,这个难度非一般。另外一个是sd卡的底层驱动。涉及到的接口多,所以也是一个难度高的地方。两个混合在一起,非常容易出问题。笔者在这块花费了很多时间。也遇到了很多问题。这里需要做一个总结了。
二 源码解析
通过使用Cube来生成的sd卡读写源码,大概率是不成功的,这就要逐步的定位了,到底是哪儿出了问题呢?
面对纷繁复杂的局势,第一步就是要学会拆解,逐步击破。接下来,就让我们来逐步的分析一下。
步骤一,首先排查硬件十分ok?
这个很简单,写一个GPIO拉高拉低的源码,来逐个验证一下这些IO口是否都是通的。代码如下所示:

 
  while (1)
{
/* code */
check_state_on();
HAL_Delay(1000);
check_state_off();
HAL_Delay(1000);
mprintf("check pc12a loopback cnt is:%d \n\r",g_leds_cnt++); }
通过万用表来测量这些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定位,能做到化繁为简,这就成功了一半了。接下来,就是对知识点的掌握和对问题的深究能力了。

基于stm32H730的解决方案开发之SD卡的读写调试的更多相关文章

  1. Android入门开发之SD卡读写操作(转)

    SD卡的读写是我们在开发android 应用程序过程中最常见的操作.下面介绍SD卡的读写操作方式: 1. 获取SD卡的根目录 String  sdCardRoot = Environment.getE ...

  2. Android开发之SD卡上文件操作

    1. 得到存储设备的目录:/SDCARD(一般情况下) SDPATH=Environment.getExternalStorageDirectory()+"/"; 2. 判断SD卡 ...

  3. 基于xmpp openfire smack开发之Android客户端开发[3]

    在上两篇文章中,我们依次介绍openfire部署以及smack常用API的使用,这一节中我们着力介绍如何基于asmack开发一个Android的客户端,本篇的重点在实践,讲解和原理环节,大家可以参考前 ...

  4. 【译】如何在 Android 5.0 上获取 SD卡 的读写权限

    因为最近项目需要,涉及到 SD卡 的读写操作,然而申请 <!-- 读写权限 --> <uses-permission android:name="android.permi ...

  5. Asp.net Mvc模块化开发之“开启模块开发、调试的简单愉快之旅”

    整个世界林林种种,把所有的事情都划分为对立的两个面. 每个人都渴望的财富划分为富有和贫穷,身高被划分为高和矮,身材被划分为胖和瘦,等等. 我们总是感叹,有钱人的生活我不懂;有钱人又何尝能懂我们每天起早 ...

  6. SD卡spi读写流程

    SD卡spi读写流程 1.SD卡的命令格式: SD卡的指令由6字节(Byte)组成,如下: Byte1:0 1 x x x x x x(命令号,由指令标志定义,如CMD39为100111即16进制0x ...

  7. 快速解决设置Android 23.0以上版本对SD卡的读写权限无效的问题

    快速解决设置Android 23.0以上版本对SD卡的读写权限无效的问题 转 https://www.jb51.net/article/144939.htm 今天小编就为大家分享一篇快速解决设置And ...

  8. 基于tiny4412的Linux内核移植 -- SD卡驱动移植(五)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  9. 基础学习总结(三)--文本、SD卡数据读写

    简单的文本数据写入文件不需要权限,读写SD卡在4.0版本前需要写权限.在4.0后需要读写权限 布局: <LinearLayout xmlns:android="http://schem ...

  10. 使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)

    了解STM32F103ZET是高容量多管脚的芯片 了解SD读写线路图 了解SD的基地址 阅读STM32F10xx英文参考 SDIO那章,我们编写代码边看文档解析 建工程,打开包含所有包括外设库函数的样 ...

随机推荐

  1. JuiceFS v1.0 beta2 发布|进一步提升稳定性

    这是 JuiceFS v1.0 正式发布前的第二个 beta 版本,共有 16 位社区伙伴贡献了 150+ 次提交 .本次更新以 Bug 修复和稳定性提升为主,辅以大量的文档更新和测试用例优化,并带来 ...

  2. 零基础入门学习Java课堂笔记 ——day02

    流程控制 1.Scanner对象 来源:java.util.Scanner 我们可以通过Scanner类来获取用户的输入输出 用户交互Scanner 通过new关键词创建Scanner对象 Scann ...

  3. NC26253 小石的妹子

    题目链接 题目 题目描述 小石有 n 个妹子,每个妹子都有一个细心程度 \(a_i\)和一个热心程度 \(b_i\) , 小石想给她们一个重要程度 \(t_i\)​(重要程度为 1 表示最重要,重要程 ...

  4. Python中保存字典类型数据到文件

    三种方法: 1.在 Python 中使用 pickle 模块的 dump 函数将字典保存到文件中import pickle my_dict = { 'Apple': 4, 'Banana': 2, ' ...

  5. HTMLElement对象

    HTMLElement对象 任何HTML元素都继承于HTMLElement对象,一些元素直接实现这个接口,而另一些元素通过多层继承来实现它. 属性 从其父元素Element继承属性,并从Documen ...

  6. org.apache.http.client.ClientProtocolException: URI does not specify a valid host name:localhost:xxx

    今天部署应用的时候遇到的,总结一下我知道的有2个原因: 1.地址前要加http://  这就是标题报错的原因,他用的是localhost:xxx 2.地址本身拼错了也会报这个,例如地址:http:// ...

  7. F - Subarrays题解

    F - Subarrays 题意:给你一个序列,问这个序列里有多少个子串的和能被k整除. 思路:求前缀和,然后每个位置对k取模,模数相等的位置之间,是一个满足条件的字串. 因为求的是前缀和,所以取模后 ...

  8. SpringBoot整合aspectj实现面向切面编程(即AOP)

    前言 "面向切面编程",这样的名字并不是非常容易理解,且容易产生一些误导.但在实际业务中,AOP有着广泛的用途,比如日志记录,性能统计,安全控制,事务处理,异常处理等等. 举些栗子 ...

  9. VS2019 添加三方文件夹遇到的坑

    在开发新项目时需要用到一些三方 API,这些三方 API 没有生成 lib,所以我们在 VS 编译器中添加这些三方文件夹的头文件路径后 会出现 ERROR LNK2019 的错误提示,这些提示通常都是 ...

  10. 时序数据库timescaleDB安装

    一:前言相关 环境:Red Hat 8.3.1-5安装程序:PostgreSQL 14.1,TimescaleDB 2.5.1,cmake3.22.1PostgreSQL编译安装需要cmake3.4以 ...