M5StampS3+3.2インチLCDでLovyanGFXを使ってみる

# はじめに

以下の記事で、M5StampS3 に ILI9341 の 3.2 インチ LCD の接続を試みましたが、編集後記で記載しているように描画速度が遅いというネックがありました。

https://qiita.com/suzukiplan/items/b5882449fdbf571b4e80

描画に用いていた [Adafruit_ILI9341](https://github.com/adafruit/Adafruit_ILI9341) を [LovyanGFX](https://github.com/lovyan03/LovyanGFX) に差し替えてみたところ、描画速度が実用的になったので、本書では M5StampS3 + ILI9341 LCD での LovyanGFX の使い方について解説します。

# ハードウェア構成

ILI9341 (3.2インチ) LCD と M5StampS3 を下表の PIN アサインで接続しています。

|LCD (ILI9341)|M5StampS3|用途|
|:-:|:-:|:-|
|`VCC`|`3V3`|電源給電|
|`GND`|`GND`|グランド|
|`CS`|`1` (GPIO)|LCD チップ選択有効化の信号|
|`RESET`|`5` (GPIO)|リセット信号|
|`DC`|`3` (GPIO)|データ/レジスタ選択の信号|
|`SDI (MOSI)`|`13 SDA`|I2C (Stamp→LCD方向のデータ送信)|
|`SCK`|`15 SCL`|I2C (クロック同期)|
|`LED`|`7` (GPIO)|バックライト|
|`SDO (MISO)`|配線なし|LCD→Stamp方向のデータ送信は不要なため|
|`T_CS`|配線なし|今回はタッチ不要のため|
|`T_DIN`|配線なし|今回はタッチ不要のため|
|`T_DO`|配線なし|今回はタッチ不要のため|
|`T_IRQ`|配線なし|今回はタッチ不要のため|

> [こちらの記事](https://qiita.com/suzukiplan/items/b5882449fdbf571b4e80)から変更ありません。

# コード (PlatformIO)

```platformio.ini
[env:m5stamps3]
platform = espressif32
framework = arduino
board = esp32-s3-devkitc-1
board_build.mcu = esp32s3
lib_deps = lovyan03/LovyanGFX
```

```src/app.cpp
#include <LovyanGFX.hpp>

// LCD の PIN 割当（ブレッドボードに挿した GPIO ポート番号を指定する）
#define PIN_CS GPIO_NUM_1
#define PIN_DC GPIO_NUM_3
#define PIN_RST GPIO_NUM_5
#define PIN_LED GPIO_NUM_7
#define PIN_MOSI GPIO_NUM_13
#define PIN_SCLK GPIO_NUM_15

class MyLCD : public lgfx::LGFX_Device
{
private:
    lgfx::Panel_ILI9341 panel;
    lgfx::Bus_SPI bus;
    lgfx::Light_PWM light;

    void setupBusConfig() {
        auto config = this->bus.config();
        config.spi_host = SPI3_HOST;
        config.spi_mode = 0;
        config.freq_write = 40000000; 
        config.freq_read  = 16000000;
        config.spi_3wire  = true;
        config.use_lock   = true;
        config.dma_channel = SPI_DMA_CH_AUTO;
        config.pin_sclk = PIN_SCLK;
        config.pin_mosi = PIN_MOSI;
        config.pin_miso = -1;
        config.pin_dc = PIN_DC;
        this->bus.config(config);
    }

    void setupPanelConfig() {
        auto config = this->panel.config();
        config.pin_cs = PIN_CS;
        config.pin_rst = PIN_RST;
        config.pin_busy = -1;
        config.panel_width = 240;
        config.panel_height = 320;
        config.offset_x = 0;
        config.offset_y = 0;
        config.offset_rotation = 2;
        config.dummy_read_pixel = 8;
        config.dummy_read_bits = 1;
        config.readable = false;
        config.invert = false;
        config.rgb_order = false;
        config.dlen_16bit = false;
        config.bus_shared = true;
        this->panel.config(config);
    }

    void setupLightConfig() {
        auto config = this->light.config();
        config.pin_bl = PIN_LED;
        config.invert = false;
        config.freq   = 44100;
        config.pwm_channel = 7;
        this->light.config(config);
    }

public:
    MyLCD(void) {
        this->setupBusConfig();
        this->setupPanelConfig();
        this->setupLightConfig();
        this->panel.setBus(&this->bus);
        this->panel.setLight(&this->light);
        setPanel(&this->panel);
    }
};

static MyLCD lcd;

void setup()
{
    lcd.init();
    lcd.startWrite();
    lcd.setRotation(2);
    lcd.writeFillRect(0, 0, lcd.width(), lcd.height(), 0x003F);
    lcd.setTextSize(2);
    lcd.setTextColor(0xFFFF, 0x0000);
    lcd.setCursor(0, 0);
    lcd.println("Hello, World!");
    lcd.println("Using LovyanGFX.");
    lcd.endWrite();
}

void loop() { }
```

## コード解説

`lgfx::LGFX_Device` のサブクラス `MyLCD` を作成して、Bus、Panel、Light の各種 config を接続先ディスプレイに接続できる構成に変更しています。

未検証ですが、`#define` で定義している `PIN_xxx` のポート番号（`GPIO_NUM_xx`）をカスタマイズすれば ILI9341 の LCD と接続する Arduino 機器全般で問題なく動作するものと思われます。

```c++
#define PIN_CS GPIO_NUM_1
#define PIN_DC GPIO_NUM_3
#define PIN_RST GPIO_NUM_5
#define PIN_LED GPIO_NUM_7
#define PIN_MOSI GPIO_NUM_13
#define PIN_SCLK GPIO_NUM_15
```

## 実行結果

実行結果はこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2161417/bcf8c335-8e21-34c8-0bb2-a0ddd092f559.png)
Adafruit_ILI9341 と比べてだいぶ高速で描画されたので、これならゲームなども実用レベルで実装できそうです。


# Adafruit_ILI9341 vs Lovyan GFX

全く同じ描画処理を Adafruit_ILI9341 と Lovyan GFX で実行した時の描画速度の比較動画を作ってみました。

https://www.youtube.com/watch?v=WdYIcSiD9BM

Adafruit_ILI9341 は起動から描画完了まで2,000ミリ秒程度の時間を要していますが、Lovyan GFX なら即座に描画が完了していることが分かります。