板载nor flash这么大,当裸数据存储区用不太好,还是移植个文件系统吧。开源的、适用于mcu的nor flash文件系统也不多,流行的就是littlefs和spiffs了。后者的开发已经基本停滞,所以笔者就选littlefs啦。
littlefs代码准备
从github clone或下载littlefs,把它顶层目录下的lfs.c、lfs.h、lfs_util.c、lfs_util.h拷贝到middlewares/littlefs/目录下,其余文件无用可以忽略。
git clone https://github.com/littlefs-project/littlefs.git
littlefs的移植
根据littlefs的README和lfs.h头文件,我们只要实现如下结构体即可:
static const struct lfs_config cfg = {
.read = lfs_sf_read,
.prog = lfs_sf_write,
.erase = lfs_sf_erase,
.sync = lfs_sf_sync,
.read_size = 64,
.prog_size = 256,
.block_size = 4096,
.block_count = 128,
.cache_size = 256,
.lookahead_size = 128,
.block_cycles = 100,
};
这个结构体成员除cache_size、lookahead_size、block_cycles三个成员外,其它成员的填充需要依据flash的结构以及flash的使用量来填充,比如block_count设成128意思是准备用128个block用作littlefs,很明显大部分情况下不会把flash的所有block都用于littlefs,那block_count就是用于控制用于littlefs的flash空间大小的;像read_size、prog_size、block_size可由flash的datasheet获得。
而cache_size等拎出来三个成员在lfs.h中有详细的说明,比如cache_size、lookahead_size需要根据系统的sram富余程度、lfs使用频繁程度配置,这两个量均以字节为单位,消耗sram加速lfs用的;而block_cycles成员用于读写均衡,比如笔者这里配置成100,意思是擦除100次后,需要挪到其它block上。
接下来咱们需要实现三个重要的钩子函数.read、.prog和.erase,分别用于flash的读、写、擦除。上一篇文章我们已经实现了nor flash的读、写、擦除,所以这三个钩子函数的实现其实很简单的,笔者的实现如下:
注:sync钩子可以简单返回0即可,大部分情况下用不上的。
static int lfs_sf_read(const struct lfs_config *c, uint32_t block,
uint32_t offset, void *buf, uint32_t size)
{
uint32_t addr = (block * c->block_size) + offset;
qspi_data_read(addr, size, buf);
return 0;
}
static int lfs_sf_write(const struct lfs_config *c, uint32_t block,
uint32_t offset, const void *buf, uint32_t size)
{
uint32_t addr = (block * c->block_size) + offset;
qspi_data_write(addr, size, buf);
return 0;
}
static int lfs_sf_erase(const struct lfs_config *c, uint32_t block)
{
uint32_t addr = block * c->block_size;
qspi_erase(addr);
return 0;
}
static int lfs_sf_sync(const struct lfs_config *c)
{
return 0;
}
到此littlefs已经移植好啦,咱们可以用littlefs的API去测试或者实现一些nr_micro_shell下的相关命令,笔者就初步实现了lfs的mount、umount、demo等子命令:
static void shell_lfs_cmd(char argc, char *argv)
{
if (!strcmp(&argv[(int)argv[1]], "mount")) {
lfs_cmd_mount();
} else if (!strcmp(&argv[(int)argv[1]], "umount")) {
lfs_cmd_umount();
} else if (!strcmp(&argv[(int)argv[1]], "demo")) {
lfs_cmd_demo();
}
}
NR_SHELL_CMD_EXPORT(lfs, shell_lfs_cmd);
lfs_cmd_umount()函数的实现比较简单,一行调用littlefs的api的代码就可以了:
static void lfs_cmd_umount(void)
{
lfs_unmount(&lfs);
}
lfs_cmd_mount()函数稍微绕了一点,仅仅调用littlefs的相mount函数还不够,因为第一次nor flash上还没有littlefs文件系统呢,所以这时littlefs的mount会失败,这时候我们需要格式化再mount尝试一次:
static void lfs_cmd_mount(void)
{
int err = lfs_mount(&lfs, &cfg);
if (err) {
lfs_format(&lfs, &cfg);
lfs_mount(&lfs, &cfg);
}
}
而其中lfs_cmd_demo()其实就是littlefs中的一个测试例子,打开一个名为boot_count的文件,读取一个整数加一后写入(第一次是空文件所以读不到,但是并不妨碍写入数据),并把这个整数同时打印到终端:
static void lfs_cmd_demo(void)
{
lfs_file_t file;
// read current count
unsigned int boot_count = 0;
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
// update boot count
boot_count += 1;
lfs_file_rewind(&lfs, &file);
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs, &file);
// print the boot count
printf("boot_count: %d\r\n", boot_count);
}
这样nr_micro_shell的配套命令也实现好了,coding工作可收工了。编译烧录和以前一样,在此略过不提。咱们测试看看
然后重启板子看看boot_count这个文件能否正确存储数据:
看到了吧,这次加一以后的整数是3,说明原来的整数是2,这个数字恰好是板子重启前写入文件的,说明littlefs移植成功。