2024年1月6日发(作者:)

SD卡读写

(1)系统硬件

文件系统只是数据的组织格式的统称,不涉及到硬件,所以系统的硬件与上篇日志中相同,不作修改。

(2)文件系统结构和读写原理

带有文件系统的SD卡的内部结构一般如下表:

256MSD卡的文件系统结构

文件系统结构 说明 扇区起始号 占用扇区数 Partiton Boot Sector 分区记录扇区 0 1 Reserved Sectors 保留扇区 0 4 FAT1 文件分配表1 4 242 FAT2 文件分配表2 246 242 DIR(FDT) 文件根目录区 488 32 User Data 数据区 520

493560 SD卡的保留扇区中一般不要写入数据,否则可能破坏其文件系统结构,导致操作系统不能识别。

在FAT文件系统中,BPB(Bios ParameterBlock)是一个很重要的参数表,该表通常位于0扇区(保留扇区中的第一个扇区)中的12-36字节,它记录了分区中的一些重要数据如总扇区数、每扇区的字节数、每簇的扇区数、保留扇区数、FAT表占用扇区数等,我这里的256M的SD卡中的BPB表如下:

名称 占用字节数 内容 说明

BPB_BytesPerSec 2 0x0200 扇区大小为512字节 BPB_SecPerChus 1 0x08 每簇有8个扇区 BPB_RsvdSecCnt 2 0x0004 有4个保留扇区 BPB_NumFATs 1 0x02

有2个FAT表

BPB_RootEntCnt 2 0x0200 根目录中可有512个登记项 BPB_TotSec16 2

0x0000 为0表示总扇区数大于65536

BPB_MediaType 1 0xF8 磁盘介质为硬盘 BPB_FATSize16 2 0x00F2 每个FAT表占242个扇区 BPB_SecPerTrk 2 0x3F 每个磁道有63个扇区 BPB_NumHeads 2

0x00FF 磁头数为255

BPB_HiddSec 4 0x00000000 有0个隐藏扇区 BPB_TotSec32 4 0x00078A00 共有494080个扇区 保留扇区之后是文件分配表,FAT16文件系统有两份文件分配表(FAT),FAT的大小可以在BPB中查到。文件在磁盘中以簇为单位进行存储,同一个文件的数据也可能不连续地存储在几个簇上,FAT表就是记录文件簇与簇之间的连接信息的,这就是所谓的链式存储。FAT16系统以两个字节表示一簇(所以一个FAT16分区中最多存储2的16次方簇),起始2个字节固定为FFF8H、FFFFH,后面的FAT表中FFFF表示终止、0000H表示未使用、0XFFF7表示坏簇、0X0002-0XFFEF表示文件的下一簇。FAT12和FAT32文件系统中的FAT表与FAT16的不同之处就是分别用12位和32位表示一簇,通用的FAT表及其意义如下: 所表示的意义 FAT12代码 FAT16代码 FAT32代码 空簇 0x000 0x0000 0x00000000 文件的下一簇

0x002-0xFEF 0x0002-0xFFEF 0x00000002-0xFFFFFF0E 文件的最后一簇 0xFFF

0xFFFF 0xFFFFFF0F 坏簇 0xFF7 0xFFF7 0x00000001 FAT表之后是FDT表(也称为文件根目录区),主要记录分区中目录和文件的名称、属性、起始簇等信息,FDT表固定占用32个扇区,每个扇区可以容纳16个登记项,每个登记项占用32个字节。SD卡中的FDT表一个登记相的信息如下表:

偏字节名称 所表示的意义 移 数

文件的名称,首字节为0x00表示未使用, DIR_Name1 0 8

0xE5表示已删除,0x2E表示目录

DIR_Name2 8 3 扩展名

DIR_Attr 11 1 属性[1]

DIR_Reserv12 10 建立时间,校验等信息 ed

DIR_WrtTim22 2 最后写入时间 e

DIR_WrtDat24 2 最后写入日期 e

DIR_FstCluL26 2 起始簇的低16位 O

DIR_FileSize 28 4 文件的大小,目录的此项为0

[1]文件的属性中每bit对应的意义如下

7 6 5 4 3 2 1 0

保留 保留 存档 目录 卷标 系统 隐藏 只读

有了以上概念以后,文件的读取和写入就比较容易理解了,其中需要说明的是:在FDT中(或者说文件系统中)文件的位置描述是簇号,而SD卡的底层读写单位是扇区(512字节或更小),所以在读写数据时必须进行起始簇号和起始扇区号的转换,根据上面对文件系统结构的描述,该转换可用一个式子表示,即:起始扇区号=隐藏扇区数+保留扇区数+2*(FAT表占用扇区数)+根目录区所占扇区数+(起始簇号-2)*每簇的扇区数,有了这个式子,文件读写过程分别如下:

读文件:从FDT表得到文件的名称、扩展名、创建时间、大小和起始簇号等信息,再根据起始簇号计算文件的起始扇区号,读第一簇的内容,然后查询FAT表确认该簇是否是最后一簇,是的话就结束读取,否则从FAT表得到下一簇号,再重复上面的步骤读取,直到文件的最后一簇。

写文件:首先得到要写的文件的FDT登记项,根据文件大小计算文件所需簇数(也可以动态申请),然后在FAT表中申请空白簇并将包括起始簇的登记项写入FDT表,更新FAT表,最后在数据区对应的扇区写文件数据。

删除文件:删除文件不涉及对数据区的操作,只需在FDT表中将相应的登记项标记为删除,并在FAT表中将相应的簇号置为0x0000H(空簇)就可以了。

