Conversation
|
Thanks @pottekkat for this fix! Some notes, lightly edited from an automated review.
reg.Set(i2cCMD_RSTART) // COMD0
reg = nextAddress(reg)
reg.Set(i2cCMD_WRITE | 1) // COMD1The write segment before it left This is segment 4, so the note in TRM section 21.3.5 applies. A write of 32 bytes or more, followed by a read, goes through this path. Did you test that specific case? If the hardware does object, you can clear the command slots at the start of each segment, or keep END at the same slot.
// Always end a write segment with END, so STOP goes out alone. TRM 21.3.5
|
Fixes #5673
The I2C controller has 16 command slots and 32 bytes of RAM. So it splits longer transfers into segments. A segment ends with
i2cCMD_END, which suspends the transfer and holds the bus. And as mentioned in the TRM section 21.3.5:transmitresetsregtoCOMD0after every segment and does not clear the slots. The segment that finishes a read or a write does not end withEND, soi2cCMD_STOPis programmed into the next slot instead.For example:
Up to 63 bytes, a write is two segments, so the final
STOPatCOMD1overwrites aWRITE. From 64 bytes (three segments or more), the finalSTOPoverwrites theENDleft by the previous segment inCOMD1(which the note above mentions not to do).Reads fail at 32 bytes for another reason. A segment that reads 32 bytes NACKs the last with
i2cCMD_READLASTand ends withi2cCMD_STOP, with noi2cCMD_END.The fix is to ensure write segments always end with
i2cCMD_END, soi2cCMD_STOPgoes out alone atCOMD0. Follows the same logic in ESP-IDF i2c_master.c#L202-L204.Reads put the ACKed bytes and the final NACKed byte in separate segments. The
READLASTsegment now pulls one byte and ends withEND. Also follows the same logic in ESP-IDF i2c_master.c#L395-L396.An
isReadflag replacesreadTobecauseSTOPis now in its own segment afterreadTohas been cleared.Tested on an ESP32-D0WD-V3 with an SH1106 OLED.