Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ void common_hal_i2ctarget_i2c_target_deinit(i2ctarget_i2c_target_obj_t *self) {
}

int common_hal_i2ctarget_i2c_target_is_addressed(i2ctarget_i2c_target_obj_t *self, uint8_t *address, bool *is_read, bool *is_restart) {
// Interrupt bits must be cleared by software. Clear the STOP and
// RESTART interrupt bits after an I2C transaction finishes.
if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) {
self->peripheral->hw->clr_stop_det;
self->peripheral->hw->clr_restart_det;
}

if (!((self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) || (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS))) {
return 0;
}
Expand All @@ -98,12 +105,20 @@ int common_hal_i2ctarget_i2c_target_is_addressed(i2ctarget_i2c_target_obj_t *sel
}

int common_hal_i2ctarget_i2c_target_read_byte(i2ctarget_i2c_target_obj_t *self, uint8_t *data) {
if (self->peripheral->hw->status & I2C_IC_STATUS_RFNE_BITS) {
*data = (uint8_t)self->peripheral->hw->data_cmd;
return 1;
} else {
return 0;
// Wait for data to arrive or a STOP condition indicating the transfer is over.
// Without this wait, the CPU can drain the FIFO faster than bytes arrive on the
// I2C bus, causing transfers to be split into multiple partial reads.
for (int t = 0; t < 100; t++) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want to formalize the timeout here? Maybe based on the baudrate?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is my first experience with i2ctarget side, so I'm not sure of the details. But talking it over with claude a bit and seems like the target side code does not have a reliable way to know the baudrate. The raspberrypi port implementation makes reference to setting a value here: https://github.com/FoamyGuy/circuitpython/blob/e376e2416b84d7b06f3f74843682d1d74d13efd4/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c#L52-L53 but the comment indicates it's not used.

Claude had this to say about the idea of basing it on bus speed:

the current fix is already effectively speed-independent — the loop exits early via the R_STOP_DET check as soon as the master finishes the transfer. The 1ms ceiling (100 × 10µs) is just a safety timeout, not a tuned wait. At the slowest standard I2C speed (100kHz), one byte is 9 clocks = 90µs, so 4 bytes would be ~360µs, well within the 1ms budget. The loop won't actually spin for 1ms in normal operation; it'll break out as soon as either data arrives or STOP is detected.

The values that it chose came from the atmel-samd implementation I think: https://github.com/FoamyGuy/circuitpython/blob/e376e2416b84d7b06f3f74843682d1d74d13efd4/ports/atmel-samd/common-hal/i2ctarget/I2CTarget.c#L148-L150

if (self->peripheral->hw->status & I2C_IC_STATUS_RFNE_BITS) {
*data = (uint8_t)self->peripheral->hw->data_cmd;
return 1;
}
if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) {
break;
}
mp_hal_delay_us(10);
}
return 0;
}

int common_hal_i2ctarget_i2c_target_write_byte(i2ctarget_i2c_target_obj_t *self, uint8_t data) {
Expand Down
Loading