Added mag calibration and interrupt-based data ready

Dependencies:   BLE_API mbed-src nRF51822

Revision:
4:8d11bfc7cac0
Parent:
3:fe46f14f5aef
--- a/MPU9250.h	Mon Jun 15 21:34:33 2015 +0000
+++ b/MPU9250.h	Thu Sep 22 01:21:24 2016 +0000
@@ -191,18 +191,24 @@
 
 //Set up I2C, (SDA,SCL)
 //I2C i2c(I2C_SDA, I2C_SCL);
-I2C i2c(P0_20, P0_22);
+//I2C i2c(P0_0, P0_1); // nRF51 FC
+//I2C i2c(P0_6, P0_7); // nRF52 QFN dev board
+I2C i2c(P0_6, P0_5); // nRF52 CIAA dev board
 
-DigitalOut myled(LED1);
+//DigitalOut myled(P0_19); // mbed kit
+//DigitalOut myled(P0_18);  // nRF51822 module
+DigitalOut myled(P0_24);  // nRF52832 module
     
 // Pin definitions
-int intPin = 12;  // These can be changed, 2 and 3 are the Arduinos ext int pins
+bool newData = false;
+bool newMagData = false;
 
+int16_t MPU9250Data[7]; // used to read all 14 bytes at once from the MPU9250 accel/gyro
 int16_t accelCount[3];  // Stores the 16-bit signed accelerometer sensor output
 int16_t gyroCount[3];   // Stores the 16-bit signed gyro sensor output
 int16_t magCount[3];    // Stores the 16-bit signed magnetometer sensor output
 float magCalibration[3] = {0, 0, 0}, magbias[3] = {0, 0, 0};  // Factory mag calibration and mag bias
-float gyroBias[3] = {0, 0, 0}, accelBias[3] = {0, 0, 0}; // Bias corrections for gyro and accelerometer
+float gyroBias[3] = {0, 0, 0}, accelBias[3] = {0, 0, 0}, magBias[3] = {0, 0, 0}, magScale[3]  = {0, 0, 0}; // Bias corrections for gyro and accelerometer
 float ax, ay, az, gx, gy, gz, mx, my, mz; // variables to hold latest sensor data values 
 int16_t tempCount;   // Stores the real internal chip temperature in degrees Celsius
 float temperature;
@@ -235,7 +241,7 @@
 //====== Set of useful function to access acceleratio, gyroscope, and temperature data
 //===================================================================================================================
 
-    void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
+   void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
 {
    char data_write[2];
    data_write[0] = subAddress;
@@ -272,10 +278,10 @@
     // Possible magnetometer scales (and their register bit settings) are:
     // 14 bit resolution (0) and 16 bit resolution (1)
     case MFS_14BITS:
-          mRes = 10.0*4219.0/8190.0; // Proper scale to return milliGauss
+          mRes = 10.0*4912.0/8190.0; // Proper scale to return milliGauss
           break;
     case MFS_16BITS:
-          mRes = 10.0*4219.0/32760.0; // Proper scale to return milliGauss
+          mRes = 10.0*4912.0/32760.0; // Proper scale to return milliGauss
           break;
   }
 }
@@ -288,16 +294,16 @@
     // 250 DPS (00), 500 DPS (01), 1000 DPS (10), and 2000 DPS  (11). 
         // Here's a bit of an algorith to calculate DPS/(ADC tick) based on that 2-bit value:
     case GFS_250DPS:
-          gRes = 250.0/32768.0;
+          gRes = 250.0f/32768.0f;
           break;
     case GFS_500DPS:
-          gRes = 500.0/32768.0;
+          gRes = 500.0f/32768.0f;
           break;
     case GFS_1000DPS:
-          gRes = 1000.0/32768.0;
+          gRes = 1000.0f/32768.0f;
           break;
     case GFS_2000DPS:
-          gRes = 2000.0/32768.0;
+          gRes = 2000.0f/32768.0f;
           break;
   }
 }
@@ -310,20 +316,32 @@
     // 2 Gs (00), 4 Gs (01), 8 Gs (10), and 16 Gs  (11). 
         // Here's a bit of an algorith to calculate DPS/(ADC tick) based on that 2-bit value:
     case AFS_2G:
-          aRes = 2.0/32768.0;
+          aRes = 2.0f/32768.0f;
           break;
     case AFS_4G:
-          aRes = 4.0/32768.0;
+          aRes = 4.0f/32768.0f;
           break;
     case AFS_8G:
