ESP32 Arduino2 LovyanGFX1 4.0インチSPI LCD駆動

ESP32からArduinoCoreを使用してLovyanGFXを使用、4インチLCDに出力する方法。

 

環境について

- 環境 バージョン 概要
開発環境 Arduino 2.2.1 Arduino IDE使用
ボード定義 esp32 2.0.14 ESP32 Dev Module設定
ライブラリ LovyanGFX 1.1.12 LCD出力用

 

LCD接続情報

以下のように接続しました。

 

LCDピン ESP32接続先ピン 概要
VCC 5V USB電源で駆動
GND GND -
CS GPIO 5 (VSPI_SS) デフォルトのVSPIピン使用
RESET GPIO 16 LCDリセットピン
DC/RS GPIO 17 LCDコマンド制御ピン
SDI (MOSI) GPIO 23 (VSPI_MOSI) デフォルトのVSPIピン使用
SCK GPIO 18 (VSPI_SCK) デフォルトのVSPIピン使用
LED GPIO 4 LED調光用・HIGHでLCDバックライト点灯
SDO (MISO) GPIO 19 (VSPI_MISO) デフォルトのVSPIピン使用

 

 

Arduinoコード

LovyanGFXの2_user_setting.inoを参考にコードを書いていきます。

#include <LovyanGFX.hpp>

class LGFX : public lgfx::LGFX_Device
{
  lgfx::Panel_ST7796      _panel_instance;
  lgfx::Bus_SPI        _bus_instance;
  lgfx::Light_PWM     _light_instance;

public:
  LGFX(void)
  {
    {
      auto cfg = _bus_instance.config();
      cfg.spi_host = VSPI_HOST;
      cfg.spi_mode = 0;
      cfg.freq_write = 15000000;
      cfg.freq_read  = 6000000;
      cfg.use_lock   = true;
      cfg.dma_channel = SPI_DMA_CH_AUTO;
      cfg.pin_sclk = 18;
      cfg.pin_mosi = 23;
      cfg.pin_miso = 19;
      cfg.pin_dc   = 17;
      _bus_instance.config(cfg);
      _panel_instance.setBus(&_bus_instance);
    }
    {
      auto cfg = _panel_instance.config();
      cfg.pin_cs           =    5;
      cfg.pin_rst          =    16;
      cfg.pin_busy         =    -1;
      cfg.panel_width      =   320;
      cfg.panel_height     =   480;
      _panel_instance.config(cfg);
    }
    {
      auto cfg = _light_instance.config();
      cfg.pin_bl = 4;
      cfg.invert = false;
      cfg.freq   = 44100;
      cfg.pwm_channel = 7;
      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);
    }
    setPanel(&_panel_instance);
  }
};

LGFX display;

void setup(void)
{
  display.init();
  display.setBrightness(128);
  display.setTextSize((std::max(display.width(), display.height()) + 255) >> 8);
  display.fillScreen(TFT_BLACK);
}

uint32_t count = ~0;
void loop(void)
{
  display.startWrite();
  display.setRotation(++count & 7);
  display.setColorDepth((count & 8) ? 16 : 24);

  display.setTextColor(TFT_WHITE);
  display.drawNumber(display.getRotation(), 16, 0);

  display.setTextColor(0xFF0000U);
  display.drawString("R", 30, 16);
  display.setTextColor(0x00FF00U);
  display.drawString("G", 40, 16);
  display.setTextColor(0x0000FFU);
  display.drawString("B", 50, 16);

  display.drawRect(30,30,display.width()-60,display.height()-60,count*7);
  display.drawFastHLine(0, 0, 10);

  display.endWrite();
}

ST7796のデータシートを参考(TSCYCW:(min)66ns≒15MHz / TSCYCR :(min)150ns≒6MHz)に動作クロックを設定しましたが、データシート記載の速度以上(80MHz)でもなんとか動作しています。

個体差で表示がうまくいかない場合もあるとは思うので、データシート記載の動作クロックに従うのが良いと思われます。

 

LCD接続情報(2画面接続テスト)

次に2画面出力のテストです。

以下のように接続しました。

 

LCD1ピン ESP32接続先ピン 概要
VCC 5V USB電源で駆動
GND GND -
CS GPIO 5 (VSPI_SS) デフォルトのVSPIピン使用
RESET GPIO 16 LCDリセットピン・個別にリセットできるように
DC/RS GPIO 2 LCDコマンド制御ピン(共通)
SDI (MOSI) GPIO 23 (VSPI_MOSI) デフォルトのVSPIピン使用(共通)
SCK GPIO 18 (VSPI_SCK) デフォルトのVSPIピン使用(共通)
LED GPIO 32 LED調光用・HIGHでLCDバックライト点灯・個別に制御できるように
SDO (MISO) GPIO 19 (VSPI_MISO) デフォルトのVSPIピン使用(共通)

 

LCD2ピン ESP32接続先ピン 概要
VCC 5V USB電源で駆動
GND GND -
CS GPIO 4 CSでSPIを振り分けています
RESET GPIO 17 LCDリセットピン・個別にリセットできるように
DC/RS GPIO 2 LCDコマンド制御ピン(共通)
SDI (MOSI) GPIO 23 (VSPI_MOSI) デフォルトのVSPIピン使用(共通)
SCK GPIO 18 (VSPI_SCK) デフォルトのVSPIピン使用(共通)
LED GPIO 33 LED調光用・HIGHでLCDバックライト点灯・個別に制御できるように
SDO (MISO) GPIO 19 (VSPI_MISO) デフォルトのVSPIピン使用(共通)

 

 

