ESP32と小型の液晶ディスプレイを使って,フラクタル図形として有名なマンデルブロ集合を表示するビューアーを作りました. 作ったきっかけは,Pyxisというマンデルブロ集合描画専用ハードウェアを知って,とてもおもしろそうだと思ったけれど作るのは大変そうなので,できるだけ簡単なスタンドアローンのマンデルブロ集合ビューアーを作ってみたくなったからです.
マイコンにはESP32を使いました. 元々は浮動小数点演算装置(FPU)を内蔵したSTM32F4を使うことを考えていましたが,同様にFPUを内蔵したESP32はデュアルコアが使えるのでこちらを選択しました. ESP32といえばWi-Fiが使えるのが大きな特徴ですが今回は利用しません. ESP32は、AliexpressでAdapter Boardとして売っていたシンプルなピッチ変換基盤に実装して使いました。 この基板はESP32の他に、ENとIO0用の2つのタクトスイッチと2つの10kΩのプルアップ抵抗を取り付けます。 さらに3V3とGNDピンの間に0.1uFのコンデンサも取り付けました。 LCDは240x240のIPS液晶を使いました.これは高精細で発色もきれいな液晶です. 描画領域を移動するための入力手段として,スイッチ付きのロータリーエンコーダを使いました. アナログジョイスティックやタクトスイッチの利用も考えましたが,基板への固定や配線の数を考えて一番シンプルになりそうな方法を選びました.
回路が単純なので,回路図は描かずに以下の配線の対応表を用意してユニバーサル基板の裏面で配線を行いました. LCD,PIN,ESP,REはそれぞれ液晶,ヘッダーピン,ESP32,ロータリーエンコーダーを表します.
LCD:GND | PIN:GND |
LCD:VCC | PIN:VCC |
LCD:SCK | ESP:IO18 |
LCD:SDA | ESP:IO23 |
LCD:RES | ESP:IO22 |
LCD:DC | ESP:IO21 |
LCD:BLK | NC |
RE:A | ESP:IO17 |
RE:B | ESP:IO16 |
RE:C | PIN:GND |
RE:SW1 | ESP:IO4 |
RE:SW2 | PIN:GND |
PIN:RXD | ESP:RXD0 |
PIN:TXD | ESP:TXD0 |
PIN:VCC | ESP:3V3 |
PIN:GND | ESP:GND |
完成した基板を下に示します.
マンデルブロ集合は,ziおよびcを複素数とした場合の
zn+1=zn2+c z0=0
という複素数列に対して,nを無限大にしても発散しないような複素数cからなる集合として定義されます. 計算機上で実装する場合は,cの実数部と虚数部に対応する画面上の各点について数列が一定の値を越えないか適当な数までチェックして,もし越えた場合はその時のnの値に応じて色を付与することがよく行われます.
今回実装したコードでは,マンデルブロ集合の計算で特別なことは行っていません. できるだけシンプルかつ効率的に計算するため,複素数の実数部と虚数部をそれぞれfloat型で計算しているため,図形をどんどん拡大していくとやがて浮動小数点の指数部の限界を越えて描画できなくなります. ESP32は2つのコアを内蔵しているため,計算用のタスクを2つ並列で動かして高速化しています. 各タスクは液晶の行単位で処理するようになっていて,未処理の行があればそのライン上の各ピクセルに対して数列のチェックを行いラインの描画を行います. マンデルブロ集合の描画では,各ピクセルを独立に計算できるので並列処理に向いています. 共有変数や液晶にアクセスする際には排他制御を行います. 液晶ディスプレイへの表示は,以前作ったライブラリをESP32用に若干修正して使いました.
キー入力に関しては1ms毎にタイマー割り込みでポートをチェックして,描画領域の移動を行います. ロータリーエンコーダーを回転させると,回転方向に応じて現在の移動軸(X軸あるいはY軸)上の移動を行います. ロータリーエンコーダーのプッシュスイッチを押すと,移動する軸をX軸かY軸に切り替えます. ロータリーエンコーダーを押しながら回転させると,回転方向に応じて拡大・縮小します.
描画速度は演算の繰り返し回数(チェックするnの最大数)に大きく依存しますが,今回は繰り返し回数は128回としました. その場合,一番遅い描画範囲の(画面全体が集合に含まれる)場合で383ms,通常の描画では概ね100〜200ms程度で描画することができて,ロータリーエンコーダーで移動しながらリアルタイムで描画できます. デュアルコアを使う前と後で計算時間を比較しましたが,繰り返し回数が240回でもっとも遅い描画範囲の場合,シングルコアで1,386ms,デュアルコアで695msとほぼ半分になりました. 集合の色付けについて,最初は色の数を増やしてグラデーションが滑らかになるようにしたところあまり見た目が派手にならなかったので,色数を16色にして表示するようにしました.
大昔にN88-BASIC (10MHzのV30 CPU)で一晩中パソコンを動かしてマンデルブロ集合を描画した思い出がありますが,小さくて安いマイコンでここまで計算できるのは隔世の感があります.