ESP32マイコンはWi-FiやBluetoothを搭載していてとても多機能です。以前購入したM5Core2(実際はCore2 for AWS)にもこのESP32が搭載されています。また、秋月電子のESP32-WROOM-32Eボードにもほぼ同様のCPUが搭載されています。
したがってESP32-WROOM-32Eで出来ることはCore2でも出来るので、ESP32-WROOM-32Eで検証やプログラムのデバッグをしてからCore2で動かすといったことが可能です。でも、そのためにはESP32-WROOM-32EにCore2と同等の320×240のディスプレイをつなげた方が便利ですよね。
そこでILI9341のTFT液晶ディスプレイモジュールを購入しようと考えていたのですが、ひょんなことからESP32はNTSC出力が行えることを知りました。よく調べてみると、ESP32シリーズではおなじみのグラフィックライブラリLovyanGFXでもコンポジット出力をサポートしているようです。そこで、実用的に使えるのか試してみたくなりました。
まずは、LovyanGFXのインストールです。Arduino IDEのライブラリマネージャーの検索ボックスに"LovyanGFX"と入力すればすぐにライブラリが見つかるので、インストールボタンを押すだけです。コンポジット出力についてはここに詳しく書いてあります。
他にM5Stack社の公式ライブラリでLovyanGFXをベースにしたM5GFXというライブラリがあります。たぶんこれもコンポジット出力が出来るはずですが、ESP32-WROOM-32EはM5Stack社の製品ではないので本家LovyanGFXを使うことにしました。
ちなみにM5Stack社の全ボード共通に使えるライブラリM5Unifiedというものがあって、このライブラリもM5GFXを利用するようです。M5Unifiedはまだ未確認ですが、いずれ詳しく調べてみようと思います。
話をコンポジット出力に戻しますが、ビデオモニターと接続するケーブルは簡単に作れます。デフォルトでESP32のGPIO26ピンから映像信号が出力されるので、これをRCAケーブル(オス)の映像端子につなぎ、RCAケーブルのGND側をESP32のGNDにつなぎます。あとはこのRCAケーブルをビデをモニターにつなぐだけです。
ビデオモニターはハードオフで購入したジャンク品を使いました。
コンポジット出力のサンプルプログラムのパラメーターを変えながら試してみたところ、高解像度の出力はPSRAMの無いESP32-WROOM-32Eでは無理ですが320×240程度なら問題なく表示できました。使用したジャンクモニターではちらつきもほとんど感じず綺麗に表示できました。ビデオモニターによって多少修正が必要かもしれませんが、設定値については下記のスケッチを参照してください。
表示については問題なさそうだったので、LovyanGFXのサンプルをNTSC-J出力に書き換えて動かしてみました。
どれもきれいに表示され、アニメーションは高速に動作しました。これなら実用的に使えそうです。
ついでに自分でもプログラムを作成して試してみることにしました。まずは24ビットフルカラーの画像表示です。
fullcolor.zip (クリックしてウィンドウが開いたら右上のダウンロードボタンを押してください)
モニターのアスペクト比が16:9なので4:3の画像は横に伸びます。
コンポジット出力ではESP32のRAMを多めに使うそうなので、24ビットの表示はどうかなと思ってみたのですが表示されました。しかし、よく見るとフルカラー出力ではなさそうなクオリティです。ためしに16ビットカラーモードにして表示してみたら同じクオリティでした。24ビットに指定しても16ビットになるようです。
このスケッチをM5Core2に移植するのはとても簡単なので試してみました。
fullcolor_core2.zip(クリックしてウィンドウが開いたら右上のダウンロードボタンを押してください)
ちゃんとフルカラーで綺麗に表示されました。
次はスプライト機能を使ってみます。背景画像を表示してその前をキャラクターがアニメーションしながら移動するだけの簡単なものです。ただ、そのためには背景とアニメーション用の画像を数枚読み込まなければなりません。RAMの使用量を抑えるために画像は256色の8ビットカラーにします。
複数の画像データをどこに置くのかということ考えなければなりません。普通は上記のスケッチのように配列の初期化データとしてプログラムに組み込んでしまい、フラッシュメモリにプログラムとともに書き込んで利用することになるでしょう。でも、別の方法もあります。
ESP32はフラッシュメモリをパーティション分けして、プログラム書き込み用とファイル書き込み用に分けて使えます。内部にフラッシュメモリディスクが入っているのと同じです。ESP32-WROOM-32Eには(M5Core2も)フラッシュメモリが16Mあるのでこれは利用してみたいですね。
そこで画像データをこのフラッシュメモリに書き込んでおいて、表示するときに読み込んで利用することにします。
まずはフラッシュメモリのパーティション分けをします。そのためにはパーティションテーブルを記述したファイルを作成しなければならないのですが、Arduino IDEにはすでにいくつかのパーティション設定がプリセットされています。"ツール"メニューのボート設定の中の"Paetition Scheme:"の部分です。
今回は"16M Flash (2MB APP/12.5MB FATFS)"を使います。さらに"Flash Size:"の項目も"16MB (128Mb)"にしておく必要があります。
フラッシュディスクで使うファイルシステムについてネットでは簡易的なファイルシステムのSPIFFSの使用例が目につきますが、大容量なのでより高速に読み込めるFATの方がいいでしょう。ちなみにSPIFFSで大容量のフラッシュを使いたいときは、プリセットに無いので自分でパーティションテーブルを記述する必要があります。
余談ですがOTAというWi-Fiを使ったプログラム書き込みを使用しないのであれば、上記のパーティション設定で更に2Mの領域を利用することが出来ます。上記では2MB APPとなっていますが、実際にはもう1つ2MBの領域が確保されています。OTAではプログラム書き込みのたびに、この2つの領域を交互に使います。OTAを使わないのであれば自分でパーティションテーブルを書き換えて、この領域をプログラムもしくはファイルシステムで使うことが出来ます。
それではまず、複数の画像ファイルを内部のフラッシュディスクに書き込むスケッチから。
ESP32-WROOM-32E: image_write.zip
M5Core2: image_write_core2.zip
元となる画像は256色でBMP形式のものをテキストに変換してヘッダファイルに組み込みました。最初はFAT領域がフォーマットされていないはずなので、実行すると"FATFSをマウントできません"と表示して停止します。
この時点でFATパーティションのフォーマットが終了しています。ここでESP32-WROOM-32EをリセットするとFATFSをマウントして画像ファイルの書き込みを行います。完了すると書き込まれたファイル名とサイズ、FAT領域の空き容量が表示されます。
画像ファイル書き込みが終了した後は、再度プログラムを実行しても書き込み作業は行いません。これはM5Core2向けの対策で、スケッチ書き込み時にCore2のリセットが頻繁に発生するからです。何もしないとフラッシュへの書き込みが何度も行われてしまいますし、フラッシュ書き込み時にリセットがかかってファイルを破壊したりするからです。ESP32-WROOM-32Eではこのような問題は発生しませんが、一応Core2と同じ処理をするようになっています。
続いてFAT領域に書き込んだ画像ファイルを読み込んで表示するスケッチです。
ESP32-WROOM-32E image_read.zip
M5Core2: image_read_core2.zip
背景画像は直接フレームバッファに書き込み、アニメーションさせる画像ファイルはスプライトに書き込みます。本当は背景画像もスプライトに書き込んでスプライト同士で画像を合成してからフレームバッファに書き込みたかったのですが(この方が画面がちらつかないかも)、ESP32のRAM容量が足りないのか320×240のサイズのスプライトは作成できませんでした。(エラーにはなりませんが何も表示されない)
画像データをプロクラムに組み込んでフラッシュメモリに書き込んでおき、それをスプライトに見せるという機能もありますが、このスプライトは読み出し専用なので画像の書き込み先には使えません。結局キャラクターデータと別々にフレームバッファに書き込まなければならないので今回は採用しませんでした。
ところでスプライトとはいいますが、一般的なスプライトとは違って重ね合わされた先の画像は消されてしまいます。RAM上の画像データバッファくらいにとらえた方がいいかもしれません。
したがって背景画像がある場合は、スプライト表示のあとに消された背景の復元作業も必要になります。今回は描画先の画像領域を保存しておき、キャラクター表示後に上書きして消すという作業を行っています。
ESP32-WROOM-32EによるNTSC-J出力ではアニメーションの書き換えによるちらつきもなく綺麗に動いていました。
ところがM5Core2の方では書き換え時のちらつきが気になります。スケッチではキャラクターの移動を1ドットと細かい単位で行っていたので、これを8ドット単位に変更してみるとちらつきは目立たなくなりました。
LovyanGFXのおかげでESP32-WROOM-32E用にわざわざ液晶ディスプレイを買う必要がなくなりました。むしろコンポジット出力の方が書き換えのちらつきが目立たず、アニメーションの表示に向いているような気がします。
NTSC出力に使用しているGPIO26ピンはCore2 for AWSのポートBにも出力されているので、ケーブルをつなげばコンポジット出力が出来そうです。もしかしてM5.Lcdライブラリと組み合わせれば2画面出力が可能なのかもと思って試してみましたが、エラーでコンパイルできませんでした。上記で紹介したM5Unifiedでは複数画面が扱えるようなのでいつか試してみたいですね。
実際にCore2 for AWSのポートBにビデオケーブルをつないだらコンポジット出力できました。LovyanGFXはすごいですね。
※コメント投稿者のブログIDはブログ作成者のみに通知されます