Skip to content

Fix CoreS3 LCD not working on ESP-IDF v6 (SPI bus pins left in open-drain) - #251

Merged
lovyan03 merged 2 commits into
m5stack:developfrom
ainyan03:idf6_spi_od_fix
Aug 15, 2026
Merged

Fix CoreS3 LCD not working on ESP-IDF v6 (SPI bus pins left in open-drain)#251
lovyan03 merged 2 commits into
m5stack:developfrom
ainyan03:idf6_spi_od_fix

Conversation

@ainyan03

@ainyan03 ainyan03 commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #192

Root cause

On ESP-IDF v6, the CoreS3 / CoreS3SE display stops working (blank screen; with a cold NVS the board autodetection itself fails with board=0). Three conditions combine:

  1. The board autodetection probes GPIO35/36/37 as input_pullup to decide whether the SPI bus lines are externally pulled up. The internal pinMode() implements inputs as open-drain outputs driven high, which sets the pad's pad_driver (open-drain) flag.
  2. Up to ESP-IDF v5, spi_bus_initialize() configured the bus pins via gpio_set_direction(..., GPIO_MODE_INPUT_OUTPUT), which cleared the open-drain flag as a side effect.
  3. ESP-IDF v6 routes the pins with gpio_matrix_output() only and no longer touches pad_driver (components/esp_driver_spi/src/gpspi/spi_common.c).

As a result SCLK/MOSI remain open-drain. Through the weak pull-up the lines can no longer rise at MHz clock rates, so the panel never receives a valid command and _read_panel_id() reads back all-ones. Register-level comparison between v5.5.4 and v6.0.1 shows the GPSPI2 block, GPIO matrix, and IO_MUX byte-identical — the only differing state is GPIO_PINn.pad_driver of GPIO36/37.

Fix

  • 1st commit: explicitly restore the SPI bus pins to push-pull after spi_bus_initialize(). Harmless on all IDF versions (v5 and earlier already end up in this state).
  • 2nd commit: replace the chip-specific register-field #ifdef branches in pinMode() (pad_driver / func_out_sel) with gpio_ll_od_enable/od_disable and esp_rom_gpio_connect_out_signal, whose per-chip implementations absorb the register naming differences, so future chips need no new branches.

Verification

  • M5Stack CoreS3SE + ESP-IDF v6.0.1: autodetect, drawing, and readRect round-trip verification all pass (previously blank / autodetect failure)
  • No regression on device: CoreS3SE + v5.5.4, M5StickS3 + v6.0.1
  • Builds: esp32s3 (ESP-IDF v5.5.4 / v6.0.1), esp32, esp32c61 (v5.5.4), Arduino-ESP32 2.x (ESP-IDF v4.4)
  • CI on the fork: all workflows green

ESP-IDF v6 spi_bus_initialize() routes bus pins with gpio_matrix_output()
and no longer clears the pad open-drain flag (v5 used gpio_set_direction(),
which did). If the pads were previously configured as open-drain outputs
(e.g. by the autodetect pull-up probing of the bus pins), SCLK/MOSI stay
open-drain, the waveform cannot rise at MHz clock rates through the weak
pull-up, and the panel never responds.

Explicitly restore the bus pins to push-pull after bus initialization.

Verified on M5Stack CoreS3SE: ESP-IDF v6.0.1 autodetect and draw/readRect
round-trip now pass; no regression on ESP-IDF v5.5.4 (CoreS3SE) or
M5StickS3 (v6.0.1).
…s in pinMode

The pad_driver and func_out_sel register layouts are named differently on
some chips (e.g. ESP32-C61), which required chip-specific #ifdef branches
and SFINAE helpers. Delegate to gpio_ll_od_enable/od_disable and
esp_rom_gpio_connect_out_signal, whose per-chip implementations absorb
the naming differences, so future chips need no new branches here.
Direct register access remains only as a fallback for ESP-IDF v3.

esp_rom_gpio_connect_out_signal also normalizes the inv/oen matrix bits,
which matches the intended plain-GPIO-output contract of pinMode.

Build-verified on esp32s3 (ESP-IDF v5.5.4 / v6.0.1), esp32c61, esp32
(v5.5.4), and Arduino-ESP32 2.x (ESP-IDF v4.4); runtime-verified on
M5Stack CoreS3SE with ESP-IDF v6.0.1.
@lovyan03
lovyan03 requested a lite review from Copilot August 15, 2026 05:05
@lovyan03
lovyan03 merged commit 75f41bc into m5stack:develop Aug 15, 2026
27 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes CoreS3/CoreS3SE LCD failures on ESP-IDF v6 by ensuring SPI bus pins don’t remain in open-drain mode after prior pinMode(input_*) usage, and by reducing chip-specific register-field handling in pinMode() via ESP-IDF LL/ROM helpers.

Changes:

  • Use gpio_ll_od_enable/disable (when available) to manage open-drain configuration in pinMode() without chip-specific register-field branching.
  • Use esp_rom_gpio_connect_out_signal() (when available) to restore GPIO output signal routing in a chip-agnostic way.
  • After spi_bus_initialize(), explicitly force SPI SCLK/MOSI/MISO pads back to push-pull (disable open-drain) to avoid slow-rising edges on ESP-IDF v6.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants