apache / nuttx

Apache NuttX is a mature, real-time embedded operating system (RTOS)
https://nuttx.apache.org/
Apache License 2.0
2.49k stars 1.05k forks source link

I2C NAK issue : Tiva c 123gxl #12466

Open shijogeorge24 opened 4 weeks ago

shijogeorge24 commented 4 weeks ago

I was working with the I2C sensor AS5048B. I encountered this error and tried different clock speeds and changed the I2C timeout settings, but the problem still exists.

tiva_i2c_transfer: I2C1: msgc=1 tiva_i2c_setclock: I2C1: frequency: 25 tiva_i2c_transfer: ERROR: I2C1 I2C error status: 0000002e tiva_i2c_transfer: I2C1: msgc=1 tiva_i2c_setclock: I2C1: frequency: 25 tiva_i2c_transfer: ERROR: I2C1 I2C error status: 0000002e wortiva_i2c_transfer: I2C1: msgc=1 tiva_i2c_setclock: I2C1: frequency: 25 tiva_i2c_transfer: ERROR: I2C1 I2C error status: 0000002e ld utiva_i2c_transfer: I2C1: msgc=1 tiva_i2c_setclock: I2C1: frequency: 25 tiva_i2c_transfer: ERROR: I2C1 I2C error status: 0000002e

I checked the driver code, and there is no reset option in the AS5048B driver or in QECoder. The TivaC I2C driver does not handle this error condition; it simply returns the error values. How can I solve this issue?

https://github.com/apache/nuttx/tree/master/drivers/i2c https://github.com/apache/nuttx/blob/master/arch/arm/src/tiva/common/tiva_i2c.c https://github.com/apache/nuttx/blob/master/arch/arm/src/tiva/tiva_i2c.h https://github.com/apache/nuttx/blob/master/drivers/sensors/as5048b.c https://github.com/apache/nuttx/blob/master/drivers/sensors/qencoder.c

Here, I am attaching the bus logic analyzer outputs. Screenshot 2024-06-06 202605 The device 0x43 was supposed to receive the data 0xFE and send the ACK for it, but this is missing.

acassis commented 4 weeks ago

@hartmannathan I think you are used to work with TIVA, do you have any idea?

hartmannathan commented 4 weeks ago

Hi Alan,

I still work with Tiva. However, I haven't done any work with I2C (on any microcontroller). I can try to take a look a little later...

On Thu, Jun 6, 2024 at 12:37 PM Alan Carvalho de Assis < @.***> wrote:

@hartmannathan https://github.com/hartmannathan I think you are used to work with TIVA, do you have any idea?

— Reply to this email directly, view it on GitHub https://github.com/apache/nuttx/issues/12466#issuecomment-2152956436, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOD4O5ZKRUJD2JR6VYFJVELZGCF5FAVCNFSM6AAAAABI45MFR2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNJSHE2TMNBTGY . You are receiving this because you were mentioned.Message ID: @.***>

hartmannathan commented 4 weeks ago

@hartmannathan I think you are used to work with TIVA, do you have any idea?

@acassis first idea is to check hardware: pull-up or pull-down resistors required/installed? (Probably pull-ups on SCL/SDA?) All hardware IC pins are soldered well? Inspected and don't have solder bridges? These are the most common hardware issues I've run into over the years.

If hardware is OK, is there a minimal I2C example that can be run with TI's TivaWare (without NuttX)? If it works with TivaWare but not with NuttX then we will know we have a bug that needs to be fixed.

As mentioned earlier I have not worked with I2C so I don't know the status of our I2C code. But I suggest to check the things above to narrow down the cause to hardware or software.

acassis commented 4 weeks ago

@shijogeorge24 try to send the same I2C command that is failing using TivaWare as Nathan suggested, maybe it could be some hardware issue.

shijogeorge24 commented 3 weeks ago

Today, I conducted a series of tests and identified that the issue originates from the hardware. I am running an I2C line approximately 1 meter long with 4 sensors on the same bus. Additionally, I use an Adafruit LTC 4311 to extend the I2C bus. I have driver code for the AS5048B sensor using TivaWare. There are some bus issues in that setup, but the code resets the line whenever there is a problem. It recover and i can use it after an error.

In my NuttX RTOS setup, I have three different tasks: one for the AS5048B sensor, one for UART, and another for the CAN bus. In this setup, I have 4 motors which introduce noise to the I2C bus. Everything works fine when the motors are off, but the issues start whenever the motors are on. Sometimes the problems begin immediately, while other times they start after 5 or 10 minutes,Once the error occurs, the bus never recovers from the issue. Here is my I2C task:

include <nuttx/config.h>

include <nuttx/mqueue.h>

include

include

include

include

include

include

include <nuttx/fs/ioctl.h>

include <nuttx/sensors/qencoder.h>

include <nuttx/clock.h>

include

include

include "encoder.h"

define ACCEL_TASK_INTERVAL_MS 10

// struct encoder_queue_msg { // uint32_t a0; // uint32_t a1; // uint32_t a2; // uint32_t a3; // };

