こんにちは、shotaです。
このシリーズではESP32を搭載したオリジナルマイクロマウスEspecialを製作します。
前回の記事ではESP32のアドレスマッピングについて書きました。
今回はESP32のファイルシステムについて書きます。
記事の最後では、ESP-IDFのサンプルプログラムを実行します。
フラッシュメモリのアドレスについて
前回のブログ記事で、ESP32モジュール内にはESP32チップと、容量4 MB以上のフラッシュメモリが内蔵されていることを話しました。
ESP32チップから見ると、このフラッシュメモリは「外部フラッシュメモリ」です。
今回の記事では、ファイルシステムを使って、外部フラッシュメモリにファイルを保存する方法を説明します。
ESP32チップのアドレスマッピングとフラッシュメモリのアドレスの違い
前回のアドレスマッピングの話で、外部フラッシュメモリには4 MBのアドレス領域(0x3F40_0000 ~ 0x3F7F_FFFF)が割り当てられていることが分かりました。
このアドレスは、実際のフラッシュメモリ内部のアドレスと異なります。
フラッシュメモリの内部にもアドレスマッピングがあります。
4 MBの容量から推測すると、0x0000_0000 ~ 0x003F_FFFFのアドレスを使用してると思います。
少なくともアドレスの始まりは0x3F40_0000では無いです。
以降の説明でアドレス0x0000や0x10000が登場しますが、これはフラッシュメモリの内部のアドレスであることに注意してください。
パーティションテーブル
ESP32では、フラッシュメモリの内部をパーティションと呼ばれる領域に分けて管理しています。
各パーティションの名前、種類、サイズ、オフセットアドレスをまとめたものがパーティションテーブルです。
パーティションテーブルについてはESP32の公式ドキュメントで詳しく説明されています。
ESP32で扱うパーティションテーブルは、次のようなCSVファイルで設定されます。
# Espressif ESP32 Partition Table # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 1M,
※https://docs.espressif.com/projects/esp-idf/en/stable/api-guides/partition-tables.htmlから引用
パーティションテーブルを見るのは初めてですが、
ESP32にプログラムを書き込む時、その裏側でパーティションテーブルも書き込まれているので、
実は普段から使用していたのです。
パーティションテーブルにはName、Type、SubType、Offset、Sizeという項目があります。
上記の例ではNameがnvs、phy_init、factoryとなる、3つのパーティションが設定されています。
各パーティションのうち、nvs、phy_initについては説明を省略します。
factoryは重要なパーティションです。
factoryパーティションに、私が(あなたが)作成したESP32のプログラム(app)が書き込まれます。
factoryパーティションのOffsetは0x10000で、Sizeが1Mとなっています。
つまりフラッシュメモリのアドレス領域0x10000 ~ 0x110000が作成したプログラム(app)のために確保されているのです。
上の文字だけ見ても理解しにくいので表を作りました。
参考にしたページはGeneral Notes About ESP-IDF ProgrammingとPartition Tablesです。
ここで重要なのは、factoryパーティションまではほぼ中身が決まっている、ということです。
後ほどファイルシステムのサンプルプログラムを実行しますが、factoryパーティション以降の空きスペースに、新しいパーティションを作るということを覚えておいてください。
ファイルシステムのサンプルプログラムを実行する・・・前に
さて、ようやくファイルシステムのお話です。
さっそくサンプルプログラムを実行しましょう!
と進みたいところですが「ファイルシステムって何?」、ってなりますよね。
ファイルシステムとは
簡単に言うと、ファイルシステムは、パーティションテーブルやアドレスを意識せずにファイルを読み書きできる仕組みです。
さきほどの説明で、フラッシュメモリの空きスペースは2 MBくらいあると分かりました。
ということは、容量数KBの小さいファイルであれば、何百個も保存できそうです。
ファイルシステムが無い場合、どのようにファイルを保存するのか想像してみましょう。
容量4 KBのファイルA.txtがフラッシュメモリのアドレス0x0011_0000~に保存されていると想定します。
ここに容量8 KBのファイルB.txtを保存します。ファイルA.txtの続きのアドレス0x0011_1000から、8 KBの領域を確保して完了です。
続いて、20 KBのファイルC.txtをA.txtに上書き保存します。A.txtはアドレス0x0011_0000に保存されているので、そこから20 KB分確保しますが・・・
0x0011_1000にはB.txtが保存されているので、このままではB.txtにも上書きされてしまいます。
となると、C.txtはB.txtの続きに保存しなくてはいけません。B.txtの続きの0x0011_3000から20 KB分確保し、、、
あっ、A.txtに上書き保存だからA.txtも消さないといけませんね!最終的どうなるかというと・・・
面倒ですね!!!この調子で何百個のファイルを人力で管理するのはとても大変です。
この面倒な作業をいい感じに処理してくれるのがファイルシステムです。
ESP32のファイルシステム
ESP32では、SPIFFSとFatFsの2種類のファイルシステムが使えます。
それぞれの特徴については下記ドキュメントを参照してください。
SPIFFSとFatFsのどちらを使うべきかは分かりませんが、
SPIFFSのドキュメントに、SPIFFSはディレクトリに対応していない、書き込みに時間がかかる、不良ブロックを見つけられない(処理できない)
という特徴が書かれているので、FatFsを使用したほうが良さそうです。
今回使うWear LevellingサンプルでもFatFsファイルシステムが使用されています。
Wear Levellingって何?
Wear Levelling(ウェアレベリング)も、重要なコンピュータ用語の1つです。
ウェアレベリング (wear levelling / wear leveling) とは、コンピュータの記憶媒体に用いられる技術の1つ。書き換え限度回数が存在する媒体において、その使用寿命を延ばすための手法。
引用元:https://ja.wikipedia.org/wiki/ウェアレベリング
と書かれています。
フラッシュメモリには寿命があります。
無限に読み書きできるのではなく、読み書き回数に限りがあります(数万回や数十万回くらい)。
ESP32のフラッシュメモリの寿命は分かりませんが、
下記のフォーラムでは10万回と噂されているようです。
(フラッシュメモリの型番はGD25Q32Cだ、とも噂されています)
10万回はとても大きな数字のように見えますが、
同じアドレスに対して何回もデータを読み書きすれば、いずれは寿命に到達します。
(毎秒データを読み書きすれば1年以内には寿命に到達します)
ESP32のウェアレベリングでは、データの保存場所を分散して(同じアドレスに読み書きし続けないようにして)寿命を伸ばしているようです。
フラッシュメモリのサンプルプログラムを実行する
おまたせしました!ようやくサンプルプログラムの実行です。
下記のWear Levellingサンプルを実行します。
まずはいつもどおりサンプルプロジェクトをコピーして、実行してみましょう。
$ get_idf $ cd ~/esp $ cp -r esp-idf/examples/storage/wear_levelling . $ cd wear_levelling $ idf.py -p /dev/ttyUSB0 flash monitor
実行すると次のようにログが表示されます。
--- 省略 --- I (54) boot: SPI Flash Size : 4MB I (58) boot: Partition Table: I (61) boot: ## Label Usage Type ST Offset Length I (68) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (76) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (83) boot: 2 factory factory app 00 00 00010000 00100000 I (91) boot: 3 storage Unknown data 01 81 00110000 00100000 I (98) boot: End of partition table --- 省略 ---
このログを見ると、ESP32に書き込まれたパーティションテーブルがわかります。
storageというパーティションが、アドレス0x0011_0000から容量1 MBだけ確保されていることが分かります。
ログの続きを見てみます。
--- 省略 --- I (317) example: Mounting FAT filesystem I (327) example: Opening file I (817) example: File written I (817) example: Reading file I (817) example: Read from file: 'written using ESP-IDF v4.0.1' I (817) example: Unmounting FAT filesystem I (1017) example: Done
FATファイルシステムを使い、何かしらのテキストファイルを保存し、
そのテキストファイルの中身(‘written using ESP-IDF v4.0.1’)を読み出し、出力しています。
これだけだとファイルシステムを扱った実感が湧かないので、プログラムを編集してみましょう。
サンプルプログラムを編集する
まずはパーティションテーブルを編集します。
$ cd ~/esp/wear_levelling $ vim partitions_example.csv
storageパーティションのサイズを、半分の512KB(0x80000)よりちょっと多い0x90000にしてみましょう。
# Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 1M, storage, data, fat, , 0x90000,
プログラムのソースコードも書き換えます。
$ vim main/wear_levelling_example_main.c
ファイルに書き込む文字列を変更してみましょう。
ESP_LOGI(TAG, "Opening file"); FILE *f = fopen("/spiflash/hello.txt", "wb"); if (f == NULL) { ESP_LOGE(TAG, "Failed to open file for writing"); return; } fprintf(f, "Hello File System! written using ESP-IDF %s\n", esp_get_idf_version()); fclose(f); ESP_LOGI(TAG, "File written");
実行しましょう。
$ idf.py -p /dev/ttyUSB0 flash monitor --- 省略 --- I (57) boot: Partition Table: I (61) boot: ## Label Usage Type ST Offset Length I (68) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (76) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (83) boot: 2 factory factory app 00 00 00010000 00100000 I (91) boot: 3 storage Unknown data 01 81 00110000 00090000 --- 省略 --- I (807) example: File written I (807) example: Reading file I (807) example: Read from file: 'Hello File System! written using ESP-IDF v4.0.1' --- 省略 ---
パーティションのサイズとテキストファイルの中身が書き換わりました。
まとめ
今回説明したことのまとめです。
- ESP32のパーティションテーブルはCSVファイルで設定する
- ESP32ではSPIFFSとFatFsのファイルシステムが使える
- Wear Levellingを使えばフラッシュメモリの寿命を伸ばせる
次回は、私のオリジナルマイクロマウスEspecialで、ファイルシステムを使います。