M5Stack AtomS3で外部ディスプレイ(ST7789)を使う

# 概要

1.3インチ240x240ピクセルのフルカラーLCDが安かったので、M5StackのAtomS3を接続し表示させてみました。M5Stackを使っているので、M5Unified/M5GFXを使いました。この記事では[M5Unified](https://github.com/m5stack/M5Unified)/[M5GFX](https://github.com/m5stack/M5GFX)での使い方、およびおまけ的に[Arduino GFX](https://github.com/moononournation/Arduino_GFX)を使った例で説明します。

![PXL_20241013_235039037r.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111095/d52656a9-a884-d43f-8b26-520408454047.jpeg)

M5GFXは[LovyanGFX](https://github.com/lovyan03/LovyanGFX)をM5Stackで使いやすくしたものなので、LovyanGFXでも同じようにできるはずです(というか、参考にしたコードがLovyanGFX)。私はM5Stackで開発するときはM5Unifiedを使うので、同時にインストールされるM5GFXを使う方がスッキリ使えるのです。

ここで使ったのは以下です。

- M5Stack AtomS3 -- https://docs.m5stack.com/ja/core/AtomS3
- 1.3インチLCD -- https://www.amazon.co.jp/gp/product/B07QG93NPB/

## 開発環境

- Windows 10 Pro 22H2 + VSCode 1.94.2 + PlatformIO IDE 3.3.3
- m5stack/M5Unified@^0.1.17
- moononournation/GFX Library for Arduino@^1.4.9

# 配線

接続は以下のように接続しました。
バックライト制御(BLK)を行わない場合、その配線は不要です。

![AtomS3_ST7789.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111095/a629e0fc-7d0c-c4ac-fb75-45c5f4b6971e.png)

# M5Unified/M5GFXで使う

LovyanGFXの[How to useのコード](https://github.com/lovyan03/LovyanGFX/blob/360891428c1f541cc559786444c4bc3eb00d2633/examples/HowToUse/2_user_setting/2_user_setting.ino)を修正(ほぼそのまま)した以下のコードで動作を検証しました。元のコードはいろいろなパラメータを設定してありますが、最低限の設定になるように余計な部分は削除してあります。そのため、詳細を知りたい場合は元のコードを参照して下さい。

このコードでは`ExtDisplay.setBrightness()`をしたときに、AtomS3本体のディスプレイの明るさも変わってしまいます。値を200以下にしてしまうと、本体側は暗くて見えなくなってしまいます(`M5.Display.setBrightness()`を使っても同じ)。外部側の方が明るいので、別々に制御できるとよいのですが…

これを使うことで2画面表示ができるので、応用すると面白いかもしれません！また、別のチップでもこれを修正すればおそらく使えると思います。

```c++:main.cpp
// M5Unified(+M5GFX)で、AtomS3+外部ディスプレイ(ST7789)を使う
// 2024-10-14
// AtomS3: https://docs.m5stack.com/ja/core/AtomS3
// ST7789: https://www.amazon.co.jp/gp/product/B07QG93NPB/

// ST7789のみ使うならM5GFX.hでもよい
#include <M5Unified.h>
#include <lgfx/v1/panel/Panel_ST7789.hpp>

// 使用したピン
// 3.3V -> VCC
// G    -> GND
constexpr int PIN_SCL = 5;  // SCLK(SPI Clock) (SCL)
constexpr int PIN_SDA = 6;  // MOSI (SDA)
constexpr int PIN_RST = 7;  // Reset
constexpr int PIN_DC  = 8;  // Data/Command
constexpr int PIN_BLK = 39; // Backlight

// LovyanGFX: https://github.com/lovyan03/LovyanGFX
// LovyanGFXのHowToUse/2_user_setting/2_user_setting.inoのコードより
// https://github.com/lovyan03/LovyanGFX/blob/3608914/examples/HowToUse/2_user_setting/2_user_setting.ino
class LGFX_AtomS3_SPI_ST7789 : public lgfx::LGFX_Device {
  lgfx::Panel_ST7789  _panel_instance;  // 接続するパネルの型にあったインスタンスを用意
  lgfx::Bus_SPI       _bus_instance;    // パネルを接続するバスの種類にあったインスタンスを用意
  lgfx::Light_PWM     _light_instance;  // バックライト制御が可能な場合はインスタンスを用意(必要なければ削除)

  public:
    LGFX_AtomS3_SPI_ST7789(void) {
      { // バス制御の設定を行います。
        auto cfg = _bus_instance.config();  // バス設定用の構造体を取得

        // 使用するSPIを選択
        // ESP32-S2,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
        // (追記) AtomS3ではSPI2_HOST
        // ※ ESP-IDFバージョンアップに伴い、VSPI_HOST , HSPI_HOSTの記述は非推奨になるため、
        // エラーが出る場合は代わりにSPI2_HOST , SPI3_HOSTを使用してください。
        cfg.spi_host      = SPI2_HOST;
        cfg.spi_mode      = 3;        // SPI通信モードを設定(0-3)
        cfg.pin_sclk      = PIN_SCL;  // SPIのSCLK(SCL)ピン番号を設定
        cfg.pin_mosi      = PIN_SDA;  // SPIのMOSI(SDA)ピン番号を設定
        cfg.pin_miso      = -1;       // SPIのMISOピン番号を設定 (-1 = disable)
        cfg.pin_dc        = PIN_DC;   // SPIのD/C(Data/Command)ピン番号を設定 (-1 = disable)
        // SDカードと共通のSPIバスを使う場合、MISOは省略せず必ず設定してください。
        _bus_instance.config(cfg);    // 設定値をバスに反映します。
        _panel_instance.setBus(&_bus_instance); // バスをパネルにセットします。
      }

      { // 表示パネル制御の設定を行います。
        auto cfg = _panel_instance.config();  // 表示パネル設定用の構造体を取得
        cfg.pin_cs        = -1;       // CSが接続されているピン番号   (-1 = disable)
        cfg.pin_rst       = PIN_RST;  // RSTが接続されているピン番号  (-1 = disable)
        cfg.pin_busy      = -1;       // BUSYが接続されているピン番号 (-1 = disable)
        cfg.panel_width   = 240;      // 実際に表示可能な幅
        cfg.panel_height  = 240;      // 実際に表示可能な高さ
        cfg.invert        = true;     // パネルの明暗が反転してしまう場合 trueに設定
        _panel_instance.config(cfg);
      }

      { // バックライト制御の設定を行います。（必要なければ削除）
        auto cfg = _light_instance.config();  // バックライト設定用の構造体を取得
        cfg.pin_bl = PIN_BLK;         // バックライトが接続されているピン番号
        _light_instance.config(cfg);
        _panel_instance.setLight(&_light_instance); // バックライトをパネルにセット
      }

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

LGFX_AtomS3_SPI_ST7789 ExtDisplay;    // インスタンスを作成

void setup() {
  M5.begin();
  Serial.begin();
  ExtDisplay.init();              // 外部ディスプレイを初期化

  // 明るさは本体画面と共通のため(?)、同じ明るさにはならない
  // M5.Display.setBrightness(), ExtDisplay.setBrightness()のどちらをいじっても同じ
  // AtomS3画面の方が暗い感じ(この設定では200より小さいとほぼ見えなくなる)
  ExtDisplay.setBrightness(200);  // バックライトの明るさ(0-255)

  // 何か描画する
  ExtDisplay.clear(TFT_BLUE);
  ExtDisplay.setFont(&fonts::lgfxJapanGothicP_20);
  ExtDisplay.println("こんにちは、世界！");

  // AtomS3本体の画面も使用可能
  M5.Display.println("Hello, world!");
}

void loop() {
}

```

```ini:platformio.ini
[env:m5stack-atoms3]
platform = espressif32
board = m5stack-atoms3
framework = arduino
lib_deps = 
    m5stack/M5Unified@^0.1.17
build_flags = 
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DARDUINO_USB_MODE=1
```

# Arduino GFXで使う

M5GFXでの使い方がよくわからないときにテスト的に書きました。こちらの方が行数を少なく書けます(バックライト制御はしていません)。

```c++:main.cpp
#include <Arduino_GFX_Library.h>

constexpr int PIN_SCL = 5;  // SCLK(SPI Clock) (SCL)
constexpr int PIN_SDA = 6;  // MOSI (SDA)
constexpr int PIN_RST = 7;  // Reset
constexpr int PIN_DC  = 8;  // Data/Command

void setup() {
  Arduino_DataBus *bus = new Arduino_HWSPI(PIN_DC, GFX_NOT_DEFINED, PIN_SCL, PIN_SDA);
  Arduino_GFX *gfx = new Arduino_ST7789(bus, PIN_RST);
  gfx->begin();
  gfx->invertDisplay(true);
  gfx->fillScreen(BLACK);
  gfx->println("Hello, World!!");
}

void loop() {
}
```

```ini:platformio.ini
[env:m5stack-atoms3]
platform = espressif32
board = m5stack-atoms3
framework = arduino
lib_deps = 
    moononournation/GFX Library for Arduino@^1.4.9
build_flags = 
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DARDUINO_USB_MODE=1
 ```

 # 参考

- LovyanGFXのHowToUse/2_user_setting/2_user_setting.inoのコード
  - https://github.com/lovyan03/LovyanGFX/blob/3608914/examples/HowToUse/2_user_setting/2_user_setting.ino
- M5Stack AtomS3
  - https://docs.m5stack.com/ja/core/AtomS3
- ここで使用した1.3インチLCD(2024年10月に買ったとき900円！安い！)
  - https://www.amazon.co.jp/gp/product/B07QG93NPB/