int as5048_0(int argc, char *argv[]) { printf("Starting as5048 0 and 1 \n");

mqd_t mqd;
if (argc < 2) {
    printf("Usage: %s <message_queue_descriptor>\n", argv[0]);
    return EXIT_FAILURE;
}
mqd = (mqd_t)*(argv[1]);

mqd_t encoder_request = (mqd_t)*argv[5];

int fd0, fd1, fd2, fd3, ret;
struct encoder_queue_msg encoder_msg;

struct encoder_queue_request_msg requuested;

unsigned int buff0 = 0;
fd0 = open("/dev/as0", O_RDONLY);
if (fd0 < 0) {
    perror("Failed to open as5048 0");
    return EXIT_FAILURE;
}

unsigned int buff1 = 0;
fd1 = open("/dev/as1", O_RDONLY);
if (fd1 < 0) {
    perror("Failed to open as5048 1");
    close(fd0);
    return EXIT_FAILURE;
}

unsigned int buff2 = 0;
fd2 = open("/dev/as2", O_RDONLY);
if (fd2 < 0) {
    perror("Failed to open as5048 2");
    close(fd0);
    close(fd1);
    return EXIT_FAILURE;
}

unsigned int buff3 = 0;
fd3 = open("/dev/as3", O_RDONLY);
if (fd3 < 0) {
    perror("Failed to open as5048 3");
    close(fd0);
    close(fd1);
    close(fd2);
    return EXIT_FAILURE;
}

struct pollfd fds[1];
fds[0].fd = encoder_request;
fds[0].events = POLLIN;

bool sensors0,sensors1,sensors2,sensors3;

while (1) {
    // ret = poll(fds, 1, 30); // Wait for events with a timeout of 30 milliseconds
    // if (ret < 0) {
    //     perror("poll() failed");
    //     break;
    // }
    // } else if (ret == 0) {
    //     printf("Poll timeout\n");
    //     continue; // No events occurred within the timeout period, continue waiting
    // }

sensors0=0; sensors1=0; sensors2=0; sensors3=0; printf("Encoder task\n");

ret = poll(fds, 1, 50); if (ret < 0) { perror("poll() failed"); break; }

  int sched_ret=0;

    if (1) {

        sched_ret=sched_lock();

        ret = ioctl(fd0, QEIOC_POSITION, &buff0);

        sched_ret=sched_unlock();
        if (ret < 0) {
            perror("Failed to read position from as5048 0");
            printf("encoder 0  erro =%d\n",ret);

            //break;
        }
        else
        {
          sensors0=1;
        }
          sched_ret=sched_lock();

        ret = ioctl(fd1, QEIOC_POSITION, &buff1);
         sched_ret=sched_unlock();
        if (ret < 0) {
            perror("Failed to read position from as5048 1");
              printf("encoder 1  erro =%d\n",ret);

           //break;
        }
        else
        {
          sensors1=1;
        }
            sched_ret=sched_lock();
        ret = ioctl(fd2, QEIOC_POSITION, &buff2);
         sched_ret=sched_unlock();
        if (ret < 0) {
            perror("Failed to read position from as5048 2");
              printf("encoder 2  erro =%d\n",ret);
          // break;
        }
        else
        {
          sensors2=1;
        }
        sched_ret=sched_lock();
        ret = ioctl(fd3, QEIOC_POSITION, &buff3);

        sched_ret=sched_unlock();
        if (ret < 0) {
            perror("Failed to read position from as5048 3");
              printf("encoder 3  erro =%d\n",ret);
            //break;
        }
        else
        {

          sensors3=1;
        }

       if(sensors0 && sensors1 && sensors2 && sensors3)
       {
        float y = 16384;
        encoder_msg.a0 = (uint32_t)((buff0 / y) * 360);
        encoder_msg.a1 = (uint32_t)((buff1 / y) * 360);
        encoder_msg.a2 = (uint32_t)((buff2 / y) * 360);
        encoder_msg.a3 = (uint32_t)((buff3 / y) * 360);

       }
       else
       {
        printf("one sensor error\n");
       }

    }

     if (fds[0].revents & POLLIN) {

ret = mq_receive(encoder_request, (char*)&requuested, sizeof(requuested), NULL);

// printf("waiting\n");
ret = mq_send(mqd, (const char*)&encoder_msg, sizeof(encoder_msg), 0);
if (ret < 0) {
  perror("Failed to send message");
        }

         }

    usleep(ACCEL_TASK_INTERVAL_MS *5000L); // usleep() takes microseconds
}

close(fd0);
close(fd1);
close(fd2);
close(fd3);

return EXIT_SUCCESS;

}

Do you think just returning an error value without clearing the error flags and not checking for errors before transmission in the I2C Tiva driver might be the issue?

https://github.com/apache/nuttx/blob/c885ba7080f843be0d2881886ff0f48412bc70cc/arch/arm/src/tiva/common/tiva_i2c.c#L1590

IMG-20240605-WA0009 IMG-20240605-WA0015

acassis commented 3 weeks ago

@shijogeorge24 I2C is not a kind of bus for long distance applications. You can try these ideas to improve the signal integrity:

1) use a low value pull up resistors: i.e. 2Kohms or even 1Kohms 2) use an I2C booster/accelerator like LTC4311

shijogeorge24 commented 3 weeks ago

I am using the Adafruit LTC4311, which has onboard 10k pull-ups

hartmannathan commented 3 weeks ago

@shijogeorge24 I2C is not a kind of bus for long distance applications.

You can try these ideas to improve the signal integrity:

1) use a low value pull up resistors: i.e. 2Kohms or even 1Kohms

2) use an I2C booster/accelerator like LTC4311

Agreed I2C isn't optimal for long distance (like 4 meters). Usually when we need long distances we use differential transceivers, though it isn't obvious to me how to do bidirectional communication of I2C like that.

You showed the logic analyzer screenshot earlier; I recommend to also look with an oscilloscope if one is available and see if the signals are nice and square.

Regarding the software, if I understand correctly you said TivaWare does recover from the error but NuttX does not recover. Therefore I recommend to study the TivaWare code to see how the error handling code works, and then see if NuttX I2C driver is missing that handling. Maybe there is an additional interrupt we need to handle, probably to clear some error bit in some register and signal somehow the error to the application.

acassis commented 1 week ago

@shijogeorge24 did you get it working? If so, please close this issue

shijogeorge24 commented 1 week ago

I am making a few hardware changes. Once they are done, I will test and update.