マイコン向け外部Flashメモリに効率的なファイルシステムを実装する方法:理論から実践まで

目次

1.LittleFSファイルシステム

1.1 ファイルシステム概要

2 LittleFSのマイコンへの移植

2.1 ソースコード追加

2.2 インターフェース関数編集

2.3 テストコード

1.LittleFSファイルシステム

1.1 ファイルシステム概要

LittleFSソースコードダウンロード先:littlefs-project/littlefs: A little fail-safe filesystem designed for microcontrollers (github.com)

LittleFSはARM社が公式リリースした、ARM mbedOSの推奨ファイルシステムであり、軽量性と停電耐性を特徴としています。主にマイクロコントローラとフラッシュメモリで使用され、以下の特徴があります:

停電回復機能:書き込み中にリセットや停電が発生しても直前の正常な状態に戻ることが可能
消去回数の均等化:フラッシュメモリの寿命を延ばします。例えばW25QXXシリーズのSPIインターフェースを持つフラッシュメモリは消去回数が約10万回ですが、頻繁な操作を行うとこの限界にすぐに達してチップが破損する可能性があります。
制限されたRAM/ROM:FATFSに比べてROMとRAMの消費を削減できます

欠点:Windowsとの互換性がない

2. LittleFSのマイコンへの移植

2.1 ソースコード追加

Githubからソースコードをダウンロード後、プロジェクト内に配置します:

次に、インターフェース関数を格納するためのCファイルを作成します:

筆者は動的メモリ割り当て方式を採用しており、同様の方法を使用したい場合は、マイコン上でmalloc()とfree()関数がサポートされているかを確認する簡単なテストプログラムを作成する必要があります。

2.2 インターフェース関数編集

LittleFS公式ドキュメントREADME.mdを参照すると、以下のようなサンプルコードが提供されています:

#include "lfs.h"

// ファイルシステムで使用される変数
lfs_t fs_handle;
lfs_file_t data_file;

// ファイルシステム構成はこの構造体で提供されます
const struct lfs_config fs_config = {
    // ブロックデバイス操作
    .read  = flash_read_operation,
    .prog  = flash_program_operation,
    .erase = flash_erase_operation,
    .sync  = flash_sync_operation,

    // ブロックデバイス構成
    .read_size = 16,
    .prog_size = 16,
    .block_size = 4096,
    .block_count = 128,
    .cache_size = 16,
    .lookahead_size = 16,
    .block_cycles = 500,
};

// エントリポイント
int main(void) {
    // ファイルシステムマウント
    int result = lfs_mount(&fs_handle, &fs_config);

    // マウントできない場合に再フォーマット
    // 最初の起動時のみ発生すべきです
    if (result) {
        lfs_format(&fs_handle, &fs_config);
        lfs_mount(&fs_handle, &fs_config);
    }

    // 現在のカウント読み取り
    uint32_t startup_counter = 0;
    lfs_file_open(&fs_handle, &data_file, "startup_counter", LFS_O_RDWR | LFS_O_CREAT);
    lfs_file_read(&fs_handle, &data_file, &startup_counter, sizeof(startup_counter));

    // 起動カウント更新
    startup_counter += 1;
    lfs_file_rewind(&fs_handle, &data_file);
    lfs_file_write(&fs_handle, &data_file, &startup_counter, sizeof(startup_counter));

    // ファイルが正常に閉じられるまでストレージは更新されないことを覚えておいてください
    lfs_file_close(&fs_handle, &data_file);

    // 使用していたリソースを解放
    lfs_unmount(&fs_handle);

    // 起動カウント表示
    printf("startup_counter: %d\n", startup_counter);
}

struct lfs_config構造体を設定し、自身のSPI Flashに関する情報を記述する必要があります。筆者の使用しているSPI Flash型番はW25QJVSSIQで、以下のように構成しています(関数として記述):

インターフェース関数は主にcfg構造体内のread、prog、erase、syncの4つの関数ポインタであり、これらを実際のFLASH操作関数に割り当てればOKです。例:

2.3 テストコード

完全なテストコードは以下の通りです:

