Advanced 4: A Minimal Filesystem — LittleFS#
Sample code#
Directory:
docs/os-on-arduino/code/adv4_fs
uv run pio run -d adv4_fs -t upload
uv run pio device monitor -b 115200
Output starts when you send the single character S.
Source code#
The sketch for this chapter. Click a file name to expand. The code is mirrored automatically from the manuscript repository, so what you see here is always current.
src/main.cpp — 97 lines
1/**
2 * 応用編 第4章: 最小ファイルシステム — Flash 上に LittleFS を載せる
3 *
4 * RA4M1 内蔵の Data Flash (8 KB) を LittleFS のブロックデバイスとして使う。
5 *
6 * 起動するたびにカウンタを 1 増やして書き戻すので、電源を入れ直しても
7 * 値が残っていることで永続化を確認できる。4.2 で説明した電源断耐性
8 * (LittleFS のコピーオンライト)が効いていることの一番簡単な確かめ方。
9 *
10 * 注意: Data Flash の書き換え回数は 1 万〜10 万回程度。デモとはいえ
11 * リセットを繰り返しすぎないこと。
12 */
13
14#include <Arduino.h>
15#include "fs.h"
16#include "fs_flash_hal.h"
17
18static void print_err(const char *what, int err)
19{
20 Serial.print(F("[fs] "));
21 Serial.print(what);
22 Serial.print(F(" failed: "));
23 Serial.println(err);
24}
25
26// 起動回数を /boot_count に読み書きする
27static void demo_boot_count(lfs_t *lfs)
28{
29 lfs_file_t f;
30 uint32_t count = 0;
31
32 int err = lfs_file_open(lfs, &f, "/boot_count", LFS_O_RDWR | LFS_O_CREAT);
33 if (err) { print_err("open", err); return; }
34
35 lfs_file_read(lfs, &f, &count, sizeof(count));
36 count++;
37 lfs_file_rewind(lfs, &f);
38 lfs_file_write(lfs, &f, &count, sizeof(count));
39 lfs_file_close(lfs, &f); // close で初めて Flash へ確定する
40
41 Serial.print(F("boot count = "));
42 Serial.println(count);
43}
44
45// ルートディレクトリの一覧(シェルの ls 相当)
46static void demo_ls(lfs_t *lfs)
47{
48 lfs_dir_t dir;
49 struct lfs_info info;
50
51 int err = lfs_dir_open(lfs, &dir, "/");
52 if (err) { print_err("dir_open", err); return; }
53
54 Serial.println(F("--- / ---"));
55 while (lfs_dir_read(lfs, &dir, &info) > 0) {
56 if (info.type == LFS_TYPE_DIR) {
57 Serial.print(F(" [DIR ] "));
58 Serial.println(info.name);
59 } else {
60 Serial.print(F(" [FILE] "));
61 Serial.print(info.name);
62 Serial.print(F(" "));
63 Serial.print(info.size);
64 Serial.println(F(" B"));
65 }
66 }
67 lfs_dir_close(lfs, &dir);
68}
69
70void setup()
71{
72 Serial.begin(115200);
73 while (!Serial) { }
74 while (Serial.read() != 'S') { delay(1); }
75
76 Serial.println(F("=== Advanced 4: LittleFS on Data Flash ==="));
77 Serial.print(F("data flash: "));
78 Serial.print(FLASH_DATA_SIZE);
79 Serial.print(F(" B / sector "));
80 Serial.print(FLASH_SECTOR_SIZE);
81 Serial.println(F(" B"));
82
83 int err = fs_init();
84 if (err) {
85 print_err("mount", err);
86 while (1) { delay(1000); }
87 }
88 Serial.println(F("mounted."));
89
90 lfs_t *lfs = fs_get();
91 demo_boot_count(lfs);
92 demo_ls(lfs);
93
94 Serial.println(F("done. リセットすると boot count が増えます"));
95}
96
97void loop() { }
include/fs.h — 18 lines
1// fs.h
2//
3// 本書 応用編 第4章。LittleFS を Data Flash の上にマウントする薄いラッパ。
4
5#pragma once
6
7#include "lfs.h"
8
9#ifdef __cplusplus
10extern "C" {
11#endif
12
13int fs_init(void); // mount。失敗したら format してから再 mount
14lfs_t *fs_get(void);
15
16#ifdef __cplusplus
17}
18#endif
include/fs_flash_hal.h — 19 lines
1// fs_flash_hal.h
2
3#ifndef FS_FLASH_HAL_H
4#define FS_FLASH_HAL_H
5
6#include <stdint.h>
7#include <stddef.h>
8
9#define FLASH_DATA_BASE 0x40100000UL // Data Flash の先頭物理アドレス
10#define FLASH_DATA_SIZE (8 * 1024) // 8 KB
11#define FLASH_SECTOR_SIZE 1024 // 1 KB = Data Flash の消去ブロック = LittleFS の block_size
12
13int flash_hal_init(void);
14int flash_hal_read (uint32_t offset, void *buf, size_t len);
15int flash_hal_prog (uint32_t offset, const void *buf, size_t len);
16int flash_hal_erase(uint32_t offset); // セクタ消去
17int flash_hal_sync (void);
18
19#endif
src/fs.cpp — 70 lines
1// fs.c
2
3#include <Arduino.h>
4#include "lfs.h"
5#include "fs.h"
6#include "fs_flash_hal.h"
7
8static lfs_t g_lfs;
9static struct lfs_config g_cfg;
10
11static uint8_t g_read_buf[16];
12static uint8_t g_prog_buf[16];
13static uint8_t g_lookahead_buf[16];
14
15// --- LittleFS から呼ばれる callback ---
16
17static int blk_read(const struct lfs_config *c, lfs_block_t block,
18 lfs_off_t off, void *buf, lfs_size_t size) {
19 (void)c;
20 return flash_hal_read(block * FLASH_SECTOR_SIZE + off, buf, size);
21}
22
23static int blk_prog(const struct lfs_config *c, lfs_block_t block,
24 lfs_off_t off, const void *buf, lfs_size_t size) {
25 (void)c;
26 return flash_hal_prog(block * FLASH_SECTOR_SIZE + off, buf, size);
27}
28
29static int blk_erase(const struct lfs_config *c, lfs_block_t block) {
30 (void)c;
31 return flash_hal_erase(block * FLASH_SECTOR_SIZE);
32}
33
34static int blk_sync(const struct lfs_config *c) {
35 (void)c;
36 return flash_hal_sync();
37}
38
39// --- 初期化 ---
40
41int fs_init(void) {
42 flash_hal_init();
43
44 g_cfg.read = blk_read;
45 g_cfg.prog = blk_prog;
46 g_cfg.erase = blk_erase;
47 g_cfg.sync = blk_sync;
48
49 g_cfg.read_size = 4;
50 g_cfg.prog_size = 4;
51 g_cfg.block_size = FLASH_SECTOR_SIZE; // 1 KB
52 g_cfg.block_count = FLASH_DATA_SIZE / FLASH_SECTOR_SIZE; // 8
53 g_cfg.cache_size = 16;
54 g_cfg.lookahead_size = 16;
55 g_cfg.block_cycles = 500;
56
57 g_cfg.read_buffer = g_read_buf;
58 g_cfg.prog_buffer = g_prog_buf;
59 g_cfg.lookahead_buffer = g_lookahead_buf;
60
61 // 既存 FS を mount。失敗したら新規 format してから再 mount
62 int err = lfs_mount(&g_lfs, &g_cfg);
63 if (err) {
64 lfs_format(&g_lfs, &g_cfg);
65 err = lfs_mount(&g_lfs, &g_cfg);
66 }
67 return err;
68}
69
70lfs_t *fs_get(void) { return &g_lfs; }
src/fs_flash_hal.cpp — 53 lines
1// fs_flash_hal.c
2// RA4M1 の Data Flash (8KB) を FSP の R_FLASH_LP ドライバ経由で読み書きする。
3
4#include <Arduino.h>
5#include "fs_flash_hal.h"
6#include "r_flash_lp.h" // FSP のドライバ
7
8static flash_lp_instance_ctrl_t g_ctrl;
9static flash_cfg_t g_cfg;
10
11int flash_hal_init(void) {
12 g_cfg.data_flash_bgo = false; // foreground (同期) 動作
13 g_cfg.p_callback = NULL;
14 g_cfg.p_context = NULL;
15 g_cfg.ipl = 0;
16 g_cfg.irq = FSP_INVALID_VECTOR;
17 g_cfg.err_irq = FSP_INVALID_VECTOR; // 本文では省略されていた
18 g_cfg.err_ipl = 0;
19
20 if (R_FLASH_LP_Open(&g_ctrl, &g_cfg) != FSP_SUCCESS) {
21 return -1;
22 }
23 return 0;
24}
25
26int flash_hal_read(uint32_t offset, void *buf, size_t len) {
27 // Data Flash はメモリマップされているので、直接ロードで読める
28 const uint8_t *src = (const uint8_t *)(FLASH_DATA_BASE + offset);
29 uint8_t *dst = (uint8_t *)buf;
30 for (size_t i = 0; i < len; i++) dst[i] = src[i];
31 return 0;
32}
33
34int flash_hal_prog(uint32_t offset, const void *buf, size_t len) {
35 fsp_err_t err = R_FLASH_LP_Write(&g_ctrl,
36 (uint32_t)buf,
37 FLASH_DATA_BASE + offset,
38 len);
39 return (err == FSP_SUCCESS) ? 0 : -1;
40}
41
42int flash_hal_erase(uint32_t offset) {
43 // offset は FLASH_SECTOR_SIZE の倍数であることが前提
44 fsp_err_t err = R_FLASH_LP_Erase(&g_ctrl,
45 FLASH_DATA_BASE + offset,
46 1); // 1 セクタ
47 return (err == FSP_SUCCESS) ? 0 : -1;
48}
49
50int flash_hal_sync(void) {
51 // foreground モードでは書き込みが完了してから戻るので何もしなくてよい
52 return 0;
53}
On the board#
Recorded while pressing RESET six times. boot count goes 9, 10, 11 … with each press. A RAM variable would go back to 0, so this shows it is stored in Data Flash.#
Pitfalls#
(Collected from reader questions — ask via GitHub Issues.)