sTodo-m5paper-client/libraries/M5GFX/src/M5GFX.cpp
2025-06-30 20:47:33 +02:00

2144 lines
71 KiB
C++

// Copyright (c) M5Stack. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "M5GFX.h"
#if defined ( ESP_PLATFORM )
#include <cstdint>
#include <sdkconfig.h>
#include <nvs.h>
#include <esp_log.h>
#include <driver/i2c.h>
#include <soc/efuse_reg.h>
#include <soc/gpio_reg.h>
#include "lgfx/v1/panel/Panel_ILI9342.hpp"
#include "lgfx/v1/panel/Panel_ST7735.hpp"
#include "lgfx/v1/panel/Panel_ST7789.hpp"
#include "lgfx/v1/panel/Panel_GC9A01.hpp"
#include "lgfx/v1/panel/Panel_GDEW0154M09.hpp"
#include "lgfx/v1/panel/Panel_GDEW0154D67.hpp"
#include "lgfx/v1/panel/Panel_IT8951.hpp"
#include "lgfx/v1/touch/Touch_CST816S.hpp"
#include "lgfx/v1/touch/Touch_FT5x06.hpp"
#include "lgfx/v1/touch/Touch_GT911.hpp"
#if defined ( CONFIG_IDF_TARGET_ESP32P4 )
#include "lgfx/v1/platforms/esp32p4/Panel_ILI9881C.hpp"
static constexpr int_fast16_t in_i2c_port = I2C_NUM_1;
#elif defined ( CONFIG_IDF_TARGET_ESP32S3 )
// for M5PaperS3
#if defined (CONFIG_ESP32S3_SPIRAM_SUPPORT) && defined (CONFIG_SPIRAM_MODE_OCT)
#include <lgfx/v1/platforms/esp32/Panel_EPD.hpp>
#endif
#endif
#else
#include "lgfx/v1/platforms/sdl/Panel_sdl.hpp"
#include "picture_frame/picture_frame.h"
#endif
namespace m5gfx
{
static constexpr char LIBRARY_NAME[] = "M5GFX";
M5GFX* M5GFX::_instance = nullptr;
M5GFX::M5GFX(void) : LGFX_Device()
{
if (_instance == nullptr) _instance = this;
}
#if defined ( ESP_PLATFORM )
void i2c_write_register8_array(int_fast16_t i2c_port, uint_fast8_t i2c_addr, const uint8_t* reg_data_mask, uint32_t freq)
{
while (reg_data_mask[0] != 0xFF || reg_data_mask[1] != 0xFF || reg_data_mask[2] != 0xFF)
{
lgfx::i2c::writeRegister8(i2c_port, i2c_addr, reg_data_mask[0], reg_data_mask[1], reg_data_mask[2], freq);
reg_data_mask += 3;
}
}
static constexpr std::uint_fast8_t pi4io1_i2c_addr = 0x43;
static constexpr std::uint_fast8_t pi4io2_i2c_addr = 0x44;
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
static constexpr std::int32_t axp_i2c_freq = 400000;
static constexpr std::uint_fast8_t axp_i2c_addr = 0x34;
static constexpr std::int_fast16_t axp_i2c_port = I2C_NUM_1;
static constexpr std::int_fast16_t axp_i2c_sda = GPIO_NUM_21;
static constexpr std::int_fast16_t axp_i2c_scl = GPIO_NUM_22;
struct Panel_M5Stack : public lgfx::Panel_ILI9342
{
Panel_M5Stack(void)
{
_cfg.pin_cs = GPIO_NUM_14;
_cfg.pin_rst = GPIO_NUM_33;
_cfg.offset_rotation = 3;
_rotation = 1;
}
bool init(bool use_reset) override
{
_cfg.invert = lgfx::gpio::command(
(const uint8_t[]) {
lgfx::gpio::command_mode_output , GPIO_NUM_33,
lgfx::gpio::command_write_low , GPIO_NUM_33,
lgfx::gpio::command_mode_input_pulldown, GPIO_NUM_33,
lgfx::gpio::command_write_high , GPIO_NUM_33,
lgfx::gpio::command_read , GPIO_NUM_33,
lgfx::gpio::command_mode_output , GPIO_NUM_33,
lgfx::gpio::command_end
});
return lgfx::Panel_ILI9342::init(use_reset);
}
};
struct Panel_M5StackCore2 : public lgfx::Panel_ILI9342
{
Panel_M5StackCore2(void)
{
_cfg.pin_cs = GPIO_NUM_5;
_cfg.invert = true;
_cfg.offset_rotation = 3;
_rotation = 1; // default rotation
}
void rst_control(bool level) override
{
uint8_t bits = level ? 2 : 0;
uint8_t mask = level ? ~0 : ~2;
// AXP192 reg 0x96 = GPIO3&4 control
lgfx::i2c::writeRegister8(axp_i2c_port, axp_i2c_addr, 0x96, bits, mask, axp_i2c_freq);
}
};
struct Light_M5StackCore2 : public lgfx::ILight
{
bool init(std::uint8_t brightness) override
{
setBrightness(brightness);
return true;
}
void setBrightness(std::uint8_t brightness) override
{
if (brightness)
{
brightness = (brightness >> 3) + 72;
lgfx::i2c::bitOn(axp_i2c_port, axp_i2c_addr, 0x12, 0x02, axp_i2c_freq); // DC3 enable
}
else
{
lgfx::i2c::bitOff(axp_i2c_port, axp_i2c_addr, 0x12, 0x02, axp_i2c_freq); // DC3 disable
}
// AXP192 reg 0x27 = DC3
lgfx::i2c::writeRegister8(axp_i2c_port, axp_i2c_addr, 0x27, brightness, 0x80, axp_i2c_freq);
}
};
struct Light_M5StackCore2_AXP2101 : public lgfx::ILight
{
bool init(std::uint8_t brightness) override
{
setBrightness(brightness);
return true;
}
void setBrightness(std::uint8_t brightness) override
{
// BLDO1
if (brightness)
{
brightness = ((brightness + 641) >> 5);
lgfx::i2c::bitOn(axp_i2c_port, axp_i2c_addr, 0x90, 0x10, axp_i2c_freq); // BLDO1 enable
}
else
{
lgfx::i2c::bitOff(axp_i2c_port, axp_i2c_addr, 0x90, 0x10, axp_i2c_freq); // BLDO1 disable
}
// AXP192 reg 0x96 = BLO1 voltage setting (0.5v ~ 3.5v 100mv/step)
lgfx::i2c::writeRegister8(axp_i2c_port, axp_i2c_addr, 0x96, brightness, 0, axp_i2c_freq);
}
};
struct Light_M5Tough : public lgfx::ILight
{
bool init(std::uint8_t brightness) override
{
setBrightness(brightness);
return true;
}
void setBrightness(std::uint8_t brightness) override
{
if (brightness)
{
if (brightness > 4)
{
brightness = (brightness / 24) + 5;
}
lgfx::i2c::bitOn(axp_i2c_port, axp_i2c_addr, 0x12, 0x08, axp_i2c_freq); // LDO3 enable
}
else
{
lgfx::i2c::bitOff(axp_i2c_port, axp_i2c_addr, 0x12, 0x08, axp_i2c_freq); // LDO3 disable
}
lgfx::i2c::writeRegister8(axp_i2c_port, axp_i2c_addr, 0x28, brightness, 0xF0, axp_i2c_freq);
}
};
struct Touch_M5Tough : public lgfx::ITouch
{
void wakeup(void) override {}
void sleep(void) override {}
bool init(void) override
{
_inited = false;
if (isSPI()) return false;
if (_cfg.pin_int >= 0)
{
lgfx::pinMode(_cfg.pin_int, lgfx::pin_mode_t::input);
}
_inited = lgfx::i2c::init(_cfg.i2c_port, _cfg.pin_sda, _cfg.pin_scl).has_value();
static constexpr uint8_t irq_modechange_cmd[] = { 0x5a, 0x5a }; /// (INT mode change)
lgfx::i2c::transactionWrite(_cfg.i2c_port, _cfg.i2c_addr, irq_modechange_cmd, 2);
return _inited;
}
std::uint_fast8_t getTouchRaw(touch_point_t *tp, std::uint_fast8_t count) override
{
if (tp) tp->size = 0;
if (!_inited || count == 0) return 0;
if (count > 2) count = 2; // max 2 point.
if (_cfg.pin_int >= 0)
{
if (gpio_in(_cfg.pin_int)) return 0;
}
std::size_t len = 3 + count * 6;
std::uint8_t buf[2][len];
std::int32_t retry = 5;
bool flip = false;
std::uint8_t* tmp;
for (;;)
{
tmp = buf[flip];
memset(tmp, 0, len);
if (lgfx::i2c::beginTransaction(_cfg.i2c_port, _cfg.i2c_addr, _cfg.freq, false))
{
static constexpr std::uint8_t reg_number = 2;
if (lgfx::i2c::writeBytes(_cfg.i2c_port, &reg_number, 1)
&& lgfx::i2c::restart(_cfg.i2c_port, _cfg.i2c_addr, _cfg.freq, true)
&& lgfx::i2c::readBytes(_cfg.i2c_port, tmp, 1)
&& (tmp[0] != 0))
{
flip = !flip;
std::size_t points = std::min<std::uint_fast8_t>(count, tmp[0]);
if (points && lgfx::i2c::readBytes(_cfg.i2c_port, &tmp[1], points * 6 - 2))
{}
}
if (lgfx::i2c::endTransaction(_cfg.i2c_port)) {}
if (tmp[0] == 0 || memcmp(buf[0], buf[1], len) == 0) break;
}
if (0 == --retry) return 0;
}
if (count > tmp[0]) count = tmp[0];
for (std::size_t idx = 0; idx < count; ++idx)
{
auto data = &tmp[1 + idx * 6];
tp[idx].size = 1;
tp[idx].x = (data[0] & 0x0F) << 8 | data[1];
tp[idx].y = (data[2] & 0x0F) << 8 | data[3];
tp[idx].id = idx;
}
return count;
}
};
struct Panel_M5StickC : public lgfx::Panel_ST7735S
{
Panel_M5StickC(void)
{
_cfg.invert = true;
_cfg.pin_cs = GPIO_NUM_5;
_cfg.pin_rst = GPIO_NUM_18;
_cfg.panel_width = 80;
_cfg.panel_height = 160;
_cfg.offset_x = 26;
_cfg.offset_y = 1;
_cfg.offset_rotation = 2;
}
protected:
const std::uint8_t* getInitCommands(std::uint8_t listno) const override
{
static constexpr std::uint8_t list[] = {
CMD_GAMMASET, 1, 0x08, // Gamma set, curve 4
0xFF,0xFF, // end
};
if (listno == 2) return list;
return Panel_ST7735S::getInitCommands(listno);
}
};
struct Light_M5StickC : public lgfx::ILight
{
bool init(std::uint8_t brightness) override
{
lgfx::i2c::init(axp_i2c_port, axp_i2c_sda, axp_i2c_scl);
lgfx::i2c::writeRegister8(axp_i2c_port, axp_i2c_addr, 0x12, 0x4D, ~0, axp_i2c_freq);
setBrightness(brightness);
return true;
}
void setBrightness(std::uint8_t brightness) override
{
if (brightness)
{
brightness = (((brightness >> 1) + 8) / 13) + 5;
lgfx::i2c::bitOn(axp_i2c_port, axp_i2c_addr, 0x12, 1 << 2, axp_i2c_freq);
}
else
{
lgfx::i2c::bitOff(axp_i2c_port, axp_i2c_addr, 0x12, 1 << 2, axp_i2c_freq);
}
lgfx::i2c::writeRegister8(axp_i2c_port, axp_i2c_addr, 0x28, brightness << 4, 0x0F, axp_i2c_freq);
}
};
struct Panel_M5StickCPlus : public lgfx::Panel_ST7789
{
Panel_M5StickCPlus(void)
{
_cfg.invert = true;
_cfg.pin_cs = GPIO_NUM_5;
_cfg.pin_rst = GPIO_NUM_18;
_cfg.panel_width = 135;
_cfg.panel_height = 240;
_cfg.offset_x = 52;
_cfg.offset_y = 40;
}
};
#elif defined (CONFIG_IDF_TARGET_ESP32S3)
static constexpr int32_t i2c_freq = 400000;
static constexpr int_fast16_t aw9523_i2c_addr = 0x58; // AW9523B
static constexpr int_fast16_t axp_i2c_addr = 0x34; // AXP2101
static constexpr int_fast16_t gc0308_i2c_addr = 0x21; // GC0308
static constexpr int_fast16_t i2c_port = I2C_NUM_1;
static constexpr int_fast16_t i2c_sda = GPIO_NUM_12;
static constexpr int_fast16_t i2c_scl = GPIO_NUM_11;
struct Panel_M5StackCoreS3 : public lgfx::Panel_ILI9342
{
Panel_M5StackCoreS3(void)
{
_cfg.pin_cs = GPIO_NUM_3;
_cfg.invert = true;
_cfg.offset_rotation = 3;
_rotation = 1; // default rotation
}
void rst_control(bool level) override
{
uint8_t bits = level ? (1<<5) : 0;
uint8_t mask = level ? ~0 : ~(1<<5);
// LCD_RST
lgfx::i2c::writeRegister8(i2c_port, aw9523_i2c_addr, 0x03, bits, mask, i2c_freq);
}
void cs_control(bool flg) override
{
lgfx::Panel_ILI9342::cs_control(flg);
// CS操作時にGPIO35の役割を切り替える (MISO or D/C);
// FSPIQ_IN_IDX==FSPI MISO / SIG_GPIO_OUT_IDX==GPIO OUT
// *(volatile uint32_t*)GPIO_FUNC35_OUT_SEL_CFG_REG = flg ? FSPIQ_OUT_IDX : SIG_GPIO_OUT_IDX;
// CS HIGHの場合はGPIO出力を無効化し、MISO入力として機能させる。
// CS LOW の場合はGPIO出力を有効化し、D/Cとして機能させる。
*(volatile uint32_t*)( flg
? GPIO_ENABLE1_W1TC_REG
: GPIO_ENABLE1_W1TS_REG
) = 1u << (GPIO_NUM_35 & 31);
}
};
struct Touch_M5StackCoreS3 : public lgfx::Touch_FT5x06
{
Touch_M5StackCoreS3(void)
{
_cfg.pin_int = GPIO_NUM_21;
_cfg.pin_sda = i2c_sda;
_cfg.pin_scl = i2c_scl;
_cfg.i2c_addr = 0x38;
_cfg.i2c_port = i2c_port;
_cfg.freq = i2c_freq;
_cfg.x_min = 0;
_cfg.x_max = 319;
_cfg.y_min = 0;
_cfg.y_max = 239;
_cfg.bus_shared = false;
}
uint_fast8_t getTouchRaw(touch_point_t* tp, uint_fast8_t count) override
{
uint_fast8_t res = 0;
if (!gpio_in(_cfg.pin_int))
{
res = lgfx::Touch_FT5x06::getTouchRaw(tp, count);
if (res == 0)
{ /// clear INT.
// レジスタ 0x00を読み出すとPort0のINTがクリアされ、レジスタ 0x01を読み出すとPort1のINTがクリアされる。
lgfx::i2c::readRegister8(i2c_port, aw9523_i2c_addr, 0x00, i2c_freq);
lgfx::i2c::readRegister8(i2c_port, aw9523_i2c_addr, 0x01, i2c_freq);
}
}
return res;
}
};
struct Light_M5StackCoreS3 : public lgfx::ILight
{
bool init(uint8_t brightness) override
{
setBrightness(brightness);
return true;
}
void setBrightness(uint8_t brightness) override
{
if (brightness)
{
brightness = ((brightness + 641) >> 5);
// AXP2101 reg 0x90 = LDOS ON/OFF control
lgfx::i2c::bitOn(i2c_port, axp_i2c_addr, 0x90, 0x80, i2c_freq); // DLDO1 enable
}
else
{
lgfx::i2c::bitOff(i2c_port, axp_i2c_addr, 0x90, 0x80, i2c_freq); // DLDO1 disable
}
// AXP2101 reg 0x99 = DLDO1 voltage setting
lgfx::i2c::writeRegister8(i2c_port, axp_i2c_addr, 0x99, brightness, 0, i2c_freq);
}
};
struct Light_M5StackAtomS3R : public lgfx::ILight
{
bool init(uint8_t brightness) override
{
lgfx::i2c::init(i2c_port, GPIO_NUM_45, GPIO_NUM_0);
lgfx::i2c::writeRegister8(i2c_port, 48, 0x00, 0b01000000, 0, i2c_freq);
lgfx::delay(1);
lgfx::i2c::writeRegister8(i2c_port, 48, 0x08, 0b00000001, 0, i2c_freq);
lgfx::i2c::writeRegister8(i2c_port, 48, 0x70, 0b00000000, 0, i2c_freq);
setBrightness(brightness);
return true;
}
void setBrightness(uint8_t brightness) override
{
lgfx::i2c::writeRegister8(i2c_port, 48, 0x0e, brightness, 0, i2c_freq);
}
};
struct Light_M5StackStampPLC : public lgfx::ILight
{
bool _is_backlight_inited = false;
bool init(uint8_t brightness) override
{
lgfx::i2c::init(i2c_port, GPIO_NUM_13, GPIO_NUM_15);
// set direction: output
auto value = lgfx::i2c::readRegister8(i2c_port, pi4io1_i2c_addr, 0x03, i2c_freq).has_value();
value |= (1 << 7);
lgfx::i2c::writeRegister8(i2c_port, pi4io1_i2c_addr, 0x03, value, 0, i2c_freq);
// set pull mode: down
value = lgfx::i2c::readRegister8(i2c_port, pi4io1_i2c_addr, 0x0d, i2c_freq).has_value();
value &= ~(1 << 7);
lgfx::i2c::writeRegister8(i2c_port, pi4io1_i2c_addr, 0x0d, value, 0, i2c_freq);
// set high impedance: off
value = lgfx::i2c::readRegister8(i2c_port, pi4io1_i2c_addr, 0x07, i2c_freq).has_value();
value &= ~(1 << 7);
lgfx::i2c::writeRegister8(i2c_port, pi4io1_i2c_addr, 0x07, value, 0, i2c_freq);
_is_backlight_inited = true;
setBrightness(brightness);
return true;
}
void setBrightness(uint8_t brightness) override
{
if (!_is_backlight_inited) init(127);
auto value = lgfx::i2c::readRegister8(i2c_port, pi4io1_i2c_addr, 0x05, i2c_freq).has_value();
if (brightness == 0) {
value |= (1 << 7);
} else {
value &= ~(1 << 7);
}
lgfx::i2c::writeRegister8(i2c_port, pi4io1_i2c_addr, 0x05, value, 0, i2c_freq);
}
};
#endif
__attribute__ ((unused))
static void _pin_level(std::int_fast16_t pin, bool level)
{
lgfx::pinMode(pin, lgfx::pin_mode_t::output);
if (level) lgfx::gpio_hi(pin);
else lgfx::gpio_lo(pin);
}
__attribute__ ((unused))
static void _pin_reset(std::int_fast16_t pin, bool use_reset)
{
lgfx::gpio_hi(pin);
lgfx::pinMode(pin, lgfx::pin_mode_t::output);
lgfx::delay(1);
if (!use_reset) return;
lgfx::gpio_lo(pin);
lgfx::delay(2);
lgfx::gpio_hi(pin);
lgfx::delay(10);
}
/// TF card dummy clock送信 ;
static void _send_sd_dummy_clock(int spi_host, int_fast16_t pin_cs)
{
static constexpr uint32_t dummy_clock[] = { ~0u, ~0u, ~0u, ~0u };
_pin_level(pin_cs, true);
m5gfx::spi::writeBytes(spi_host, (const uint8_t*)dummy_clock, sizeof(dummy_clock));
_pin_level(pin_cs, false);
}
/// TF card をSPIモードに移行する ;
__attribute__ ((unused))
static void _set_sd_spimode(int spi_host, int_fast16_t pin_cs)
{
m5gfx::spi::beginTransaction(spi_host, 400000, 0);
_send_sd_dummy_clock(spi_host, pin_cs);
uint8_t sd_cmd58[] = { 0x7A, 0, 0, 0, 0, 0xFD, 0xFF, 0xFF }; // READ_OCR command.
m5gfx::spi::readBytes(spi_host, sd_cmd58, sizeof(sd_cmd58));
if (sd_cmd58[6] == sd_cmd58[7]) // not SPI mode
{
_send_sd_dummy_clock(spi_host, pin_cs);
static constexpr uint8_t sd_cmd0[] = { 0x40, 0, 0, 0, 0, 0x95, 0xFF, 0xFF }; // GO_IDLE_STATE command.
m5gfx::spi::writeBytes(spi_host, sd_cmd0, sizeof(sd_cmd0));
}
_pin_level(pin_cs, true);
m5gfx::spi::endTransaction(spi_host);
}
__attribute__ ((unused))
static std::uint32_t _read_panel_id(lgfx::Bus_SPI* bus, std::int32_t pin_cs, std::uint32_t cmd = 0x04, std::uint8_t dummy_read_bit = 1) // 0x04 = RDDID command
{
bus->beginTransaction();
_pin_level(pin_cs, true);
bus->writeCommand(0, 8);
bus->wait();
_pin_level(pin_cs, false);
bus->writeCommand(cmd, 8);
bus->beginRead(dummy_read_bit);
std::uint32_t res = bus->readData(32);
bus->endTransaction();
_pin_level(pin_cs, true);
ESP_LOGD(LIBRARY_NAME, "[Autodetect] read cmd:%02x = %08x", (int)cmd, (int)res);
return res;
}
void M5GFX::_set_backlight(lgfx::ILight* bl)
{
// if (_light_last) { delete _light_last; }
_light_last.reset(bl);
_panel_last->setLight(bl);
}
void M5GFX::_set_pwm_backlight(std::int16_t pin, std::uint8_t ch, std::uint32_t freq, bool invert, uint8_t offset)
{
auto bl = new lgfx::Light_PWM();
auto cfg = bl->config();
cfg.pin_bl = pin;
cfg.freq = freq;
cfg.pwm_channel = ch;
cfg.offset = offset;
cfg.invert = invert;
bl->config(cfg);
_set_backlight(bl);
}
bool M5GFX::init_impl(bool use_reset, bool use_clear)
{
if (getBoard() != board_t::board_unknown)
{
return true;
}
static constexpr char NVS_KEY[] = "AUTODETECT";
std::uint32_t nvs_board = 0;
std::uint32_t nvs_handle = 0;
if (0 == nvs_open(LIBRARY_NAME, NVS_READONLY, &nvs_handle))
{
nvs_get_u32(nvs_handle, NVS_KEY, static_cast<uint32_t*>(&nvs_board));
nvs_close(nvs_handle);
ESP_LOGI(LIBRARY_NAME, "[Autodetect] load from NVS : board:%d", (int)nvs_board);
}
if (0 == nvs_board)
{
#if defined ( M5GFX_BOARD )
nvs_board = M5GFX_BOARD;
#elif defined ( ARDUINO_M5STACK_CORE_ESP32 ) || defined ( ARDUINO_M5STACK_FIRE ) || defined ( ARDUINO_M5Stack_Core_ESP32 )
nvs_board = board_t::board_M5Stack;
#elif defined ( ARDUINO_M5STACK_CORE2 ) || defined ( ARDUINO_M5STACK_Core2 )
nvs_board = board_t::board_M5StackCore2;
#elif defined ( ARDUINO_M5STICK_C ) || defined ( ARDUINO_M5Stick_C )
nvs_board = board_t::board_M5StickC;
#elif defined ( ARDUINO_M5STICK_C_PLUS ) || defined ( ARDUINO_M5Stick_C_Plus )
nvs_board = board_t::board_M5StickCPlus;
#elif defined ( ARDUINO_M5STACK_COREINK ) || defined ( ARDUINO_M5Stack_CoreInk )
nvs_board = board_t::board_M5StackCoreInk;
#elif defined ( ARDUINO_M5STACK_PAPER ) || defined ( ARDUINO_M5STACK_Paper )
nvs_board = board_t::board_M5Paper;
#elif defined ( ARDUINO_M5STACK_TOUGH )
nvs_board = board_t::board_M5Tough;
#elif defined ( ARDUINO_M5STACK_ATOM ) || defined ( ARDUINO_M5Stack_ATOM )
nvs_board = board_t::board_M5Atom;
//#elif defined ( ARDUINO_M5STACK_TIMER_CAM ) || defined ( ARDUINO_M5Stack_Timer_CAM )
#endif
}
auto board = (board_t)nvs_board;
int retry = 4;
do
{
if (retry == 1) use_reset = true;
board = autodetect(use_reset, board);
//ESP_LOGD(LIBRARY_NAME,"autodetect board:%d", (int)board);
} while (board_t::board_unknown == board && --retry >= 0);
_board = board;
#if defined ( ARDUINO_M5STACK_ATOM ) || defined ( ARDUINO_M5Stack_ATOM )
if (board == board_t::board_unknown || board == board_t::board_M5Atom)
{
return false;
}
#endif
if (nvs_board != board) {
if (0 == nvs_open(LIBRARY_NAME, NVS_READWRITE, &nvs_handle)) {
ESP_LOGI(LIBRARY_NAME, "[Autodetect] save to NVS : board:%d", (int)board);
nvs_set_u32(nvs_handle, NVS_KEY, board);
nvs_close(nvs_handle);
}
}
/// autodetectの際にreset済みなのでここではuse_resetをfalseで呼び出す。;
/// M5Paperはreset後の復帰に800msec程度掛かるのでreset省略は起動時間短縮に有効;
return LGFX_Device::init_impl(false, use_clear);
}
board_t M5GFX::autodetect(bool use_reset, board_t board)
{
auto bus_spi = new Bus_SPI();
_bus_last.reset(bus_spi);
panel(nullptr);
auto bus_cfg = bus_spi->config();
(void)bus_cfg; // prevent compiler warning.
bus_cfg.freq_write = 8000000;
bus_cfg.freq_read = 8000000;
bus_cfg.spi_mode = 0;
bus_cfg.use_lock = true;
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
bus_cfg.spi_host = VSPI_HOST;
bus_cfg.dma_channel = 1;
std::uint32_t id;
std::uint32_t pkg_ver = m5gfx::get_pkg_ver();
// ESP_LOGD(LIBRARY_NAME, "pkg_ver : %02x", (int)pkg_ver);
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) /// check PICO-D4 (M5StickC,CPlus,T,T2 / CoreInk / ATOM )
{
if (board == 0 || board == board_t::board_M5StickC || board == board_t::board_M5StickCPlus)
{
_pin_reset(GPIO_NUM_18, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_15;
bus_cfg.pin_miso = GPIO_NUM_14;
bus_cfg.pin_sclk = GPIO_NUM_13;
bus_cfg.pin_dc = GPIO_NUM_23;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_5);
if ((id & 0xFB) == 0x81) // 0x81 or 0x85
{ // check panel (ST7789)
board = board_t::board_M5StickCPlus;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5StickCPlus");
bus_spi->release();
bus_cfg.spi_host = HSPI_HOST;
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 15000000;
bus_spi->config(bus_cfg);
auto p = new Panel_M5StickCPlus();
p->bus(bus_spi);
_panel_last.reset(p);
_set_backlight(new Light_M5StickC());
goto init_clear;
}
if ((id & 0xFF) == 0x7C)
{ // check panel (ST7735)
board = board_t::board_M5StickC;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5StickC");
bus_spi->release();
bus_cfg.spi_host = HSPI_HOST;
bus_cfg.freq_write = 27000000;
bus_cfg.freq_read = 14000000;
bus_spi->config(bus_cfg);
auto p = new Panel_M5StickC();
p->bus(bus_spi);
_panel_last.reset(p);
_set_backlight(new Light_M5StickC());
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_18, lgfx::pin_mode_t::input); // LCD RST
lgfx::pinMode(GPIO_NUM_5 , lgfx::pin_mode_t::input); // LCD CS
bus_spi->release();
}
if (board == 0 || board == board_t::board_M5StackCoreInk)
{
_pin_reset( GPIO_NUM_0, true); // EPDがDeepSleepしている場合は自動認識に失敗する。そのためRST制御を必ず行う。;
bus_cfg.pin_mosi = GPIO_NUM_23;
bus_cfg.pin_miso = GPIO_NUM_34;
bus_cfg.pin_sclk = GPIO_NUM_18;
bus_cfg.pin_dc = GPIO_NUM_15;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
lgfx::Panel_HasBuffer* p = nullptr;
id = _read_panel_id(bus_spi, GPIO_NUM_9, 0x2f ,0);
if (id == 0x00010001)
{ // check panel (e-paper GDEW0154D67)
p = new lgfx::Panel_GDEW0154D67();
} else {
id = _read_panel_id(bus_spi, GPIO_NUM_9, 0x70, 0);
if ((id & 0xFFFF00FFu) == 0x00F00000u)
{ // check panel (e-paper GDEW0154M09)
// ID of first lot : 0x00F00000u
// ID of 2023/11/17 : 0x00F01600u
p = new lgfx::Panel_GDEW0154M09();
}
}
if (p != nullptr)
{
_pin_level(GPIO_NUM_12, true); // POWER_HOLD_PIN 12
board = board_t::board_M5StackCoreInk;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5StackCoreInk");
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
p->bus(bus_spi);
_panel_last.reset(p);
auto cfg = p->config();
cfg.panel_height = 200;
cfg.panel_width = 200;
cfg.pin_cs = GPIO_NUM_9;
cfg.pin_rst = GPIO_NUM_0;
cfg.pin_busy = GPIO_NUM_4;
p->config(cfg);
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_0, lgfx::pin_mode_t::input); // RST
lgfx::pinMode(GPIO_NUM_9, lgfx::pin_mode_t::input); // CS
bus_spi->release();
}
/// LCD / EPD 検出失敗の場合はATOM 判定;
}
else if (pkg_ver == 6) // PICOV3_02 (StickCPlus2 / ATOM PSRAM)
{
if (board == 0 || board == board_t::board_M5StickCPlus2)
{
_pin_reset(GPIO_NUM_12, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_15;
bus_cfg.pin_miso = (gpio_num_t)-1; //GPIO_NUM_NC;
bus_cfg.pin_sclk = GPIO_NUM_13;
bus_cfg.pin_dc = GPIO_NUM_14;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_5);
if ((id & 0xFB) == 0x81) // 0x81 or 0x85
{ // check panel (ST7789)
_pin_level(GPIO_NUM_4, true); // POWER_HOLD_PIN 4
board = board_t::board_M5StickCPlus2;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5StickCPlus2");
bus_spi->release();
bus_cfg.spi_host = HSPI_HOST;
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 15000000;
bus_spi->config(bus_cfg);
auto p = new Panel_M5StickCPlus();
{
auto cfg = p->config();
cfg.pin_rst = GPIO_NUM_12;
p->config(cfg);
}
p->bus(bus_spi);
_panel_last.reset(p);
_set_pwm_backlight(GPIO_NUM_27, 7, 256, false, 40);
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_12, lgfx::pin_mode_t::input); // LCD RST
lgfx::pinMode(GPIO_NUM_5 , lgfx::pin_mode_t::input); // LCD CS
bus_spi->release();
}
if (board == 0)
{
board = board_t::board_M5AtomPsram;
goto init_clear;
}
}
else
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6)
{
/// AXP192の有無を最初に判定し、分岐する。;
if (board == 0
|| board == board_t::board_M5Station
|| board == board_t::board_M5StackCore2
|| board == board_t::board_M5Tough)
{
// I2C addr 0x34 = AXP192
lgfx::i2c::init(axp_i2c_port, axp_i2c_sda, axp_i2c_scl);
auto chk_axp = lgfx::i2c::readRegister8(axp_i2c_port, axp_i2c_addr, 0x03, 400000);
if (chk_axp.has_value())
{
uint_fast16_t axp_exists = 0;
if (chk_axp.value() == 0x03) { // AXP192 found
axp_exists = 192;
ESP_LOGD(LIBRARY_NAME, "AXP192 found");
}
else if (chk_axp.value() == 0x4A) { // AXP2101 found
axp_exists = 2101;
ESP_LOGD(LIBRARY_NAME, "AXP2101 found");
}
if (axp_exists == 192 && (board == 0 || board == board_t::board_M5Station))
{
_pin_reset(GPIO_NUM_15, use_reset); // LCD RST;
bus_cfg.pin_mosi = GPIO_NUM_23;
bus_cfg.pin_miso = -1;
bus_cfg.pin_sclk = GPIO_NUM_18;
bus_cfg.pin_dc = GPIO_NUM_19;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_5);
if ((id & 0xFB) == 0x81) // 0x81 or 0x85
{ // check panel (ST7789)
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5Station");
board = board_t::board_M5Station;
bus_spi->release();
bus_cfg.spi_host = HSPI_HOST;
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 15000000;
bus_spi->config(bus_cfg);
auto p = new Panel_M5StickCPlus();
{
auto cfg = p->config();
cfg.pin_rst = 15;
p->config(cfg);
p->setRotation(1);
}
p->bus(bus_spi);
_panel_last.reset(p);
/// M5StationのバックライトはM5Toughと同じ;
_set_backlight(new Light_M5Tough());
goto init_clear;
}
bus_spi->release();
lgfx::pinMode(GPIO_NUM_5 , lgfx::pin_mode_t::input); // LCD CS
lgfx::pinMode(GPIO_NUM_15, lgfx::pin_mode_t::input); // LCD RST
}
if (axp_exists && (board == 0 || board == board_t::board_M5StackCore2 || board == board_t::board_M5Tough))
{
// fore Core2 1st gen (AXP192)
// AXP192_LDO2 = LCD PWR
// AXP192_IO4 = LCD RST
// AXP192_DC3 = LCD BL (Core2)
// AXP192_LDO3 = LCD BL (Tough)
// AXP192_IO1 = TP RST (Tough)
static constexpr uint8_t reg_data_axp192_first[] = {
0x95, 0x84, 0x72, // GPIO4 enable
0x28, 0xF0, 0xFF, // set LDO2 3300mv // LCD PWR
0x12, 0x04, 0xFF, // LDO2 enable
0x92, 0x00, 0xF8, // GPIO1 OpenDrain (M5Tough TOUCH)
0xFF, 0xFF, 0xFF,
};
static constexpr uint8_t reg_data_axp192_reset[] = {
0x96, 0x00, 0xFD, // GPIO4 LOW (LCD RST)
0x94, 0x00, 0xFD, // GPIO1 LOW (M5Tough TOUCH RST)
0xFF, 0xFF, 0xFF,
};
static constexpr uint8_t reg_data_axp192_second[] = {
0x96, 0x02, 0xFF, // GPIO4 HIGH (LCD RST)
0x94, 0x02, 0xFF, // GPIO1 HIGH (M5Tough TOUCH RST)
0xFF, 0xFF, 0xFF,
};
// for Core2 v1.1 (AXP2101)
// ALDO2 == LCD+TOUCH RST
// ALDO3 == SPK EN
// ALDO4 == TF, TP, LCD PWR
// BLDO1 == LCD BL
// BLDO2 == Boost EN
// DLDO1 == Vibration Motor
static constexpr uint8_t reg_data_axp2101_first[] = {
0x90, 0x08, 0x7B, // ALDO4 ON / ALDO3 OFF, DLDO1 OFF
0x80, 0x05, 0xFF, // DCDC1 + DCDC3 ON
0x82, 0x12, 0x00, // DCDC1 3.3V
0x84, 0x6A, 0x00, // DCDC3 3.3V
0xFF, 0xFF, 0xFF,
};
static constexpr uint8_t reg_data_axp2101_reset[] = {
0x90, 0x00, 0xFD, // ALDO2 OFF
0xFF, 0xFF, 0xFF,
};
static constexpr uint8_t reg_data_axp2101_second[] = {
0x90, 0x02, 0xFF, // ALDO2 ON
0xFF, 0xFF, 0xFF,
};
_pin_level(GPIO_NUM_5, true);
bool isAxp192 = axp_exists == 192;
i2c_write_register8_array(axp_i2c_port, axp_i2c_addr, isAxp192 ? reg_data_axp192_first : reg_data_axp2101_first, axp_i2c_freq);
if (use_reset) {
i2c_write_register8_array(axp_i2c_port, axp_i2c_addr, isAxp192 ? reg_data_axp192_reset : reg_data_axp2101_reset, axp_i2c_freq);
lgfx::delay(1);
}
i2c_write_register8_array(axp_i2c_port, axp_i2c_addr, isAxp192 ? reg_data_axp192_second : reg_data_axp2101_second, axp_i2c_freq);
lgfx::delay(1);
{
bus_cfg.pin_mosi = GPIO_NUM_23;
bus_cfg.pin_miso = GPIO_NUM_38;
bus_cfg.pin_sclk = GPIO_NUM_18;
bus_cfg.pin_dc = GPIO_NUM_15;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
_set_sd_spimode(bus_cfg.spi_host, GPIO_NUM_4);
id = _read_panel_id(bus_spi, GPIO_NUM_5);
if ((id & 0xFF) == 0xE3)
{ // ILI9342c
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
auto p = new Panel_M5StackCore2();
p->bus(bus_spi);
_panel_last.reset(p);
// Tough のタッチコントローラ有無をチェックする;
// Core2/Tough 判別条件としてCore2のTP(0x38)の有無を用いた場合、以下の問題が生じる;
// ・Core2のTPがスリープしている場合は反応が得られない;
// ・ToughにGoPlus2を組み合わせると0x38に反応がある;
// 上記のことから、ここではToughのTP(0x2E)の有無によって判定する;
if ( ! lgfx::i2c::readRegister8(axp_i2c_port, 0x2E, 0, 400000).has_value()) // 0x2E:M5Tough TOUCH
{
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5StackCore2");
board = board_t::board_M5StackCore2;
ILight* light = nullptr;
if (isAxp192) {
light = new Light_M5StackCore2();
} else {
light = new Light_M5StackCore2_AXP2101();
}
_set_backlight(light);
auto t = new lgfx::Touch_FT5x06();
_touch_last.reset(t);
auto cfg = t->config();
cfg.pin_int = GPIO_NUM_39;
cfg.pin_sda = GPIO_NUM_21;
cfg.pin_scl = GPIO_NUM_22;
cfg.i2c_addr = 0x38;
cfg.i2c_port = I2C_NUM_1;
cfg.freq = 400000;
cfg.x_min = 0;
cfg.x_max = 319;
cfg.y_min = 0;
cfg.y_max = 279;
cfg.bus_shared = false;
t->config(cfg);
p->touch(t);
float affine[6] = { 1, 0, 0, 0, 1, 0 };
p->setCalibrateAffine(affine);
}
else
{
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5Tough");
board = board_t::board_M5Tough;
_set_backlight(new Light_M5Tough());
auto t = new Touch_M5Tough();
_touch_last.reset(t);
auto cfg = t->config();
cfg.pin_int = GPIO_NUM_39;
cfg.pin_sda = GPIO_NUM_21;
cfg.pin_scl = GPIO_NUM_22;
cfg.i2c_addr = 0x2E;
cfg.i2c_port = I2C_NUM_1;
cfg.freq = 400000;
cfg.x_min = 0;
cfg.x_max = 319;
cfg.y_min = 0;
cfg.y_max = 239;
cfg.bus_shared = false;
t->config(cfg);
p->touch(t);
}
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_4, lgfx::pin_mode_t::input); // TF card CS
lgfx::pinMode(GPIO_NUM_5, lgfx::pin_mode_t::input); // LCD CS
bus_spi->release();
}
}
}
}
if (board == 0 || board == board_t::board_M5Stack)
{
_pin_reset(GPIO_NUM_33, use_reset); // LCD RST;
bus_cfg.pin_mosi = GPIO_NUM_23;
bus_cfg.pin_miso = GPIO_NUM_19;
bus_cfg.pin_sclk = GPIO_NUM_18;
bus_cfg.pin_dc = GPIO_NUM_27;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
_set_sd_spimode(bus_cfg.spi_host, GPIO_NUM_4);
id = _read_panel_id(bus_spi, GPIO_NUM_14);
if ((id & 0xFF) == 0xE3)
{ // ILI9342c
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5Stack");
board = board_t::board_M5Stack;
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
auto p = new Panel_M5Stack();
p->bus(bus_spi);
_panel_last.reset(p);
_set_pwm_backlight(GPIO_NUM_32, 7, 44100);
goto init_clear;
}
bus_spi->release();
lgfx::pinMode(GPIO_NUM_4 , lgfx::pin_mode_t::input); // TF card CS
lgfx::pinMode(GPIO_NUM_14, lgfx::pin_mode_t::input); // LCD CS
lgfx::pinMode(GPIO_NUM_33, lgfx::pin_mode_t::input); // LCD RST
}
if (board == 0 || board == board_t::board_M5Paper)
{
_pin_reset(GPIO_NUM_23, true);
lgfx::pinMode(GPIO_NUM_27, lgfx::pin_mode_t::input_pullup); // M5Paper EPD busy pin
if (!lgfx::gpio_in(GPIO_NUM_27))
{
_pin_level(GPIO_NUM_2, true); // M5EPD_MAIN_PWR_PIN 2
lgfx::pinMode(GPIO_NUM_27, lgfx::pin_mode_t::input);
bus_cfg.pin_mosi = GPIO_NUM_12;
bus_cfg.pin_miso = GPIO_NUM_13;
bus_cfg.pin_sclk = GPIO_NUM_14;
bus_cfg.pin_dc = -1;
bus_cfg.spi_3wire = false;
bus_spi->config(bus_cfg);
id = lgfx::millis();
_pin_level(GPIO_NUM_15, true); // M5Paper CS;
bus_spi->init();
_set_sd_spimode(bus_cfg.spi_host, GPIO_NUM_4);
do
{
vTaskDelay(1);
if (lgfx::millis() - id > 1024) { id = 0; break; }
} while (!lgfx::gpio_in(GPIO_NUM_27));
if (id)
{
bus_spi->beginTransaction();
lgfx::gpio_lo(GPIO_NUM_15);
bus_spi->writeData(__builtin_bswap16(0x6000), 16);
bus_spi->writeData(__builtin_bswap16(0x0302), 16); // read DevInfo
id = lgfx::millis();
bus_spi->wait();
lgfx::gpio_hi(GPIO_NUM_15);
do
{
vTaskDelay(1);
if (lgfx::millis() - id > 192) { break; }
} while (!lgfx::gpio_in(GPIO_NUM_27));
lgfx::gpio_lo(GPIO_NUM_15);
bus_spi->writeData(__builtin_bswap16(0x1000), 16);
bus_spi->writeData(__builtin_bswap16(0x0000), 16);
std::uint8_t buf[40];
bus_spi->beginRead();
bus_spi->readBytes(buf, 40, false);
bus_spi->endRead();
bus_spi->endTransaction();
lgfx::gpio_hi(GPIO_NUM_15);
id = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
// ESP_LOGI(LIBRARY_NAME, "[Autodetect] panel size :%08x", (int)id);
if (id == 0x03C0021C)
{ // check panel ( panel size 960(0x03C0) x 540(0x021C) )
board = board_t::board_M5Paper;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5Paper");
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 20000000;
bus_spi->config(bus_cfg);
{
auto p = new lgfx::Panel_IT8951();
p->bus(bus_spi);
_panel_last.reset(p);
auto cfg = p->config();
cfg.panel_height = 540;
cfg.panel_width = 960;
cfg.pin_cs = GPIO_NUM_15;
cfg.pin_rst = GPIO_NUM_23;
cfg.pin_busy = GPIO_NUM_27;
cfg.offset_rotation = 3;
p->config(cfg);
}
{
auto t = new lgfx::Touch_GT911();
_touch_last.reset(t);
auto cfg = t->config();
cfg.pin_int = GPIO_NUM_36;
cfg.pin_sda = GPIO_NUM_21;
cfg.pin_scl = GPIO_NUM_22;
#ifdef _M5EPD_H_
cfg.i2c_port = I2C_NUM_0;
#else
cfg.i2c_port = I2C_NUM_1;
#endif
cfg.freq = 400000;
cfg.x_min = 0;
cfg.x_max = 539;
cfg.y_min = 0;
cfg.y_max = 959;
cfg.offset_rotation = 1;
cfg.bus_shared = false;
t->config(cfg);
_panel_last->touch(t);
}
goto init_clear;
}
}
bus_spi->release();
lgfx::pinMode(GPIO_NUM_15, lgfx::pin_mode_t::input); // EPD CS
lgfx::pinMode(GPIO_NUM_4, lgfx::pin_mode_t::input); // M5Paper TF card CS
lgfx::pinMode(GPIO_NUM_2, lgfx::pin_mode_t::input); // M5EPD_MAIN_PWR_PIN 2
}
lgfx::pinMode(GPIO_NUM_27, lgfx::pin_mode_t::input); // BUSY
lgfx::pinMode(GPIO_NUM_23, lgfx::pin_mode_t::input); // RST
}
}
#elif defined (CONFIG_IDF_TARGET_ESP32S3)
bus_cfg.spi_host = SPI2_HOST;
bus_cfg.dma_channel = SPI_DMA_CH_AUTO;
std::uint32_t id;
std::uint32_t pkg_ver = m5gfx::get_pkg_ver();
// ESP_LOGD(LIBRARY_NAME, "pkg_ver : %02x / board:%d", (int)pkg_ver, (int)board);
switch (pkg_ver) {
case 0: // EFUSE_PKG_VERSION_ESP32S3: // QFN56
if (board == 0 || board == board_t::board_M5StackCoreS3 || board == board_t::board_M5StackCoreS3SE)
{
lgfx::i2c::init(i2c_port, i2c_sda, i2c_scl);
// ESP_LOGI("DEBUG","AW 0x10 :%02x", (int)lgfx::i2c::readRegister8(i2c_port, aw9523_i2c_addr, 0x10, 400000).value());
// ESP_LOGI("DEBUG","AXP0x03 :%02x", (int)lgfx::i2c::readRegister8(i2c_port, axp_i2c_addr, 0x03, 400000).value());
auto chk_axp = lgfx::i2c::readRegister8(i2c_port, axp_i2c_addr, 0x03, i2c_freq);
if (chk_axp.has_value() && chk_axp.value() == 0x4A)
{
auto chk_aw = lgfx::i2c::readRegister8(i2c_port, aw9523_i2c_addr, 0x10, i2c_freq);
if (chk_aw .has_value() && chk_aw .value() == 0x23)
{
auto result = lgfx::gpio::command(
(const uint8_t[]) {
lgfx::gpio::command_mode_input_pullup, GPIO_NUM_35,
lgfx::gpio::command_mode_input_pullup, GPIO_NUM_36,
lgfx::gpio::command_mode_input_pullup, GPIO_NUM_37,
lgfx::gpio::command_read , GPIO_NUM_35,
lgfx::gpio::command_read , GPIO_NUM_36,
lgfx::gpio::command_read , GPIO_NUM_37,
lgfx::gpio::command_end
}
);
/// SPIバスのプルアップが効いていない場合はVBUS 5V出力を有効化する。
/// (USBホストモジュール等、5Vが出ていないと信号線の電気を吸い込む組合せがあるため)
uint8_t reg0x02 = (result == 0) ? 0b00000111 : 0b00000101;
uint8_t reg0x03 = (result == 0) ? 0b10000011 : 0b00000011;
m5gfx::i2c::bitOn(i2c_port, aw9523_i2c_addr, 0x02, reg0x02); //port0 output ctrl
m5gfx::i2c::bitOn(i2c_port, aw9523_i2c_addr, 0x03, reg0x03); //port1 output ctrl
m5gfx::i2c::writeRegister8(i2c_port, aw9523_i2c_addr, 0x04, 0b00011000); // CONFIG_P0
m5gfx::i2c::writeRegister8(i2c_port, aw9523_i2c_addr, 0x05, 0b00001100); // CONFIG_P1
m5gfx::i2c::writeRegister8(i2c_port, aw9523_i2c_addr, 0x11, 0b00010000); // GCR P0 port is Push-Pull mode.
m5gfx::i2c::writeRegister8(i2c_port, aw9523_i2c_addr, 0x12, 0b11111111); // LEDMODE_P0
m5gfx::i2c::writeRegister8(i2c_port, aw9523_i2c_addr, 0x13, 0b11111111); // LEDMODE_P1
m5gfx::i2c::writeRegister8(i2c_port, axp_i2c_addr, 0x90, 0xBF); // LDOS ON/OFF control 0
m5gfx::i2c::writeRegister8(i2c_port, axp_i2c_addr, 0x94, 33 - 5); // ALDO3 set to 3.3v // for GC0308 Camera
m5gfx::i2c::writeRegister8(i2c_port, axp_i2c_addr, 0x95, 33 - 5); // ALDO4 set to 3.3v // for TF card slot
bus_cfg.pin_mosi = GPIO_NUM_37;
bus_cfg.pin_miso = GPIO_NUM_35;
bus_cfg.pin_sclk = GPIO_NUM_36;
bus_cfg.pin_dc = GPIO_NUM_35;// MISOとLCD D/CをGPIO35でシェアしている;
bus_cfg.spi_mode = 0;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
_set_sd_spimode(bus_cfg.spi_host, GPIO_NUM_4);
id = _read_panel_id(bus_spi, GPIO_NUM_3);
if ((id & 0xFF) == 0xE3)
{ // check panel (ILI9342)
board = board_t::board_M5StackCoreS3;
// Camera GC0308 check (not found == M5StackCoreS3SE)
auto chk_gc = lgfx::i2c::readRegister8(i2c_port, gc0308_i2c_addr, 0x00, i2c_freq);
if (chk_gc .has_value() && chk_gc .value() == 0x9b) {
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5StackCoreS3");
} else {
board = board_M5StackCoreS3SE;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5StackCoreS3SE");
}
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
auto p = new Panel_M5StackCoreS3();
p->bus(bus_spi);
_panel_last.reset(p);
_set_backlight(new Light_M5StackCoreS3());
{
auto t = new Touch_M5StackCoreS3();
_touch_last.reset(t);
_panel_last->touch(t);
}
goto init_clear;
}
bus_spi->release();
lgfx::pinMode(GPIO_NUM_4, lgfx::pin_mode_t::input); // TF card CS
lgfx::pinMode(GPIO_NUM_3, lgfx::pin_mode_t::input); // LCD CS
}
}
lgfx::i2c::release(i2c_port);
}
if (board == 0 || board == board_t::board_M5Dial)
{
_pin_reset(GPIO_NUM_8, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_5;
bus_cfg.pin_miso = (gpio_num_t)-1; //GPIO_NUM_NC;
bus_cfg.pin_sclk = GPIO_NUM_6;
bus_cfg.pin_dc = GPIO_NUM_4;
bus_cfg.spi_mode = 0;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_7);
if ((id & 0xFFFFFF) == 0x019a00)
{ // check panel (GC9A01)
board = board_t::board_M5Dial;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5Dial");
bus_spi->release();
bus_cfg.freq_write = 80000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
bus_spi->init();
auto p = new Panel_GC9A01();
p->bus(bus_spi);
{
auto cfg = p->config();
cfg.pin_cs = GPIO_NUM_7;
cfg.pin_rst = GPIO_NUM_8;
cfg.panel_width = 240;
cfg.panel_height = 240;
cfg.readable = false;
cfg.invert = true;
p->config(cfg);
}
_panel_last.reset(p);
_set_pwm_backlight(GPIO_NUM_9, 7, 44100);
{
auto t = new m5gfx::Touch_FT5x06();
if (t) {
_touch_last.reset(t);
auto cfg = t->config();
cfg.x_min = 0;
cfg.x_max = 239;
cfg.y_min = 0;
cfg.y_max = 239;
cfg.pin_int = GPIO_NUM_14;
cfg.bus_shared = false;
cfg.offset_rotation = 0;
cfg.i2c_port = 1;
cfg.i2c_addr = 0x38;
cfg.pin_sda = GPIO_NUM_11;
cfg.pin_scl = GPIO_NUM_12;
cfg.freq = 400000;
t->config(cfg);
_panel_last->touch(t);
}
}
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_8, lgfx::pin_mode_t::input); // LCD RST
bus_spi->release();
}
if (board == 0 || board == board_t::board_M5PaperS3)
{
static constexpr int_fast16_t papers3_i2c_sda = GPIO_NUM_41;
static constexpr int_fast16_t papers3_i2c_scl = GPIO_NUM_42;
static constexpr const uint8_t gt911_i2c_addr[] = { 0x14, 0x5D };
gpio::pin_backup_t backup_pins[] = { papers3_i2c_sda, papers3_i2c_scl };
auto result = lgfx::gpio::command(
(const uint8_t[]) {
lgfx::gpio::command_mode_output , papers3_i2c_scl,
lgfx::gpio::command_write_low , papers3_i2c_scl,
lgfx::gpio::command_mode_output , papers3_i2c_sda,
lgfx::gpio::command_write_low , papers3_i2c_sda,
lgfx::gpio::command_write_high , papers3_i2c_scl,
lgfx::gpio::command_write_high , papers3_i2c_sda,
lgfx::gpio::command_mode_input_pulldown, papers3_i2c_scl,
lgfx::gpio::command_mode_input_pulldown, papers3_i2c_sda,
lgfx::gpio::command_delay , 1,
lgfx::gpio::command_read , papers3_i2c_scl,
lgfx::gpio::command_read , papers3_i2c_sda,
lgfx::gpio::command_end
}
);
// Check G41,G42 HIGH
if (result == 0x03) {
lgfx::i2c::init(i2c_port, papers3_i2c_sda, papers3_i2c_scl);
{
bool gt911_found = false;
for (auto addr: gt911_i2c_addr) {
if (lgfx::i2c::beginTransaction(i2c_port, addr, 400000).has_value()) {
gt911_found = lgfx::i2c::endTransaction(i2c_port).has_value();
if (gt911_found) {
break;
}
}
}
if (gt911_found) {
board = board_t::board_M5PaperS3;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5PaperS3");
// PWROFF_PULSE_PIN
lgfx::pinMode(GPIO_NUM_44, lgfx::pin_mode_t::output);
lgfx::gpio_lo(GPIO_NUM_44);
#if !(defined(CONFIG_ESP32S3_SPIRAM_SUPPORT))
ESP_LOGE(LIBRARY_NAME, "M5PaperS3 need OPI-PSRAM enabled");
#elif !defined (CONFIG_SPIRAM_MODE_OCT)
ESP_LOGE(LIBRARY_NAME, "M5PaperS3 need OPI-PSRAM enabled");
#else
auto bus_epd = new Bus_EPD();
_bus_last.reset(bus_epd);
auto p = new lgfx::Panel_EPD();
_panel_last.reset(p);
{
auto bus_cfg = bus_epd->config();
bus_cfg.bus_speed = 20000000;
bus_cfg.pin_data[0] = GPIO_NUM_6;
bus_cfg.pin_data[1] = GPIO_NUM_14;
bus_cfg.pin_data[2] = GPIO_NUM_7;
bus_cfg.pin_data[3] = GPIO_NUM_12;
bus_cfg.pin_data[4] = GPIO_NUM_9;
bus_cfg.pin_data[5] = GPIO_NUM_11;
bus_cfg.pin_data[6] = GPIO_NUM_8;
bus_cfg.pin_data[7] = GPIO_NUM_10;
bus_cfg.pin_pwr = GPIO_NUM_46;
bus_cfg.pin_spv = GPIO_NUM_17;
bus_cfg.pin_ckv = GPIO_NUM_18;
bus_cfg.pin_sph = GPIO_NUM_13;
bus_cfg.pin_oe = GPIO_NUM_45;
bus_cfg.pin_le = GPIO_NUM_15;
bus_cfg.pin_cl = GPIO_NUM_16;
bus_cfg.bus_width = 8;
bus_epd->config(bus_cfg);
p->setBus(bus_epd);
}
{
auto cfg_detail = p->config_detail();
cfg_detail.line_padding = 8;
p->config_detail(cfg_detail);
}
{
auto cfg = p->config();
cfg.memory_width = 960;
cfg.panel_width = 960;
cfg.memory_height = 540;
cfg.panel_height = 540;
cfg.offset_rotation = 3;
cfg.offset_x = 0;
cfg.offset_y = 0;
cfg.bus_shared = false;
p->config(cfg);
}
{
auto t = new lgfx::Touch_GT911();
_touch_last.reset(t);
auto cfg = t->config();
cfg.pin_int = GPIO_NUM_48;
cfg.pin_sda = GPIO_NUM_41;
cfg.pin_scl = GPIO_NUM_42;
cfg.freq = 400000;
cfg.i2c_port = I2C_NUM_1;
cfg.x_min = 0;
cfg.x_max = 539;
cfg.y_min = 0;
cfg.y_max = 959;
cfg.offset_rotation = 1;
cfg.bus_shared = false;
t->config(cfg);
_panel_last->touch(t);
p->touch(t);
}
goto init_clear;
#endif
}
}
lgfx::i2c::release(i2c_port);
}
for (auto &bup : backup_pins) { bup.restore(); }
}
if (board == 0 || board == board_t::board_M5AtomS3)
{
_pin_reset(GPIO_NUM_34, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_21;
bus_cfg.pin_miso = (gpio_num_t)-1; //GPIO_NUM_NC;
bus_cfg.pin_sclk = GPIO_NUM_17;
bus_cfg.pin_dc = GPIO_NUM_33;
bus_cfg.spi_mode = 0;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_15);
if ((id & 0xFFFFFF) == 0x079100)
{ // check panel (GC9107)
board = board_t::board_M5AtomS3;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5AtomS3");
bus_spi->release();
bus_cfg.spi_host = SPI3_HOST;
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
bus_spi->init();
auto p = new Panel_GC9107();
p->bus(bus_spi);
{
auto cfg = p->config();
cfg.pin_cs = GPIO_NUM_15;
cfg.pin_rst = GPIO_NUM_34;
cfg.panel_width = 128;
cfg.panel_height = 128;
cfg.offset_y = 32;
cfg.readable = false;
p->config(cfg);
}
_panel_last.reset(p);
_set_pwm_backlight(GPIO_NUM_16, 7, 256, false, 48);
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_48, lgfx::pin_mode_t::input); // LCD RST
bus_spi->release();
}
if (board == 0 || board == board_t::board_M5DinMeter)
{
_pin_reset(GPIO_NUM_8, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_5;
bus_cfg.pin_miso = (gpio_num_t)-1; //GPIO_NUM_NC;
bus_cfg.pin_sclk = GPIO_NUM_6;
bus_cfg.pin_dc = GPIO_NUM_4;
bus_cfg.spi_mode = 0;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_7);
if ((id & 0xFB) == 0x81) // 0x81 or 0x85
{ // check panel (ST7789)
board = board_t::board_M5DinMeter;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5DinMeter");
bus_spi->release();
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
bus_spi->init();
auto p = new Panel_ST7789();
p->bus(bus_spi);
{
auto cfg = p->config();
cfg.pin_cs = GPIO_NUM_7;
cfg.pin_rst = GPIO_NUM_8;
cfg.panel_width = 135;
cfg.panel_height = 240;
cfg.offset_x = 52;
cfg.offset_y = 40;
cfg.offset_rotation = 2;
cfg.readable = true;
cfg.invert = true;
p->config(cfg);
}
_panel_last.reset(p);
_set_pwm_backlight(GPIO_NUM_9, 7, 256, false, 16);
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_8, lgfx::pin_mode_t::input); // LCD RST
bus_spi->release();
}
if (board == 0 || board == board_t::board_M5Cardputer || board == board_t::board_M5VAMeter)
{
_pin_reset(GPIO_NUM_33, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_35;
bus_cfg.pin_miso = (gpio_num_t)-1; //GPIO_NUM_NC;
bus_cfg.pin_sclk = GPIO_NUM_36;
bus_cfg.pin_dc = GPIO_NUM_34;
bus_cfg.spi_mode = 0;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_37);
// check panel (ST7789)
if ((id & 0xFB) == 0x81) // 0x81 or 0x85
{
board = board_t::board_M5Cardputer;
gpio::pin_backup_t backup_pins[] = { GPIO_NUM_5, GPIO_NUM_6 };
auto result = lgfx::gpio::command(
(const uint8_t[]) {
lgfx::gpio::command_mode_input_pulldown, GPIO_NUM_6,
lgfx::gpio::command_mode_input_pulldown, GPIO_NUM_5,
lgfx::gpio::command_read , GPIO_NUM_6,
lgfx::gpio::command_read , GPIO_NUM_5,
lgfx::gpio::command_end
}
);
for (auto &bup : backup_pins) { bup.restore(); }
if (result == 3) {
m5gfx::i2c::i2c_temporary_switcher_t backup_i2c_setting(1, GPIO_NUM_5, GPIO_NUM_6);
result = (m5gfx::i2c::transactionWrite(1, 0x40, nullptr, 0).has_value()
&& m5gfx::i2c::transactionWrite(1, 0x41, nullptr, 0).has_value());
backup_i2c_setting.restore();
if (result) {
board = board_t::board_M5VAMeter;
}
}
bus_spi->release();
bus_cfg.spi_host = SPI3_HOST;
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
bus_spi->init();
auto p = new Panel_ST7789();
p->bus(bus_spi);
{
auto cfg = p->config();
cfg.pin_cs = GPIO_NUM_37;
cfg.pin_rst = GPIO_NUM_33;
cfg.panel_height = 240;
cfg.offset_rotation = 0;
cfg.readable = true;
cfg.invert = true;
int rotation = 0;
int bl_freq = 256;
int bl_offset = 16;
if (board == board_t::board_M5Cardputer) {
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5Cardputer");
cfg.panel_width = 135;
cfg.offset_x = 52;
cfg.offset_y = 40;
rotation = 1;
} else {
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5VAMeter");
cfg.panel_width = 240;
cfg.offset_x = 0;
cfg.offset_y = 0;
bl_freq = 512;
bl_offset = 64;
}
p->config(cfg);
p->setRotation(rotation);
_panel_last.reset(p);
_set_pwm_backlight(GPIO_NUM_38, 7, bl_freq, false, bl_offset);
}
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_33, lgfx::pin_mode_t::input); // LCD RST
bus_spi->release();
}
if (board == 0 || board == board_t::board_M5AirQ)
{
_pin_reset( GPIO_NUM_2, true); // EPDがDeepSleepしている場合は自動認識に失敗する。そのためRST制御を必ず行う。;
bus_cfg.pin_mosi = GPIO_NUM_6;
bus_cfg.pin_miso = (gpio_num_t)-1; //GPIO_NUM_NC;
bus_cfg.pin_sclk = GPIO_NUM_5;
bus_cfg.pin_dc = GPIO_NUM_3;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
lgfx::Panel_HasBuffer* p = nullptr;
id = _read_panel_id(bus_spi, GPIO_NUM_4, 0x2f ,0);
if (id == 0x00010001)
{ // check panel (e-paper GDEW0154D67)
p = new lgfx::Panel_GDEW0154D67();
} else {
id = _read_panel_id(bus_spi, GPIO_NUM_4, 0x70, 0);
if ((id & 0xFFFF00FFu) == 0x00F00000u)
{ // check panel (e-paper GDEW0154M09)
// ID of first lot : 0x00F00000u
// ID of 2023/11/17 : 0x00F01600u
p = new lgfx::Panel_GDEW0154M09();
}
}
if (p != nullptr)
{
_pin_level(GPIO_NUM_46, true); // POWER_HOLD_PIN 46
board = board_t::board_M5AirQ;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] M5AirQ");
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
p->bus(bus_spi);
_panel_last.reset(p);
auto cfg = p->config();
cfg.panel_height = 200;
cfg.panel_width = 200;
cfg.pin_cs = GPIO_NUM_4;
cfg.pin_rst = GPIO_NUM_2;
cfg.pin_busy = GPIO_NUM_1;
p->config(cfg);
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_2, lgfx::pin_mode_t::input); // RST
lgfx::pinMode(GPIO_NUM_4, lgfx::pin_mode_t::input); // CS
bus_spi->release();
}
if (board == 0 || board == board_t::board_M5StampPLC)
{
_pin_reset(GPIO_NUM_3, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_8;
bus_cfg.pin_miso = GPIO_NUM_9;
bus_cfg.pin_sclk = GPIO_NUM_7;
bus_cfg.pin_dc = GPIO_NUM_6;
bus_cfg.spi_mode = 0;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
_set_sd_spimode(bus_cfg.spi_host, GPIO_NUM_10);
id = _read_panel_id(bus_spi, GPIO_NUM_12);
// check panel (ST7789)
if ((id & 0xFB) == 0x81) // 0x81 or 0x85
{
board = board_t::board_M5StampPLC;
bus_spi->release();
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
auto p = new Panel_ST7789();
p->bus(bus_spi);
{
auto cfg = p->config();
cfg.pin_cs = GPIO_NUM_12;
cfg.pin_rst = GPIO_NUM_3;
cfg.panel_width = 135;
cfg.panel_height = 240;
cfg.offset_x = 52;
cfg.offset_y = 40;
cfg.offset_rotation = 0;
cfg.readable = true;
cfg.invert = true;
cfg.bus_shared = true;
p->config(cfg);
p->setRotation(1);
}
_panel_last.reset(p);
_set_backlight(new Light_M5StackStampPLC());
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_3, lgfx::pin_mode_t::input); // LCD RST
bus_spi->release();
}
break;
case 1: // EFUSE_PKG_VERSION_ESP32S3PICO: // LGA56
if (board == 0 || board == board_t::board_M5AtomS3R)
{
_pin_reset(GPIO_NUM_48, use_reset); // LCD RST
bus_cfg.pin_mosi = GPIO_NUM_21;
bus_cfg.pin_miso = (gpio_num_t)-1; //GPIO_NUM_NC;
bus_cfg.pin_sclk = GPIO_NUM_15;
bus_cfg.pin_dc = GPIO_NUM_42;
bus_cfg.spi_mode = 0;
bus_cfg.spi_3wire = true;
bus_spi->config(bus_cfg);
bus_spi->init();
id = _read_panel_id(bus_spi, GPIO_NUM_14);
if ((id & 0xFFFFFF) == 0x079100)
{ // check panel (GC9107)
board = board_t::board_M5AtomS3R;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5AtomS3R");
bus_spi->release();
bus_cfg.spi_host = SPI3_HOST;
bus_cfg.freq_write = 40000000;
bus_cfg.freq_read = 16000000;
bus_spi->config(bus_cfg);
bus_spi->init();
auto p = new Panel_GC9107();
p->bus(bus_spi);
{
auto cfg = p->config();
cfg.pin_cs = GPIO_NUM_14;
cfg.pin_rst = GPIO_NUM_48;
cfg.panel_width = 128;
cfg.panel_height = 128;
cfg.offset_y = 32;
cfg.readable = false;
cfg.bus_shared = false;
p->config(cfg);
}
_panel_last.reset(p);
_set_backlight(new Light_M5StackAtomS3R());
goto init_clear;
}
lgfx::pinMode(GPIO_NUM_48, lgfx::pin_mode_t::input); // LCD RST
bus_spi->release();
}
break;
default: break;
}
#elif defined (CONFIG_IDF_TARGET_ESP32P4)
std::uint32_t id;
std::uint32_t pkg_ver = m5gfx::get_pkg_ver();
ESP_LOGD(LIBRARY_NAME, "pkg_ver : %02x", (int)pkg_ver);
if (true) // pkg_ver == EFUSE_RD_CHIP_VER_PKG_
{
if (board == 0 || board == board_t::board_M5Tab5)
{
// SDA = GPIO_NUM_31
// SCL = GPIO_NUM_32
// TP INT = GPIO_NUM_23
lgfx::pinMode(GPIO_NUM_23, lgfx::pin_mode_t::output); // TP INT
lgfx::gpio_hi(GPIO_NUM_23); // select I2C Addr (high=0x14 / low=0x5D)
lgfx::i2c::init(in_i2c_port, GPIO_NUM_31, GPIO_NUM_32);
id = lgfx::i2c::readRegister8(in_i2c_port, pi4io1_i2c_addr, 0x01).has_value()
&& lgfx::i2c::readRegister8(in_i2c_port, pi4io2_i2c_addr, 0x01).has_value();
if (id != 0) {
board = board_t::board_M5Tab5;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5Tab5");
static constexpr const uint8_t reg_data_io1_1[] = {
0x03, 0b01111111, 0, // PI4IO_REG_IO_DIR
0x05, 0b01010110, 0, // PI4IO_REG_OUT_SET (bit5=GT911 TouchReset LOW)
0x07, 0b00000000, 0, // PI4IO_REG_OUT_H_IM
0x0D, 0b01111111, 0, // PI4IO_REG_PULL_SEL
0x0B, 0b01111111, 0, // PI4IO_REG_PULL_EN
0xFF,0xFF,0xFF,
};
static constexpr const uint8_t reg_data_io1_2[] = {
0x05, 0b01110110, 0, // PI4IO_REG_OUT_SET (bit5=GT911 TouchReset HIGH)
0xFF,0xFF,0xFF,
};
static constexpr const uint8_t reg_data_io2[] = {
0x03, 0b10111001, 0, // PI4IO_REG_IO_DIR
0x07, 0b00000110, 0, // PI4IO_REG_OUT_H_IM
0x0D, 0b10111001, 0, // PI4IO_REG_PULL_SEL
0x0B, 0b11111001, 0, // PI4IO_REG_PULL_EN
0x09, 0b01000000, 0, // PI4IO_REG_IN_DEF_STA
0x11, 0b10111111, 0, // PI4IO_REG_INT_MASK
0x05, 0b10001001, 0, // PI4IO_REG_OUT_SET
0xFF,0xFF,0xFF,
};
i2c_write_register8_array(in_i2c_port, pi4io1_i2c_addr, reg_data_io1_1, 400000);
i2c_write_register8_array(in_i2c_port, pi4io2_i2c_addr, reg_data_io2, 400000);
i2c_write_register8_array(in_i2c_port, pi4io1_i2c_addr, reg_data_io1_2, 400000);
auto p = new Panel_ILI9881C();
{
auto cfg = p->config();
cfg.panel_width = 720;
cfg.panel_height = 1280;
cfg.memory_width = 720;
cfg.memory_height = 1280;
cfg.readable = true;
p->config(cfg);
}
_panel_last.reset(p);
_set_pwm_backlight(GPIO_NUM_22, 7, 44100);
{
auto t = new m5gfx::Touch_GT911();
if (t) {
_touch_last.reset(t);
auto cfg = t->config();
cfg.pin_rst = -1;
cfg.pin_sda = GPIO_NUM_31;
cfg.pin_scl = GPIO_NUM_32;
cfg.pin_int = GPIO_NUM_23;
cfg.freq = 400000;
cfg.x_min = 0;
cfg.x_max = 719;
cfg.y_min = 0;
cfg.y_max = 1279;
cfg.i2c_port = 1;
cfg.bus_shared = true;
cfg.offset_rotation = 0;
t->config(cfg);
_panel_last->touch(t);
}
}
goto init_clear;
}
}
}
#endif
board = board_t::board_unknown;
goto init_clear;
init_clear:
panel(_panel_last.get());
return board;
}
#else
bool M5GFX::init_impl(bool use_reset, bool use_clear)
{
board_t b = board_t::board_unknown;
#if defined (M5GFX_BOARD)
b = M5GFX_BOARD;
#endif
_board = autodetect(use_reset, b);
return LGFX_Device::init_impl(use_reset, use_clear);
}
board_t M5GFX::autodetect(bool use_reset, board_t board)
{
(void)use_reset;
auto p = new Panel_sdl();
_panel_last.reset(p);
auto pnl_cfg = p->config();
int_fast16_t w = 320;
int_fast16_t h = 240;
int_fast16_t r = 0;
int scale = 1;
#if defined (M5GFX_SCALE)
#if M5GFX_SCALE > 1
scale = M5GFX_SCALE;
#endif
#endif
if (board == 0) {
board = board_M5Stack;
}
const char* title;
switch (board) {
case board_M5Stack: title = "M5Stack"; break;
case board_M5StackCore2: title = "M5StackCore2"; break;
case board_M5StackCoreS3: title = "M5StackCoreS3"; break;
case board_M5StackCoreS3SE:title = "M5StackCoreS3SE";break;
case board_M5StickC: title = "M5StickC"; break;
case board_M5StickCPlus: title = "M5StickCPlus"; break;
case board_M5StickCPlus2: title = "M5StickCPlus2"; break;
case board_M5StackCoreInk: title = "M5StackCoreInk"; break;
case board_M5Paper: title = "M5Paper"; break;
case board_M5PaperS3: title = "M5PaperS3"; break;
case board_M5Tough: title = "M5Tough"; break;
case board_M5Station: title = "M5Station"; break;
case board_M5AtomS3: title = "M5AtomS3"; break;
case board_M5AtomS3R: title = "M5AtomS3R"; break;
case board_M5Dial: title = "M5Dial"; break;
case board_M5Cardputer: title = "M5Cardputer"; break;
case board_M5DinMeter: title = "M5DinMeter"; break;
case board_M5AirQ: title = "M5AirQ"; break;
case board_M5VAMeter: title = "M5VAMeter"; break;
case board_M5StampPLC: title = "M5StampPLC"; break;
case board_M5Tab5: title = "M5Tab5"; break;
default: title = "M5GFX"; break;
}
p->setWindowTitle(title);
switch (board) {
case board_M5AtomS3:
case board_M5AtomS3R:
w = 128;
h = 128;
break;
case board_M5Paper:
case board_M5PaperS3:
w = 960;
h = 540;
pnl_cfg.offset_rotation = 3;
p->setColorDepth(lgfx::color_depth_t::grayscale_8bit);
r = 1;
break;
case board_M5StackCoreInk:
case board_M5AirQ:
w = 200;
h = 200;
p->setColorDepth(lgfx::color_depth_t::grayscale_8bit);
break;
case board_M5StickC:
w = 80;
h = 160;
break;
case board_M5Station:
case board_M5Cardputer:
w = 240;
h = 135;
pnl_cfg.offset_rotation = 3;
r = 1;
break;
case board_M5StickCPlus:
case board_M5StickCPlus2:
case board_M5DinMeter:
case board_M5StampPLC:
w = 135;
h = 240;
break;
case board_M5StackCore2:
pnl_cfg.offset_rotation = 3;
r = 1;
break;
case board_M5Stack:
case board_M5StackCoreS3:
case board_M5StackCoreS3SE:
pnl_cfg.offset_rotation = 3;
r = 1;
break;
case board_M5Dial:
w = 240;
h = 240;
break;
case board_M5VAMeter:
w = 240;
h = 240;
break;
case board_M5Tab5:
w = 720;
h = 1280;
break;
default:
break;
}
#if defined (M5GFX_SHORTCUT_MOD)
p->setShortcutKeymod(M5GFX_SHORTCUT_MOD);
#endif
#if defined (M5GFX_SHOW_FRAME)
auto pf = getPictureFrame(board);
if (pf) {
p->setFrameImage(pf->img, pf->w, pf->h, pf->x, pf->y);
}
#endif
pnl_cfg.memory_width = w;
pnl_cfg.panel_width = w;
pnl_cfg.memory_height = h;
pnl_cfg.panel_height = h;
pnl_cfg.bus_shared = false;
p->config(pnl_cfg);
p->setScaling(scale, scale);
#if defined (M5GFX_ROTATION)
p->setFrameRotation(M5GFX_ROTATION);
#endif
p->setRotation(r);
auto t = new lgfx::Touch_sdl();
_touch_last.reset(t);
{
auto cfg = t->config();
cfg.x_min = 0;
cfg.x_max = w - 1;
cfg.y_min = 0;
cfg.y_max = h - 1;
cfg.bus_shared = false;
t->config(cfg);
p->touch(t);
// float affine[6] = { 1, 0, 0, 0, 1, 0 };
// p->setCalibrateAffine(affine);
}
panel(_panel_last.get());
return board;
}
#endif /// end of if defined (ESP_PLATFORM)
void M5GFX::progressBar(int x, int y, int w, int h, uint8_t val)
{
drawRect(x, y, w, h, 0x09F1);
fillRect(x + 1, y + 1, w * (((float)val) / 100.0f), h - 1, 0x09F1);
}
void M5GFX::pushState(void)
{
DisplayState s;
s.gfxFont = _font;
s.style = _text_style;
s.metrics = _font_metrics;
s.cursor_x = _cursor_x;
s.cursor_y = _cursor_y;
_displayStateStack.push_back(s);
}
void M5GFX::popState(void)
{
if (_displayStateStack.empty()) return;
DisplayState s = _displayStateStack.back();
_displayStateStack.pop_back();
_font = s.gfxFont;
_text_style = s.style;
_font_metrics = s.metrics;
_cursor_x = s.cursor_x;
_cursor_y = s.cursor_y;
}
}