0
\$\begingroup\$

I have been trying to read FIFO buffer from LSM6DS3 inertial sensor via i2c. The processor will read LSM6DS3 buffer every 20s so the FIFO will contain approximately 3120 bytes of accelerometer data. The sensor contains 2 registers to read FIFO data which are FIFO_DATA_OUT_L and FIFO_DATA_OUT_H.

In my host, I use read-write system call to communicate with the sensor. And My i2c clock is 400KHz also I have couple of other devices connected on the same bus.

i2c_init()
{
    fd_i2c4=open("/dev/i2c-4",O_RDWR);/*open i2c. fd_i2c4 is global*/
    if(fd_i2c4<0)
    {
        perror("i2c device open : ");
        BLOGV(LOG_TAG "i2c device open error");
        return -1;
    }
    if(ioctl(fd_i2c4,I2C_SLAVE,addr)<0)
    {
        perror("i2c slave error : ");
        BLOGV(LOG_TAG "i2c slave error");
        return -1;
   }
}

This is my i2c read function

static int32_t i2c_read_low_latency(int *file,__u8 reg,__u8 *buff,__u16 len)
{
    buff[0]=reg;
    int temp=write(*file, buff, 1);
    temp=read(*file, buff, len);
    if ( temp!= len) {
        /* ERROR HANDLING: i2c transaction failed */
        printf("i2c read error \n");
      }
    return 0;
}

and FIFO reading function pseudo code,

fifo_read()
{
    int fifo length=get_fifo_data_level()  //to get number of available data in fifo.
    while(length-- >0)
    {
        i2c_read_low_latency(&fd_i2c4,LSM6DS3_FIFO_DATA_OUT_L,ch,2); // x axis data.
        i2c_read_low_latency(&fd_i2c4,LSM6DS3_FIFO_DATA_OUT_L,ch,2); // y axis data.
        i2c_read_low_latency(&fd_i2c4,LSM6DS3_FIFO_DATA_OUT_L,ch,2); // z axis data.
    }
}

The problem is it takes around 2-3 sec to read complete data. this consumes lot of power. Any idea why this slow reading?. link to data sheet lsm6ds3

\$\endgroup\$
  • \$\begingroup\$ Can you check the I2C communication on a scope once to see how long the actual communication is happening? \$\endgroup\$ – User323693 Feb 5 at 8:58
  • \$\begingroup\$ I cant directly probe the i2c line due to product hardware limitations. anyway, I will try with the LSM6DS3 development board. \$\endgroup\$ – code_numb Feb 5 at 11:02
  • \$\begingroup\$ What Linux system (hardware) are you using? \$\endgroup\$ – Codo Feb 5 at 14:03
  • \$\begingroup\$ Qualcomm processor. \$\endgroup\$ – code_numb Feb 6 at 5:26
2
\$\begingroup\$

If I'm not mistaken, your code is a regular Linux application, i.e. it runs in user space. Therefore, each call to read or write requires a context switch from user to kernel space. Most likely the process will also be paused while it waits for the answer from the I2C bus and needs to be rescheduled once the answer is ready. That has its cost.

Given your data, your code reads 3120 bytes from the sensor in about 2 seconds. Each byte requires two operations (a read and a write call):

2s ÷ (*3120* × 2) = 0.32ms

0.32ms sounds about right as the cost for the context switching and process scheduling. The I2C speed is bascially irrelevant. It's the pauses between the I2C transactions that make up the bulk of the cost.

To improve the speed, you will have to reduce the number of context switches, i.e. the number of I2C operations.

An easy solution to cut the number of operations in half is to use i2c_smbus_read_byte_data. It combines writing the regiser address and reading a value into a single operation.

Further optimization depends on the sensor. Usually, there is a way to read many bytes from a FIFO at once. If that's possible, i2c_smbus_read_block_data might be helpful. I'm not sure the LSM6DS3 supports it. Its documentation left me clueless.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I'm not completely sure about context switching. I have tried direct IOCTL calls and smbus_read/write calls but these took 3-4 sec. I cannot use SMBus block read since sensor has no option to read FIFO as blocks. \$\endgroup\$ – code_numb Feb 5 at 10:59
  • \$\begingroup\$ The idea is to replace the two IO operations (read and write) in i2c_read_low_latency with a single i2c_smbus_read_byte_data call. That should increase the speed. It doesn't sound as if that's what you have tried. And for the block read: Are you sure? Have you tried it? \$\endgroup\$ – Codo Feb 5 at 11:52
  • \$\begingroup\$ I replaced my i2c_read_low_latency() calles with res=i2c_smbus_read_word_data(fd_i2c4,LSM6DS3_FIFO_DATA_OUT_L); but no improvement \$\endgroup\$ – code_numb Feb 5 at 12:54
  • \$\begingroup\$ If context switching is limiting factor and if most significant 8 bit suffice then according to datasheet I think it is possible to disable automatic address increment and read whole fifo in one (two) context switch. If all 16 bits are needed then you will be limited by context switching or you could write/modify i2c driver. @code_numb \$\endgroup\$ – Rokta Feb 5 at 13:39
  • \$\begingroup\$ @Rokta That's the feature I was looking for in the datasheet. Can the address increment be configured via registers? \$\endgroup\$ – Codo Feb 5 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.