[LPCOpen][LPC17xx] I2C master freeze

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

[LPCOpen][LPC17xx] I2C master freeze

3,417 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pgr on Thu Apr 10 01:56:38 MST 2014
Dear all,

I am using I2C0 as a master on a LPC1763 with LPCOpen stack.
Sometimes randomly the I2C driver freeze inside Chip_I2C_EventHandlerPolling() function.
The Chip_I2C_IsStateChanged() function never return a change so the status is never updated...
I have added some trace to see that I2CSTAT stay at 0xf8 and I2CONSET stay at 0x60.

I have tried to use the interrupt base handler with exactly the same results.

Do you have any idea why I have this behavior ? Any idea to add a timeout mechanism in the I2C stack ?

Thanks in advance for your help
Labels (1)
10 Replies

2,069 Views
danielholala
Senior Contributor II

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; // adapt value to hardware setup and I2C clock rate
 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;
 
      /* Only WAIT event needs to be handled */
      if (event != I2C_EVENT_WAIT) {
           return;
      }
 
      stat = &iic->mXfer->status;
      /* Wait for the status to change */
-     while (*stat == I2C_STATUS_BUSY) {}
+     while (*stat == I2C_STATUS_BUSY) {
+          busy_counter++;
+          if (busy_counter > i2c_busy_limit) {
+               // I2C bus hang-up identified...
+               i2c_busy_timeouts++;
+               *stat = I2C_STATUS_BUSERR;
+               i2c[id].ip->CONSET = I2C_CON_STO; // set stop bit for forced access to the I2C bus
+               break;
+          }
+     }
 }

and further down in Chip_I2C_MasterTransfer():

      iic->mEvent(id, I2C_EVENT_WAIT);
      iic->mXfer = 0;
 
