あみらぼ

電子工作がメインのDIYもの作り雑記

ESP32でのLCD描画あれこれ

さて、前回、ESP32のDirectPortAccessが遅い事に苦戦しながらも、何とか240x320の全画素書き換えを32fpsで行う事ができました。目標の60fpsには遠く及ばないものの、何とか実用レベルにはなるかも?といったところです。

 

しかし、新たな問題が…、240x320(=150KB)のバッファを2枚用意して、デュアルコアの方法で1枚のバッファをLCDに出力、もう一つのコアでもう一枚のバッファを更新(作成)、というダブルバッファを考えていたのですが、ESP32ではSRAM上に150KB×2のバッファは取れない様子…、ESP32のSRAMは512KBあるものの、通常の設定では少なくとも64KBはキャッシュに使われてたり、512KBのSRAM領域はいくつかに分かれていて、メモリアドレスが連続ではないなどが理由と思われます。

 

んー、これは困りました。更新を1行ずつ順に行うのであれば、シングルバッファでも問題ないかもですが、更新する場所がいろいろ変わる場合や、描画を重ね書きして絵を完成させるような場合は、その作成途中の絵が出力されてしまうのは、ティアリングが出るのと別次元で大きく崩れた絵が出るので、非常にまずいです。LCDへの出力が何行目まで終わったかを確認しながら、書き換えても安全な場所だけを更新する、という方法も、なくはないかもですが、それは描画のプログラミングを著しく困難にさせる上、速度的にもコストが大幅に増して描画できる内容も大きく制約を受けるので、今回考えてる最終的な目標を考えると、シングルバッファは絶対にやりたくないところです。

 

まぁダメもとですが、今使ってるのはESP32-WROVERですのでPSRAM領域が8MBあります!今のところ実際に使えるのは4MBまでとかの制約はあるようですが、少なくとも、300KB程度のバッファを確保するのは余裕です。ただ、PSRAMはSPI-Flashと共有のSPI接続な上に、2つのCPUから読み込みと書き込みを同時に行うので、そこのメモリバンド幅で速度がかなり落ちる事は容易に想像できます。で、ダメもとでPSRAM上にダブルバッファを取って、片方のCPUで書き込み、片方のCPUで読み込み&LCD出力を試してみた結果…、fpsになりました!

いくらなんでも遅すぎませんかね…?PSRAMはP(pseudo:疑似的)SRAMで、内部的にはDRAMらしいので、読み書きタイミングとか、メモリバスの問題とかあるかもですが、それにしても遅すぎる気がします…。試しに、片方のCPUは何もせずに、1つのCPUでPSRAMからの読み込み&LCDへの出力だけを行ってみたところ、23fps程度の速度が出たので、同時アクセス時に特に速度低下が顕著になる様子です…。

で、原因を調べてる中で偶然見つけたのですが、現在のESP32にはPSRAMの読み書きにエラッタ(HWバグ)があるようです!

概要としては、

f:id:amilabo:20200412022406j:plain

  • CPUが特定の順序で外部SRAMにWrite/Readを行った際に予期しない動作となる場合がある

というもので、今回のケースで思いっきりこのエラッタを踏んでいるような気がします。ただ、これに関してはソフトウェアのワークアラウンドがあるようで、プログラムのコンパイル時にこのエラッタで問題が起こらないよう、修正を加えた形でコンパイルがされるようです。確証はありませんが、画乱れ等は起こらない一方で、速度が著しく低下するのは、このワークアラウンドが影響している可能性が高いのではないかと思います。

ちなみに、今年の初め辺りから出荷が開始されているらしい、末尾に「E」がつくバージョンではこのエラッタは無くなった(シリコンウエハレベルでHW的に修正された)ようです。コンパイラの対応がいつ頃されるのか、既にされているのかは不明ですが、「ESP32-WROVER-E」ではいずれこの速度低下は改善されそうです。

この辺の情報って日々更新されると思うので、あえてリンクは貼りませんが、詳細が気になる方は「ESP32 ECO」とかで検索してみると、最新の情報がいろいろわかると思います。

