first release fork of MPU9250AHRS from Kris Winer

Dependents:   mbed-os-i2c-test mbed-test-i2c-PCA-biquad-peakdet Mix-code-v2 mbed-os-step-counting ... more

Revision:
0:76dc2aad77bc
Child:
1:c27bb1a0deca
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MPU9250.cpp	Wed Oct 05 10:33:45 2016 +0000
@@ -0,0 +1,300 @@
+
+#include <mbed.h>
+#include "MPU9250.h"
+
+
+mpu9250::mpu9250(PinName _sda, PinName _scl) : i2c(_sda, _scl)
+{
+    i2c.frequency(400000);
+}
+
+
+void mpu9250::writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
+{
+    char data_write[2];
+    data_write[0] = subAddress;
+    data_write[1] = data;
+    i2c.write(address, data_write, 2, 0);
+}
+
+char mpu9250::readByte(uint8_t address, uint8_t subAddress)
+{
+    char data[1]; // `data` will store the register data
+    char data_write[1];
+    data_write[0] = subAddress;
+    i2c.write(address, data_write, 1, 1); // no stop
+    i2c.read(address, data, 1, 0);
+    return data[0];
+}
+
+void mpu9250::readBytes(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest)
+{
+    char data[14];
+    char data_write[1];
+    data_write[0] = subAddress;
+    i2c.write(address, data_write, 1, 1); // no stop
+    i2c.read(address, data, count, 0);
+    for(int ii = 0; ii < count; ii++) {
+        dest[ii] = data[ii];
+    }
+}
+
+bool mpu9250::alive()
+{
+    if(readByte(MPU9250_ADDRESS, WHO_AM_I_MPU9250) == 0x71)
+        return true;
+    else
+        return false;
+}
+
+void mpu9250::getGres(uint8_t Gscale)
+{
+    switch (Gscale) {
+        case GFS_250DPS:
+            gRes = 250.0/32768.0;
+            break;
+        case GFS_500DPS:
+            gRes = 500.0/32768.0;
+            break;
+        case GFS_1000DPS:
+            gRes = 1000.0/32768.0;
+            break;
+        case GFS_2000DPS:
+            gRes = 2000.0/32768.0;
+            break;
+    }
+}
+
+void mpu9250::getAres(uint8_t Ascale)
+{
+    switch (Ascale) {
+        case AFS_2G:
+            aRes = 2.0/32768.0;
+            break;
+        case AFS_4G:
+            aRes = 4.0/32768.0;
+            break;
+        case AFS_8G:
+            aRes = 8.0/32768.0;
+            break;
+        case AFS_16G:
+            aRes = 16.0/32768.0;
+            break;
+    }
+}
+
+void mpu9250::readAccelData(int16_t * destination)
+{
+    uint8_t rawData[6];  // x/y/z accel register data stored here
+    readBytes(MPU9250_ADDRESS, ACCEL_XOUT_H, 6, &rawData[0]);  // Read the six raw data registers into data array
+    destination[0] = (int16_t)(((int16_t)rawData[0] << 8) | rawData[1]) ;  // Turn the MSB and LSB into a signed 16-bit value
+    destination[1] = (int16_t)(((int16_t)rawData[2] << 8) | rawData[3]) ;
+    destination[2] = (int16_t)(((int16_t)rawData[4] << 8) | rawData[5]) ;
+}
+
+void mpu9250::readGyroData(int16_t * destination)
+{
+    uint8_t rawData[6];  // x/y/z gyro register data stored here
+    readBytes(MPU9250_ADDRESS, GYRO_XOUT_H, 6, &rawData[0]);  // Read the six raw data registers sequentially into data array
+    destination[0] = (int16_t)(((int16_t)rawData[0] << 8) | rawData[1]) ;  // Turn the MSB and LSB into a signed 16-bit value
+    destination[1] = (int16_t)(((int16_t)rawData[2] << 8) | rawData[3]) ;
+    destination[2] = (int16_t)(((int16_t)rawData[4] << 8) | rawData[5]) ;
+}
+
+void mpu9250::readTempData(int16_t * destination)
+{
+    uint8_t rawData[2];
+    readBytes(MPU9250_ADDRESS, TEMP_OUT_H, 2, &rawData[0]);  // Read the two raw data registers sequentially into data array
+    destination[0] = (int16_t)(((int16_t)rawData[0]) << 8 | rawData[1]) ;  // Turn the MSB and LSB into a 16-bit value
+}
+
+void mpu9250::readAll(int16_t * destinationAcc, int16_t * destinationGyro, int16_t * destinationTemp)
+{
+    readAccelData(destinationAcc);  // Read the x/y/z adc values
+    readGyroData(destinationGyro);  // Read the x/y/z adc values
+    readTempData(destinationTemp);  // Read the adc values
+}
+
+void mpu9250::ReadConvertAll(float * destinationAcc, float * destinationGyro, float * destinationTemp)
+{
+    int16_t AccRead[3];
+    int16_t GyroRead[3];
+    int16_t TempRead[1];
+
+    readAll(AccRead,GyroRead,TempRead);
+
+    destinationAcc[0] = -1000*((float)AccRead[1]*aRes - accelBias[1]);  // get actual g value, this depends on scale being set
+    destinationAcc[1] = -1000*((float)AccRead[0]*aRes - accelBias[0]);
+    destinationAcc[2] = 1000*((float)AccRead[2]*aRes - accelBias[2]);
+
+    destinationGyro[0] = (float)GyroRead[0]*gRes - gyroBias[0];  // get actual gyro value, this depends on scale being set
+    destinationGyro[1] = (float)GyroRead[1]*gRes - gyroBias[1];
+    destinationGyro[2] = (float)GyroRead[2]*gRes - gyroBias[2];
+
+    destinationTemp[0] = ((float) TempRead[0]) / 333.87f + 21.0f; // Temperature in degrees Centigrade
+}
+
+void mpu9250::resetMPU9250()
+{
+    writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x80); // Write a one to bit 7 reset bit; toggle reset device
+    wait(0.1);
+}
+
+void mpu9250::initMPU9250(uint8_t Ascale,uint8_t Gscale)
+{
+    resetMPU9250();
+    wait(0.2);
+
+    writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x00); // Clear sleep mode bit (6), enable all sensors
+    wait(0.1); // Delay 100 ms for PLL to get established on x-axis gyro; should check for PLL ready interrupt
+
+    writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x01);  // Set clock source to be PLL with x-axis gyroscope reference, bits 2:0 = 001
+    writeByte(MPU9250_ADDRESS, CONFIG, 0x03);
+
+    writeByte(MPU9250_ADDRESS, SMPLRT_DIV, 0x04);  // Use a 200 Hz rate; the same rate set in CONFIG above
+
+    uint8_t c =  readByte(MPU9250_ADDRESS, GYRO_CONFIG);
+    writeByte(MPU9250_ADDRESS, GYRO_CONFIG, c & ~0xE0); // Clear self-test bits [7:5]
+    writeByte(MPU9250_ADDRESS, GYRO_CONFIG, c & ~0x18); // Clear AFS bits [4:3]
+    writeByte(MPU9250_ADDRESS, GYRO_CONFIG, c | Gscale); // Set full scale range for the gyro
+
+    c =  readByte(MPU9250_ADDRESS, ACCEL_CONFIG);
+    writeByte(MPU9250_ADDRESS, ACCEL_CONFIG, c & ~0xE0); // Clear self-test bits [7:5]
+    writeByte(MPU9250_ADDRESS, ACCEL_CONFIG, c & ~0x18); // Clear AFS bits [4:3]
+    writeByte(MPU9250_ADDRESS, ACCEL_CONFIG, c | Ascale); // Set full scale range for the accelerometer
+
+    c = readByte(MPU9250_ADDRESS, ACCEL_CONFIG2);
+    writeByte(MPU9250_ADDRESS, ACCEL_CONFIG2, c & ~0x0F); // Clear accel_fchoice_b (bit 3) and A_DLPFG (bits [2:0])
+    writeByte(MPU9250_ADDRESS, ACCEL_CONFIG2, c | 0x03); // Set accelerometer rate to 1 kHz and bandwidth to 41 Hz
+
+    //  writeByte(MPU9250_ADDRESS, INT_PIN_CFG, 0x22);
+    //  writeByte(MPU9250_ADDRESS, INT_ENABLE, 0x01);  // Enable data ready (bit 0) interrupt
+
+    getAres(Ascale); // Get accelerometer sensitivity
+    getGres(Gscale); // Get gyro sensitivity
+
+    calibrateMPU9250(gyroBias, accelBias); // Calibrate gyro and accelerometers, load biases in bias registers
+}
+
+
+void mpu9250::calibrateMPU9250(float * dest1, float * dest2)
+{
+    uint8_t data[12]; // data array to hold accelerometer and gyro x, y, z, data
+    uint16_t ii, packet_count, fifo_count;
+    int32_t gyro_bias[3] = {0, 0, 0}, accel_bias[3] = {0, 0, 0};
+
+    writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x80); // Write a one to bit 7 reset bit; toggle reset device
+    wait(0.1);
+
+    writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x01);
+    writeByte(MPU9250_ADDRESS, PWR_MGMT_2, 0x00);
+    wait(0.2);
+
+    // Configure device for bias calculation
+    writeByte(MPU9250_ADDRESS, INT_ENABLE, 0x00);   // Disable all interrupts
+    writeByte(MPU9250_ADDRESS, FIFO_EN, 0x00);      // Disable FIFO
+    writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x00);   // Turn on internal clock source
+    writeByte(MPU9250_ADDRESS, I2C_MST_CTRL, 0x00); // Disable I2C master
+    writeByte(MPU9250_ADDRESS, USER_CTRL, 0x00);    // Disable FIFO and I2C master modes
+    writeByte(MPU9250_ADDRESS, USER_CTRL, 0x0C);    // Reset FIFO and DMP
+    wait(0.015);
+
+    // Configure MPU9250 gyro and accelerometer for bias calculation
+    writeByte(MPU9250_ADDRESS, CONFIG, 0x01);      // Set low-pass filter to 188 Hz
+    writeByte(MPU9250_ADDRESS, SMPLRT_DIV, 0x00);  // Set sample rate to 1 kHz
+    writeByte(MPU9250_ADDRESS, GYRO_CONFIG, 0x00);  // Set gyro full-scale to 250 degrees per second, maximum sensitivity
+    writeByte(MPU9250_ADDRESS, ACCEL_CONFIG, 0x00); // Set accelerometer full-scale to 2 g, maximum sensitivity
+
+    uint16_t  gyrosensitivity  = 131;   // = 131 LSB/degrees/sec
+    uint16_t  accelsensitivity = 16384;  // = 16384 LSB/g
+
+    // Configure FIFO to capture accelerometer and gyro data for bias calculation
+    writeByte(MPU9250_ADDRESS, USER_CTRL, 0x40);   // Enable FIFO
+    writeByte(MPU9250_ADDRESS, FIFO_EN, 0x78);     // Enable gyro and accelerometer sensors for FIFO (max size 512 bytes in MPU-9250)
+    wait(0.04); // accumulate 40 samples in 80 milliseconds = 480 bytes
+
+    // At end of sample accumulation, turn off FIFO sensor read
+    writeByte(MPU9250_ADDRESS, FIFO_EN, 0x00);        // Disable gyro and accelerometer sensors for FIFO
+    readBytes(MPU9250_ADDRESS, FIFO_COUNTH, 2, &data[0]); // read FIFO sample count
+    fifo_count = ((uint16_t)data[0] << 8) | data[1];
+    packet_count = fifo_count/12;// How many sets of full gyro and accelerometer data for averaging
+
+    for (ii = 0; ii < packet_count; ii++) {
+        int16_t accel_temp[3] = {0, 0, 0}, gyro_temp[3] = {0, 0, 0};
+        readBytes(MPU9250_ADDRESS, FIFO_R_W, 12, &data[0]); // read data for averaging
+        accel_temp[0] = (int16_t) (((int16_t)data[0] << 8) | data[1]  ) ;  // Form signed 16-bit integer for each sample in FIFO
+        accel_temp[1] = (int16_t) (((int16_t)data[2] << 8) | data[3]  ) ;
+        accel_temp[2] = (int16_t) (((int16_t)data[4] << 8) | data[5]  ) ;
+        gyro_temp[0]  = (int16_t) (((int16_t)data[6] << 8) | data[7]  ) ;
+        gyro_temp[1]  = (int16_t) (((int16_t)data[8] << 8) | data[9]  ) ;
+        gyro_temp[2]  = (int16_t) (((int16_t)data[10] << 8) | data[11]) ;
+
+        accel_bias[0] += (int32_t) accel_temp[0]; // Sum individual signed 16-bit biases to get accumulated signed 32-bit biases
+        accel_bias[1] += (int32_t) accel_temp[1];
+        accel_bias[2] += (int32_t) accel_temp[2];
+        gyro_bias[0]  += (int32_t) gyro_temp[0];
+        gyro_bias[1]  += (int32_t) gyro_temp[1];
+        gyro_bias[2]  += (int32_t) gyro_temp[2];
+
+    }
+    accel_bias[0] /= (int32_t) packet_count; // Normalize sums to get average count biases
+    accel_bias[1] /= (int32_t) packet_count;
+    accel_bias[2] /= (int32_t) packet_count;
+    gyro_bias[0]  /= (int32_t) packet_count;
+    gyro_bias[1]  /= (int32_t) packet_count;
+    gyro_bias[2]  /= (int32_t) packet_count;
+
+    if(accel_bias[2] > 0L) {
+        accel_bias[2] -= (int32_t) accelsensitivity;   // Remove gravity from the z-axis accelerometer bias calculation
+    } else {
+        accel_bias[2] += (int32_t) accelsensitivity;
+    }
+
+    // Construct the gyro biases for push to the hardware gyro bias registers, which are reset to zero upon device startup
+    data[0] = (-gyro_bias[0]/4  >> 8) & 0xFF; // Divide by 4 to get 32.9 LSB per deg/s to conform to expected bias input format
+    data[1] = (-gyro_bias[0]/4)       & 0xFF; // Biases are additive, so change sign on calculated average gyro biases
+    data[2] = (-gyro_bias[1]/4  >> 8) & 0xFF;
+    data[3] = (-gyro_bias[1]/4)       & 0xFF;
+    data[4] = (-gyro_bias[2]/4  >> 8) & 0xFF;
+    data[5] = (-gyro_bias[2]/4)       & 0xFF;
+
+    dest1[0] = (float) gyro_bias[0]/(float) gyrosensitivity; // construct gyro bias in deg/s for later manual subtraction
+    dest1[1] = (float) gyro_bias[1]/(float) gyrosensitivity;
+    dest1[2] = (float) gyro_bias[2]/(float) gyrosensitivity;
+
+    int32_t accel_bias_reg[3] = {0, 0, 0}; // A place to hold the factory accelerometer trim biases
+    readBytes(MPU9250_ADDRESS, XA_OFFSET_H, 2, &data[0]); // Read factory accelerometer trim values
+    accel_bias_reg[0] = (int16_t) ((int16_t)data[0] << 8) | data[1];
+    readBytes(MPU9250_ADDRESS, YA_OFFSET_H, 2, &data[0]);
+    accel_bias_reg[1] = (int16_t) ((int16_t)data[0] << 8) | data[1];
+    readBytes(MPU9250_ADDRESS, ZA_OFFSET_H, 2, &data[0]);
+    accel_bias_reg[2] = (int16_t) ((int16_t)data[0] << 8) | data[1];
+
+    uint32_t mask = 1uL; // Define mask for temperature compensation bit 0 of lower byte of accelerometer bias registers
+    uint8_t mask_bit[3] = {0, 0, 0}; // Define array to hold mask bit for each accelerometer bias axis
+
+    for(ii = 0; ii < 3; ii++) {
+        if(accel_bias_reg[ii] & mask) mask_bit[ii] = 0x01; // If temperature compensation bit is set, record that fact in mask_bit
+    }
+
+    // Construct total accelerometer bias, including calculated average accelerometer bias from above
+    accel_bias_reg[0] -= (accel_bias[0]/8); // Subtract calculated averaged accelerometer bias scaled to 2048 LSB/g (16 g full scale)
+    accel_bias_reg[1] -= (accel_bias[1]/8);
+    accel_bias_reg[2] -= (accel_bias[2]/8);
+
+    data[0] = (accel_bias_reg[0] >> 8) & 0xFF;
+    data[1] = (accel_bias_reg[0])      & 0xFF;
+    data[1] = data[1] | mask_bit[0]; // preserve temperature compensation bit when writing back to accelerometer bias registers
+    data[2] = (accel_bias_reg[1] >> 8) & 0xFF;
+    data[3] = (accel_bias_reg[1])      & 0xFF;
+    data[3] = data[3] | mask_bit[1]; // preserve temperature compensation bit when writing back to accelerometer bias registers
+    data[4] = (accel_bias_reg[2] >> 8) & 0xFF;
+    data[5] = (accel_bias_reg[2])      & 0xFF;
+    data[5] = data[5] | mask_bit[2]; // preserve temperature compensation bit when writing back to accelerometer bias registers
+
+    // Output scaled accelerometer biases for manual subtraction in the main program
+    dest2[0] = (float)accel_bias[0]/(float)accelsensitivity;
+    dest2[1] = (float)accel_bias[1]/(float)accelsensitivity;
+    dest2[2] = (float)accel_bias[2]/(float)accelsensitivity;
+}
+