前期准备
需要用到上一篇SPI驱动,保证与Flash芯片通信正常
Env
使能文件系统
设置文件系统扇区与flash芯片扇区大小一致
开启SFUD组件,支持将SPI设备映射为块设备
scons —target=mdk5`` 生成工程
驱动配置成功后可以在工程目录下看到
MDK
接口函数
注册文件系统
在 DFS 组件初始化之后,还需要初始化使用的具体类型的文件系统,也就是将具体类型的文件系统注册到 DFS 中。注册文件系统的接口如下所示:
int dfs_register(const struct dfs_filesystem_ops *ops);
参数 | 描述 |
---|---|
ops | 文件系统的操作函数的集合 |
返回 | —— |
0 | 文件注册成功 |
-1 | 文件注册失败 |
该函数不需要用户调用,
他会被不同文件系统的初始化函数调用,如 elm-FAT 文件系统的初始化函数elm_init()
。
开启对应的文件系统后,如果开启了自动初始化(默认开启),文件系统初始化函数也将被自动调用。
将存储设备注册为块设备
因为只有块设备才可以挂载到文件系统上,因此需要在存储设备上创建所需的块设备。存储设备是 SPI Flash,则使用 “串行 Flash 通用驱动库 SFUD” 组件,它可以将SPI设备抽象为块设备来和文件系统对应
rt_spi_flash_device_t rt_sfud_flash_probe(const char *spi_flash_dev_name,
const char *spi_dev_name);
spi_flash_dev_name | 自定义块设备名称 |
---|---|
spi_dev_name | 已经驱动好的SPI设备名称 |
格式化文件系统
注册了块设备之后,还需要在块设备上创建指定类型的文件系统,也就是格式化文件系统。可以使用 dfs_mkfs()
函数对指定的存储设备进行格式化,创建文件系统,格式化文件系统的接口如下所示:
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 中,挂载是指将一个存储设备挂接到一个已存在的路径上。我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的路径上,然后通过这个路径来访问存储设备。挂载文件系统的接口如下所示:
int dfs_mount(const char *device_name,
const char *path,
const char *filesystemtype,
unsigned long rwflag,
const void *data);
参数 | 描述 |
---|---|
device_name | 已经格式化的块设备名称 |
path | 挂载路径,即挂载点 |
filesystemtype | 挂载的文件系统类型,可取值见 dfs_mkfs() 函数描述 |
rwflag | 读写标志位 |
data | 特定文件系统的私有数据 |
返回 | —— |
0 | 文件系统挂载成功 |
-1 | 文件系统挂载失败 |
如果只有一个存储设备,则可以直接挂载到根目录 /
上。
可以理解为一个文件系统支持连接多个外设,即多枚存储芯片,多个块设备,挂载,即定义该存储块在文件目录下的哪个路径下,类似于电脑的分区(C,D,E,F盘)
注意事项
搭建文件系统的整个过程都有十分明确的先后顺序,第一步注册系统已经在设置驱动时完成,在RTT工程定义的自动初始化
INIT_COMPONENT_EXPORT();`` 中完成,但后面的三个步骤,不得也使用RTT的自动初始化来完成,此时会导致顺序颠倒,而发生错误。
后面三步的初始化,按步骤调用后,可以使用手动初始化,在main中统一调用。
格式化文件系统仅需在对一个新的存储体使用文件系统时进行即可,不必每次下载程序都对存储体进行格式化而导致数据被刷掉
上机实验
#include <flash_app.h>
#include <drv_spi.h>
#include <dfs_fs.h>
#include <dfs.h>
#include <spi_flash_sfud.h>
#define W25Q_SPI_DEVICE_NAME "spi10"
rt_err_t rt_hw_spi_flash_init(void)
{
/* SPI设备的初始化 */
rt_err_t err_message = rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4);
if(err_message != RT_EOK)
{
rt_kprintf("spi Init failed!");
return RT_NULL;
}
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
cfg.max_hz = 20 * 1000 *1000; /* 20M */
rt_spi_configure((struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME), &cfg);
/* 将SPI设备注册为块设备 */
rt_sfud_flash_probe("W25Q256", "spi10");
/* 将块设备按照elm-fats文件系统格式化 */
dfs_mkfs("elm", "W25Q256");
/* 将块设备挂载到文件系统上 */
dfs_mount("W25Q256","/","elm",0,0);
return RT_EOK;
}
/* 可以用来验证SPI设备是否正常工作 */
static void flash_app(int argc, char *argv[])
{
struct rt_spi_device *spi_dev_w25q;
rt_uint8_t w25x_read_id = 0x9F;
rt_uint8_t id[3] = {0};
/* 查找 spi 设备获取设备句柄 */
spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);
if (!spi_dev_w25q)
{
rt_kprintf("spi sample run failed! can't find %s device!\n", W25Q_SPI_DEVICE_NAME);
}
else
{
/* 方式1:使用 rt_spi_send_then_recv()发送命令读取ID */
rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5);
rt_kprintf("use rt_spi_send_then_recv() read w25q ID is:\n%x%x%x\n",id[0],id[1],id[2]);
}
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(flash_app, flash ID test);
函数
rt_hw_spi_flash_init()`` 在main中被调用
当输入ls指令能显示当前文件夹路径即文件系统正常工作
可以通过指令进一步文件操作(创建,删除文件或文件夹)
其它指令见RTT说明文档【文件系统】
https://www.rt-thread.org/document/site/programming-manual/filesystem/filesystem/