From 63e93927fbf86488ee7358eb51ab46cffe0dde0f Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Mon, 19 Oct 2026 11:25:12 +0000 Subject: [PATCH 1/2] Initialize hardware I2C in consumers instead of board detection Detection communicates over software I2C only; opening hardware peripherals is outside its responsibility. Like SPI, hardware I2C ports are now initialized by their consumers. Remove preparation handoff and bus journaling. Initialize the affected backlights and CoreMatrix transport in their own setup. Observe Tab5 touch identities with scoped software I2C after power, preserving retries and partial-power-failure observations while keeping public options and NVS unchanged. --- extras/board_spec/test/board_spec.test.js | 2 +- src/M5GFX.cpp | 29 ++++-- src/board_detect/board_detect.hpp | 19 +--- src/board_detect/detect_types.hpp | 3 + src/board_detect/detection_integration.inl | 12 --- src/board_detect/m5/esp32c5/toughc5_setup.inl | 3 +- .../m5/esp32c6/c6_display_setup.inl | 3 +- .../m5/esp32c61/corematrix_setup.inl | 4 + src/board_detect/m5/esp32p4.inl | 4 +- src/board_detect/m5/esp32p4/corep4x_setup.inl | 1 + src/board_detect/m5/esp32p4/tab5.inl | 66 +++++++++++++ src/board_detect/m5/esp32p4/tab5_setup.inl | 48 +-------- src/board_detect/prepare.inl | 30 +----- src/board_detect/transaction.inl | 25 ----- test/board_detect/README.md | 4 +- test/board_detect/construction_c61.cpp | 47 +++++++++ test/board_detect/construction_p4.cpp | 97 +++++++++++++++++++ test/board_detect/fixtures/p4.txt | 4 +- test/board_detect/integration.cpp | 31 +++++- test/board_detect/package_p4.cpp | 96 +++++++++++++++++- .../stubs/lgfx/v1/platforms/esp32/common.hpp | 1 + test/board_detect/support/display_devices.hpp | 2 + test/board_detect/support/dsi_devices.hpp | 48 +++++++++ 23 files changed, 441 insertions(+), 138 deletions(-) create mode 100644 test/board_detect/construction_c61.cpp create mode 100644 test/board_detect/construction_p4.cpp create mode 100644 test/board_detect/support/dsi_devices.hpp diff --git a/extras/board_spec/test/board_spec.test.js b/extras/board_spec/test/board_spec.test.js index 4c05c2b8..0a760080 100644 --- a/extras/board_spec/test/board_spec.test.js +++ b/extras/board_spec/test/board_spec.test.js @@ -2329,7 +2329,7 @@ test("Tab5 touch identities select the three DSI panels", async () => { const tab5SetupSource = await fs.readFile(path.join(root, "../../src/board_detect/m5/esp32p4/tab5_setup.inl"), "utf8"); 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, /hit_st7121 = result.prepared & tab5_detail::touch_st7121[\s\S]*?hit_st7123 = result.prepared & tab5_detail::touch_st7123/); 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/); }); diff --git a/src/M5GFX.cpp b/src/M5GFX.cpp index c00cc932..159e8d36 100644 --- a/src/M5GFX.cpp +++ b/src/M5GFX.cpp @@ -34,6 +34,9 @@ #include "lgfx/v1/touch/Touch_CSTxxx.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/Touch_ST7123.hpp" +#endif #include "board_detect/m5/setup_sentinels.hpp" @@ -69,7 +72,6 @@ #include "lgfx/v1/platforms/esp32p4/Panel_ST7102.hpp" #include "lgfx/v1/platforms/esp32p4/Panel_ST7121.hpp" #include "lgfx/v1/platforms/esp32p4/Panel_ST7123.hpp" -#include "lgfx/v1/platforms/esp32p4/Touch_ST7123.hpp" #elif defined ( CONFIG_IDF_TARGET_ESP32S3 ) @@ -138,11 +140,12 @@ namespace m5gfx #if defined (CONFIG_IDF_TARGET_ESP32P4) struct Light_M5CoreP4X : public lgfx::ILight { - Light_M5CoreP4X(int port, std::uint8_t addr, std::uint32_t freq) - : _port(port), _addr(addr), _freq(freq) {} + Light_M5CoreP4X(int port, int sda, int scl, std::uint8_t addr, std::uint32_t freq) + : _port(port), _sda(sda), _scl(scl), _addr(addr), _freq(freq) {} bool init(uint8_t brightness) override { + lgfx::i2c::init(_port, _sda, _scl); static constexpr uint16_t pwm_freq = 1000; const uint8_t freq_data[] = { 0x25, static_cast(pwm_freq), static_cast(pwm_freq >> 8) @@ -163,6 +166,8 @@ namespace m5gfx } private: int _port; + int _sda; + int _scl; std::uint8_t _addr; std::uint32_t _freq; }; @@ -237,6 +242,7 @@ namespace m5gfx { bool init(std::uint8_t brightness) override { + lgfx::i2c::init(axp_i2c_port, axp_i2c_sda, axp_i2c_scl); setBrightness(brightness); return true; } @@ -261,6 +267,7 @@ namespace m5gfx { bool init(std::uint8_t brightness) override { + lgfx::i2c::init(axp_i2c_port, axp_i2c_sda, axp_i2c_scl); setBrightness(brightness); return true; } @@ -286,6 +293,7 @@ namespace m5gfx { bool init(std::uint8_t brightness) override { + lgfx::i2c::init(axp_i2c_port, axp_i2c_sda, axp_i2c_scl); setBrightness(brightness); return true; } @@ -478,6 +486,7 @@ namespace m5gfx { bool init(uint8_t brightness) override { + lgfx::i2c::init(i2c_port, i2c_sda, i2c_scl); setBrightness(brightness); return true; } @@ -700,12 +709,13 @@ namespace m5gfx struct Light_M5ToughC5 : public lgfx::ILight { - Light_M5ToughC5(int_fast16_t port, std::uint8_t addr, std::uint32_t freq) - : _port(port), _addr(addr), _freq(freq) {} + Light_M5ToughC5(int_fast16_t port, int sda, int scl, std::uint8_t addr, std::uint32_t freq) + : _port(port), _sda(sda), _scl(scl), _addr(addr), _freq(freq) {} // LCD backlight = M5IOE1 PIN10, driven by the expander's PWM channel 4. // Registers: 0x21/0x22 = PWM4 duty (12bit, H[7]=enable), 0x25/0x26 = shared PWM frequency (Hz). bool init(uint8_t brightness) override { + lgfx::i2c::init(_port, _sda, _scl); static constexpr uint8_t freq_1khz[] = { 0x25, 0xE8, 0x03 }; lgfx::i2c::transactionWrite(_port, _addr, freq_1khz, sizeof(freq_1khz), _freq); lgfx::i2c::bitOn(_port, _addr, 0x04, 0x02, _freq); // PIN10 output mode @@ -728,6 +738,8 @@ namespace m5gfx } private: int_fast16_t _port; + int _sda; + int _scl; std::uint8_t _addr; std::uint32_t _freq; }; @@ -736,11 +748,12 @@ namespace m5gfx struct Light_ArduinoNessoN1 : public lgfx::ILight { - Light_ArduinoNessoN1(int_fast16_t port, std::uint8_t addr, std::uint32_t freq) - : _port(port), _addr(addr), _freq(freq) {} + Light_ArduinoNessoN1(int_fast16_t port, int sda, int scl, std::uint8_t addr, std::uint32_t freq) + : _port(port), _sda(sda), _scl(scl), _addr(addr), _freq(freq) {} bool init(uint8_t brightness) override { + lgfx::i2c::init(_port, _sda, _scl); setBrightness(brightness); return true; } @@ -756,6 +769,8 @@ namespace m5gfx private: int_fast16_t _port; + int _sda; + int _scl; std::uint8_t _addr; std::uint32_t _freq; }; diff --git a/src/board_detect/board_detect.hpp b/src/board_detect/board_detect.hpp index b9b896c8..7a28bf2d 100644 --- a/src/board_detect/board_detect.hpp +++ b/src/board_detect/board_detect.hpp @@ -96,20 +96,8 @@ namespace board_detect std::uint8_t count_ = 0; }; - class detection_bus_journal_t - { - public: - void opened_i2c(int port); - void rollback(); - void commit(); - - private: - std::int8_t i2c_ports_[2] = { -1, -1 }; - std::uint8_t i2c_count_ = 0; - }; - // Capture releases LP pad ownership after saving GPIO state. - // Rollback restores GPIO and releases journaled buses, but deliberately leaves + // Rollback restores GPIO, but deliberately leaves // LP ownership released: stale LP routing would hide the pad behind a controller-held line. // PMIC register writes are not restored. class detection_transaction_t @@ -127,11 +115,9 @@ namespace board_detect void restore_start(const std::int8_t (&pins)[N]) { restore_start(pins, N); } void rollback(); void commit(); - detection_bus_journal_t& buses() { return buses_; } private: detection_gpio_snapshot_t gpio_; - detection_bus_journal_t buses_; bool valid_ = false; bool committed_ = false; }; @@ -229,7 +215,8 @@ namespace board_detect struct i2c_desc_t { // PMIC power/reset descriptions require valid SDA/SCL even when hw_port - // is -1; hw_port only controls whether the bus remains open after prepare. + // is -1. hw_port tells consumers which hardware port to open themselves; + // detection uses only software I2C and does not initialize hardware ports. std::int8_t sda; std::int8_t scl; std::int8_t hw_port; diff --git a/src/board_detect/detect_types.hpp b/src/board_detect/detect_types.hpp index f1662e8e..1ab8787b 100644 --- a/src/board_detect/detect_types.hpp +++ b/src/board_detect/detect_types.hpp @@ -88,6 +88,9 @@ namespace board_detect // Optional post-power observation for an already confirmed member. Unlike // refinement, failure of retained power may skip it without losing identity. refine_fn_t observe_after_power = nullptr; + // Some controllers still answer after a partial power sequence; preserve + // their observations without changing the default skip policy. + bool observe_on_power_failure = false; void assign(const board_desc_t* value); }; diff --git a/src/board_detect/detection_integration.inl b/src/board_detect/detection_integration.inl index 83a485b0..83b9bb80 100644 --- a/src/board_detect/detection_integration.inl +++ b/src/board_detect/detection_integration.inl @@ -119,16 +119,8 @@ const auto setup_board = static_cast(result.def->id); const board_detect::prepare_ctx_t& prepare_ctx = probe; - const int adopted_i2c_port = result.desc->internal_i2c.hw_port; - const bool adopted_i2c_was_open = adopted_i2c_port >= 0 - && lgfx::i2c::isInitialized(adopted_i2c_port); const bool prepared = result.desc != nullptr && board_detect::prepare(*result.desc, result, prepare_ctx); - if (adopted_i2c_port >= 0 && !adopted_i2c_was_open - && lgfx::i2c::isInitialized(adopted_i2c_port)) - { - transaction.buses().opened_i2c(adopted_i2c_port); - } outcome.result = result; const bool accepted = board_detect::finalize_prepared_result(outcome, request.preferred); if (!prepared) @@ -216,12 +208,8 @@ ctx.allow_reset = allow_reset; ctx.i2c_port_probe = probe_i2c_port; ctx.transaction = &transaction; - const int port = desc.internal_i2c.hw_port; - const bool was_open = port >= 0 && lgfx::i2c::isInitialized(port); const bool started = entry.fixed_start ? entry.fixed_start(result, ctx) : prepare(desc, result, ctx); - if (port >= 0 && !was_open && lgfx::i2c::isInitialized(port)) - { transaction.buses().opened_i2c(port); } outcome.result = result; outcome.verdict = verdict_t::confirmed; outcome.attempts = 1; diff --git a/src/board_detect/m5/esp32c5/toughc5_setup.inl b/src/board_detect/m5/esp32c5/toughc5_setup.inl index 1ac62481..052b129f 100644 --- a/src/board_detect/m5/esp32c5/toughc5_setup.inl +++ b/src/board_detect/m5/esp32c5/toughc5_setup.inl @@ -32,7 +32,8 @@ namespace m5 out.bus.reset(make_spi_bus(bus_toughc5)); out.panel.reset(make_panel(panel_toughc5, out.bus.get())); out.light.reset(make_default_part( - toughc5_detail::i2c_port, pmic_ops::ioe1_i2c_addr, toughc5_detail::i2c_freq)); + toughc5_detail::i2c_port, toughc5_detail::sda, toughc5_detail::scl, + pmic_ops::ioe1_i2c_addr, toughc5_detail::i2c_freq)); out.touch.reset(make_i2c_touch(touch_toughc5)); out.panel->touch(out.touch.get()); return construct_status(out.release_to(parts)); diff --git a/src/board_detect/m5/esp32c6/c6_display_setup.inl b/src/board_detect/m5/esp32c6/c6_display_setup.inl index 7996bed8..71f7d319 100644 --- a/src/board_detect/m5/esp32c6/c6_display_setup.inl +++ b/src/board_detect/m5/esp32c6/c6_display_setup.inl @@ -58,7 +58,8 @@ construct_status_t construct_nesson1(const board_result_t&, display_parts_t* par out.panel.reset(make_panel(panel_nesson1, out.bus.get())); out.touch.reset(make_i2c_touch(touch_nesson1)); out.light.reset(new Light_ArduinoNessoN1( - c6_display_detail::i2c_port, specs::nesson1::backlight_i2c::i2c_addr, + c6_display_detail::i2c_port, c6_display_detail::sda, c6_display_detail::scl, + specs::nesson1::backlight_i2c::i2c_addr, specs::nesson1::backlight_i2c::i2c_freq)); out.panel->touch(out.touch.get()); return construct_status(out.release_to(parts)); diff --git a/src/board_detect/m5/esp32c61/corematrix_setup.inl b/src/board_detect/m5/esp32c61/corematrix_setup.inl index d39f30d1..11311cd7 100644 --- a/src/board_detect/m5/esp32c61/corematrix_setup.inl +++ b/src/board_detect/m5/esp32c61/corematrix_setup.inl @@ -15,6 +15,8 @@ namespace m5 construct_status_t construct_corematrix(const board_result_t&, display_parts_t* parts) { + lgfx::i2c::init(corematrix_detail::i2c_port, wiring::corematrix::internal_i2c_sda, + wiring::corematrix::internal_i2c_scl); bool tm1680_ack = lgfx::i2c::beginTransaction( corematrix_detail::i2c_port, specs::corematrix::bus_i2c_addr, corematrix_detail::i2c_freq, false).has_value(); @@ -22,6 +24,8 @@ namespace m5 { tm1680_ack = lgfx::i2c::endTransaction(corematrix_detail::i2c_port).has_value(); } + // Construction may fail; close the ACK probe and let Bus_I2C::init own the port. + lgfx::i2c::release(corematrix_detail::i2c_port); if (!tm1680_ack) { ESP_LOGW(LIBRARY_NAME, "[Autodetect] CoreMatrix TM1680 did not acknowledge"); diff --git a/src/board_detect/m5/esp32p4.inl b/src/board_detect/m5/esp32p4.inl index 3fc1cb02..87691519 100644 --- a/src/board_detect/m5/esp32p4.inl +++ b/src/board_detect/m5/esp32p4.inl @@ -29,8 +29,8 @@ static constexpr board_desc_t desc_stampp4 = { #include "generated/esp32p4_detector_order.hpp" static const board_entry_t esp32p4_boards[] = { { &desc_corep4x, construct_corep4x, "board_M5CoreP4X", nullptr }, - { &desc_tab5, construct_tab5, "board_M5Tab5", nullptr }, - { &desc_tab5x, construct_tab5, "board_M5Tab5X", nullptr }, + { &desc_tab5, construct_tab5, "board_M5Tab5", nullptr, tab5_detail::fixed_start }, + { &desc_tab5x, construct_tab5, "board_M5Tab5X", nullptr, tab5_detail::fixed_start }, { &desc_unitpoep4, construct_displayless, "board_M5UnitPoEP4", nullptr }, { &desc_stampp4, construct_displayless, "board_M5StampP4", nullptr }, }; diff --git a/src/board_detect/m5/esp32p4/corep4x_setup.inl b/src/board_detect/m5/esp32p4/corep4x_setup.inl index 9bdf8ab0..5cb90e22 100644 --- a/src/board_detect/m5/esp32p4/corep4x_setup.inl +++ b/src/board_detect/m5/esp32p4/corep4x_setup.inl @@ -50,6 +50,7 @@ namespace m5 out.touch.reset(make_i2c_touch(touch_corep4x)); out.panel->setTouch(out.touch.get()); out.light.reset(new Light_M5CoreP4X(corep4x_detail::i2c_port, + corep4x_detail::sda, corep4x_detail::scl, pmic_ops::ioe1_i2c_addr, corep4x_detail::i2c_freq)); return construct_status(out.release_to(parts)); diff --git a/src/board_detect/m5/esp32p4/tab5.inl b/src/board_detect/m5/esp32p4/tab5.inl index 7245d7bd..55aac5e9 100644 --- a/src/board_detect/m5/esp32p4/tab5.inl +++ b/src/board_detect/m5/esp32p4/tab5.inl @@ -7,6 +7,69 @@ namespace tab5_detail constexpr std::uint32_t i2c_freq = 100000; constexpr std::uint8_t pi4io1_addr = 0x43; constexpr std::uint8_t pi4io2_addr = 0x44; + // Board-private prepared bits carry observations to construction. They never + // enter the public option or NVS; detection must use software I2C only. + enum : std::uint32_t + { + read_st_touch_fw = 1u << 16, + touch_st7121 = 1u << 17, + touch_st7123 = 1u << 18, + touch_gt911 = 1u << 19, + }; + + bool observe_touch(board_result_t& result, const prepare_ctx_t& ctx) + { +#if CONFIG_SPIRAM + startup_detail::i2c_scope_t i2c(*ctx.transaction, ctx.i2c_port_probe, + result.desc->internal_i2c); + if (!i2c.opened) { return false; } + int last_logged_fw = -1; + for (int i = 0; i < 60; ++i) + { + std::uint8_t fw_version = 0; + std::uint8_t fw_reg[2] = { 0, 0 }; + if (lgfx::i2c::transactionWriteRead(i2c.port, lgfx::Touch_ST7123::default_addr, + fw_reg, sizeof(fw_reg), &fw_version, 1, + i2c_freq).has_value()) + { + result.prepared |= read_st_touch_fw; + if (fw_version != last_logged_fw) + { + last_logged_fw = fw_version; + ESP_LOGI("M5GFX", "M5Tab5 ST touch FW version %02x", fw_version); + if (fw_version != 1 && fw_version != 3) + { + ESP_LOGW("M5GFX", "M5Tab5 unknown ST touch FW version %02x", fw_version); + } + } + if (fw_version == 1) { result.prepared |= touch_st7121; break; } + if (fw_version == 3) { result.prepared |= touch_st7123; break; } + } + else if (lgfx::i2c::beginTransaction(i2c.port, lgfx::Touch_GT911::default_addr_1, + i2c_freq, false).has_value() + && lgfx::i2c::endTransaction(i2c.port).has_value()) + { + result.prepared |= touch_gt911; + ESP_LOGI("M5GFX", "M5Tab5 GT911 touch detected"); + break; + } + lgfx::delay(10); + } + if (!(result.prepared & (read_st_touch_fw | touch_gt911))) + { + ESP_LOGW("M5GFX", "M5Tab5 ST touch FW version read failed"); + } +#endif + return true; + } + + bool fixed_start(board_result_t& result, const prepare_ctx_t& ctx) + { + result.observe_after_power = observe_touch; + result.observe_on_power_failure = true; + return prepare(*result.desc, result, ctx); + } + // prepare drives TP_INT while releasing the touch controller, so it must be // inside both the operation scope and the detection transaction snapshot. static constexpr std::int8_t prepare_gpio_pins[] = { touch_int }; @@ -73,6 +136,9 @@ public: const auto* desc = revision_desc(); warn_hint_mismatch(ctx, &desc->def); result->assign(desc); + result->observe_after_power = tab5_detail::observe_touch; + // The touch controller may be readable even if a later power write failed. + result->observe_on_power_failure = true; result->candidate = ctx.candidate; return true; } diff --git a/src/board_detect/m5/esp32p4/tab5_setup.inl b/src/board_detect/m5/esp32p4/tab5_setup.inl index cb798e94..266bd5e5 100644 --- a/src/board_detect/m5/esp32p4/tab5_setup.inl +++ b/src/board_detect/m5/esp32p4/tab5_setup.inl @@ -63,7 +63,7 @@ namespace m5 .with_invert(specs::tab5::backlight::invert) .with_offset(specs::tab5::backlight::offset); - construct_status_t construct_tab5(const board_result_t&, display_parts_t* parts) + construct_status_t construct_tab5(const board_result_t& result, display_parts_t* parts) { #if !CONFIG_SPIRAM ESP_LOGE(LIBRARY_NAME, "M5Tab5 need PSRAM enabled"); @@ -78,48 +78,10 @@ namespace m5 #endif #endif - bool hit_st7121 = false; - bool hit_st7123 = false; - bool read_st_touch_fw = false; - bool found_gt911 = false; - int last_logged_fw = -1; - for (int i = 0; i < 60; ++i) - { - std::uint8_t fw_version = 0; - std::uint8_t fw_reg[2] = { 0, 0 }; - if (lgfx::i2c::transactionWriteRead(tab5_detail::i2c_port, - lgfx::Touch_ST7123::default_addr, - fw_reg, sizeof(fw_reg), &fw_version, 1, - tab5_detail::i2c_freq).has_value()) - { - read_st_touch_fw = true; - if (fw_version != last_logged_fw) - { - last_logged_fw = fw_version; - ESP_LOGI(LIBRARY_NAME, "M5Tab5 ST touch FW version %02x", fw_version); - if (fw_version != 1 && fw_version != 3) - { - ESP_LOGW(LIBRARY_NAME, "M5Tab5 unknown ST touch FW version %02x", fw_version); - } - } - if (fw_version == 1) { hit_st7121 = true; break; } - if (fw_version == 3) { hit_st7123 = true; break; } - } - else if (lgfx::i2c::beginTransaction(tab5_detail::i2c_port, - lgfx::Touch_GT911::default_addr_1, - tab5_detail::i2c_freq, false).has_value() - && lgfx::i2c::endTransaction(tab5_detail::i2c_port).has_value()) - { - found_gt911 = true; - ESP_LOGI(LIBRARY_NAME, "M5Tab5 GT911 touch detected"); - break; - } - lgfx::delay(10); - } - if (!read_st_touch_fw && !found_gt911) - { - ESP_LOGW(LIBRARY_NAME, "M5Tab5 ST touch FW version read failed"); - } + const bool hit_st7121 = result.prepared & tab5_detail::touch_st7121; + const bool hit_st7123 = result.prepared & tab5_detail::touch_st7123; + const bool read_st_touch_fw = result.prepared & tab5_detail::read_st_touch_fw; + const bool found_gt911 = result.prepared & tab5_detail::touch_gt911; const dsi_bus_desc_t bus_tab5 = dsi_bus(specs::tab5::bus_id, specs::tab5::bus_lane_num, diff --git a/src/board_detect/prepare.inl b/src/board_detect/prepare.inl index 504c5f4d..63838250 100644 --- a/src/board_detect/prepare.inl +++ b/src/board_detect/prepare.inl @@ -49,9 +49,9 @@ } if (!(result.prepared & prepared_observation) && result.observe_after_power != nullptr) { - // Panel metadata is optional after retained power failure; the member - // was already confirmed before this observer was attached. - if (!(result.prepared & prepared_power_failed) + // Optional metadata may skip retained power failure. Observers whose + // controllers can still answer may opt in without changing board identity. + if ((!(result.prepared & prepared_power_failed) || result.observe_on_power_failure) && !result.observe_after_power(result, ctx)) { // Optional panel metadata must not discard a member confirmed by touch. @@ -78,27 +78,7 @@ } } startup_detail::hold_chip_selects(*current); - // The internal port is taken over here and handed to later users. Opening it - // on other pins before autodetect is a misuse; it is reported, not restored. - if (current->internal_i2c.hw_port >= 0 - && lgfx::i2c::isInitialized(current->internal_i2c.hw_port)) - { - const auto sda = lgfx::i2c::getPinSDA(current->internal_i2c.hw_port); - const auto scl = lgfx::i2c::getPinSCL(current->internal_i2c.hw_port); - if (sda.has_value() && scl.has_value() - && (sda.value() != current->internal_i2c.sda || scl.value() != current->internal_i2c.scl)) - { - ESP_LOGW("board_detect", "I2C%d was open on SDA=%d SCL=%d; moving it to SDA=%d SCL=%d", - current->internal_i2c.hw_port, sda.value(), scl.value(), - current->internal_i2c.sda, current->internal_i2c.scl); - } - } - if (current->internal_i2c.hw_port >= 0 - && !lgfx::i2c::init(current->internal_i2c.hw_port, current->internal_i2c.sda, - current->internal_i2c.scl).has_value()) - { - ESP_LOGW("board_detect", "I2C%d could not be opened for SDA=%d SCL=%d", - current->internal_i2c.hw_port, current->internal_i2c.sda, current->internal_i2c.scl); - } + // Detection uses software I2C only. Like SPI, hardware I2C belongs to + // its consumers; preparation leaves only power and reset ready. return true; } diff --git a/src/board_detect/transaction.inl b/src/board_detect/transaction.inl index db203a14..15263293 100644 --- a/src/board_detect/transaction.inl +++ b/src/board_detect/transaction.inl @@ -91,29 +91,6 @@ } } - void detection_bus_journal_t::opened_i2c(int port) - { - if (port < 0) { return; } - for (std::size_t index = 0; index < i2c_count_; ++index) - { - if (i2c_ports_[index] == port) { return; } - } - if (i2c_count_ < sizeof(i2c_ports_) / sizeof(i2c_ports_[0])) - { - i2c_ports_[i2c_count_++] = static_cast(port); - } - } - - void detection_bus_journal_t::rollback() - { - while (i2c_count_ != 0) { lgfx::i2c::release(i2c_ports_[--i2c_count_]); } - } - - void detection_bus_journal_t::commit() - { - i2c_count_ = 0; - } - detection_transaction_t::detection_transaction_t( pin_list_t unconditional, pin_list_t conditional, bool include_conditional) : valid_ { gpio_.capture(unconditional, conditional, include_conditional) } @@ -141,13 +118,11 @@ void detection_transaction_t::rollback() { if (!valid_ || committed_) { return; } - buses_.rollback(); gpio_.rollback(); } void detection_transaction_t::commit() { if (!valid_ || committed_) { return; } - buses_.commit(); committed_ = true; } diff --git a/test/board_detect/README.md b/test/board_detect/README.md index 8d05b244..3ec51243 100644 --- a/test/board_detect/README.md +++ b/test/board_detect/README.md @@ -29,9 +29,11 @@ required. `BoardDetect.yml` runs the same command independently of catalog CI. | `integration_s3.cpp` | S3 runner conditional GPIO capture and probe flag propagation, plus fixed-start OPI ownership refusal | | `spi_preamble.cpp` | Real soft-SPI zero command, clock count and delay before asserting CS, followed by the actual ID read | | `package_module.cpp`, `package_c5.cpp` | Actual C5/C6 eFuse-only candidates, generated protection edges, previous weak evidence, no GPIO/prepare/construct/persist | -| `package_p4.cpp` | Actual P4 detector list, absent evidence, Tab5/Tab5X revision candidates and retry policy; fixed StampP4 displayless acceptance and detector exclusion | +| `package_p4.cpp` | Actual P4 detector list, absent evidence, Tab5/Tab5X revision candidates and retry policy; post-power touch polling limits/timing and caller hardware-I2C ownership; fixed StampP4 displayless acceptance and detector exclusion | | `description_*.cpp`, chip tests, `fixtures/` | Complete production descriptors, PMIC device/operation lists, register masks, GPIO scopes, board registrations, constructor identity, GPIO power-hold agreement with generated catalog output | | `construction_esp32.cpp` | Complete production setup helpers and constructors; fixed Tough PMIC backlight, panel variant/reset register, touch identity, bus frequencies and display-part ownership | +| `construction_c61.cpp` | CoreMatrix ACK success/failure and unavailable I2C; the temporary hardware port is closed before either successful construction or rejection | +| `construction_p4.cpp` | Tab5/Tab5X post-power software-I2C observations through real construction: ST firmware 1/3/unknown, GT911, silent touch with DSI ID fallback, retained power failures, DSI lane rate and display-part ownership | | `ops_host.cpp` | Typed operation validation/execution, retry, dedicated-release summaries and I2C bus recovery policy | | `detect_class_host.cpp`, `detect_session_host.cpp` | Detect-class matching and session verdict/preference/candidate/persistence rules | diff --git a/test/board_detect/construction_c61.cpp b/test/board_detect/construction_c61.cpp new file mode 100644 index 00000000..a2b8d6c6 --- /dev/null +++ b/test/board_detect/construction_c61.cpp @@ -0,0 +1,47 @@ +#define CONFIG_IDF_TARGET_ESP32C61 1 +#define CONFIG_IDF_TARGET "esp32c61" +#include +#include +#include "support/runtime.hpp" +#include "support/probes.hpp" +#include "board_detect/m5/esp32c61.inl" +#include "support/display_devices.hpp" +namespace lgfx { struct Panel_TM1680 : Panel_Device {}; } +namespace m5gfx { +static const char LIBRARY_NAME[] = "host"; +#include "board_detect/m5/esp32c61/corematrix_setup.inl" +} +using namespace m5gfx::board_detect; +using namespace m5gfx::board_detect::m5; +int main() { + unsigned cases = 0; + for (bool opened : {false, true}) + for (bool ack : {false, true}) + for (bool accepts_parts : {false, true}) { + host_hardware::reset(); + host_hardware::i2c_open_ok = opened; + unsigned probes = 0; + host_hardware::wire = [&](int addr, int reg, std::uint8_t *, std::size_t len, bool write) { + ++probes; + assert(addr == 0x72 && reg == -1 && len == 0 && !write); + assert(lgfx::i2c::isInitialized(0) == opened); + if (opened) assert(host_hardware::ports.at(0) == std::make_pair(0, 1)); + return opened && ack; + }; + board_result_t result; + result.assign(&desc_corematrix); + display_parts_t parts; + const auto status = construct_corematrix(result, accepts_parts ? &parts : nullptr); + assert(status == (accepts_parts ? construct_status_t::ok : construct_status_t::failed)); + assert(probes == 1 && host_hardware::ports.empty()); + if (accepts_parts) { + assert(parts.bus && parts.panel); + assert(parts.bus->config().i2c_port == 0); + assert(parts.bus->config().pin_sda == 0 && parts.bus->config().pin_scl == 1); + destroy_display_parts(&parts); + } + assert(host_hardware::ports.empty()); + ++cases; + } + std::printf("C61 construction/I2C ownership: %u cases passed\n", cases); +} diff --git a/test/board_detect/construction_p4.cpp b/test/board_detect/construction_p4.cpp new file mode 100644 index 00000000..f740f630 --- /dev/null +++ b/test/board_detect/construction_p4.cpp @@ -0,0 +1,97 @@ +#define CONFIG_IDF_TARGET_ESP32P4 1 +#define CONFIG_IDF_TARGET "esp32p4" +#define CONFIG_SPIRAM 1 +#define CONFIG_SPIRAM_SPEED 200 +#include +#include +#include "support/runtime.hpp" +#include "support/probes.hpp" +#include "support/dsi_devices.hpp" +#include "board_detect/m5/esp32p4.inl" +namespace m5gfx { +static const char LIBRARY_NAME[] = "host"; +#include "board_detect/m5/esp32p4/tab5_setup.inl" +namespace board_detect { +namespace m5 { +construct_status_t construct_corep4x(const board_result_t &, display_parts_t *) { std::abort(); } +} +} +} +using namespace m5gfx::board_detect; +using namespace m5gfx::board_detect::m5; +int main() { + struct scenario_t { int fw; bool gt911, dsi; int panel; unsigned waits, id_reads; }; + const scenario_t scenarios[] = { + {1, false, false, 2, 0, 0}, + {3, false, false, 3, 0, 0}, + {7, true, true, 0, 600, 0}, // An unknown but answering ST must not become GT911/ILI9881C. + {-1, true, false, 1, 0, 0}, + {-1, false, true, 1, 680, 3}, + {-1, false, false, 0, 680, 9}, + }; + unsigned cases = 0; + for (const auto *desc : {&desc_tab5, &desc_tab5x}) + for (bool fixed : {false, true}) + for (bool power_failure : {false, true}) + for (const auto &scenario : scenarios) { + host_hardware::reset(); + host_probes::reset(); + host_dsi::reset(scenario.dsi); + host_chip::revision = desc == &desc_tab5x ? 300 : 0; + unsigned touch_reads = 0; + host_hardware::wire = [&](int addr, int reg, std::uint8_t *data, + std::size_t len, bool write) { + if (addr == 0x43 || addr == 0x44) { + if (!write && len) *data = 0xA0; + return !(power_failure && write && addr == 0x43 && reg == 5 && *data == 0x76); + } + assert(host_hardware::ports.at(-1) == std::make_pair(31, 32)); + if (addr == 0x55) { + ++touch_reads; + if (scenario.fw < 0) return false; + *data = scenario.fw; + return true; + } + assert(addr == 0x14 && reg == -1); + return scenario.gt911; + }; + const std::int8_t watched[] = {31, 32, 23}; + detection_transaction_t transaction(pins(watched), no_pins(), false); + probe_ctx_t ctx; + ctx.transaction = &transaction; + board_result_t result; + result.option = 0xA5; + if (fixed) { + result.assign(desc); + const auto *entry = find_board(esp32p4_boards, desc->def.id); + assert(entry->fixed_start(result, ctx)); + } else { + assert(tab5_family_detector.confirm(ctx, &result)); + assert(prepare(*result.desc, result, ctx)); + } + assert(bool(result.prepared & prepared_power_failed) == power_failure); + assert(result.observe_on_power_failure && touch_reads > 0); + assert(host_hardware::ports.empty()); + const auto reads_before_construction = touch_reads; + display_parts_t parts; + const auto status = construct_tab5(result, &parts); + assert(status == (scenario.panel ? construct_status_t::ok : construct_status_t::no_display)); + assert(touch_reads == reads_before_construction && result.option == 0xA5); + assert(host_dsi::id_reads == scenario.id_reads); + assert(host_hardware::now_us == ((power_failure ? 10 : 110) + scenario.waits) * 1000); + if (scenario.panel) { + assert(parts.panel && parts.bus && parts.touch && parts.light); + assert(parts.panel->kind() == scenario.panel); + assert(parts.touch->kind() == (scenario.panel == 1 ? 3 : 4)); + const auto cfg = static_cast(parts.bus)->config(); + assert(cfg.lane_mbps == (scenario.panel == 2 ? 900 : 1040)); + assert(parts.touch->config().i2c_port == 1); + assert(parts.touch->config().pin_sda == 31 && parts.touch->config().pin_scl == 32); + destroy_display_parts(&parts); + } else { + assert(!parts.bus && !parts.panel && !parts.touch && !parts.light); + } + ++cases; + } + std::printf("P4 construction/observation: %u cases passed\n", cases); +} diff --git a/test/board_detect/fixtures/p4.txt b/test/board_detect/fixtures/p4.txt index 5e4c7292..213bfc47 100644 --- a/test/board_detect/fixtures/p4.txt +++ b/test/board_detect/fixtures/p4.txt @@ -17,7 +17,7 @@ reset 0 -1 0 0 0 0 sd -1 -1 -1 -1 -1 display -1 -1 -1 -1 -1 -1 -1 i2c 31 32 1 hold [] ops [23,] options[] -entry construct_tab5 board_M5Tab5 0 0 +entry construct_tab5 board_M5Tab5 0 1 35 M5Tab5X 0 power -1 1 100000 0 0 0 1 device 100000 0 67 0 1 0 @@ -27,7 +27,7 @@ reset 0 -1 0 0 0 0 sd -1 -1 -1 -1 -1 display -1 -1 -1 -1 -1 -1 -1 i2c 31 32 1 hold [] ops [23,] options[] -entry construct_tab5 board_M5Tab5X 0 0 +entry construct_tab5 board_M5Tab5X 0 1 148 M5UnitPoEP4 0 power -1 1 0 0 0 0 0 reset 0 -1 0 0 0 0 diff --git a/test/board_detect/integration.cpp b/test/board_detect/integration.cpp index c88f3a9c..d54b637a 100644 --- a/test/board_detect/integration.cpp +++ b/test/board_detect/integration.cpp @@ -39,7 +39,7 @@ static const board_desc_t member = {{60000, "test member", 0}, no_shared_sd(), no_display_pins(), pins(hold), - no_internal_i2c(), + internal_i2c(8, 9, 1), no_options(), pins(hold)}; static const board_desc_t other = {{60001, "other member", 0}, @@ -420,12 +420,41 @@ static void reset_faults() { assert(host_hardware::ports.empty()); ++cases; } +// Detection must neither remap nor release a hardware port owned by the caller, +// including when construction or adoption rolls the GPIO transaction back. +static void caller_i2c_ownership() { + for (bool fixed : {false, true}) + for (int failure : {0, 1, 2}) { + reset(); + lgfx::i2c::init(1, 10, 11); + const auto caller_ports = host_hardware::ports; + host_hardware::events.clear(); + construct_ok = failure != 1; + adopt_ok = failure != 2; + detector_t detector; + const board_detector_t *list[] = {&detector, nullptr}; + const auto adopt = [](m5gfx::board_detect::m5::display_parts_t &, lgfx::board_t) { + return adopt_ok; + }; + detect_request_t req; + req.max_attempts = 1; + const auto out = fixed + ? m5gfx::run_fixed_detection(entries[0], false, adopt) + : m5gfx::run_detection_attempt(list, req, true, adopt); + assert(out.setup_succeeded == (failure == 0)); + assert(host_hardware::ports == caller_ports); + for (const auto &event : host_hardware::events) + assert(event.kind != 6 && event.kind != 7); + ++cases; + } +} int main() { attempts(); fixed_startup(); identified_stop(); prepare_states(); reset_faults(); + caller_i2c_ownership(); std::printf("Integration/fixed/prepare: %u cases passed\n", cases); return 0; } diff --git a/test/board_detect/package_p4.cpp b/test/board_detect/package_p4.cpp index 08e5805a..1ae3e1d0 100644 --- a/test/board_detect/package_p4.cpp +++ b/test/board_detect/package_p4.cpp @@ -1,9 +1,11 @@ #define CONFIG_IDF_TARGET_ESP32P4 1 +#define CONFIG_SPIRAM 1 #define CONFIG_IDF_TARGET "esp32p4" #include #include #include "support/runtime.hpp" #include "support/probes.hpp" +#include "support/dsi_devices.hpp" #include "board_detect/m5/esp32p4.inl" #include "support/display_backend.hpp" #include "support/snapshot.hpp" @@ -34,9 +36,101 @@ using lgfx::board_t; } using namespace m5gfx::board_detect; using namespace m5gfx::board_detect::m5; +static unsigned tab5_observations() { + struct scenario_t { + int version; // -1 means no ST response; 7 is an unknown version. + unsigned ready; // Number of unsuccessful or unknown first samples. + bool gt911; + std::uint32_t flags; + unsigned samples, waits; + }; + const scenario_t scenarios[] = { + {1, 0, false, tab5_detail::read_st_touch_fw | tab5_detail::touch_st7121, 1, 0}, + {3, 0, false, tab5_detail::read_st_touch_fw | tab5_detail::touch_st7123, 1, 0}, + {1, 3, false, tab5_detail::read_st_touch_fw | tab5_detail::touch_st7121, 4, 3}, + {7, 0, true, tab5_detail::read_st_touch_fw, 60, 60}, + {-1, 0, false, 0, 60, 60}, + {-1, 0, true, tab5_detail::touch_gt911, 1, 0}, + {-1, 1, true, tab5_detail::read_st_touch_fw | tab5_detail::touch_gt911, 2, 1}, + {3, 1, true, tab5_detail::read_st_touch_fw | tab5_detail::touch_st7123, 2, 1}, + }; + unsigned cases = 0; + for (const auto *desc : {&desc_tab5, &desc_tab5x}) + for (bool fixed : {false, true}) + for (bool already_open : {false, true}) + for (const auto &scenario : scenarios) { + host_hardware::reset(); + host_probes::reset(); + host_chip::revision = desc == &desc_tab5x ? 300 : 0; + if (already_open) lgfx::i2c::init(1, 7, 8); + const auto caller_ports = host_hardware::ports; + unsigned samples = 0, gt911_probes = 0, writes = 0; + host_hardware::wire = [&](int addr, int reg, std::uint8_t *data, + std::size_t len, bool write) { + if (addr == 0x43 || addr == 0x44) { + if (write) ++writes; + else if (len) *data = 0xA0; + return true; + } + assert(host_hardware::ports.count(-1) == 1); + assert(host_hardware::ports.at(-1) == std::make_pair(31, 32)); + assert(writes > 0 && host_hardware::now_us >= 110000); + assert(host_hardware::gpio[23].high); + if (addr == 0x55) { + assert(reg == 0 && len == 1 && !write); + ++samples; + if (samples <= scenario.ready) { + if (scenario.ready == 3) return false; + *data = 7; + return true; + } + if (scenario.version < 0) return false; + *data = scenario.version; + return true; + } + assert(addr == 0x14 && reg == -1 && len == 0 && !write); + ++gt911_probes; + return scenario.gt911; + }; + const std::int8_t watched[] = {31, 32, 23}; + detection_transaction_t transaction(pins(watched), no_pins(), false); + probe_ctx_t ctx; + ctx.transaction = &transaction; + ctx.i2c_port_probe = -1; + board_result_t result; + result.option = 0xA5; + if (fixed) { + result.assign(desc); + const auto *entry = find_board(esp32p4_boards, desc->def.id); + assert(entry && entry->fixed_start == tab5_detail::fixed_start); + assert(entry->fixed_start(result, ctx)); + } else { + assert(tab5_family_detector.confirm(ctx, &result)); + assert(result.observe_after_power == tab5_detail::observe_touch); + assert(prepare(*result.desc, result, ctx)); + } + assert(result.desc == desc && result.option == 0xA5); + assert((result.prepared & 0xFFFF0000u) == scenario.flags); + assert(result.prepared & prepared_observation); + assert(samples == scenario.samples); + const auto expected_gt911 = scenario.version < 0 + ? (scenario.gt911 ? 1u : 60u) + : (scenario.ready == 3 ? 3u : 0u); + assert(gt911_probes == expected_gt911); + assert(host_hardware::now_us == 110000 + 10000 * scenario.waits); + assert(host_hardware::ports == caller_ports); + const auto before = host_hardware::now_us; + assert(prepare(*desc, result, ctx)); + assert(host_hardware::now_us == before && samples == scenario.samples); + transaction.rollback(); + assert(host_hardware::ports == caller_ports); + ++cases; + } + return cases; +} int main() { host_snapshot::check("p4", esp32p4_boards); - unsigned cases = 0; + unsigned cases = tab5_observations(); for (bool weak : {false, true}) for (int revision : {0, 300}) { host_hardware::reset(); diff --git a/test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp b/test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp index 54c2fa87..10058268 100644 --- a/test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp +++ b/test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp @@ -15,6 +15,7 @@ enum { SPI2_HOST = 1, SPI3_HOST = 2, GPIO_NUM_MAX = 64, + GPIO_NUM_NC = -1, GPIO_NUM_0 = 0, GPIO_NUM_1 = 1, GPIO_NUM_2 = 2, diff --git a/test/board_detect/support/display_devices.hpp b/test/board_detect/support/display_devices.hpp index 2946ce7a..6598bd51 100644 --- a/test/board_detect/support/display_devices.hpp +++ b/test/board_detect/support/display_devices.hpp @@ -43,6 +43,7 @@ struct Touch_FT5x06 : ITouch { int kind() const override { return 2; } }; struct Touch_GT911 : ITouch { + static constexpr std::uint8_t default_addr_1 = 0x14; int kind() const override { return 3; } }; struct Panel_Device : configured_t { @@ -54,6 +55,7 @@ struct Panel_Device : configured_t { void setRotation(int) {} void bus(IBus *b) { attached = b; } void touch(ITouch *t) { attached_touch = t; } + void setTouch(ITouch *t) { touch(t); } void setCalibrateAffine(float *) {} void setResetReg(int reg) { reset_reg = reg; } }; diff --git a/test/board_detect/support/dsi_devices.hpp b/test/board_detect/support/dsi_devices.hpp new file mode 100644 index 00000000..2f685a52 --- /dev/null +++ b/test/board_detect/support/dsi_devices.hpp @@ -0,0 +1,48 @@ +// DSI transport/panel boundary; production setup descriptors and owners stay real. +#pragma once +#include "display_devices.hpp" +namespace host_dsi { +static bool ili9881; +static unsigned id_reads, page_writes; +inline void reset(bool answers) { + ili9881 = answers; + id_reads = page_writes = 0; +} +} +namespace lgfx { +struct Bus_DSI : IBus { + struct config_t { + int bus_id = 0, lane_num = 0, lane_mbps = 0, ldo_chan_id = 0, ldo_voltage_mv = 0; + }; + config_t cfg; + config_t config() const { return cfg; } + void config(const config_t &value) { cfg = value; } + bool init() { return true; } + void writeParams(std::uint8_t reg, const std::uint8_t *, std::size_t) { + assert(reg == 0xFF); + ++host_dsi::page_writes; + } + void readParams(std::uint8_t reg, std::uint8_t *data, std::size_t len) { + assert(reg <= 2 && len == 1); + ++host_dsi::id_reads; + *data = host_dsi::ili9881 ? (reg == 0 ? 0x98 : reg == 1 ? 0x81 : 0) : 0; + } +}; +struct DsiPanel : Panel_Device { + struct detail_t { + int dpi_freq_mhz = 0, hsync_back_porch = 0, hsync_pulse_width = 0, + hsync_front_porch = 0, vsync_back_porch = 0, vsync_pulse_width = 0, + vsync_front_porch = 0; + }; + detail_t detail; + detail_t config_detail() const { return detail; } + void config_detail(const detail_t &value) { detail = value; } +}; +struct Panel_ILI9881C : DsiPanel { int kind() const override { return 1; } }; +struct Panel_ST7121 : DsiPanel { int kind() const override { return 2; } }; +struct Panel_ST7123 : DsiPanel { int kind() const override { return 3; } }; +struct Touch_ST7123 : ITouch { + static constexpr std::uint8_t default_addr = 0x55; + int kind() const override { return 4; } +}; +} From dcc0b816609f811f6380907d7926e1c3b23bf17f Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Mon, 19 Oct 2026 11:25:12 +0000 Subject: [PATCH 2/2] Route board detection through an inline I/O boundary Keep driver and platform dependencies in a replaceable implementation so detection can be reused independently. Add compile-time selection for pin, timing, I2C and chip-data operations without exposing driver types. Leave identification, retry policies and dedicated-GPIO aggregation with the detectors. Preserve timing-sensitive operation sequences and replace SDK host stubs with independent I/O simulation plus a native adapter for construction fixtures. --- src/board_detect/board_detect.hpp | 8 +- src/board_detect/board_detect.inl | 108 ----- src/board_detect/detect_io.hpp | 137 ++++++ src/board_detect/detect_io_default.inl | 437 ++++++++++++++++++ src/board_detect/detection_package.inl | 18 +- src/board_detect/m5/atom_touch.inl | 106 +---- src/board_detect/m5/board_registry.inl | 18 +- src/board_detect/m5/esp32_d0wdq6.inl | 71 ++- src/board_detect/m5/esp32_pico.inl | 36 +- src/board_detect/m5/esp32c3.inl | 15 +- src/board_detect/m5/esp32c5/toughc5.inl | 13 +- src/board_detect/m5/esp32c6/c6_display.inl | 2 +- .../m5/esp32c6/c6_displayless.inl | 2 +- src/board_detect/m5/esp32c61/corematrix.inl | 2 +- src/board_detect/m5/esp32p4.inl | 1 - src/board_detect/m5/esp32p4/corep4x.inl | 2 +- src/board_detect/m5/esp32p4/tab5.inl | 21 +- src/board_detect/m5/esp32p4/unitpoep4.inl | 16 +- src/board_detect/m5/esp32s3/cores3.inl | 44 +- src/board_detect/m5/esp32s3/families.inl | 67 ++- src/board_detect/ops.inl | 70 ++- src/board_detect/probe_hardware.inl | 293 ++++++------ src/board_detect/startup.inl | 75 ++- src/board_detect/transaction.inl | 17 +- test/board_detect/README.md | 22 +- test/board_detect/run.sh | 6 +- .../stubs/board_detect/m5/atom_touch.inl | 16 - test/board_detect/stubs/driver/gpio.h | 2 - test/board_detect/stubs/esp_attr.h | 2 - test/board_detect/stubs/esp_chip_info.h | 8 - test/board_detect/stubs/esp_cpu.h | 2 - test/board_detect/stubs/esp_log.h | 2 - test/board_detect/stubs/esp_rom_sys.h | 3 - test/board_detect/stubs/esp_timer.h | 2 - .../stubs/lgfx/v1/platforms/esp32/common.hpp | 278 ----------- test/board_detect/stubs/soc/gpio_reg.h | 2 - test/board_detect/stubs/soc/gpio_struct.h | 2 - test/board_detect/stubs/soc/io_mux_reg.h | 3 - test/board_detect/stubs/soc/rtc.h | 2 - test/board_detect/stubs/soc/rtc_cntl_reg.h | 2 - test/board_detect/stubs/soc/rtc_io_reg.h | 2 - test/board_detect/stubs/soc/sens_reg.h | 2 - .../stubs/soc/usb_serial_jtag_reg.h | 1 - test/board_detect/support/detect_io.hpp | 219 +++++++++ .../support/native_devices_io.hpp | 123 +++++ .../support/package_selection.hpp | 6 +- test/board_detect/support/pin_coverage.hpp | 6 +- test/board_detect/support/probes.hpp | 6 - 48 files changed, 1304 insertions(+), 994 deletions(-) create mode 100644 src/board_detect/detect_io.hpp create mode 100644 src/board_detect/detect_io_default.inl delete mode 100644 test/board_detect/stubs/board_detect/m5/atom_touch.inl delete mode 100644 test/board_detect/stubs/driver/gpio.h delete mode 100644 test/board_detect/stubs/esp_attr.h delete mode 100644 test/board_detect/stubs/esp_chip_info.h delete mode 100644 test/board_detect/stubs/esp_cpu.h delete mode 100644 test/board_detect/stubs/esp_log.h delete mode 100644 test/board_detect/stubs/esp_rom_sys.h delete mode 100644 test/board_detect/stubs/esp_timer.h delete mode 100644 test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp delete mode 100644 test/board_detect/stubs/soc/gpio_reg.h delete mode 100644 test/board_detect/stubs/soc/gpio_struct.h delete mode 100644 test/board_detect/stubs/soc/io_mux_reg.h delete mode 100644 test/board_detect/stubs/soc/rtc.h delete mode 100644 test/board_detect/stubs/soc/rtc_cntl_reg.h delete mode 100644 test/board_detect/stubs/soc/rtc_io_reg.h delete mode 100644 test/board_detect/stubs/soc/sens_reg.h delete mode 100644 test/board_detect/stubs/soc/usb_serial_jtag_reg.h create mode 100644 test/board_detect/support/detect_io.hpp create mode 100644 test/board_detect/support/native_devices_io.hpp diff --git a/src/board_detect/board_detect.hpp b/src/board_detect/board_detect.hpp index 7a28bf2d..fccfc335 100644 --- a/src/board_detect/board_detect.hpp +++ b/src/board_detect/board_detect.hpp @@ -6,11 +6,7 @@ #include #include -#if defined (M5GFX_BOARD_DETECT_HOST_HAL) -#include M5GFX_BOARD_DETECT_HOST_HAL -#else -#include "../lgfx/v1/platforms/esp32/common.hpp" -#endif +#include "detect_io.hpp" #include "dedicated_release_probe.hpp" #include "ops.hpp" #include "detect_types.hpp" @@ -92,7 +88,7 @@ namespace board_detect void rollback(); private: - lgfx::gpio::pin_backup_t saved_[max_detection_pins]; + io::pin_state_t saved_[max_detection_pins]; std::uint8_t count_ = 0; }; diff --git a/src/board_detect/board_detect.inl b/src/board_detect/board_detect.inl index 255274d8..53d8e674 100644 --- a/src/board_detect/board_detect.inl +++ b/src/board_detect/board_detect.inl @@ -10,119 +10,11 @@ #include #include -#include -#include -#include -#if defined (CONFIG_IDF_TARGET_ESP32S3) \ - && __has_include() \ - && (__has_include() || __has_include()) \ - && (__has_include() || __has_include()) - #define M5GFX_HAS_S3_DEDICATED_GPIO_RELEASE_PROBE - #include - #include - #include - #if __has_include() - #include - #else - #include - #endif - #include - #if __has_include() - #include - #else - // IDF 4.4 exposes the same S3 instructions through cpu_ll.h and reports - // allocated channels as masks rather than offsets. - #define M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL - #include - #endif - #include - #include - #include - #include - #include -#endif namespace m5gfx { namespace board_detect { -#if defined (M5GFX_HAS_S3_DEDICATED_GPIO_RELEASE_PROBE) - static inline std::uint32_t dedicated_release_read_in() - { -#if defined (M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL) - return cpu_ll_read_dedic_gpio_in(); -#else - return dedic_gpio_cpu_ll_read_in(); -#endif - } - - static inline void dedicated_release_write_mask(std::uint32_t mask, std::uint32_t value) - { -#if defined (M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL) - cpu_ll_write_dedic_gpio_mask(mask, value); -#else - dedic_gpio_cpu_ll_write_mask(mask, value); -#endif - } - - static void dedicated_release_channel_masks( - dedic_gpio_bundle_handle_t bundle, std::uint8_t pin_count, - std::uint32_t* in_mask, std::uint32_t* out_mask, - esp_err_t* in_error, esp_err_t* out_error) - { -#if defined (M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL) - *in_error = dedic_gpio_get_in_mask(bundle, in_mask); - *out_error = dedic_gpio_get_out_mask(bundle, out_mask); -#else - std::uint32_t in_offset = 0; - std::uint32_t out_offset = 0; - *in_error = dedic_gpio_get_in_offset(bundle, &in_offset); - *out_error = dedic_gpio_get_out_offset(bundle, &out_offset); - const std::uint32_t channel_mask = (1u << pin_count) - 1u; - *in_mask = channel_mask << in_offset; - *out_mask = channel_mask << out_offset; -#endif - } - - static inline int dedicated_release_core_id() - { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - return esp_cpu_get_core_id(); -#else - return xPortGetCoreID(); -#endif - } - - static inline std::uint32_t dedicated_release_cycle_count() - { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - return esp_cpu_get_cycle_count(); -#else - return esp_cpu_get_ccount(); -#endif - } - - static inline int dedicated_release_task_core() - { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) - return xTaskGetCoreID(nullptr); -#else - return xTaskGetAffinity(nullptr); -#endif - } - - static bool delete_dedicated_release_bundle(dedic_gpio_bundle_handle_t bundle) - { - const esp_err_t error = dedic_gpio_del_bundle(bundle); - if (error != ESP_OK) - { - ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO bundle cleanup failed (%d)", int(error)); - return false; - } - return true; - } -#endif - // Call after family-specific evidence gates; a hint is eligible only while // that member remains possible. Defaults may be absent for open families. // Only some chips' detectors use it. diff --git a/src/board_detect/detect_io.hpp b/src/board_detect/detect_io.hpp new file mode 100644 index 00000000..11804b51 --- /dev/null +++ b/src/board_detect/detect_io.hpp @@ -0,0 +1,137 @@ +// Copyright (c) M5Stack. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma once + +#include +#include + +namespace m5gfx { namespace board_detect { namespace io { + struct dedicated_gpio_handle_t { void* value = nullptr; }; + // Values are independent of the backend's public types. + enum class pin_mode_t : std::uint8_t { output, input, input_pullup, input_pulldown }; +}}} + +/* + * Replacement contract: M5GFX_BOARD_DETECT_IO_HEADER supplies the following in + * m5gfx::board_detect::io. Use inline implementations and no public SDK types. + * The replacement header must make ESP_LOGx macros visible to detection code. + * The caller owns board choices, thresholds, retries, sampling and rollback order. + * Chip-specific entries are required only where their detection path is compiled. + * + * Constants (constexpr): + * int i2c_port_0; // First hardware I2C controller ID. + * int i2c_port_1; // Second controller ID, when available. + * int low_power_i2c_port_0; // LP controller ID, or first HP fallback. + * int pin_count; // Exclusive upper bound of GPIO numbers. + * unsigned package_classic_q6; // Classic ESP32 Q6 package code. + * unsigned package_classic_pico_d4; // Classic ESP32 PICO-D4 package code. + * bool dedicated_gpio_supported; // Whether the S3 release probe can run. + * + * Types: + * pin_mode_t is defined above: output, input, input_pullup, input_pulldown. + * pin_state_t is default-constructible, with backend-private saved state: + * void save(int pin); // Capture this pin's configuration. + * bool matches_current() const; // Compare captured and current state. + * void restore(); // Restore the captured configuration. + * int pin() const; // Return the captured pin number. + * critical_section_t is default-constructible and initially unlocked: + * void enter(); // Enter a critical interval. + * void exit(); // Leave it; pairing belongs to the caller. + * dedicated_gpio_handle_t is defined above, with void* value = nullptr; + * value holds an opaque backend handle used by open/close/channel_masks. + * + * GPIO: + * void pin_mode(int pin, pin_mode_t mode); // Configure direction and pulls. + * void pin_high(int pin); // Set the output latch. + * void pin_low(int pin); // Clear the output latch. + * bool pin_read(int pin); // Read the raw input level. + * bool pin_valid(int pin); // Test physical GPIO validity only. + * bool pin_output_level(int pin, bool* level); + * // Write the output latch to *level; return whether output is enabled. + * void release_lp_pad(int pin); // Release stale LP pad routing. + * volatile std::uint32_t* pin_low_register(int pin); + * // Return the GPIO clear register; caller supplies the bit mask and pulses. + * volatile std::uint32_t* pin_high_register(int pin); + * // Return the GPIO set register; caller supplies the bit mask and pulses. + * + * Time: + * std::uint32_t cpu_cycles(); // Read the wrapping CPU cycle counter. + * std::uint32_t cpu_hz(); // Return the current CPU clock in Hz. + * std::uint32_t millis(); // Read the wrapping millisecond clock. + * std::uint32_t micros(); // Read the wrapping microsecond clock. + * void delay_ms(std::uint32_t ms); // Delay in milliseconds. + * void delay_us(std::uint32_t us); // Delay in microseconds. + * void delay_rom_us(std::uint32_t us); // Use the ROM microsecond delay primitive. + * + * I2C (bool means success; one operation per call, with no policy retries): + * bool i2c_open(int port, int sda, int scl); // Initialize the selected controller. + * void i2c_close(int port); // Release the selected controller. + * bool i2c_ack(int port, std::uint16_t addr, std::uint32_t freq, + * bool end_on_failure = false); + * // Begin a write and end it; true requires both to succeed. On failed begin, + * // end only when end_on_failure is true, preserving the caller's policy. + * bool i2c_read_reg8(int port, std::uint16_t addr, std::uint8_t reg, + * std::uint8_t* value, std::uint32_t freq); + * // Read one register byte; leave *value unchanged on failure. + * bool i2c_write_reg8(int port, std::uint16_t addr, std::uint8_t reg, + * std::uint8_t value, std::uint32_t freq); + * // Write one full register byte without a read/modify/write mask. + * bool i2c_write(int port, std::uint16_t addr, const std::uint8_t* data, + * std::size_t len, std::uint32_t freq); + * // Perform one write transaction; address interpretation belongs to caller. + * bool i2c_write_read(int port, std::uint16_t addr, const std::uint8_t* wdata, + * std::size_t wlen, std::uint8_t* rdata, std::size_t rlen, + * std::uint32_t freq); + * // Perform one combined write/read transaction. + * + * Chip information: + * unsigned package_version(); // Read the raw package code. + * bool psram_ready(); // Read S3 OPI PSRAM initialization state. + * unsigned chip_revision(); // Read P4 revision; caller sets cutoffs. + * unsigned flash_capacity_code(); // Read the C6/C5 flash capacity field. + * void module_memory_capacity(unsigned* flash, unsigned* psram); + * // Read C5 flash/PSRAM fields together; caller maps codes to module types. + * std::uint8_t st_touch_address(); // P4: ST7123 driver's default address. + * std::uint8_t gt911_touch_address(); // P4: GT911 driver's first default address. + * unsigned usb_serial_jtag_frame(); // C3: read raw USB SOF frame index. + * bool uart_rx_held_high(); // C3: pulldown/read UART RX, restoring mux. + * + * S3 dedicated GPIO (also provide compilable stubs when unsupported): + * std::uint32_t dedicated_release_read_in(); // Read raw dedicated input bits. + * void dedicated_release_write_mask(std::uint32_t mask, std::uint32_t value); + * // Write selected dedicated output bits. + * void dedicated_release_channel_masks(dedicated_gpio_handle_t handle, + * std::uint8_t channel_count, std::uint32_t* in_mask, std::uint32_t* out_mask, + * int* in_error, int* out_error); + * // Return bundle channel masks and native status codes; caller validates them. + * int dedicated_release_core_id(); // Read the current CPU core ID. + * std::uint32_t dedicated_release_cycle_count(); // Read its CPU cycle counter. + * int dedicated_release_task_core(); // Read the task's core affinity. + * int no_core_affinity(); // Return the unpinned-affinity sentinel. + * bool dedicated_gpio_in_use(); // Read the dedicated peripheral clock bit. + * void pin_weak_pullup_open_drain(int pin); + * // Configure weak pullup, minimum drive and open drain; no sample decisions. + * int dedicated_gpio_open(const int* pins, std::uint8_t count, + * dedicated_gpio_handle_t* handle); + * // Allocate an input/output bundle; return zero on success, native error else. + * int dedicated_gpio_close(dedicated_gpio_handle_t handle); + * // Release the bundle; return zero on success, native error else. + * + * Classic ESP32 transfers (compile-time wiring and limits come from the caller): + * template + * bool classic_touch_measure(std::uint16_t* t4, std::uint16_t* t7); + * // channel_a/b select touch channels; pin_a/b are for host event recording + * // and are unused by the default implementation. + * // timeout_us bounds one scan. Restore peripheral state and return completion + * // plus raw T4/T7 counts (zero on timeout); caller applies ratios/thresholds. + * template + * void classic_led_clear(); + * // led_gpio selects DIN, pixels sets the GRB black-pixel count; perform one + * // WS2812 transfer with reset intervals and timing protected from flash stalls. + */ +#if defined (M5GFX_BOARD_DETECT_IO_HEADER) +#include M5GFX_BOARD_DETECT_IO_HEADER +#else +#include "detect_io_default.inl" +#endif diff --git a/src/board_detect/detect_io_default.inl b/src/board_detect/detect_io_default.inl new file mode 100644 index 00000000..fd7309a2 --- /dev/null +++ b/src/board_detect/detect_io_default.inl @@ -0,0 +1,437 @@ +// Copyright (c) M5Stack. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma once + +#include "../lgfx/v1/platforms/esp32/common.hpp" +#include +#include +#include +#include +#if __has_include() +#include +#else +#include +#endif +#include +#include +#if __has_include() +#include +#else +#include +#endif +#include +#include +#include // Detection code needs ESP_LOGx through the I/O header contract. +#if defined (CONFIG_IDF_TARGET_ESP32S3) \ + && __has_include() \ + && (__has_include() || __has_include()) \ + && (__has_include() || __has_include()) + #define M5GFX_HAS_S3_DEDICATED_GPIO_RELEASE_PROBE + #include + #include + #include + #if __has_include() + #include + #else + #include + #endif + #include + #if __has_include() + #include + #else + // IDF 4.4 exposes the same S3 instructions through cpu_ll.h and reports + // allocated channels as masks rather than offsets. + #define M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL + #include + #endif + #include + #include + #include + #include + #include +#endif + +#if defined (CONFIG_IDF_TARGET_ESP32C3) +#include +#endif +#if defined (CONFIG_IDF_TARGET_ESP32P4) +#include +#include "../lgfx/v1/platforms/esp32p4/Touch_ST7123.hpp" +#include "../lgfx/v1/touch/Touch_GT911.hpp" +#endif +#if defined (CONFIG_IDF_TARGET_ESP32S3) && defined (CONFIG_SPIRAM_MODE_OCT) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#include +#else +#include +#endif +#endif +#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +namespace m5gfx { namespace board_detect { namespace io { + static constexpr int i2c_port_0 = I2C_NUM_0; +#if (defined(SOC_HP_I2C_NUM) && SOC_HP_I2C_NUM > 1) || (!defined(SOC_HP_I2C_NUM) && defined(SOC_I2C_NUM) && SOC_I2C_NUM > 1) + static constexpr int i2c_port_1 = I2C_NUM_1; +#endif +#if defined(SOC_LP_I2C_NUM) && SOC_LP_I2C_NUM > 0 && __has_include() \ + && defined(ESP_IDF_VERSION_VAL) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + static constexpr int low_power_i2c_port_0 = LP_I2C_NUM_0; +#else + static constexpr int low_power_i2c_port_0 = I2C_NUM_0; +#endif + __attribute__((always_inline)) inline void pin_mode(int pin, pin_mode_t mode) + { lgfx::pinMode(pin, static_cast(mode)); } + __attribute__((always_inline)) inline void pin_high(int pin) { lgfx::gpio_hi(pin); } + __attribute__((always_inline)) inline void pin_low(int pin) { lgfx::gpio_lo(pin); } + __attribute__((always_inline)) inline bool pin_read(int pin) { return lgfx::gpio_in(pin); } + __attribute__((always_inline)) inline bool pin_valid(int pin) + { return pin >= 0 && pin < GPIO_NUM_MAX && GPIO_IS_VALID_GPIO(pin); } + static constexpr int pin_count = GPIO_NUM_MAX; + __attribute__((always_inline)) inline bool pin_output_level(int pin, bool* level) + { + const auto bit = std::uint32_t(1) << (pin & 31); +#if !defined (SOC_GPIO_PIN_COUNT) || SOC_GPIO_PIN_COUNT > 32 + const bool output = pin & 32 ? REG_READ(GPIO_ENABLE1_REG) & bit : REG_READ(GPIO_ENABLE_REG) & bit; + *level = pin & 32 ? REG_READ(GPIO_OUT1_REG) & bit : REG_READ(GPIO_OUT_REG) & bit; +#else + const bool output = REG_READ(GPIO_ENABLE_REG) & bit; + *level = REG_READ(GPIO_OUT_REG) & bit; +#endif + return output; + } + class pin_state_t + { + public: + __attribute__((always_inline)) void save(int pin) { native_.setPin(pin); native_.backup(); } + __attribute__((always_inline)) bool matches_current() const { return native_.matches_current(); } + __attribute__((always_inline)) void restore() { native_.restore(); } + __attribute__((always_inline)) int pin() const { return native_.getPin(); } + private: + lgfx::gpio::pin_backup_t native_; + }; + __attribute__((always_inline)) inline void release_lp_pad(int pin) { lgfx::gpio::release_lp_pad(pin); } + __attribute__((always_inline)) inline volatile std::uint32_t* pin_low_register(int pin) { return lgfx::get_gpio_lo_reg(pin); } + __attribute__((always_inline)) inline volatile std::uint32_t* pin_high_register(int pin) { return lgfx::get_gpio_hi_reg(pin); } + __attribute__((always_inline)) inline IRAM_ATTR std::uint32_t cpu_cycles() + { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + return esp_cpu_get_cycle_count(); +#else + return esp_cpu_get_ccount(); +#endif + } + + __attribute__((always_inline)) inline std::uint32_t cpu_hz() { return static_cast(esp_clk_cpu_freq()); } + __attribute__((always_inline)) inline std::uint32_t millis() { return lgfx::millis(); } + __attribute__((always_inline)) inline std::uint32_t micros() { return lgfx::micros(); } + __attribute__((always_inline)) inline void delay_ms(std::uint32_t ms) { lgfx::delay(ms); } + __attribute__((always_inline)) inline void delay_us(std::uint32_t us) { lgfx::delayMicroseconds(us); } + __attribute__((always_inline)) inline void delay_rom_us(std::uint32_t us) { esp_rom_delay_us(us); } + + __attribute__((always_inline)) inline bool i2c_open(int port, int sda, int scl) + { return lgfx::i2c::init(port, sda, scl).has_value(); } + __attribute__((always_inline)) inline void i2c_close(int port) { lgfx::i2c::release(port); } + // Some callers historically end even a failed begin; preserve both policies. + __attribute__((always_inline)) inline bool i2c_ack(int port, std::uint16_t addr, std::uint32_t freq, bool end_on_failure = false) + { + const bool began = lgfx::i2c::beginTransaction(port, addr, freq, false).has_value(); + if (!began && !end_on_failure) { return false; } + const bool ended = lgfx::i2c::endTransaction(port).has_value(); + return began && ended; + } + __attribute__((always_inline)) inline bool i2c_read_reg8(int port, std::uint16_t addr, std::uint8_t reg, + std::uint8_t* value, std::uint32_t freq) + { + const auto read = lgfx::i2c::readRegister8(port, addr, reg, freq); + if (!read.has_value()) { return false; } + *value = read.value(); + return true; + } + __attribute__((always_inline)) inline bool i2c_write_reg8(int port, std::uint16_t addr, std::uint8_t reg, + std::uint8_t value, std::uint32_t freq) + { return lgfx::i2c::writeRegister8(port, addr, reg, value, 0, freq).has_value(); } + __attribute__((always_inline)) inline bool i2c_write(int port, std::uint16_t addr, const std::uint8_t* data, + std::size_t len, std::uint32_t freq) + { return lgfx::i2c::transactionWrite(port, addr, data, len, freq).has_value(); } + __attribute__((always_inline)) inline bool i2c_write_read(int port, std::uint16_t addr, const std::uint8_t* wdata, + std::size_t wlen, std::uint8_t* rdata, std::size_t rlen, + std::uint32_t freq) + { return lgfx::i2c::transactionWriteRead(port, addr, wdata, wlen, rdata, rlen, freq).has_value(); } +#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + static constexpr unsigned package_classic_q6 = EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6; + static constexpr unsigned package_classic_pico_d4 = EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4; +#endif + __attribute__((always_inline)) inline unsigned package_version() { return lgfx::get_pkg_ver(); } + __attribute__((always_inline)) inline bool psram_ready() + { +#if defined (CONFIG_IDF_TARGET_ESP32S3) && defined (CONFIG_SPIRAM_MODE_OCT) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + return esp_psram_is_initialized(); +#else + return esp_spiram_is_initialized(); +#endif +#else + return false; +#endif + } + __attribute__((always_inline)) inline unsigned chip_revision() + { +#if defined (CONFIG_IDF_TARGET_ESP32P4) + esp_chip_info_t info; + esp_chip_info(&info); + return info.revision; +#else + return 0; +#endif + } + __attribute__((always_inline)) inline unsigned flash_capacity_code() + { +#if defined (CONFIG_IDF_TARGET_ESP32C6) + return REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_4_REG, EFUSE_FLASH_CAP); +#elif defined (CONFIG_IDF_TARGET_ESP32C5) + return (REG_READ(EFUSE_RD_MAC_SYS2_REG) >> EFUSE_FLASH_CAP_S) & EFUSE_FLASH_CAP_V; +#else + return 0; +#endif + } + __attribute__((always_inline)) inline void module_memory_capacity(unsigned* flash, unsigned* psram) + { +#if defined (CONFIG_IDF_TARGET_ESP32C5) + const auto sys2 = REG_READ(EFUSE_RD_MAC_SYS2_REG); + *flash = (sys2 >> EFUSE_FLASH_CAP_S) & EFUSE_FLASH_CAP_V; + *psram = (sys2 >> EFUSE_PSRAM_CAP_S) & EFUSE_PSRAM_CAP_V; +#else + *flash = *psram = 0; +#endif + } +#if defined (CONFIG_IDF_TARGET_ESP32P4) + __attribute__((always_inline)) inline std::uint8_t st_touch_address() { return lgfx::Touch_ST7123::default_addr; } + __attribute__((always_inline)) inline std::uint8_t gt911_touch_address() { return lgfx::Touch_GT911::default_addr_1; } +#endif +#if defined (CONFIG_IDF_TARGET_ESP32C3) + __attribute__((always_inline)) inline unsigned usb_serial_jtag_frame() + { return REG_GET_FIELD(USB_SERIAL_JTAG_FRAM_NUM_REG, USB_SERIAL_JTAG_SOF_FRAME_INDEX); } + __attribute__((always_inline)) inline bool uart_rx_held_high() + { + const auto mux = REG_READ(IO_MUX_GPIO20_REG); + pin_mode(20, pin_mode_t::input_pulldown); + const bool high = pin_read(20); + REG_WRITE(IO_MUX_GPIO20_REG, mux); + return high; + } +#endif +#if defined (M5GFX_HAS_S3_DEDICATED_GPIO_RELEASE_PROBE) + static constexpr bool dedicated_gpio_supported = true; + __attribute__((always_inline)) inline std::uint32_t dedicated_release_read_in() + { +#if defined (M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL) + return cpu_ll_read_dedic_gpio_in(); +#else + return dedic_gpio_cpu_ll_read_in(); +#endif + } + __attribute__((always_inline)) inline void dedicated_release_write_mask(std::uint32_t mask, std::uint32_t value) + { +#if defined (M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL) + cpu_ll_write_dedic_gpio_mask(mask, value); +#else + dedic_gpio_cpu_ll_write_mask(mask, value); +#endif + } + static inline void dedicated_release_channel_masks(dedicated_gpio_handle_t handle, + std::uint8_t channel_count, std::uint32_t* in_mask, std::uint32_t* out_mask, + int* in_error, int* out_error) + { + const auto bundle = static_cast(handle.value); +#if defined (M5GFX_S3_DEDICATED_GPIO_LEGACY_CPU_LL) + *in_error = dedic_gpio_get_in_mask(bundle, in_mask); + *out_error = dedic_gpio_get_out_mask(bundle, out_mask); +#else + std::uint32_t in_offset = 0; + std::uint32_t out_offset = 0; + *in_error = dedic_gpio_get_in_offset(bundle, &in_offset); + *out_error = dedic_gpio_get_out_offset(bundle, &out_offset); + const std::uint32_t channel_mask = (1u << channel_count) - 1u; + *in_mask = channel_mask << in_offset; + *out_mask = channel_mask << out_offset; +#endif + } + __attribute__((always_inline)) inline int dedicated_release_core_id() + { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + return esp_cpu_get_core_id(); +#else + return xPortGetCoreID(); +#endif + } + __attribute__((always_inline)) inline std::uint32_t dedicated_release_cycle_count() { return cpu_cycles(); } + __attribute__((always_inline)) inline int dedicated_release_task_core() + { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + return xTaskGetCoreID(nullptr); +#else + return xTaskGetAffinity(nullptr); +#endif + } + __attribute__((always_inline)) inline int no_core_affinity() { return static_cast(tskNO_AFFINITY); } + __attribute__((always_inline)) inline bool dedicated_gpio_in_use() + { return REG_GET_BIT(SYSTEM_CPU_PERI_CLK_EN_REG, SYSTEM_CLK_EN_DEDICATED_GPIO); } + __attribute__((always_inline)) inline void pin_weak_pullup_open_drain(int pin) + { + pin_mode(pin, pin_mode_t::input_pullup); + const auto mux = GPIO_PIN_MUX_REG[pin]; + REG_WRITE(mux, (REG_READ(mux) & ~(FUN_PU | FUN_PD | FUN_DRV_M)) | FUN_PU); + gpio_ll_od_enable(&GPIO, static_cast(pin)); + } + __attribute__((always_inline)) inline int dedicated_gpio_open(const int* pins, std::uint8_t count, dedicated_gpio_handle_t* handle) + { + dedic_gpio_bundle_handle_t bundle = nullptr; + dedic_gpio_bundle_config_t config = {}; + config.gpio_array = pins; + config.array_size = count; + config.flags.in_en = 1; + config.flags.out_en = 1; + const auto error = dedic_gpio_new_bundle(&config, &bundle); + handle->value = bundle; + return error; + } + __attribute__((always_inline)) inline int dedicated_gpio_close(dedicated_gpio_handle_t handle) + { return dedic_gpio_del_bundle(static_cast(handle.value)); } + class critical_section_t + { + portMUX_TYPE native_ = portMUX_INITIALIZER_UNLOCKED; + public: + __attribute__((always_inline)) void enter() { taskENTER_CRITICAL(&native_); } + __attribute__((always_inline)) void exit() { taskEXIT_CRITICAL(&native_); } + }; +#else + static constexpr bool dedicated_gpio_supported = false; + __attribute__((always_inline)) inline std::uint32_t dedicated_release_read_in() { return 0; } + __attribute__((always_inline)) inline void dedicated_release_write_mask(std::uint32_t, std::uint32_t) {} + static inline void dedicated_release_channel_masks(dedicated_gpio_handle_t, std::uint8_t, + std::uint32_t*, std::uint32_t*, int*, int*) {} + __attribute__((always_inline)) inline int dedicated_release_core_id() { return 0; } + __attribute__((always_inline)) inline std::uint32_t dedicated_release_cycle_count() { return 0; } + __attribute__((always_inline)) inline int dedicated_release_task_core() { return 0; } + __attribute__((always_inline)) inline int no_core_affinity() { return -1; } + __attribute__((always_inline)) inline bool dedicated_gpio_in_use() { return false; } + __attribute__((always_inline)) inline void pin_weak_pullup_open_drain(int) {} + __attribute__((always_inline)) inline int dedicated_gpio_open(const int*, std::uint8_t, dedicated_gpio_handle_t*) { return -1; } + __attribute__((always_inline)) inline int dedicated_gpio_close(dedicated_gpio_handle_t) { return -1; } + class critical_section_t { public: void enter() {} void exit() {} }; +#endif + +#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + __attribute__((always_inline)) inline volatile std::uint32_t& reg(std::uint32_t addr) + { return *reinterpret_cast(addr); } + + template + __attribute__((always_inline)) inline bool classic_touch_measure(std::uint16_t* t4, std::uint16_t* t7) + { + (void)pin_a; + (void)pin_b; + constexpr int channels[] = { channel_a, channel_b }; + const auto state0 = reg(RTC_CNTL_STATE0_REG); + const auto ctrl1 = reg(SENS_SAR_TOUCH_CTRL1_REG); + const auto ctrl2 = reg(SENS_SAR_TOUCH_CTRL2_REG); + const auto enable = reg(SENS_SAR_TOUCH_ENABLE_REG); + const auto cfg = reg(RTC_IO_TOUCH_CFG_REG); + std::uint32_t pads[2]; + for (int i = 0; i < 2; ++i) { pads[i] = reg(RTC_IO_TOUCH_PAD0_REG + channels[i] * 4); } + + constexpr std::uint32_t clear = RTC_IO_TOUCH_PAD0_RDE_M | RTC_IO_TOUCH_PAD0_RUE_M + | RTC_IO_TOUCH_PAD0_DAC_M | RTC_IO_TOUCH_PAD0_TIE_OPT_M | RTC_IO_TOUCH_PAD0_XPD_M + | RTC_IO_TOUCH_PAD0_MUX_SEL_M | RTC_IO_TOUCH_PAD0_FUN_SEL_M + | RTC_IO_TOUCH_PAD0_FUN_IE_M | RTC_IO_TOUCH_PAD0_TO_GPIO_M | RTC_IO_TOUCH_PAD0_START_M; + constexpr std::uint32_t set = (7u << RTC_IO_TOUCH_PAD0_DAC_S) + | RTC_IO_TOUCH_PAD0_XPD_M | RTC_IO_TOUCH_PAD0_MUX_SEL_M; + + // Stop the automatic timer, use a software start, and disable pad pulls. + // Only these saved control registers are changed; wake thresholds/masks stay intact. + reg(RTC_CNTL_STATE0_REG) = state0 & ~RTC_CNTL_TOUCH_SLP_TIMER_EN; + reg(SENS_SAR_TOUCH_CTRL2_REG) = (ctrl2 & ~SENS_TOUCH_START_EN) + | SENS_TOUCH_START_FORCE | SENS_TOUCH_START_FSM_EN; + reg(SENS_SAR_TOUCH_CTRL1_REG) = (ctrl1 & ~((SENS_TOUCH_XPD_WAIT << SENS_TOUCH_XPD_WAIT_S) + | (SENS_TOUCH_MEAS_DELAY << SENS_TOUCH_MEAS_DELAY_S))) + | (0xFFu << SENS_TOUCH_XPD_WAIT_S) + | ((800u & SENS_TOUCH_MEAS_DELAY) << SENS_TOUCH_MEAS_DELAY_S); // 0.1 ms at 8 MHz + // DREFH=3, DRANGE=0 caps the charge at 1.2 V. A 2.7 V scan can light the WS2812 array white. + reg(RTC_IO_TOUCH_CFG_REG) = (cfg & ~((RTC_IO_TOUCH_DREFH << RTC_IO_TOUCH_DREFH_S) + | (RTC_IO_TOUCH_DREFL << RTC_IO_TOUCH_DREFL_S) + | (RTC_IO_TOUCH_DRANGE << RTC_IO_TOUCH_DRANGE_S))) + | (3u << RTC_IO_TOUCH_DREFH_S); + for (int i = 0; i < 2; ++i) + { reg(RTC_IO_TOUCH_PAD0_REG + channels[i] * 4) = (pads[i] & ~clear) | set; } + reg(SENS_SAR_TOUCH_ENABLE_REG) = (enable & ~(SENS_TOUCH_PAD_WORKEN << SENS_TOUCH_PAD_WORKEN_S)) + | (((1u << channel_a) | (1u << channel_b)) << SENS_TOUCH_PAD_WORKEN_S); + + reg(SENS_SAR_TOUCH_CTRL2_REG) = reg(SENS_SAR_TOUCH_CTRL2_REG) & ~SENS_TOUCH_START_EN; + reg(SENS_SAR_TOUCH_CTRL2_REG) = reg(SENS_SAR_TOUCH_CTRL2_REG) | SENS_TOUCH_START_EN; + const auto started = esp_timer_get_time(); + while (!(reg(SENS_SAR_TOUCH_CTRL2_REG) & SENS_TOUCH_MEAS_DONE)) + { + if (esp_timer_get_time() - started > timeout_us) { break; } + } + const bool ok = (reg(SENS_SAR_TOUCH_CTRL2_REG) & SENS_TOUCH_MEAS_DONE) != 0; + *t4 = ok ? (reg(SENS_SAR_TOUCH_OUT3_REG) >> 16) : 0; + *t7 = ok ? (reg(SENS_SAR_TOUCH_OUT4_REG) & 0xFFFF) : 0; + reg(SENS_SAR_TOUCH_CTRL2_REG) = reg(SENS_SAR_TOUCH_CTRL2_REG) & ~SENS_TOUCH_START_EN; + + reg(SENS_SAR_TOUCH_ENABLE_REG) = enable; + for (int i = 0; i < 2; ++i) { reg(RTC_IO_TOUCH_PAD0_REG + channels[i] * 4) = pads[i]; } + reg(RTC_IO_TOUCH_CFG_REG) = cfg; + reg(SENS_SAR_TOUCH_CTRL1_REG) = ctrl1; + reg(SENS_SAR_TOUCH_CTRL2_REG) = ctrl2; + reg(RTC_CNTL_STATE0_REG) = state0; + reg(RTC_CNTL_INT_CLR_REG) = RTC_CNTL_TOUCH_INT_CLR; + return ok; + } + + static IRAM_ATTR std::uint32_t classic_led_cycles() { return cpu_cycles(); } + + // Send black GRB pixels. Flash stalls must not extend a high pulse. + template + static IRAM_ATTR void send_black(std::uint32_t mhz) + { + // WS2812C-2020 T0H = 0.22-0.38 us. Aim at the middle: loop overhead adds to the set value, and 0.35 us + // sat at the upper edge (single LEDs mid-chain lit, varying with the USB hub = LED supply voltage). + const std::uint32_t high = mhz * 28 / 100; // 0.28 us + const std::uint32_t low = mhz * 90 / 100; // 0.9 us + for (int bit = 0; bit < pixels * 24; ++bit) + { + auto at = classic_led_cycles(); + GPIO.out_w1ts = 1u << led_gpio; + while (classic_led_cycles() - at < high) {} + at = classic_led_cycles(); + GPIO.out_w1tc = 1u << led_gpio; + while (classic_led_cycles() - at < low) {} + } + } + + template + __attribute__((always_inline)) inline void classic_led_clear() + { + gpio_set_pull_mode(static_cast(led_gpio), GPIO_FLOATING); + GPIO.out_w1tc = 1u << led_gpio; // Set low before enabling output: no high spike on DIN. + gpio_set_direction(static_cast(led_gpio), GPIO_MODE_OUTPUT); + rtc_cpu_freq_config_t cpu_freq; + rtc_clk_cpu_freq_get_config(&cpu_freq); // Same cross-IDF API as lgfx::getApbFrequency. + const auto mhz = cpu_freq.freq_mhz; + portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + esp_rom_delay_us(300); + taskENTER_CRITICAL(&mux); + send_black(mhz); + taskEXIT_CRITICAL(&mux); + esp_rom_delay_us(300); + } +#endif +}}} diff --git a/src/board_detect/detection_package.inl b/src/board_detect/detection_package.inl index 8ceb4661..43920888 100644 --- a/src/board_detect/detection_package.inl +++ b/src/board_detect/detection_package.inl @@ -2,11 +2,7 @@ static bool conditional_detection_pins_unavailable() { #if defined (CONFIG_SPIRAM_MODE_OCT) - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - return esp_psram_is_initialized(); - #else - return esp_spiram_is_initialized(); - #endif + return board_detect::io::psram_ready(); #else return false; #endif @@ -29,16 +25,16 @@ package.direct_setup = true; // The protected legacy autodetect method never probed ESP32 directly. if (legacy_autodetect) { return package; } - const auto pkg = m5gfx::get_pkg_ver(); - if (pkg == EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6) + const auto pkg = board_detect::io::package_version(); + if (pkg == board_detect::io::package_classic_q6) { package.detectors = board_detect::m5::esp32_d0wdq6_detectors; } - else if (pkg == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) + else if (pkg == board_detect::io::package_classic_pico_d4) { package.detectors = board_detect::m5::esp32_pico_d4_detectors; } else if (pkg == 6) { package.detectors = board_detect::m5::esp32_picov3_detectors; } #elif defined(CONFIG_IDF_TARGET_ESP32S3) package.conditional_pins_unavailable = conditional_detection_pins_unavailable(); - switch (m5gfx::get_pkg_ver()) + switch (board_detect::io::package_version()) { case 0: package.detectors = board_detect::m5::esp32s3_detectors_qfn56; break; case 1: package.detectors = board_detect::m5::esp32s3_detectors_lga56; break; @@ -49,7 +45,7 @@ #elif defined(CONFIG_IDF_TARGET_ESP32C5) package.detectors = board_detect::m5::esp32c5_detectors; #elif defined(CONFIG_IDF_TARGET_ESP32C6) - const auto pkg = m5gfx::get_pkg_ver(); + const auto pkg = board_detect::io::package_version(); ESP_LOGD(LIBRARY_NAME, "pkg_ver : %02x", (int)pkg); // Package version: 1 = QFN32 (ESP32-C6FH4/FH8), 0 = QFN40. if (pkg == 1) { package.detectors = board_detect::m5::esp32c6_detectors_qfn32; } @@ -62,7 +58,7 @@ #elif defined(CONFIG_IDF_TARGET_ESP32H2) package.detectors = board_detect::m5::esp32h2_detectors; #elif defined(CONFIG_IDF_TARGET_ESP32P4) - const auto pkg = m5gfx::get_pkg_ver(); + const auto pkg = board_detect::io::package_version(); ESP_LOGD(LIBRARY_NAME, "pkg_ver : %02x", (int)pkg); if (pkg == 0) { package.detectors = board_detect::m5::esp32p4_detectors; } #endif diff --git a/src/board_detect/m5/atom_touch.inl b/src/board_detect/m5/atom_touch.inl index 07897a2a..cb0c0229 100644 --- a/src/board_detect/m5/atom_touch.inl +++ b/src/board_detect/m5/atom_touch.inl @@ -1,109 +1,11 @@ -// Classic ESP32 touch measurement for PICO-D4. Included inside m5gfx::board_detect::m5. +// Classic ESP32 touch/LED transport is supplied by the detection I/O layer. namespace atom_touch { - inline volatile std::uint32_t& reg(std::uint32_t addr) - { return *reinterpret_cast(addr); } - static bool measure(std::uint16_t* t4, std::uint16_t* t7) { - constexpr int channels[] = { t4_pad.channel, t7_pad.channel }; - const auto state0 = reg(RTC_CNTL_STATE0_REG); - const auto ctrl1 = reg(SENS_SAR_TOUCH_CTRL1_REG); - const auto ctrl2 = reg(SENS_SAR_TOUCH_CTRL2_REG); - const auto enable = reg(SENS_SAR_TOUCH_ENABLE_REG); - const auto cfg = reg(RTC_IO_TOUCH_CFG_REG); - std::uint32_t pads[2]; - for (int i = 0; i < 2; ++i) { pads[i] = reg(RTC_IO_TOUCH_PAD0_REG + channels[i] * 4); } - - constexpr std::uint32_t clear = RTC_IO_TOUCH_PAD0_RDE_M | RTC_IO_TOUCH_PAD0_RUE_M - | RTC_IO_TOUCH_PAD0_DAC_M | RTC_IO_TOUCH_PAD0_TIE_OPT_M | RTC_IO_TOUCH_PAD0_XPD_M - | RTC_IO_TOUCH_PAD0_MUX_SEL_M | RTC_IO_TOUCH_PAD0_FUN_SEL_M - | RTC_IO_TOUCH_PAD0_FUN_IE_M | RTC_IO_TOUCH_PAD0_TO_GPIO_M | RTC_IO_TOUCH_PAD0_START_M; - constexpr std::uint32_t set = (7u << RTC_IO_TOUCH_PAD0_DAC_S) - | RTC_IO_TOUCH_PAD0_XPD_M | RTC_IO_TOUCH_PAD0_MUX_SEL_M; - - // Stop the automatic timer, use a software start, and disable pad pulls. - // Only these saved control registers are changed; wake thresholds/masks stay intact. - reg(RTC_CNTL_STATE0_REG) = state0 & ~RTC_CNTL_TOUCH_SLP_TIMER_EN; - reg(SENS_SAR_TOUCH_CTRL2_REG) = (ctrl2 & ~SENS_TOUCH_START_EN) - | SENS_TOUCH_START_FORCE | SENS_TOUCH_START_FSM_EN; - reg(SENS_SAR_TOUCH_CTRL1_REG) = (ctrl1 & ~((SENS_TOUCH_XPD_WAIT << SENS_TOUCH_XPD_WAIT_S) - | (SENS_TOUCH_MEAS_DELAY << SENS_TOUCH_MEAS_DELAY_S))) - | (0xFFu << SENS_TOUCH_XPD_WAIT_S) - | ((800u & SENS_TOUCH_MEAS_DELAY) << SENS_TOUCH_MEAS_DELAY_S); // 0.1 ms at 8 MHz - // Investigation atom-picod4-pin-edge-2026-09-28: DREFH=3, DRANGE=0 - // caps the charge at 1.2 V. A 2.7 V scan can light the WS2812 array white. - reg(RTC_IO_TOUCH_CFG_REG) = (cfg & ~((RTC_IO_TOUCH_DREFH << RTC_IO_TOUCH_DREFH_S) - | (RTC_IO_TOUCH_DREFL << RTC_IO_TOUCH_DREFL_S) - | (RTC_IO_TOUCH_DRANGE << RTC_IO_TOUCH_DRANGE_S))) - | (3u << RTC_IO_TOUCH_DREFH_S); - for (int i = 0; i < 2; ++i) - { reg(RTC_IO_TOUCH_PAD0_REG + channels[i] * 4) = (pads[i] & ~clear) | set; } - reg(SENS_SAR_TOUCH_ENABLE_REG) = (enable & ~(SENS_TOUCH_PAD_WORKEN << SENS_TOUCH_PAD_WORKEN_S)) - | (((1u << t4_pad.channel) | (1u << t7_pad.channel)) << SENS_TOUCH_PAD_WORKEN_S); - - reg(SENS_SAR_TOUCH_CTRL2_REG) = reg(SENS_SAR_TOUCH_CTRL2_REG) & ~SENS_TOUCH_START_EN; - reg(SENS_SAR_TOUCH_CTRL2_REG) = reg(SENS_SAR_TOUCH_CTRL2_REG) | SENS_TOUCH_START_EN; - const auto started = esp_timer_get_time(); - while (!(reg(SENS_SAR_TOUCH_CTRL2_REG) & SENS_TOUCH_MEAS_DONE)) - { - if (esp_timer_get_time() - started > 200000) { break; } - } - const bool ok = (reg(SENS_SAR_TOUCH_CTRL2_REG) & SENS_TOUCH_MEAS_DONE) != 0; - *t4 = ok ? (reg(SENS_SAR_TOUCH_OUT3_REG) >> 16) : 0; - *t7 = ok ? (reg(SENS_SAR_TOUCH_OUT4_REG) & 0xFFFF) : 0; - reg(SENS_SAR_TOUCH_CTRL2_REG) = reg(SENS_SAR_TOUCH_CTRL2_REG) & ~SENS_TOUCH_START_EN; - - reg(SENS_SAR_TOUCH_ENABLE_REG) = enable; - for (int i = 0; i < 2; ++i) { reg(RTC_IO_TOUCH_PAD0_REG + channels[i] * 4) = pads[i]; } - reg(RTC_IO_TOUCH_CFG_REG) = cfg; - reg(SENS_SAR_TOUCH_CTRL1_REG) = ctrl1; - reg(SENS_SAR_TOUCH_CTRL2_REG) = ctrl2; - reg(RTC_CNTL_STATE0_REG) = state0; - reg(RTC_CNTL_INT_CLR_REG) = RTC_CNTL_TOUCH_INT_CLR; - return ok && *t4 != 0; + return io::classic_touch_measure(t4, t7) && *t4 != 0; } - static IRAM_ATTR std::uint32_t cycles() - { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - return esp_cpu_get_cycle_count(); -#else - return esp_cpu_get_ccount(); -#endif - } - - // Send 25 black GRB pixels. Flash stalls must not extend a high pulse. - static IRAM_ATTR void send_black(std::uint32_t mhz) - { - // WS2812C-2020 T0H = 0.22-0.38 us. Aim at the middle: loop overhead adds to the set value, and 0.35 us - // sat at the upper edge (single LEDs mid-chain lit, varying with the USB hub = LED supply voltage). - const std::uint32_t high = mhz * 28 / 100; // 0.28 us - const std::uint32_t low = mhz * 90 / 100; // 0.9 us - for (int bit = 0; bit < 25 * 24; ++bit) - { - auto at = cycles(); - GPIO.out_w1ts = 1u << led_gpio; - while (cycles() - at < high) {} - at = cycles(); - GPIO.out_w1tc = 1u << led_gpio; - while (cycles() - at < low) {} - } - } - - static void clear_led() - { - gpio_set_pull_mode(static_cast(led_gpio), GPIO_FLOATING); - GPIO.out_w1tc = 1u << led_gpio; // Set low before enabling output: no high spike on DIN. - gpio_set_direction(static_cast(led_gpio), GPIO_MODE_OUTPUT); - rtc_cpu_freq_config_t cpu_freq; - rtc_clk_cpu_freq_get_config(&cpu_freq); // Same cross-IDF API as lgfx::getApbFrequency. - const auto mhz = cpu_freq.freq_mhz; - portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - esp_rom_delay_us(300); - taskENTER_CRITICAL(&mux); - send_black(mhz); - taskEXIT_CRITICAL(&mux); - esp_rom_delay_us(300); - } + static void clear_led() { io::classic_led_clear(); } } diff --git a/src/board_detect/m5/board_registry.inl b/src/board_detect/m5/board_registry.inl index 8b289f0c..fce1e624 100644 --- a/src/board_detect/m5/board_registry.inl +++ b/src/board_detect/m5/board_registry.inl @@ -92,14 +92,14 @@ namespace m5 void write8(soft_spi_t& bus, int pin_cs, std::uint8_t cmd, std::uint8_t data) { bus.beginTransaction(); - lgfx::gpio_lo(pin_cs); + io::pin_low(pin_cs); bus.writeCommand(cmd, 8); bus.writeData(data, 8); // An uninitialised Panel_LCD still has _nop_closing=true. Its endWrite() // emits NOP before raising CS, so reproduce that probe transaction exactly. bus.writeCommand(0x00, 8); bus.wait(); - lgfx::gpio_hi(pin_cs); + io::pin_high(pin_cs); bus.endTransaction(); } @@ -107,11 +107,11 @@ namespace m5 { write8(bus, pin_cs, 0xD9, 0x10 | index); bus.beginTransaction(); - lgfx::gpio_lo(pin_cs); + io::pin_low(pin_cs); bus.writeCommand(cmd, 8); bus.beginRead(1); const auto value = static_cast(bus.readData(8)); - lgfx::gpio_hi(pin_cs); + io::pin_high(pin_cs); bus.endRead(); // Panel_LCD::readCommand() raises CS before endWrite(). The latter still // clocks its closing NOP (with CS high) because the panel is uninitialised. @@ -134,14 +134,14 @@ namespace m5 if (!try_c_key) { return panel_variant_t::unknown; } bus.beginTransaction(); - lgfx::gpio_lo(pin_cs); + io::pin_low(pin_cs); bus.writeCommand(0xC8, 8); bus.writeData(0xFF, 8); bus.writeData(0x93, 8); bus.writeData(0x42, 8); bus.writeCommand(0x00, 8); bus.wait(); - lgfx::gpio_hi(pin_cs); + io::pin_high(pin_cs); bus.endTransaction(); keys[2] = read_parameter(bus, pin_cs, 0xD3, 2); keys[3] = read_parameter(bus, pin_cs, 0xD3, 3); @@ -155,12 +155,12 @@ namespace m5 bool try_c_key = true) { auto variant = probe_panel_variant(bus, pin_cs, keys, try_c_key); - const auto started = lgfx::millis(); + const auto started = io::millis(); while (variant == panel_variant_t::unknown && poll_ms) { - lgfx::delay(1); + io::delay_ms(1); variant = probe_panel_variant(bus, pin_cs, keys, try_c_key); - if (lgfx::millis() - started >= poll_ms) { break; } + if (io::millis() - started >= poll_ms) { break; } } return variant; } diff --git a/src/board_detect/m5/esp32_d0wdq6.inl b/src/board_detect/m5/esp32_d0wdq6.inl index d76e8460..1a9eec37 100644 --- a/src/board_detect/m5/esp32_d0wdq6.inl +++ b/src/board_detect/m5/esp32_d0wdq6.inl @@ -9,16 +9,6 @@ #include "pmic_ops.hpp" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace m5gfx { @@ -170,14 +160,14 @@ namespace m5 { const auto& reset = desc.reset; startup_detail::pin_level(reset.pin, true); - lgfx::delay(1); - lgfx::gpio_lo(reset.pin); - lgfx::delay(reset.low_ms); - lgfx::pinMode(reset.pin, lgfx::pin_mode_t::input_pulldown); - lgfx::gpio_hi(reset.pin); - const bool ips = lgfx::gpio_in(reset.pin); - lgfx::pinMode(reset.pin, lgfx::pin_mode_t::output); - lgfx::delay(reset.post_ms); + io::delay_ms(1); + io::pin_low(reset.pin); + io::delay_ms(reset.low_ms); + io::pin_mode(reset.pin, io::pin_mode_t::input_pulldown); + io::pin_high(reset.pin); + const bool ips = io::pin_read(reset.pin); + io::pin_mode(reset.pin, io::pin_mode_t::output); + io::delay_ms(reset.post_ms); if (ctx.allow_reset && ips && detected_option != nullptr) { *detected_option |= generated_options::stack::ips; @@ -315,23 +305,22 @@ namespace m5 } bool tough = false; bool core2 = false; - const auto touch_start = lgfx::millis(); + const auto touch_start = io::millis(); do { - tough = lgfx::i2c::readRegister8( - i2c.port, tough_touch_address, touch_probe_register, - tough_touch_i2c_frequency).has_value(); - core2 = lgfx::i2c::readRegister8( - i2c.port, core2_touch_address, touch_probe_register, - tough_touch_i2c_frequency).has_value(); + std::uint8_t touch_value = 0; + tough = io::i2c_read_reg8(i2c.port, tough_touch_address, touch_probe_register, + &touch_value, tough_touch_i2c_frequency); + core2 = io::i2c_read_reg8(i2c.port, core2_touch_address, touch_probe_register, + &touch_value, tough_touch_i2c_frequency); #if defined (M5GFX_AUTODETECT_TEST_FAIL_CORE2_TOUCH) || defined (M5GFX_AUTODETECT_TEST_FAIL_CORE2_TOUCH_POST) tough = core2 = false; #endif // M-Bus devices can answer at either address. Read both even if Tough // answers first; simultaneous responses cannot confirm a member. if (tough || core2) { break; } - lgfx::delay(1); - } while (lgfx::millis() - touch_start < touch_startup_poll_ms); + io::delay_ms(1); + } while (io::millis() - touch_start < touch_startup_poll_ms); if ((tough && core2) || (tough && (result.option & generated_options::core2::new_pmic))) { @@ -407,9 +396,7 @@ namespace m5 // Keep the scoped port alive; probe_i2c_ack would release it between reads. const auto touch_answers = [&](std::uint8_t addr) -> bool { - const bool began = lgfx::i2c::beginTransaction(i2c.port, addr, 100000, false).has_value(); - const bool ended = lgfx::i2c::endTransaction(i2c.port).has_value(); - return began && ended; + return io::i2c_ack(i2c.port, addr, 100000, true); }; bool core2_touch = touch_answers(detail::core2_touch_address); bool tough_touch = touch_answers(detail::tough_touch_address); @@ -595,8 +582,8 @@ namespace m5 // This family contract keeps the mandatory reset from stage 1 through // stage 2, where prepared_reset records that it already completed. startup_detail::pin_reset(desc_paper.reset, true); - lgfx::pinMode(desc_paper.display.busy, lgfx::pin_mode_t::input_pullup); - const bool matched = !lgfx::gpio_in(desc_paper.display.busy); + io::pin_mode(desc_paper.display.busy, io::pin_mode_t::input_pullup); + const bool matched = !io::pin_read(desc_paper.display.busy); ctx.transaction->restore_start(desc_paper.display.busy); if (!matched) { ctx.transaction->restore_start(desc_paper.reset.pin); } return matched; @@ -634,24 +621,24 @@ namespace m5 { return restore_and_fail(); } startup_detail::hold_chip_selects(desc_paper); const auto& display = desc_paper.display; - lgfx::pinMode(display.busy, lgfx::pin_mode_t::input); + io::pin_mode(display.busy, io::pin_mode_t::input); soft_spi_t bus(display.sclk, display.mosi, display.miso, display.dc); bus.init(); bool matched = false; - auto started = lgfx::millis(); - while (!lgfx::gpio_in(display.busy) && lgfx::millis() - started <= 1024) { lgfx::delay(1); } - if (lgfx::gpio_in(display.busy)) + auto started = io::millis(); + while (!io::pin_read(display.busy) && io::millis() - started <= 1024) { io::delay_ms(1); } + if (io::pin_read(display.busy)) { bus.beginTransaction(); - lgfx::gpio_lo(display.cs); + io::pin_low(display.cs); bus.writeData(__builtin_bswap16(0x6000), 16); bus.writeData(__builtin_bswap16(0x0302), 16); bus.wait(); - lgfx::gpio_hi(display.cs); - started = lgfx::millis(); - while (!lgfx::gpio_in(display.busy) && lgfx::millis() - started <= 192) { lgfx::delay(1); } - lgfx::gpio_lo(display.cs); + io::pin_high(display.cs); + started = io::millis(); + while (!io::pin_read(display.busy) && io::millis() - started <= 192) { io::delay_ms(1); } + io::pin_low(display.cs); bus.writeData(__builtin_bswap16(0x1000), 16); bus.writeData(__builtin_bswap16(0x0000), 16); std::uint8_t data[40] = {}; @@ -659,7 +646,7 @@ namespace m5 bus.readBytes(data, sizeof(data)); bus.endRead(); bus.endTransaction(); - lgfx::gpio_hi(display.cs); + io::pin_high(display.cs); const std::uint32_t panel_size = (std::uint32_t(data[0]) << 24) | (std::uint32_t(data[1]) << 16) | (std::uint32_t(data[2]) << 8) diff --git a/src/board_detect/m5/esp32_pico.inl b/src/board_detect/m5/esp32_pico.inl index 6467a06e..c39d1c07 100644 --- a/src/board_detect/m5/esp32_pico.inl +++ b/src/board_detect/m5/esp32_pico.inl @@ -143,11 +143,7 @@ static const spi_id_detector_t stickcplus2_detector( stickcplus2_members, stickcplus2_member_descs, 1); #include "atom_touch_pins.inl" -#if defined (M5GFX_BOARD_DETECT_HOST_TOUCH) -#include M5GFX_BOARD_DETECT_HOST_TOUCH -#else #include "atom_touch.inl" -#endif class atom_family_detector_t final : public board_detector_t { @@ -169,24 +165,24 @@ private: { if (result == nullptr) { return false; } // StampPico holds G2 low; exclude it before probing the Atom family. - lgfx::pinMode(2, lgfx::pin_mode_t::input_pullup); - esp_rom_delay_us(5); - const bool atom_g2 = lgfx::gpio_in(2); + io::pin_mode(2, io::pin_mode_t::input_pullup); + io::delay_rom_us(5); + const bool atom_g2 = io::pin_read(2); ctx.transaction->restore_start({ 2 }); if (!atom_g2) { return false; } // StickC LCD reset is pulled high; Atom leaves G18 unconnected. const auto g18 = probe_pin_pulls(ctx, 1ULL << 18); if (g18.pulldown_high || g18.pullup_high != (1ULL << 18)) { return false; } - const auto read_g34 = [](lgfx::pin_mode_t mode) { - lgfx::pinMode(23, mode); // G23 is exposed on Lite/Matrix: internal pull only, never drive it. - esp_rom_delay_us(5); - return lgfx::gpio_in(34); + const auto read_g34 = [](io::pin_mode_t mode) { + io::pin_mode(23, mode); // G23 is exposed on Lite/Matrix: internal pull only, never drive it. + io::delay_rom_us(5); + return io::pin_read(34); }; - lgfx::pinMode(34, lgfx::pin_mode_t::input); - const bool down = read_g34(lgfx::pin_mode_t::input_pulldown); - const bool up1 = read_g34(lgfx::pin_mode_t::input_pullup); - const bool down2 = read_g34(lgfx::pin_mode_t::input_pulldown); - const bool up2 = read_g34(lgfx::pin_mode_t::input_pullup); + io::pin_mode(34, io::pin_mode_t::input); + const bool down = read_g34(io::pin_mode_t::input_pulldown); + const bool up1 = read_g34(io::pin_mode_t::input_pullup); + const bool down2 = read_g34(io::pin_mode_t::input_pulldown); + const bool up2 = read_g34(io::pin_mode_t::input_pullup); ctx.transaction->restore_start({ 23, 34 }); if (down) @@ -220,10 +216,10 @@ private: if (ratio100 < matrix_limit * reference) { result->assign(&desc_atommatrix); - lgfx::pinMode(21, lgfx::pin_mode_t::input_pulldown); - lgfx::pinMode(25, lgfx::pin_mode_t::input_pulldown); - esp_rom_delay_us(10); - const bool matrix_pulls = lgfx::gpio_in(21) && lgfx::gpio_in(25); + io::pin_mode(21, io::pin_mode_t::input_pulldown); + io::pin_mode(25, io::pin_mode_t::input_pulldown); + io::delay_rom_us(10); + const bool matrix_pulls = io::pin_read(21) && io::pin_read(25); ctx.transaction->restore_start({ 21, 25 }); if (!linked || !matrix_pulls) { result->provisional = true; } } diff --git a/src/board_detect/m5/esp32c3.inl b/src/board_detect/m5/esp32c3.inl index 05a3ca66..df2d51fd 100644 --- a/src/board_detect/m5/esp32c3.inl +++ b/src/board_detect/m5/esp32c3.inl @@ -5,8 +5,6 @@ #include "../board_detect.hpp" #include "board_registry.inl" #include "generated/esp32c3_wiring.hpp" -#include -#include namespace m5gfx { namespace board_detect { namespace m5 { @@ -25,15 +23,13 @@ namespace c3_detail { inline bool sof_advances() { - const auto first = REG_GET_FIELD(USB_SERIAL_JTAG_FRAM_NUM_REG, - USB_SERIAL_JTAG_SOF_FRAME_INDEX); + const auto first = io::usb_serial_jtag_frame(); // USB SOF is 1 ms apart. Four further samples span at most 4 ms; do not // wait for USB enumeration, which starts much later on a cold boot. for (int i = 0; i < 4; ++i) { - lgfx::delayMicroseconds(1000); - if (REG_GET_FIELD(USB_SERIAL_JTAG_FRAM_NUM_REG, - USB_SERIAL_JTAG_SOF_FRAME_INDEX) != first) { return true; } + io::delay_us(1000); + if (io::usb_serial_jtag_frame() != first) { return true; } } return false; } @@ -42,10 +38,7 @@ namespace c3_detail { // GPIO20 is UART0 RX. Restore its IO_MUX immediately after the read so // the console input keeps its pre-detection configuration. - const auto mux = REG_READ(IO_MUX_GPIO20_REG); - lgfx::pinMode(20, lgfx::pin_mode_t::input_pulldown); - const bool high = lgfx::gpio_in(20); - REG_WRITE(IO_MUX_GPIO20_REG, mux); + const bool high = io::uart_rx_held_high(); ctx.transaction->restore_start({ 20 }); return high; } diff --git a/src/board_detect/m5/esp32c5/toughc5.inl b/src/board_detect/m5/esp32c5/toughc5.inl index 70f5ed5e..f155f9a3 100644 --- a/src/board_detect/m5/esp32c5/toughc5.inl +++ b/src/board_detect/m5/esp32c5/toughc5.inl @@ -2,12 +2,7 @@ { constexpr int sda = wiring::toughc5::internal_i2c_sda; constexpr int scl = wiring::toughc5::internal_i2c_scl; -#if defined(SOC_LP_I2C_NUM) && SOC_LP_I2C_NUM > 0 && __has_include() \ - && defined(ESP_IDF_VERSION_VAL) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) - constexpr int_fast16_t i2c_port = LP_I2C_NUM_0; -#else - constexpr int_fast16_t i2c_port = I2C_NUM_0; -#endif + constexpr int_fast16_t i2c_port = io::low_power_i2c_port_0; constexpr std::uint32_t i2c_freq = pmic_ops::pm1_i2c_freq; static const pmic_variant_t power_variants[] = { pmic_variant_ack_only(pmic_ops::pm1_i2c_addr, 0, @@ -47,9 +42,9 @@ stampc5_detector_t() : board_detector_t(members_) {} bool signature(probe_ctx_t& ctx) const override { - const auto sys2 = REG_READ(EFUSE_RD_MAC_SYS2_REG); - if (((sys2 >> EFUSE_FLASH_CAP_S) & EFUSE_FLASH_CAP_V) != 1 - || ((sys2 >> EFUSE_PSRAM_CAP_S) & EFUSE_PSRAM_CAP_V) != 0) { return false; } + unsigned flash, psram; + io::module_memory_capacity(&flash, &psram); + if (flash != 1 || psram != 0) { return false; } // Package facts only suggest a board; keep later families from probing // this module's user pins without treating it as confirmed. if (ctx.candidate == nullptr) { ctx.candidate = &desc_stampc5.def; } diff --git a/src/board_detect/m5/esp32c6/c6_display.inl b/src/board_detect/m5/esp32c6/c6_display.inl index 8a211cf6..d7d3e115 100644 --- a/src/board_detect/m5/esp32c6/c6_display.inl +++ b/src/board_detect/m5/esp32c6/c6_display.inl @@ -2,7 +2,7 @@ namespace c6_display_detail { constexpr int sda = wiring::nesson1::internal_i2c_sda; constexpr int scl = wiring::nesson1::internal_i2c_scl; - constexpr int_fast16_t i2c_port = I2C_NUM_0; + constexpr int_fast16_t i2c_port = io::i2c_port_0; constexpr std::uint32_t pi4io_freq = 100000; constexpr std::uint64_t signature_bit = std::uint64_t(1) << 18; enum class candidate_t : std::uint8_t { none, unitc6l, nesson1 }; diff --git a/src/board_detect/m5/esp32c6/c6_displayless.inl b/src/board_detect/m5/esp32c6/c6_displayless.inl index b51a4774..700f49c4 100644 --- a/src/board_detect/m5/esp32c6/c6_displayless.inl +++ b/src/board_detect/m5/esp32c6/c6_displayless.inl @@ -17,7 +17,7 @@ namespace c6_displayless_detail // The package's flash capacity is a stable eFuse fact; check it before // probing NanoC6 GPIO3, which is exposed on StampC6 as a user header pin. inline unsigned flash_capacity() - { return REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_4_REG, EFUSE_FLASH_CAP); } + { return io::flash_capacity_code(); } } class stampc6_detector_t final : public board_detector_t diff --git a/src/board_detect/m5/esp32c61/corematrix.inl b/src/board_detect/m5/esp32c61/corematrix.inl index 3f6688c0..315ca60a 100644 --- a/src/board_detect/m5/esp32c61/corematrix.inl +++ b/src/board_detect/m5/esp32c61/corematrix.inl @@ -2,7 +2,7 @@ { constexpr int sda = wiring::corematrix::internal_i2c_sda; constexpr int scl = wiring::corematrix::internal_i2c_scl; - constexpr int_fast16_t i2c_port = I2C_NUM_0; + constexpr int_fast16_t i2c_port = io::i2c_port_0; constexpr std::uint32_t i2c_freq = pmic_ops::pm1_i2c_freq; static const pmic_variant_t power_variants[] = { pmic_variant_ack_only(pmic_ops::pm1_i2c_addr, 0, diff --git a/src/board_detect/m5/esp32p4.inl b/src/board_detect/m5/esp32p4.inl index 87691519..4cfd4d71 100644 --- a/src/board_detect/m5/esp32p4.inl +++ b/src/board_detect/m5/esp32p4.inl @@ -6,7 +6,6 @@ #include "generated/esp32p4_wiring.hpp" #include "generated/esp32p4_specs.hpp" #include "pmic_ops.hpp" -#include namespace m5gfx { diff --git a/src/board_detect/m5/esp32p4/corep4x.inl b/src/board_detect/m5/esp32p4/corep4x.inl index 01d89ae5..c5da64f2 100644 --- a/src/board_detect/m5/esp32p4/corep4x.inl +++ b/src/board_detect/m5/esp32p4/corep4x.inl @@ -2,7 +2,7 @@ namespace corep4x_detail { constexpr int sda = wiring::corep4x::internal_i2c_sda; constexpr int scl = wiring::corep4x::internal_i2c_scl; - constexpr int_fast16_t i2c_port = I2C_NUM_1; + constexpr int_fast16_t i2c_port = io::i2c_port_1; constexpr std::uint32_t i2c_freq = pmic_ops::pm1_i2c_freq; static const pmic_variant_t power_variants[] = { pmic_variant_ack_only(pmic_ops::pm1_i2c_addr, 0, diff --git a/src/board_detect/m5/esp32p4/tab5.inl b/src/board_detect/m5/esp32p4/tab5.inl index 55aac5e9..e2ea0d42 100644 --- a/src/board_detect/m5/esp32p4/tab5.inl +++ b/src/board_detect/m5/esp32p4/tab5.inl @@ -3,7 +3,7 @@ namespace tab5_detail constexpr int sda = wiring::tab5::internal_i2c_sda; constexpr int scl = wiring::tab5::internal_i2c_scl; constexpr int touch_int = wiring::tab5::touch_int; - constexpr int_fast16_t i2c_port = I2C_NUM_1; + constexpr int_fast16_t i2c_port = io::i2c_port_1; constexpr std::uint32_t i2c_freq = 100000; constexpr std::uint8_t pi4io1_addr = 0x43; constexpr std::uint8_t pi4io2_addr = 0x44; @@ -28,9 +28,9 @@ namespace tab5_detail { std::uint8_t fw_version = 0; std::uint8_t fw_reg[2] = { 0, 0 }; - if (lgfx::i2c::transactionWriteRead(i2c.port, lgfx::Touch_ST7123::default_addr, + if (io::i2c_write_read(i2c.port, io::st_touch_address(), fw_reg, sizeof(fw_reg), &fw_version, 1, - i2c_freq).has_value()) + i2c_freq)) { result.prepared |= read_st_touch_fw; if (fw_version != last_logged_fw) @@ -45,15 +45,13 @@ namespace tab5_detail if (fw_version == 1) { result.prepared |= touch_st7121; break; } if (fw_version == 3) { result.prepared |= touch_st7123; break; } } - else if (lgfx::i2c::beginTransaction(i2c.port, lgfx::Touch_GT911::default_addr_1, - i2c_freq, false).has_value() - && lgfx::i2c::endTransaction(i2c.port).has_value()) + else if (io::i2c_ack(i2c.port, io::gt911_touch_address(), i2c_freq)) { result.prepared |= touch_gt911; ESP_LOGI("M5GFX", "M5Tab5 GT911 touch detected"); break; } - lgfx::delay(10); + io::delay_ms(10); } if (!(result.prepared & (read_st_touch_fw | touch_gt911))) { @@ -145,9 +143,7 @@ public: private: static const board_desc_t* revision_desc() { - esp_chip_info_t info; - esp_chip_info(&info); - return info.revision >= 300 ? &desc_tab5x : &desc_tab5; + return io::chip_revision() >= 300 ? &desc_tab5x : &desc_tab5; } static void warn_hint_mismatch(const probe_ctx_t& ctx, const board_def_t* def) { @@ -155,10 +151,9 @@ private: && (ctx.hint == desc_tab5.def.id || ctx.hint == desc_tab5x.def.id) && ctx.hint != def->id) { - esp_chip_info_t info; - esp_chip_info(&info); + const auto revision = io::chip_revision(); ESP_LOGW("M5GFX", "[Autodetect] Tab5 hint:%u disagrees with chip revision:%u; using %s", - static_cast(ctx.hint), static_cast(info.revision), + static_cast(ctx.hint), static_cast(revision), def->name); } } diff --git a/src/board_detect/m5/esp32p4/unitpoep4.inl b/src/board_detect/m5/esp32p4/unitpoep4.inl index 57e7d94d..45490005 100644 --- a/src/board_detect/m5/esp32p4/unitpoep4.inl +++ b/src/board_detect/m5/esp32p4/unitpoep4.inl @@ -7,11 +7,11 @@ namespace unitpoep4_detail // Open-drain signaling: output latches stay Low; High releases the pin. inline void drive(int pin, bool high) { - if (high) { lgfx::pinMode(pin, lgfx::pin_mode_t::input); } + if (high) { io::pin_mode(pin, io::pin_mode_t::input); } else { - lgfx::gpio_lo(pin); - lgfx::pinMode(pin, lgfx::pin_mode_t::output); + io::pin_low(pin); + io::pin_mode(pin, io::pin_mode_t::output); } } @@ -20,20 +20,20 @@ namespace unitpoep4_detail auto clock = []() { drive(mdc, true); - lgfx::delayMicroseconds(2); + io::delay_us(2); drive(mdc, false); }; auto bit_out = [&](bool bit) { drive(mdio, bit); - lgfx::delayMicroseconds(2); + io::delay_us(2); clock(); }; auto bit_in = [&]() { // IP101 changes MDIO after the rising edge; sample while MDC is Low. - lgfx::delayMicroseconds(2); - const bool bit = lgfx::gpio_in(mdio); + io::delay_us(2); + const bool bit = io::pin_read(mdio); clock(); return bit; }; @@ -76,7 +76,7 @@ public: { if (result == nullptr || ctx.transaction == nullptr) { return false; } const ops::gpio_scope_t scope = { - GPIO_NUM_MAX, desc_unitpoep4.op_gpio_pins.data, desc_unitpoep4.op_gpio_pins.size, + io::pin_count, desc_unitpoep4.op_gpio_pins.data, desc_unitpoep4.op_gpio_pins.size, }; if (!ops::gpio_allowed(scope, unitpoep4_detail::mdc) || !ops::gpio_allowed(scope, unitpoep4_detail::mdio)) { return false; } diff --git a/src/board_detect/m5/esp32s3/cores3.inl b/src/board_detect/m5/esp32s3/cores3.inl index ba3f1f40..e4c12313 100644 --- a/src/board_detect/m5/esp32s3/cores3.inl +++ b/src/board_detect/m5/esp32s3/cores3.inl @@ -151,44 +151,46 @@ constexpr std::uint8_t port0_reg = 0x02; constexpr std::uint8_t bus_en = 0x02; constexpr std::uint32_t freq = 100000; - const auto port0 = lgfx::i2c::readRegister8(port, aw_addr, port0_reg, freq); - if (!port0.has_value()) { return "read failed"; } - if (port0.value() & bus_en) { return nullptr; } - const std::uint8_t off_value = port0.value(); + std::uint8_t port0 = 0; + if (!io::i2c_read_reg8(port, aw_addr, port0_reg, &port0, freq)) { return "read failed"; } + if (port0 & bus_en) { return nullptr; } + const std::uint8_t off_value = port0; enum class off_state_t { unknown, off, on }; const auto confirm_off = [&]() { bool read_ok = false; for (int retry = 0; retry < 3; ++retry) { - lgfx::i2c::writeRegister8(port, aw_addr, port0_reg, off_value, 0, freq); - const auto read = lgfx::i2c::readRegister8(port, aw_addr, port0_reg, freq); - read_ok = read.has_value(); - if (read_ok && !(read.value() & bus_en)) { return off_state_t::off; } + io::i2c_write_reg8(port, aw_addr, port0_reg, off_value, freq); + std::uint8_t read = 0; + read_ok = io::i2c_read_reg8(port, aw_addr, port0_reg, &read, freq); + if (read_ok && !(read & bus_en)) { return off_state_t::off; } } return read_ok ? off_state_t::on : off_state_t::unknown; }; for (std::uint32_t i = 0; i < 8; ++i) { - if (!lgfx::i2c::writeRegister8(port, aw_addr, port0_reg, off_value | bus_en, 0, freq).has_value()) + if (!io::i2c_write_reg8(port, aw_addr, port0_reg, off_value | bus_en, freq)) { const auto state = confirm_off(); if (state == off_state_t::on) { break; } return state == off_state_t::off ? "precharge write failed" : "precharge read failed"; } - lgfx::delayMicroseconds(i * 16); - if (!lgfx::i2c::writeRegister8(port, aw_addr, port0_reg, off_value, 0, freq).has_value()) + io::delay_us(i * 16); + if (!io::i2c_write_reg8(port, aw_addr, port0_reg, off_value, freq)) { const auto state = confirm_off(); if (state == off_state_t::unknown) { return "precharge read failed"; } if (state == off_state_t::on) { break; } } - lgfx::delayMicroseconds(1000); + io::delay_us(1000); } for (int retry = 0; retry < 3; ++retry) { - if (lgfx::i2c::writeRegister8(port, aw_addr, port0_reg, off_value | bus_en, 0, freq).has_value() - && lgfx::i2c::readRegister8(port, aw_addr, port0_reg, freq).value_or(0) == (off_value | bus_en)) + std::uint8_t read = 0; + if (io::i2c_write_reg8(port, aw_addr, port0_reg, off_value | bus_en, freq) + && io::i2c_read_reg8(port, aw_addr, port0_reg, &read, freq) + && read == (off_value | bus_en)) { return nullptr; } @@ -252,21 +254,19 @@ // clocks and STOP. startup_detail::i2c_scope_t i2c(*ctx.transaction, ctx.i2c_port_probe, desc_cores3.internal_i2c); if (!i2c.opened) { return false; } - const auto started = lgfx::millis(); + const auto started = io::millis(); do { #if defined(M5GFX_AUTODETECT_TEST_CORES3_STACKCHAN_NACK) const bool ack = false; #else - const bool began = lgfx::i2c::beginTransaction( - i2c.port, specs::stackchan::i2c_stackchan_ioe::i2c_addr, - specs::stackchan::i2c_stackchan_ioe::i2c_freq, false).has_value(); - const bool ended = lgfx::i2c::endTransaction(i2c.port).has_value(); - const bool ack = began && ended; + const bool ack = io::i2c_ack(i2c.port, + specs::stackchan::i2c_stackchan_ioe::i2c_addr, + specs::stackchan::i2c_stackchan_ioe::i2c_freq, true); #endif // In-flight ACK timeout and START recovery may overrun the 50 ms window. - if (ack || !gate || lgfx::millis() - started >= 50) { return ack; } - lgfx::delay(1); + if (ack || !gate || io::millis() - started >= 50) { return ack; } + io::delay_ms(1); } while (true); #endif } diff --git a/src/board_detect/m5/esp32s3/families.inl b/src/board_detect/m5/esp32s3/families.inl index 33609789..4ec3ca94 100644 --- a/src/board_detect/m5/esp32s3/families.inl +++ b/src/board_detect/m5/esp32s3/families.inl @@ -241,7 +241,7 @@ namespace m5 { nullptr, 0 }, 0), }; // Construction configures the panel TE input before display adoption. - static constexpr std::int8_t stopwatch_te_pin = GPIO_NUM_38; + static constexpr std::int8_t stopwatch_te_pin = 38; static const std::int8_t stopwatch_startup_pins[] = { wiring::stopwatch::display_cs, stopwatch_te_pin, }; @@ -444,7 +444,7 @@ namespace m5 // Both boards have pull-ups on G47/G48. A failed read in the wrong // direction gets one transaction, not the full 200 ms StickS3 poll. // Retry the two orientations under one shared budget for a waking PM1. - const auto started = lgfx::millis(); + const auto started = io::millis(); do { for (auto desc = descriptions_; *desc != nullptr; ++desc) @@ -459,8 +459,8 @@ namespace m5 result->assign(*desc); return true; } - lgfx::delay(1); - } while (lgfx::millis() - started < 200); + io::delay_ms(1); + } while (io::millis() - started < 200); return false; } @@ -489,15 +489,14 @@ namespace m5 desc_papermono.internal_i2c); if (i2c.opened) { - const auto started = lgfx::millis(); + const auto started = io::millis(); do { - if (lgfx::i2c::beginTransaction(i2c.port, papermono_probe_addr, - specs::papermono::touch::i2c_freq, false).has_value() - && lgfx::i2c::endTransaction(i2c.port).has_value()) + if (io::i2c_ack(i2c.port, papermono_probe_addr, + specs::papermono::touch::i2c_freq)) { return true; } - lgfx::delay(1); - } while (lgfx::millis() - started < 200); + io::delay_ms(1); + } while (io::millis() - started < 200); } if (!ctx.final_attempt) { return false; } // Keep the powered member; no second member's power sequence in this attempt. @@ -861,14 +860,14 @@ namespace m5 const auto button = std::uint64_t(1) << 41; if (!(probe_pin_pulls(ctx, button).pulldown_high & button)) { return false; } // AtomS3's backlight PWM input (G16) falls low about 3 us after release. Lite/U have no such pulldown. - lgfx::gpio_hi(16); - lgfx::pinMode(16, lgfx::pin_mode_t::output); - lgfx::delayMicroseconds(10); - lgfx::pinMode(16, lgfx::pin_mode_t::input); - const auto start = lgfx::micros(); - while (lgfx::micros() - start < 2000) + io::pin_high(16); + io::pin_mode(16, io::pin_mode_t::output); + io::delay_us(10); + io::pin_mode(16, io::pin_mode_t::input); + const auto start = io::micros(); + while (io::micros() - start < 2000) { - if (!lgfx::gpio_in(16)) + if (!io::pin_read(16)) { ctx.transaction->restore_start({ 16 }); return false; @@ -892,8 +891,8 @@ namespace m5 { // G46 is Capsule's power hold. M5Unified already asserts it before detection; // asserting here also covers M5GFX standalone startup. - lgfx::gpio_hi(wiring::capsule::power_gpio); - lgfx::pinMode(wiring::capsule::power_gpio, lgfx::pin_mode_t::output); + io::pin_high(wiring::capsule::power_gpio); + io::pin_mode(wiring::capsule::power_gpio, io::pin_mode_t::output); const auto mask = (std::uint64_t(1) << wiring::capsule::internal_i2c_sda) | (std::uint64_t(1) << wiring::capsule::internal_i2c_scl); return (probe_pin_pulls(ctx, mask).pulldown_high & mask) == mask; @@ -1007,18 +1006,18 @@ namespace m5 // former M5Unified GPIO probe without borrowing hardware I2C or LEDC. static bool probe_atoms3r_camera(probe_ctx_t& ctx) { - lgfx::gpio_lo(18); // camera power-down, active high - lgfx::pinMode(18, lgfx::pin_mode_t::output); - lgfx::gpio_lo(9); - lgfx::pinMode(9, lgfx::pin_mode_t::output); - lgfx::gpio_lo(12); - lgfx::pinMode(12, lgfx::pin_mode_t::output); - lgfx::gpio_hi(21); - lgfx::pinMode(21, lgfx::pin_mode_t::output); - lgfx::gpio_hi(9); - lgfx::gpio_hi(12); - auto lo = m5gfx::get_gpio_lo_reg(GPIO_NUM_21); - auto hi = m5gfx::get_gpio_hi_reg(GPIO_NUM_21); + io::pin_low(18); // camera power-down, active high + io::pin_mode(18, io::pin_mode_t::output); + io::pin_low(9); + io::pin_mode(9, io::pin_mode_t::output); + io::pin_low(12); + io::pin_mode(12, io::pin_mode_t::output); + io::pin_high(21); + io::pin_mode(21, io::pin_mode_t::output); + io::pin_high(9); + io::pin_high(12); + auto lo = io::pin_low_register(21); + auto hi = io::pin_high_register(21); const auto clock_xclk = [&](int cycles) { for (; cycles != 0; --cycles) @@ -1043,7 +1042,7 @@ namespace m5 } if (cycle & 1) { - if (cycle == 17) { nack = lgfx::gpio_in(12); } + if (cycle == 17) { nack = io::pin_read(12); } } else { @@ -1051,8 +1050,8 @@ namespace m5 addr <<= 1; if (cycle >= 16) { - lgfx::pinMode(12, (cycle == 16) ? lgfx::pin_mode_t::input - : lgfx::pin_mode_t::output); + io::pin_mode(12, (cycle == 16) ? io::pin_mode_t::input + : io::pin_mode_t::output); } } } diff --git a/src/board_detect/ops.inl b/src/board_detect/ops.inl index c6787f8a..55137439 100644 --- a/src/board_detect/ops.inl +++ b/src/board_detect/ops.inl @@ -10,98 +10,90 @@ namespace board_detect { namespace ops { - struct lgfx_backend_context_t + struct io_backend_context_t { const int* ports; std::size_t port_count; }; - inline int lgfx_port(void* context, const i2c_device_t& device) + inline int io_port(void* context, const i2c_device_t& device) { - auto* value = static_cast(context); + auto* value = static_cast(context); return value != nullptr && device.bus_id < value->port_count ? value->ports[device.bus_id] : -1; } - inline bool lgfx_i2c_read8(void* context, const i2c_device_t& device, + inline bool io_i2c_read8(void* context, const i2c_device_t& device, std::uint16_t reg, std::uint8_t* value) { - const auto read = lgfx::i2c::readRegister8(lgfx_port(context, device), device.addr, - static_cast(reg), device.freq_hz); - if (!read.has_value()) { return false; } - *value = read.value(); - return true; + return io::i2c_read_reg8(io_port(context, device), device.addr, + static_cast(reg), value, device.freq_hz); } - inline bool lgfx_i2c_write8(void* context, const i2c_device_t& device, + inline bool io_i2c_write8(void* context, const i2c_device_t& device, std::uint16_t reg, std::uint8_t value) { - // mask 0 is lgfx's full-write path and deliberately performs no read. - return lgfx::i2c::writeRegister8(lgfx_port(context, device), device.addr, - static_cast(reg), value, 0, - device.freq_hz).has_value(); + // Write the full register byte without reading it first. + return io::i2c_write_reg8(io_port(context, device), device.addr, static_cast(reg), value, device.freq_hz); } - inline bool lgfx_i2c_write16le(void* context, const i2c_device_t& device, + inline bool io_i2c_write16le(void* context, const i2c_device_t& device, std::uint16_t reg, std::uint16_t value) { const std::uint8_t data[] = { static_cast(reg), static_cast(value), static_cast(value >> 8), }; - return lgfx::i2c::transactionWrite(lgfx_port(context, device), device.addr, - data, sizeof(data), device.freq_hz).has_value(); + return io::i2c_write(io_port(context, device), device.addr, + data, sizeof(data), device.freq_hz); } - inline bool lgfx_i2c_ready(void* context, const i2c_device_t& device, + inline bool io_i2c_ready(void* context, const i2c_device_t& device, std::uint32_t timeout_ms) { - // lgfx has no per-call timeout. Do not begin another fixed-time transaction + // The backend has no per-call timeout. Do not begin another fixed-time transaction // once the runner's deadline is reached. An in-flight attempt's overrun // depends on the lower I2C implementation: hardware is about 26 ms, while // software I2C may take tens of milliseconds with SCL stuck. if (timeout_ms == 0) { return false; } - const auto port = lgfx_port(context, device); - const bool began = lgfx::i2c::beginTransaction(port, device.addr, - device.freq_hz, false).has_value(); - const bool ended = lgfx::i2c::endTransaction(port).has_value(); - return began && ended; + const auto port = io_port(context, device); + return io::i2c_ack(port, device.addr, device.freq_hz, true); } - inline void lgfx_wait_ready_finished(void*, const i2c_device_t& device, + inline void io_wait_ready_finished(void*, const i2c_device_t& device, std::uint32_t elapsed_ms, bool ready) { ESP_LOGD("board_detect", "power wait_ready addr=0x%02x elapsed=%ums ready=%u", device.addr, static_cast(elapsed_ms), ready); } - inline bool lgfx_gpio_set_mode(void*, std::uint16_t pin, gpio_mode_t mode) + inline bool io_gpio_set_mode(void*, std::uint16_t pin, gpio_mode_t mode) { - lgfx::pin_mode_t native = lgfx::pin_mode_t::output; + io::pin_mode_t native = io::pin_mode_t::output; switch (mode) { - case gpio_mode_t::input: native = lgfx::pin_mode_t::input; break; - case gpio_mode_t::input_pullup: native = lgfx::pin_mode_t::input_pullup; break; - case gpio_mode_t::input_pulldown: native = lgfx::pin_mode_t::input_pulldown; break; + case gpio_mode_t::input: native = io::pin_mode_t::input; break; + case gpio_mode_t::input_pullup: native = io::pin_mode_t::input_pullup; break; + case gpio_mode_t::input_pulldown: native = io::pin_mode_t::input_pulldown; break; default: break; } - lgfx::pinMode(pin, native); + io::pin_mode(pin, native); return true; } - inline bool lgfx_gpio_write(void*, std::uint16_t pin, bool high) + inline bool io_gpio_write(void*, std::uint16_t pin, bool high) { - if (high) { lgfx::gpio_hi(pin); } - else { lgfx::gpio_lo(pin); } + if (high) { io::pin_high(pin); } + else { io::pin_low(pin); } return true; } - inline void lgfx_delay_ms(void*, std::uint32_t milliseconds) { lgfx::delay(milliseconds); } - inline std::uint32_t lgfx_millis(void*) { return lgfx::millis(); } + inline void io_delay_ms(void*, std::uint32_t milliseconds) { io::delay_ms(milliseconds); } + inline std::uint32_t io_millis(void*) { return io::millis(); } - inline backend_t lgfx_backend(lgfx_backend_context_t* context) + inline backend_t io_backend(io_backend_context_t* context) { - return { lgfx_i2c_read8, lgfx_i2c_write8, lgfx_i2c_write16le, lgfx_i2c_ready, - lgfx_gpio_set_mode, lgfx_gpio_write, lgfx_delay_ms, lgfx_millis, context }; + return { io_i2c_read8, io_i2c_write8, io_i2c_write16le, io_i2c_ready, + io_gpio_set_mode, io_gpio_write, io_delay_ms, io_millis, context }; } } } diff --git a/src/board_detect/probe_hardware.inl b/src/board_detect/probe_hardware.inl index 55dce357..c3223ac5 100644 --- a/src/board_detect/probe_hardware.inl +++ b/src/board_detect/probe_hardware.inl @@ -1,49 +1,49 @@ bool wait_i2c_scl_high(int pin_scl) { - lgfx::gpio_hi(pin_scl); - lgfx::delayMicroseconds(5); - if (lgfx::gpio_in(pin_scl)) { return true; } - const auto started = lgfx::micros(); - while (lgfx::micros() - started < 25000) - { - if (lgfx::gpio_in(pin_scl)) { return true; } - lgfx::delayMicroseconds(1); + io::pin_high(pin_scl); + io::delay_us(5); + if (io::pin_read(pin_scl)) { return true; } + const auto started = io::micros(); + while (io::micros() - started < 25000) + { + if (io::pin_read(pin_scl)) { return true; } + io::delay_us(1); } return false; } bool release_held_sda(int pin_sda, int pin_scl) { - lgfx::pinMode(pin_sda, lgfx::pin_mode_t::input); - lgfx::pinMode(pin_scl, lgfx::pin_mode_t::input); - lgfx::delayMicroseconds(10); + io::pin_mode(pin_sda, io::pin_mode_t::input); + io::pin_mode(pin_scl, io::pin_mode_t::input); + io::delay_us(10); // A reset of this MCU during a transfer can leave a peripheral holding // SDA low while it waits for more clocks. Clock it out (up to 9 bits) and // finish with STOP. Input mode is open drain with the output latch high. - bool bus_released = lgfx::gpio_in(pin_scl); - if (bus_released && !lgfx::gpio_in(pin_sda)) + bool bus_released = io::pin_read(pin_scl); + if (bus_released && !io::pin_read(pin_sda)) { - for (int i = 0; i < 9 && !lgfx::gpio_in(pin_sda); ++i) + for (int i = 0; i < 9 && !io::pin_read(pin_sda); ++i) { - lgfx::gpio_lo(pin_scl); - lgfx::delayMicroseconds(5); + io::pin_low(pin_scl); + io::delay_us(5); if (!wait_i2c_scl_high(pin_scl)) { bus_released = false; break; } } if (bus_released) { - lgfx::gpio_lo(pin_scl); - lgfx::gpio_lo(pin_sda); - lgfx::delayMicroseconds(5); + io::pin_low(pin_scl); + io::pin_low(pin_sda); + io::delay_us(5); bus_released = wait_i2c_scl_high(pin_scl); if (bus_released) { - lgfx::gpio_hi(pin_sda); - lgfx::delayMicroseconds(10); + io::pin_high(pin_sda); + io::delay_us(10); } } } - const bool released = bus_released && lgfx::gpio_in(pin_sda) && lgfx::gpio_in(pin_scl); + const bool released = bus_released && io::pin_read(pin_sda) && io::pin_read(pin_scl); return released; } @@ -79,8 +79,8 @@ // A floating pin is raised by the internal pull-up during sample() and // never reaches this path. Only an externally held-low SCL receives the // legacy soft-I2C clock-stretch allowance. - lgfx::pinMode(pin_sda, lgfx::pin_mode_t::input); - lgfx::pinMode(pin_scl, lgfx::pin_mode_t::input); + io::pin_mode(pin_sda, io::pin_mode_t::input); + io::pin_mode(pin_scl, io::pin_mode_t::input); wait_i2c_scl_high(pin_scl); ctx.transaction->restore_start({ pin_sda, pin_scl }); }; @@ -98,9 +98,9 @@ bool probe_pin_pullup_low(probe_ctx_t& ctx, int pin) { - lgfx::pinMode(pin, lgfx::pin_mode_t::input_pullup); - lgfx::delayMicroseconds(10); - const bool held_low = !lgfx::gpio_in(pin); + io::pin_mode(pin, io::pin_mode_t::input_pullup); + io::delay_us(10); + const bool held_low = !io::pin_read(pin); ctx.transaction->restore_start({ pin }); return held_low; } @@ -116,22 +116,22 @@ cache.pin_sda = pin_sda; cache.pin_scl = pin_scl; - lgfx::pinMode(pin_sda, lgfx::pin_mode_t::input_pulldown); - lgfx::pinMode(pin_scl, lgfx::pin_mode_t::input_pulldown); - lgfx::delayMicroseconds(10); - lgfx::pinMode(pin_sda, lgfx::pin_mode_t::input); - lgfx::pinMode(pin_scl, lgfx::pin_mode_t::input); - lgfx::delayMicroseconds(10); + io::pin_mode(pin_sda, io::pin_mode_t::input_pulldown); + io::pin_mode(pin_scl, io::pin_mode_t::input_pulldown); + io::delay_us(10); + io::pin_mode(pin_sda, io::pin_mode_t::input); + io::pin_mode(pin_scl, io::pin_mode_t::input); + io::delay_us(10); // The first check does not wait: a pin pair without pull-ups reads SCL // low here, and waiting for it would delay every such detection. Only a // bus that has pull-ups and a held SDA is clocked, and only those clocks // wait for a stretching peripheral. - bool bus_released = lgfx::gpio_in(pin_scl); - if (bus_released && !lgfx::gpio_in(pin_sda)) + bool bus_released = io::pin_read(pin_scl); + if (bus_released && !io::pin_read(pin_sda)) { bus_released = release_held_sda(pin_sda, pin_scl); } - cache.pullup_ok = bus_released && lgfx::gpio_in(pin_sda) && lgfx::gpio_in(pin_scl); + cache.pullup_ok = bus_released && io::pin_read(pin_sda) && io::pin_read(pin_scl); ctx.transaction->restore_start({ pin_sda, pin_scl }); } @@ -141,12 +141,11 @@ cache.checked[addr >> 5] |= bit; if (cache.pullup_ok) { - if (lgfx::i2c::init(ctx.i2c_port_probe, pin_sda, pin_scl).has_value()) + if (io::i2c_open(ctx.i2c_port_probe, pin_sda, pin_scl)) { - const bool hit = lgfx::i2c::beginTransaction(ctx.i2c_port_probe, addr, 100000, false).has_value() - && lgfx::i2c::endTransaction(ctx.i2c_port_probe).has_value(); + const bool hit = io::i2c_ack(ctx.i2c_port_probe, addr, 100000); if (hit) { cache.ack[addr >> 5] |= bit; } - lgfx::i2c::release(ctx.i2c_port_probe); + io::i2c_close(ctx.i2c_port_probe); } ctx.transaction->restore_start({ pin_sda, pin_scl }); } @@ -159,21 +158,21 @@ std::uint32_t freq, std::uint32_t poll_ms, bool reg16) { if (data == nullptr || length == 0 || addr < 0x08 || addr > 0x77) { return false; } - if (!lgfx::i2c::init(ctx.i2c_port_probe, pin_sda, pin_scl).has_value()) { return false; } + if (!io::i2c_open(ctx.i2c_port_probe, pin_sda, pin_scl)) { return false; } const std::uint8_t reg_bytes[] = { static_cast(reg >> 8), static_cast(reg), }; - const auto started = lgfx::millis(); + const auto started = io::millis(); bool success = false; do { - success = lgfx::i2c::transactionWriteRead( + success = io::i2c_write_read( ctx.i2c_port_probe, addr, reg_bytes + !reg16, 1 + reg16, - data, length, freq).has_value(); + data, length, freq); if (success) { break; } - lgfx::delay(1); - } while (lgfx::millis() - started < poll_ms); - lgfx::i2c::release(ctx.i2c_port_probe); + io::delay_ms(1); + } while (io::millis() - started < poll_ms); + io::i2c_close(ctx.i2c_port_probe); ctx.transaction->restore_start({ pin_sda, pin_scl }); return success; } @@ -190,29 +189,40 @@ if (!(pin_mask & bit)) { continue; } // Measure one pin completely before touching the next. On a native-mode // SD bus this avoids raising CLK while CMD is temporarily pulled low. - lgfx::pinMode(pin, lgfx::pin_mode_t::input_pulldown); - lgfx::delayMicroseconds(10); - if (lgfx::gpio_in(pin)) { result.pulldown_high |= bit; } + io::pin_mode(pin, io::pin_mode_t::input_pulldown); + io::delay_us(10); + if (io::pin_read(pin)) { result.pulldown_high |= bit; } if (release_us != 0) { - lgfx::pinMode(pin, lgfx::pin_mode_t::input); - lgfx::delayMicroseconds(release_us); - if (lgfx::gpio_in(pin)) { result.pulldown_release_high |= bit; } + io::pin_mode(pin, io::pin_mode_t::input); + io::delay_us(release_us); + if (io::pin_read(pin)) { result.pulldown_release_high |= bit; } } - lgfx::pinMode(pin, lgfx::pin_mode_t::input_pullup); - lgfx::delayMicroseconds(10); - if (lgfx::gpio_in(pin)) { result.pullup_high |= bit; } + io::pin_mode(pin, io::pin_mode_t::input_pullup); + io::delay_us(10); + if (io::pin_read(pin)) { result.pullup_high |= bit; } if (release_us != 0) { - lgfx::pinMode(pin, lgfx::pin_mode_t::input); - lgfx::delayMicroseconds(release_us); - if (lgfx::gpio_in(pin)) { result.pullup_release_high |= bit; } + io::pin_mode(pin, io::pin_mode_t::input); + io::delay_us(release_us); + if (io::pin_read(pin)) { result.pullup_release_high |= bit; } } ctx.transaction->restore_start(static_cast(pin)); } return result; } + static bool close_dedicated_release_bundle(io::dedicated_gpio_handle_t bundle) + { + const int error = io::dedicated_gpio_close(bundle); + if (error != 0) + { + ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO bundle cleanup failed (%d)", error); + return false; + } + return true; + } + dedicated_release_result_t probe_dedicated_pin_release( probe_ctx_t& ctx, const std::int8_t* pins, std::uint8_t pin_count, std::uint16_t reads, std::uint8_t samples, std::uint32_t settle_us) @@ -222,7 +232,11 @@ result.requested_samples = samples; result.reads = reads; for (auto& value : result.median_first_high) { value = dedicated_release_no_high; } -#if defined (M5GFX_HAS_S3_DEDICATED_GPIO_RELEASE_PROBE) + if (!io::dedicated_gpio_supported) + { + ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO release probe is unavailable in this SDK"); + return result; + } if (ctx.transaction == nullptr || pins == nullptr || pin_count == 0 || pin_count > max_dedicated_release_pins || reads == 0 || reads > max_dedicated_release_reads || samples == 0 @@ -231,9 +245,9 @@ ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO probe has invalid parameters"); return result; } - const int pinned_core = dedicated_release_task_core(); - if (pinned_core == static_cast(tskNO_AFFINITY) - || dedicated_release_core_id() != pinned_core) + const int pinned_core = io::dedicated_release_task_core(); + if (pinned_core == io::no_core_affinity() + || io::dedicated_release_core_id() != pinned_core) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO probe requires a core-pinned task"); return result; @@ -245,7 +259,7 @@ // new_bundle are not atomic: applications that initialize dedicated GPIO // concurrently on another core during this brief M5.begin probe are not // supported. - if (REG_GET_BIT(SYSTEM_CPU_PERI_CLK_EN_REG, SYSTEM_CLK_EN_DEDICATED_GPIO)) + if (io::dedicated_gpio_in_use()) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO in use; skipping release probe"); return result; @@ -254,7 +268,7 @@ for (std::uint8_t index = 0; index < pin_count; ++index) { const int pin = pins[index]; - if (pin < 0 || pin >= GPIO_NUM_MAX || !GPIO_IS_VALID_GPIO(pin)) + if (!io::pin_valid(pin)) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO probe has invalid pin (%d)", pin); return result; @@ -267,50 +281,42 @@ // input_pullup retains output-enable but sets OD with a High latch, so // the pad is Hi-Z. Prepare the weakest drive before new_bundle connects // its active output route to avoid a transient push-pull/strong drive. - lgfx::pinMode(pin, lgfx::pin_mode_t::input_pullup); - const auto io_mux = GPIO_PIN_MUX_REG[pin]; - REG_WRITE(io_mux, (REG_READ(io_mux) & ~(FUN_PU | FUN_PD | FUN_DRV_M)) | FUN_PU); - gpio_ll_od_enable(&GPIO, static_cast(pin)); + io::pin_weak_pullup_open_drain(pin); } - dedic_gpio_bundle_handle_t bundle = nullptr; - dedic_gpio_bundle_config_t config = {}; - config.gpio_array = bundle_pins; - config.array_size = pin_count; - config.flags.in_en = 1; - config.flags.out_en = 1; - const int creation_core = dedicated_release_core_id(); + io::dedicated_gpio_handle_t bundle; + const int creation_core = io::dedicated_release_core_id(); if (creation_core != pinned_core) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO probe task migrated before bundle creation"); ctx.transaction->restore_start(pins, pin_count); return result; } - const esp_err_t bundle_error = dedic_gpio_new_bundle(&config, &bundle); - if (bundle_error != ESP_OK) + const int bundle_error = io::dedicated_gpio_open(bundle_pins, pin_count, &bundle); + if (bundle_error != 0) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO bundle creation failed (%d)", int(bundle_error)); ctx.transaction->restore_start(pins, pin_count); return result; } - if (dedicated_release_core_id() != pinned_core) + if (io::dedicated_release_core_id() != pinned_core) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO probe task migrated after bundle creation"); - delete_dedicated_release_bundle(bundle); + close_dedicated_release_bundle(bundle); ctx.transaction->restore_start(pins, pin_count); return result; } std::uint32_t input_mask = 0; std::uint32_t output_mask = 0; - esp_err_t input_mask_error = ESP_OK; - esp_err_t output_mask_error = ESP_OK; - dedicated_release_channel_masks(bundle, pin_count, &input_mask, &output_mask, + int input_mask_error = 0; + int output_mask_error = 0; + io::dedicated_release_channel_masks(bundle, pin_count, &input_mask, &output_mask, &input_mask_error, &output_mask_error); - if (input_mask_error != ESP_OK || output_mask_error != ESP_OK) + if (input_mask_error != 0 || output_mask_error != 0) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO channel lookup failed (in=%d out=%d)", int(input_mask_error), int(output_mask_error)); - delete_dedicated_release_bundle(bundle); + close_dedicated_release_bundle(bundle); ctx.transaction->restore_start(pins, pin_count); return result; } @@ -320,30 +326,30 @@ || output_mask == 0) { ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO channel allocation is not contiguous"); - delete_dedicated_release_bundle(bundle); + close_dedicated_release_bundle(bundle); ctx.transaction->restore_start(pins, pin_count); return result; } - static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + static io::critical_section_t mux; const auto measure = [&](std::uint16_t* first_high, std::uint32_t* cycles) { volatile std::uint32_t raw[max_dedicated_release_reads]; - taskENTER_CRITICAL(&mux); + mux.enter(); // A dedicated bundle belongs to its creation core. Check immediately // after scheduling is stopped and reject the measurement if it migrated. - if (dedicated_release_core_id() != creation_core) + if (io::dedicated_release_core_id() != creation_core) { - taskEXIT_CRITICAL(&mux); + mux.exit(); return false; } - dedicated_release_write_mask(output_mask, 0); - lgfx::delayMicroseconds(settle_us); - const std::uint32_t started = dedicated_release_cycle_count(); - dedicated_release_write_mask(output_mask, output_mask); + io::dedicated_release_write_mask(output_mask, 0); + io::delay_us(settle_us); + const std::uint32_t started = io::dedicated_release_cycle_count(); + io::dedicated_release_write_mask(output_mask, output_mask); for (std::uint16_t read = 0; read < reads; ++read) - { raw[read] = dedicated_release_read_in(); } - const std::uint32_t stopped = dedicated_release_cycle_count(); - taskEXIT_CRITICAL(&mux); + { raw[read] = io::dedicated_release_read_in(); } + const std::uint32_t stopped = io::dedicated_release_cycle_count(); + mux.exit(); *cycles = stopped - started; for (std::uint8_t index = 0; index < pin_count; ++index) { @@ -373,7 +379,7 @@ } if (complete) { - result.cpu_hz = static_cast(esp_clk_cpu_freq()); + result.cpu_hz = io::cpu_hz(); result.available = result.cpu_hz != 0 && result.total_cycles != 0; if (result.cpu_hz == 0) { @@ -401,98 +407,91 @@ ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO probe task migrated during capture"); } - if (!delete_dedicated_release_bundle(bundle)) { result.available = false; } + if (!close_dedicated_release_bundle(bundle)) { result.available = false; } // del_bundle pin cleanup differs by IDF version (4.4/5.2/5.3 leave the // pads alone; newer versions disable output), and out_sel is not restored. // Dedicated input routing is peripheral-side. In every version, - // The transaction's restore_start (pin_backup_t::restore) restores out_sel, + // The transaction's restore_start (pin_state_t::restore) restores out_sel, // GPIO_PINn, output-enable, latch, and the complete IO_MUX register // (including temporary FUN_DRV). ctx.transaction->restore_start(pins, pin_count); return result; -#else - ESP_LOGW("M5GFX", "[Autodetect] dedicated GPIO release probe is unavailable in this SDK"); - (void)ctx; - (void)pins; - (void)settle_us; - return result; -#endif } void soft_spi_t::init() { - lgfx::gpio_lo(pin_sclk_); - lgfx::pinMode(pin_sclk_, lgfx::pin_mode_t::output); - lgfx::gpio_lo(pin_mosi_); - lgfx::pinMode(pin_mosi_, lgfx::pin_mode_t::output); - if (pin_miso_ != pin_mosi_) { lgfx::pinMode(pin_miso_, lgfx::pin_mode_t::input); } - if (pin_dc_ >= 0) { lgfx::pinMode(pin_dc_, lgfx::pin_mode_t::output); } + io::pin_low(pin_sclk_); + io::pin_mode(pin_sclk_, io::pin_mode_t::output); + io::pin_low(pin_mosi_); + io::pin_mode(pin_mosi_, io::pin_mode_t::output); + if (pin_miso_ != pin_mosi_) { io::pin_mode(pin_miso_, io::pin_mode_t::input); } + if (pin_dc_ >= 0) { io::pin_mode(pin_dc_, io::pin_mode_t::output); } } void soft_spi_t::beginTransaction() { - lgfx::gpio_lo(pin_sclk_); + io::pin_low(pin_sclk_); } void soft_spi_t::endTransaction() { endRead(); - lgfx::gpio_lo(pin_sclk_); + io::pin_low(pin_sclk_); } void soft_spi_t::clock() { - lgfx::delayMicroseconds(half_us_); - lgfx::gpio_hi(pin_sclk_); - lgfx::delayMicroseconds(half_us_); - lgfx::gpio_lo(pin_sclk_); - lgfx::delayMicroseconds(half_us_); + io::delay_us(half_us_); + io::pin_high(pin_sclk_); + io::delay_us(half_us_); + io::pin_low(pin_sclk_); + io::delay_us(half_us_); } void soft_spi_t::send(std::uint32_t data, std::uint_fast8_t bits) { for (std::uint_fast8_t i = 0; i < bits; ++i) { - if (data & (std::uint32_t(1) << bit_index(i))) { lgfx::gpio_hi(pin_mosi_); } - else { lgfx::gpio_lo(pin_mosi_); } + if (data & (std::uint32_t(1) << bit_index(i))) { io::pin_high(pin_mosi_); } + else { io::pin_low(pin_mosi_); } clock(); } } void soft_spi_t::writeCommand(std::uint32_t data, std::uint_fast8_t bits) { - if (pin_dc_ >= 0) { lgfx::gpio_lo(pin_dc_); } + if (pin_dc_ >= 0) { io::pin_low(pin_dc_); } send(data, bits); } void soft_spi_t::writeData(std::uint32_t data, std::uint_fast8_t bits) { - if (pin_dc_ >= 0) { lgfx::gpio_hi(pin_dc_); } + if (pin_dc_ >= 0) { io::pin_high(pin_dc_); } send(data, bits); } std::uint32_t soft_spi_t::transferData(std::uint32_t data, std::uint_fast8_t bits) { - if (pin_dc_ >= 0) { lgfx::gpio_hi(pin_dc_); } + if (pin_dc_ >= 0) { io::pin_high(pin_dc_); } std::uint32_t value = 0; for (std::uint_fast8_t i = 0; i < bits; ++i) { const auto index = bit_index(i); - if (data & (std::uint32_t(1) << index)) { lgfx::gpio_hi(pin_mosi_); } - else { lgfx::gpio_lo(pin_mosi_); } - lgfx::delayMicroseconds(half_us_); - if (lgfx::gpio_in(pin_miso_)) { value |= std::uint32_t(1) << index; } - lgfx::gpio_hi(pin_sclk_); - lgfx::delayMicroseconds(half_us_); - lgfx::gpio_lo(pin_sclk_); + if (data & (std::uint32_t(1) << index)) { io::pin_high(pin_mosi_); } + else { io::pin_low(pin_mosi_); } + io::delay_us(half_us_); + if (io::pin_read(pin_miso_)) { value |= std::uint32_t(1) << index; } + io::pin_high(pin_sclk_); + io::delay_us(half_us_); + io::pin_low(pin_sclk_); } return value; } void soft_spi_t::beginRead(std::uint_fast8_t dummy_bits) { - if (pin_dc_ >= 0) { lgfx::gpio_hi(pin_dc_); } - if (pin_miso_ == pin_mosi_) { lgfx::pinMode(pin_mosi_, lgfx::pin_mode_t::input); } + if (pin_dc_ >= 0) { io::pin_high(pin_dc_); } + if (pin_miso_ == pin_mosi_) { io::pin_mode(pin_mosi_, io::pin_mode_t::input); } for (std::uint_fast8_t i = 0; i < dummy_bits; ++i) { clock(); } } @@ -501,11 +500,11 @@ std::uint32_t value = 0; for (std::uint_fast8_t i = 0; i < bits; ++i) { - lgfx::delayMicroseconds(half_us_); - if (lgfx::gpio_in(pin_miso_)) { value |= std::uint32_t(1) << bit_index(i); } - lgfx::gpio_hi(pin_sclk_); - lgfx::delayMicroseconds(half_us_); - lgfx::gpio_lo(pin_sclk_); + io::delay_us(half_us_); + if (io::pin_read(pin_miso_)) { value |= std::uint32_t(1) << bit_index(i); } + io::pin_high(pin_sclk_); + io::delay_us(half_us_); + io::pin_low(pin_sclk_); } return value; } @@ -521,8 +520,8 @@ { // Input mode leaves the latch high. Clear it before driving MOSI again // to avoid a long high pulse on shared data lines such as WS2812 inputs. - lgfx::gpio_lo(pin_mosi_); - lgfx::pinMode(pin_mosi_, lgfx::pin_mode_t::output); + io::pin_low(pin_mosi_); + io::pin_mode(pin_mosi_, io::pin_mode_t::output); } } @@ -537,23 +536,23 @@ // selecting the panel. ESP32 PICO display probes retain that exact read. bus.init(); bus.beginTransaction(); - lgfx::gpio_hi(pin_cs); - lgfx::pinMode(pin_cs, lgfx::pin_mode_t::output); + io::pin_high(pin_cs); + io::pin_mode(pin_cs, io::pin_mode_t::output); bus.writeCommand(0, 8); bus.wait(); } else { - lgfx::gpio_hi(pin_cs); - lgfx::pinMode(pin_cs, lgfx::pin_mode_t::output); + io::pin_high(pin_cs); + io::pin_mode(pin_cs, io::pin_mode_t::output); bus.init(); bus.beginTransaction(); } - lgfx::gpio_lo(pin_cs); + io::pin_low(pin_cs); bus.writeCommand(cmd, 8); bus.beginRead(dummy_bits); const auto value = bus.readData(32); - lgfx::gpio_hi(pin_cs); + io::pin_high(pin_cs); bus.endTransaction(); // CS is owned by the caller. Restore only the shared bus signals that this diff --git a/src/board_detect/startup.inl b/src/board_detect/startup.inl index b4a77750..3c1bf787 100644 --- a/src/board_detect/startup.inl +++ b/src/board_detect/startup.inl @@ -4,9 +4,9 @@ { public: explicit retry_budget_t(std::uint8_t milliseconds) - : started_(lgfx::millis()), milliseconds_(milliseconds) {} + : started_(io::millis()), milliseconds_(milliseconds) {} - bool exhausted() const { return lgfx::millis() - started_ >= milliseconds_; } + bool exhausted() const { return io::millis() - started_ >= milliseconds_; } std::uint32_t deadline_ms() const { return started_ + milliseconds_; } private: @@ -16,21 +16,15 @@ void pin_level(int pin, bool high) { - if (high) { lgfx::gpio_hi(pin); } - else { lgfx::gpio_lo(pin); } - lgfx::pinMode(pin, lgfx::pin_mode_t::output); + if (high) { io::pin_high(pin); } + else { io::pin_low(pin); } + io::pin_mode(pin, io::pin_mode_t::output); } void hold_pin_level(int pin, bool high) { - const auto bit = std::uint32_t(1) << (pin & 31); -#if !defined (SOC_GPIO_PIN_COUNT) || SOC_GPIO_PIN_COUNT > 32 - const bool output = pin & 32 ? REG_READ(GPIO_ENABLE1_REG) & bit : REG_READ(GPIO_ENABLE_REG) & bit; - const bool level = pin & 32 ? REG_READ(GPIO_OUT1_REG) & bit : REG_READ(GPIO_OUT_REG) & bit; -#else - const bool output = REG_READ(GPIO_ENABLE_REG) & bit; - const bool level = REG_READ(GPIO_OUT_REG) & bit; -#endif + bool level = false; + const bool output = io::pin_output_level(pin, &level); if (output && level == high) { return; } // Set the output latch before enabling the driver, including after M5Unified prehold. pin_level(pin, high); @@ -42,14 +36,9 @@ { for (;;) { - const auto result = lgfx::i2c::readRegister8(port, addr, reg, power.i2c_freq); - if (result.has_value()) - { - *value = result.value(); - return true; - } + if (io::i2c_read_reg8(port, addr, reg, value, power.i2c_freq)) { return true; } if (retry_budget.exhausted()) { return false; } - lgfx::delay(1); + io::delay_ms(1); } } @@ -67,12 +56,12 @@ const ops::retry_policy_t* policy = nullptr) { const int ports[] = { port }; - ops::lgfx_backend_context_t backend_context { ports, 1 }; - const ops::gpio_scope_t gpio_scope { GPIO_NUM_MAX, gpio_pins.data, gpio_pins.size }; - return ops::run_ops(ops::lgfx_backend(&backend_context), + ops::io_backend_context_t backend_context { ports, 1 }; + const ops::gpio_scope_t gpio_scope { io::pin_count, gpio_pins.data, gpio_pins.size }; + return ops::run_ops(ops::io_backend(&backend_context), power.devices, power.device_count, sequence.data, sequence.size, gpio_scope, policy, - ops::lgfx_wait_ready_finished); + ops::io_wait_ready_finished); } const pmic_variant_t* read_variant(const power_desc_t& power, int port, @@ -115,7 +104,7 @@ pin_level(sd.sd_cs, true); for (int i = 0; i < 10; ++i) { bus.writeData(0xFF, 8); } - lgfx::gpio_lo(sd.sd_cs); + io::pin_low(sd.sd_cs); static const std::uint8_t cmd58[] = { 0x7A, 0, 0, 0, 0, 0xFD, 0xFF, 0xFF }; std::uint8_t response[sizeof(cmd58)]; for (std::size_t i = 0; i < sizeof(cmd58); ++i) @@ -124,27 +113,27 @@ } if (response[6] == response[7]) { - lgfx::gpio_hi(sd.sd_cs); + io::pin_high(sd.sd_cs); for (int i = 0; i < 10; ++i) { bus.writeData(0xFF, 8); } - lgfx::gpio_lo(sd.sd_cs); + io::pin_low(sd.sd_cs); static const std::uint8_t cmd0[] = { 0x40, 0, 0, 0, 0, 0x95, 0xFF, 0xFF }; for (auto value : cmd0) { bus.transferData(value, 8); } } - lgfx::gpio_hi(sd.sd_cs); + io::pin_high(sd.sd_cs); bus.endTransaction(); ctx.transaction->restore_start({ sd.sclk, sd.mosi, sd.miso }); } void pin_reset(const reset_desc_t& reset, bool active) { - lgfx::gpio_hi(reset.pin); - lgfx::pinMode(reset.pin, lgfx::pin_mode_t::output); - lgfx::delay(1); + io::pin_high(reset.pin); + io::pin_mode(reset.pin, io::pin_mode_t::output); + io::delay_ms(1); if (!active) { return; } - lgfx::gpio_lo(reset.pin); - lgfx::delay(reset.low_ms); - lgfx::gpio_hi(reset.pin); - lgfx::delay(reset.post_ms); + io::pin_low(reset.pin); + io::delay_ms(reset.low_ms); + io::pin_high(reset.pin); + io::delay_ms(reset.post_ms); } class i2c_scope_t @@ -153,12 +142,12 @@ i2c_scope_t(detection_transaction_t& transaction_, int port_, const i2c_desc_t& wiring) : transaction(transaction_), port(port_), pins { wiring.sda, wiring.scl } { - opened = lgfx::i2c::init(port, wiring.sda, wiring.scl).has_value(); + opened = io::i2c_open(port, wiring.sda, wiring.scl); } ~i2c_scope_t() { - if (opened) { lgfx::i2c::release(port); } + if (opened) { io::i2c_close(port); } transaction.restore_start(pins); } @@ -243,7 +232,7 @@ static_cast(power_result.status), 0); } if (power_was_off) { result.prepared &= ~prepared_sd_spi; } - lgfx::delay(power_was_off || reset_was_low ? power.cold_wait_ms : power.warm_wait_ms); + io::delay_ms(power_was_off || reset_was_low ? power.cold_wait_ms : power.warm_wait_ms); result.prepared |= prepared_power; return true; } @@ -285,7 +274,7 @@ bool gpio_valid(int pin) { - return pin >= 0 && pin < GPIO_NUM_MAX && GPIO_IS_VALID_GPIO(pin); + return io::pin_valid(pin); } bool optional_gpio_valid(int pin) { return pin == -1 || gpio_valid(pin); } @@ -377,7 +366,7 @@ { const auto& variant = desc.power.variants[i]; const ops::gpio_scope_t gpio_scope { - GPIO_NUM_MAX, desc.op_gpio_pins.data, desc.op_gpio_pins.size + io::pin_count, desc.op_gpio_pins.data, desc.op_gpio_pins.size }; valid = list_valid(variant.power_on) && list_valid(variant.reset_assert) @@ -455,7 +444,7 @@ static_cast(release_result.status), 0); } - lgfx::delay(reset.post_ms); + io::delay_ms(reset.post_ms); } else { @@ -492,7 +481,7 @@ } if (ok || retain_confirmed_board) { - lgfx::delay(reset.low_ms); + io::delay_ms(reset.low_ms); startup_detail::retry_budget_t release_budget(desc.power.wake_poll_ms); const ops::retry_policy_t release_policy { release_budget.deadline_ms(), 1 }; const auto release_result = startup_detail::run_sequence( @@ -506,7 +495,7 @@ static_cast(release_result.failed_index), static_cast(release_result.status), 0); } - if (ok || retain_confirmed_board) { lgfx::delay(reset.post_ms); } + if (ok || retain_confirmed_board) { io::delay_ms(reset.post_ms); } } if (retain_confirmed_board) { ok = true; } } diff --git a/src/board_detect/transaction.inl b/src/board_detect/transaction.inl index 15263293..e74381da 100644 --- a/src/board_detect/transaction.inl +++ b/src/board_detect/transaction.inl @@ -1,4 +1,4 @@ - static void restore_if_changed(lgfx::gpio::pin_backup_t& saved) + static void restore_if_changed(io::pin_state_t& saved) { // restore() briefly disables output driving. Skip it when every saved // register already matches so an unchanged output cannot momentarily float. @@ -23,13 +23,12 @@ for (std::size_t index = 0; index < list.size; ++index) { const auto pin = list.data[index]; - if (pin < 0 || pin >= GPIO_NUM_MAX || !GPIO_IS_VALID_GPIO(pin)) { return false; } + if (!io::pin_valid(pin)) { return false; } for (std::size_t prior = 0; prior < count_; ++prior) { - if (saved_[prior].getPin() == pin) { return false; } + if (saved_[prior].pin() == pin) { return false; } } - saved_[count_].setPin(pin); - saved_[count_].backup(); + saved_[count_].save(pin); ++count_; } } @@ -38,7 +37,7 @@ // only after the complete snapshot succeeds. for (std::size_t index = 0; index < count_; ++index) { - lgfx::gpio::release_lp_pad(saved_[index].getPin()); + io::release_lp_pad(saved_[index].pin()); } return true; } @@ -63,11 +62,11 @@ const int pin = pins[requested]; if (pin < 0) { continue; } // an unused optional signal bool found = false; - if (pin >= 0 && pin < GPIO_NUM_MAX && GPIO_IS_VALID_GPIO(pin)) + if (io::pin_valid(pin)) { for (std::size_t index = count_; index != 0; --index) { - if (saved_[index - 1].getPin() == pin) { found = true; break; } + if (saved_[index - 1].pin() == pin) { found = true; break; } } } if (!found && !missing) { missing = true; missing_pin = pin; } @@ -75,7 +74,7 @@ // Match full rollback ordering while restoring each captured pin at most once. for (std::size_t index = count_; index != 0; --index) { - const auto pin = saved_[index - 1].getPin(); + const auto pin = saved_[index - 1].pin(); for (std::size_t requested = 0; requested < count; ++requested) { if (pins[requested] == pin) diff --git a/test/board_detect/README.md b/test/board_detect/README.md index 3ec51243..3433eea7 100644 --- a/test/board_detect/README.md +++ b/test/board_detect/README.md @@ -57,11 +57,14 @@ production function text or redeclare reduced model types. Internal includes separate transactions, probing, startup, SPI selection, preparation, package selection, and integration without rewriting their bodies. -The runner defines explicit host-only include macros for the ESP32 common header -and classic touch peripheral substitutes. Without those macros production keeps -its original relative includes and resolution within M5GFX. The low-level GPIO/I2C backend -records operations, clocks, snapshots, and responses. Family tests replace the -GPIO/I2C/SPI/dedicated-GPIO probes; timing tests compile the real probe instead. +The runner selects `support/detect_io.hpp` through +`M5GFX_BOARD_DETECT_IO_HEADER`. It records GPIO/I2C operations, clocks, +snapshots, and responses without SDK headers or firmware driver result types. +`support/native_devices_io.hpp` adapts native device calls only for construction +fixtures and test setup; production detection calls the selected I/O directly. +Family tests replace GPIO/I2C/SPI/dedicated-GPIO probes; timing tests compile the +real probe instead. Classic touch/LED transport returns the simulated raw counts, +while detection decisions remain production code. Transactions, candidate selection, startup validation, power/refine policy, and session policy remain production implementations. Family probes check expected I2C SDA/SCL pins where configured; multi-bus Cardputer probes select their expected @@ -125,9 +128,10 @@ assertions are independent of the fixture update option. - Catalog invariants need rule checks for new entries: no-display eligibility, active-high GPIO holds, reverse rollback order, LP-pad release at capture, and absence of hardcoded board selection in legacy autodetect. -- The host configuration does not compile S3 dedicated-GPIO release probing, - ToughC5 LP-I2C, the <=32-pin hold branch, or ESP-IDF 4.4 branches. -- Stub `pin_backup_t` and I2C response types can drift from the real HAL; +- The host configuration compiles S3 dedicated-GPIO release probing against + stubs but does not execute it. It does not compile ToughC5 LP-I2C, + the <=32-pin hold branch, or ESP-IDF 4.4 branches. +- The in-memory I/O implementation can drift from the firmware implementation; firmware compilation remains necessary to detect API changes. The pin-coverage tests call each family's signature and confirmation separately @@ -136,7 +140,7 @@ success scenarios: the S3 test supplies real SPI ID bits and checks the later Cardputer sense reads, both I2C buses and both SDA recovery clock sequences. They use the production probe implementations, not the response-level probe doubles. Snapshot membership comes from capture events, while GPIO mode/read/output, restore and -hardware-I2C pad selection are observed through the HAL. Low/high, floating and +hardware-I2C pad selection are observed through the I/O implementation. Low/high, floating and mixed GPIO responses, I2C response bytes and every member hint exercise different paths; this is a snapshot containment check, not exhaustive hardware simulation. Peripheral-specific touch/LED transport remains a host boundary, as in the diff --git a/test/board_detect/run.sh b/test/board_detect/run.sh index 053c97c7..e3e12290 100644 --- a/test/board_detect/run.sh +++ b/test/board_detect/run.sh @@ -12,9 +12,9 @@ failed=0 for source in "$root"/test/board_detect/*.cpp; do name=$(basename "$source" .cpp) "${CXX:-c++}" -std=c++11 -O2 -Wall -Wextra -Wno-unused-function -Wno-unused-parameter -pedantic \ - '-DM5GFX_BOARD_DETECT_HOST_HAL="lgfx/v1/platforms/esp32/common.hpp"' \ - '-DM5GFX_BOARD_DETECT_HOST_TOUCH="board_detect/m5/atom_touch.inl"' \ - -I"$root/test/board_detect/stubs" -I"$root/src" \ + '-DM5GFX_BOARD_DETECT_IO_HEADER="support/detect_io.hpp"' \ + -include "$root/test/board_detect/support/native_devices_io.hpp" \ + -I"$root/test/board_detect" -I"$root/test/board_detect/stubs" -I"$root/src" \ "$source" -o "$out/$name" if (cd "$root" "$out/$name"); then diff --git a/test/board_detect/stubs/board_detect/m5/atom_touch.inl b/test/board_detect/stubs/board_detect/m5/atom_touch.inl deleted file mode 100644 index c75d4d09..00000000 --- a/test/board_detect/stubs/board_detect/m5/atom_touch.inl +++ /dev/null @@ -1,16 +0,0 @@ -// Touch peripheral and WS2812 transport only; the Atom detector remains production code. -namespace atom_touch { -static bool measure(std::uint16_t *a, std::uint16_t *b) { - ++::host_touch::scans; - for (const auto& pad : {t4_pad, t7_pad}) - ::host_hardware::events.push_back({0, pad.gpio, 0}); - *a = ::host_touch::t4; - *b = ::host_touch::t7; - return ::host_touch::available; -} -static void clear_led() { - ++::host_touch::clears; - ::host_hardware::events.push_back({0, led_gpio, unsigned(lgfx::output)}); - ::host_hardware::events.push_back({4, led_gpio, 0}); -} -} // namespace atom_touch diff --git a/test/board_detect/stubs/driver/gpio.h b/test/board_detect/stubs/driver/gpio.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/driver/gpio.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/esp_attr.h b/test/board_detect/stubs/esp_attr.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/esp_attr.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/esp_chip_info.h b/test/board_detect/stubs/esp_chip_info.h deleted file mode 100644 index 58c0829c..00000000 --- a/test/board_detect/stubs/esp_chip_info.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -namespace host_chip { -static int revision; -} -struct esp_chip_info_t { - int revision; -}; -inline void esp_chip_info(esp_chip_info_t *info) { info->revision = host_chip::revision; } diff --git a/test/board_detect/stubs/esp_cpu.h b/test/board_detect/stubs/esp_cpu.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/esp_cpu.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/esp_log.h b/test/board_detect/stubs/esp_log.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/esp_log.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/esp_rom_sys.h b/test/board_detect/stubs/esp_rom_sys.h deleted file mode 100644 index 068ac2e1..00000000 --- a/test/board_detect/stubs/esp_rom_sys.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -#include -inline void esp_rom_delay_us(unsigned us) { lgfx::delayMicroseconds(us); } diff --git a/test/board_detect/stubs/esp_timer.h b/test/board_detect/stubs/esp_timer.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/esp_timer.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp b/test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp deleted file mode 100644 index 10058268..00000000 --- a/test/board_detect/stubs/lgfx/v1/platforms/esp32/common.hpp +++ /dev/null @@ -1,278 +0,0 @@ -// Host hardware boundary. Production descriptions and detector types are included unchanged. -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include "lgfx/boards.hpp" - -enum { - I2C_NUM_0 = 0, - I2C_NUM_1 = 1, - SPI2_HOST = 1, - SPI3_HOST = 2, - GPIO_NUM_MAX = 64, - GPIO_NUM_NC = -1, - GPIO_NUM_0 = 0, - GPIO_NUM_1 = 1, - GPIO_NUM_2 = 2, - GPIO_NUM_3 = 3, - GPIO_NUM_4 = 4, - GPIO_NUM_5 = 5, - GPIO_NUM_6 = 6, - GPIO_NUM_7 = 7, - GPIO_NUM_8 = 8, - GPIO_NUM_9 = 9, - GPIO_NUM_10 = 10, - GPIO_NUM_11 = 11, - GPIO_NUM_12 = 12, - GPIO_NUM_13 = 13, - GPIO_NUM_14 = 14, - GPIO_NUM_15 = 15, - GPIO_NUM_16 = 16, - GPIO_NUM_17 = 17, - GPIO_NUM_18 = 18, - GPIO_NUM_19 = 19, - GPIO_NUM_20 = 20, - GPIO_NUM_21 = 21, - GPIO_NUM_22 = 22, - GPIO_NUM_23 = 23, - GPIO_NUM_24 = 24, - GPIO_NUM_25 = 25, - GPIO_NUM_26 = 26, - GPIO_NUM_27 = 27, - GPIO_NUM_28 = 28, - GPIO_NUM_29 = 29, - GPIO_NUM_30 = 30, - GPIO_NUM_31 = 31, - GPIO_NUM_32 = 32, - GPIO_NUM_33 = 33, - GPIO_NUM_34 = 34, - GPIO_NUM_35 = 35, - GPIO_NUM_36 = 36, - GPIO_NUM_37 = 37, - GPIO_NUM_38 = 38, - GPIO_NUM_39 = 39, - GPIO_NUM_40 = 40, - GPIO_NUM_41 = 41, - GPIO_NUM_42 = 42, - GPIO_NUM_43 = 43, - GPIO_NUM_44 = 44, - GPIO_NUM_45 = 45, - GPIO_NUM_46 = 46, - GPIO_NUM_47 = 47, - GPIO_NUM_48 = 48, - GPIO_NUM_49 = 49, - GPIO_NUM_50 = 50, - GPIO_NUM_51 = 51, - GPIO_NUM_52 = 52, - GPIO_NUM_53 = 53, - GPIO_NUM_54 = 54, - GPIO_NUM_55 = 55, - GPIO_NUM_56 = 56, - GPIO_NUM_57 = 57, - GPIO_NUM_58 = 58, - GPIO_NUM_59 = 59, - GPIO_NUM_60 = 60, - GPIO_NUM_61 = 61, - GPIO_NUM_62 = 62, - GPIO_NUM_63 = 63 -}; -#define GPIO_IS_VALID_GPIO(pin) ((pin) >= 0 && (pin) < GPIO_NUM_MAX) -namespace host_hardware { -struct event_t { - int kind, pin; - unsigned value; - bool operator==(const event_t &b) const { - return kind == b.kind && pin == b.pin && value == b.value; - } -}; -struct pin_t { - int mode = 0; - bool high = false; - bool operator==(const pin_t &b) const { return mode == b.mode && high == b.high; } -}; -static pin_t gpio[64]; -static std::vector events; -static std::uint32_t now_us; -static std::function input; -static std::function wire; -static std::map> ports; -static int flash_capacity = 0, psram_capacity = 0, package = 0; -static unsigned warnings; -static bool i2c_open_ok = true; -template void log(Args...) { ++warnings; } -template void quiet_log(Args...) {} -inline void reset() { - for (auto &pin : gpio) - pin = {}; - events.clear(); - now_us = 0; - input = {}; - wire = {}; - ports.clear(); - warnings = 0; - i2c_open_ok = true; -} -inline unsigned reg(int which) { - if (which == 100) - return unsigned(flash_capacity) | (unsigned(psram_capacity) << 3); - unsigned value = 0; - for (int bit = 0; bit < 32; ++bit) { - auto &p = gpio[(which & 1) * 32 + bit]; - if (which < 2 ? p.mode == 3 : p.high) - value |= 1u << bit; - } - return value; -} -struct response_t { - bool ok; - int data; - bool has_value() const { return ok; } - int value() const { return data; } - int value_or(int fallback) const { return ok ? data : fallback; } -}; -} // namespace host_hardware -#define ESP_LOGE(...) host_hardware::log(__VA_ARGS__) -#define ESP_LOGW(...) host_hardware::log(__VA_ARGS__) -#define ESP_LOGI(...) host_hardware::quiet_log(__VA_ARGS__) -#define ESP_LOGD(...) host_hardware::quiet_log(__VA_ARGS__) -#define REG_GET_FIELD(...) host_hardware::flash_capacity -#define REG_READ(register_index) host_hardware::reg(register_index) -#define GPIO_ENABLE_REG 0 -#define GPIO_ENABLE1_REG 1 -#define GPIO_OUT_REG 2 -#define GPIO_OUT1_REG 3 -namespace lgfx { -enum pin_mode_t { - input = 0, - input_pullup = 1, - input_pulldown = 2, - output = 3, - output_open_drain = 4 -}; -inline void pinMode(int pin, pin_mode_t mode) { - if (pin < 0) - return; - host_hardware::gpio[pin].mode = mode; - host_hardware::events.push_back({0, pin, unsigned(mode)}); -} -inline void gpio_lo(int pin) { - if (pin >= 0) { - host_hardware::gpio[pin].high = false; - host_hardware::events.push_back({4, pin, 0}); - } -} -inline void gpio_hi(int pin) { - if (pin >= 0) { - host_hardware::gpio[pin].high = true; - host_hardware::events.push_back({4, pin, 1}); - } -} -inline bool gpio_in(int pin) { - assert(pin >= 0); - auto &p = host_hardware::gpio[pin]; - bool high = host_hardware::input ? host_hardware::input(pin, p.mode, p.high) : p.high; - host_hardware::events.push_back({2, pin, unsigned(high)}); - return high; -} -inline std::uint32_t millis() { return host_hardware::now_us / 1000; } -inline std::uint32_t micros() { return host_hardware::now_us; } -inline void delay(unsigned long ms) { - host_hardware::now_us += ms * 1000; - host_hardware::events.push_back({1, -1, unsigned(ms * 1000)}); -} -inline void delayMicroseconds(unsigned us) { - host_hardware::now_us += us; - host_hardware::events.push_back({1, -1, us}); -} -namespace gpio { -struct pin_backup_t { - int pin = -1; - host_hardware::pin_t saved; - int getPin() const { return pin; } - void setPin(int p) { pin = p; } - void backup() { saved = host_hardware::gpio[pin]; } - bool matches_current() const { return saved == host_hardware::gpio[pin]; } - void restore() { - host_hardware::gpio[pin] = saved; - host_hardware::events.push_back({3, pin, 0}); - } -}; -inline void release_lp_pad(int pin) { host_hardware::events.push_back({5, pin, 0}); } -} // namespace gpio -namespace i2c { -using host_hardware::response_t; -inline bool isInitialized(int port) { return host_hardware::ports.count(port) != 0; } -inline response_t init(int port, int sda, int scl) { - if (!host_hardware::i2c_open_ok) - return {false, 0}; - host_hardware::events.push_back({7, sda, 0}); - host_hardware::events.push_back({7, scl, 0}); - host_hardware::ports[port] = {sda, scl}; - return {true, 0}; -} -inline response_t release(int port) { - host_hardware::ports.erase(port); - host_hardware::events.push_back({6, port, 0}); - return {true, 0}; -} -inline response_t getPinSDA(int port) { - return {isInitialized(port), host_hardware::ports[port].first}; -} -inline response_t getPinSCL(int port) { - return {isInitialized(port), host_hardware::ports[port].second}; -} -inline response_t readRegister8(int, int addr, int reg, int) { - std::uint8_t data = 0; - bool ok = host_hardware::wire && host_hardware::wire(addr, reg, &data, 1, false); - return {ok, data}; -} -inline response_t writeRegister8(int, int addr, int reg, int value, int, int) { - std::uint8_t data = value; - bool ok = host_hardware::wire && host_hardware::wire(addr, reg, &data, 1, true); - return {ok, 0}; -} -inline response_t beginTransaction(int, int addr, int, bool) { - std::uint8_t data = 0; - return {host_hardware::wire && host_hardware::wire(addr, -1, &data, 0, false), 0}; -} -inline response_t endTransaction(int) { return {true, 0}; } -inline response_t transactionWrite(int, int addr, const std::uint8_t *data, std::size_t len, int) { - return {host_hardware::wire && - host_hardware::wire(addr, len ? data[0] : -1, const_cast(data + 1), - len ? len - 1 : 0, true), - 0}; -} -inline response_t transactionWriteRead(int, int addr, const std::uint8_t *reg, std::size_t reglen, - std::uint8_t *data, std::size_t len, int) { - return {host_hardware::wire && host_hardware::wire(addr, reglen ? reg[0] : -1, data, len, false), - 0}; -} -} // namespace i2c -} // namespace lgfx - -namespace lgfx { -inline volatile std::uint32_t *get_gpio_lo_reg(int) { - static std::uint32_t reg; - return ® -} -inline volatile std::uint32_t *get_gpio_hi_reg(int) { - static std::uint32_t reg; - return ® -} -} // namespace lgfx -namespace m5gfx { -using namespace lgfx; -} - -#define EFUSE_RD_MAC_SYS2_REG 100 -#define EFUSE_FLASH_CAP_S 0 -#define EFUSE_FLASH_CAP_V 7 -#define EFUSE_PSRAM_CAP_S 3 -#define EFUSE_PSRAM_CAP_V 3 -#define ESP_IDF_VERSION_VAL(a, b, c) (((a) << 16) | ((b) << 8) | (c)) -#define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(5, 5, 0) diff --git a/test/board_detect/stubs/soc/gpio_reg.h b/test/board_detect/stubs/soc/gpio_reg.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/soc/gpio_reg.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/soc/gpio_struct.h b/test/board_detect/stubs/soc/gpio_struct.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/soc/gpio_struct.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/soc/io_mux_reg.h b/test/board_detect/stubs/soc/io_mux_reg.h deleted file mode 100644 index 93151812..00000000 --- a/test/board_detect/stubs/soc/io_mux_reg.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -#define IO_MUX_GPIO20_REG 4 -#define REG_WRITE(register_index, value) ((void)(register_index), (void)(value)) diff --git a/test/board_detect/stubs/soc/rtc.h b/test/board_detect/stubs/soc/rtc.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/soc/rtc.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/soc/rtc_cntl_reg.h b/test/board_detect/stubs/soc/rtc_cntl_reg.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/soc/rtc_cntl_reg.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/soc/rtc_io_reg.h b/test/board_detect/stubs/soc/rtc_io_reg.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/soc/rtc_io_reg.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/soc/sens_reg.h b/test/board_detect/stubs/soc/sens_reg.h deleted file mode 100644 index c7ac90de..00000000 --- a/test/board_detect/stubs/soc/sens_reg.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/test/board_detect/stubs/soc/usb_serial_jtag_reg.h b/test/board_detect/stubs/soc/usb_serial_jtag_reg.h deleted file mode 100644 index 6f70f09b..00000000 --- a/test/board_detect/stubs/soc/usb_serial_jtag_reg.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/test/board_detect/support/detect_io.hpp b/test/board_detect/support/detect_io.hpp new file mode 100644 index 00000000..cb18b11a --- /dev/null +++ b/test/board_detect/support/detect_io.hpp @@ -0,0 +1,219 @@ +// In-memory detection I/O; independent of the firmware's device implementation. +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "lgfx/boards.hpp" +namespace host_hardware { +struct event_t { + int kind, pin; + unsigned value; + bool operator==(const event_t &b) const { + return kind == b.kind && pin == b.pin && value == b.value; + } +}; +struct pin_t { + int mode = 0; + bool high = false; + bool operator==(const pin_t &b) const { return mode == b.mode && high == b.high; } +}; +static pin_t gpio[64]; +static std::vector events; +static std::uint32_t now_us; +static std::function input; +static std::function wire; +static std::map> ports; +static int flash_capacity = 0, psram_capacity = 0, package = 0; +static unsigned warnings; +static bool i2c_open_ok = true; +static bool psram_active; +template void log(Args...) { ++warnings; } +template void quiet_log(Args...) {} +inline void reset() { + for (auto &pin : gpio) + pin = {}; + events.clear(); + now_us = 0; + input = {}; + wire = {}; + ports.clear(); + warnings = 0; + i2c_open_ok = true; +} +inline unsigned reg(int which) { + if (which == 100) + return unsigned(flash_capacity) | (unsigned(psram_capacity) << 3); + unsigned value = 0; + for (int bit = 0; bit < 32; ++bit) { + auto &p = gpio[(which & 1) * 32 + bit]; + if (which < 2 ? p.mode == 3 : p.high) + value |= 1u << bit; + } + return value; +} +struct response_t { + bool ok; + int data; + bool has_value() const { return ok; } + int value() const { return data; } + int value_or(int fallback) const { return ok ? data : fallback; } +}; +} // namespace host_hardware + +namespace host_touch { +static std::uint16_t t4 = 100, t7 = 25; +static bool available = true; +static unsigned scans, clears; +} +namespace host_chip { static int revision; } +namespace m5gfx { namespace board_detect { namespace io { + static constexpr int pin_count = 64; + static constexpr int i2c_port_0 = 0, i2c_port_1 = 1, low_power_i2c_port_0 = 0; + static constexpr unsigned package_classic_q6 = 0, package_classic_pico_d4 = 5; + inline bool pin_valid(int pin) { return pin >= 0 && pin < pin_count; } + inline int host_mode(pin_mode_t mode) + { + switch (mode) { + case pin_mode_t::input: return 0; + case pin_mode_t::input_pullup: return 1; + case pin_mode_t::input_pulldown: return 2; + default: return 3; + } + } + inline void pin_mode(int pin, pin_mode_t mode) + { + if (pin < 0) return; + const auto value = host_mode(mode); + host_hardware::gpio[pin].mode = value; + host_hardware::events.push_back({0, pin, unsigned(value)}); + } + inline void pin_low(int pin) + { + if (pin >= 0) { + host_hardware::gpio[pin].high = false; + host_hardware::events.push_back({4, pin, 0}); + } + } + inline void pin_high(int pin) + { + if (pin >= 0) { + host_hardware::gpio[pin].high = true; + host_hardware::events.push_back({4, pin, 1}); + } + } + inline bool pin_read(int pin) + { + assert(pin >= 0); + auto &p = host_hardware::gpio[pin]; + const bool high = host_hardware::input ? host_hardware::input(pin, p.mode, p.high) : p.high; + host_hardware::events.push_back({2, pin, unsigned(high)}); + return high; + } + inline bool pin_output_level(int pin, bool* level) + { + *level = host_hardware::gpio[pin].high; + return host_hardware::gpio[pin].mode == 3; + } + class pin_state_t { + int pin_ = -1; + host_hardware::pin_t saved_; + public: + int pin() const { return pin_; } + void save(int pin) { pin_ = pin; saved_ = host_hardware::gpio[pin]; } + bool matches_current() const { return saved_ == host_hardware::gpio[pin_]; } + void restore() { + host_hardware::gpio[pin_] = saved_; + host_hardware::events.push_back({3, pin_, 0}); + } + }; + inline void release_lp_pad(int pin) { host_hardware::events.push_back({5, pin, 0}); } + inline volatile std::uint32_t* pin_low_register(int) { static std::uint32_t r; return &r; } + inline volatile std::uint32_t* pin_high_register(int) { static std::uint32_t r; return &r; } + inline std::uint32_t cpu_cycles() { return host_hardware::now_us * 240; } + inline std::uint32_t cpu_hz() { return 240000000; } + inline std::uint32_t millis() { return host_hardware::now_us / 1000; } + inline std::uint32_t micros() { return host_hardware::now_us; } + inline void delay_us(std::uint32_t us) { + host_hardware::now_us += us; + host_hardware::events.push_back({1, -1, us}); + } + inline void delay_ms(std::uint32_t ms) { delay_us(ms * 1000); } + inline void delay_rom_us(std::uint32_t us) { delay_us(us); } + inline bool i2c_open(int port, int sda, int scl) { + if (!host_hardware::i2c_open_ok) return false; + host_hardware::events.push_back({7, sda, 0}); + host_hardware::events.push_back({7, scl, 0}); + host_hardware::ports[port] = {sda, scl}; + return true; + } + inline void i2c_close(int port) { + host_hardware::ports.erase(port); + host_hardware::events.push_back({6, port, 0}); + } + inline bool i2c_ack(int, std::uint16_t addr, std::uint32_t, bool = false) { + std::uint8_t data = 0; + return host_hardware::wire && host_hardware::wire(addr, -1, &data, 0, false); + } + inline bool i2c_read_reg8(int, std::uint16_t addr, std::uint8_t reg, + std::uint8_t* value, std::uint32_t) { + std::uint8_t data = 0; + const bool ok = host_hardware::wire && host_hardware::wire(addr, reg, &data, 1, false); + if (ok) *value = data; + return ok; + } + inline bool i2c_write_reg8(int, std::uint16_t addr, std::uint8_t reg, + std::uint8_t value, std::uint32_t) { + return host_hardware::wire && host_hardware::wire(addr, reg, &value, 1, true); + } + inline bool i2c_write(int, std::uint16_t addr, const std::uint8_t* data, + std::size_t len, std::uint32_t) { + return host_hardware::wire && host_hardware::wire(addr, len ? data[0] : -1, + const_cast(data + 1), len ? len - 1 : 0, true); + } + inline bool i2c_write_read(int, std::uint16_t addr, const std::uint8_t* reg, + std::size_t reglen, std::uint8_t* data, std::size_t len, + std::uint32_t) { + return host_hardware::wire && host_hardware::wire(addr, reglen ? reg[0] : -1, data, len, false); + } + inline unsigned package_version() { return host_hardware::package; } + inline bool psram_ready() { return host_hardware::psram_active; } + inline unsigned chip_revision() { return host_chip::revision; } + inline unsigned flash_capacity_code() { return host_hardware::flash_capacity; } + inline void module_memory_capacity(unsigned* flash, unsigned* psram) + { *flash = host_hardware::flash_capacity; *psram = host_hardware::psram_capacity; } + inline std::uint8_t st_touch_address() { return 0x55; } + inline std::uint8_t gt911_touch_address() { return 0x14; } + inline unsigned usb_serial_jtag_frame() { return host_hardware::flash_capacity; } + inline bool uart_rx_held_high() { pin_mode(20, pin_mode_t::input_pulldown); return pin_read(20); } + static constexpr bool dedicated_gpio_supported = false; + inline std::uint32_t dedicated_release_read_in() { return 0; } + inline void dedicated_release_write_mask(std::uint32_t, std::uint32_t) {} + inline void dedicated_release_channel_masks(dedicated_gpio_handle_t, std::uint8_t, + std::uint32_t*, std::uint32_t*, int*, int*) {} + inline int dedicated_release_core_id() { return 0; } + inline std::uint32_t dedicated_release_cycle_count() { return 0; } + inline int dedicated_release_task_core() { return 0; } + inline int no_core_affinity() { return -1; } + inline bool dedicated_gpio_in_use() { return false; } + inline void pin_weak_pullup_open_drain(int) {} + inline int dedicated_gpio_open(const int*, std::uint8_t, dedicated_gpio_handle_t*) { return -1; } + inline int dedicated_gpio_close(dedicated_gpio_handle_t) { return -1; } + class critical_section_t { public: void enter() {} void exit() {} }; + template + inline bool classic_touch_measure(std::uint16_t* a, std::uint16_t* b) { + ++host_touch::scans; + for (const auto pin : {pin_a, pin_b}) host_hardware::events.push_back({0, pin, 0}); + *a = host_touch::t4; *b = host_touch::t7; + return host_touch::available; + } + template + inline void classic_led_clear() { + ++host_touch::clears; + host_hardware::events.push_back({0, pin, 3}); + host_hardware::events.push_back({4, pin, 0}); + } +}}} diff --git a/test/board_detect/support/native_devices_io.hpp b/test/board_detect/support/native_devices_io.hpp new file mode 100644 index 00000000..e9d6f614 --- /dev/null +++ b/test/board_detect/support/native_devices_io.hpp @@ -0,0 +1,123 @@ +// Native device calls used only by construction fixtures and test setup. +#pragma once +#include "board_detect/detect_io.hpp" +enum { + I2C_NUM_0 = 0, + I2C_NUM_1 = 1, + SPI2_HOST = 1, + SPI3_HOST = 2, + GPIO_NUM_MAX = 64, + GPIO_NUM_NC = -1, + GPIO_NUM_0 = 0, + GPIO_NUM_1 = 1, + GPIO_NUM_2 = 2, + GPIO_NUM_3 = 3, + GPIO_NUM_4 = 4, + GPIO_NUM_5 = 5, + GPIO_NUM_6 = 6, + GPIO_NUM_7 = 7, + GPIO_NUM_8 = 8, + GPIO_NUM_9 = 9, + GPIO_NUM_10 = 10, + GPIO_NUM_11 = 11, + GPIO_NUM_12 = 12, + GPIO_NUM_13 = 13, + GPIO_NUM_14 = 14, + GPIO_NUM_15 = 15, + GPIO_NUM_16 = 16, + GPIO_NUM_17 = 17, + GPIO_NUM_18 = 18, + GPIO_NUM_19 = 19, + GPIO_NUM_20 = 20, + GPIO_NUM_21 = 21, + GPIO_NUM_22 = 22, + GPIO_NUM_23 = 23, + GPIO_NUM_24 = 24, + GPIO_NUM_25 = 25, + GPIO_NUM_26 = 26, + GPIO_NUM_27 = 27, + GPIO_NUM_28 = 28, + GPIO_NUM_29 = 29, + GPIO_NUM_30 = 30, + GPIO_NUM_31 = 31, + GPIO_NUM_32 = 32, + GPIO_NUM_33 = 33, + GPIO_NUM_34 = 34, + GPIO_NUM_35 = 35, + GPIO_NUM_36 = 36, + GPIO_NUM_37 = 37, + GPIO_NUM_38 = 38, + GPIO_NUM_39 = 39, + GPIO_NUM_40 = 40, + GPIO_NUM_41 = 41, + GPIO_NUM_42 = 42, + GPIO_NUM_43 = 43, + GPIO_NUM_44 = 44, + GPIO_NUM_45 = 45, + GPIO_NUM_46 = 46, + GPIO_NUM_47 = 47, + GPIO_NUM_48 = 48, + GPIO_NUM_49 = 49, + GPIO_NUM_50 = 50, + GPIO_NUM_51 = 51, + GPIO_NUM_52 = 52, + GPIO_NUM_53 = 53, + GPIO_NUM_54 = 54, + GPIO_NUM_55 = 55, + GPIO_NUM_56 = 56, + GPIO_NUM_57 = 57, + GPIO_NUM_58 = 58, + GPIO_NUM_59 = 59, + GPIO_NUM_60 = 60, + GPIO_NUM_61 = 61, + GPIO_NUM_62 = 62, + GPIO_NUM_63 = 63 +}; +#define GPIO_IS_VALID_GPIO(pin) ((pin) >= 0 && (pin) < GPIO_NUM_MAX) + +#define ESP_LOGE(...) host_hardware::log(__VA_ARGS__) +#define ESP_LOGW(...) host_hardware::log(__VA_ARGS__) +#define ESP_LOGI(...) host_hardware::quiet_log(__VA_ARGS__) +#define ESP_LOGD(...) host_hardware::quiet_log(__VA_ARGS__) +#define ESP_IDF_VERSION_VAL(a, b, c) (((a) << 16) | ((b) << 8) | (c)) +#define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(5, 5, 0) +namespace lgfx { + enum pin_mode_t { input, input_pullup, input_pulldown, output, output_open_drain }; + inline void pinMode(int pin, pin_mode_t mode) { + using m5gfx::board_detect::io::pin_mode_t; + const auto value = mode == input ? pin_mode_t::input : mode == input_pullup ? pin_mode_t::input_pullup + : mode == input_pulldown ? pin_mode_t::input_pulldown : pin_mode_t::output; + m5gfx::board_detect::io::pin_mode(pin, value); + } + inline void gpio_lo(int pin) { m5gfx::board_detect::io::pin_low(pin); } + inline void gpio_hi(int pin) { m5gfx::board_detect::io::pin_high(pin); } + inline bool gpio_in(int pin) { return m5gfx::board_detect::io::pin_read(pin); } + inline void delay(unsigned long ms) { m5gfx::board_detect::io::delay_ms(ms); } + inline void delayMicroseconds(unsigned us) { m5gfx::board_detect::io::delay_us(us); } + inline std::uint32_t millis() { return m5gfx::board_detect::io::millis(); } + inline std::uint32_t micros() { return m5gfx::board_detect::io::micros(); } +namespace i2c { + using host_hardware::response_t; + inline bool isInitialized(int port) { return host_hardware::ports.count(port) != 0; } + inline response_t init(int port, int sda, int scl) + { return {m5gfx::board_detect::io::i2c_open(port, sda, scl), 0}; } + inline response_t release(int port) { m5gfx::board_detect::io::i2c_close(port); return {true,0}; } + inline response_t readRegister8(int port, int addr, int reg, int freq) { + std::uint8_t value = 0; + const bool ok = m5gfx::board_detect::io::i2c_read_reg8(port, addr, reg, &value, freq); + return {ok,value}; + } + inline response_t writeRegister8(int port, int addr, int reg, int value, int, int freq) + { return {m5gfx::board_detect::io::i2c_write_reg8(port, addr, reg, value, freq),0}; } + inline response_t beginTransaction(int port, int addr, int freq, bool) + { return {m5gfx::board_detect::io::i2c_ack(port, addr, freq),0}; } + inline response_t endTransaction(int) { return {true,0}; } + inline response_t transactionWrite(int port, int addr, const std::uint8_t* data, std::size_t len, int freq) + { return {m5gfx::board_detect::io::i2c_write(port,addr,data,len,freq),0}; } + inline response_t transactionWriteRead(int port, int addr, const std::uint8_t* reg, std::size_t rlen, + std::uint8_t* data, std::size_t len, int freq) + { return {m5gfx::board_detect::io::i2c_write_read(port,addr,reg,rlen,data,len,freq),0}; } +} +} + +namespace m5gfx { using lgfx::board_t; } diff --git a/test/board_detect/support/package_selection.hpp b/test/board_detect/support/package_selection.hpp index 715e2ec9..b2636e94 100644 --- a/test/board_detect/support/package_selection.hpp +++ b/test/board_detect/support/package_selection.hpp @@ -13,9 +13,9 @@ #include "display_backend.hpp" static unsigned package_id; static bool psram_active; -static bool esp_psram_is_initialized() { return psram_active; } + namespace m5gfx { -static unsigned get_pkg_ver() { return package_id; } + static const char LIBRARY_NAME[] = "host"; using lgfx::board_t; #include "board_detect/detection_package.inl" @@ -28,6 +28,8 @@ int main() { for (bool legacy : {false, true}) { host_hardware::reset(); psram_active = active; + host_hardware::psram_active = active; + host_hardware::package = package_id; const auto out = m5gfx::select_detection_package(legacy); #if defined(CONFIG_IDF_TARGET_ESP32S3) const auto expected = package_id == 0 ? esp32s3_detectors_qfn56 : diff --git a/test/board_detect/support/pin_coverage.hpp b/test/board_detect/support/pin_coverage.hpp index 0972d596..819d4900 100644 --- a/test/board_detect/support/pin_coverage.hpp +++ b/test/board_detect/support/pin_coverage.hpp @@ -6,11 +6,7 @@ #include #include #include "board_detect/board_detect.inl" -namespace host_touch { -static std::uint16_t t4 = 100, t7 = 25; -static bool available = true; -static unsigned scans, clears; -} + #include COVERAGE_BOARD_HEADER #include "display_backend.hpp" using namespace m5gfx::board_detect; diff --git a/test/board_detect/support/probes.hpp b/test/board_detect/support/probes.hpp index 9067bd09..48933a86 100644 --- a/test/board_detect/support/probes.hpp +++ b/test/board_detect/support/probes.hpp @@ -96,9 +96,3 @@ std::uint32_t soft_spi_read32(probe_ctx_t &, int sclk, int mosi, int miso, int d } } // namespace board_detect } // namespace m5gfx - -namespace host_touch { -static std::uint16_t t4 = 100, t7 = 25; -static bool available = true; -static unsigned scans, clears; -} // namespace host_touch