esp8266 ArduinoのPROGMEM

160903a0HSES-LCD24での日本語表示を試みる際に、PROGMEMを調べた。

ArduinoのPROGMEMとは、データをROM領域に格納する仕組み。(reference,日本語)

C(C++)言語だとconst修飾子を付ければ、十分な気がするが、AVR等の小型のCPUだと、そうはならない。命令用のROMとデータ用のRAMのアクセス経路を分離し、回路を節約しているためだ。ATmegaとかだとROMのデータを読み込むには、専用の機械語があった気がする。

このためCのプログラムでは、起動前の初期化部(crt.Sとか)でROMにある変数の初期値を一旦RAMにコピーし、その後のプログラムでは全てRAM部をアクセスして処理を行う。これはconst宣言してあっても同じだし、”foo”などのような文字列定数であっても同じ。

RAMの容量が足りていれば問題無いが、RAMの使用を節約したい時や、大きなデータを扱いたい時は問題になる。PROGMEMおよび関連のマクロ、関数を使用するとRAMを消費せず、データをプログラムに格納できるようになる。具体的には、ROMに格納されているデータはpgm_read_byte()等でアクセスするようになるのだが、Arduinoでは、多くのライブラリー関数に ROM上のデータ(多くは文字列)用のインターフェースが準備されているので、若干のマクロをかませる程度で使用できる。

でも、こんなのは、小さなCPUを使っているからで、ESP8266ならconstで十分ではなかろうか、と思っていたのだが甘かった。

まず fontxのデータを .rodataセクションに埋め込んでみる。リンク時にエラー。RAM領域が足りないそうだ。そこでPROGMEMの定義を探す。

./hardware/esp8266/2.3.0/cores/esp8266/pgmspace.h:

#define PROGMEM     ICACHE_RODATA_ATTR

./hardware/esp8266/2.3.0/tools/sdk/include/c_types.h:

#define ICACHE_RODATA_ATTR  __attribute__((section(".irom.text")))

埋込先を .irom.textに変更し、リンクできた。プログラムを実行させると例外が発生。いつものWatchDogTimerかと思ったがそうではない。いろいろ試すと、ROMデータをアクセスするところで発生している。

どうすりゃ良いの?と、いろいろ調べて以下の行を発見。

./hardware/esp8266/2.3.0/cores/esp8266/pgmspace.h

// flash memory must be read using 32 bit aligned addresses else a processor
// exception will be triggered
// order within the 32 bit values are
// --------------
// b3, b2, b1, b0
//     w1,     w0

flashメモリーは32bit-alignでないアクセスでないと例外が発生する!? なるほど、そこをケチったか。だったら例外のハンドラを準備すれば対応できそう..とも思うが大変そうなので、pgm_read_byte()を使って、漢字をLCDに表示できるようになった。

プログラムは、整理して、公開したいと思う。うまくAdafruit_GFXに統合できるか?

Leave a Reply

メールアドレスが公開されることはありません。