ESP32-S3で1.9"IPSカラーLCDをパラレル接続した

# はじめに

ESP32-S3で170x320の1.9インチIPSカラーLCDを、[TFT_eSPI](https://github.com/Bodmer/TFT_eSPI)ライブラリを使って8ビットパラレル接続したので、ピンアサインとサンプルスケッチ変更点をまとめておく（備忘録）。


# 使用機材

|主なパーツ|購入先|（購入時点）<br>税込価格|
|:-|:-|-:|
|ESP32-S3-WROOM-1-N16R8|[秋月電子通商](https://akizukidenshi.com/catalog/g/gM-17256/)|530円|
|ESP32S3用開発ボード（部品＋PCB）|[74th@Booth](https://booth.pm/ja/items/4179263)|700円|
|1.9インチTFT液晶ディスプレイ|[AliExpress](https://ja.aliexpress.com/item/1005004604305753.html)|581円|
|FFC変換基板（30P）|[aitendo](https://www.aitendo.com/product/18899)|650円|

<img alt="IMG_0055.png" width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/bf291c0a-322e-6d48-bd4c-f99c1293813b.png">



# LCDとESP32s3のピン接続

LCDとESP32s3の接続は[`LILYGO® T-Display-S3`](https://github.com/Xinyuan-LilyGO/T-Display-S3)に倣って、以下のアサインとした。

|LCD FFC#|PIN|ESP32s3 GPIO||LCD FFC#|PIN|ESP32s3 GPIO|
|:-:|:-:|:-:|-|:-:|:-:|:-:|
|1|GND|GND|     |16|DB5|46|                 
|2|VDD|15 (3V3)||17|DB6|47|                      
|3|IM2|GND|     |18|DB7|48|                 
|4|IM1|GND|     |19|SDO|-|                 
|5|RES|5|       |20|LEDA|38 (3V3)|               
|6|CS|6|        |21|LEDK|GND|              
|7|DC|7|        |22|LEDK|-|              
|8|WR|8|        |23|LEDK|-|              
|9|RD|9|        |24|LEDK|-|              
|10|SDA|GND|    |25|GND|-|                  
|11|DB0|39|     |26|-|-|                 
|12|DB1|40|     |27|-|-|                 
|13|DB2|41|     |28|-|-|                 
|14|DB3|42|     |29|-|-|                 
|15|DB4|45|     |30|GND|-|                 


今回使用したLCDは、`IM1`,`IM2`を`Lo`とすることで、`8ビットParallel`接続となる。（`IM1`,`IM2`を`Hi`にすると`4-line SPI`接続になる）
（`ST7789`の`IM0`,`IM3`が引き出されていないが、上記の動作から`Lo`固定と推測する）

# 動作検証プログラム

Arduino IDEを使用して、TFT-eSPIに用意されているサンプルスケッチを使い、使用パネルの設定を行った。

サンプルスケッチ パス：`TFT_eSPI/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino`

#### 変更その1

スケッチに`User_Setup_Select.h`のインクルード文を追加

```diff_cpp:TFT_eSPI/examples/160 x 128/TFT_graphicstest_PDQ3/TFT_graphicstest_PDQ3.ino:17
   :  : (省略)
+ #include <User_Setup_Select.h>
  #include <TFT_eSPI.h> // Hardware-specific library
  #include <SPI.h>

  TFT_eSPI tft = TFT_eSPI();       // Invoke custom library
   :  : (省略)
```


#### 変更その2

`#include <User_Setups/Setup206_LilyGo_T_Display_S3.h>`のコメントを外す（使用パネルの宣言）
（ピンアサインを`LILYGO® T-Display-S3`に倣ったのは、設定を楽にするため）


```diff_cpp:TFT_eSPI/User_Setup_Select.h:131
  :  : (省略)
  //#include <User_Setups/Setup205_ESP32_TouchDown_S3.h>     // Setup file for the ESP32 TouchDown S3 based on ILI9488 480 x 320 TFT 

- //#include <User_Setups/Setup206_LilyGo_T_Display_S3.h>     // For the LilyGo T-Display S3 based ESP32S3 with ST7789 170 x 320 TFT
+ #include <User_Setups/Setup206_LilyGo_T_Display_S3.h>     // For the LilyGo T-Display S3 based ESP32S3 with ST7789 170 x 320 TFT
  //#include <User_Setups/Setup207_LilyGo_T_HMI.h>            // For the LilyGo T-HMI S3 based ESP32S3 with ST7789 240 x 320 TFT
  :  : (省略)
```


#### 変更その3

今回のLCDは`ST7789V3`ドライバのため、`#define ILI9341_DRIVER`をコメントアウト。
（最初うまく動作せず、ここに気付くまでに苦労した）

```diff_cpp:TFT_eSPI/User_Setup.h:45
  :  : (省略)
  // Only define one driver, the other ones must be commented out
- #define ILI9341_DRIVER       // Generic driver for common displays
+ //#define ILI9341_DRIVER       // Generic driver for common displays
  //#define ILI9341_2_DRIVER     // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
  :  : (省略)
```



# 動作結果

以下の動画のとおり、うまく動作しました。

https://twitter.com/nak435/status/1648204121252728833

描画性能はどうでしょうか？ SPI接続よりは高速だと思いますが・・・
動画に映っているベンチマーク結果をリストにしておきます。
（単位：マイクロ秒）

|Benchmark	|TFT-eSPI|LovyanGFX|
|:-	|-:	|-:|
|HaD logo	|102,682		|44,709|
|Clear	|1,317,037		|350,834|
|Text	|8,468		|6,270|
|Pixels	|136,271		|118,911|
|Lines	|166,924		|144,065|
|H/V Lines	|11,310		|2,822|
|Rectangles	|5,005		|1,431|
|Rects-Fill	|145,034		|31,091|
|Circles	|13,196		|13,326|
|CircleFill	|30,490		|12,535|
|Triangles	|7,019		|5,787|
|Tris-Fill	|49,284		|14,717|
|Rnd-Rects	|7,650		|3,997|
|RRects-Fill	|145,861		|32,577|
|**Total**	|**2,146,231**		|**783,072**|


# 追記<br>[LovyanGFX](https://github.com/lovyan03/LovyanGFX)ライブラリを使ってみる

TFT-eSPIより高速との評判のLovyanGFXで表示してみた。下記動画のとおり、確かに高速に描画している。
ベンチマーク結果は、上の表に列追加しました。fill系のAPIが高速です。DMA転送しているから？

https://twitter.com/nak435/status/1648904335915622402

ピン接続などは、TFT-eSPIと同じです。下記にパネル定義を載せておきます。

```c++
#include <LovyanGFX.hpp>

class LGFX : public lgfx::LGFX_Device
{
  // 接続するパネルの型にあったインスタンスを用意します。
  lgfx::Panel_ST7789      _panel_instance;
  // パネルを接続するバスの種類にあったインスタンスを用意します。
  lgfx::Bus_Parallel8  _bus_instance;   // 8ビットパラレルバスのインスタンス
  // バックライト制御が可能な場合はインスタンスを用意します。(必要なければ削除)
  lgfx::Light_PWM     _light_instance;

public:

  // コンストラクタを作成し、ここで各種設定を行います。
  LGFX(void)
  {
    { // バス制御の設定を行います。
      auto cfg = _bus_instance.config();    // バス設定用の構造体を取得します。

// 8ビットパラレルバスの設定
    //cfg.i2s_port = I2S_NUM_0;     // 使用するI2Sポートを選択 (I2S_NUM_0 or I2S_NUM_1) (ESP32のI2S LCDモードを使用します)
      cfg.freq_write = 20000000;    // 送信クロック (最大20MHz, 80MHzを整数で割った値に丸められます)
      cfg.pin_wr =  8;              // WR を接続しているピン番号
      cfg.pin_rd =  9;              // RD を接続しているピン番号
      cfg.pin_rs =  7;              // RS(D/C)を接続しているピン番号
      cfg.pin_d0 = 39;              // D0を接続しているピン番号
      cfg.pin_d1 = 40;              // D1を接続しているピン番号
      cfg.pin_d2 = 41;              // D2を接続しているピン番号
      cfg.pin_d3 = 42;              // D3を接続しているピン番号
      cfg.pin_d4 = 45;              // D4を接続しているピン番号
      cfg.pin_d5 = 46;              // D5を接続しているピン番号
      cfg.pin_d6 = 47;              // D6を接続しているピン番号
      cfg.pin_d7 = 48;              // D7を接続しているピン番号

      _bus_instance.config(cfg);    // 設定値をバスに反映します。
      _panel_instance.setBus(&_bus_instance);      // バスをパネルにセットします。
    }

    { // 表示パネル制御の設定を行います。
      auto cfg = _panel_instance.config();    // 表示パネル設定用の構造体を取得します。

      cfg.pin_cs           =     6;  // CSが接続されているピン番号   (-1 = disable)
      cfg.pin_rst          =     5;  // RSTが接続されているピン番号  (-1 = disable)
      cfg.pin_busy         =    -1;  // BUSYが接続されているピン番号 (-1 = disable)

      // ※ 以下の設定値はパネル毎に一般的な初期値が設定されていますので、不明な項目はコメントアウトして試してみてください。

      cfg.panel_width      =   170;  // 実際に表示可能な幅
      cfg.panel_height     =   320;  // 実際に表示可能な高さ
      cfg.offset_x         =    35;  // パネルのX方向オフセット量
      cfg.offset_y         =     0;  // パネルのY方向オフセット量
      cfg.offset_rotation  =     0;  // 回転方向の値のオフセット 0~7 (4~7は上下反転)
      cfg.dummy_read_pixel =     8;  // ピクセル読出し前のダミーリードのビット数
      cfg.dummy_read_bits  =     1;  // ピクセル以外のデータ読出し前のダミーリードのビット数
      cfg.readable         =  true;  // データ読出しが可能な場合 trueに設定
      cfg.invert           =  true;  // パネルの明暗が反転してしまう場合 trueに設定
      cfg.rgb_order        = false;  // パネルの赤と青が入れ替わってしまう場合 trueに設定
      cfg.dlen_16bit       = false;  // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
      cfg.bus_shared       = false;  // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)

      //cfg.memory_width     =   170;  // ドライバICがサポートしている最大の幅
      //cfg.memory_height    =   320;  // ドライバICがサポートしている最大の高さ

      _panel_instance.config(cfg);
    }

    { // バックライト制御の設定を行います。（必要なければ削除）
      auto cfg = _light_instance.config();    // バックライト設定用の構造体を取得します。

      cfg.pin_bl = 38;              // バックライトが接続されているピン番号
      cfg.invert = false;           // バックライトの輝度を反転させる場合 true
      cfg.freq   = 44100;           // バックライトのPWM周波数
      cfg.pwm_channel = 7;          // 使用するPWMのチャンネル番号

      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);  // バックライトをパネルにセットします。
    }

    setPanel(&_panel_instance); // 使用するパネルをセットします。
  }
};
```


# おわりに

元々は、CircuitPythonでパラレル接続を使いたかったが、ESP32s3では`paralleldisplay`が使えず、SPI接続しかできなかった。
少しでも高速描画したいため、CircuitPythonからCの~~TFT-eSPI~~ LovyanGFXを呼び出すブリッジ機能を作ろうかと思います。実現できるか分かりませんが、上手くできたらQiita記事にします。