一、读文件的流程读物理扇区0,得到引导扇(逻辑扇区0)的偏移地址。

读引导扇的内容,得到文件系统基本配置信息。

根据文件系统的基本配置信息计算FAT,FDT,数据簇的起始地址和大小。

根据要读的文件名搜索FDT表,找到要读文件的起始数据簇编号,大小。

根据文件的起始数据簇编号在FAT表中查找所有该文件占用的数据簇及数据簇访问的先后关系。

读取该文件的起始数据簇的内容,及下一数据簇内容(有需要时)。

二、读取物理扇区0

先读取SD卡的第一个扇区(512字节),即扇区0,然后该扇区最后部分的数据如下图所示

正常的话,该扇区最后两个字节如上图所示为55 AA,如果不是则证明是读错了,或者该SD没有被格式化。

该扇区有两个重要信息:

一、 在0x1ca开始的四个字节9f c9 03 00,即0x3c99f=248223,代表该SD卡友248223个扇区,因为每个扇区有512字节,所以该SD卡容量为248223*512/1024/1024=121.2MByte

二、在0x1c6开始的四个字节61 00 00 00,即0x61=97,它表示引导扇区在扇区97。那我们就接着读扇区97,获取SD卡里更详细的信息,这个扇区0就可以不用管了。

三、读取引导扇区

以下是扇区97前64Byte的内容。因为97*512=0xc200,所以可以下图左边的偏移地址为c200

首先第0x3到0xA的内容为ASCII码的“MSDOS5.0”,不是重要信息

第0xb开始的两个字节00 02,即0x2000=512,代表每个扇区(sector)有512个字节(byte)

接下来的04代表每个簇(cluster)有4个扇区

接下来的04 00 即0x4代表有4-1个保留扇区,即是第一个FAT表所在扇区为引导扇区(97)+4=扇区101

接下来的02 代表有两个FAT表

接下来的00 02,即0x2000=512,代表FDT(目录区)有512登记项

第0x16开始的两个字节f2 00,即0xf2=242,代表每个FAT表占242个扇区

第0x36到0x3d代表的就是“FAT16   ”的ASCII码,说明这SD卡是FAT16的格式

四、FAT16文件系统的结构

知道以上的信息之后我们就可以根据以上信息计算出FAT1,FAT2,FDT和数据簇的首地址和结束地址,但在计算之前,我有必要介绍一下整个FAT16文件系统的结构和各个区的含义与作用。

五、获取文件系统基本配置信息

现在既然已经大概了解了引导扇,FAT1,FAT2,FDT和数据簇的作用,接下来就说一下怎么计算它们的起始地址和结束地址。我们用扇区来作为地址单位。

我们首先定义两个结构体

typedef struct

{

U16 BytesPerSector;          //每个扇区多少字节

U8  SectorsPerCluster;         //每个簇有多少个扇区

U16 ReserveSectors;          //保留扇区数

U8  FatTableNums;           //有多少个FAT表

U16 RootDirRegNums;      //根目录允许的登记项数目

U16 SectorsPerFat;                //每个FAT表有多少个扇区

U32 SectorNums;               //总的扇区数

U8   FileType[7];              //文件系统类型

}FAT_PARA;

typedef struct

{

U32 Logic;  //引导扇(逻辑扇区0)对物理0扇区里的偏移地址

U32 FAT1;

U32 FAT2;

U32 FDT;

U32 Cluster;  //数据簇的偏移地址

}FAT_OFFSET;

FAT_PARA      SD_para;   //声明两个结构体变量

FAT_OFFSET   SD_offset;

由之前的知识可以知道,我们首先从物理扇区0知道SD_offset.Logic的地址,然后就可以找到引导扇.然后再在引导扇里找到SD_para里面所有变量的值。

U8  buffer[512];

Read_Single_Block(0, buffer);//把物理扇区0的512个字节的数据读到buffer里

SD_offset.Logic = (* (U16 *) (buffer + 0x1c6)) + ((* (U16 *) (buffer + 0x1c8)) << 16); //得到引导扇的偏移地址

Read_Single_Block(SD_offset.Logic, buffer) ; //读引导扇的数据

//获取参数,以便计算各个区的偏移地址

SD_para.BytesPerSector = (* (U8 *) (buffer + 0xb)) + ((* (U8 *) (buffer + 0xc)) << 8);

SD_para.SectorsPerCluster = * (buffer + 0x0d);

SD_para.ReserveSectors = * (U16 *) (buffer + 0x0e);

SD_para.FatTableNums = * (buffer + 0x10);

SD_para.RootDirRegNums =  (* (U8 *) (buffer + 0x11)) + ((* (U8 *) (buffer + 0x12)) << 8);

SD_para.SectorsPerFat = * (U16 *) (buffer + 0x16);

SD_para.SectorNums = * (U32 *) (buffer + 0x20);

for(i = 0; i < 6; i++)

SD_para.FileType[i] = *(buffer+0x36+i);

