FatFs文件系统移植应用笔记

发布时间 2023-09-27 15:11:35作者: WCH_CH32

FatFs 文件系统移植

应用笔记

使单片机拥有按文件访问存储器中数据的能力,要满足两个必要的条件。其一是存储器已完成格式化操作,即存储器按 FAT/FAT16/FAT32 等格式记录数据,其二是软件中实现文件系统功能,即能够按照存储器中文件记录的格式,操作已有的数据或添加新数据。

FatFs 是一个轻量级、可移植的文件系统,主要用于嵌入式系统。它基于 FAT 文件系统架构,具有良好的兼容性和跨平台性能。FatFs 文件系统可以轻松移植到各种操作系统中,并支持多种文件操作,如创建、删除、改名和目录管理等。此外,FatFs 还提供内存管理和文件权限等功能,确保文件系统的稳定性和安全性。总的来说,FatFs 文件系统是一个简单易用、可扩展的文件系统,适用于各种嵌入式应用场景。

目前 FatFs 文件系统最新版本为 R0.15,下载后压缩包中包含了两个文件夹以及一份TXT 格式的许可证书。其中 documents 文件夹中主要存储了文件系统的介绍与帮助文档,而 source 文件夹中存储的是 FatFs 的源码。

在 source 文件夹中,ff.c、ff.h 与 ffconf.h 三个文件实现了 FatFs 文件系统的核心功能,包括文件读写、文件删除、文件属性修改、文件移动等。其中 ff.c 是文件操作的主要实现代码,ff.h是头文件,ffconf.h 用于配置文件系统的参数。

diskio.c 与 diskio.h 两个文件实现了 FatFs 文件系统和存储介质的底层交互功能,包括存储介质的初始化、读写、删除、移动等。其中 diskio.c 是底层磁盘操作的实现代码,diskio.h 是 diskio.c 文件的头文件。

ffunicode.c 文件实现了 FatFs 文件系统的字符编码转换功能,它将文件系统的内部unicode 编码转换为操作系统常用的 ANSI 编码或者将 ANSI 编码转换为 Unicode 编码。

综上所述,在移植的过程中,应重点关注 diskio.c 与 ffconf.h 两个文件,下面就以 CH32V307VCT6芯片为例,基于 SDIO 操作 SD 卡例程,移植 FatFs 文件系统。diskio.c 文件内disk_status 函数中,通过 SD_GetState 函数获取 SD 卡工作状态,并将结果赋值给局部变量进行存储,经卡状态判断后,将文件系统标志位置位。

1. DSTATUS disk_status (

2. BYTE pdrv /* Physical drive nmuber to identify the drive */

3. )

4. {

5. DSTATUS stat = STA_NOINIT;

6. u32 result;

7.  

8. switch (pdrv) {

9. // case DEV_RAM :

10. // result = RAM_disk_status();

11. // return stat;

12.  

13. case DEV_MMC :

14. result = SD_GetState();

15. if (result < SD_CARD_DISCONNECTED) {

16. stat &= ~STA_NOINIT; // SD已连接

17. } else {

18. stat = STA_NOINIT;

19. }

20. return stat;

21.  

22. // case DEV_USB :

23. // result = USB_disk_status();

24. // return stat;

25. }

26. return STA_NOINIT;

27. }

由于 SD 卡在上电后会立即初始化,disk_initialize 函数中不再重复操作,直接调用disk_status 函数返回当前 SD 卡状态。

1. DSTATUS disk_initialize (

2. BYTE pdrv /* Physical drive nmuber to identify the drive */

3. )

4. {

5. DSTATUS stat = STA_NOINIT;

6. // int result;

7.  

8. switch (pdrv) {

9. // case DEV_RAM :

10. // result = RAM_disk_initialize();

11. // return stat;

12.  

13. case DEV_MMC :

14. // 卡在上电的时候已经完成初始化,不需要再次执行直接返回状态

15. stat = disk_status(DEV_MMC);

16. return stat;

17.  

18. // case DEV_USB :

19. // result = USB_disk_initialize();

20. // return stat;

21. }

22. return STA_NOINIT;

23. }

