Page 1 of 2

I2C burst read corrupting data

Posted: Tue Aug 27, 2024 2:40 pm
by PeteDD
I am attempting to read 192 bytes of data from the FIFO buffer of an MMA8451Q accelerometer using an ESP32-S3-Devkit-U1.
This should be pretty simple... write to the appropriate register and then conduct the 192 reads.

Code: Select all

#define use14bit true
#define MMA8451_FIFO_REG 0x01
#uint8_t FIFObuffer[192];

bool PD_MMA8451::getFIFO(bool use14bit)
{
  uint8_t targetRegister[1] = {MMA8451_FIFO_REG};
  return (i2c_dev->write_then_read(targetRegister, 1, FIFObuffer, use14bit ? 192 : 96));
}

The 192 bytes are organized as 32 sets of 6 bytes. The 6 bytes are 2 bytes for each axis: x, y, and z. The two bytes are then converted to a 14-bit twos-complement value and that value is then converted to a g value.

What I am seeing is that the first 21 sets of six bytes is correct and then the remaining 11 sets are non-sensical.
Below is the raw data followed by the data converted to g values. With the sensor motionless, the converted x and y values should be near zero and the z values should be near 1.00. That is what we see for the first 21 events and then corrupted data for the remaining 11. The first column contains the sample number.

I have tried various I2C frequencies from 400KHz down to 50KHz and nothing changes (except the elapsed time).
I have tried this with and without pull-up resistors and nothing changes.

Code: Select all

Elasped Time 4604 microseconds
0       0       80      255     152     16      48
1       0       80      255     152     16      52
2       0       84      255     152     16      48
3       0       80      255     152     16      48
4       0       72      255     152     16      52
5       0       80      255     160     16      48
6       0       80      255     156     16      28
7       0       84      255     152     16      56
8       0       80      255     156     16      44
9       0       80      255     152     16      44
10      0       84      255     156     16      48
11      0       84      255     152     16      48
12      0       80      255     148     16      48
13      0       80      255     156     16      56
14      0       80      255     148     16      44
15      0       76      255     148     16      48
16      0       80      255     156     16      40
17      0       80      255     152     16      48
18      0       72      255     152     16      60
19      0       80      255     156     16      56
20      0       76      255     152     16      48
21      0       80      11      0       84      255
22      160     16      36      0       72      255
23      152     16      40      0       80      255
24      156     16      48      0       84      255
25      152     16      52      0       80      255
26      148     16      40      0       84      255
27      156     16      40      0       80      255
28      152     16      44      0       80      255
29      152     16      48      0       84      255
30      156     16      44      0       80      255
31      156     16      48      128     128     128

0       0.02,   -0.03,  1.01
1       0.02,   -0.03,  1.01
2       0.02,   -0.03,  1.01
3       0.02,   -0.03,  1.01
4       0.02,   -0.03,  1.01
5       0.02,   -0.02,  1.01
6       0.02,   -0.02,  1.01
7       0.02,   -0.03,  1.01
8       0.02,   -0.02,  1.01
9       0.02,   -0.03,  1.01
10      0.02,   -0.02,  1.01
11      0.02,   -0.03,  1.01
12      0.02,   -0.03,  1.01
13      0.02,   -0.02,  1.01
14      0.02,   -0.03,  1.01
15      0.02,   -0.03,  1.01
16      0.02,   -0.02,  1.01
17      0.02,   -0.03,  1.01
18      0.02,   -0.03,  1.01
19      0.02,   -0.02,  1.01
20      0.02,   -0.03,  1.01
21      0.02,   0.69,   5.31
22      -6.00,  2.25,   4.56
23      -6.50,  2.50,   5.06
24      -6.25,  3.00,   5.31
25      -6.50,  3.25,   5.06
26      -6.75,  2.50,   5.31
27      -6.25,  2.50,   5.06
28      -6.50,  2.75,   5.06
29      -6.50,  3.00,   5.31
30      -6.25,  2.75,   5.06
31      -6.25,  3.03,   -7.97

The I2C library I am using is from Adafruit. The exact code that is executing for the write_then_read function is as follows and looks pretty straight forward. (I have striped this down to remove any #ifdefs that would not be active) It all really comes down to just this...

Code: Select all

write(write_buffer, write_len, stop);
// and then this...
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len, (uint8_t)stop);
but here is all the code...