SD_para.FileType[6] = 0;

六、计算各重要区域的大小与起始地址

//计算各个区的偏移地址

//FAT1地址=引导扇地址+保留扇区数,大小为SD_para.SectorsPerFat

SD_offset.FAT1 = SD_offset.Logic + SD_para.ReserveSectors;

//如果存在两个FAT表,一般不是1就是2

if (SD_para.FatTableNums == 2)

//FAT2地址=FAT1地址+ SD_para.SectorsPerFat

SD_offset.FAT2 = SD_offset.FAT1 + SD_para.SectorsPerFat;

else SD_offset.FAT2 = 0;

//FDT地址=FAT1+FAT表数*FAT表大小

SD_offset.FDT=SD_offset.FAT1+SD_para.FatTableNums*SD_para.SectorsPerFat;

//因为数据簇2紧跟在FDT后,所以数据簇0易求得

SD_offset.Cluster = SD_offset.FDT + 32 - 2 * SD_para.SectorsPerCluster;

七、FDT与FAT表的简单介绍

读取文件之前要先详细了解一下FDT,和FAT表的内容

一个FDT表占32个扇区,共有512个文件登记信息,所以每个文件登记信息的大小为32*512/512=32Byte

每个文件登记信息如下图所示

对于我们来说,这个文件记录信息最重要的就是最后六个字节

最后四个字节代表文件大小,由文件大小可以推算出该文件占用多少个数据簇

第0x1a到0x1b个字节道标文件开始的首簇号,知道文件的首簇号我们就可以查看FAT表的相应信息,就可得到该文件所占用的所有数据簇的簇号。

以下是FAT表的结构

上表中,06、07单元映射了磁盘3号簇区。有之前的介绍中可以知道,我这张SD卡1个簇包含4个扇区。也就是说在写数据时,只有写完了3号簇的4个扇后,将FAT表的06,07单元填写04,00;才可继续在04号簇上写数据。如果数据写完后还没有写满3号簇,则在FAT表的06,07单元填写FF,FF.

也就是说在FAT表中记录着每个数据簇的状态,且每个数据簇的状态占用两个字节。如果这两个字节等于0xffff,则代表该数据簇以被占用,且文件在该数据簇中结束。如果这两个字节等于0x0001~0xfffe,则代表该数据簇已被占用,且该文件没有结束,而该文件存放的下一数据簇的簇号就等于这两个字节的大小。

八、读取一个文件

下面以我的SD卡为例子,向大家介绍读写SD的FAT文件系统的文件(最好先安装一个叫做winhex的软件)。

首先我的SD卡存放着这样一个文件

要打开我这个名为lqz.txt的文件的,我们先查找FDT表中关于lqz.txt这个文件的登记信息。

因为之前已经知道了FDT的首地址是第585扇区,我们来到585扇区,开始搜索LQZ.TXT(必须先转换成大写字母)

最后在地址为0x4a310(也就是第0x4a310/512=593扇区)的地方搜索到LQZ.TXT的文件登记信息,在最后四个字节得知该文件大小为0x00002c89=11401Byte,占用11401/512/4=6个数据簇,从倒数第5,6个字节可以知道文件的首簇号为0x2fe4,然后在FAT表根据文件的首簇号查找接下来文件占用的五个数据簇簇号,数据簇0x2fe4在FAT的登记位置=FAT地址+0xfe4*2=0xca00+0x2fe4*2=0x129c8,我们来到0x129c8这个地址

数据簇0x2fe4的信息就存放在0x129c8,和0x129c9这个字节里,从上图可以看出这两个字节等于0x46F4,也就是说LQZ.TXT存放的下一个数据簇的簇号为0x46F4,由于该文件占用6个数据簇,所以我们必须继续查找剩下的4个数据簇的簇号。我们继续查找簇号为0x46f4的数据簇在FAT表的信息,地址为0xca00+0x46f4*2=0x157e8

由上图可以下一数据簇的簇号为0x46f5。然后按照上诉方法查找剩余三个簇号分别为0x46f6,0x46f7,0x46f8,最后在0x46f8对应的地方存放着0xffff,代表文件到此结束。

经过上诉步骤我们知道我lqz.txt文件依次存放在0x2fe4,0x46f4,0x46f5,0x46f6,0x46f7,0x46f8这6个数据簇,接下来我们就读取这6个数据簇的内容即可。

比如说:数据簇0x2fe4的地址=数据簇0地址+0x2fe4*4*512=0x4c200+0x2fe4*4*512=0x183e200,0x183e200/512=49649,也就是在物理扇区49649~49652这个四个扇区都是数据簇0x2fe4的内容