-          aRes = 8.0/32768.0;
+          aRes = 8.0f/32768.0f;
           break;
     case AFS_16G:
-          aRes = 16.0/32768.0;
+          aRes = 16.0f/32768.0f;
           break;
   }
 }
 
+void readMPU9250Data(int16_t * destination)
+{
+  uint8_t rawData[14];  // x/y/z accel register data stored here
+  readBytes(MPU9250_ADDRESS, ACCEL_XOUT_H, 14, &rawData[0]);  // Read the 14 raw data registers into data array
+  destination[0] = ((int16_t)rawData[0] << 8) | rawData[1] ;  // Turn the MSB and LSB into a signed 16-bit value
+  destination[1] = ((int16_t)rawData[2] << 8) | rawData[3] ;  
+  destination[2] = ((int16_t)rawData[4] << 8) | rawData[5] ; 
+  destination[3] = ((int16_t)rawData[6] << 8) | rawData[7] ;   
+  destination[4] = ((int16_t)rawData[8] << 8) | rawData[9] ;  
+  destination[5] = ((int16_t)rawData[10] << 8) | rawData[11] ;  
+  destination[6] = ((int16_t)rawData[12] << 8) | rawData[13] ; 
+}
 
 void readAccelData(int16_t * destination)
 {
@@ -346,15 +364,13 @@
 void readMagData(int16_t * destination)
 {
   uint8_t rawData[7];  // x/y/z gyro register data, ST2 register stored here, must read ST2 at end of data acquisition
-  if(readByte(AK8963_ADDRESS, AK8963_ST1) & 0x01) { // wait for magnetometer data ready bit to be set
   readBytes(AK8963_ADDRESS, AK8963_XOUT_L, 7, &rawData[0]);  // Read the six raw data and ST2 registers sequentially into data array
   uint8_t c = rawData[6]; // End data read by reading ST2 register
     if(!(c & 0x08)) { // Check if magnetic sensor overflow set, if not then report data
-    destination[0] = (int16_t)(((int16_t)rawData[1] << 8) | rawData[0]);  // Turn the MSB and LSB into a signed 16-bit value
-    destination[1] = (int16_t)(((int16_t)rawData[3] << 8) | rawData[2]) ;  // Data stored as little Endian
-    destination[2] = (int16_t)(((int16_t)rawData[5] << 8) | rawData[4]) ; 
+    destination[0] = ((int16_t)rawData[1] << 8) | rawData[0] ;  // Turn the MSB and LSB into a signed 16-bit value
+    destination[1] = ((int16_t)rawData[3] << 8) | rawData[2] ;  // Data stored as little Endian
+    destination[2] = ((int16_t)rawData[5] << 8) | rawData[4] ; 
    }
-  }
 }
 
 int16_t readTempData()
@@ -374,12 +390,15 @@
   void initAK8963(float * destination)
 {
   // First extract the factory calibration for each magnetometer axis
-  uint8_t rawData[3];  // x/y/z gyro calibration data stored here
+  uint8_t rawData[3] = {0, 0, 0};  // x/y/z gyro calibration data stored here
   writeByte(AK8963_ADDRESS, AK8963_CNTL, 0x00); // Power down magnetometer  
   wait(0.01);
   writeByte(AK8963_ADDRESS, AK8963_CNTL, 0x0F); // Enter Fuse ROM access mode
   wait(0.01);
-  readBytes(AK8963_ADDRESS, AK8963_ASAX, 3, &rawData[0]);  // Read the x-, y-, and z-axis calibration values
+ // readBytes(AK8963_ADDRESS, AK8963_ASAX, 3, &rawData[0]);  // Read the x-, y-, and z-axis calibration values
+  rawData[0] = readByte(AK8963_ADDRESS, AK8963_ASAX);
+  rawData[1] = readByte(AK8963_ADDRESS, AK8963_ASAY);
+  rawData[2] = readByte(AK8963_ADDRESS, AK8963_ASAZ);
   destination[0] =  (float)(rawData[0] - 128)/256.0f + 1.0f;   // Return x-axis sensitivity adjustment values, etc.
   destination[1] =  (float)(rawData[1] - 128)/256.0f + 1.0f;  
   destination[2] =  (float)(rawData[2] - 128)/256.0f + 1.0f; 
@@ -438,8 +457,8 @@
   // Configure Interrupts and Bypass Enable
   // Set interrupt pin active high, push-pull, and clear on read of INT_STATUS, enable I2C_BYPASS_EN so additional chips 
   // can join the I2C bus and all can be controlled by the Arduino as master
-   writeByte(MPU9250_ADDRESS, INT_PIN_CFG, 0x22);    
-   writeByte(MPU9250_ADDRESS, INT_ENABLE, 0x01);  // Enable data ready (bit 0) interrupt
+  writeByte(MPU9250_ADDRESS, INT_PIN_CFG, 0x12);  // INT is 50 microsecond pulse and any read to clear  
+  writeByte(MPU9250_ADDRESS, INT_ENABLE, 0x01);  // Enable data ready (bit 0) interrupt
 }
 
 // Function which accumulates gyro and accelerometer data after device initialization. It calculates the average
@@ -589,13 +608,57 @@
    dest2[2] = (float)accel_bias[2]/(float)accelsensitivity;
 }
 
