diff --git a/pio/i2c/i2c.pio b/pio/i2c/i2c.pio index 3808bccda..2d3984d65 100644 --- a/pio/i2c/i2c.pio +++ b/pio/i2c/i2c.pio @@ -48,6 +48,10 @@ do_nack: do_byte: set x, 7 ; Loop 8 times + mov isr, null ; Set ISR and input shift counter to zero. This + ; helps fix a race condition when autopush is + ; disabled and re-enabled, which can leave the + ; counter in an inconsistent state. bitloop: out pindirs, 1 [7] ; Serialise write data (all-ones if reading) nop side 1 [2] ; SCL rising edge diff --git a/pio/i2c/pio_i2c.c b/pio/i2c/pio_i2c.c index a3a19f576..da00ab616 100644 --- a/pio/i2c/pio_i2c.c +++ b/pio/i2c/pio_i2c.c @@ -22,6 +22,11 @@ void pio_i2c_resume_after_error(PIO pio, uint sm) { pio_interrupt_clear(pio, sm); } +// Disable autopush of read I2C bytes, which is useful when only writing to +// the I2C bus and we don't want to bother with cleaning the RX FIFO. But be +// careful because this isn't synchronized to the state machine program and in +// a race condition can leave its input shift counter in an unexpected state, +// shifting any subsequently read bytes by an unexpected number of bits. void pio_i2c_rx_enable(PIO pio, uint sm, bool en) { if (en) hw_set_bits(&pio->sm[sm].shiftctrl, PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS);