Hi all,
as some of you have found out before, LPCOpen I2C stack does not implement timeouts. If your I2C lines are blocked or pulled to GND, the LPCOpen transfer function will not return.
To add a timeout to the LPCOpen I2C stack, I suggest the following changes to i2c_17xx_40xx.c:
+volatile uint32_t i2c_busy_timeouts = 0;
+const uint32_t i2c_busy_limit = 9999;
void Chip_I2C_EventHandler(I2C_ID_T id, I2C_EVENT_T event)
{
struct i2c_interface *iic = &i2c[id];
volatile I2C_STATUS_T *stat;
+ uint32_t busy_counter = 0;
if (event != I2C_EVENT_WAIT) {
return;
}
stat = &iic->mXfer->status;
- while (*stat == I2C_STATUS_BUSY) {}
+ while (*stat == I2C_STATUS_BUSY) {
+ busy_counter++;
+ if (busy_counter > i2c_busy_limit) {
+
+ i2c_busy_timeouts++;
+ *stat = I2C_STATUS_BUSERR;
+ i2c[id].ip->CONSET = I2C_CON_STO;
+ break;
+ }
+ }
}
and further down in Chip_I2C_MasterTransfer():
iic->mEvent(id, I2C_EVENT_WAIT);
iic->mXfer = 0;
-
- while (!isI2CBusFree(iic->ip)) {}
+ if (xfer->status != I2C_STATUS_BUSERR) {
+
+ while (!isI2CBusFree(iic->ip)) {}
+ }
The timeout breaks the busy wait loop which otherwise would not exit on bus obstruction (e.g., due to holding either the SDA or SCL line LOW by any device on the bus) or bus hang-ups (e.g., due to intermittant signals by an uncontrolled source that generate a superfluous START condition or mask a STOP condition).
While we are at it, you can see that in Chip_I2C_MasterTransfer() after the event I2C_EVENT_WAIT has been handled, the pointer iic->mXfer is set to NULL. If thereafter an I2C interrupt occurs (maybe due to intermittent signals), a hard fault will generated. This is because the I2C interrupt service routine (at least in master mode) will call Chip_I2C_MasterStateHandler() and this function will then call handleMasterXferState() with mXfer as parameter. handleMasterXferState() will then happily dereference this NULL pointer and consequently generate a hard fault.
To fix this issue, I suggest the following changes to Chip_I2C_MasterStateHandler():
void Chip_I2C_MasterStateHandler(I2C_ID_T id)
{
+ if (!i2c[id].mXfer) {
+
+
+
+
+
+ i2c[id].ip->CONCLR = I2C_CON_SI;
+ return;
+ }
if (!handleMasterXferState(i2c[id].ip, i2c[id].mXfer)) {
i2c[id].mEvent(id, I2C_EVENT_DONE);
}
}
I appreciate any comments. Thank you.
Daniel