电子说
由于之前需要使用片上的flash多余的部分来搭建文件系统,但是没有找到使用片上的教程,都是利用片外的flash教程。后来发现能直接使用fal软件包作为flash设备抽象层,向上可以提供文件系统的接口,向下可以驱动片内的flash。这里放上之前使用的笔记。
1.简介
littlefs 在 RT-Thread 上运行的层级关系图如下所示:
开发者使用的是 DFS 框架提供的统一的 POSIX API,DFS 框架会调用 littlefs 的 API,littlefs 会使用 MTD 设备的读写接口,开发者可以使用 RT-Thread 提供的 fal 组件和 SFUD 组件来完成对 FLASH 的读写任务,也可以自己实现 MTD 设备的驱动程序,使 littlefs 可以挂载到更多的存储介质上。
2.FAL MCU Flash移植
2.1 FAL软件包源码获取
打开bsp工程的ENV环境,运行menuconfig命令。
2.2 fal具体配置
FAL uses SFUD drivers:是用来驱动外置的flash设备,由于我们只使用内部flash,所以不需要选上。
每个功能的配置说明如下:
开启调试日志输出(默认开启);
分区表是否在fal_cfg.h中定义(默认开启)。如果关闭此选项,fal 将会自动去指定 Flash 的指定位置去检索并装载分区表,具体配置详见下面两个选项;
存放分区表的 Flash 设备;
分区表的 结束地址 位于 Flash 设备上的偏移。fal 将从此地址开始往回进行检索分区表,直接读取到 Flash 顶部。如果不确定分区表具体位置,这里也可以配置为 Flash 的结束地址,fal 将会检索整个 Flash,检索时间可能会增加。
启用 FAL 针对 SFUD 的移植文件(默认关闭);
应输入调用 rt_sfud_flash_probe 函数时传入的 FLASH 设备名称(也可以通过 list_device 命令查看 Block Device 的名字获取)。该名称与分区表中的 Flash 名称对应,只有正确设置设备名字,才能完成对 FLASH 的读写操作。
关于分区表,下文还会提及并解释。
2.3 pkgs —update
保存配置退出后,在env环境执行pkgs —update命令,会自动从FAL的github仓库获取FAL软件包源码到本地工程目录,如下图所示:
2.4 设备表和分区表
FAL组件初始化最重要的是维护两个表:一个是flash设备表;另一个是FAL分区表,两个表的元素分别是前面介绍过的fal_flash_dev结构体地址和fal_partition结构体对象。
fal_flash_dev设备表主要由底层的Flash驱动(包括MCU片内Flash和SFUD驱动的片外Flash)提供,也即FAL移植的重点就是在Flash驱动层向FAL提供fal_flash_dev设备表,每个flash设备提供设备表中的一个元素。
fal_partition分区表由用户事先配置在fal_cfg.h头文件中,FAL向上面的用户层提供的分区访问接口函数操作的内存区间就是从fal_partition分区表获取的,最后对分区的访问还是通过Flash驱动提供的接口函数(fal_flash_dev.ops)实现的。
设备表管理不同的flash设备,可以是片内的也可以是片外的。有点像管理不同的硬盘。分区表就是我们电脑上经常说的那个磁盘分区的意思。
2.5 复制文件
考虑到packages下面的软件版本后续可能会升级覆盖,我们不在packagesfal-latest目录下直接进行移植修改,而是在packages目录外新建一个文件夹ports专门保存软件包的移植文件信息。
(如果不担心升级覆盖问题,跳过2.5和2.6直接看2.9,再回来看2.7和2.8)
新建与packages软件包同级的移植文件目录ports,将packagesfal-latestsamplesporting目录下的fal_cfg.h文件复制一份到portsfal目录下,将packagesfal-latestSConscript复制一份到portsfal目录下,将packagesSConscript复制一份到ports目录下,复制文件后的目录结构如下图所示:
(注:图片里多了fal_flash_sfud_port.c文件,是原博客图片,但是我们不需要驱动外置的flash)
2.6 修改sconscript文件
由于portsfal目录及下面的文件名有变化,所以需要修改编译脚本portsfalSConscript,主要是修改文件目录及文件名,修改后的编译脚本如下:(直接覆盖,其实不改这个SConscript文件也没啥问题,强迫症建议覆盖)
from building import *
import rtconfig
cwd = GetCurrentDir()
src = []
CPPPATH = [cwd]
LOCAL_CCFLAGS = ''
if rtconfig.CROSS_TOOL == 'gcc':
LOCAL_CCFLAGS += ' -std=c99'
elif rtconfig.CROSS_TOOL == 'keil':
LOCAL_CCFLAGS += ' --c99'
group = DefineGroup('fal', src, depend = ['PKG_USING_FAL'], CPPPATH = CPPPATH, LOCAL_CCFLAGS = LOCAL_CCFLAGS)
Return('group')
别忘了执行scons —target=mdk5命令,到此直接编译工程会出现error,需要接下来继续更改文件。
.buildkeilObjrt-thread.axf: Error: L6218E: Undefined symbol nor_flash0 (referred from fal_flash.o).
.buildkeilObjrt-thread.axf: Error: L6218E: Undefined symbol stm32f2_onchip_flash (referred from fal_flash.o).
2.7 修改Kconfig
在原bsp目录的board/Kconfig中的menu “On-chip Peripheral Drivers”内添加下面语句
config BSP_USING_ON_CHIP_FLASH
bool "Enable On-Chip Flash"
default n
添加完,进入menuconfig选中该项,并保存退出。
2.8 修改drv_flash_f4.c文件
STM32f427片内Flash驱动,RT-Thread已经在librariesHAL_Drivers drv_flashdrv_flash_f4.c目录下提供了,同时还通过条件宏提供了向FAL注册fal_flash_dev设备表项的代码。但还需我们自己添加一部分代码(个人觉得应该是rtt 官方留白,让我们自定义分区基地址和大小),如下图:
#define STM32_FLASH_START_ADRESS_16K ((uint32_t)0x08100000)
#define STM32_FLASH_START_ADRESS_64K ((uint32_t)0x08110000)
#define STM32_FLASH_START_ADRESS_128K ((uint32_t)0x08120000)
#define FLASH_SIZE_GRANULARITY_16K (641024 )
#define FLASH_SIZE_GRANULARITY_64K (641024 )
#define FLASH_SIZE_GRANULARITY_128K (896*1024)
为什么这样添加分区基地址和大小?
先看下面的图
因此我们现在这样是在使用扇区12到23,又由于STM32F42x的这几个扇区有16K,64K,128K大小的。所以使用fal时,必须把他们看成3个不同的flash设备进行管理,这也是上文所说flash设备表的作用。
2.9 覆盖(或新建)fal_cfg.h
新建一个下面代码块内容的fal_cfg.h文件在packagesfal-latestinc 内
/*
2.x FAL使用示例
见博客 1.4 FAL使用示例 或者** **FAL:Flash 抽象层的 3、Finsh/MSH 测试命令
我的部分操作见 4.2 fal指令实验
到此已经移植完fal了
3.搭载 littlefs 文件系统
littlefs 是 ARM 官方推出的,专为嵌入式系统设计的文件系统,相比传统的文件系统,littlefs 具有以下优点:
自带擦写均衡
支持掉电保护
占用的 RAM/ROM 少
littlefs 自带的擦写均衡和掉电保护使开发者可以放心的将文件系统挂载到 nor flash 上。
3.1 使能DFS框架
打开 env,输入 menuconfig,在 RT-Thread Components → Device virtual file system 中打开 DFS 框架。
使用默认配置
3.2 配置 littlefs
在 RT-Thread online packages → system packages → Littlefs: A high-integrity embedded file system 中打开 littlefs。
注意lfs enable wear leveling要改成100,这项意思是 lfs启用损耗均衡
3.2.1 猜测
代码中对于disk block size的注释
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, non-inlined files
// take up at minimum one block. Must be a multiple of the read
// and program sizes.
Google 翻译
//可擦除块的大小。 这不会影响ram的消耗,并且
//可能大于物理擦除大小。 但是,非内联文件
//至少占用一个街区。 必须是读取的倍数
//和程序大小。
结合程序调试中mtd_nor->block_size值为0x0002 0000。即128KB,又是”filesystem”分区所在的扇区的大小。
所以基本确定disk block size应该填128*1024,即131072。
其他配置我觉得默认配置问题不大。比如下面这个注释的描述。
// Minimum size of a block read. All read operations will be a
// multiple of this value.
lfs_size_t read_size;
3.3 使能 MTD 设备
在 RT-Thread Components → Device Drivers 中使能 MTD 设备。
使用pkgs —update更新软件包和scons —target=mdk5
3.4 创建 MTD 设备并挂载文件系统
fal 组件并没有加入自动初始化的代码,所以我们需要在 main 函数中初始化 fal,并使用 fal 提供的 API 来创建一个 MTD 设备。创建 MTD 设备后,就可以将 littlefs 挂载到刚刚生成的 MTD 设备上了。
在 main.c 文件中添加(覆盖)的代码如下所示:
/* 添加 fal 头文件 /
#include
/ 添加文件系统头文件 /
#include
/ 添加 DEBUG 头文件 /
#define DBG_SECTION_NAME "main"
#define DBG_LEVEL DBG_INFO
#include
/ 定义要使用的分区名字 */
#define FS_PARTITION_NAME "filesystem"
int main(void)
{
struct rt_device mtd_dev = RT_NULL;
/ 初始化 fal /
fal_init();
/ 生成 mtd 设备 /
mtd_dev = fal_mtd_nor_device_create(FS_PARTITION_NAME);
if (!mtd_dev)
{
LOG_E("Can't create a mtd device on '%s' partition.", FS_PARTITION_NAME);
}
else
{
/ 挂载 littlefs /
if (dfs_mount(FS_PARTITION_NAME, "/", "lfs", 0, 0) == 0)
{
LOG_I("Filesystem initialized!");
}
else
{
/ 格式化文件系统 /
dfs_mkfs("lfs", FS_PARTITION_NAME);
/ 挂载 littlefs */
if (dfs_mount("filesystem", "/", "lfs", 0, 0) == 0)
{
LOG_I("Filesystem initialized!");
}
else
{
LOG_E("Failed to initialize filesystem!");
}
}
}
while (1)
{
rt_thread_mdelay(100);
}
}
注意这里使用了一个分区”filesystem”。所以上面创建分区必须也有名为”filesystem”的分区。
3.5 使用littlefs 文件系统
3.5.1 参考
文件系统语句都是通用的,都是基于POSIX 标准的
3.5.2 问题
需要注意:我这一直有个问题,就是只能使用mkdir 创建2条路径,创建第3条就会出错。如下:
暂未解决,因为创建二三十个.txt .c也没出错,就先这样吧。
3.5.3 格式化
文档里讲的不清楚。
littlefs的格式化语句
:
1.程序里面写的
/* 定义要使用的分区名字 /
#define FS_PARTITION_NAME "filesystem"
/ 格式化文件系统 */
dfs_mkfs("lfs", FS_PARTITION_NAME);
2.Finsh组件
mkfs -t lfs filesystem
文档里漏了
3.5.4 官方例程
4.实验
4.1 分区实验
看从0x0800 0000开始是否影响原程序
(是影响的,所以建议从扇区12开始。当然也可以看.map文件来知道程序所占ROM内存大小,来算从第几个扇区开始是安全的,不过这样就需要自己不按照我上文讲的那样配置设备表和分区表)
再步进一次,擦除所有扇区,即原来的程序也被擦除,导致hard_fault。匿名上位机保持上一张图片的状态
4.2 fal指令实验
FAL为便于用户调试,也提供了finsh命令fal,包括fal probe / read / write / erase / bench等命令
通过使用随机的人为数据给“写入函数”,读取出“读取函数”的值,来验证是否复位后还保存着数据
(是能继续存储的,所以fal确实是操作着掉电不丢失数据的东西)
全部0条评论
快来发表一下你的评论吧 !