+void magcalMPU9250(float * dest1, float * dest2) 
+{
+  uint16_t ii = 0, sample_count = 0;
+  int32_t mag_bias[3] = {0, 0, 0}, mag_scale[3] = {0, 0, 0};
+  int16_t mag_max[3] = {-32767, -32767, -32767}, mag_min[3] = {32767, 32767, 32767}, mag_temp[3] = {0, 0, 0};
+  
+    // shoot for ~fifteen seconds of mag data
+    if(Mmode == 0x02) sample_count = 128;  // at 8 Hz ODR, new mag data is available every 125 ms
+    if(Mmode == 0x06) sample_count = 1500;  // at 100 Hz ODR, new mag data is available every 10 ms
+    
+   for(ii = 0; ii < sample_count; ii++) {
+    readMagData(mag_temp);  // Read the mag data  
+    
+    for (int jj = 0; jj < 3; jj++) {
+      if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
+      if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
+    }
+    
+    if(Mmode == 0x02) wait(0.135);  // at 8 Hz ODR, new mag data is available every 125 ms
+    if(Mmode == 0x06) wait(0.012);  // at 100 Hz ODR, new mag data is available every 10 ms
+    }
+
+   // Get hard iron correction
+    mag_bias[0]  = (mag_max[0] + mag_min[0])/2;  // get average x mag bias in counts
+    mag_bias[1]  = (mag_max[1] + mag_min[1])/2;  // get average y mag bias in counts
+    mag_bias[2]  = (mag_max[2] + mag_min[2])/2;  // get average z mag bias in counts
+    
+    dest1[0] = (float) mag_bias[0]*mRes*magCalibration[0];  // save mag biases in G for main program
+    dest1[1] = (float) mag_bias[1]*mRes*magCalibration[1];   
+    dest1[2] = (float) mag_bias[2]*mRes*magCalibration[2];  
+       
+    // Get soft iron correction estimate
+    mag_scale[0]  = (mag_max[0] - mag_min[0])/2;  // get average x axis max chord length in counts
+    mag_scale[1]  = (mag_max[1] - mag_min[1])/2;  // get average y axis max chord length in counts
+    mag_scale[2]  = (mag_max[2] - mag_min[2])/2;  // get average z axis max chord length in counts
+
+    float avg_rad = mag_scale[0] + mag_scale[1] + mag_scale[2];
+    avg_rad /= 3.0;
+
+    dest2[0] = avg_rad/((float)mag_scale[0]);
+    dest2[1] = avg_rad/((float)mag_scale[1]);
+    dest2[2] = avg_rad/((float)mag_scale[2]);
+}
+
 
 // Accelerometer and gyroscope self test; check calibration wrt factory settings
 void MPU9250SelfTest(float * destination) // Should return percent deviation from factory trim values, +/- 14 or less deviation is a pass
 {
    uint8_t rawData[6] = {0, 0, 0, 0, 0, 0};
    uint8_t selfTest[6];
-   int16_t gAvg[3], aAvg[3], aSTAvg[3], gSTAvg[3];
+   int32_t gAvg[3] = {0, 0, 0}, aAvg[3] = {0, 0, 0}, aSTAvg[3] = {0, 0, 0}, gSTAvg[3] = {0, 0, 0};
    float factoryTrim[6];
    uint8_t FS = 0;
    
@@ -670,8 +733,8 @@
  // Report results as a ratio of (STR - FT)/FT; the change from Factory Trim of the Self-Test Response
  // To get percent, must multiply by 100
    for (int i = 0; i < 3; i++) {
-     destination[i] = 100.0*((float)(aSTAvg[i] - aAvg[i]))/factoryTrim[i]; // Report percent differences
-     destination[i+3] = 100.0*((float)(gSTAvg[i] - gAvg[i]))/factoryTrim[i+3]; // Report percent differences
+     destination[i] = 100.0f*((float)(aSTAvg[i] - aAvg[i]))/factoryTrim[i] - 100.0f; // Report percent differences
+     destination[i+3] = 100.0f*((float)(gSTAvg[i] - gAvg[i]))/factoryTrim[i+3] - 100.0f; // Report percent differences
    }
    
 }
