From 23a949415e75f15d142a94bd63f4a6fac49eb7f8 Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Sun, 27 Sep 2026 11:51:49 +0000 Subject: [PATCH 1/4] Remove leftovers of the legacy autodetect chain All boards are detected through the board detection framework, so the helpers and settings kept for the removed per-board blocks are unused: the soft I2C probe helpers, the SD SPI-mode and panel reset helpers, an unused I2C port constant, an unused include, and the SPI bus settings that autodetect() filled in but never applied. autodetect() also no longer replaces _bus_last with an unconfigured Bus_SPI before probing. A successful detection installs its own bus; when a re-detection ended unknown, the previous panel was restored while its bus had already been freed by that placeholder. --- src/M5GFX.cpp | 186 +------------------------------------------------- 1 file changed, 2 insertions(+), 184 deletions(-) diff --git a/src/M5GFX.cpp b/src/M5GFX.cpp index f264ace..118b3ae 100644 --- a/src/M5GFX.cpp +++ b/src/M5GFX.cpp @@ -18,7 +18,6 @@ #include #include -#include "lgfx/v1/panel/Panel_AMOLED.hpp" #include "lgfx/v1/panel/Panel_CO5300.hpp" #include "lgfx/v1/panel/Panel_ILI9342.hpp" #include "lgfx/v1/panel/Panel_SSD1306.hpp" @@ -65,8 +64,6 @@ #include "lgfx/v1/platforms/esp32p4/Panel_ST7123.hpp" #include "lgfx/v1/platforms/esp32p4/Touch_ST7123.hpp" -static constexpr int_fast16_t in_i2c_port = I2C_NUM_1; - #elif defined ( CONFIG_IDF_TARGET_ESP32S3 ) #include "lgfx/v1/panel/Panel_ED2208.hpp" @@ -112,124 +109,9 @@ namespace m5gfx #if defined ( ESP_PLATFORM ) - // ボード未確定段階の I2C プローブに使うソフトウェア I2C ポート (GPIO ビットバン)。 - // ハードウェアのペリフェラルを一切確保・設定しないため、候補ボードの試行が - // ペリフェラルやピンの状態を汚さない。ボード確定後の常用バスは従来どおり - // ハードウェアポートを使う。 - __attribute__ ((unused)) + // 判別 transaction の候補機種 I2C プローブはソフトウェアポートを使う。 static constexpr int_fast16_t probe_i2c_port = -1; - // ボード未確定段階の探索をソフト I2C で行い、確定時に常用のハードウェアポートへ引き継ぐ。 - // probe が変えたパッドの配線 (pinMode による GPIO 出力への付け替え) は引き継ぎ前に戻す: - // アプリが先に同じポートを開いていた共有バスでは init() がピンに触らないため、 - // 戻さないと以後の HW I2C が通らない。 - struct __attribute__ ((unused)) probe_i2c_t - { - probe_i2c_t(int sda, int scl) : _pins { sda, scl }, _sda { sda }, _scl { scl } - { - lgfx::i2c::init(probe_i2c_port, _sda, _scl); - } - // パッドが probe の前に既に触られている (プルアップ試験など) ブロックでは、 - // その前に取ったバックアップを渡して復元先にする - probe_i2c_t(const gpio::pin_backup_t& sda_backup, const gpio::pin_backup_t& scl_backup) - : _pins { sda_backup, scl_backup }, _sda { sda_backup.getPin() }, _scl { scl_backup.getPin() } - { - lgfx::i2c::init(probe_i2c_port, _sda, _scl); - } - // ボードが確定した: probe を閉じ、パッドを戻してから常用ポートを開く。 - // 開けなくてもボードの判定は取り消さない (表示まで失うため)。バックライトやタッチなど - // このポートの利用者が通信できなくなるので、原因が追えるよう警告だけ残す - void handover(int hw_port) - { - release(); - if (!lgfx::i2c::init(hw_port, _sda, _scl).has_value()) - { - ESP_LOGW(LIBRARY_NAME, "[Autodetect] I2C port %d could not be opened for SDA=%d SCL=%d", hw_port, _sda, _scl); - } - } - // ボードが一致しなかった: probe を閉じ、パッドを探索前の状態に戻す - void release(void) - { - lgfx::i2c::release(probe_i2c_port); - for (auto &pin : _pins) { pin.restore(); } - } - private: - gpio::pin_backup_t _pins[2]; - int _sda, _scl; - }; - - // I2Cデバイスの存在をチェックする。 - // SDA,SCLのプルアップが確認できない場合は0を返す。 - // 内部でパッドを backup/restore するので、呼び出し側はこの後に probe_i2c_t を作れば - // 探索前の状態を復元先にできる (プルアップ試験を自前で行うブロックは試験前の backup を渡す)。 - // プルアップが確認できた場合は ~0u を返すが、存在しないデバイスに対応するビットは 0 となる。 - // つまり、引数のアドレスリストにある全てのデバイスが存在する場合は ~0u となる。 - __attribute__ ((unused)) - static uint32_t _detect_i2c_device(uint8_t pin_sda, uint8_t pin_scl, const uint8_t* addr_list) { - gpio::pin_backup_t backup_pins[] = { pin_sda, pin_scl }; - - const uint8_t cmd_i2c_stop_list[] = { - lgfx::gpio::command_write_low , pin_scl, - lgfx::gpio::command_delay_usec , 3, - lgfx::gpio::command_write_low , pin_sda, - lgfx::gpio::command_delay_usec , 3, - lgfx::gpio::command_write_high , pin_scl, // SCL high - lgfx::gpio::command_delay_usec , 5, - lgfx::gpio::command_write_high , pin_sda, // SDA high (I2C STOP) - lgfx::gpio::command_delay_usec , 5, - lgfx::gpio::command_end - }; - - { - lgfx::pinMode(pin_scl, lgfx::pin_mode_t::output); - lgfx::pinMode(pin_sda, lgfx::pin_mode_t::output); - // force I2C stop - for (size_t i = 0; i < 8; ++i) { - lgfx::gpio::command(cmd_i2c_stop_list); - } - } - - const uint8_t cmd_list[] = { - lgfx::gpio::command_write_low , pin_scl, - lgfx::gpio::command_read , pin_scl, // low チェック - lgfx::gpio::command_write_low , pin_sda, - lgfx::gpio::command_read , pin_sda, // low チェック - lgfx::gpio::command_mode_input_pulldown, pin_scl, - lgfx::gpio::command_delay_usec , 10, - lgfx::gpio::command_read , pin_scl, // pulldownチェック (外部プルアップがあるならここでHIGHになる) - lgfx::gpio::command_mode_input_pullup , pin_scl, - lgfx::gpio::command_mode_input_pulldown, pin_sda, - lgfx::gpio::command_delay_usec , 10, - lgfx::gpio::command_read , pin_sda, // pulldownチェック (外部プルアップがあるならここでHIGHになる) - lgfx::gpio::command_mode_input_pullup , pin_sda, - lgfx::gpio::command_end - }; - - // ここでSDA,SCL各2回,合計4回のreadチェックが行われる。 - uint32_t result = lgfx::gpio::command(cmd_list); - // I2Cピンであれば0x03になっているはず - if (result == 0x03) { - // 全ビットを立てる - result = ~0u; - - // アドレスの存在確認はソフトウェア I2C ポートで行う (オープンドレイン駆動で - // ACK 競合が起きず、ハードウェアのペリフェラルにも触れない) - lgfx::i2c::init(probe_i2c_port, pin_sda, pin_scl); - for (; addr_list[0] != 0; ++addr_list) { - uint_fast8_t addr7bit = addr_list[0]; - bool hit = lgfx::i2c::beginTransaction(probe_i2c_port, addr7bit, 100000, false).has_value() - && lgfx::i2c::endTransaction(probe_i2c_port).has_value(); - result = (result << 1) + hit; - ESP_LOGV(LIBRARY_NAME, "[Autodetect] i2c addr:%02x = %s", (int)addr7bit, hit ? "hit" : "--"); - } - lgfx::i2c::release(probe_i2c_port); - } else { - result = 0; - } - for (auto pin: backup_pins) { pin.restore(); } - return result; - } - static constexpr std::uint32_t m5pm1_i2c_freq = 100000; static constexpr std::uint8_t m5pm1_i2c_addr = 0x6E; // M5PM1 device i2c address @@ -862,49 +744,6 @@ namespace m5gfx 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 { @@ -1380,27 +1219,9 @@ namespace m5gfx 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 = SPI3_HOST; - bus_cfg.dma_channel = 1; - -#elif defined (CONFIG_IDF_TARGET_ESP32S3) - - bus_cfg.spi_host = SPI2_HOST; - bus_cfg.dma_channel = SPI_DMA_CH_AUTO; +#if defined (CONFIG_IDF_TARGET_ESP32S3) const board_detect::board_detector_t* const* detectors = nullptr; switch (m5gfx::get_pkg_ver()) @@ -1523,9 +1344,6 @@ namespace m5gfx #elif defined (CONFIG_IDF_TARGET_ESP32C5) - bus_cfg.spi_host = SPI2_HOST; - bus_cfg.dma_channel = SPI_DMA_CH_AUTO; - if (board == 0 || board == board_t::board_M5ToughC5) { // ESP32-C5HF4 (in-package 4MB flash, no PSRAM) boards such as the From 9a6b212f146460725d6fd07dc8492fd408056fc3 Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Sun, 27 Sep 2026 11:51:49 +0000 Subject: [PATCH 2/4] Confirm Core2 and Tough by the PMIC, then read the panel The Core2 family is now confirmed by the AXP192 / AXP2101 ID and the Station exclusion. The ILI9342C / ILI9342E check and the Tough touch probe run in the refine stage after the PMIC power sequence, as on the CoreS3 family. No PMIC register is written before confirmation, so the register backup and restore paths are removed. AXP192 is shared by Core2 v1.0, Tough and Station, and Station is told apart only by its LCD ID. When that read misses and the Core2 panel and the Tough touch controller do not answer either, the attempt fails and detection retries instead of storing Core2. An AXP2101 Core2 whose panel does not answer is still detected as Core2 (the variant then defaults to ILI9342C). --- src/M5GFX.cpp | 5 - src/board_detect/board_detect.hpp | 1 - src/board_detect/m5/esp32_d0wdq6.inl | 357 ++++++--------------------- 3 files changed, 82 insertions(+), 281 deletions(-) diff --git a/src/M5GFX.cpp b/src/M5GFX.cpp index 118b3ae..987bfe6 100644 --- a/src/M5GFX.cpp +++ b/src/M5GFX.cpp @@ -987,11 +987,6 @@ namespace m5gfx { transaction.buses().opened_i2c(adopted_i2c_port); } - if ((result.prepared & board_detect::panel_dirty) && !allow_reset) - { - ESP_LOGD(LIBRARY_NAME, - "[Autodetect] panel probe changed registers while reset was disabled"); - } board_detect::m5::display_parts_t parts; const auto construct_result = board_detect::m5::setup_detected_board(result, &parts); if (construct_result == board_detect::m5::construct_status_t::failed) diff --git a/src/board_detect/board_detect.hpp b/src/board_detect/board_detect.hpp index cb750a3..ed4bda8 100644 --- a/src/board_detect/board_detect.hpp +++ b/src/board_detect/board_detect.hpp @@ -46,7 +46,6 @@ namespace board_detect prepared_power = 1u << 0, prepared_reset = 1u << 1, prepared_sd_spi = 1u << 2, - panel_dirty = 1u << 3, // The GPIO reset line has been driven inactive, but a reset pulse may not // have been allowed. Keep this distinct from prepared_reset so a later // reset-enabled prepare can still pulse the panel reset. diff --git a/src/board_detect/m5/esp32_d0wdq6.inl b/src/board_detect/m5/esp32_d0wdq6.inl index e3a1189..ee7dc80 100644 --- a/src/board_detect/m5/esp32_d0wdq6.inl +++ b/src/board_detect/m5/esp32_d0wdq6.inl @@ -30,23 +30,17 @@ namespace m5 std::uint32_t* detected_option); } - static constexpr std::size_t max_restore_regs = max_pmic_restore_registers; - static const std::uint8_t restore_order192[] = { 0x12, 0x28, 0x92, 0x95, 0x96, 0x94 }; - static const std::uint8_t restore_order2101[] = { 0x80, 0x82, 0x84, 0x90 }; - static_assert(sizeof(restore_order192) <= max_restore_regs - && sizeof(restore_order2101) <= max_restore_regs, - "PMIC restore list exceeds backup capacity"); static_assert(generated_options::core2::new_pmic == generated_options::tough::reserved, "Core2 new-PMIC and Tough reserved option bits must remain shared"); static const pmic_variant_t core_pmic_variants[] = { pmic_variant(0x34, 0x03, 0x03, ops::list(pmic_ops::power192), reg_bit(0x12, 0x04), reg_bit(0x96, 0x02), ops::list(pmic_ops::reset192), ops::list(pmic_ops::release192), - registers(restore_order192), 0), + { nullptr, 0 }, 0), pmic_variant(0x34, 0x03, 0x4A, ops::list(pmic_ops::power2101), reg_bit(0x90, 0x08), reg_bit(0x90, 0x02), ops::list(pmic_ops::reset2101), ops::list(pmic_ops::release2101), - registers(restore_order2101), + { nullptr, 0 }, generated_options::core2::new_pmic), }; @@ -148,148 +142,6 @@ namespace m5 return true; } - std::uint32_t read_panel_id(soft_spi_t& bus, int pin_cs, - std::uint8_t cmd = panel_id_command, - std::uint8_t dummy_bits = 1) - { - bus.beginTransaction(); - startup_detail::pin_level(pin_cs, true); - bus.writeCommand(0, 8); - bus.wait(); - lgfx::gpio_lo(pin_cs); - bus.writeCommand(cmd, 8); - bus.beginRead(dummy_bits); - const auto value = bus.readData(32); - lgfx::gpio_hi(pin_cs); - bus.endTransaction(); - ESP_LOGD("board_detect_m5", "read cmd:%02x = %08x", - static_cast(cmd), static_cast(value)); - return value; - } - - struct register_backup_t - { - std::uint8_t addr = 0; - std::uint8_t reg = 0; - std::uint8_t value = 0; - std::uint8_t mask = 0; - }; - - struct register_spec_t - { - std::uint8_t addr; - std::uint8_t reg; - std::uint8_t mask; - }; - - std::uint8_t changed_mask(const power_desc_t& power, ops::op_list_t sequence, - std::uint8_t addr, std::uint8_t reg) - { - std::uint8_t mask = 0; - for (std::size_t i = 0; i < sequence.size; ++i) - { - ops::decoded_register_write_t write; - if (ops::decode_register_write(sequence.data[i], &write) - && write.dev < power.device_count - && power.devices[write.dev].addr == addr && write.reg == reg) - { - mask |= write.mask; - } - } - return mask; - } - - std::uint8_t changed_mask(const power_desc_t& power, const pmic_variant_t& variant, - std::uint8_t addr, std::uint8_t reg) - { - return changed_mask(power, variant.power_on, addr, reg) - | changed_mask(power, variant.reset_assert, addr, reg) - | changed_mask(power, variant.reset_release, addr, reg); - } - - bool restore_list_covers(const power_desc_t& power, const pmic_variant_t& variant) - { - const ops::op_list_t sequences[] = { - variant.power_on, variant.reset_assert, variant.reset_release - }; - for (auto sequence : sequences) - { - for (std::size_t i = 0; i < sequence.size; ++i) - { - ops::decoded_register_write_t write; - if (!ops::decode_register_write(sequence.data[i], &write)) { continue; } - bool covered = false; - const auto addr = write.dev < power.device_count - ? power.devices[write.dev].addr : 0xFFFF; - if (addr == variant.i2c_addr) - { - for (std::size_t reg = 0; reg < variant.restore_registers.size; ++reg) - { - if (variant.restore_registers.data[reg] == write.reg) - { - covered = true; - break; - } - } - } - if (!covered) - { - ESP_LOGW("board_detect_m5", "PMIC write is absent from restore list addr=%02x reg=%02x", - static_cast(addr), - static_cast(write.reg)); - return false; - } - } - } - return true; - } - - bool save_registers(int port, const register_spec_t* specs, - register_backup_t* saved, std::size_t count, - std::uint32_t i2c_freq) - { - for (std::size_t i = 0; i < count; ++i) - { - auto value = lgfx::i2c::readRegister8(port, specs[i].addr, specs[i].reg, i2c_freq); - if (!value.has_value()) { return false; } - saved[i].addr = specs[i].addr; - saved[i].reg = specs[i].reg; - saved[i].value = value.value(); - saved[i].mask = specs[i].mask; - } - return true; - } - - bool restore_registers(int port, const register_backup_t* saved, std::size_t count, - std::uint32_t i2c_freq) - { - bool restored_all = true; - while (count) - { - --count; - bool restored = false; - for (int attempt = 0; attempt < 4; ++attempt) - { - if (lgfx::i2c::writeRegister8(port, saved[count].addr, saved[count].reg, - saved[count].value & saved[count].mask, - static_cast(~saved[count].mask), - i2c_freq).has_value()) - { - restored = true; - break; - } - } - if (!restored) - { - restored_all = false; - ESP_LOGW("board_detect_m5", "PMIC register restore failed addr=%02x reg=%02x", - static_cast(saved[count].addr), - static_cast(saved[count].reg)); - } - } - return restored_all; - } - enum class panel_variant_t : std::uint8_t { unknown, c, e }; void write8(soft_spi_t& bus, int pin_cs, std::uint8_t cmd, std::uint8_t data) @@ -409,6 +261,83 @@ namespace m5 { &desc_atompsram, construct_atompsram, "", nullptr }, }; + namespace detail + { + bool refine_core_family(board_result_t& result, const prepare_ctx_t& ctx) + { + const auto& display = desc_core2.display; + const std::int8_t signals[] = { + display.dc, display.sclk, display.mosi, display.miso + }; + startup_detail::pin_level(display.cs, true); + startup_detail::pin_level(desc_core2.sd.sd_cs, true); + soft_spi_t bus(display.sclk, display.mosi, display.mosi, display.dc); + bus.init(); + std::uint32_t keys[4] = {}; + // Without reset, leave the same 120 ms window for a waking panel. + auto variant = identify_panel_variant(bus, display.cs, keys, + ctx.allow_reset ? 1 : 120); + if (ctx.allow_reset) + { + startup_detail::i2c_scope_t i2c(*ctx.transaction, ctx.i2c_port_probe, + desc_core2.internal_i2c); + if (!i2c.opened || !prepare_reset(desc_core2, result, ctx, i2c.port)) + { + ctx.transaction->restore_start(signals); + return false; + } + std::uint32_t after_keys[4] = {}; + const auto after = identify_panel_variant(bus, display.cs, after_keys, + 120, variant != panel_variant_t::e); + if (after != panel_variant_t::unknown) + { + variant = after; + for (int i = 0; i < 4; ++i) { keys[i] = after_keys[i]; } + } + } + startup_detail::i2c_scope_t i2c(*ctx.transaction, ctx.i2c_port_probe, + desc_core2.internal_i2c); + if (!i2c.opened) + { + ctx.transaction->restore_start(signals); + return false; + } + const bool tough = lgfx::i2c::readRegister8( + i2c.port, tough_touch_address, touch_probe_register, + tough_touch_i2c_frequency).has_value(); + // AXP192 is shared with Station. If neither LCD key answers and Tough's + // touch is absent, a transient Station probe miss must not become Core2. + if (!tough && variant == panel_variant_t::unknown + && !(result.option & generated_options::core2::new_pmic)) + { + ctx.transaction->restore_start(signals); + return false; + } + if (variant == panel_variant_t::e) + { + ESP_LOGI("board_detect_m5", "ILI9342 read-back DDh:%02x CBh:%02x -> ILI9342E", + static_cast(keys[0]), static_cast(keys[1])); + } + else if (variant == panel_variant_t::c) + { + ESP_LOGI("board_detect_m5", "ILI9342 read-back DDh:%02x CBh:%02x ID4:%02x%02x -> ILI9342C", + static_cast(keys[0]), static_cast(keys[1]), + static_cast(keys[2]), static_cast(keys[3])); + } + else + { + ESP_LOGW("board_detect_m5", "ILI9342 read-back DDh:%02x CBh:%02x ID4:%02x%02x -> neither key answered, ILI9342C assumed", + static_cast(keys[0]), static_cast(keys[1]), + static_cast(keys[2]), static_cast(keys[3])); + } + result.assign(tough ? &desc_tough : &desc_core2); + if (variant == panel_variant_t::e) { result.option |= generated_options::core2::lcd_e; } + if (tough) { result.option &= ~generated_options::core2::new_pmic; } + ctx.transaction->restore_start(signals); + return true; + } + } + class axp_family_detector_t final : public board_detector_t { public: @@ -492,135 +421,13 @@ namespace m5 return true; }; - const bool core_first = -#if defined (M5GFX_AUTODETECT_TEST_STATION_TO_CORE2) - false; -#else - ctx.hint == id(lgfx::board_M5StackCore2) - || ctx.hint == id(lgfx::board_M5Tough); -#endif - if (!core_first && try_station()) { return true; } - - // Before the touch controller distinguishes Tough from Core2, a caller's - // explicit Tough hint selects the Tough descriptor for PMIC bookkeeping. - const auto& core_desc = ctx.hint == id(lgfx::board_M5Tough) ? desc_tough : desc_core2; - - // The variant owns the legacy read order; masks remain derived from all - // of its power/reset writes so the restore description cannot drift. - const auto& restore_regs = pmic->restore_registers; - if (restore_regs.size > max_restore_regs - || !detail::restore_list_covers(core_desc.power, *pmic)) - { - return false; - } - detail::register_spec_t regs[max_restore_regs]; - for (std::size_t i = 0; i < restore_regs.size; ++i) - { - regs[i].addr = pmic->i2c_addr; - regs[i].reg = restore_regs.data[i]; - regs[i].mask = detail::changed_mask(core_desc.power, *pmic, - pmic->i2c_addr, restore_regs.data[i]); - } - detail::register_backup_t saved[max_restore_regs]; - if (!detail::save_registers(i2c.port, regs, saved, restore_regs.size, - desc_core2.power.i2c_freq)) - { - return core_first && try_station(); - } - + // Station must be excluded before accepting the shared AXP signature. + if (try_station()) { return true; } *result = {}; result->assign(&desc_core2); result->option = pmic->detected_option; result->prepared = preprepared; - const std::int8_t signals[] = { - desc_core2.display.dc, desc_core2.display.sclk, - desc_core2.display.mosi, desc_core2.display.miso - }; - auto restore_and_fail = [&]() -> bool - { - ctx.transaction->restore_start(signals); - detail::restore_registers(i2c.port, saved, restore_regs.size, - desc_core2.power.i2c_freq); - return core_first && try_station(); - }; - if (!startup_detail::prepare_power(desc_core2, *result, i2c.port)) { return restore_and_fail(); } - startup_detail::pin_level(desc_core2.sd.sd_cs, true); - if (!startup_detail::prepare_sd_spi(desc_core2, *result, prepare_ctx)) { return restore_and_fail(); } - startup_detail::hold_chip_selects(desc_core2); - - const auto& display = desc_core2.display; - soft_spi_t bus(display.sclk, display.mosi, display.mosi, display.dc); - bus.init(); - auto panel_id = detail::read_panel_id(bus, display.cs); - bool reset_before_identify = false; - if ((panel_id & detail::panel_id_mask) != detail::common_panel_id && ctx.allow_reset) - { - // Some valid panels do not answer until reset. Retry here because a - // later detection attempt must not relax the caller's reset policy. - auto forced_prepare_ctx = prepare_ctx; - forced_prepare_ctx.allow_reset = true; - if (!prepare_reset(desc_core2, *result, forced_prepare_ctx, i2c.port)) - { - return restore_and_fail(); - } - reset_before_identify = true; - panel_id = detail::read_panel_id(bus, display.cs); - } - if ((panel_id & detail::panel_id_mask) != detail::common_panel_id) - { - return restore_and_fail(); - } - - std::uint32_t keys[4] = {}; - auto variant = detail::identify_panel_variant( - bus, display.cs, keys, reset_before_identify ? 120 : 1); - result->prepared |= panel_dirty; - if (ctx.allow_reset && !reset_before_identify) - { - auto forced_prepare_ctx = prepare_ctx; - forced_prepare_ctx.allow_reset = true; - if (!prepare_reset(desc_core2, *result, forced_prepare_ctx, i2c.port)) - { - return restore_and_fail(); - } - std::uint32_t after_keys[4] = {}; - const auto after = detail::identify_panel_variant( - bus, display.cs, after_keys, 120, variant != detail::panel_variant_t::e); - if (after != detail::panel_variant_t::unknown) - { - variant = after; - for (int i = 0; i < 4; ++i) { keys[i] = after_keys[i]; } - } - } - if (variant == detail::panel_variant_t::e) - { - ESP_LOGI("board_detect_m5", "ILI9342 read-back DDh:%02x CBh:%02x -> ILI9342E", - static_cast(keys[0]), static_cast(keys[1])); - } - else if (variant == detail::panel_variant_t::c) - { - ESP_LOGI("board_detect_m5", "ILI9342 read-back DDh:%02x CBh:%02x ID4:%02x%02x -> ILI9342C", - static_cast(keys[0]), static_cast(keys[1]), - static_cast(keys[2]), static_cast(keys[3])); - } - else - { - ESP_LOGW("board_detect_m5", "ILI9342 read-back DDh:%02x CBh:%02x ID4:%02x%02x -> neither key answered, ILI9342C assumed", - static_cast(keys[0]), static_cast(keys[1]), - static_cast(keys[2]), static_cast(keys[3])); - } - - const bool tough = lgfx::i2c::readRegister8( - i2c.port, detail::tough_touch_address, detail::touch_probe_register, - detail::tough_touch_i2c_frequency).has_value(); - result->assign(tough ? &desc_tough : &desc_core2); - result->option = pmic->detected_option - | (variant == detail::panel_variant_t::e - ? (tough ? generated_options::tough::lcd_e - : generated_options::core2::lcd_e) - : 0); - if (tough) { result->option &= ~generated_options::core2::new_pmic; } - ctx.transaction->restore_start(signals); + result->refine = detail::refine_core_family; return true; } From e8488842d76a9757392b37adf315175f5d628aeb Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Sun, 27 Sep 2026 11:51:49 +0000 Subject: [PATCH 3/4] Select the Tab5 panel from the touch controller The touch controller identifies the panel: ST touch firmware 1 means ST7121, 3 means ST7123, and a GT911 means ILI9881C. Use that result directly, without the 80 ms wait and the DSI ID reads that repeated the check. Only when no touch controller answers, fall back to the DSI ID read for ILI9881C as before. --- extras/board_spec/test/board_spec.test.js | 5 ++- src/board_detect/m5/esp32p4/tab5_setup.inl | 39 ++++++++++------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/extras/board_spec/test/board_spec.test.js b/extras/board_spec/test/board_spec.test.js index 510468c..29be980 100644 --- a/extras/board_spec/test/board_spec.test.js +++ b/extras/board_spec/test/board_spec.test.js @@ -2322,7 +2322,7 @@ test("CoreP4X generated DSI setup preserves the legacy fields", async () => { assert.match(main, /pkg_ver == 0[\s\S]*?try_setup_detected\(board_detect::m5::esp32p4_detectors/); }); -test("Tab5 generated DSI setup preserves all three legacy panels", () => { +test("Tab5 touch identities select the three DSI panels", () => { const source = catalogBoards.find((item) => item.id === "m5stack_tab5"); const resolved = resolveCatalog(source).map((item) => item.board); const specs = emitM5GFXSpecs(source, resolved, parts, m5gfxBoardMapping(target, source.id)); @@ -2332,6 +2332,9 @@ test("Tab5 generated DSI setup preserves all three legacy panels", () => { // requires 900 Mbps after runtime touch-FW identification. assert.match(tab5SetupSource, /tab5_st7121_lane_mbps = 900/); assert.match(tab5SetupSource, /hit_st7121 \? tab5_st7121_lane_mbps : specs::tab5::bus_lane_mbps/); + assert.match(tab5SetupSource, /fw_version == 1[\s\S]*?hit_st7121 = true[\s\S]*?fw_version == 3[\s\S]*?hit_st7123 = true/); + assert.match(tab5SetupSource, /if \(!read_st_touch_fw && !found_gt911\)[\s\S]*?delay\(80\)[\s\S]*?i < 3 && !hit_ili9881[\s\S]*?id\[0\] == 0x98 && id\[1\] == 0x81/); + assert.match(tab5SetupSource, /if \(found_gt911 \|\| hit_ili9881\)[\s\S]*?Panel_ILI9881C/); assert.equal(specs.panels.ili9881c.dpi_freq_mhz, 80); assert.equal(specs.panels.st7121.dpi_freq_mhz, 70); assert.equal(specs.panels.st7123.vsync_back_porch + specs.panels.st7123.vsync_pulse_width, 10); diff --git a/src/board_detect/m5/esp32p4/tab5_setup.inl b/src/board_detect/m5/esp32p4/tab5_setup.inl index 01cd477..cb798e9 100644 --- a/src/board_detect/m5/esp32p4/tab5_setup.inl +++ b/src/board_detect/m5/esp32p4/tab5_setup.inl @@ -131,31 +131,28 @@ namespace m5 if (!bus_dsi->init()) { return construct_status_t::no_display; } bool hit_ili9881 = false; - lgfx::delay(80); - for (int i = 0; !hit_st7121 && !hit_st7123 && !hit_ili9881 && i < 3; ++i) + if (!read_st_touch_fw && !found_gt911) { - std::uint8_t id[3] = {}; - bus_dsi->readParams(0xF4, id, 2); - ESP_LOGD(LIBRARY_NAME, "ST ID %02x %02x", id[0], id[1]); - if (id[0] == 0x71 && id[1] == 0x23) - { - ESP_LOGI(LIBRARY_NAME, "M5Tab5 ST DSI ID matched 71 23"); - } - static constexpr std::uint8_t params_page1[] = { 0x98, 0x81, 0x01 }; - bus_dsi->writeParams(0xFF, params_page1, 3); - bus_dsi->readParams(0x00, &id[0], 1); - bus_dsi->readParams(0x01, &id[1], 1); - bus_dsi->readParams(0x02, &id[2], 1); - ESP_LOGD(LIBRARY_NAME, "ILI ID %02x %02x %02x", id[0], id[1], id[2]); - if (id[0] == 0x98 && id[1] == 0x81) + // A temporarily silent touch must not hide an answering ILI9881C. + lgfx::delay(80); + for (int i = 0; i < 3 && !hit_ili9881; ++i) { - static constexpr std::uint8_t params_page0[] = { 0x98, 0x81, 0x00 }; - bus_dsi->writeParams(0xFF, params_page0, 3); - hit_ili9881 = true; + std::uint8_t id[3] = {}; + static constexpr std::uint8_t params_page1[] = { 0x98, 0x81, 0x01 }; + bus_dsi->writeParams(0xFF, params_page1, 3); + bus_dsi->readParams(0x00, &id[0], 1); + bus_dsi->readParams(0x01, &id[1], 1); + bus_dsi->readParams(0x02, &id[2], 1); + ESP_LOGD(LIBRARY_NAME, "ILI ID %02x %02x %02x", id[0], id[1], id[2]); + if (id[0] == 0x98 && id[1] == 0x81) + { + static constexpr std::uint8_t params_page0[] = { 0x98, 0x81, 0x00 }; + bus_dsi->writeParams(0xFF, params_page0, 3); + hit_ili9881 = true; + } } } - - if (hit_ili9881) + if (found_gt911 || hit_ili9881) { out.touch.reset(make_i2c_touch(touch_tab5)); out.panel.reset(make_dsi_panel(panel_tab5_ili9881c, bus_dsi)); From c770038dcf595ffa1aa227f0319a6d8c47701b22 Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Sun, 27 Sep 2026 11:51:49 +0000 Subject: [PATCH 4/4] Read the CoreS3 panel variant in the refine stage Read the ILI9342C / ILI9342E keys with the same software SPI sequence as Core2, from the refine stage, and pass the result to construction as an option bit. The LCD bus is 3-wire, so read data returns on MOSI. This removes the hardware-SPI panel ID helpers from M5GFX.cpp, and the construction now starts the bus at its normal 40 / 16 MHz. ToughC5 and NessoN1 no longer read the panel ID only to log a mismatch warning; the result never affected detection. --- extras/board_spec/dist/board_spec_editor.html | 2 +- extras/board_spec/targets.json | 6 +- extras/board_spec/test/board_spec.test.js | 10 +- src/M5GFX.cpp | 121 +----------------- src/board_detect/board_detect.inl | 2 + src/board_detect/m5/board_registry.inl | 103 +++++++++++++++ src/board_detect/m5/esp32_d0wdq6.inl | 96 +------------- src/board_detect/m5/esp32c5/toughc5_setup.inl | 24 +--- .../m5/esp32c6/c6_display_setup.inl | 19 +-- src/board_detect/m5/esp32s3/cores3.inl | 35 ++++- src/board_detect/m5/esp32s3/cores3_setup.inl | 30 +---- .../m5/generated/esp32s3_wiring.hpp | 11 +- 12 files changed, 165 insertions(+), 294 deletions(-) diff --git a/extras/board_spec/dist/board_spec_editor.html b/extras/board_spec/dist/board_spec_editor.html index c52b34f..8aa85ac 100644 --- a/extras/board_spec/dist/board_spec_editor.html +++ b/extras/board_spec/dist/board_spec_editor.html @@ -959,7 +959,7 @@

- +