Code: Select all


bool Adafruit_I2CDevice::write_then_read(const uint8_t *write_buffer,
                                         size_t write_len, uint8_t *read_buffer,
                                         size_t read_len, bool stop) {
  if (!write(write_buffer, write_len, stop)) {
    return false;
  }

  return read(read_buffer, read_len);
}

bool Adafruit_I2CDevice::read(uint8_t *buffer, size_t len, bool stop) {
  size_t pos = 0;
  while (pos < len) {
    size_t read_len =
        ((len - pos) > maxBufferSize()) ? maxBufferSize() : (len - pos);
    bool read_stop = (pos < (len - read_len)) ? false : stop;
    if (!_read(buffer + pos, read_len, read_stop))
      return false;
    pos += read_len;
  }
  return true;
}

bool Adafruit_I2CDevice::_read(uint8_t *buffer, size_t len, bool stop) {
  size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len, (uint8_t)stop);

  if (recv != len) {
    // Not enough data available to fulfill our obligation!
    return false;
  }

  for (uint16_t i = 0; i < len; i++) {
    buffer[i] = _wire->read();
  }

  return true;
}

So... what could be causing the data to go crazy after the 21st set of data?
Any help is greatly appreciated! THANK YOU

Re: I2C burst read corrupting data

Posted: Tue Aug 27, 2024 5:37 pm
by MicroController
Are you sure that, by the time you read the FIFO, there are more than 21 valid samples in the buffer to read?

Re: I2C burst read corrupting data

Posted: Tue Aug 27, 2024 5:46 pm
by PeteDD
A reasonable question.

The read is initiated after the processor receives an interrupt I dictating that the buffer is full. There s also a register which provides the number of samples in the buffer. I have checked that register to also confirm the buffer is full.

If you look at the raw data, there is a a sudden offset in the data pattern in the middle of row 21. (11, 0, 84). If we take out those bytes, the pattern resumes correctly.

Re: I2C burst read corrupting data

Posted: Tue Aug 27, 2024 9:21 pm
by PeteDD
Here are the scope traces and decoding of the I2C stream in the region of the "glitch". What reason would we be consistently seeing the break in the 192 byte data stream here?

1- The 192 byte read is interrupted right before the "11" (decimal) value which we saw in line 21 of the raw data mentioned earlier.
Image

2- Switching the output to hexidecimal, we can see line 5 of the decoding writes the register address 0x01 which is the lead byte of the FIFO buffer. The 192 byte read then begins. (Incidentally, lines 3 and 4 are the resetting of the interrupt flag of the MMA8451Q and the results do not change if I move this reset to after the FIFO read).
This image shows the line 6 full decoding of the FIFO data up to the point of the break in the clock signal.
Image

3- This next screen capture shows the line 7 decoding of the FIFO data after the break in the clock signal:
Image

4- In the final screen capture, below, I am zooming in on the clock (SCL) (yellow) and data (SDA) (purple) signals around the "glitch". The top pair of traces indicates the zoom window and the bottom pair of traces is the zoomed in data. What we see here is:
- a 8u-sec low clock (green oval) followed by 0x10 then
- a 91u-sec high clock and then
- the 0x0B (11 decimal) (blue oval) followed by
- a skipped click cycle and then
- two bytes of data that make no sense where they are (there appears to be four bytes from the the FIFO missing here or an offset in the pattern of that many bytes) and then
- a resumption of the data pattern which appears to be correct data
Image

What reason would we be consistently seeing the break in the 192 byte data stream here? Is this a timeout or other issue on the ESP32 side or a problem with the I2C chip I am trying to read?
Thanks again for any help on this!

Re: I2C burst read corrupting data

Posted: Wed Aug 28, 2024 8:10 am
by MicroController
It seems like the single read transaction is split up into two transactions, including a STOP and a START between them.
This may well confuse the slave if the break happens in the middle of reading a FIFO entry.

Adafruit_I2CDevice::read is likely the culprit here, splitting reads into maxBufferSize() chunks. (mayBufferSize() seems to be 128, which corresponds to 21.3333 6-byte FIFO entries...)
Easy work-around to try: 'Manually' split the read yourself into multiple transactions with len = 0 mod 6. Or check if you can set maxBufferSize() to at least 192.

