Skip to content

0.2.28 - #268

Merged
lovyan03 merged 18 commits into
masterfrom
develop
Aug 25, 2026
Merged

0.2.28#268
lovyan03 merged 18 commits into
masterfrom
develop

Conversation

@lovyan03

Copy link
Copy Markdown
Collaborator

New device / panel support

  • Add support for M5Tab5X (by @hlym123)
  • Add support for M5Stack CoreP4X: board detection, ST7102 MIPI-DSI panel, CST3530 touch (by @hlym123)

Fixes

  • SPI: derive the clock divider from the active GPSPI source clock instead of a fixed 80 MHz. On ESP32-P4 / C5
    / C61 / H2 the SCLK could come out far below the requested value, and on ESP32-S3 up to twice as fast;
    Arduino builds now also select the faster source. Dividers are clamped to their register widths.
  • I2C (ESP32 hardware master): survive slaves that stretch the clock, from microseconds up to the tens of
    milliseconds SMBus allows, and wait for each chunk of a split read to complete before issuing the next
    command.
  • Software I2C (negative port numbers): hold the data line until the clock is actually low, so devices
    acknowledge as they should.
  • pin_backup_t: restore the output latch together with the rest of the pin state, so a pin that was driving
    low does not come back driving high after a probe; clarify which negative port numbers the library reserves.

ainyan03 and others added 18 commits August 19, 2026 03:51
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.
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.
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.
Derive the SPI divider base from the active GPSPI clock source
The software I2C moved SDA in the same breath as pulling SCL low. A data
line that changes while the clock still reads high is a start or a stop to
every device on the bus, so the transfer ended instead of carrying a bit
and no device ever acknowledged its address.

Wait for each line to reach the level it was just given, rather than
budgeting a fixed time for it: the clock is driven low, so waiting for it
costs the fall time of the bus and nothing more, and the released data
line is given the time its own rise actually takes. A fixed hold would
have to come out of the setup time, which is what the slow rise of a
released line needs at the higher clock rates.

The same wait now precedes the start, the repeated start and the stop,
where the rise of the released data line decides whether the condition
appears on the bus at all. A line that never reaches its level is
reported the way a clock that will not rise already was, so the transfer
ends instead of carrying on with a bus that is not there. The recovery
path is the one exception: a clock that will not settle is the condition
it exists to clear.

An acknowledge needs one more distinction. This master drives the data
line low for the bit before it, so a low reading is either a device
holding the line or a rise that has not finished. A device holds it for
the whole pulse while a rise is over within the time the bus is allowed
to take for one, so the level is read again to separate them.
Software I2C: hold the data line until the clock is actually down
A backup restored the mux, the routing and the output enable, but not the
level the pin was driving. A pin that had been held low came back driving
high, because whoever borrowed it left the latch there - releasing an open
drain line means letting the latch go high, and every probe ends that way.

Restoring in place was not enough on its own: putting the pad
configuration back can turn an open drain output into a push-pull one
while the enable is still set and the latch is still high, and the pin
drives that high before the latch is ever reached. So the output is taken
down first, then the latch goes back, then the configuration, and the pin
is only enabled again at the end if that is how it was found. The enable
is set and cleared through its own registers rather than read back and
written, so a pin being restored elsewhere is not caught in between.
The note told a sketch to use port -2 because no library takes it. Board
identification in M5Unified takes it, so the advice would have handed a
sketch the slot a probe is about to reopen on other pins - and a transfer
does not check that the slot is still the one it was given, so the wrong
pins would move with no error to show for it.

Describe what actually happens instead: both slots are borrowed while a
board is being brought up, and a sketch that wants one should open it
afterwards.
Restore the output latch with the pin, and correct the note on the negative ports
Tab5X upgrades the ESP32-P4 silicon revision used by Tab5.
1. Detect CoreP4X on the internal I2C bus using GPIO11 (SDA) and
   GPIO9 (SCL), with M5IOE1 at 0x4F and M5PM1 at 0x6E.
2. Add ST7102 MIPI DSI display and Touch_CST3530 touch support.
readBytes splits transfers at the hardware RX FIFO boundary. Record the same data-transfer wait stage used by writeBytes after starting each read command, so the next chunk or endTransaction waits for and clears that command completion instead of racing a stale END interrupt.
The software wait limits gave a stalled transfer 1-2ms before declaring
connection_lost: the FIFO poll in readBytes allowed us_limit+1024us and
the END wait in i2c_wait allowed 512<<stage. A slave holding SCL longer
than that - legal under the I2C spec, and up to 25ms under SMBus - had
its transfer killed.

Raise both limits to a shared 25ms constant and make the waits and the
watchdog agree on what it means:

- i2c_wait also wakes on TIME_OUT, and treats TIME_OUT or a lost
  arbitration as fatal even when END is set alongside, matching the
  SDK event priority: the bus state is unknown past either of them.
- The STOP wait requires TRANS_COMPLETE: a watchdog bite, a lost
  arbitration, or the limit expiring with no interrupt at all used to
  return success from endTransaction.
- The exponent-encoded SCL-low watchdog is set to 2^20 source clocks
  (about 26ms at 40MHz), just past the software limit so the software
  limit is the sole authority on tolerated stretching; the previous
  value 31 meant almost a minute. The ESP32 register is at its
  ceiling, about 13ms, so that target tolerates less stretching.
- The soft I2C SCL-high wait adopts the same 25ms allowance.

NACK handling does not lean on these limits, so probing an absent
device stays fast: measured on the ESP32, an address NACK raises no
error interrupt at all - the command sequence still reaches END and
the error only surfaces in the ack_err evaluation after STOP - and
that path is unchanged (absent-address probe: 99-113us before and
after).

Measured with hosts reading 31-128 bytes from a slave that stretches
at the 32-byte chunk boundary: previously failures began at 1ms of
stretch; now the ESP32 passes everything through 10ms and reports
15/30ms as connection_lost (its watchdog ceiling), the ESP32-S3
passes through 15ms and reports 30ms (the 25ms software limit), and
both recover cleanly on the next transaction.
Add M5Stack CoreP4X display support
Survive I2C slaves that stretch the clock
@lovyan03
lovyan03 merged commit d91077b into master Aug 25, 2026
54 checks passed
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.

2 participants