jrowberg / i2cdevlib

I2C device library collection for AVR/Arduino or other C++-based MCUs
http://www.i2cdevlib.com
3.95k stars 7.51k forks source link

MPU6050 accelerometer offset routines don't set the low byte properly - DFRobot SEN0142 library outdated #585

Closed acrylic-origami closed 3 years ago

acrylic-origami commented 3 years ago

I'm running into an issue with MPU6050 calibration where the registers for setting offsets don't seem aligned to my chip. The high byte of each accelerometer axis does work (i.e. the MPU6050_RA_*A_OFFS_H registers) but I've discovered that the low byte actually sets the low byte of the next axis over (e.g. setting the X low byte actually sets the Y low byte), that is to say:

+--------+-------+--------+-------+--------+-------+
| 0x06   | 0x07  | 0x08   | 0x09  | 0x0A   | 0x0B  |
+--------+-------+--------+-------+--------+-------+
| X HIGH | Y LOW | Y HIGH | Z LOW | Z HIGH | X LOW |
+--------+-------+--------+-------+--------+-------+

Also, there are very specific conditions needed to set the low byte: it only persists if the high byte of the next axis is written after that, and for the first time. This is the only way I've discovered to set offsets, by twisting the bytes and registers:

int16_t xoff = -392, yoff = -213, zoff = 1301;

mpu.setZAccelOffset((zoff & 0xFF00) | (xoff & 0xFF));
mpu.setXAccelOffset((xoff & 0xFF00) | (yoff & 0xFF));
mpu.setYAccelOffset((yoff & 0xFF00) | (zoff & 0xFF));
// mpu.setZAccelOffset((zoff & 0xFF00) | (xoff & 0xFF)); // <- this call won't persist the Z low byte because it's not the first call, so the Z low byte is already locked

And because of this I also can't set the low byte of the first axis (Z in the example above) because it's already locked. The obvious alternatives don't work:

ZHomeSlice commented 3 years ago

Per the document MPU-6500 Register Map and Descriptions page 8 see Table 2 MPU-6050 compatible mode register map

0x06 6 XA_OFFSET_H R/W XA_OFFSET [14:7]
0x07 7 XA_OFFSET_L R/W XA_OFFSET [6:0] 
0x08 8 YA_OFFSET_H R/W YA_OFFSET [14:7]
0x09 9 YA_OFFSET_L R/W YA_OFFSET [6:0] 
0x0A 10 ZA_OFFSET_H R/W ZA_OFFSET [14:7]
0x0B 11 ZA_OFFSET_L R/W ZA_OFFSET [6:0]

While calibrating the gyro The calibration routine starts at address 0x06 It cycles through each access one at a time using the I2Cdev::writeWords(devAddr, SaveAddress + (i shift), 1, (uint16_t )&Data); to store the calibration on each pass. in the function writeWords the most significant bit is sent first then the least significant bit. so XA_OFFSET_H then XA_OFFSET_L get written in that order note that the pointer advances automatically to the next memory location.

            Wire.write((uint8_t)(data[i] >> 8));    // send MSB
            Wire.write((uint8_t)data[i]);         // send LSB

I don't see a problem with this...

this should read:

+--------+-------+--------+-------+--------+-------+
| 0x06   | 0x07  | 0x08   | 0x09  | 0x0A   | 0x0B  |
+--------+-------+--------+-------+--------+-------+
| X HIGH | X LOW | Y HIGH | Y LOW | Z HIGH | Z LOW |
+--------+-------+--------+-------+--------+-------+

I also checked the code for The offsets will work for the mpu6050

void MPU6050::setXAccelOffset(int16_t offset) {
    uint8_t SaveAddress = ((getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77);
    I2Cdev::writeWord(devAddr, SaveAddress, offset);
}

Z

acrylic-origami commented 3 years ago

Turns out the problem was that I2Cdev provided by the manufacturer through DigiKey was a couple versions too old, and a change in the intervening time fixed the issue. To hopefully hit some searches, this is SEN0142 by DFRobot. I've brought it up with DFRobot.