From f6bf92e5d001d808bf2ea4b8fe821fd380862118 Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Wed, 19 Aug 2026 03:51:06 +0000 Subject: [PATCH 1/3] Clamp SPI clock dividers to their register widths FreqToClockDiv could produce a CLKDIV_PRE value wider than the register field when the requested frequency was far below the base clock, spilling into adjacent fields. Clamp both divider components with the target register definitions so each SoC retains its supported range without corrupting neighboring bits. --- src/lgfx/v1/platforms/esp32/common.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lgfx/v1/platforms/esp32/common.cpp b/src/lgfx/v1/platforms/esp32/common.cpp index 8a944f6..1ff3c8a 100644 --- a/src/lgfx/v1/platforms/esp32/common.cpp +++ b/src/lgfx/v1/platforms/esp32/common.cpp @@ -272,8 +272,8 @@ namespace lgfx { if (fapb <= hz) return SPI_CLK_EQU_SYSCLK; uint32_t div_num = fapb / (1 + hz); - uint32_t pre = div_num / 64u; - div_num = div_num / (pre+1); + uint32_t pre = std::min(div_num / (SPI_CLKCNT_N_V + 1u), SPI_CLKDIV_PRE_V); + div_num = std::min(div_num / (pre+1), SPI_CLKCNT_N_V); return div_num << 12 | ((div_num-1)>>1) << 6 | div_num | pre << 18; } From db4ebea0b900d52e43851afe4c48134964805568 Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Wed, 19 Aug 2026 03:51:06 +0000 Subject: [PATCH 2/3] Use the active GPSPI source clock for divider calculations Decode each supported ESP32 target's live GPSPI clock source and pre-divider instead of assuming an 80 MHz APB clock. Acquire the bus before reading that state because the driver may change it, allowing the existing cache to recalculate whenever another owner selects a different source. Unknown selectors use a safe upper bound so divider calculation does not accidentally overspeed the request. --- src/lgfx/v1/platforms/esp32/Bus_SPI.cpp | 9 +- src/lgfx/v1/platforms/esp32/common.cpp | 144 +++++++++++++++++++++++- src/lgfx/v1/platforms/esp32/common.hpp | 1 + 3 files changed, 148 insertions(+), 6 deletions(-) diff --git a/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp b/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp index aa2d0c9..d5e9e63 100644 --- a/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp +++ b/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp @@ -288,7 +288,12 @@ namespace lgfx void Bus_SPI::beginTransaction(void) { //ESP_LOGI("LGFX","Bus_SPI::beginTransaction"); - uint32_t freq_apb = getApbFrequency(); + // Bus acquisition can change the SPI source or its pre-dividers. + if (_cfg.use_lock) + { + spi::beginTransaction(_cfg.spi_host); + } + uint32_t freq_apb = getSpiClockFrequency(_cfg.spi_host); uint32_t clkdiv_write = _clkdiv_write; if (_last_freq_apb != freq_apb) { @@ -323,8 +328,6 @@ namespace lgfx #endif ; - if (_cfg.use_lock) spi::beginTransaction(_cfg.spi_host); - *_spi_user_reg = _user_reg; auto spi_port = _spi_port; (void)spi_port; diff --git a/src/lgfx/v1/platforms/esp32/common.cpp b/src/lgfx/v1/platforms/esp32/common.cpp index 1ff3c8a..41ed701 100644 --- a/src/lgfx/v1/platforms/esp32/common.cpp +++ b/src/lgfx/v1/platforms/esp32/common.cpp @@ -38,6 +38,17 @@ Original Source: #include #include #include +#if defined ( CONFIG_IDF_TARGET_ESP32P4 ) + #include +#elif defined ( CONFIG_IDF_TARGET_ESP32C5 ) || defined ( CONFIG_IDF_TARGET_ESP32C6 ) \ + || defined ( CONFIG_IDF_TARGET_ESP32C61 ) || defined ( CONFIG_IDF_TARGET_ESP32H2 ) + #include +#endif +#if __has_include() && __has_include() + #include + #include + #define LGFX_HAS_ESP_CLK_TREE +#endif #include #include #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) @@ -268,6 +279,133 @@ namespace lgfx #endif } + uint32_t getSpiClockFrequency(int spi_host) + { +#if defined ( CONFIG_IDF_TARGET_ESP32 ) || defined ( CONFIG_IDF_TARGET_ESP32S2 ) \ + || !defined ( CONFIG_IDF_TARGET ) + (void)spi_host; + return getApbFrequency(); +#else + const auto get_xtal_frequency = []() -> uint32_t + { + return static_cast(rtc_clk_xtal_freq_get()) * 1000000u; + }; + const auto get_rc_fast_frequency = []() -> uint32_t + { +#if defined ( LGFX_HAS_ESP_CLK_TREE ) && defined ( SOC_MOD_CLK_RC_FAST ) + uint32_t frequency = 0; + if (ESP_OK == esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_RC_FAST + , ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &frequency) && frequency) + { + return frequency; + } +#endif +#if defined ( SOC_CLK_RC_FAST_FREQ_APPROX ) + return SOC_CLK_RC_FAST_FREQ_APPROX; +#else + return 17500000u; +#endif + }; + +#if defined ( CONFIG_IDF_TARGET_ESP32S3 ) || defined ( CONFIG_IDF_TARGET_ESP32C2 ) \ + || defined ( CONFIG_IDF_TARGET_ESP32C3 ) + static_assert(SPI_MST_CLK_SEL_V == 1u, "SPI clock source selector must be one bit"); + // SPI_MST_CLK_SEL is a shifted mask in the older SPI register headers; + // decode with the explicit value mask rather than VALUE_GET_FIELD. + const uint32_t source_sel = (REG_READ(SPI_CLK_GATE_REG(spi_host + 1)) + >> SPI_MST_CLK_SEL_S) & SPI_MST_CLK_SEL_V; + if (source_sel == 0) { return get_xtal_frequency(); } + #if defined ( CONFIG_IDF_TARGET_ESP32C2 ) + return 40000000u; + #else + return 80000000u; // PLL_F80M is independent of CPU/APB frequency scaling. + #endif + +#elif defined ( CONFIG_IDF_TARGET_ESP32C5 ) || defined ( CONFIG_IDF_TARGET_ESP32C61 ) + if (spi_host != SPI2_HOST) { return 160000000u; } + const uint32_t clkm = REG_READ(PCR_SPI2_CLKM_CONF_REG); + const uint32_t source_sel = VALUE_GET_FIELD(clkm, PCR_SPI2_CLKM_SEL); + const uint32_t source_div = VALUE_GET_FIELD(clkm, PCR_SPI2_CLKM_DIV_NUM) + 1u; + uint32_t source_hz; + switch (source_sel) + { + case 0: source_hz = get_xtal_frequency(); break; + case 1: source_hz = 160000000u; break; + case 2: source_hz = get_rc_fast_frequency(); break; +#if defined ( CONFIG_IDF_TARGET_ESP32C5 ) + case 3: source_hz = 120000000u; break; +#endif + default: source_hz = 160000000u; break; // Safe upper bound for an unknown source. + } + return source_hz / source_div; + +#elif defined ( CONFIG_IDF_TARGET_ESP32C6 ) || defined ( CONFIG_IDF_TARGET_ESP32H2 ) + if (spi_host != SPI2_HOST) + { + #if defined ( CONFIG_IDF_TARGET_ESP32C6 ) + return 80000000u; + #else + return 48000000u; + #endif + } + const uint32_t clkm = REG_READ(PCR_SPI2_CLKM_CONF_REG); + const uint32_t source_sel = VALUE_GET_FIELD(clkm, PCR_SPI2_CLKM_SEL); + switch (source_sel) + { + case 0: return get_xtal_frequency(); + #if defined ( CONFIG_IDF_TARGET_ESP32C6 ) + case 1: return 80000000u; + #else + case 1: return 48000000u; + #endif + case 2: return get_rc_fast_frequency(); + default: + #if defined ( CONFIG_IDF_TARGET_ESP32C6 ) + return 80000000u; // Safe upper bound for an unknown source. + #else + return 48000000u; // Safe upper bound for an unknown source. + #endif + } + +#elif defined ( CONFIG_IDF_TARGET_ESP32P4 ) + const uint32_t ctrl116 = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG); + uint32_t source_sel; + uint32_t hs_div; + uint32_t mst_div; + if (spi_host == SPI2_HOST) + { + source_sel = VALUE_GET_FIELD(ctrl116, HP_SYS_CLKRST_REG_GPSPI2_CLK_SRC_SEL); + hs_div = VALUE_GET_FIELD(ctrl116, HP_SYS_CLKRST_REG_GPSPI2_HS_CLK_DIV_NUM); + mst_div = VALUE_GET_FIELD(ctrl116, HP_SYS_CLKRST_REG_GPSPI2_MST_CLK_DIV_NUM); + } + else if (spi_host == SPI3_HOST) + { + const uint32_t ctrl117 = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL117_REG); + source_sel = VALUE_GET_FIELD(ctrl116, HP_SYS_CLKRST_REG_GPSPI3_CLK_SRC_SEL); + hs_div = VALUE_GET_FIELD(ctrl117, HP_SYS_CLKRST_REG_GPSPI3_HS_CLK_DIV_NUM); + mst_div = VALUE_GET_FIELD(ctrl117, HP_SYS_CLKRST_REG_GPSPI3_MST_CLK_DIV_NUM); + } + else + { + return getApbFrequency(); + } + + uint32_t source_hz; + switch (source_sel) + { + case 0: source_hz = get_xtal_frequency(); break; + case 1: source_hz = get_rc_fast_frequency(); break; + case 4: source_hz = 480000000u; break; // SPLL + default: source_hz = 480000000u; break; // Safe upper bound for an unknown source. + } + return source_hz / (hs_div + 1) / (mst_div + 1); +#else + (void)spi_host; + return getApbFrequency(); +#endif +#endif + } + uint32_t FreqToClockDiv(uint32_t fapb, uint32_t hz) { if (fapb <= hz) return SPI_CLK_EQU_SYSCLK; @@ -891,7 +1029,9 @@ namespace lgfx } uint32_t spi_port = (spi_host + 1); (void)spi_port; - uint32_t clkdiv = FreqToClockDiv(getApbFrequency(), freq); + // Bus acquisition may select a different source or pre-divider. + beginTransaction(spi_host); + uint32_t clkdiv = FreqToClockDiv(getSpiClockFrequency(spi_host), freq); uint32_t user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN; if (spi_mode == 1 || spi_mode == 2) user |= SPI_CK_OUT_EDGE; @@ -917,8 +1057,6 @@ namespace lgfx #endif ; - beginTransaction(spi_host); - writereg(SPI_USER_REG(spi_port), user); #if defined (SPI_PIN_REG) writereg(SPI_PIN_REG(spi_port), pin); diff --git a/src/lgfx/v1/platforms/esp32/common.hpp b/src/lgfx/v1/platforms/esp32/common.hpp index fa8b4c8..8ce48ad 100644 --- a/src/lgfx/v1/platforms/esp32/common.hpp +++ b/src/lgfx/v1/platforms/esp32/common.hpp @@ -192,6 +192,7 @@ namespace lgfx static inline void gpio_lo(int_fast8_t pin) { if (pin >= 0) *get_gpio_lo_reg(pin) = 1 << (pin & 31); } // ESP_LOGI("LGFX", "gpio_lo: %d", pin); } uint32_t getApbFrequency(void); + uint32_t getSpiClockFrequency(int spi_host); uint32_t FreqToClockDiv(uint32_t fapb, uint32_t hz); /// for I2S and LCD_CAM peripheral clock From 5bce6a2d52828e0ccf165e460735072da83163ae Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Wed, 19 Aug 2026 03:51:06 +0000 Subject: [PATCH 3/3] Select faster GPSPI sources for Arduino transactions On ESP32-C5, C61, C6, and P4 Arduino builds, temporarily select an 80 MHz GPSPI base only when it produces a strictly closer write clock without overspeeding either requested rate. Equal write results keep the current source; read throughput may decrease when write throughput improves. Save and restore only the owned clock fields inside the RCC atomic section, and clean up active ownership when a bus is released or destroyed. ESP-IDF transactions retain driver ownership, and the Arduino mutex does not serialize mixed API users. --- src/lgfx/v1/platforms/esp32/Bus_SPI.cpp | 222 ++++++++++++++++++++++++ src/lgfx/v1/platforms/esp32/Bus_SPI.hpp | 5 + 2 files changed, 227 insertions(+) diff --git a/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp b/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp index d5e9e63..c08fb59 100644 --- a/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp +++ b/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp @@ -39,6 +39,17 @@ Original Source: #include #include +#if defined (ARDUINO) && (defined (CONFIG_IDF_TARGET_ESP32P4) \ + || defined (CONFIG_IDF_TARGET_ESP32C5) || defined (CONFIG_IDF_TARGET_ESP32C6) \ + || defined (CONFIG_IDF_TARGET_ESP32C61)) + #define LGFX_SPI_CLOCK_TAKEOVER + #if defined (CONFIG_IDF_TARGET_ESP32P4) + #include + #else + #include + #endif +#endif + #if __has_include () #include #else @@ -149,6 +160,199 @@ namespace lgfx static __attribute__ ((always_inline)) inline void writereg(uint32_t addr, uint32_t value) { *(volatile uint32_t*)addr = value; } #pragma GCC diagnostic pop +#if defined (LGFX_SPI_CLOCK_TAKEOVER) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" + struct spi_clock_state_t + { + uint32_t saved0 = 0; + uint32_t saved1 = 0; + const Bus_SPI* owner = nullptr; + bool active = false; + }; + + static spi_clock_state_t spi_clock_state[SOC_SPI_PERIPH_NUM]; + + static bool spi_clock_host_supported(int spi_host) + { +#if defined (CONFIG_IDF_TARGET_ESP32P4) + return spi_host == SPI2_HOST || spi_host == SPI3_HOST; +#else + return spi_host == SPI2_HOST; +#endif + } + + static uint32_t spi_clock_output_frequency(uint32_t source_hz, uint32_t requested_hz) + { + const uint32_t clock_div = FreqToClockDiv(source_hz, requested_hz); + if (clock_div & SPI_CLK_EQU_SYSCLK) { return source_hz; } + const uint32_t pre = VALUE_GET_FIELD(clock_div, SPI_CLKDIV_PRE) + 1u; + const uint32_t n = VALUE_GET_FIELD(clock_div, SPI_CLKCNT_N) + 1u; + return source_hz / pre / n; + } + + static uint32_t spi_clock_error(uint32_t actual_hz, uint32_t requested_hz) + { + return actual_hz > requested_hz ? actual_hz - requested_hz : requested_hz - actual_hz; + } + + struct spi_clock_target_t + { + uint32_t base_hz; + uint32_t source_div; + }; + + static spi_clock_target_t spi_clock_find_target(uint32_t requested_hz) + { +#if defined (CONFIG_IDF_TARGET_ESP32C5) || defined (CONFIG_IDF_TARGET_ESP32C61) + (void)requested_hz; + // The 80 MHz root preserves both common write and read rates (for example + // 40 and 16 MHz), while a 40 MHz root would make 16 MHz inexact. + return { 80000000u, 2u }; +#else + (void)requested_hz; + return { 80000000u, 1u }; +#endif + } + + static bool spi_clock_acquire(const Bus_SPI* owner, int spi_host + , uint32_t write_hz, uint32_t read_hz) + { + if (!spi_clock_host_supported(spi_host) || write_hz == 0 || read_hz == 0) { return false; } + + const auto target = spi_clock_find_target(std::max(write_hz, read_hz)); + const uint32_t current_hz = getSpiClockFrequency(spi_host); + const uint32_t current_write = spi_clock_output_frequency(current_hz, write_hz); + const uint32_t target_write = spi_clock_output_frequency(target.base_hz, write_hz); + const uint32_t target_read = spi_clock_output_frequency(target.base_hz, read_hz); + const uint32_t current_write_error = spi_clock_error(current_write, write_hz); + const uint32_t target_write_error = spi_clock_error(target_write, write_hz); + // Prefer write throughput: a slower read clock is acceptable, but neither + // clock may exceed its request. Equal write results leave the current owner + // untouched to avoid an unnecessary source change. + if (target_write > write_hz || target_read > read_hz + || target_write_error >= current_write_error) + { + return false; + } + + auto& state = spi_clock_state[spi_host]; + if (state.active) { return false; } // Host-scoped ownership is not nestable. + + // The Arduino bus mutex does not serialize ESP-IDF SPI driver users on the + // same host; mixing the two APIs cannot provide transaction-wide exclusion. + PERIPH_RCC_ATOMIC() + { +#if defined (CONFIG_IDF_TARGET_ESP32P4) + if (spi_host == SPI2_HOST) + { + constexpr uint32_t mask = HP_SYS_CLKRST_REG_GPSPI2_CLK_SRC_SEL_M + | HP_SYS_CLKRST_REG_GPSPI2_HS_CLK_DIV_NUM_M + | HP_SYS_CLKRST_REG_GPSPI2_MST_CLK_DIV_NUM_M; + constexpr uint32_t target_value = (4u << HP_SYS_CLKRST_REG_GPSPI2_CLK_SRC_SEL_S) + | (2u << HP_SYS_CLKRST_REG_GPSPI2_HS_CLK_DIV_NUM_S) + | (1u << HP_SYS_CLKRST_REG_GPSPI2_MST_CLK_DIV_NUM_S); + const uint32_t current = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG); + state.saved0 = current & mask; + REG_WRITE(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG, (current & ~mask) | target_value); + } + else + { + constexpr uint32_t source_mask = HP_SYS_CLKRST_REG_GPSPI3_CLK_SRC_SEL_M; + constexpr uint32_t source_target = 4u << HP_SYS_CLKRST_REG_GPSPI3_CLK_SRC_SEL_S; + constexpr uint32_t divider_mask = HP_SYS_CLKRST_REG_GPSPI3_HS_CLK_DIV_NUM_M + | HP_SYS_CLKRST_REG_GPSPI3_MST_CLK_DIV_NUM_M; + constexpr uint32_t divider_target = (2u << HP_SYS_CLKRST_REG_GPSPI3_HS_CLK_DIV_NUM_S) + | (1u << HP_SYS_CLKRST_REG_GPSPI3_MST_CLK_DIV_NUM_S); + const uint32_t ctrl116 = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG); + const uint32_t ctrl117 = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL117_REG); + state.saved0 = ctrl116 & source_mask; + state.saved1 = ctrl117 & divider_mask; + // Install safe dividers before selecting the 480 MHz source. + REG_WRITE(HP_SYS_CLKRST_PERI_CLK_CTRL117_REG, (ctrl117 & ~divider_mask) | divider_target); + REG_WRITE(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG, (ctrl116 & ~source_mask) | source_target); + } +#elif defined (CONFIG_IDF_TARGET_ESP32C5) || defined (CONFIG_IDF_TARGET_ESP32C61) + constexpr uint32_t mask = PCR_SPI2_CLKM_SEL_M | PCR_SPI2_CLKM_DIV_NUM_M; + const uint32_t target_value = (1u << PCR_SPI2_CLKM_SEL_S) + | ((target.source_div - 1u) << PCR_SPI2_CLKM_DIV_NUM_S); + const uint32_t current = REG_READ(PCR_SPI2_CLKM_CONF_REG); + state.saved0 = current & mask; + REG_WRITE(PCR_SPI2_CLKM_CONF_REG, (current & ~mask) | target_value); +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + constexpr uint32_t mask = PCR_SPI2_CLKM_SEL_M; + constexpr uint32_t target_value = 1u << PCR_SPI2_CLKM_SEL_S; + const uint32_t current = REG_READ(PCR_SPI2_CLKM_CONF_REG); + state.saved0 = current & mask; + REG_WRITE(PCR_SPI2_CLKM_CONF_REG, (current & ~mask) | target_value); +#endif + state.owner = owner; + state.active = true; + } + return true; + } + + static bool spi_clock_owned_by(const Bus_SPI* owner, int spi_host) + { + if (!spi_clock_host_supported(spi_host)) { return false; } + const auto& state = spi_clock_state[spi_host]; + return state.active && state.owner == owner; + } + + static bool spi_clock_restore(const Bus_SPI* owner, int spi_host) + { + if (!spi_clock_host_supported(spi_host)) { return false; } + + auto& state = spi_clock_state[spi_host]; + if (!state.active || state.owner != owner) { return false; } + + PERIPH_RCC_ATOMIC() + { +#if defined (CONFIG_IDF_TARGET_ESP32P4) + if (spi_host == SPI2_HOST) + { + constexpr uint32_t mask = HP_SYS_CLKRST_REG_GPSPI2_CLK_SRC_SEL_M + | HP_SYS_CLKRST_REG_GPSPI2_HS_CLK_DIV_NUM_M + | HP_SYS_CLKRST_REG_GPSPI2_MST_CLK_DIV_NUM_M; + const uint32_t current = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG); + REG_WRITE(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG, (current & ~mask) | state.saved0); + } + else + { + constexpr uint32_t source_mask = HP_SYS_CLKRST_REG_GPSPI3_CLK_SRC_SEL_M; + constexpr uint32_t divider_mask = HP_SYS_CLKRST_REG_GPSPI3_HS_CLK_DIV_NUM_M + | HP_SYS_CLKRST_REG_GPSPI3_MST_CLK_DIV_NUM_M; + const uint32_t ctrl116 = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG); + const uint32_t ctrl117 = REG_READ(HP_SYS_CLKRST_PERI_CLK_CTRL117_REG); + // Leave the 480 MHz source before restoring potentially smaller dividers. + REG_WRITE(HP_SYS_CLKRST_PERI_CLK_CTRL116_REG, (ctrl116 & ~source_mask) | state.saved0); + REG_WRITE(HP_SYS_CLKRST_PERI_CLK_CTRL117_REG, (ctrl117 & ~divider_mask) | state.saved1); + } +#elif defined (CONFIG_IDF_TARGET_ESP32C5) || defined (CONFIG_IDF_TARGET_ESP32C61) + constexpr uint32_t mask = PCR_SPI2_CLKM_SEL_M | PCR_SPI2_CLKM_DIV_NUM_M; + const uint32_t current = REG_READ(PCR_SPI2_CLKM_CONF_REG); + REG_WRITE(PCR_SPI2_CLKM_CONF_REG, (current & ~mask) | state.saved0); +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + constexpr uint32_t mask = PCR_SPI2_CLKM_SEL_M; + const uint32_t current = REG_READ(PCR_SPI2_CLKM_CONF_REG); + REG_WRITE(PCR_SPI2_CLKM_CONF_REG, (current & ~mask) | state.saved0); +#endif + state.active = false; + state.owner = nullptr; + } + return true; + } +#pragma GCC diagnostic pop + + Bus_SPI::~Bus_SPI(void) + { + if (spi_clock_owned_by(this, _cfg.spi_host)) + { + release(); + } + } +#endif + void Bus_SPI::config(const config_t& cfg) { _cfg = cfg; @@ -262,6 +466,18 @@ namespace lgfx void Bus_SPI::release(void) { //ESP_LOGI("LGFX","Bus_SPI::release"); +#if defined (LGFX_SPI_CLOCK_TAKEOVER) + if (spi_clock_owned_by(this, _cfg.spi_host)) + { + // An active takeover implies this instance still owns the Arduino bus + // mutex. Finish the transfer and restore the host before releasing it. + dc_control(true); + if (spi_clock_restore(this, _cfg.spi_host)) + { + spi::endTransaction(_cfg.spi_host); + } + } +#endif if (!_inited) return; _inited = false; spi::release(_cfg.spi_host); @@ -292,6 +508,9 @@ namespace lgfx if (_cfg.use_lock) { spi::beginTransaction(_cfg.spi_host); +#if defined (LGFX_SPI_CLOCK_TAKEOVER) + spi_clock_acquire(this, _cfg.spi_host, _cfg.freq_write, _cfg.freq_read); +#endif } uint32_t freq_apb = getSpiClockFrequency(_cfg.spi_host); uint32_t clkdiv_write = _clkdiv_write; @@ -343,6 +562,9 @@ namespace lgfx dc_control(true); #if defined ( LGFX_SPIDMA_WORKAROUND ) if (_dma_ch) { spicommon_dmaworkaround_idle(_dma_ch); } +#endif +#if defined (LGFX_SPI_CLOCK_TAKEOVER) + if (_cfg.use_lock) { spi_clock_restore(this, _cfg.spi_host); } #endif if (_cfg.use_lock) spi::endTransaction(_cfg.spi_host); #if defined (ARDUINO) // Arduino ESP32 diff --git a/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp b/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp index 996c826..c144b7b 100644 --- a/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp +++ b/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp @@ -103,6 +103,11 @@ namespace lgfx }; constexpr Bus_SPI(void) = default; +#if defined (ARDUINO) && (defined (CONFIG_IDF_TARGET_ESP32P4) \ + || defined (CONFIG_IDF_TARGET_ESP32C5) || defined (CONFIG_IDF_TARGET_ESP32C6) \ + || defined (CONFIG_IDF_TARGET_ESP32C61)) + ~Bus_SPI(void) override; +#endif const config_t& config(void) const { return _cfg; }