说说FATFS文件系统(转)

说说FATFS文件系统(转)

FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块。FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开。因此,它独立(不依赖)于硬件架构。它可以被嵌入到低成本的微控制器中,如AVR, 8051, PIC, ARM, Z80, 68K 等等,而不需要做任何修改。 

 

FatFS文件系统包含了文件

 

ff.h         :文件系统实现头文件,定义有文件系统所需的数据结构

diskio.h  :底层驱动头文件,就一些状态宏的定义和底层驱动函数的申明

integer.h:仅实现数据类型重定义,增加系统的可移植性

ffconf.h :文件系统配置

ff.c  :文件系统实现。

diskio.c 底层驱动

 

FatFs 提供下面的函数API:

f_mount - 注册/注销一个工作区域(Work Area)

f_open - 打开/创建一个文件f_close - 关闭一个文件

f_read - 读文件f_write - 写文件

f_lseek - 移动文件读/写指针

f_truncate - 截断文件

f_sync - 冲洗缓冲数据 Flush Cached Data

f_opendir - 打开一个目录

f_readdir - 读取目录条目

f_getfree - 获取空闲簇 Get Free Clusters

f_stat - 获取文件状态

f_mkdir - 创建一个目录

f_unlink - 删除一个文件或目录

f_chmod - 改变属性(Attribute)

f_utime - 改变时间戳(Timestamp)

f_rename - 重命名/移动一个文件或文件夹

f_mkfs - 在驱动器上创建一个文件系统

f_forward - 直接转移文件数据到一个数据流 Forward file data to the stream directly

f_gets - 读一个字符串

f_putc - 写一个字符

f_puts - 写一个字符串

f_printf - 写一个格式化的字符磁盘I/O接口

f_tell - 获取当前读/写指针

f_eof - 测试一个文件是否到达文件末尾

f_size - 获取一个文件大小

f_error - 测试一个文件是否出错

 

因为FatFs模块完全与磁盘I/O层分开,因此需要下面的函数来实现底层物理磁盘的读写与获取当前时间。底层磁盘I/O模块并不是FatFs的一部分,并且必须由用户提供。

disk_initialize - Initialize disk drive 初始化磁盘驱动器

disk_status - Get disk status 获取磁盘状态

disk_read - Read sector(s) 读扇区

disk_write - Write sector(s) 写扇区

disk_ioctl - Control device dependent features 设备相关的控制特性

get_fattime - Get current time 获取当前时间

FatFS系统特性

打开文件数量:无限制,与可用内存有关。 卷(volume)数量:最多10个。 

文件大小:与FAT规范有关(最大4G-1字节)。 

卷大小:与FAT规范有关(在512字节/扇区上,最大2T字节) 

簇(Cluster)大小:与FAT规范有关(在512字节/扇区上,最大64K字节) 扇区(Sector)大小:与FAT规范有关(最大4K字节)

创建文件并读写的使用例程

1)f_mount(0, &fatFS);                                       

2)f_mkfs(0,1,512);                 //创建文件系统

FRESULT f_mkfs (
 BYTE  Drive,            
 BYTE  PartitioningRule, 
 WORD  AllocSize         );

 

分区规则:当给定0时,首先在驱动器上的第一个扇区创建一个分区表,然后文件系统被创建在分区上。这被称为FDISK格式化,用于硬盘和存储卡。当给定1时,文件系统从第一个扇区开始创建,而没有分区表。这被称为超级软盘(SFD)格式化,用于软盘和可移动磁盘。

3)f_getfree("0:", &fre_clust, &fs2)   //获得磁盘存储空间大小 

4)f_open(&file1, "/srcfile.txt",  FA_OPEN_ALWAYS | FA_READ | FA_WRITE);  //打开文件

FIL     file1;   

5)f_puts(cDataBuf, &file1);    //将cDataBuf[]数据写入 文件srcfile.txt

6)f_sync (&file1);   //刷新文件

7)f_lseek(&file1, 0);   //从文件 偏移字节0出开始读文件

8)f_read(&file1, buffer, 10,&r);     //读出10个字节到 buffer[]中

9)f_close(&file1);   //操作完成  关闭文件

 

创建目录例程

1) f_chdir("0:");   //切换到根目录

2)f_mkdir("folder");   //创建目录 folder

3)f_open(&file1, "folder/oldname2.txt", FA_CREATE_ALWAYS | FA_WRITE);  //打开folder目录下的文件

4)f_close(&file1);    //关闭文件

