Post Go back to editing

ADXL372 - FIFO data unpacking with I2C

Category: Software
Product Number: ADXL372


I'm trying to work with the ADXL372 using I2C. Due to application requirements, I want to use it in FIFO 'stream mode', by periodically fetching samples whenever it is close to being full and receive all 3-axis of accel data. I'm struggling to obtain properly aligned axis data and need a few clarifications on what some of the terms in the data sheet actually mean.

My configuration sequence at init looks like this:

  • Config reg FIFO_SAMPLES with LSB of FIFO water mark level
  • Config reg FIFO_CTL to say FIFO stream mode, contain XYZ data, and most significant bit of watermark lvl
  • Config reg INT1_MAP to FIFO_FULL. INT1 is connected to a GPIO pin on our MCU that will trigger an ISR explained below
  • Config reg TIMING to ODR 400hz
  • Finally set POWER_CTL to FULL_BW_MEASUREMENT so the sensor starts sampling now

From what I managed to gather (confirm with me if I'm correct):  

  • a FIFO entry = a 1 axis 'sample' = 2 bytes
  • 1 'sample set' = x+y+z samples, so 6 bytes total
  • Series start indicator (B0) is '1' at start of every sample set
  • The FIFO 'watermark level' that constitutes a FIFO 'full' condition, is expressed in 'samples'. Therefore I have set it to 360, a multiple of 3, which is about 70% of the length of the FIFO

Each time the ISR on the GPIO pin fires, I attempt to read the 360 samples from the and FIFO, and unpack them. Before reading them I actually read the status reg and fifo len reg also. This is to confirm there is data, no FIFO overflow and the length is >= the watermark level I set. Everything seems ok there, but I can't figure out how to read/unpack the data. The description of the FIFO_DATA reg I feel is quite vague.

FIFO Data. A read to this address pops a 2-byte word of axis data from the FIFO. FIFO data is formatted to 2 bytes (16 bits), most significant byte first. Two subsequent reads complete the transaction of this data onto the interface. Continued reading of this field continues to pop the FIFO every third read. Multibyte reads to this address do not increment the address pointer. If this address is read due to an auto-increment from the previous address, it does not pop the FIFO. It returns zeros and increment on to the next address. 

This statement reads quite confusing in a I2C context. "A read to this address pops a 2-byte word of axis data from the FIFO"  - so a multi-byte read like below, will result in what? Will it pop out a single sample onto the I2C interface? "Two subsequent reads complete the transaction of this data onto the interface." So I need to actually send 3 single-byte read transactions instead? "Continued reading of this field continues to pop the FIFO every third read" Then, it works differently? Data is only popped off the FIFO after 3 reads? What do the first 2 reads results then? Nothing?

My first attempt was to do a multi-byte read on the FIFO_DATA reg, similar to how another MEMS sensor from another vendor worked. This is what my current read looks like:

#define FIFO_WATERMARK_LVL 360 // About 70% of the FIFO len (512 total) -- IN SAMPLES (not bytes)!!
#define BYTES_PER_SAMPLE 2 // 1 axis sample = 2 bytes

uint8_t buf[READ_BUFFER_LEN];

// Performs I2C multi-byte read. READ_BUFFER_LEN bytes are read into buf 
err = i2c_perform_reg8_read(ADXL372_I2C_ADDRESS, ADXL372_FIFO_DATA, buf, READ_BUFFER_LEN, 0);

/* Refer to page 21 of datasheet to see how FIFO data is formatted. NOTE: does
it ever lose alignment? */

for (uint16_t i = 0; i < READ_BUFFER_LEN; i += 6)
// 'sample' received. It should be '1' indicating series start */
if (!(buf[1] & 0b1))
NRF_LOG_ERROR("FIFO read misaligned");

memset(&adxl_data, 0, sizeof(adxl372_data_t));
adxl_data.x = ((buf[i] << 4) | ((buf[i + 1] & 0xF0) >> 4));
adxl_data.y = ((buf[i + 2] << 4) | ((buf[i + 3] & 0xF0) >> 4));
adxl_data.z = ((buf[i + 4] << 4) | ((buf[i + 5] & 0xF0) >> 4));
The data ends up looking like this, showing the data hasn't been received properly. How am I supposed to read from the FIFO with I2C?
<debug> mode_test: ADXL: X: 1 | Y: 4094 | Z: 5
<debug> mode_test: ADXL: X: 4094 | Y: 1 | Z: 4093
<debug> mode_test: ADXL: X: 4095 | Y: 4092 | Z: 5
<debug> mode_test: ADXL: X: 4093 | Y: 2 | Z: 2
<debug> mode_test: ADXL: X: 4092 | Y: 3 | Z: 3
<debug> mode_test: ADXL: X: 4089 | Y: 3 | Z: 4091
<debug> mode_test: ADXL: X: 1 | Y: 4091 | Z: 3
<debug> mode_test: ADXL: X: 4093 | Y: 2 | Z: 4085
<debug> mode_test: ADXL: X: 4088 | Y: 4087 | Z: 4094
<debug> mode_test: ADXL: X: 4092 | Y: 4086 | Z: 4086
<debug> mode_test: ADXL: X: 4095 | Y: 12 | Z: 4089
<debug> mode_test: ADXL: X: 8 | Y: 6 | Z: 1
<debug> mode_test: ADXL: X: 4094 | Y: 12 | Z: 4092
<debug> mode_test: ADXL: X: 10 | Y: 5 | Z: 7
<debug> mode_test: ADXL: X: 4088 | Y: 6 | Z: 4091
<debug> mode_test: ADXL: X: 4092 | Y: 4093 | Z: 7
<debug> mode_test: ADXL: X: 4 | Y: 3 | Z: 6
<debug> mode_test: ADXL: X: 6 | Y: 4090 | Z: 6

  • Hi  , 

    Thanks for your post, I agree the text on FIFO_DATA register sounds a little confusing, I will take actions to make this more clear. 

    Regarding to your code, It seems you have configured the part correctly and the multi-byte I2C transaction is working properly. Unfortunately we have found the FIFO feature on the ADXL372 does have misalignment issues. Please see the attached document explaining the issue and workaround.


    I have also included an example code for the FIFO workaround for Arduino Uno in case it helps, although is written for SPI.

    I hope this helps, 


  • Hi Pablo,

    I've read through the attached document, unfortunately in our current design we don't have INT2 connected and we want to continuously stream for data collection purposes.

    We do have INT1 connected, so I will see if if it's feasible to get our MCU to output a PWM type clock signal to INT1 as a workaround.

    I also tried just completely not using the FIFO, configuring the chip like so:

    • TIMING => ODR 400hz
    • POWER_CTL => Full BW measurement 

    And then just periodically polling the registers X_DATA_H  up to Z_DATA_L instead. However I still see the same data misalignment issue. Is this expected behaviour or does it indicate a mistake in our end?

    If we have the chance to redesign, can you recommend any similar parts (high g range accelerometers) that can work in a similar fashion with an internal FIFO?

  • Hi  , 

    unfortunately INT1 cannot be used for the FIFO workaround. 

    the registers X_DATA_H  up to Z_DATA_L do not have the problem of data misalignment, this happens only in the FIFO. So I think there might be a mistake somewhere in your end. 

    If you have the chance to re-design you could use the ADXL375, the FIFO is only 32 samples deep and it is not as low power consumption as the ADXL372, but there is no FIFO misalignment issue. 

    I hope this helps, 


  • I saw the document says apply external clock and the data sheet says you can apply an external clock to INT1 pin, so I've gone ahead and done this, applying a 307.2kHz signal as stated. I then read the FIFO, pretty much same as the code in my first post, except the function is called by a timer interrupt instead to call every 100ms.

    I need to verify how stable the PWM output from my MCU is (using that for clock signal), but it could at least be a workaround for now?

  • Hmm ok nevermind, I think I misread the document. It seems to imply that the the ADC should be disabled before reading FIFO as the clock conflict is to do with that.

  • Hi  , 

    sorry for the delay in my reply. Yes, unfortunately INT1 will not work to implement the workaround. Were you able to get the data correctly using the registers X_DATA_H  up to Z_DATA_L? 


  • Hi Pablo,

    I managed to get aligned data by X_DATA_H  up to Z_DATA_L after following your arduino code. Actually I before I just did the bitshifting the code I had in my original post, but then I saw the twos_complement function and copied that and seems to work now. 


  • Hi  , 

    great I am glad it works now.