From abaae7e5fa2615c7ca2b71d5eaadc8647a5bca52 Mon Sep 17 00:00:00 2001
From: William Clark <wrvc96@gmail.com>
Date: Sun, 14 Jan 2024 19:02:42 +0000
Subject: [PATCH] compensate constant gyro drift with on-device registers

---
 main.c      |  9 +++---
 mpu6050.c   | 82 ++++++++++++++++++++++++++++++++++++++---------------
 mpu6050.h   |  3 +-
 registers.h | 12 ++++++++
 4 files changed, 77 insertions(+), 29 deletions(-)

diff --git a/main.c b/main.c
index 4ed1187..9e7a831 100644
--- a/main.c
+++ b/main.c
@@ -21,14 +21,13 @@ int main() {
         exit(1);
     }
 
-    if (mpu6050_calibrate_gyro(&mpu6050)) {
+    if (mpu6050_reset(&mpu6050)) {
         exit(1);
     }
 
-    printf("[CALIB] G_x=%4.1f, G_y=%4.1f, G_z=%4.1f\n",
-        (float)mpu6050.offset.gyro_x / 10.f,
-        (float)mpu6050.offset.gyro_y / 10.f,
-        (float)mpu6050.offset.gyro_z / 10.f);
+    if (mpu6050_calibrate_gyro(&mpu6050)) {
+        exit(1);
+    }
 
     mpu6050.cfg.gyro = MPU6050_GYRO_FS_250;
     mpu6050.cfg.acc = MPU6050_ACC_FS_2G;
diff --git a/mpu6050.c b/mpu6050.c
index bce40b0..952b3e5 100644
--- a/mpu6050.c
+++ b/mpu6050.c
@@ -91,11 +91,6 @@ int mpu6050_read_gyro(mpu6050_t *mpu6050) {
     mpu6050->data.gyro.y = (int16_t)(data[2] << 8 | data[3]) >> shift;
     mpu6050->data.gyro.z = (int16_t)(data[4] << 8 | data[5]) >> shift;
 
-    /* Apply offsets from calibration */
-    mpu6050->data.gyro.x += mpu6050->offset.gyro_x;
-    mpu6050->data.gyro.y += mpu6050->offset.gyro_y;
-    mpu6050->data.gyro.z += mpu6050->offset.gyro_z;
-
     return err;
 }
 
@@ -143,11 +138,6 @@ int mpu6050_read(mpu6050_t *mpu6050) {
     mpu6050->data.gyro.y = (int16_t)(data[10] << 8 | data[11]) >> shift_gyro;
     mpu6050->data.gyro.z = (int16_t)(data[12] << 8 | data[13]) >> shift_gyro;
 
-    /* Apply offsets from calibration */
-    mpu6050->data.gyro.x += mpu6050->offset.gyro_x;
-    mpu6050->data.gyro.y += mpu6050->offset.gyro_y;
-    mpu6050->data.gyro.z += mpu6050->offset.gyro_z;
-
     return err;
 }
 
@@ -198,12 +188,8 @@ int mpu6050_configure(mpu6050_t *mpu6050) {
 
 /* configures the device in the recommended mode for determining calibration */
 /* read gyro N times to determine a constant offset */
-/* this offset is stored in the mpu6050_t struct, */
-/* and is corrected for in all subsequent read operations */
-/* assumes stationary device */
-/* values tend not to change much over time, so can be re-used, */
-/* without calling this function again. */
-/* simply write the known offsets to the `mpu6050.offset' struct */
+/* write the offsets to device, which will in turn consider them for all */
+/* subsequent gyro reading operations. Assumes device is stationary. */
 int mpu6050_calibrate_gyro(mpu6050_t *mpu6050) {
     uint8_t data[6];
     int err = 0;
@@ -220,14 +206,14 @@ int mpu6050_calibrate_gyro(mpu6050_t *mpu6050) {
     err |= mpu6050->dev.write(REG_SIGNAL_PATH_RESET, 1 << 2);
     /* Set digital low-pass filter level to 7 */
     err |= mpu6050->dev.write(REG_CONFIG, 0);
-    /* Set 250 deg mode */
-    err |= mpu6050->dev.write(REG_GYRO_CONFIG, MPU6050_GYRO_FS_250 << 3);
+    /* Set 1000 deg mode */
+    err |= mpu6050->dev.write(REG_GYRO_CONFIG, MPU6050_GYRO_FS_1000 << 3);
     /* No sleep */
     err |= mpu6050->dev.write(REG_PWR_MGMT1, 0);
 
-    mpu6050->dev.sleep(250000); /* 250 ms */
+    mpu6050->dev.sleep(200000); /* 200 ms */
 
-    shift = gyro_i16_shift(MPU6050_GYRO_FS_250);
+    shift = gyro_i16_shift(MPU6050_GYRO_FS_1000);
 
     for (i=0; i<MPU6050_CALIBRATION_SAMPLES; i++) {
 
@@ -241,13 +227,63 @@ int mpu6050_calibrate_gyro(mpu6050_t *mpu6050) {
         mpu6050->dev.sleep(1000); /* 1 ms */
     }
 
-    mpu6050->offset.gyro_x = -x;
-    mpu6050->offset.gyro_y = -y;
-    mpu6050->offset.gyro_z = -z;
+    /* the values expected for these registers are in the form 1 dps = 32.8 LSB */
+    /* the values are subtracted, so negative drift is amplified ! */
+    x = x ? -x : x;
+    y = y ? -y : y;
+    z = z ? -z : z;
+
+    /* write the values to appropriate registers */
+    err |= mpu6050->dev.write(REG_XG_OFF_USR_H, (x >> 8) & 0xFF);
+    err |= mpu6050->dev.write(REG_XG_OFF_USR_L, x & 0xFF);
+    err |= mpu6050->dev.write(REG_YG_OFF_USR_H, (y >> 8) & 0xFF);
+    err |= mpu6050->dev.write(REG_YG_OFF_USR_L, y & 0xFF);
+    err |= mpu6050->dev.write(REG_ZG_OFF_USR_H, (z >> 8) & 0xFF);
+    err |= mpu6050->dev.write(REG_ZG_OFF_USR_L, z & 0xFF);
 
     return err;
 }
 
+/* simply set sleep mode */
+int mpu6050_reset(mpu6050_t *mpu6050) {
+    uint8_t pwrmgmt1, sigcond;
+    int err = 0;
+
+    /* enable DEVICE_RESET */
+    err |= mpu6050->dev.write(REG_PWR_MGMT1, 0x80);
+
+    /* poll PWR_MGMT1 register until MSb is 0, which means */
+    /* the device reset process has finished. */
+    do {
+        err |= mpu6050->dev.read(REG_PWR_MGMT1, &pwrmgmt1, 1);
+        mpu6050->dev.sleep(1000); /* 1 ms */
+    } while (!err && pwrmgmt1 & 0x80);
+
+    /* enable SIG_COND_RESET */
+    err |= mpu6050->dev.write(REG_USER_CTRL, 0x01);
+
+    /* poll PWR_MGMT1 register until MSb is 0, which means */
+    /* the device reset process has finished. */
+    do {
+        err |= mpu6050->dev.read(REG_USER_CTRL, &sigcond, 1);
+        mpu6050->dev.sleep(1000); /* 1 ms */
+    } while (!err && sigcond & 0x01);
+
+    /* Overwrite old gyro offsets with 0 as they only clear on power-off */
+    err |= mpu6050->dev.write(REG_XG_OFF_USR_H, 0);
+    err |= mpu6050->dev.write(REG_XG_OFF_USR_L, 0);
+    err |= mpu6050->dev.write(REG_YG_OFF_USR_H, 0);
+    err |= mpu6050->dev.write(REG_YG_OFF_USR_L, 0);
+    err |= mpu6050->dev.write(REG_ZG_OFF_USR_H, 0);
+    err |= mpu6050->dev.write(REG_ZG_OFF_USR_L, 0);
+
+    /* enable sleep mode */
+    err |= mpu6050->dev.write(REG_PWR_MGMT1, 0x40);
+
+    return err;
+}
+
+
 /* How much to shift down an i16 accel sample depending on full-scale mode set */
 static uint8_t acc_i16_shift(uint8_t fs) {
     switch(fs) {
diff --git a/mpu6050.h b/mpu6050.h
index 821a1b6..6eaf227 100644
--- a/mpu6050.h
+++ b/mpu6050.h
@@ -15,7 +15,7 @@
 #define MPU6050_ACC_FS_8G    0x02 /* ± 8g */
 #define MPU6050_ACC_FS_16G   0x03 /* ± 16g */
 
-#define MPU6050_CALIBRATION_SAMPLES 1000
+#define MPU6050_CALIBRATION_SAMPLES 100
 
 struct mpu6050_dev {
     int (*init)(void);
@@ -90,5 +90,6 @@ int mpu6050_read_temp(mpu6050_t *mpu6050);
 int mpu6050_read(mpu6050_t *mpu6050);
 int mpu6050_configure(mpu6050_t *mpu6050);
 int mpu6050_calibrate_gyro(mpu6050_t *mpu6050);
+int mpu6050_reset(mpu6050_t *mpu6050);
 
 #endif
\ No newline at end of file
diff --git a/registers.h b/registers.h
index 22c1e61..2a7a81e 100644
--- a/registers.h
+++ b/registers.h
@@ -1,10 +1,22 @@
 #ifndef REGISTERS_H
 #define REGISTERS_H
 
+#define REG_XA_OFF_USR_H       0x06 /* Accel X offset H */
+#define REG_XA_OFF_USR_L       0x07 /* Accel X offset L */
+#define REG_YA_OFF_USR_H       0x08 /* Accel Y offset H */
+#define REG_YA_OFF_USR_L       0x09 /* Accel Y offset L */
+#define REG_ZA_OFF_USR_H       0x0A /* Accel Z offset H */
+#define REG_ZA_OFF_USR_L       0x0B /* Accel Z offset L */
 #define REG_SELF_TEST_X        0x0D 
 #define REG_SELF_TEST_Y        0x0E 
 #define REG_SELF_TEST_Z        0x0F 
 #define REG_SELF_TEST_A        0x10 
+#define REG_XG_OFF_USR_H       0x13 /* Gyro X offset H */
+#define REG_XG_OFF_USR_L       0x14 /* Gyro X offset L */
+#define REG_YG_OFF_USR_H       0x15 /* Gyro Y offset H */
+#define REG_YG_OFF_USR_L       0x16 /* Gyro Y offset L */
+#define REG_ZG_OFF_USR_H       0x17 /* Gyro Z offset H */
+#define REG_ZG_OFF_USR_L       0x18 /* Gyro Z offset L */
 #define REG_SMPLRT_DIV         0x19 
 #define REG_CONFIG             0x1A 
 #define REG_GYRO_CONFIG        0x1B