#概要
この記事はESP-WROOM-32の外部SPIフラッシュメモリ(4MB)にアクセスする方法を記述しています。
今回はSPIフラッシュメモリにアクセスするAPIを使用しています。progmemやファイルシステム(SPIFFS)を使用していません。
>>>>>>>>>>>>>>>>>>>>>>>>>>>
2019/4/24追記
ファイルシステム(SPIFFS)を使用したSPIフラッシュメモリアクセスのライブラリを作りました。
次の記事でサンプルコードを公開しています。
「ESP32のSPIFFSサンプルコードをライブラリ化しました」
>>>>>>>>>>>>>>>>>>>>>>>>>>>
作成したスケッチファイルはGitHubで公開しています。ライセンスはフリーです。
外部メモリにアクセスするので処理時間が気になりますが、今回は処理時間の計測を省いて動作のみ行っています。
#開発環境
開発環境:Arduino-IDE(Arduino-ESP32)
使用ボード:ESP-32-DevKit
ライブラリ:esp_spi_flash.h ※1
※1:Arduino-IDEでESP-WROOM-32の開発環境が整っていればデフォルトで用意されています。
ESP-WROOM-32の外部SPIフラッシュメモリについて
ESP-WROOM-32の外部フラッシュメモリは4MBあります。このメモリ空間は用途によって区切られています。(パーティションテーブル)
デフォルトのパーティションテーブル(default.csv)は次のように設定されています。
Name | Type | SubType | Offset | Size |
---|---|---|---|---|
nvs | data | nvs | 0x9000 | 0x5000 |
otadata | data | ota | 0xe000 | 0x2000 |
app0 | app | ota_0 | 0x10000 | 0x140000 |
app1 | app | ota_1 | 0x150000 | 0x140000 |
eeprom | data | 0x99 | 0x290000 | 0x1000 |
spiffs | data | spiffs | 0x291000 | 0x16F000 |
パーティションテーブルの詳しい説明をすると長くなるので、主観での簡単な説明にします。
システムが「nvs」、「otadata」、「app0」、「app1」を使用しています。
「eeprom」はスケッチの中でprogmemを使用すると使われる領域のようです。
「spiffs」はファイルシステムSPIFFSを利用するときの領域です。
今回はパーティションテーブルをデフォルト(default.csv)とし、フラッシュメモリの「spiffs」領域にアクセスしています。
ソースコード
大まかな処理フローは次のとおりです。
1.SPIフラッシュからデータを4kB(1セクタ)読み出す
2.シリアルログに読み出したデータの先頭データを出力
3.シリアルフラッシュの指定アドレスから4kB(1セクタ)を消去
4.読み出したデータをインクリメントして、書き込み用の配列に格納
5.シリアルログに書き込むデータを出力
6.シリアルフラッシュに1byteデータを書き込む
7.1秒wait
#include "esp_spi_flash.h"
#define SPIFFS_BASE_ADDR 0x291000 // SPIFFS領域のベースアドレス
uint32_t chip_size = 0;
uint8_t w_buf[SPI_FLASH_SEC_SIZE];
uint8_t r_buf[SPI_FLASH_SEC_SIZE];
void setup() {
Serial.begin(115200, SERIAL_8N1);
w_buf[0] = 0x00;
}
void loop() {
// SPI flashから読み込み
spi_flash_read(SPIFFS_BASE_ADDR,r_buf,SPI_FLASH_SEC_SIZE);
Serial.printf("Read SPI flash data = %x\n",r_buf[0]);
w_buf[0] = r_buf[0] + 1;
// SPI flashの指定アドレスから指定サイズだけデータを消去する
spi_flash_erase_range(SPIFFS_BASE_ADDR,SPI_FLASH_SEC_SIZE);
// SPI flashへ書き込み
Serial.printf("Write to SPI flash = %x\n\n",w_buf[0]);
spi_flash_write(SPIFFS_BASE_ADDR,w_buf,1);
delay(1000);
}
スケッチファイルをボードに書き込み、シリアルモニタを起動すると次のようなログが表示されます。
読み込むたびにデータがインクリメントされてることが確認できます。
また、電源を落として起動しなおしてみると、前回の続きからデータがインクリメントされていきます。
以下spi_flash APIの説明です。
SPIフラッシュメモリから読み込み
// SPI flashから読み込み
spi_flash_read(SPIFFS_BASE_ADDR,r_buf,SPI_FLASH_SEC_SIZE);
このAPIはSPIフラッシュメモリからRAMに読み込みます。
APIは次のように定義されています。
esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size)
・src_addr
フラッシュメモリ内の読み込みたいデータが格納されている先頭アドレスを指定します。
今回はデフォルトのパーティションテーブルでSPIFFS領域の先頭アドレスを指定しています。
・*dest
フラッシュメモリから読み込んだデータの格納先ポインタを指定します。
今回はuint8_t型配列の先頭アドレスを指定しています。
・size
フラッシュメモリから読み込むデータのサイズを指定します。
今回は1セクタ(4kB)を指定しています。
####SPIフラッシュメモリの内容を削除(フラッシュ)する
// SPI flashの指定アドレスから指定サイズだけデータを消去する
spi_flash_erase_range(SPIFFS_BASE_ADDR,SPI_FLASH_SEC_SIZE);
このAPIはフラッシュメモリの指定アドレスから、指定したサイズのデータを削除(フラッシュ)します。
APIは次のように定義されています。
esp_err_t spi_flash_erase_range(size_t start_address, size_t size)
・start_address
フラッシュメモリ内の削除したいデータが格納されている先頭アドレスを指定します。
今回はspiffs領域の先頭アドレスを指定しています。
・size
削除するデータサイズを指定します。
今回は1セクタ(4kB)を指定しています。
####SPIフラッシュメモリに書き込む
// SPI flashへ書き込み
spi_flash_write(SPIFFS_BASE_ADDR,w_buf,1);
このAPIはフラッシュメモリの指定したアドレスから、指定したサイズだけデータを書き込みます。
APIは次のように定義されています。
esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size)
・dest_addr
フラッシュメモリの書き込み先アドレスを指定します。
今回はspiff領域の先頭アドレスを指定しています。
・*src
書き込むデータが格納されているポインタを指定します。
今回はuint8_t型配列の先頭アドレスを指定しています。
・size
書き込むデータのサイズを指定します。
今回は1byteを指定しています。
#あとがき
SPIフラッシュのアクセスはセクタ単位が便利そうです。spi flash APIの中に
esp_err_t spi_flash_erase_sector(size_t sector)
といった、セクタ単位で処理するAPIを使う事ができるからです。
spi_flash_mmap()というAPIでも外部SPIフラッシュにアクセスできそうです。試していないので動作は分かりませんが、SPIフラッシュメモリから領域を指定して内部メモリ(SRAM)上に展開するようです。
その他にファイルシステム(FATやSPIFFS)を利用した方法もあります。こちらのほうが直感的に扱いやすいかと思いますが、こういった方法もある事が分かりました。
#参考
ESPRESSIF ESP-IDE Programming Guide SPI Flash APIs
https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/spi_flash.html