I2C driver, Read function can block

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

I2C driver, Read function can block

1,282 Views
arnogir
Senior Contributor II

Hello,

I'm on a Kinetis K70 with an I2C device (to manage tactile screen)

To be faster, this device can accept an "immediate" read: We don't sent adress where we want read, by this, we automatically receive the touch screen coordinate.

To do this, I wrote the following code:

Param = MySlaveAddress;

ioctl(fd, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &Param);

Param = 4;
Error = ioctl(fd, IO_IOCTL_I2C_SET_RX_REQUEST, &Param);

/* Read all data */
  ReturnValue = 0;
  do
  {
    ReturnValue += fread(&BufferPtr[ReturnValue], 1, 4- ReturnValue, fd);
  }
  while (ReturnValue < 4);

When peripheral not connected (or with problem), the ACK after "START + SlaveAddress + R" is a NACK.

But we not detect it and then the first "fread" return 0 (because 0 byte read), with the while, we call again the fread which in this case never return..

How can we detect the NACK between FIsrt byte (START + SLA +R) and first read Byte?

With oscilloscope, all 5 pulses clock (1 start + 4 Bytes) are sent on the call of first "fread" (when all work correctly with an ACK).

When Nack, only first pulse clock is sent with  NACK.

Labels (1)
0 Kudos
7 Replies

858 Views
arnogir
Senior Contributor II

Hi,

I tested the proposed solution.

When I unplug manually the I2C slave just before call fread (With breakpoint on debug mode), the while loop is exited because status is finished.

But I made also the following test:

- Unplug and plug the I2C slave: I2C slave communication is recover both in polling and interrupt mode ==> OK

- Make power supply noise with specific tool to pass EMF tests: Polling mode is OK, Interrupt mode ==> NOK: I2C communication is lost. But in this case, I can't read with the P&E debug because the generated nois make lost the communication between KDS on target...

0 Kudos

858 Views
isaacavila
NXP Employee
NXP Employee

Hi Arnaud,

Have you tried to attach to MCU once it failed?

Here are some post on how to attach to a running target in CW and KDS:

https://mcuoneclipse.com/2012/10/10/attach-connect-download/ 

https://community.nxp.com/thread/330219 

I hope you can find them useful to detect the exact point where I2C communication crashes.

Regards,

Isaac

0 Kudos

858 Views
arnogir
Senior Contributor II

Hello,

Just to inform I will test you asked, but currently, I'm working on other project which have higher priority. (Other project with current USB bug using MQX 4.1 without success and not yet response from the community :-( https://community.nxp.com/thread/437700?prevContainerType=14&prevContainerID=2019)

I try to do this on i2c as soon as possible.

Thank for your help and implication

0 Kudos

858 Views
arnogir
Senior Contributor II

Hello

Test the State seems a good idea.

I will test that and back here.

Thank you

0 Kudos

858 Views
isaacavila
NXP Employee
NXP Employee

Hello Arnaud,

Doing a quick review on K70's driver, it seems that after a RX request, MQX's I2C driver does not check for the ACK/NACK when sends the ADDRESS + R data.

Also, there is no API to detect if ACK/NACK was received when module is waiting for slave's data.

In this case, you can use this "0" read bytes to identify if communication is being handled correct. You can try to use something like this to try 5 times and in case that in this 5 times fread returns 0, then, finish the do-while loop and notify an error.

Param = 4;
Error = ioctl(fd, IO_IOCTL_I2C_SET_RX_REQUEST, &Param);

uint32_t times = 0;
/* Read all data */
ReturnValue = 0;
do
{
     ReturnValue += fread(&BufferPtr[ReturnValue], 1, 4- ReturnValue, fd);
     if (ReturnValue == 0)
     {
             times++;
     }
}
while ((ReturnValue < 4) && (times < 5));

if (times >= 5)
{
    /* there was a timeout error, notify to user/application */
}
 


This workaround will avoid to be in the do-while loop if slave does not response in more than 5 retries.

I hope this can help you!

Best Regards,

Isaac

0 Kudos

858 Views
arnogir
Senior Contributor II

Hello,

Yes I tried this. But in fact, the first called to fread return 0. But on the second call, fread never return and my task is finally blocked.

I d'ont know if in normal situation, the fread can return 0? (I think yes when return is made before first interrupt notification of end of I2C transmission, depending to the I2C clock speed I think..

So I havn't solution for the moment except use driver in pulling instead of interrupt. The fread is called only one time so problem not occurs because on fread return, we must have a value different to 0.

But use driver in pulling mode is not efficient for me :-(

0 Kudos

858 Views
isaacavila
NXP Employee
NXP Employee

Hello Arnaud,

Yes, Indeed previous solution applies for polling mode driver, if you are using interrupt you will need to add following extra logic.

As you already suppose, fread will return the first time with 0 and this will wait the interrupt and check if ACK was received, in case that NO ACK is received, then I2C communication is finished:

     /* Transmit */
      if (i2c_ptr->C1 & I2C_C1_TX_MASK)
      {
         /* Not ack */
         if (i2csr & I2C_S_RXAK_MASK)
         {
            io_info_ptr->STATE = I2C_STATE_FINISHED;
            io_info_ptr->STATISTICS.TX_NAKS++;
            _bsp_int_disable(io_info_ptr->VECTOR);
            _lwsem_post((LWSEM_STRUCT_PTR)(&(io_info_ptr->LWSEM)));
         }‍‍‍‍‍‍‍‍‍‍‍

So, after requesting data from slave, you can get the STATE for current transaction and, in case that this state is I2C_STATE_FINISHED, then you can stop your do-while loop.

I've made a quick test and it worked for me:

     uint8_t state = 0;
     do {
          ReturnValue += fread(&BufferPtr[ReturnValue], 1, 4- ReturnValue, fd);
          (void)ioctl(fd, IO_IOCTL_I2C_GET_STATE, (void *) &state);
     } while ((ReturnValue < 4) && (state != I2C_STATE_FINISHED));‍‍‍‍‍

This will avoid that program gets blocked on fread function when slave didn't send the ACK.

P.S. Another workaround could be to validate the NACK saved in statistics, you can use a ioctl to get statistics and validate if NACK were received.

I hope this can help you!

Regards,

Isaac

0 Kudos