前期准备

需要用到上一篇SPI驱动,保证与Flash芯片通信正常

Env

image.png
image.png

使能文件系统

image.png

设置文件系统扇区与flash芯片扇区大小一致

image.png
image.png

开启SFUD组件,支持将SPI设备映射为块设备

scons —target=mdk5`` 生成工程

驱动配置成功后可以在工程目录下看到
image.png

MDK

接口函数

注册文件系统

在 DFS 组件初始化之后,还需要初始化使用的具体类型的文件系统,也就是将具体类型的文件系统注册到 DFS 中。注册文件系统的接口如下所示:

  1. int dfs_register(const struct dfs_filesystem_ops *ops);
参数 描述
ops 文件系统的操作函数的集合
返回 ——
0 文件注册成功
-1 文件注册失败

该函数不需要用户调用
他会被不同文件系统的初始化函数调用,如 elm-FAT 文件系统的初始化函数elm_init()
开启对应的文件系统后,如果开启了自动初始化(默认开启),文件系统初始化函数也将被自动调用。

将存储设备注册为块设备

因为只有块设备才可以挂载到文件系统上,因此需要在存储设备上创建所需的块设备。存储设备是 SPI Flash,则使用 “串行 Flash 通用驱动库 SFUD” 组件,它可以将SPI设备抽象为块设备来和文件系统对应

  1. rt_spi_flash_device_t rt_sfud_flash_probe(const char *spi_flash_dev_name,
  2. const char *spi_dev_name);
spi_flash_dev_name 自定义块设备名称
spi_dev_name 已经驱动好的SPI设备名称

格式化文件系统

注册了块设备之后,还需要在块设备上创建指定类型的文件系统,也就是格式化文件系统。可以使用 dfs_mkfs() 函数对指定的存储设备进行格式化,创建文件系统,格式化文件系统的接口如下所示:

  1. int dfs_mkfs(const char * fs_name, const char * device_name);
参数 描述
fs_name 文件系统类型
device_name 块设备名称
返回 ——
0 文件系统格式化成功
-1 文件系统格式化失败

文件系统类型(fs_name)可取值及对应的文件系统如下表所示:

取值 文件系统类型
elm elm-FAT 文件系统
jffs2 jffs2 日志闪存文件系统
nfs NFS 网络文件系统
ram RamFS 文件系统
rom RomFS 只读文件系统
uffs uffs 文件系统

挂载文件系统

在 RT-Thread 中,挂载是指将一个存储设备挂接到一个已存在的路径上。我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的路径上,然后通过这个路径来访问存储设备。挂载文件系统的接口如下所示:

  1. int dfs_mount(const char *device_name,
  2. const char *path,
  3. const char *filesystemtype,
  4. unsigned long rwflag,
  5. const void *data);
参数 描述
device_name 已经格式化的块设备名称
path 挂载路径,即挂载点
filesystemtype 挂载的文件系统类型,可取值见 dfs_mkfs() 函数描述
rwflag 读写标志位
data 特定文件系统的私有数据
返回 ——
0 文件系统挂载成功
-1 文件系统挂载失败

如果只有一个存储设备,则可以直接挂载到根目录 / 上。
可以理解为一个文件系统支持连接多个外设,即多枚存储芯片,多个块设备,挂载,即定义该存储块在文件目录下的哪个路径下,类似于电脑的分区(C,D,E,F盘)

注意事项

搭建文件系统的整个过程都有十分明确的先后顺序,第一步注册系统已经在设置驱动时完成,在RTT工程定义的自动初始化 INIT_COMPONENT_EXPORT();`` 中完成,但后面的三个步骤,不得也使用RTT的自动初始化来完成,此时会导致顺序颠倒,而发生错误。
后面三步的初始化,按步骤调用后,可以使用手动初始化,在main中统一调用。

格式化文件系统仅需在对一个新的存储体使用文件系统时进行即可,不必每次下载程序都对存储体进行格式化而导致数据被刷掉

上机实验

  1. #include <flash_app.h>
  2. #include <drv_spi.h>
  3. #include <dfs_fs.h>
  4. #include <dfs.h>
  5. #include <spi_flash_sfud.h>
  6. #define W25Q_SPI_DEVICE_NAME "spi10"
  7. rt_err_t rt_hw_spi_flash_init(void)
  8. {
  9. /* SPI设备的初始化 */
  10. rt_err_t err_message = rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4);
  11. if(err_message != RT_EOK)
  12. {
  13. rt_kprintf("spi Init failed!");
  14. return RT_NULL;
  15. }
  16. struct rt_spi_configuration cfg;
  17. cfg.data_width = 8;
  18. cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
  19. cfg.max_hz = 20 * 1000 *1000; /* 20M */
  20. rt_spi_configure((struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME), &cfg);
  21. /* 将SPI设备注册为块设备 */
  22. rt_sfud_flash_probe("W25Q256", "spi10");
  23. /* 将块设备按照elm-fats文件系统格式化 */
  24. dfs_mkfs("elm", "W25Q256");
  25. /* 将块设备挂载到文件系统上 */
  26. dfs_mount("W25Q256","/","elm",0,0);
  27. return RT_EOK;
  28. }
  29. /* 可以用来验证SPI设备是否正常工作 */
  30. static void flash_app(int argc, char *argv[])
  31. {
  32. struct rt_spi_device *spi_dev_w25q;
  33. rt_uint8_t w25x_read_id = 0x9F;
  34. rt_uint8_t id[3] = {0};
  35. /* 查找 spi 设备获取设备句柄 */
  36. spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);
  37. if (!spi_dev_w25q)
  38. {
  39. rt_kprintf("spi sample run failed! can't find %s device!\n", W25Q_SPI_DEVICE_NAME);
  40. }
  41. else
  42. {
  43. /* 方式1:使用 rt_spi_send_then_recv()发送命令读取ID */
  44. rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5);
  45. rt_kprintf("use rt_spi_send_then_recv() read w25q ID is:\n%x%x%x\n",id[0],id[1],id[2]);
  46. }
  47. }
  48. /* 导出到 msh 命令列表中 */
  49. MSH_CMD_EXPORT(flash_app, flash ID test);

函数 rt_hw_spi_flash_init()`` 在main中被调用

image.png
image.png

当输入ls指令能显示当前文件夹路径即文件系统正常工作
可以通过指令进一步文件操作(创建,删除文件或文件夹)

其它指令见RTT说明文档【文件系统】
https://www.rt-thread.org/document/site/programming-manual/filesystem/filesystem/