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出力用 |
- 使用ボード:ESP32-DevKitC-32E
- LCD購入先:4.0 inch TFT LCD (SKU: MSP4020)
- LCD情報:4.0inch SPI Module ST7796
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チャンネルが被らないようにします。