FileX和ThreadX精简版

FILEX精简版

本地下载:
https://files.cnblogs.com/files/blogs/575121/filex_v6.4.1_lite.zip
GitHub仓库:

  • 合并函数级源文件到整个类级源文件

原版Filex和Threadx是每个函数一个文件,(我能想到的好处是方便做单元测试,也可能功能安全认证方便点?)这样在windows环境下编译速度会提高一些(编译一个c/cpp文件都会创建一个gcc进程,win下创建进程开销略大),链接时也不会看到一长串的obj文件。
2025-10-11_162646

  • 移除掉电保护模块

传统的FAT32通过存2份FAT表来保证写入时异常掉电时文件系统不至于损坏(回档到上一次的目录项),实际上这个保护功能很弱,例如U盘写入时拔出,掉文件系统的概率较高;Filex则实现了fault tolerant中间层,在写入数据时,先存到备份分区,完成后再修改真实的FAT目录项和数据区。当备份区写入时掉电则只丢失本次修改,若备份区写入完成后,同步到文件系统时掉电,在下次上电后根据备份区的日志,来对文件系统进行修补和还原。启用掉电保护模块后需要在fx_media_open()之后,调用fx_fault_tolerant_enable()读取备份区的日志进行文件系统的恢复工作。

可以明显看出启用掉电保护模块时,文件系统层的写入放大倍数是2,就意味着写入速度直接减半,性能损失还是比较大的;每次挂载文件系统时,都需要扫描备份分区尝试修补文件系统,也会降低挂载速度。
如果想要掉电安全,还有其他更合适的文件系统可供选择,在FAT文件系统上实现掉电安全代价较大,这是移除该模块的原因。

2025-10-29_102510

  • 文件时间戳改为C库函数提供的API

FAT类文件系统时间戳的注意事项:
1:秒的位宽只有5bit,因此1LSB=2秒。
2:年的位宽为7bit,基准点是1980,localtime_r返回的年份基准点是1900,算出真实年份后需要判断下时间是否早于1980年,防止减成负数。
tm_new.tm_year += 1900
if(tm_new.tm_year < FX_BASE_YEAR) {
tm_new.tm_year = FX_BASE_YEAR;
}
(tm_new.tm_year - FX_BASE_YEAR) << FX_YEAR_SHIFT
3:使用C库中的time相关函数需要自行实现C库依赖的桩函数(localtime_r, asctime_r这些),对接到自己芯片平台的RTC驱动,这部分可以参考RTThread用newlib的实现。

// fx_system.c
VOID _fx_system_time_update(VOID) {
    time_t now;
    struct tm tm_new;

    /* get current time */
    now = time(NULL);
    /* converts calendar time into local time. */
    localtime_r(&now, &tm_new);

    tm_new.tm_year += 1900;
    tm_new.tm_mon++;

    if(tm_new.tm_year < FX_BASE_YEAR) {
        tm_new.tm_year = FX_BASE_YEAR;
    }
    /* Set the system date.  */
    _fx_system_date =  ((tm_new.tm_year - FX_BASE_YEAR) << FX_YEAR_SHIFT) |
                        (tm_new.tm_mon << FX_MONTH_SHIFT) | tm_new.tm_mday;

    /* Set the new system time.  */
    _fx_system_time  =  (tm_new.tm_hour << FX_HOUR_SHIFT) |
                        (tm_new.tm_min << FX_MINUTE_SHIFT) | (tm_new.tm_sec / 2);
}
  • 修改互斥锁为CMSIS API的实现

原版Filex使用的是自家ThreadX的互斥锁实现,这里换成了CMSIS API标准的实现,CMSIS API有很多不同操作系统的封装层,底层可以是FreeRTOS,RTX等,自由度更高一些。

// fx_api.h
typedef struct FX_MEDIA_STRUCT {
// ...
#ifndef FX_SINGLE_THREAD
    osMutexId_t            fx_media_protect;
#endif
// ...
}

引用互斥锁进行资源保护在fx_port.h中的宏函数FX_PROTECT和FX_UNPROTECT

// fx_port.h
#if defined(FX_SINGLE_THREAD) || defined(FX_STANDALONE_ENABLE)
#define FX_PROTECT                   
#define FX_UNPROTECT
#else
#define FX_PROTECT                      if (media_ptr -> fx_media_id != FX_MEDIA_ID) return(FX_MEDIA_NOT_OPEN); \
                                        else if (osMutexAcquire(media_ptr->fx_media_protect, osWaitForever) != osOK) return(FX_MEDIA_NOT_OPEN);
#define FX_UNPROTECT                    osMutexRelease(media_ptr->fx_media_protect);
#endif
  • 存储器驱动移植

文件系统需要依赖存储介质驱动接口进行数据持久化
FILEX只需要实现一个函数原型:void fx_sdcard_driver(FX_MEDIA *media),函数名称任意,入参FX_MEDIA指针,在fx_media_open传入函数指针即可。
驱动函数中判断media->fx_media_driver_request的不同类型进行操作分发:

FX_DRIVER_READ 读取扇区
数据缓冲区指针media->fx_media_driver_buffer和文件读写位置和长度相关,不能保证是4字节对齐,如果SDIO驱动中使用DMA传输,由于大部分芯片SDIO的DMA控制器要求地址指针4字节对齐,因此不能直接将这个缓冲区指针传给DMA控制器。先判断fx_media_driver_buffer是否在4字节边界,不对齐的情况需要先读到本地缓冲区,再复制给media->fx_media_driver_buffer。
单次读取扇区数量media->fx_media_driver_sectors的最大值由配置文件fx_user.h的FX_MAX_SECTOR_CACHE宏指定,假设FX_MAX_SECTOR_CACHE=2,那么最大的DMA本地缓冲区的大小也是固定的1KB。

FX_DRIVER_WRITE 写扇区
和读扇区一样,需要对fx_media_driver_buffer对齐情况进行检查,满足对齐要求可以直接将指针传给DMA控制器,不满足仍需要读到本地缓冲区再拷贝一遍。

FX_DRIVER_INIT 初始化
只会在fx_media_open时调用一次

FX_DRIVER_RELEASE_SECTORS 释放扇区
SD/TF卡模拟的是512字节/扇区的块设备,写入前不需要擦除,但这里也可以调用SD卡的擦除扇区指令,告诉SD卡主控该扇区废弃,主动做垃圾回收。

FX_DRIVER_BOOT_WRITE 写引导扇区
实际上就是将MBR主引导程序和分区表写到存储器的第0扇区。

FILEX精简版

本地下载:
https://files.cnblogs.com/files/blogs/575121/threadx_lite.zip
GitHub仓库:

posted @ 2025-10-11 22:49  Yanye  阅读(50)  评论(0)    收藏  举报