0
0
« 上一篇: 以操作系统的角度述说线程与进程(转)以及区别及联系
» 下一篇: C语言100个经典的算法之兔子总数计算内含printf的用法
	<p class="postfoot">
		posted on <span id="post-date">2018-02-22 15:52</span> <a href="https://www.cnblogs.com/zhugeanran/">张凌001</a> 阅读(<span id="post_view_count">776</span>) 评论(<span id="post_comment_count">0</span>)  <a href="https://i.cnblogs.com/EditPosts.aspx?postid=8459150" rel="nofollow">编辑</a> <a href="#" onclick="AddToWz(8459150);return false;">收藏</a>
	</p>
</div>
<script type="text/javascript">var allowComments=true,cb_blogId=406829,cb_entryId=8459150,cb_blogApp=currentBlogApp,cb_blogUserGuid='7d0073df-8601-4e21-2355-08d54dbac325',cb_entryCreatedDate='2018/2/22 15:52:00';loadViewCount(cb_entryId);var cb_postType=1;</script>

</div><a name="!comments"></a><div id="blog-comments-placeholder"></div><script type="text/javascript">var commentManager = new blogCommentManager();commentManager.renderComments(0);</script>
<a id="Footer1_Hyperlink3" name="Hyperlink1" href="https://www.cnblogs.com/" style="font-family:Verdana;font-size:12px;">博客园</a>
<br>
Copyright © 张凌001

