对于FATFS文件系统的移植,正点原子已经有比较完整的移植视频和相应源码。因此关于FATFS内部诸如diskio.c、ff.c和ffconf.h等文件的功能和修改这里就不再详述。博主在这里主要共享一下自己debug的一些过程(主要是针对FR_NO_FILESYSTEM),希望能让网友们在移植过程中少走一些弯路。
首先,对于移植FATFS,必经的几个步骤我还是再这里概述一下。
1. 移植好官方提供的.c和.h文件。(详情参照正点原子)
2. 编写diskio.c的硬件连接部分代码。
3. 配置好ffconf.h内部的控制用宏变量。
4. 编写好内存管理部分。这一块最开始我也没多大注意,但是十分必要。最简单的表述就是,你要把一个数据块写到SD卡中,你先得在内存中开辟一块空间来存放这些数据才行。
其次,博主描述一下自己遇到的第一个BUG。在f_mount挂载SD卡的时候,通过串口观察该函数的FREASULT型返回值,观察到的始终都是error 13: FR_NO_FILESYSTEM。为了弄清楚这个BUG的来源,我们最好弄清楚SD卡的文件结构以及FATFS文件系统本身的工作原理。
SD卡的文件结构:
在f_mount之前,博主先在PC机上格式化SD卡为FAT32格式。对于SD卡等存储介质,我们需要了解,它一般都有两个地址,一个是物理地址一个是逻辑地址,逻辑地址往往是物理地址基础上加一个偏移量,即逻辑地址零 = 物理地址零 + 偏移。
考虑已格式化为FAT32格式后,在物理地址零处的最初是的512个字节是MBR(Master Boot Record),即主引导记录。其中的前446个字节为引导代码,我们这里不做详解。接下来的64个字节为分区表,其中16个字节为一组总共四组,每一个组都描述了一个分区,最后两个字节为固定的末尾签名,0x55、0xAA。
我们可以看到在0x01BE处开始到0x01CD处的16个字节指示了一个分区。对于这16个字节,我们所需要知道的主要是以该16个字节距起始处偏移0x08个字节处为起始的四个字节表示的是跳转量。即从这个物理地址的0扇区到达逻辑地址的0扇区的偏移量(单位为扇区)。这里我们可以看到这个偏移量为0x0000_00E3。
于是我们到这个偏移量的扇区去看,就可以看到我们逻辑扇区0处的数据。也就是DBR(Dos Boot Record)Dos引导记录。这一块的引导记录与我们FATFS文件系统能否正常工作密切相关。
第一张图是从物理扇区打开看到的效果,第二张图是从逻辑扇区打开看到的效果。具体这一块数据的详细解释将在下面与FATFS内部的文件系统结构体FATFS struct结合在一起解释。
除此之外我们还需要知道的两大块是。对于FAT32,它从DBR开始,间隔若干扇区后会有一个根目录(Directory),在该根目录后间隔若干扇区后会有一个存储数据起始处。知道这点后,我们就可以开启下面的讨论了。
FATFS工作原理
首先我们需要熟悉结构体FATFS,FIL和DIR的具体含义。在我的理解里,FATFS结构体记录的主要是和FATFS文件系统本身相关的一些参数;FIL主要记录的是和特定文件有关的参数;DIR记录的是和目录有关的参数。
下面我们重点讨论一下FATFS的内部变量的含义。
typedef struct {
BYTE fs_type; /* FAT sub-type (0:Not mounted) */
BYTE drv; /* Physical drive number */
BYTE csize; /* Sectors per cluster (1,2,4...128)簇的大小,一般一个簇 = 八个扇区;给文件分配簇的时候一次分配三个簇 */
BYTE n_fats; /* Number of FAT copies (1 or 2) */
BYTE wflag; /* win[] flag (b0:dirty) */
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
WORD id; /* File system mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
#if _MAX_SS != _MIN_SS
WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */
#endif
#if _FS_REENTRANT
_SYNC_t sobj; /* Identifier of sync object */
#endif
#if !_FS_READONLY
DWORD last_clust; /* Last allocated cluster 最后写入的一个文件的最后一个簇的簇地址,这里标志的是下一个文件写进来的时候应该写在哪个位置上*/
DWORD free_clust; /* Number of free clusters 这里标志的是该文件系统管理下还剩下多少个可用簇*/
#endif
#if _FS_RPATH
DWORD cdir; /* Current directory start cluster (0:root) */
#endif
DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */
DWORD fsize; /* Sectors per FAT 一个FAT分区共有多少个扇区*/