Content originally posted in LPCWare by gregd on Tue Sep 01 14:03:24 MST 2015
Hello,
I am having problems with the Chip_I2C_MasterTransfer function in LPCOpen 2.12. I have an application that communicates to several different I2C slaves using I2C1. The M0 core application handles the I2C communications. I am using the polled version without interrupts. The application will run fine for weeks/months and then lock up. Each time that I have seen the lockup, I connect with my IAR debugger using the "attach to running target" option and I find that the code is locked up in the Chip_I2C_EventHandlerPolling function in i2c_18xx_43xx.c. Actually I have defined my own handler which is shown below. The problem occurs when the SI flag never gets set in the CONSET register. This causes and endless loop waiting for the SI bit.
In one of the prior instances where I saw this problem, the I2EN and STA bits were both set but the SI bit was never set. It seemed to match the description in section 46.10.6.3 of the LPC4350 user manual. I implemented a custom My_I2C_EventHandlerPolling function (shown below) to force access to the bus as described in the manual. I also found some other examples on LPCWare that showed fixes very similar to the one I implemented.
In my most recent occurrence of the problem that I have captured in my IAR debugger today, only the I2EN flag is set in the CONSET register. The check that I have requiring both the I2EN and STA bits to be set was never true. This means that I am once again stuck in this endless loop. Additionally, I am not sure if simply setting the STO bit is the proper way to trigger an exit out of the endless loop either. Looking at the SDA and SCL lines on my scope, both are resting high. Analyzing all of the other information available to me in the debugger in this case: I was sending 3 bytes of data to an i2c humidity/temperature sensor. From the iic structure declared in the My_I2C_EventHandlerPolling function (attached as an image file), it looks like all three bytes were transmitted, txSz = 0. All of the flags appear to be in the normal condition as if the code had just completed sending the 3 bytes successfully, except that the status is still = I2C_STATUS_BUSY. Apparently after sending the last byte, the SI bit was never set, or maybe cleared somehow after being set and before being detected. The STATUS register was equal to 0xF8 so no state information is available.
Any ideas on what may have caused this or the proper way to fix/recover from it. I certainly wish that NXP would jump on this and solve the problem in the LPCOpen library. I would think that NXP, being the I2C inventor would want to provide the best support of the I2C bus in their latest processors!
Here is my custom version of the EventHandler:
// Redefined I2C_EventHandlerPolling to allow for timeouts when the
// Start Flag (SA) is set but no State Change Interrupt Flag SI gets set.
// In this case, you have to force bus access by setting the STOP flag STO
#define MAX_I2C_ATTEMPTS_BEFORE_FORCING ( 1000000 )
/* Chip polling event handler */
void My_I2C_EventHandlerPolling(I2C_ID_T id, I2C_EVENT_T event)
{
uint32_t attempts = 0;
struct i2c_interface *iic = get_i2c_interface(id);
volatile I2C_STATUS_T *stat;
/* Only WAIT event needs to be handled */
if (event != I2C_EVENT_WAIT) {
return;
}
stat = &iic->mXfer->status;
/* Call the state change handler till xfer is done */
while (*stat == I2C_STATUS_BUSY) {
if (Chip_I2C_IsStateChanged(id)) {
Chip_I2C_MasterStateHandler(id);
} else {
// 6/22/2015 GAD added to allow recovery from and error condition
// that doesn't allow the SI bit to be set in the CONSET register.
// See the following section in the users guide:
// 46.10.6.3 Forced access to the I2C-bus
if (iic->ip->CONSET == (I2C_CON_STA | I2C_CON_I2EN)) {
// if we have been stuck with only the STA and I2EN bits set for
// a long time then force access by setting the STO bit.
if (++attempts > MAX_I2C_ATTEMPTS_BEFORE_FORCING) {
iic->ip->CONSET = I2C_I2CONSET_STO;
attempts = 0;
i2c.i2c_SI_timeouts[id]++;
}
} else
attempts = 0;
}
}
}
Here is the main calling code:
void i2c1_read_humidity( void ) {
I2C_XFER_T transferMCfg;
I2C_STATUS_T status;
if (humidity_sensor.mode == trigger_read) {
transferMCfg.slaveAddr = HUMIDITY_SENSOR_ADDR;
transferMCfg.rxBuff = i2c1_rx_buf;
transferMCfg.txBuff = i2c1_tx_buf;
transferMCfg.txSz = 3;
transferMCfg.rxSz = 0;
i2c1_tx_buf[0] = 0x80;
i2c1_tx_buf[1] = 0;
i2c1_tx_buf[2] = 0;
Chip_I2C_MasterTransfer(I2C1, &transferMCfg);
status = transferMCfg.status;
if (status == I2C_STATUS_DONE) {
humidity_sensor.mode = trigger_fetch1;
return;
} else {
humidity_sensor.mode = read_failure;
}
}
After several weeks, it gets stuck in the call to Chip_I2C_MasterTransfer.
Thanks,
Greg Dunn