void filesystem_test()
{
	fs_handle_t 				fs_handle;
	data_file_t 				data_file;

	const char					*test_message = "Hello, LittleFS!";
	char 						receive_buffer[100];
	lfs_ssize_t 				actual_read_size;
	int							mount_result;
	uint32_t					startup_count = 0;
	/* LittleFSファイルシステム構成 */
	initialize_fs_config();
	HAL_Delay(10);
	/* LittleFSファイルシステムマウント */

	printf("Starting filesystem_test\n");
	HAL_Delay(10);
#ifdef ENABLE_FS_DEBUG
	debug_output("Mounting file system...\n");
#endif
	mount_result = lfs_mount(&fs_handle, &fs_config);
	if( mount_result )
	{
		SPI_Flash_ChipErase();
		printf("start to format...\r\n");
		mount_result = lfs_format( &fs_handle, &fs_config );
		if( mount_result )
		{
            printf("lfs_format error: %d\r\n", mount_result);
            return ;
		}
		printf("format successful\n");
		mount_result = lfs_mount(&fs_handle, &fs_config);
		if (mount_result)
		{
			printf("lfs_mount error: %d\r\n", mount_result);
		    return;
		}
	}
#ifdef ENABLE_FS_DEBUG
	debug_output("the first period okay\r\n");
#endif
	/* 現在のカウント読み取り */
#ifdef ENABLE_FS_DEBUG
	debug_output("Opening file...\n");
#endif
	mount_result = lfs_file_open( &fs_handle, &data_file, "startup_counter", LFS_O_RDWR | LFS_O_CREAT);
	if ( mount_result )
	{
	    printf("lfs_file_open error: %d\r\n", mount_result);
	    lfs_unmount(&fs_handle);
	    return;
	}
#ifdef ENABLE_FS_DEBUG
	debug_output("File opened successfully\r\n");
#endif
	/* 読み取りと自動カウント開始 */
#ifdef ENABLE_FS_DEBUG
	debug_output("Reading startup count...\r\n");
#endif
	actual_read_size = lfs_file_read( &fs_handle, &data_file, &startup_count, sizeof(startup_count) );
	if( actual_read_size < 0 )
	{
		printf("lfs_file_read error :%d\r\n", actual_read_size);
		lfs_file_close(&fs_handle, &data_file);
		lfs_unmount(&fs_handle);
		return;
	}
	else if( actual_read_size == 0 )
	{
		startup_count = 0;
	}
#ifdef ENABLE_FS_DEBUG
	debug_output("Startup count read successfully: %d\r\n", startup_count);
#endif
	startup_count += 1;
#ifdef ENABLE_FS_DEBUG
	debug_output("Rewinding file...\r\n");
#endif
	mount_result = lfs_file_rewind( &fs_handle, &data_file);
	if (mount_result)
	{
		printf("lfs_file_rewind error: %d\r\n", mount_result);
		lfs_file_close(&fs_handle, &data_file);
		lfs_unmount(&fs_handle);
		return;
	}
#ifdef ENABLE_FS_DEBUG
	debug_output("File rewinded successfully\r\n");
	debug_output("Writing new startup count...\r\n");
#endif
	mount_result = lfs_file_write( &fs_handle, &data_file, &startup_count, sizeof(startup_count) );
	if (mount_result < 0 )
	{
		printf("lfs_file_write error: %d\r\n", mount_result);
		lfs_file_close(&fs_handle, &data_file);
		lfs_unmount(&fs_handle);
		return;
	}
#ifdef ENABLE_FS_DEBUG
	debug_output("Closing file...\r\n");
#endif
	mount_result = lfs_file_close(&fs_handle, &data_file);
	if (mount_result < 0)
	{
		printf("lfs_file_write error: %d\r\n", mount_result);
		return;
	}
#ifdef ENABLE_FS_DEBUG
	debug_output("File closed successfully\r\n");
	debug_output("Unmounting file system...\r\n");
#endif
	lfs_unmount(&fs_handle);
#ifdef ENABLE_FS_DEBUG
	debug_output("File system unmounted successfully.\r\n");
#endif
	printf("startup_count:%d\r\n", startup_count);
	HAL_Delay(100);

}

最後にmain関数でvoid test()関数を呼び出します。ファイルシステムが正常にインストールされた後、リセットボタンを押すごとにboot_countが1ずつ増加することが確認できます。

タグ: littlefs Embedded Systems microcontroller flash memory file system

6月1日 06:27 投稿