(3)系统软件

有了(2)中的原理之后,软件也就是把各个操作所需的步骤联系起来,所以如果原理清楚

了,软件部分没什么好讲的,我这里的宏定义及函数列表如下:

1 /*

2 * This head file is written to establish struct fo fat for sd

card.*

3 * It is designed to contain such struct and functions as BPB,FDT,

*

4 * and so on. *

5 * NOTE:befor run these functions,the sd_init() is required to run

*

6 * Author:lpj Version:0.1 *

7 */

8 #ifndef _SDFAT_H_

9 #define _SDFAT_H_

10

11 #include "sdcard.h"

12

13 /*define the struct of BPB*/

14 struct _bpb_{

15 UINT16 bytes_per_sec; /*BPB_BytesPerSec*/

16 UINT8 sec_per_chus; /*BPB_SecPerChus*/

17 UINT16 rsvd_sec_cnt; /*BPB_RsvdSecCnt*/

18 UINT8 num_fats; /*BPB_NumFATs*/

19 UINT16 root_ent_cnt; /*BPB_RootEntCnt*/

20 UINT16 tot_sec16; /*BPB_TotSec16*/

21 UINT8 media_type; /*BPB_MediaType*/

22 UINT16 fat_size16; /*BPB_FATSize16*/

23 UINT16 sec_per_trk; /*BPB_SecPerTrk*/

24 UINT16 num_heads; /*BPB_NumHeads*/

25 UINT32 hidd_secs; /*BPB_HiddSec*/

26 UINT32 tot_sec32; /*BPB_TotSec32*/

27 };

28 typedef struct _bpb_ BPB;

29

30 /*define the struct of FDT*/

31 struct _fdt_{

32 UINT8 main_name[8]; /*The main name of file or dir*/

33 UINT8 expa_name[3]; /*The expand name of file or dir*/

34 UINT8 attr; /*Attribute*/

35 UINT8 rsvd_data[10]; /*Reserved data*/

36 UINT16 wrt_time; /*The last update time*/

37 UINT16 wrt_date; /*The last update date*/

38 UINT16 fst_clu; /*The first cluster of file or dir*/

39 UINT32 file_size; /*File size*/

40 };

41 typedef struct _fdt_ FDT;

42

43 /*define Linked list of _fdt_*/

44 struct FdtNode{

45 FDT fdt_data;

46 struct FdtNode *next;

47 };

48 typedef struct FdtNode FDTNODE,*FDTLIST;

49

50 /*define macros for bpb constants, they are import for read *

51 *or write,For different SD card,it is neccessary to change *

52 *these macros */

53 #define RSVDSTA 0 /*start number of reserved sectors*/

54 #define RSVDSEC 4 /*number of reserved sectors*/

55 #define SECPCLU 8 /*number of sectors for a cluster*/

56 #define BYTPSEC 512 /*number of Bytes for a sector*/

57 #define FAT1STA 4 /*start number of FAT1 table*/

58 #define FAT1SEC 242 /*number of FAT1 table's sectors*/

59 #define FAT2STA 246 /*start number of FAT2 table*/

60 #define FAT2SEC 242 /*number of FAT2 table's sectors*/

61 #define FDTSTA 488 /*start number of FDT table*/

62 #define FDTSEC 32/*number of FDT table's sectors*/

63 #define HIDESEC 0 /*number of HIDE sectors*/

64 #define DATASTA 520 /*start number of USERDATA start*/

65 #define DATASEC 493560/*number of USERDATA's sectors*/

66

67 /*convert start_cluster to start_sectors, it is calculate by *

68 *the way:start sectors=(HIDESEC+RSVDSEC+FAT1SEC+FAT2SEC+ *

69 *FDTSEC+(start_cluster_number-2)*SECPCLU */

70 UINT32 get_sta_sec(UINT16 sta_clu);

71

72 /* get BPB info from 0 sector */

73 int get_bpb(BPB *bpb_data);

74

75 /* get FAT table form SD card */

76 int get_fat_table(UINT16 *tfat);

77

78 /* get FDT table form SD card */

79 int get_fdt_table(UINT8 *tfdt);

80

81 /* get dir and file info form FDT table */

82 int get_files_info(FDTLIST *sd_files, UINT8 *tfdt);

83

84 /* list the file and dir of root */

85 int disp_files_list(FDTLIST sd_files);

86

87 /* read a certain file form DATA sectors */

88 int read_file(FDT ffdt, UINT8 *fdata, UINT16 *fat);

89

90 /* update the FAT table in SD ,ref[] record which sector

91 * need update * if ref[N]==1 then the Nth sector will be updated

*/

92 int update_fat_table(UINT16 *fat, UINT8 *ref);

93

94 /* create a new file in SD card,which can read in PC */

95 int create_file(FDT ffdt, UINT8 *fdata, UINT16 *fat, UINT8 *fdt);

96

97 /* append 512 bytes of data to the certain file */

98 int append_file(FDT ffdt, UINT8 *adata, UINT16 *fat);

99

100 /* delete a file in SD card */

101 int delete_file(FDT ffdt, UINT16 *fat, UINT8 *fdt);

102

103 #endif/*_SDFAT_H_*/

有一点儿需要注意的是这些函数都是在SD卡初始化的基础上才能做的,所以主程序中一开始就要对SD卡进行初始化,OK,SD卡的读写设计到此为止,我这里除了create_file()和appen_file()两个函数还没有测试外,其他部分都没有问题。