UNO R4+ILI9341/ST7789(SPI、8bitパラレル)Arduino_GFXの注意点とベンチマーク
はじめに
ESP32 と UNO R4 WiFi で動作する ArduinoOpenWeather の制作で Arduino GFX(IDE ライブラリ マネージャ上は GFX Library for Arduino)を使ってみました。
UNO R4 とカラー TFT LCD の組み合わせでは、Adafruit GFX がよく使われていますし、稀に UNO R3/Mega 2560 用 LCD シールドを MCUFRIEND_kbv で 動かした例 を見つける事ができます。
Arduino GFX はこれらのライブラリに比べて適用範囲の広さと速さがウリですが、正しく動作させるには設定に注意が必要な事が分かりました。ソレさえ分かれば他の LCD にも応用できると期待しています。
ということで、本記事では Arduino_GFX に関して以下を共有したいと思います。
と、その前に今回購入した LCD の紹介です。
おことわり
本記事では、「見た目が似ていても、中身がどこか違う製品がある」ことを、「クローン」や「派生」ではなく「変異体(Variant)」と表現しています。コロナウィルスの「変異株」に近いニュアンスです。ご了解ください。
本記事で取り上げる LCD
Aliexpress で TFT LCD を探すと、仕様が不明なモノや、基板やピンの色違い、設計が古そうなモノなど「変異体」が数多くあり、自分のボードで動くか心配になりますよネ。
開拓精神が旺盛な方はともかく、私は LCD wiki と見比べながら、以下を購入しました。
SPI 系
- 2.4inch SPI Module ILI9341 SKU:MSP2402 - LCD wiki
Aliexpress で ILI9341 と ST7789 の両タイプを購入しました(Aliexpress ではよくリンクが切れるので、敢えてリンクは貼りません)。両方ともタッチ機能付きです。 - 2.8inch SPI Module ILI9341 SKU:MSP2807 - LCD wiki
秋月電子で購入しました。データシートや LCD wiki へのリンクが添付されているので、安心して使えます。
8-bit パラレル系シールドタイプ
「Arduino UNO/Mega2560 用」として販売されているシールドです。LCD wiki からデモを落とすか、MCUFRIEND_kbv を Adafruit GFX と組み合わせるのが一般的のようです。
一方 Arduino GFX では、このプルリク で UNO R4 系でも動作するようになりました。
- 2.4inch Arduino Display - LCD wiki
写真の左側は TZT 社 (TuoZhanTeng Electronic Technology Co., Ltd) 製で、LCD wiki に掲載のモノと同一のようです。
残りの2つは設計が少し古そうで、画面が少し暗めです。これらは ILI9341 版と ST7789 版をペアで購入してみました。
-
3.5inch Arduino Display-UNO - LCD wiki
私が購入したのは ILI9486 版ですが、LCD wiki によると ST7796 版もあるようで、MCUFRIEND_kbv の作者によって Adafruit_ST7796S_kbv が提供されています。UNO R4 Minima/WiFi で動作しているデモビデオが Currently Supported Dev Board に公開されています。
ハードウェア仕様の確認と消費電流の測定
公式マニュアルの UNO R4 Minima 編 や UNO R4 WiFi 編 によると、ボードへの電源を VIN から供給した場合はオンボードのレギュレータで 5V に降圧され、最大 1.2A の供給能力があるとのことです(ボード自体の消費電流を含む)。
また 3.3V の供給可能な最大電流に関しては、「Arduino UNO R4 その3(RA4M1 の隠された機能)」の見積もりによると、50mA 程度が安心のようです。
一方、過去に サーモカメラを製作した経験から、2.4 インチ LCD で 90mA 近くを消費していたことを考えると、ちゃんと検証しないとヤバそうです。
事実、少々古い記事(2019年 9月) ですが、シールドタイプの変異体には次のような仕様のモノが存在したようです。
現在のMcufriendシールドの設計は誤っています。バックライトの電源をArduinoの3.3Vピンから取っている点が間違いです。本来のシールドでは、ディスプレイ基板上に3.3Vレギュレータが搭載されており、そこへ5Vピンから電源が供給される仕組みになっていました。
…
とても単純な話です。基板を見てみてください。 元の基板にはAMS1117レギュレータが搭載されており、5Vピンと3.3Vピンの両方が使用されています。 現在の基板にはSOIC-8用の空きパターンがあるだけで、レギュレータは搭載されていません。使用されているのは3.3Vピンのみです。
コストダウンのためにレギュレータを取っちゃったのでしょう。2026 年の現在ではそういう粗悪な基板はないと思いますが、念の為に回路図を確認し、3.3V、5V のそれぞれから引き出される電流を実測してみました。
SPI 系の回路図と消費電流測定
LCD wiki に添付された回路図を見ると、次の事が分かります。
- VCC 選択用はんだジャンパ J1 は、開放で 5V、短絡で 3.3V を切り替え
TFT_BLは、内部のトランジスタがスイッチとなり4つのバックライト用 LED を駆動
以下はこれらを裏付ける測定結果で、何れも許容範囲内でした。メデタシ、メデタシ 🤗
| 端子 | 2.4インチ | 2.8インチ |
|---|---|---|
| 3.3V 端子 | 3.2mA | 3.3mA |
| 5V 端子 | 82.1mA | 67.3mA |
8-bit パラレル系シールドタイプの消費電流測定
LCD wiki に添付された回路図は、.SchLib ファイルを開く環境が構築できず未確認です 😅 が、3.3V で直接バックライトを駆動していることもなく、安心しました 😮💨
| 端子 | 2.4inch Arduino Display | 3.5inch Arduino Display-UNO |
|---|---|---|
| 3.3V 端子 | 0 | 0 |
| 5V 端子 | 46mA | 105.6mA |
Arduino GFX 設定の注意点
Arduino GFX では MCU ⇔ LCD 間の通信を、経路である「データバス」クラスとディスプレイ ドライバ IC に送るデータを扱う「ディスプレイ」クラスに分けることで、様々なデバイス間で動作するように設計されています。
これを踏まえ、特に本章では Adafruit GFX との違いについて、次の二点を掘り下げます。
- データバスのクロック周波数
- ディプレイ変数のコンストラクタ
データバスのクロック周波数
ここでの「データバス」は SPI バスとします。Arduino の SPI クラスにおけるクロック周波数の最大値は 12MHz とされています。事実、Adafruit GFX では ILI9341 用クラスの begin() にクロック周波数を指定できますが、12MHz より大きな値を指定してもベンチマークの結果(後述)は変わりませんでした。
一方 Arduino GFX の ILI9341 用 begin() には 24MHz(RA4M1 で設定可能な最大値)も指定可能で、(さすがに2倍とまではいきませんが…)ベンチマーク結果も向上します。
拙作の記事「ESP32 2432S028R (CYD)でLVGL - ILI9341 vs ST7789 速度比較」で調べた通り、ILI9341 は 40MHz までは行けるので大丈夫だとは思いますが、Arduino_GFX は内部の VRAM を読み出す機能がないので描画結果の比較ができません。どこかおかしい?と感じたらデフォルトの 12MHz を設定してください 😓
ディプレイ変数のコンストラクタ
Arduino GFX で特質すべきは、例えば同じ ILI9341 用ドライバでも変異体による動作の違いにもキッチリ対応している点です。
例えば ILI9341 用のコンストラクタ は、次のように多くの引数が指定可能となっています。
Arduino_ILI9341(
Arduino_DataBus *bus, int8_t rst = GFX_NOT_DEFINED, uint8_t r = 0,
bool ips = false, int16_t w = ILI9341_TFTWIDTH, int16_t h = ILI9341_TFTHEIGHT,
uint8_t col_offset1 = 0, uint8_t row_offset1 = 0, uint8_t col_offset2 = 0, uint8_t row_offset2 = 0,
const uint8_t *init_operations = ili9341_type1_init_operations, size_t init_operations_len = sizeof(ili9341_type1_init_operations)
);ここでの col_offset1 〜 row_offset2 は、最終的に Arduino_TFT::setRotation() でビデオ RAM の開始・終了座標に反映されます。つまり LCD パネル上の原点座標 (0, 0) の位置が微妙に異なる変異体に対応できるようになっています。
また 注目すべきは、初期化シーケンスを指定する init_operations です。デフォルトでは ili9341_type1_init_operations が設定されますが、変異体の違いを吸収する type1 〜 type3 の3種類が定義済み なことです。3.5 インチの ILI9486 版シールドを除き、今回取り上げた LCD では全て type2 を指定しないと「色ズレ」が起きる事が分かりました。
ILI9341 の色ズレ問題
次に挙げる例は、パンダの JPEG 元画像に対し Adafruit GFX と Arduino GFX の結果を比較したものです。LCD が同じならグラフィック ライブラリが異なっても見た目が同じになる事が期待されますが、デフォルトの type1 ではパンダの頭頂部と鼻の頭の部分で「色ズレ」が顕著となりました。
また次の画像は、上段にはRGB565 ビットマップ画像を 、下段には JPEG 画像を描画させていますが、デフォルトの type1 ではオレンジ色が濃くなり、また JPEG 特有のブロックノイズが見えてしまっています。
この問題は「黄色い基板」で知られる ILI9341 版 CYD でも再現します。解決策としては、次のように「ディスプレイ」クラスのコンストラクタに type2 を指定します。
// Data Bus
Arduino_DataBus *bus = new Arduino_HWSPI(TFT_DC, TFT_CS);
// Display
Arduino_GFX *gfx = new Arduino_ILI9341(
bus, TFT_RST, TFT_ROTATION, false /* ips */, ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT,
0 /* col offset 1 */, 0 /* row offset 1 */, 0 /* col offset 2 */, 0 /* row offset 2 */,
ili9341_type2_init_operations, sizeof(ili9341_type2_init_operations)
);この設定により、Arduino_GFX.h で定義された色 の色味も変わります。W3C Wiki の色見本 を元に定義しているようですが、RGB の値が微妙に調整されているようなので、type2 を使う場合はマクロ RGB565(r, g, b) で独自に定義した方が良いと思います。
ST7789 の色ズレ問題
同様の問題は ST7789 でも発生します。デフォルトの type1 では、Adafruit GFX と比較しなければ気付けない程度に色合いが薄くなります。まぁ気付かなければソレまでですが…
この問題も ST7789 版の CYD で再現します。UNO R4 + ST7789 の場合、解決には次のコードを使います(ST7789 のデータバスでは、Arduino_HWSPI() が正しく動作しませんでした)。
// Data Bus
Arduino_DataBus *bus = new Arduino_SWSPI(TFT_DC, TFT_CS, TFT_SCLK, TFT_MOSI, TFT_MISO);
// Display
Arduino_GFX *gfx = new Arduino_ST7789(
bus, TFT_RST, TFT_ROTATION, false /* ips */, ST7789_TFTWIDTH, ST7789_TFTHEIGHT,
0 /* col offset 1 */, 0 /* row offset 1 */, 0 /* col offset 2 */, 0 /* row offset 2 */,
st7789_type2_init_operations, sizeof(st7789_type2_init_operations)
);参考情報
- Arduino GFX vs. Adafruit GFX — Differences in Color Reproduction #820
色ズレ問題を Arduino GFX の作者に質問したところ、すぐに回答が得られました。さすが、LCD パネルの変異体について熟知しているようです。 - Bad colors with ILI9341 display board #744
同様の問題が issues で報告されていています。解決策としてtype2やtype3の利用が提示されていますが、未解決のまます。変異体問題の根深さを示している気がします。 - color accuracy issue with 16 bit (esp8266 , ILI9341) #322
Arduino_ILI9341::begin()の後にAdafruit_ILI9341::begin()を呼び出すと色ズレが解消するとのことで、issues を追跡するとtype2のバグ修正に落ち着いたようです。
Adafruit GFX を含むベンチマークの結果
お待ちかねのベンチマークは、Arduino GFX の PDQgraphicstest を Adafruit GFX でも動作するよう改修したプログラムで実施しました。
ILI9341
Adafruit GFX vs Arduino GFX の結果です。vs Adafruit は の値です。
| Benchmark | Adafruit GFX | Arduino GFX 12MHz | Arduino GFX 24MHz | vs Adafruit |
|---|---|---|---|---|
| Screen fill | 1,203,542 | 634,214 | 358,586 | 3.36 |
| Text | 253,111 | 69,719 | 52,867 | 4.79 |
| Pixels | 2,079,712 | 2,102,500 | 1,871,040 | 1.11 |
| Lines | 2,901,661 | 1,947,032 | 1,659,398 | 1.75 |
| Horiz/Vert Lines | 99,076 | 53,533 | 31,229 | 3.17 |
| Rectangles (filled) | 2,499,381 | 1,317,816 | 745,556 | 3.35 |
| Rectangles (outline) | 64,015 | 35,969 | 21,816 | 2.93 |
| Triangles (filled) | 816,981 | 474,307 | 295,822 | 2.76 |
| Triangles (outline) | 164,926 | 110,819 | 93,449 | 1.76 |
| Circles (filled) | 310,746 | 213,795 | 151,732 | 2.05 |
| Circles (outline) | 318,964 | 183,797 | 157,789 | 2.02 |
| Arcs (filled) | N/A | 148,367 | 125,878 | N/A |
| Arcs (outline) | N/A | 290,474 | 264,986 | N/A |
| Rounded rects (filled) | 2,468,541 | 1,342,219 | 769,934 | 3.21 |
| Rounded rects (outline) | 157,607 | 95,652 | 75,322 | 2.09 |
ST7789
2.4 インチの SPI 版と 8-bit パラレル版の比較です。Adafruit GFX の結果が比較対象にならないくらい遅いのは、LCD が Adafruit の純正品ではないからでしょう。
また Arduino GFX では、SPI 版の「データバス」クラスに Arduino_SWSPI を使わざるを得なかったため、ILI9341 版よりかなり遅めです。8-bit パラレル版もそれほど早くないので、UNO R4 では敢えて ST7789 版を購入する理由が見当たらないという結果になりました。
| Benchmark | Adafruit GFX | Arduino GFX SPI 24MHz | Arduino GFX 8-bit parallel |
|---|---|---|---|
| Screen fill | 14,374,396 | 2,307,269 | 407,549 |
| Text | 2,537,210 | 187,994 | 52,502 |
| Pixels | 18,874,093 | 2,991,252 | 1,250,543 |
| Lines | 25,261,955 | 3,373,366 | 1,250,933 |
| Horiz/Vert Lines | 1,169,842 | 215,490 | 49,335 |
| Rectangles (filled) | 29,848,812 | 5,497,272 | 1,231,500 |
| Rectangles (outline) | 745,337 | 137,923 | 32,373 |
| Triangles (filled) | 9,417,678 | 1,748,627 | 417,373 |
| Triangles (outline) | 1,455,392 | 202,099 | 71,827 |
| Circles (filled) | 3,355,058 | 634,576 | 172,282 |
| Circles (outline) | 2,797,201 | 287,582 | 109,493 |
| Arcs (filled) | N/A | 281,382 | 114,577 |
| Arcs (outline) | N/A | 393,430 | 216,604 |
| Rounded rects (filled) | 29,114,681 | 5,509,766 | 1,237,597 |
| Rounded rects (outline) | 1,371,760 | 220,505 | 69,683 |
今回作成したプログラムについて
Arduino GFX Library Lab に次の2本を上げたので、興味があれば覗いてみてください。
-
ColorAlignment
色ズレの有無を確認するスケッチです。ColorAlignment.ino の冒頭でサンプルの画像を選択し、Adafruit GFX の利用の有無、ドライバの種類、初期化シーケンスを設定します。また gfx_config.hpp で UNO R4 と CYD のピン配を定義しているので、実行環境に合わせて修正してください。 -
PDQgraphicstest
ベンチマークのスケッチです。PDQgraphicstest.ino の冒頭にある元のデバイス定義を無効化し、Adafruit GFX の利用の有無、ドライバの種類、初期化シーケンスを設定します。また gfx_config.hpp で LCD のピン配を定義しています。ちなみにこのベンチマークは UNO R3 でも動作します(Arduino GFX の ST7789 を除く)。
ところで、MCUFRIEND って何?
と思ったのは私だけでしょうか? Google AI によると、データシートもない変異体の格安版が氾濫したとのこと。例えば「ArduinoUNO用のTFTシールド」を拝見すると、正に「ドライバ IC ガチャ」な状態だったようです 😱
当の “mcufriend.com” といえば、過当競争に敗れた結果、今では インターネットアーカイブにその名残を見る事ができるだけ です。
そんなを状況を改善すべく開発されたのが MCUFRIEND_kbv です。作者の David Prentice 氏が立ち上げた Arduino フォーラム上のスレッド「MCUFRIEND_kbv Library for Uno 2.4, 2.8, 3.5, 3.6, 3.95 inch mcufriend Shields」を見ると、当時の奮闘ぶりが伝わってきます。
ちなみに kbv は氏独自のサインのようなもらしいです。ただ残念なことに「David Prentice RIP」によれば、氏は 2022 年交通事故で亡くなられたとのことで、以来ライブラリの開発は止まったままです。
おわりに
今回購入した LCD はどれも抵抗式のタッチパネルと SD カードスロットが付いてます。今のところ使う見込みありませんが、動作確認ができたら改めて報告しようと思います 🌾