GD32移植FATFS文件系统
Contents
起因
GD32 项目里要往 SD 卡记数据(日志、参数文件),单片机直接读写扇区只有“块”的概念,没有文件和目录,拔下来插电脑也读不回来——这层“文件”的抽象就是文件系统要做的事。FatFS 是为小型嵌入式写的 FAT 实现:纯 C、体积极小、不挑硬件,是单片机上事实标准的选择。这篇记录在 GD32 + FreeRTOS 上移植它的过程。
需求
- FatFS 在 GD32 工程里跑通 f_open / f_read / f_write
- FreeRTOS 多任务环境下的并发访问不破坏数据(线程安全)
技术实现原理
FatFS 的分层:文件系统与硬件之间只隔 6 个函数
上层 API(f_open、f_write)把“文件”翻译成“扇区读写 + FAT 表/目录项维护”,它完全不知道底层是什么存储器件;diskio.c 里的 6 个函数(状态、初始化、读、写、控制命令、取时间)是唯一的硬件边界。移植 FatFS = 把这 6 个函数接到 SD 卡驱动上,其余源码原样编译——这就是它“随处可移植”的结构来源,也是文档反复强调这 6 个函数的原因。
注意读写的单位是“块”:disk_read / disk_write 的单位是 sector(通常 512 字节),不是字节。FatFS 内部按簇/扇区管理空间,驱动层必须按块对齐地访问设备,字节级的偏移由上层换算好了再传下来。
无系统 / 带系统的取舍(线程安全的根因)
- 不上 RTOS:只添加 ff.c 和 diskio.c,_FS_REENTRANT 保持 0,没有并发就没有竞争;
- 上 FreeRTOS:多个任务同时对同一卷 f_write,FAT 表和目录项的更新是“读-改-写”过程,交错执行就会互相覆盖——这就是要打开 _FS_REENTRANT 的根因。打开后 FatFs 用互斥量保护同一卷的访问,代价是四个同步函数(ff_req_grant / ff_rel_grant / ff_cre_syncobj / ff_del_syncobj)必须自己接到 OS 的 API 上,syscall.c 就是这四个函数的模板。
官方注释还划了边界:不同卷之间的访问天然安全;f_mount / f_mkfs 这类卷管理函数无论开不开重入都不受保护,多任务调用要自己在应用层加锁。
ffconf.h 与 syscall.c 怎么配合
_SYNC_t 定义锁对象的类型,FreeRTOS 下用 SemaphoreHandle_t;ffconf.h 开了 _FS_REENTRANT 后,syscall.c 里被注释掉的 xSemaphoreCreateMutex 等实现按提示解开,接到 FreeRTOS 的信号量 API。
下载地址
http://elm-chan.org/fsw/ff/00index_e.html
解压后
|
|
文档
在doc中有相关介绍文档,提示我们需要补全这六个函数
|
|
在src中有readme文件
里面介绍了每个文件的内容
系统
非常重要的问题,在系统中使用需要考虑线程安全问题,在syscall.c中给出了相关的解决方式
使用系统
必须添加syscall.c
不使用系统
只需要添加ff.c和diskio.c
修改文件
diskio.c diskio中的文件进行补全
disk_status,可直接返回0
disk_initialize,写入初始化,如果已经初始化过设备可以直接返回0
disk_read,读函数,需要注意,读指的是读一个block,即512个字节,在设备中需要区分
disk_write,写函数,同上
disk_ioctl,命令相关,该函数需要注意,在配置文件中不一样的配置需要不一样的内容,具体可查看http://elm-chan.org/fsw/ff/doc/dioctl.html
get_fattime,该函数可直接返回0,可添加该函数,如果_FS_NORTC=1不必提供
syscall.c 这里使用了FreeRTOS系统,需要进行修改
在ffconf.h中,需要使能_FS_REENTRANT
|
|
按照提示修改
|
|
并且在添加头文件
|
|
接下来修改函数部分
按照提示取消FreeRTOS屏蔽(把注释解开)
|
|
踩坑记录与注意事项
- 链接报 undefined reference to disk_xxx:六个函数一个都不能少,哪怕 get_fattime 只返回 0 也要有实现(或设 _FS_NORTC=1 让它不需要);
- disk_read/disk_write 的返回值别写反:DRESULT 里 RES_OK 是 0,与“非 0 为真”的直觉相反,返回值错了上层全部失败;
- disk_ioctl 的 CTRL_SYNC 别空着:它是缓存落盘的钩子,不实现的话写数据停在设备缓存里,掉电丢文件甚至损坏文件系统;
- 多任务不开 _FS_REENTRANT:偶发文件内容错乱、FAT 表损坏,极难复现定位——根因就是 FAT 表读-改-写竞争,一开始就开;
- 改了 ffconf.h 要全量重编:配置宏影响所有源文件的编译结果,增量编译会带着旧配置的对象文件混链接;
- 长文件名/中文路径:默认 _USE_LFN 关闭,8.3 短名够用就别开;要开就得同时规划它的工作内存(静态/栈/堆三选一)。
Author 软件开发大郭
LastMod 2022-05-08