各種データの保存領域とフルモデムアップデートなどに対応できるように、
ファイルシステム(LittleFS)を利用する方法と、ドライバーを直接叩く方法の両方を調べてみた。
ncs のバージョンは v1.9.1 で検証した。
(2022/9/8 ncs v2.0.2 で検証済み 一部修正が必要)
(2022/10/26 ncs v2.1.1 で検証済み 一部修正が必要)
なお。nRF9160DKの場合、nRF52840側のボードコントロールにて、ハードウエアスイッチを切り替えておく必要がある。
nRF9160DK上のnRF52840用 overlay ファイル抜粋
/ {
board-control {
external_flash_pins_routing: switch-ext-mem-ctrl {
compatible = "nordic,nrf9160dk-optional-routing";
control-gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};
nRF9160 の overlay ファイル抜粋
/ {
chosen {
nordic,pm-ext-flash = &mx25r64;
};
};
&spi3 {
status = "okay";
sck-pin = <13>;
mosi-pin = <11>;
miso-pin = <12>;
cs-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
mx25r64: mx25r64@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <80000000>;
label = "MX25R64";
jedec-id = [c2 28 17];
size = <67108864>;
};
};
proj.conf 抜粋
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_IMG_ERASE_PROGRESSIVELY=y
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
CONFIG_SPI_NOR=y
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
MX25R6435Fの容量は 8MBytes 。
先頭 7MBytes をドライバーで直接読み書きする領域(大きなファイルのダウンロード用)、
後方 1MByte を LittleFS 用に割り当ててみた。
proj.confなどがあるディレクトリに、pm_static.yml というファイルを以下の内容で作成しておく。
external_flash:
address: 0x000000
end_address: 0x6FFFFF
region: external_flash
size: 0x700000
littlefs_storage:
address: 0x700000
device: MX25R64
region: external_flash
size: 0x100000
LittleFS を使用する
ソースコードに下記を追加、
#include <fs/fs.h>
#include <storage/flash_map.h>
#include <fs/littlefs.h>
#define LFS_MAX_PATH_LEN 255
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
static struct fs_mount_t lfs_storage_mnt = {
.type = FS_LITTLEFS,
.fs_data = &storage,
.storage_dev = (void *)FLASH_AREA_ID(littlefs_storage),
.mnt_point = "/lfs",
};
zephyerのサンプロコードの例(一部変更)
void lfs_test(void)
{
struct fs_mount_t *mp = &lfs_storage_mnt;
unsigned int id = (uintptr_t)mp->storage_dev;
char fname[LFS_MAX_PATH_LEN];
struct fs_statvfs sbuf;
const struct flash_area *pfa;
int rc;
snprintf(fname, sizeof(fname), "%s/boot_count", mp->mnt_point);
rc = flash_area_open(id, &pfa);
if (rc < 0) {
printk("FAIL: unable to find flash area %u: %d\n",
id, rc);
return;
}
printk("Area %u at 0x%x on %s for %u bytes\n",
id, (unsigned int)pfa->fa_off, pfa->fa_dev_name,
(unsigned int)pfa->fa_size);
/* Optional wipe flash contents */
if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) {
printk("Erasing flash area ... ");
rc = flash_area_erase(pfa, 0, pfa->fa_size);
printk("%d\n", rc);
}
flash_area_close(pfa);
rc = fs_mount(mp);
if (rc < 0) {
printk("FAIL: mount id %u at %s: %d\n",
(unsigned int)mp->storage_dev, mp->mnt_point,
rc);
return;
}
printk("%s mount: %d\n", mp->mnt_point, rc);
rc = fs_statvfs(mp->mnt_point, &sbuf);
if (rc < 0) {
printk("FAIL: statvfs: %d\n", rc);
goto out;
}
printk("%s: bsize = %lu ; frsize = %lu ;"
" blocks = %lu ; bfree = %lu\n",
mp->mnt_point,
sbuf.f_bsize, sbuf.f_frsize,
sbuf.f_blocks, sbuf.f_bfree);
struct fs_dirent dirent;
rc = fs_stat(fname, &dirent);
printk("%s stat: %d\n", fname, rc);
if (rc >= 0) {
printk("\tfn '%s' siz %u\n", dirent.name, dirent.size);
}
struct fs_file_t file;
fs_file_t_init(&file);
rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
if (rc < 0) {
printk("FAIL: open %s: %d\n", fname, rc);
goto out;
}
uint32_t boot_count = 0;
if (rc >= 0) {
rc = fs_read(&file, &boot_count, sizeof(boot_count));
printk("%s read count %u: %d\n", fname, boot_count, rc);
rc = fs_seek(&file, 0, FS_SEEK_SET);
printk("%s seek start: %d\n", fname, rc);
}
boot_count += 1;
rc = fs_write(&file, &boot_count, sizeof(boot_count));
printk("%s write new boot count %u: %d\n", fname,
boot_count, rc);
rc = fs_close(&file);
printk("%s close: %d\n", fname, rc);
struct fs_dir_t dir;
fs_dir_t_init(&dir);
rc = fs_opendir(&dir, mp->mnt_point);
printk("%s opendir: %d\n", mp->mnt_point, rc);
while (rc >= 0) {
struct fs_dirent ent = { 0 };
rc = fs_readdir(&dir, &ent);
if (rc < 0) {
break;
}
if (ent.name[0] == 0) {
printk("End of files\n");
break;
}
printk(" %c %u %s\n",
(ent.type == FS_DIR_ENTRY_FILE) ? 'F' : 'D',
ent.size,
ent.name);
}
(void)fs_closedir(&dir);
out:
rc = fs_unmount(mp);
printk("%s unmount: %d\n", mp->mnt_point, rc);
}
ファイルシステムを利用せずにドライバーを直に叩く
ソースコードに下記を追加する。
#include <drivers/flash.h>
#define EXT_FLASH_DEVICE DT_LABEL(DT_INST(0, jedec_spi_nor))
static const struct device *flash_dev;
デバイスは以下で取得できる。
flash_dev = device_get_binding(EXT_FLASH_DEVICE);
読み書きは、 flash_read関数、flash_write関数等で行える。
drivers/flash.h を参照のこと。
モデムファームウエアのフルアップデート用ライブラリは上記関数を利用してデバイスを直接叩いている。