@@ -717,7 +780,7 @@
             float q4q4 = q4 * q4;
 
             // Normalise accelerometer measurement
-            norm = sqrt(ax * ax + ay * ay + az * az);
+            norm = sqrtf(ax * ax + ay * ay + az * az);
             if (norm == 0.0f) return; // handle NaN
             norm = 1.0f/norm;
             ax *= norm;
@@ -725,7 +788,7 @@
             az *= norm;
 
             // Normalise magnetometer measurement
-            norm = sqrt(mx * mx + my * my + mz * mz);
+            norm = sqrtf(mx * mx + my * my + mz * mz);
             if (norm == 0.0f) return; // handle NaN
             norm = 1.0f/norm;
             mx *= norm;
@@ -749,7 +812,7 @@
             s2 = _2q4 * (2.0f * q2q4 - _2q1q3 - ax) + _2q1 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q2 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + _2bz * q4 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q3 + _2bz * q1) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q4 - _4bz * q2) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
             s3 = -_2q1 * (2.0f * q2q4 - _2q1q3 - ax) + _2q4 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q3 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + (-_4bx * q3 - _2bz * q1) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q2 + _2bz * q4) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q1 - _4bz * q3) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
             s4 = _2q2 * (2.0f * q2q4 - _2q1q3 - ax) + _2q3 * (2.0f * q1q2 + _2q3q4 - ay) + (-_4bx * q4 + _2bz * q2) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q1 + _2bz * q3) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q2 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
-            norm = sqrt(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4);    // normalise step magnitude
+            norm = sqrtf(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4);    // normalise step magnitude
             norm = 1.0f/norm;
             s1 *= norm;
             s2 *= norm;
@@ -767,7 +830,7 @@
             q2 += qDot2 * deltat;
             q3 += qDot3 * deltat;
             q4 += qDot4 * deltat;
-            norm = sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);    // normalise quaternion
+            norm = sqrtf(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);    // normalise quaternion
             norm = 1.0f/norm;
             q[0] = q1 * norm;
             q[1] = q2 * norm;
@@ -802,7 +865,7 @@
             float q4q4 = q4 * q4;   
 
             // Normalise accelerometer measurement
-            norm = sqrt(ax * ax + ay * ay + az * az);
+            norm = sqrtf(ax * ax + ay * ay + az * az);
             if (norm == 0.0f) return; // handle NaN
             norm = 1.0f / norm;        // use reciprocal for division
             ax *= norm;
@@ -810,7 +873,7 @@
             az *= norm;
 
             // Normalise magnetometer measurement
-            norm = sqrt(mx * mx + my * my + mz * mz);
+            norm = sqrtf(mx * mx + my * my + mz * mz);
             if (norm == 0.0f) return; // handle NaN
             norm = 1.0f / norm;        // use reciprocal for division
             mx *= norm;
@@ -820,7 +883,7 @@
             // Reference direction of Earth's magnetic field
             hx = 2.0f * mx * (0.5f - q3q3 - q4q4) + 2.0f * my * (q2q3 - q1q4) + 2.0f * mz * (q2q4 + q1q3);
             hy = 2.0f * mx * (q2q3 + q1q4) + 2.0f * my * (0.5f - q2q2 - q4q4) + 2.0f * mz * (q3q4 - q1q2);
-            bx = sqrt((hx * hx) + (hy * hy));
+            bx = sqrtf((hx * hx) + (hy * hy));
             bz = 2.0f * mx * (q2q4 - q1q3) + 2.0f * my * (q3q4 + q1q2) + 2.0f * mz * (0.5f - q2q2 - q3q3);
 
             // Estimated direction of gravity and magnetic field
@@ -863,7 +926,7 @@
             q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * deltat);
 
             // Normalise quaternion
-            norm = sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
+            norm = sqrtf(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
             norm = 1.0f / norm;
             q[0] = q1 * norm;
             q[1] = q2 * norm;