Re: I2C burst read corrupting data

Posted: Wed Aug 28, 2024 5:04 pm
by PeteDD
@MicroController

There are two ways so far that I found to fix this... and I don't particularly like either.

1) in Wire.h one can change the size of the I2C_BUFFER_LENGTH but this would be overwritten if the library were to be updated.

Code: Select all

#ifndef I2C_BUFFER_LENGTH
    #define I2C_BUFFER_LENGTH 255 //128  // Default size, if none is set using Wire::setBuffersize(size_t)
#endif
also, regarding changing the I2C_BUFFER_LENGTH, there is a method,

Code: Select all

Wire.setBufferSize(255);
but it fails to actually change the buffer size.

2) write a custom write/read/read where it reads half of the FIFO and then reads the next half. The problem I found with that is that I get an extra byte of data at the beginning of the second read and have to dispose of that byte. That leads to ugly code... (This code allows for both 8-bit (one-byte) and 14-bit (two byte) data sets in the FIFO)

Code: Select all

bool PD_MMA8451::getFIFO(bool use14bit)
{
  uint8_t targetRegister[1] = {MMA8451_FIFO_REG};

  uint32_t elapsedTime = micros();

  // get first 96 bytes... this will work with 128 byte default I2C buffer size
  // this also would complete the read for an 8-bit data set
  if (!i2c_dev->write(targetRegister, 1, false))
  {
    return (false);
  }
  i2c_dev->read(FIFObuffer, 96, true);  // done if getting an 8-bit data set


  if (use14bit) // now read the remaining 96 bytes in a separate read and add to FIFO buffer if getting 14-bit data set
  {
    // There will be an extra byte returned that we need to dispose of
    //  so the buffer is set to 97 instead of 96
    uint8_t tmpBuffer[97];
    i2c_dev->read(tmpBuffer, 97, true);

    elapsedTime = micros() - elapsedTime;

    for (uint8_t i = 1; i < 97; i++) // start at 1 in the tmpBuffer to skip the extra byte
    {
      FIFObuffer[i + 95] = tmpBuffer[i]; // offset the index by 95 to account for the extra byte
    }
  }

  Serial.printf("Elasped Time %d microseconds\n", elapsedTime);
  return(true);
}

Re: I2C burst read corrupting data

Posted: Wed Aug 28, 2024 8:02 pm
by MicroController

Code: Select all

#ifndef I2C_BUFFER_LENGTH
Seems like you should be able to override that value by passing a definition into the compilation (gcc -D, add_definitions(...) in cmake).

And a hack to quickly deal with the extraneous byte:

Code: Select all

uint8_t buf[192];
read(&buf[0],96);
const uint8_t saved = buf[95];
read(&buf[95],97);
buf[95] = saved;
;-)

Re: I2C burst read corrupting data

Posted: Wed Aug 28, 2024 9:47 pm
by PeteDD
Seems like you should be able to override t

well how about that...

Code: Select all

-DI2C_BUFFER_LENGTH=255
in the build_flags of my platformio.ini did work.

I had tried using

Code: Select all

#define I2C_BUFFER_LENGTH 255
in my main.cpp before including Wire.h and for reasons I cannot explain, that did not work.

Also, the "junk byte" is at the start of the second 96 bytes. I need to put it back on the scope and figure out why that is happening.

Thanks!

Re: I2C burst read corrupting data

Posted: Thu Aug 29, 2024 6:59 am
by MicroController
You can try and 'reset' the register address (i2c_dev->write(targetRegister, 1, false)) before the 2nd read.

Re: I2C burst read corrupting data

Posted: Thu Aug 29, 2024 7:31 am
by MicroController
PeteDD wrote:
Wed Aug 28, 2024 9:47 pm
I had tried using

Code: Select all

#define I2C_BUFFER_LENGTH 255
in my main.cpp before including Wire.h and for reasons I cannot explain, that did not work.
That did not work because your definition must be 'visible' everywhere I2C_BUFFER_LENGTH is used. This includes the C++ source file(s) of the I2C library, which are compiled independently of your main.cpp.