Bus_SPI: wait for SPI_UPDATE to complete when generating the 1-bit dummy clock - #308
Merged
Merged
Conversation
…mmy clock On chips with SPI_UPDATE (ESP32-C3/S3/C2/C6/C5/C61/H2/P4) a single dummy clock cannot be transferred, so beginRead(1) inverts SPI_CK_IDLE_EDGE, issues SPI_UPDATE, restores the polarity and issues SPI_UPDATE again. The two updates were issued back to back without waiting for the first synchronization into the SPI clock domain to finish. When the restore lands first, the clock line never moves, the panel sees no dummy clock and every read comes back shifted by one bit (RDDID 0x000000E3 read as 0x000080F1). Measured on an ESP32-C5 board: with M5.begin() omitted and the ID read every 5 ms from boot, only the first read after bus init was correct and every later one was shifted (400 reads x 12 resets); with a 0-bit dummy the same shifted value appeared every time, with a hardware 2-bit dummy the value was shifted the other way every time. The first read presumably wins only because the code is not yet in cache, which also explains why the display autodetect on that board failed at random (13 of 25 resets needed a retry, sometimes all retries failed and the board fell back to the display-less identity). Wait for the SPI_UPDATE bit to self-clear before each write that changes the polarity: once before the inversion (the preceding beginRead() issues an update of its own without waiting) and once before the restore. No wait is added after the restore: the value is already in the register when that update is issued, and the readData() that follows starts the transfer with USR|UPDATE, which carries the same value, exactly as every other transfer after a bare update does. This keeps the rule of waiting before a change rather than after it. With the two waits the same measurement gives 4800 correct reads out of 4800, and the autodetect succeeded at the first read on 30 of 30 resets plus 10 of 10 resets issued in the middle of a transfer.
ainyan03
force-pushed
the
spi_dummy_update
branch
from
September 22, 2026 10:00
d78978c to
2315f43
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On chips with
SPI_UPDATE(ESP32-C3/S3/C2/C6/C5/C61/H2/P4) a single dummy clock cannot be transferred, soBus_SPI::beginRead(1)invertsSPI_CK_IDLE_EDGE, issuesSPI_UPDATE, restores the polarity and issuesSPI_UPDATEagain to move the clock line once. The updates were issued back to back without waiting for the synchronization into
the SPI clock domain to finish (and the preceding
beginRead()had already issued one more without waiting). When therestore overtakes the inversion the clock never moves, the panel sees no dummy clock and every read comes back shifted
by one bit: RDDID
0x000000E3reads as0x000080F1.The
dummy_read_bits = 1default ofPanel_LCD::readCommand()and the display autodetect's panel ID read go throughthis path, so on an ESP32-C5 board the autodetect failed at random after a reset (13 of 25 resets needed a retry,
sometimes every retry failed and the board fell back to a display-less identity).
Measurement
With
M5.begin()/init()omitted and the panel ID read every 5 ms from boot on an ESP32-C5 board:readDataskipped): the same shifted value every timeThe first read presumably wins only because the code is not yet in cache, which also explains the boot-to-boot randomness.
Fix
Wait for
SPI_UPDATEto self-clear before each write that changes the polarity: once before the inversion (covering theupdate the preceding
beginRead()issued) and once before the restore. No wait is added after the restore: the value isalready in the register when that update is issued, and the following
readData()starts the transfer withUSR|UPDATE,which carries the same value, exactly as every other transfer after a bare update does. The wait itself is the one
ESP-IDF's
spi_ll_apply_config()uses on every chip that has the bit.Verification
of a transfer (unmodified: 13 of 25 needed retries, 3 of 10 mid-transfer)
readCommand(0x04)at 8 / 16 MHz andreadRectcomparison on ESP32-S3 (4 boards), ESP32 and ESP32-C5;ESP32-H2 / ESP32-C6 boot (no display)