MPU-6050 Burst Read Auto Increment

1.4k views Asked by At

I'm trying to write a driver for the MPU-6050 and I'm stuck on how to proceed regarding reading the raw accelerometer/gyroscope/temperature readings. For instance, the MPU-6050 has the accelerometer X readings in 2 registers: ACCEL_XOUT[15:8] at address 0x3B and ACCEL_XOUT[7:0] at address 0x3C. Of course to read the raw value I need to read both registers and put them together.

BUT

In the description of the registers (in the register map and description sheet, https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf) it says that to guarantee readings from the same sampling instant I must use burst reads b/c as soon as an idle I2C bus is detected, the sensor registers are refreshed with new data from a new sampling instant. The datasheet snippet shows the simple I2C burst read:

enter image description here

However, this approach (to the best of my understanding) would only work reading the ACCEL_X registers from the same sampling instant if the auto-increment was supported (such that the first DATA in the above sequence would be from ACCEL_XOUT[15:8] @ address 0x3B and the second DATA would be from ACCEL_XOUT[7:0] @ address 0x3C). But the datasheet (https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf) only mentions that I2C burst writes support the auto-increment feature. Without auto-increment on the I2C read side how would I go about reading two different registers whilst maintaining the same sampling instant?

I also recognize that I could use the sensor's FIFO feature or the interrupt to accomplish what I'm after, but (for my own curiosity) I would like a solution that didn't rely on either.

2

There are 2 answers

0
Luca Ciucci On

I also have the same problem, looks like the documentation on this topic is incomplete.

Reading single sample

I think you can burst read the ACCEL_*OUT_*, TEMP_OUT_* and GYRO_*OUT_*. In fact I tried reading the data one register at once, but I got frequent data corruption.
Then, just to try, I requested 6 bytes from ACCEL_XOUT_H, 6 bytes from GYRO_XOUT_H and 2 bytes from TEMP_OUT_H and... it worked! No more data corruption!

I think they simply forgot to mention this in the register map.

How to

Here is some example code that can work in the Arduino environment.

These are the function that I use, they are not very safe, but it works for my project:

////////////////////////////////////////////////////////////////
inline void requestBytes(byte SUB, byte nVals)
{
    Wire.beginTransmission(SAD);
    Wire.write(SUB);
    Wire.endTransmission(false);
    Wire.requestFrom(SAD, nVals);
    while (Wire.available() == 0);
}

////////////////////////////////////////////////////////////////
inline byte getByte(void)
{
    return Wire.read();
}

////////////////////////////////////////////////////////////////
inline void stopRead(void)
{
    Wire.endTransmission(true);
}

////////////////////////////////////////////////////////////////
byte readByte(byte SUB)
{
    requestBytes(SUB, 1);

    byte result = getByte();

    stopRead();

    return result;
}

////////////////////////////////////////////////////////////////
void readBytes(byte SUB, byte* buff, byte count)
{
    requestBytes(SUB, count);

    for (int i = 0; i < count; i++)
        buff[i] = getByte();

    stopRead();
}

At this point, you can simply read the values in this way:

// ACCEL_XOUT_H

// burst read the registers using auto-increment:
byte data[6];
readBytes(ACCEL_XOUT_H, data, 6);

// convert the data:
acc_x = (data[0] << 8) | data[1];
// ...

Warning!

Looks like this cannot be done for other registers. For example, to read the FIFO_COUNT_* I have to do this (otherwise I get incorrect results):

uint16_t FIFO_size(void)
{
    byte bytes[2];

    // this does not work
    //readBytes(FIFO_COUNT_H, bytes, 2);

    bytes[1] = readByte(FIFO_COUNT_H);
    bytes[2] = readByte(FIFO_COUNT_L);

    return unisci_bytes(bytes[1], bytes[2]);
}

Reading the FIFO

Looks like the FIFO works differently: you can burst read by simply requesting multiple bytes from the FIFO_R_W register and the MPU6050 will give you the bytes in the FIFO without incrementing the register.

I found this example where they use I2Cdev::readByte(SAD, FIFO_R_W, buffer) to read a given number of bytes from the FIFO and if you look at I2Cdev::readByte() (here) it simply requests N bytes from the FIFO register:


// ... send FIFO_R_W and request N bytes ...

for(...; ...; count++)
    data[count] = Wire.receive();
// ...

How to

This is simple since the FIFO_R_W does not auto-increment:

byte data[12];

void loop() {
// ...
readBytes(FIFO_R_W, data, 12); // <- replace 12 with your burst size
// ...
}

Warning!

  • Using FIFO_size() is very slow!
  • Also my advice is to use 400kHz I2C frequency, which is the MPU6050's maximum speed

Hope it helps ;)

0
haui On

As Luca says, the burst read semantic seems to be different depending on the register the read operation starts at.

Reading consistent samples

To read a consistent set of raw data values, you can use the method I2C.readRegister(int, ByteBuffer, int) with register number 59 (ACCEL_XOUTR[15:8]) and a length of 14 to read all the sensor data ACCEL, TEMP, and GYRO in one operation and get consistent data.

Burst read of FIFO data

However, if you use the FIFO buffer of the chip, you can start the burst read with the same method signature on register 116 (FIFO_R_W) to read the given amount of data from the chip-internal fifo buffer. Doing so you must keep in mind that there is a limit on the number of bytes that can be read in one burst operation. If I'm interpreting https://github.com/joan2937/pigpio/blob/c33738a320a3e28824af7807edafda440952c05d/pigpio.c#L3914 right, a maximum of 31 bytes can be read in a single burst operation.