-     /* Wait for stop condition to appear on bus */
-     while (!isI2CBusFree(iic->ip)) {}
+     if (xfer->status != I2C_STATUS_BUSERR) {
+          /* Wait for stop condition to appear on bus */
+          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 intermittant 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 that 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():

 /* State change handler for master transfer */
 void Chip_I2C_MasterStateHandler(I2C_ID_T id)
 {
+     if (!i2c[id].mXfer) {
+          // Sometimes (e.g., when I2C is blocked intermittently)
+          // something (e.g., Chip_I2C_MasterTransfer())
+          // clears i2c[id].mXfer. In this case don't call
+          // handleMasterXferState() as it dereferences mXfer
+          // and this leads to a hard fault.
+          i2c[id].ip->CONCLR = I2C_CON_SI; // clear interrupt bit to stop isr from being called repeatedly
+          return;
+     }
      if (!handleMasterXferState(i2c[id].ip, i2c[id].mXfer)) {
           i2c[id].mEvent(id, I2C_EVENT_DONE);
      }
 }

Does that make sense to you?

Cheers!

Daniel

0 Kudos

2,069 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mrdjoni on Tue Sep 29 04:53:45 MST 2015
Hi,
I have same problem with LPC2132 but whatever I do it remain stuck...
All the best
Miroslav
0 Kudos

2,069 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pgr on Thu Jul 10 23:39:17 MST 2014
Hi joao,

Yes I have "simply" edited LPCOpen to add the timeout and it works perfectly.

0 Kudos

2,069 Views
asobralg1
Contributor I

Will you tell me what did you do? I'm trying to solve the same issue.

Thanks!

0 Kudos

2,069 Views
danielholala
Senior Contributor II

My code suggestions are based on the LPCOpen I2C stack for LPC175x/176x mcu and are reproduced below. Also, I suggest to get a logic analyzer (if you don't have one already). I'm using a Saleae logic analyzer.

0 Kudos

2,069 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by joaoricardo on Thu Jul 10 11:53:00 MST 2014
I'm having the same problem with a 4088, the event handler keeps stuck on busy mode. I can't figure out a way to create a custom event handler without changing the original lpcopen code, since my custom event handler can't reach i2c_interface structure definition (defined in i2c_17xx_40xx.c) and i2c on DSA (declared in i2c_17xx_40xx.c) since both are private.

Anyway, checking with a scope I can see some spikes on the SDA line, maybe this is causing the problem, but I don't believe it is enough to block data transfer. Probably it is just preventing i2c hardware to reach stop condition.

Maybe time out will be enough...but I can't figure out how to make it.
0 Kudos

2,069 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by sundarapandian on Tue Apr 15 18:39:17 MST 2014
The loops inside the event handlers are intentionally in an infinite busy wait, as a way to catch bug in the I2C application or hardware (slave/bus lines). If you want to use Chip_I2C_SetMasterEventHandler API to override the function with a function of your own, with timeout.
0 Kudos

2,069 Views
danielholala
Senior Contributor II

lpcware wrote:

Content originally posted in LPCWare by sundarapandian on Tue Apr 15 18:39:17 MST 2014
The loops inside the event handlers are intentionally in an infinite busy wait, as a way to catch bug in the I2C application or hardware (slave/bus lines). If you want to use Chip_I2C_SetMasterEventHandler API to override the function with a function of your own, with timeout.

Hi,

Thank you for providing this critical piece of information. I wonder which loops are " intentionally in an infinite busy wait".

 

So I set up an I2C bus with SDA and SCL permanently pulled down low to simulate a hardware bug or bus obstruction. Then I connected this bus to an LPC1769. Finally, I used lpcopen_2_10_lpcxpresso_nxp_lpcxpresso_1769 to create a minimal I2C application that reads bytes using Chip_I2C_MasterRead()

Indeed, according to the debugger, the execution is permanently stuck in Chip_I2C_EventHandler():

/* Wait for the status to change */
 while (*stat == I2C_STATUS_BUSY) {}

Are you saying, this loop is an example for the intentionally added "infinite busy wait" in order to catch application or hardware bugs?

I'm baffled as I could not find any comments to this regard in i2c_17xx_40xx.c/h. That is, for high quality source code I expect that comments are added to indicate locations where an infinite loop is added to catch application or hardware bugs. The piece of code quoted above does not live up to this expectation. It looks "harmless" and in not way invites the developer to watch this location, e.g., by setting a break point to it. 

I'm also bewildered that the LPCOpen example code does not take into account the advise given in the User Manual, e.g. UM10360, chapter 19.9.7.4 "I2C-bus obstructed by a LOW level on SCL or SDA" etc.

Thanks

Daniel

0 Kudos

2,069 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pgr on Thu Apr 10 04:17:47 MST 2014
Ok I have made a quick implementation of this and it solved the issue I had.
Find below my quick patch, I let you fix this in LPCOpen in a more cleaner way. It should also be fixed for interrupt based I2C in function Chip_I2C_EventHandler()

#P lpc_chip_175x_6x
Index: src/i2c_17xx_40xx.c
===================================================================
--- src/i2c_17xx_40xx.c(revision 69)
+++ src/i2c_17xx_40xx.c(working copy)
@@ -359,6 +359,7 @@
 /* Chip polling event handler */
 void Chip_I2C_EventHandlerPolling(I2C_ID_T id, I2C_EVENT_T event)
 {
+int retry = 500;
 struct i2c_interface *iic = &i2c[id];
 volatile I2C_STATUS_T *stat;
 
@@ -373,6 +374,9 @@
 if (Chip_I2C_IsStateChanged(id)) {
 Chip_I2C_MasterStateHandler(id);
 }
+if(--retry == 0){
+LPC_I2Cx(id)->CONSET = I2C_CON_STO;
+}
 }
 }
 

2,069 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pgr on Thu Apr 10 02:05:23 MST 2014
I just see this on the LPC17xx user manual (UM10360):


Quote:
Forced access to the I2C-bus
In some applications, it may be possible for an uncontrolled source to cause a bus
hang-up. In such situations, the problem may be caused by interference, temporary
interruption of the bus or a temporary short-circuit between SDA and SCL.
If an uncontrolled source generates a superfluous START or masks a STOP condition,
then the I2C-bus stays busy indefinitely. If the STA flag is set and bus access is not
obtained within a reasonable amount of time, then a forced access to the I2C-bus is
possible. This is achieved by setting the STO flag while the STA flag is still set. No STOP
condition is transmitted. The I2C hardware behaves as if a STOP condition was received
and is able to transmit a START condition. The STO flag is cleared by hardware
Figure 98



I think this should be added in LPCOpen, what do you think ?