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;