-
Notifications
You must be signed in to change notification settings - Fork 23
Description
STMicro made some... interesting choices in their implementation of the single-byte I2C API. These choices cause it to not comply with the expected behavior for the API.
How their code works, in general, is:
- User calls i2c_byte_write() with byte N
- Byte N is clocked in to the I2C output register (TXDR). The hardware can then begin outputting it.
- Return 1 from i2c_byte_write() (no real way to detect an error for byte N, because we didn't wait for it to be written out)
- User calls i2c_byte_write() with byte N+1
- We wait for the TCR flag, indicating that the previous byte has been written out
- If the previous byte was NACKed, we never get this flag, so we go to the timeout case and return 2
- Also: potential race condition here, since we don't wait one full byte time like we need to
- Once the TCR flag sets, indicating byte N has been sent, we write out byte N+1 to TXDR
The huge issue here is, if we get a NACK on byte N, this shows up as a timeout error, not a NACK, and it shows up for byte N+1, not byte N. The Mbed devs were apparently aware of this issue, and considered it not serious enough to fix (see ARMmbed#9447). However, this is a very serious issue, because it means that single-byte I2C does not behave in the expected way on STMicro devices.
Consider the following code, where someone tries to write an I2C scanner:
addressToUse = 0;
for(uint8_t address : possibleI2CAddresses)
{
i2c.start();
if(i2c.write(address << 1) == 1)
{
// device found
i2c.stop();
addressToUse = address;
break;
}
i2c.stop();
}
This would work as expected on most devices, but not STM32s! There is no way for the current API to return a NACK on the address byte, so it would look like any address you tried to access was present on the bus. Clearly, this is not OK behavior and needs to be fixed.