android 读写sd文件系统,SD卡FAT16文件系统的学习笔记相关推荐

  1. Android深度探索(卷1)HAL与驱动开发学习笔记(8)

    Android深度探索(卷1)HAL与驱动开发学习笔记(8) 第八章 蜂鸣器驱动   L i n u x驱动的代码重用有很多种方法.可以采用标准C程序的方式.将要重用的代码放在其他的文件(在头文件中声 ...

  2. android中资源文件的两种访问方式,Android_Android学习笔记-保存文件(Saving Files),Android设备有两种文件存储区域 - phpStudy...

    Android学习笔记-保存文件(Saving Files) Android设备有两种文件存储区域: 内部存储和外部存储 ("internal" and "externa ...

  3. segy地震数据的读取python_Python-segy格式地震资料segyio读写包说明(二),pythonsegy,数据,学习,笔记...

    python segy格式地震数据读写包segyio学习笔记(二) 最近大致搞明白了segyio读取叠后和叠前segy数据的方法,以及内部存储结构,以两段代码为例: 叠后数据读取.这是一个从给定时窗内 ...

  4. Android入门到精通|安卓/Android开发零基础系列Ⅱ【职坐标】-学习笔记(1)-- 常用控件及资源介绍

    前言 为了巩固Android基础知识,回顾一下学习内容,才有此学习笔记. IDE Androdi Studio 4 + Genymotion 创建项目 修改项目的 build.gradle,添加国内镜 ...

  5. 31道Android面试题,字节跳动移动架构师学习笔记,大厂直通车!

    关于Android的近况 大家都知道,今年移动开发不那么火热了,完全没有了前两年Android开发那种火热的势头,如此同时,AI热火朝天,很多言论都说Android不行了.其实不光是Android,i ...

  6. xamarin android 标签,安卓端Tabbedpage调整在底部位置和标签及取消Android API28 以下的点击特效—-xamarin.forms学习笔记(一)...

    使用tabbedpage时将安卓端导航放在底部,官网也有说明方法.总结: xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x ...

  7. android 三方_面试官送你一份Android热门三方库源码面试宝典及学习笔记

    前言 众所周知,优秀源码的阅读与理解是最能提升自身功力的途径,如果想要成为一名优秀的Android工程师,那么Android中优秀三方库源码的分析和理解则是必备技能.就拿比较热门的图片加载框架Glid ...

  8. sklearn_逻辑回归制作评分卡_菜菜视频学习笔记

    逻辑回归制作评分卡 3.0 前言 逻辑回归与线性回归的关系 消除特征间的多重共线性 为什么使用逻辑回归处理金融领域数据 正则化的选择 特征选择的方法 分箱的作用 3.1导库 3.2数据预处理 3.2. ...

  9. android java与界面的关联_Android Studio安卓学习笔记(三)Android用户界面的设计布局与组件(一)用户界面布局设计(1)...

    当我们创建了一个安卓项目后,我们会发现真正建立一个完善的安卓项目并不是想象的那么容易.其实和设计GUI可视化界面一样,开发安卓也需要考虑很多方面,主要考虑的还是界面布局和需要的组件. 一:Androi ...

最新文章

  1. CentOS 7 搭建docker仓库
  2. 解开一个困扰自己多时的小问题
  3. 测试u盘信息软件,U盘检测器
  4. Python怎么安装第三方库-numpy-libnum等; (详细版)
  5. 基于'sessionStorage'与'userData'的类session存储
  6. 注意.NET Core进行请求转发问题
  7. warning: format not a string literal and no format arguments
  8. 使用Python从PDF文件中提取数据
  9. zuul zuul2 性能_我们学习如何构建Zuul CI / CD云
  10. Nginx开启gzip压缩功能
  11. 激光SLAM之Cartographer源码解析视频课程
  12. python控制蓝牙pybluez_Python之蓝牙通讯模块pybluez学习笔记
  13. 一个像素的旅行,卷积网络可视化项目火了:点点鼠标就能看懂的扫盲神器
  14. Paper reading (九十):Can Gut Microbiota Composition Predict Response to Dietary Treatments
  15. 南通万豪酒店开业;诺瓦瓦克斯任命新任总裁兼CEO | 美通企业日报
  16. Qt 之 事件总线模型
  17. SIGHUP信号相关
  18. android 计步器 开发,Android计步器开发
  19. GraphQL学习第三篇 -在Express中使用GraphQL
  20. 1.2.5 层次模型

热门文章

  1. Ant Design of Vue +TS 表单动态增加数据验证卧坑姿势
  2. 定时开关机实现原理-Android4.4/6.0
  3. android 高德卫星地图数据,白马地图 Bmap for Android v7.3.81 强大高德百度地图应用|张小北...
  4. springboot+微信小程序“微印象”在线打印预约系统的设计与实现毕业设计源码061642
  5. 史上最详细的新浪广告系统技术架构优化历程
  6. C++基础入门丨1. 初识C++像极了C语言
  7. RSS/RPS/RFS究竟是个什么东西
  8. 2021第十二届蓝桥杯大赛软件赛省赛C++ C组真题题解
  9. pmp中ram和raci的区别_信息系统项目管理师和PMP考试考哪个?
  10. jquery:toggle()方法模拟鼠标连续click事件