M5Unifiedで、m5シリーズではないカスタムボードに接続されたLCDを扱えるようにします。
Waveshare ESP32-S3 3.5inch Touch LCD Touchscreen Display N16R8 Development Board
https://ja.aliexpress.com/item/1005009056920045.html
https://www.waveshare.com/esp32-s3-touch-lcd-3.5.htm
LCDのコントロールチップとして、「ST7789」を使っているようです。
コントロールロジックとPIN設定の2つに分かれます。
M5Unifiedは、M5GFXを内部で使っており、LovyanGFXをベースとしているため、コントローラをサポートしている場合がありますが、このコントロールチップはありませんでした。
しかし、LovyanGFXにはあったので、そこから拝借しました。
/*----------------------------------------------------------------------------/
Lovyan GFX - Graphics library for embedded devices.
Original Source:
https://github.com/lovyan03/LovyanGFX/
Licence:
[FreeBSD](https://github.com/lovyan03/LovyanGFX/blob/master/license.txt)
Author:
[lovyan03](https://twitter.com/lovyan03)
Contributors:
[ciniml](https://github.com/ciniml)
[mongonta0716](https://github.com/mongonta0716)
[tobozo](https://github.com/tobozo)
/----------------------------------------------------------------------------*/
#pragma once
#include <lgfx/v1/panel/Panel_LCD.hpp>
namespace lgfx
{
inline namespace v1
{
//----------------------------------------------------------------------------
struct Panel_ST7796 : public Panel_LCD
{
Panel_ST7796(void)
{
_cfg.panel_width = _cfg.memory_width = 320;
_cfg.panel_height = _cfg.memory_height = 480;
_cfg.dummy_read_pixel = 8;
}
protected:
static constexpr uint8_t CMD_FRMCTR1 = 0xB1;
static constexpr uint8_t CMD_FRMCTR2 = 0xB2;
static constexpr uint8_t CMD_FRMCTR3 = 0xB3;
static constexpr uint8_t CMD_INVCTR = 0xB4;
static constexpr uint8_t CMD_DFUNCTR = 0xB6;
static constexpr uint8_t CMD_ETMOD = 0xB7;
static constexpr uint8_t CMD_PWCTR1 = 0xC0;
static constexpr uint8_t CMD_PWCTR2 = 0xC1;
static constexpr uint8_t CMD_PWCTR3 = 0xC2;
static constexpr uint8_t CMD_PWCTR4 = 0xC3;
static constexpr uint8_t CMD_PWCTR5 = 0xC4;
static constexpr uint8_t CMD_VMCTR = 0xC5;
static constexpr uint8_t CMD_GMCTRP1 = 0xE0; // Positive Gamma Correction
static constexpr uint8_t CMD_GMCTRN1 = 0xE1; // Negative Gamma Correction
static constexpr uint8_t CMD_DOCA = 0xE8; // Display Output Ctrl Adjust
static constexpr uint8_t CMD_CSCON = 0xF0; // Command Set Control
const uint8_t* getInitCommands(uint8_t listno) const override {
static constexpr uint8_t list0[] = {
CMD_CSCON, 1, 0xC3, // Enable extension command 2 partI
CMD_CSCON, 1, 0x96, // Enable extension command 2 partII
CMD_INVCTR, 1, 0x01, //1-dot inversion
CMD_DFUNCTR, 3, 0x80, //Display Function Control //Bypass
0x22, //Source Output Scan from S1 to S960, Gate Output scan from G1 to G480, scan cycle=2
0x3B, //LCD Drive Line=8*(59+1)
CMD_DOCA, 8, 0x40,
0x8A,
0x00,
0x00,
0x29, //Source eqaulizing period time= 22.5 us
0x19, //Timing for "Gate start"=25 (Tclk)
0xA5, //Timing for "Gate End"=37 (Tclk), Gate driver EQ function ON
0x33,
CMD_PWCTR2, 1, 0x06, //Power control2 //VAP(GVDD)=3.85+( vcom+vcom offset), VAN(GVCL)=-3.85+( vcom+vcom offset)
CMD_PWCTR3, 1, 0xA7, //Power control 3 //Source driving current level=low, Gamma driving current level=High
CMD_VMCTR, 1+CMD_INIT_DELAY, 0x18, 120, //VCOM Control //VCOM=0.9
CMD_GMCTRP1,14, 0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F,
0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B,
CMD_GMCTRN1,14+CMD_INIT_DELAY,
0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B,
0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B,
120,
CMD_CSCON, 1, 0x3C, //Command Set control // Disable extension command 2 partI
CMD_CSCON, 1, 0x69, //Command Set control // Disable extension command 2 partII
CMD_SLPOUT, 0+CMD_INIT_DELAY, 130, // Exit sleep mode
CMD_IDMOFF, 0,
CMD_DISPON, 0,
0xFF,0xFF, // end
};
switch (listno) {
case 0: return list0;
default: return nullptr;
}
}
void setColorDepth_impl(color_depth_t depth) override
{
_write_depth = ((int)depth & color_depth_t::bit_mask) > 16 ? rgb888_3Byte : rgb565_2Byte;
_read_depth = _write_depth;
}
};
//----------------------------------------------------------------------------
}
}
次が、PIN設定です。
LCDをリセットするために、TCA9554がありますが、ボードによってはない場合もあります。
#include <Arduino.h>
#include "main_config.h"
#ifdef _PANEL_ST7796_
#include <Wire.h>
#include <M5Unified.h>
#include <lgfx/v1/touch/Touch_FT5x06.hpp>
#include "Panel_ST7796.hpp"
#include "TCA9554.h"
int Panel_ST7796_display_index = -1;
int Panel_ST7796_display_type = -1;
static lgfx::Panel_ST7796 _panel;
static lgfx::Bus_SPI _bus;
static lgfx::Touch_FT5x06 _touch;
static lgfx::Light_PWM _light;
static M5GFX _display;
#define PANEL_I2C_SDA 8
#define PANEL_I2C_SCL 7
#define PIN_BACKLIGHT 6
static TCA9554 TCA(0x20);
static void lcd_reset(void) {
TCA.write1(1, 1);
delay(10);
TCA.write1(1, 0);
delay(10);
TCA.write1(1, 1);
delay(200);
}
long Panel_ST7796_Pin_Init(void) {
{
auto cfg = _bus.config();
cfg.spi_host = SPI2_HOST;
cfg.spi_mode = 0;
cfg.freq_write = 40000000;
cfg.freq_read = 16000000;
cfg.pin_sclk = 5;
cfg.pin_mosi = 1;
cfg.pin_miso = 2;
cfg.pin_dc = 3;
_bus.config(cfg);
_panel.setBus(&_bus);
}
{
auto cfg = _panel.config();
cfg.pin_cs = -1;
cfg.pin_rst = -1;
cfg.panel_width = 320;
cfg.panel_height = 480;
cfg.offset_x = 0;
cfg.offset_y = 0;
cfg.bus_shared = false;
cfg.invert = true;
_panel.config(cfg);
}
{
auto cfg = _light.config();
cfg.pin_bl = PIN_BACKLIGHT;
cfg.invert = false;
cfg.freq = 44100;
cfg.pwm_channel = 7;
_light.config(cfg);
_panel.setLight(&_light);
}
{
auto cfg = _touch.config();
cfg.freq = 100000;
cfg.x_min = 0;
cfg.x_max = 319;
cfg.y_min = 0;
cfg.y_max = 479;
cfg.pin_sda = PANEL_I2C_SDA;
cfg.pin_scl = PANEL_I2C_SCL;
cfg.i2c_port = 0;
cfg.i2c_addr = 0x38;
cfg.bus_shared = false;
cfg.pin_int = -1;
cfg.pin_rst = -1;
_touch.config(cfg);
_panel.setTouch(&_touch);
}
Wire.begin(PANEL_I2C_SDA, PANEL_I2C_SCL);
TCA.begin();
TCA.pinMode1(1, OUTPUT);
lcd_reset();
// _display.init(&_panel);
// M5.addDisplay(_display);
M5.Display.init(&_panel);
Wire.end();
Panel_ST7796_display_index = M5.getDisplayCount() - 1;
Panel_ST7796_display_type = m5gfx::board_t::board_unknown;
return 0;
}
#endif
あとはこんな感じで使います。
auto cfg = M5.config();
M5.begin(cfg);
delay(500);
Panel_ST7796_Pin_Init();
M5.setPrimaryDisplay(Panel_ST7796_display_index);
WT32-SC01
次はちょっと古いですが、コントロールチップが「ST7789」を使っています。
ちょうどこのコントロールロジックがついていたのでそのまま使います。
lgfx/v1/panel/Panel_ST7789.hpp
次が、PIN設定です。
#include <Arduino.h>
#include "main_config.h"
#ifdef _PANEL_ST7789_
#include <M5Unified.h>
#include <lgfx/v1/panel/Panel_ST7789.hpp>
#include <lgfx/v1/touch/Touch_FT5x06.hpp>
int Panel_ST7789_display_index = -1;
int Panel_ST7789_display_type = -1;
static lgfx::Panel_ST7789 _panel;
static lgfx::Bus_SPI _bus;
static lgfx::Light_PWM _light;
static lgfx::Touch_FT5x06 _touch;
//static M5GFX _display;
// WT32-SC01 pin assignments
// LCD (ST7796): SPI2/HSPI
// SCLK=14, MOSI=13, MISO=12, DC=21, CS=15, RST=22, BL=23
// Touch (FT6336U / FT5x06 compatible): I2C
// SDA=18, SCL=19, INT=39
long Panel_ST7789_Pin_Init(void) {
{
auto cfg = _bus.config();
cfg.spi_host = SPI2_HOST;
cfg.spi_mode = 0;
cfg.freq_write = 40000000;
cfg.freq_read = 16000000;
cfg.spi_3wire = false;
cfg.pin_sclk = 14;
cfg.pin_mosi = 13;
cfg.pin_miso = 12;
cfg.pin_dc = 21;
_bus.config(cfg);
_panel.setBus(&_bus);
}
// Panel (ST7796 / 320x480, CS=15, RST=22)
{
auto cfg = _panel.config();
cfg.pin_cs = 15;
cfg.pin_rst = 22;
cfg.pin_busy = -1;
cfg.memory_width = 320;
cfg.memory_height = 480;
cfg.panel_width = 320;
cfg.panel_height = 480;
cfg.offset_x = 0;
cfg.offset_y = 0;
cfg.offset_rotation = 6;
cfg.dummy_read_pixel = 8;
cfg.dummy_read_bits = 1;
cfg.readable = false;
cfg.invert = false;
cfg.rgb_order = false;
cfg.dlen_16bit = false;
cfg.bus_shared = false;
_panel.config(cfg);
}
// Backlight (BL=23, PWM ch7)
{
auto cfg = _light.config();
cfg.pin_bl = 23;
cfg.invert = false;
cfg.freq = 44100;
cfg.pwm_channel = 7;
_light.config(cfg);
_panel.setLight(&_light);
}
// Touch (FT6336U / FT5x06: I2C1, SDA=18, SCL=19, INT=39, addr=0x38)
{
auto cfg = _touch.config();
cfg.x_min = 319;
cfg.x_max = 0;
cfg.y_min = 479;
cfg.y_max = 0;
cfg.pin_int = 39;
cfg.bus_shared = false;
cfg.offset_rotation = 0;
cfg.i2c_port = 1;
cfg.i2c_addr = 0x38;
cfg.pin_sda = 18;
cfg.pin_scl = 19;
cfg.freq = 400000;
_touch.config(cfg);
_panel.setTouch(&_touch);
}
// _display.init(&_panel);
// M5.addDisplay(_display);
M5.Display.init(&_panel);
Panel_ST7789_display_index = M5.getDisplayCount() - 1;
Panel_ST7789_display_type = m5gfx::board_t::board_unknown;
return 0;
};
#endif
使うときには以下です。
実はちょうどこのボードは、GPIO22を使っており、M5Unifiedが内部的につかんでおり、解放してから初期化しました。
auto cfg = M5.config();
M5.begin(cfg);
delay(500);
M5.In_I2C.release();
gpio_reset_pin(GPIO_NUM_21);
gpio_reset_pin(GPIO_NUM_22);
Panel_ST7789_Pin_Init();
M5.setPrimaryDisplay(Panel_ST7789_display_index);
以上