この速度低下の原因を探している中でエラッタを見つけたので順番は前後していますが、以前のブログにESP32の新バージョンの情報を書いておきました。

 

amilabo.hatenablog.com

 

ただ、ESP32-WROVER-Eで速度がある程度改善されたとしても、PSRAM上にダブルバッファでは30fpsもかなり厳しそうなので、他の手を考える必要がありそうです。

デュアルコアをあまり生かせないのは残念ですが、LCDへの転送をもっと早く行えれば、シングルバッファでLCDへの転送が終わってからバッファを更新して、更新が終わったらLCDへの転送、という手も無くはなさそうです。幸い、LCDへの転送中にもう片方のCPUがSRAMを読み書きする分には速度低下は起こらなさそうなので、LCDへの転送中はもう片方のCPUが転送中のバッファを触れないような準備とかを行い、LCDへの転送が終わったら、デュアルコアで大急ぎでバッファを更新、という感じになるでしょうか。

 

いずれにしても、LCDへの転送の高速化は必要そうなので、前回以上に高速化ができないかを試してみました。前回試して上手くいかなかった以下の方法について、

  • 8ビットデータの0に対応する部分とWRピンに対応するビットをGPIO_OUT_W1TCで書き込む(必要なビットを0にすると同時にWRピンもLOWになる)
  • 8ビットデータの1に対応する部分とWRピンに対応するビットをGPIO_OUT_W1TSで書き込む(必要なビットを1にすると同時にWRピンもHIGHになる)

この方法だと、2回のレジスタ書きで済むのですが、LCD側では、WRピンの立ち上がりと同時に8ビットデータを読み込むので、さすがに正しくデータが伝わらなかったわけです。

そこで、WRピンの立ち上がりを遅らせれば、何とかなるかも?という事で、いろいろ試してみました。まず、ESP32の出力とLCDのWRの入力の間に、手元にあったFETをはさんでバッファ回路を作り、信号遅延がいい感じにならないか試してみましたが…、そもそもFETの入力容量600pFの時点で圧倒的に信号が遅れて話にならない感じでした…。遅い遅いとは言ってるものの、メガヘルツレベルでの伝送を行っているので、ほんの少しの容量変化でも大きく影響を受けるようです。いろいろ試した結果、ESP32とLCDのWRの間の接続を60cmくらいの長さのワイヤでびよーんと繋ぐと、何とか絵が乱れず、32fpsから41fpsに高速化する事ができました!ただ、動作は非常に不安定で、1mのワイヤだとダメだったり、60cmでもちょっとくるくる巻いたりすると容量が増えてダメだったり…、といった感じでした。ワイヤの長さではなく、数p~数十pのコンデンサをたくさんの種類買ってきて、ちょうどいい遅延になるような容量を探せば多少は安定するかもしれませんが、波形もひどい事になってるでしょうし、もし動いたとしても、正直、こんな怪しげな方法は絶対採用したくありませんw

 

とゆうか、今回試したことで、今たまたま動いている配線や方法も、ちょっとした事で伝送やその他の不具合が起こるんじゃないかと、いろいろ不安になってきました…。

 

実際、信号がどの程度ちゃんとした矩形波になってるかとか、Duty比はどのくらいになってるかとか、電源はどの程度安定しているのかとか…いろいろ気になってきます。今までも、オシロスコープがあったら…という事はたびたびありましたが、テスターやICレコーダーやテストの工夫等で「間接的に」波形を観測できていたつもりですが、さすがに限界を感じています…。

#後日、結局オシロスコープを買いましたw 超絶便利すぎて、世界が変わりました!

 

さて、長くなってきたので今回はこの辺で。この後、ESP32に関する意外な発見とか、それを使ったアイディアとか、先人の知恵とか、ある程度の妥協とかで、ついに75fpsが出せる見込みが立ちました!

 

次回は、その辺の詳細について書ければと思います。

 

ではでは。