I2C and RTOS
Posted: Fri Feb 18, 2022 6:53 pm
Hi, I am seeing an anomaly with the Wire library - not sure whether to call it an issue.
My app, ported from AVR, has one or more MCP23008 expanders connected to one I2C bus, polled at 50 mS intervals. All I2C operations are performed in a single thread, using Wire. It all works fine when the polling is done from loop(), but when I call it from a FreeRTOS task, I get many read errors. Without the RTOS, there are essentially zero errors, whereas with the RTOS there can be many (varies from several an hour to several a second).
After experimenting, I find the errors can be eliminated entirely by forcing the I2C task onto the same core (#1) as loop(). FWIW, running a WiFi handler either as a task on core 0, or from loop(), or not at all, makes no difference.
The errors I get are all generated after the requestFrom() call and show a lastError() of 2 (ACK err). Since commands are buffered, I'm not certain what the ACK relates to, but write operations report no errors.
I am inclined to think this is some odd interaction between Wire and whatever is running on core 0, but I'm willing to be corrected.
So I suppose I am asking if anyone who knows more about Wire and the RTOS can suggest what the issue might be? I've looked at everything I can think of without any answer.
FYI, here is the code I am using to read and write; it is in a class derived from TwoWire:
My app, ported from AVR, has one or more MCP23008 expanders connected to one I2C bus, polled at 50 mS intervals. All I2C operations are performed in a single thread, using Wire. It all works fine when the polling is done from loop(), but when I call it from a FreeRTOS task, I get many read errors. Without the RTOS, there are essentially zero errors, whereas with the RTOS there can be many (varies from several an hour to several a second).
After experimenting, I find the errors can be eliminated entirely by forcing the I2C task onto the same core (#1) as loop(). FWIW, running a WiFi handler either as a task on core 0, or from loop(), or not at all, makes no difference.
The errors I get are all generated after the requestFrom() call and show a lastError() of 2 (ACK err). Since commands are buffered, I'm not certain what the ACK relates to, but write operations report no errors.
I am inclined to think this is some odd interaction between Wire and whatever is running on core 0, but I'm willing to be corrected.
So I suppose I am asking if anyone who knows more about Wire and the RTOS can suggest what the issue might be? I've looked at everything I can think of without any answer.
FYI, here is the code I am using to read and write; it is in a class derived from TwoWire:
Code: Select all
size_t write_reg (const byte addr, const byte reg, const byte val)
{
size_t ret = 0;
beginTransmission (addr); //starts talking to slave device
if ((ret = write (reg)) != size_t (0)) //selects the register specified; unlikely to fail
ret = write (val);
if (endTransmission () != I2C_ERROR_OK) { //ends communication with the device
log_e ("endTx fail");
ret = 0;
};
return ret;
};
// read a specified device register; param reference is unchanged if error
bool read_reg (const byte addr, const byte reg, byte &val)
{
bool ok = false;
beginTransmission (addr); //starts talking to slave device
if (write (reg) == 0)
log_d ("reg write err %u", lastError()); // would be a I2C_ERROR_MEMORY
else {
uint8_t err = endTransmission (false);
if (err != I2C_ERROR_OK) {
log_d ("endTx error %u", err);
} else {
requestFrom (addr, uint8_t (1));
if (lastError () != I2C_ERROR_OK){
log_d ("read req err %u %s", lastError (), getErrorText (lastError ()));
} else {
if (available ()) {
val = uint8_t (read ());
ok = true;
} else
log_d ("nothing read");
}
}
}
return ok;