Arduinoコード(2画面接続テスト)

#include <LovyanGFX.hpp>

class LGFX1 : public lgfx::LGFX_Device
{
  lgfx::Panel_ST7796      _panel_instance;
  lgfx::Bus_SPI        _bus_instance;
  lgfx::Light_PWM     _light_instance;

public:
  LGFX1(void)
  {
    {
      auto cfg = _bus_instance.config();
      cfg.spi_host = VSPI_HOST;
      cfg.spi_mode = 0;
      cfg.freq_write = 15000000;
      cfg.freq_read  = 6000000;
      cfg.use_lock   = true;
      cfg.dma_channel = SPI_DMA_CH_AUTO;
      cfg.pin_sclk = 18;
      cfg.pin_mosi = 23;
      cfg.pin_miso = 19;
      cfg.pin_dc   = 2;
      _bus_instance.config(cfg);
      _panel_instance.setBus(&_bus_instance);
    }
    {
      auto cfg = _panel_instance.config();
      cfg.pin_cs           =    5;
      cfg.pin_rst          =    16;
      cfg.pin_busy         =    -1;
      cfg.panel_width      =   320;
      cfg.panel_height     =   480;
      _panel_instance.config(cfg);
    }
    {
      auto cfg = _light_instance.config();
      cfg.pin_bl = 32;
      cfg.invert = false;
      cfg.freq   = 44100;
      cfg.pwm_channel = 6;
      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);
    }
    setPanel(&_panel_instance);
  }
};


class LGFX2 : public lgfx::LGFX_Device
{
  lgfx::Panel_ST7796      _panel_instance;
  lgfx::Bus_SPI        _bus_instance;
  lgfx::Light_PWM     _light_instance;

public:
  LGFX2(void)
  {
    {
      auto cfg = _bus_instance.config();
      cfg.spi_host = VSPI_HOST;
      cfg.spi_mode = 0;
      cfg.freq_write = 15000000;
      cfg.freq_read  = 6000000;
      cfg.use_lock   = true;
      cfg.dma_channel = SPI_DMA_CH_AUTO;
      cfg.pin_sclk = 18;
      cfg.pin_mosi = 23;
      cfg.pin_miso = 19;
      cfg.pin_dc   = 2;
      _bus_instance.config(cfg);
      _panel_instance.setBus(&_bus_instance);
    }
    {
      auto cfg = _panel_instance.config();
      cfg.pin_cs           =    4;
      cfg.pin_rst          =    17;
      cfg.pin_busy         =    -1;
      cfg.panel_width      =   320;
      cfg.panel_height     =   480;
      _panel_instance.config(cfg);
    }
    {
      auto cfg = _light_instance.config();
      cfg.pin_bl = 33;
      cfg.invert = false;
      cfg.freq   = 44100;
      cfg.pwm_channel = 7;
      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);
    }
    setPanel(&_panel_instance);
  }
};

LGFX1 display1;
LGFX2 display2;

void setup(void)
{
  display1.init();
  display1.setBrightness(128);
  display1.setTextSize((std::max(display1.width(), display1.height()) + 255) >> 8);
  display1.fillScreen(TFT_BLACK);

  display2.init();
  display2.setBrightness(128);
  display2.setTextSize((std::max(display2.width(), display2.height()) + 255) >> 8);
  display2.fillScreen(TFT_BLUE);
}

uint32_t count1 = ~0;
uint32_t count2 = ~0;
void loop(void)
{
  display1.startWrite();
  display1.setRotation(++count1 & 7);
  display1.setColorDepth((count1 & 8) ? 16 : 24);

  display1.setTextColor(TFT_WHITE);
  display1.drawNumber(display1.getRotation(), 16, 0);

  display1.setTextColor(0xFF0000U);
  display1.drawString("R", 30, 16);
  display1.setTextColor(0x00FF00U);
  display1.drawString("G", 40, 16);
  display1.setTextColor(0x0000FFU);
  display1.drawString("B", 50, 16);

  display1.drawRect(30,30,display1.width()-60,display1.height()-60,count1*7);
  display1.drawFastHLine(0, 0, 10);

  display1.endWrite();

  display2.startWrite();
  display2.setRotation(++count2 & 7);
  display2.setColorDepth((count2 & 8) ? 16 : 24);

  display2.setTextColor(TFT_WHITE);
  display2.drawNumber(display2.getRotation(), 16, 0);

  display2.setTextColor(0xFF0000U);
  display2.drawString("R", 30, 16);
  display2.setTextColor(0x00FF00U);
  display2.drawString("G", 40, 16);
  display2.setTextColor(0x0000FFU);
  display2.drawString("B", 50, 16);

  display2.drawRect(30,30,display2.width()-60,display2.height()-60,count2*7);
  display2.drawFastHLine(0, 0, 10);

  display2.endWrite();
}

SPIバスとDC/RSピンを共通にし、それ以外のピンを個別につなぐことによって2画面制御をしています。

それぞれピン設定をしてインスタンスを生成しています。

また、バックライト制御も有効にしているので、PWMチャンネルが被らないようにします。