单片机通过 SDIO 接口操作 SD 卡时,有块读写与连续多块读写两种操作方法,在库函数中通过判断 buffer 大小,自动选择使用何种方式进行数据传输。在 disk_read 与disk_write 函数中,分别调用封装后的 SD_ReadDisk 与 SD_WriteDisk 函数,而无需手动选择其具体的操作方法,软件将根据实际情况,提高文件的读写效率。

1. DRESULT disk_read (

2. BYTE pdrv, /* Physical drive nmuber to identify the drive */

3. BYTE *buff, /* Data buffer to store read data */

4. LBA_t sector, /* Start sector in LBA */

5. UINT count /* Number of sectors to read */

6. )

7. {

8. DRESULT res = RES_PARERR;

9. int result;

10.  

11. switch (pdrv) {

12. // case DEV_RAM :

13. // result = RAM_disk_read(buff, sector, count);

14. // return res;

15.  

16. case DEV_MMC :

17. result = SD_ReadDisk(buff, sector, count);

18.  

19. if (result == SD_OK) {

20. res = RES_OK;

21. } else {

22. res = RES_ERROR;

23. }

24.  

25. return res;

26.  

27. // case DEV_USB :

28. // result = USB_disk_read(buff, sector, count);

29. // return res;

30. }

31.  

32. return RES_PARERR;

33. }

disk_ioctl 函数的作用是执行与存储介质相关的控制操作,以及获取、设置存储介质的参数。为了实现文件系统对 SD 卡进行格式化的操作,函数中通过指针的方式,依次传入扇区个数、扇区大小以及每次最少擦写的扇区数共三个基本参数。

1. DRESULT disk_ioctl (

2. BYTE pdrv, /* Physical drive nmuber (0..) */

3. BYTE cmd, /* Control code */

4. void *buff /* Buffer to send/receive control data */

5. )

6. {

7. DRESULT res = RES_PARERR;

8. // int result;

9.  

10. switch (pdrv) {

11. // case DEV_RAM :

12. // return res;

13.  

14. case DEV_MMC :

15. switch(cmd){

16. case CTRL_SYNC:

17. break;

18. case GET_SECTOR_COUNT: // 扇区个数

19. *(DWORD*)buff = Flash_Sector_Count;

20. break;

21. case GET_SECTOR_SIZE: // 扇区大小

22. *(WORD*)buff = Flash_Sector_Size;

23. break;

24. case GET_BLOCK_SIZE: // 每次最少擦除扇区个数

25. *(WORD*)buff = 1;

26. break;

27. case CTRL_TRIM:

28. break;

29. }

30.  

31. res = RES_OK;

32.  

33. return res;

34.  

35. // case DEV_USB :

36. // return res;

37. }

38.  

39. return RES_PARERR;

40. }

在文件的属性中,记录着文件创建的时间,FatFs 通过调用 get_fattime 函数获取 RTC 时钟数据,直接屏蔽 get_fattime 函数会导致工程在编译过程中出现错误。由于没有实际开启芯片 RTC 功能,这里直接填入固定日期及时间进行替代。

1. DWORD get_fattime(void) {

2. return ((DWORD)(2020 - 1980) << 25) /* Year 2020 */

3. | ((DWORD)1 << 21) /* Month 1 */

4. | ((DWORD)1 << 16) /* Mday 1 */

5. | ((DWORD)0 << 11) /* Hour 0 */

6. | ((DWORD)0 << 5) /* Min 0 */

7. | ((DWORD)0 >> 1); /* Sec 0 */

8. }

至此,FatFs 文件系统的移植基本完成,在实际测试前还应根据需要,合理调整 ffconf.h 文件中如 FF_USE_MKFS 置为 1 启用格式化功能、FF_USE_LFN 置为 3 启用长文件名等功能。配置文件中有详细的注释,解释了每个配置项的具体功能,此处不再一一赘述。