<think>嗯,用户问的是关于使用FATFS读取文件夹下的文件。我需要先回顾一下FATFS的基本知识,确保自己理解正确。FATFS是一个用于嵌入式系统的文件系统模块,支持FAT12、FAT16、FAT32等格式。用户可能是在开发嵌入式系统,比如STM32或者其他微控制器,需要访问SD卡或者Flash中的文件。 首先,用户可能需要知道如何初始化FATFS,挂载文件系统,打开目录,然后遍历其中的文件。常见的步骤包括使用f_opendir,f_readdir等函数。不过,我需要确认这些函数的具体用法和可能的参数。 接下来,用户可能遇到的一些常见问题,比如目录打开失败,文件读取错误,或者内存不足的问题。这时候需要给出一些调试建议,比如检查返回值,确认路径是否正确,是否有足够的堆内存等。 另外,用户可能对性能优化感兴趣,比如如何高效地遍历大量文件,或者如何处理长文件名。FATFS的配置选项,比如启用LFN(长文件名支持)可能需要更多的内存,这也是需要注意的地方。 还有,不同版本的FATFS可能有不同的函数名称或参数,需要指出参考具体的文档版本。比如,有些版本使用FRESULT类型作为返回值,需要检查每个调用的结果是否正确。 可能用户的应用场景是读取传感器数据存储的文件,或者进行固件升级,所以示例代码需要简洁明了,展示从挂载到读取目录的全过程。同时,提醒关闭目录和卸载文件系统的重要性,避免资源泄漏。 此外,考虑用户可能的开发环境,比如使用STM32CubeIDE或者Arduino,可能需要包含特定的头文件,链接正确的库。但为了通用性,示例代码应该保持平台无关,专注于FATFS本身的API调用。 最后,应该总结常见问题,比如路径格式是否正确,是否处理了隐藏文件或系统文件,以及如何处理中文文件名等。这些细节在实际开发中容易出错,需要特别强调。</think>以下是通过FATFS文件系统读取文件夹下文件的完整技术指南: --- ### 一、FATFS基础配置 #### 1. 系统初始化流程 ```c FATFS fs; // 文件系统对象 FILINFO fno; // 文件信息结构体 DIR dir; // 目录对象 // 挂载文件系统 FRESULT res = f_mount(&fs, "0:", 1); // "0:"表示物理驱动器号 if (res != FR_OK) { printf("挂载失败! 错误码: %d\n", res); return; } ``` #### 2. 关键数据结构 ```c typedef struct { DWORD fsize; // 文件大小 WORD fdate; // 修改日期 WORD ftime; // 修改时间 BYTE fattrib; // 文件属性 TCHAR fname[FF_LFN_BUF + 1]; // 文件名 } FILINFO; ``` --- ### 二、目录遍历实现 #### 1. 基础遍历算法 ```c void list_files(char* path) { FRESULT res = f_opendir(&dir, path); if (res != FR_OK) return; for(;;) { res = f_readdir(&dir, &fno); if (res != FR_OK || fno.fname[0] == 0) break; // 过滤系统文件 if (!(fno.fattrib & AM_HID)) { printf("%s/%s\n", path, fno.fname); } } f_closedir(&dir); } ``` #### 2. 递归遍历实现 ```c void scan_directory(char* path) { list_files(path); // 先处理当前目录 FRESULT res = f_opendir(&dir, path); while(f_readdir(&dir, &fno) == FR_OK && fno.fname[0]) { if (fno.fattrib & AM_DIR) { // 如果是子目录 char new_path[256]; sprintf(new_path, "%s/%s", path, fno.fname); scan_directory(new_path); // 递归调用 } } f_closedir(&dir); } ``` --- ### 三、关键API详解 | 函数 | 参数说明 | 返回值 | 典型应用场景 | |------|----------|--------|--------------| | `f_opendir` | DIR*, const TCHAR* | FRESULT | 打开目录 | | `f_readdir` | DIR*, FILINFO* | FRESULT | 读取目录项 | | `f_closedir` | DIR* | FRESULT | 关闭目录 | | `f_stat` | const TCHAR*, FILINFO* | FRESULT | 获取文件信息 | --- ### 四、性能优化策略 #### 1. 缓存配置优化 ```c // ffconf.h关键配置项 #define FF_USE_LFN 2 // 启用长文件(0:禁用, 1:静态缓冲, 2:堆分配) #define FF_LFN_UNICODE 1 // 使用Unicode编码 #define FF_MAX_LFN 255 // 最大文件名长度 #define FF_FS_EXFAT 1 // 启用exFAT支持 ``` #### 2. 内存管理技巧 ```c // 自定义内存分配函数(应对FF_USE_LFN=2) void* ff_memalloc(UINT size) { return malloc(size); } void ff_memfree(void* mblock) { free(mblock); } ``` --- ### 五、典型问题解决方案 #### 问题1:中文乱码 **解决方法**: 1. 设置正确编码格式: ```c #define FF_CODE_PAGE 936 // 简体中文代码页 ``` 2. 确保终端支持UTF-8输出 #### 问题2:长路径处理 **安全写法**: ```c char path[FF_MAX_LFN * 2]; // 双倍缓冲 snprintf(path, sizeof(path), "0:/documents/reports"); ``` #### 问题3:多线程访问 ```c // 使用信号量进行资源保护 osSemaphoreId fs_sem; void read_thread() { osSemaphoreWait(fs_sem, osWaitForever); f_opendir(...); // ...操作... osSemaphoreRelease(fs_sem); } ``` --- ### 六、高级应用场景 #### 1. 文件过滤搜索 ```c void find_files(const char* ext) { while(f_readdir(&dir, &fno) == FR_OK) { if(strstr(fno.fname, ext)) { printf("找到文件: %s\n", fno.fname); } } } ``` #### 2. 文件时间排序 ```c // 比较函数 int compare_time(const void* a, const void* b) { FILINFO* fa = (FILINFO*)a; FILINFO* fb = (FILINFO*)b; return (fa->ftime - fb->ftime); } // 排序实现 FILINFO file_list[50]; int count = 0; while(f_readdir(...) && count < 50) { file_list[count++] = fno; } qsort(file_list, count, sizeof(FILINFO), compare_time); ``` --- ### 七、调试技巧 #### 1. 错误代码解析表 | 错误码 | 含义 | 常见原因 | |--------|------|----------| | FR_OK (0) | 成功 | - | | FR_NO_FILE | 文件不存在 | 路径错误 | | FR_DISK_ERR | 磁盘错误 | 物理连接问题 | | FR_NOT_ENABLED | 文件系统未挂载 | 忘记调用f_mount | | FR_INVALID_NAME | 非法文件名 | 包含非法字符如: *?<> | #### 2. 调试输出示例 ```c printf("[DEBUG] 尝试打开目录: %s\n", path); FRESULT res = f_opendir(&dir, path); if(res != FR_OK) { printf("错误! 代码: 0x%02X\n", res); while(1); // 暂停执行 } ``` --- ### 八、跨平台注意事项 | 平台 | 差异点 | 适配方案 | |------|--------|----------| | STM32 | 需要实现diskio.c | 提供底层SPI/SDIO驱动 | | ESP32 | 默认使用VFS | 调用fatfs_vfs_register() | | Linux | 模拟器模式 | 配置FF_FS_REENTRANT | | Windows | 长路径支持 | 启用FF_USE_LFN=2 | --- 通过合理配置FATFS参数并正确使用API,可以实现稳定可靠的目录遍历操作。重点注意:1) 路径格式规范 2) 错误处理完整性 3) 资源释放及时性。建议开发时启用`FF_STR_VOLUME_ID`和`FF_VOLUME_STRS`配置项以增强可移植性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值