IOTON boards API using mbed SDK - http://ioton.cc/plataforma-ton
Dependents: ton-bot_teste ton-bot_seguidor_linha ton-bot_seguidor_parede
Fork of IOTON-API by
Diff: BMX055.h
- Revision:
- 0:cbba28a205fa
- Child:
- 2:b3c3bf0b9101
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BMX055.h Tue Nov 08 00:17:12 2016 +0000 @@ -0,0 +1,883 @@ +/* IMU chipset BMX055 Library + * Copyright (c) 2016 Ioton Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BMX055_H_ +#define BMX055_H_ + +/* Includes ------------------------------------------------------------------*/ +#include "mbed.h" + + +#ifndef M_PI +#define M_PI 3.1415927 +#endif + +// Accelerometer registers +#define BMX055_ACC_WHOAMI 0x00 // should return 0xFA +//#define BMX055_ACC_Reserved 0x01 +#define BMX055_ACC_D_X_LSB 0x02 +#define BMX055_ACC_D_X_MSB 0x03 +#define BMX055_ACC_D_Y_LSB 0x04 +#define BMX055_ACC_D_Y_MSB 0x05 +#define BMX055_ACC_D_Z_LSB 0x06 +#define BMX055_ACC_D_Z_MSB 0x07 +#define BMX055_ACC_D_TEMP 0x08 +#define BMX055_ACC_INT_STATUS_0 0x09 +#define BMX055_ACC_INT_STATUS_1 0x0A +#define BMX055_ACC_INT_STATUS_2 0x0B +#define BMX055_ACC_INT_STATUS_3 0x0C +//#define BMX055_ACC_Reserved 0x0D +#define BMX055_ACC_FIFO_STATUS 0x0E +#define BMX055_ACC_PMU_RANGE 0x0F +#define BMX055_ACC_PMU_BW 0x10 +#define BMX055_ACC_PMU_LPW 0x11 +#define BMX055_ACC_PMU_LOW_POWER 0x12 +#define BMX055_ACC_D_HBW 0x13 +#define BMX055_ACC_BGW_SOFTRESET 0x14 +//#define BMX055_ACC_Reserved 0x15 +#define BMX055_ACC_INT_EN_0 0x16 +#define BMX055_ACC_INT_EN_1 0x17 +#define BMX055_ACC_INT_EN_2 0x18 +#define BMX055_ACC_INT_MAP_0 0x19 +#define BMX055_ACC_INT_MAP_1 0x1A +#define BMX055_ACC_INT_MAP_2 0x1B +//#define BMX055_ACC_Reserved 0x1C +//#define BMX055_ACC_Reserved 0x1D +#define BMX055_ACC_INT_SRC 0x1E +//#define BMX055_ACC_Reserved 0x1F +#define BMX055_ACC_INT_OUT_CTRL 0x20 +#define BMX055_ACC_INT_RST_LATCH 0x21 +#define BMX055_ACC_INT_0 0x22 +#define BMX055_ACC_INT_1 0x23 +#define BMX055_ACC_INT_2 0x24 +#define BMX055_ACC_INT_3 0x25 +#define BMX055_ACC_INT_4 0x26 +#define BMX055_ACC_INT_5 0x27 +#define BMX055_ACC_INT_6 0x28 +#define BMX055_ACC_INT_7 0x29 +#define BMX055_ACC_INT_8 0x2A +#define BMX055_ACC_INT_9 0x2B +#define BMX055_ACC_INT_A 0x2C +#define BMX055_ACC_INT_B 0x2D +#define BMX055_ACC_INT_C 0x2E +#define BMX055_ACC_INT_D 0x2F +#define BMX055_ACC_FIFO_CONFIG_0 0x30 +//#define BMX055_ACC_Reserved 0x31 +#define BMX055_ACC_PMU_SELF_TEST 0x32 +#define BMX055_ACC_TRIM_NVM_CTRL 0x33 +#define BMX055_ACC_BGW_SPI3_WDT 0x34 +//#define BMX055_ACC_Reserved 0x35 +#define BMX055_ACC_OFC_CTRL 0x36 +#define BMX055_ACC_OFC_SETTING 0x37 +#define BMX055_ACC_OFC_OFFSET_X 0x38 +#define BMX055_ACC_OFC_OFFSET_Y 0x39 +#define BMX055_ACC_OFC_OFFSET_Z 0x3A +#define BMX055_ACC_TRIM_GPO 0x3B +#define BMX055_ACC_TRIM_GP1 0x3C +//#define BMX055_ACC_Reserved 0x3D +#define BMX055_ACC_FIFO_CONFIG_1 0x3E +#define BMX055_ACC_FIFO_DATA 0x3F + +// BMX055 Gyroscope Registers +#define BMX055_GYRO_WHOAMI 0x00 // should return 0x0F +//#define BMX055_GYRO_Reserved 0x01 +#define BMX055_GYRO_RATE_X_LSB 0x02 +#define BMX055_GYRO_RATE_X_MSB 0x03 +#define BMX055_GYRO_RATE_Y_LSB 0x04 +#define BMX055_GYRO_RATE_Y_MSB 0x05 +#define BMX055_GYRO_RATE_Z_LSB 0x06 +#define BMX055_GYRO_RATE_Z_MSB 0x07 +//#define BMX055_GYRO_Reserved 0x08 +#define BMX055_GYRO_INT_STATUS_0 0x09 +#define BMX055_GYRO_INT_STATUS_1 0x0A +#define BMX055_GYRO_INT_STATUS_2 0x0B +#define BMX055_GYRO_INT_STATUS_3 0x0C +//#define BMX055_GYRO_Reserved 0x0D +#define BMX055_GYRO_FIFO_STATUS 0x0E +#define BMX055_GYRO_RANGE 0x0F +#define BMX055_GYRO_BW 0x10 +#define BMX055_GYRO_LPM1 0x11 +#define BMX055_GYRO_LPM2 0x12 +#define BMX055_GYRO_RATE_HBW 0x13 +#define BMX055_GYRO_BGW_SOFTRESET 0x14 +#define BMX055_GYRO_INT_EN_0 0x15 +#define BMX055_GYRO_INT_EN_1 0x16 +#define BMX055_GYRO_INT_MAP_0 0x17 +#define BMX055_GYRO_INT_MAP_1 0x18 +#define BMX055_GYRO_INT_MAP_2 0x19 +#define BMX055_GYRO_INT_SRC_1 0x1A +#define BMX055_GYRO_INT_SRC_2 0x1B +#define BMX055_GYRO_INT_SRC_3 0x1C +//#define BMX055_GYRO_Reserved 0x1D +#define BMX055_GYRO_FIFO_EN 0x1E +//#define BMX055_GYRO_Reserved 0x1F +//#define BMX055_GYRO_Reserved 0x20 +#define BMX055_GYRO_INT_RST_LATCH 0x21 +#define BMX055_GYRO_HIGH_TH_X 0x22 +#define BMX055_GYRO_HIGH_DUR_X 0x23 +#define BMX055_GYRO_HIGH_TH_Y 0x24 +#define BMX055_GYRO_HIGH_DUR_Y 0x25 +#define BMX055_GYRO_HIGH_TH_Z 0x26 +#define BMX055_GYRO_HIGH_DUR_Z 0x27 +//#define BMX055_GYRO_Reserved 0x28 +//#define BMX055_GYRO_Reserved 0x29 +//#define BMX055_GYRO_Reserved 0x2A +#define BMX055_GYRO_SOC 0x31 +#define BMX055_GYRO_A_FOC 0x32 +#define BMX055_GYRO_TRIM_NVM_CTRL 0x33 +#define BMX055_GYRO_BGW_SPI3_WDT 0x34 +//#define BMX055_GYRO_Reserved 0x35 +#define BMX055_GYRO_OFC1 0x36 +#define BMX055_GYRO_OFC2 0x37 +#define BMX055_GYRO_OFC3 0x38 +#define BMX055_GYRO_OFC4 0x39 +#define BMX055_GYRO_TRIM_GP0 0x3A +#define BMX055_GYRO_TRIM_GP1 0x3B +#define BMX055_GYRO_BIST 0x3C +#define BMX055_GYRO_FIFO_CONFIG_0 0x3D +#define BMX055_GYRO_FIFO_CONFIG_1 0x3E + +// BMX055 magnetometer registers +#define BMX055_MAG_WHOAMI 0x40 // should return 0x32 +#define BMX055_MAG_Reserved 0x41 +#define BMX055_MAG_XOUT_LSB 0x42 +#define BMX055_MAG_XOUT_MSB 0x43 +#define BMX055_MAG_YOUT_LSB 0x44 +#define BMX055_MAG_YOUT_MSB 0x45 +#define BMX055_MAG_ZOUT_LSB 0x46 +#define BMX055_MAG_ZOUT_MSB 0x47 +#define BMX055_MAG_ROUT_LSB 0x48 +#define BMX055_MAG_ROUT_MSB 0x49 +#define BMX055_MAG_INT_STATUS 0x4A +#define BMX055_MAG_PWR_CNTL1 0x4B +#define BMX055_MAG_PWR_CNTL2 0x4C +#define BMX055_MAG_INT_EN_1 0x4D +#define BMX055_MAG_INT_EN_2 0x4E +#define BMX055_MAG_LOW_THS 0x4F +#define BMX055_MAG_HIGH_THS 0x50 +#define BMX055_MAG_REP_XY 0x51 +#define BMX055_MAG_REP_Z 0x52 +/* Trim Extended Registers */ +#define BMM050_DIG_X1 0x5D // needed for magnetic field calculation +#define BMM050_DIG_Y1 0x5E +#define BMM050_DIG_Z4_LSB 0x62 +#define BMM050_DIG_Z4_MSB 0x63 +#define BMM050_DIG_X2 0x64 +#define BMM050_DIG_Y2 0x65 +#define BMM050_DIG_Z2_LSB 0x68 +#define BMM050_DIG_Z2_MSB 0x69 +#define BMM050_DIG_Z1_LSB 0x6A +#define BMM050_DIG_Z1_MSB 0x6B +#define BMM050_DIG_XYZ1_LSB 0x6C +#define BMM050_DIG_XYZ1_MSB 0x6D +#define BMM050_DIG_Z3_LSB 0x6E +#define BMM050_DIG_Z3_MSB 0x6F +#define BMM050_DIG_XY2 0x70 +#define BMM050_DIG_XY1 0x71 + +// Using SDO1 = SDO2 = CSB3 = GND as designed +// Seven-bit device addresses are ACC = 0x18, GYRO = 0x68, MAG = 0x10 +#define BMX055_ACC_ADDRESS 0x18 << 1 // Address of BMX055 accelerometer +#define BMX055_GYRO_ADDRESS 0x68 << 1 // Address of BMX055 gyroscope +#define BMX055_MAG_ADDRESS 0x10 << 1 // Address of BMX055 magnetometer + +// Set initial input parameters +// define BMX055 ACC full scale options +#define AFS_2G 0x03 +#define AFS_4G 0x05 +#define AFS_8G 0x08 +#define AFS_16G 0x0C + +enum ACCBW { // define BMX055 accelerometer bandwidths + ABW_8Hz, // 7.81 Hz, 64 ms update time + ABW_16Hz, // 15.63 Hz, 32 ms update time + ABW_31Hz, // 31.25 Hz, 16 ms update time + ABW_63Hz, // 62.5 Hz, 8 ms update time + ABW_125Hz, // 125 Hz, 4 ms update time + ABW_250Hz, // 250 Hz, 2 ms update time + ABW_500Hz, // 500 Hz, 1 ms update time + ABW_100Hz // 1000 Hz, 0.5 ms update time +}; + +enum Gscale { + GFS_2000DPS = 0, + GFS_1000DPS, + GFS_500DPS, + GFS_250DPS, + GFS_125DPS +}; + +enum GODRBW { + G_2000Hz523Hz = 0, // 2000 Hz ODR and unfiltered (bandwidth 523Hz) + G_2000Hz230Hz, + G_1000Hz116Hz, + G_400Hz47Hz, + G_200Hz23Hz, + G_100Hz12Hz, + G_200Hz64Hz, + G_100Hz32Hz // 100 Hz ODR and 32 Hz bandwidth +}; + +enum MODR { + MODR_10Hz = 0, // 10 Hz ODR + MODR_2Hz , // 2 Hz ODR + MODR_6Hz , // 6 Hz ODR + MODR_8Hz , // 8 Hz ODR + MODR_15Hz , // 15 Hz ODR + MODR_20Hz , // 20 Hz ODR + MODR_25Hz , // 25 Hz ODR + MODR_30Hz // 30 Hz ODR +}; + +enum Mmode { + lowPower = 0, // rms noise ~1.0 microTesla, 0.17 mA power + Regular , // rms noise ~0.6 microTesla, 0.5 mA power + enhancedRegular , // rms noise ~0.5 microTesla, 0.8 mA power + highAccuracy // rms noise ~0.3 microTesla, 4.9 mA power +}; + +// Set up I2C, (SDA,SCL) +I2C i2c2(PB_11, PB_10); + +// Specify sensor full scale +uint8_t Ascale = AFS_2G; +uint8_t Gscale = GFS_125DPS; +float aRes, gRes, mRes; // scale resolutions per LSB for the sensors + +// Parameters to hold BMX055 trim values +int8_t dig_x1; +int8_t dig_y1; +int8_t dig_x2; +int8_t dig_y2; +uint16_t dig_z1; +int16_t dig_z2; +int16_t dig_z3; +int16_t dig_z4; +uint8_t dig_xy1; +int8_t dig_xy2; +uint16_t dig_xyz1; + +// BMX055 variables +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 13/15-bit signed magnetometer sensor output +float gyroBias[3] = {0, 0, 0}, accelBias[3] = {0, 0, 0}, magBias[3] = {0, 0, 0}; // Bias corrections for gyro, accelerometer, mag +float SelfTest[6]; // holds results of gyro and accelerometer self test + +// global constants for 9 DoF fusion and AHRS (Attitude and Heading Reference System) +float GyroMeasError = M_PI * (40.0f / 180.0f); // gyroscope measurement error in rads/s (start at 40 deg/s) +float GyroMeasDrift = M_PI * (0.0f / 180.0f); // gyroscope measurement drift in rad/s/s (start at 0.0 deg/s/s) +// There is a tradeoff in the beta parameter between accuracy and response speed. +// In the original Madgwick study, beta of 0.041 (corresponding to GyroMeasError of 2.7 degrees/s) was found to give optimal accuracy. +// However, with this value, the LSM9SD0 response time is about 10 seconds to a stable initial quaternion. +// Subsequent changes also require a longish lag time to a stable output, not fast enough for a quadcopter or robot car! +// By increasing beta (GyroMeasError) by about a factor of fifteen, the response time constant is reduced to ~2 sec +// I haven't noticed any reduction in solution accuracy. This is essentially the I coefficient in a PID control sense; +// the bigger the feedback coefficient, the faster the solution converges, usually at the expense of accuracy. +// In any case, this is the free parameter in the Madgwick filtering and fusion scheme. +float beta = sqrt(3.0f / 4.0f) * GyroMeasError; // compute beta +float zeta = sqrt(3.0f / 4.0f) * GyroMeasDrift; // compute zeta, the other free parameter in the Madgwick scheme usually set to a small or zero value +#define Kp 2.0f * 5.0f // these are the free parameters in the Mahony filter and fusion scheme, Kp for proportional feedback, Ki for integral +#define Ki 0.0f + +// Declination at Sao Paulo, Brazil is -21 degrees 7 minutes on 2016-03-27 +#define LOCAL_DECLINATION -21.1f + +float deltat = 0.0f; // integration interval for both filter schemes + +float q[4] = {1.0f, 0.0f, 0.0f, 0.0f}; // vector to hold quaternion +float eInt[3] = {0.0f, 0.0f, 0.0f}; // vector to hold integral error for Mahony method + + +class BMX055 { +private: + float pitch, yaw, roll; + + void writeByte(uint8_t address, uint8_t subAddress, uint8_t data) + { + char data_write[2]; + data_write[0] = subAddress; + data_write[1] = data; + i2c2.write(address, data_write, 2, 0); + } + + char readByte(uint8_t address, uint8_t subAddress) + { + char data[1]; // `data` will store the register data + char data_write[1]; + data_write[0] = subAddress; + i2c2.write(address, data_write, 1, 1); // no stop + i2c2.read(address, data, 1, 0); + return data[0]; + } + + void readBytes(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest) + { + char data[14]; + char data_write[1]; + data_write[0] = subAddress; + i2c2.write(address, data_write, 1, 1); // no stop + i2c2.read(address, data, count, 0); + for(int ii = 0; ii < count; ii++) + { + dest[ii] = data[ii]; + } + } + +public: + BMX055() + { + //Set up I2C + i2c2.frequency(400000); // use fast (400 kHz) I2C + } + + float getAres(void) + { + switch (Ascale) + { + // Possible accelerometer scales (and their register bit settings) are: + // 2 Gs (0011), 4 Gs (0101), 8 Gs (1000), and 16 Gs (1100). + // BMX055 ACC data is signed 12 bit + case AFS_2G: + aRes = 2.0/2048.0; + break; + case AFS_4G: + aRes = 4.0/2048.0; + break; + case AFS_8G: + aRes = 8.0/2048.0; + break; + case AFS_16G: + aRes = 16.0/2048.0; + break; + } + + return aRes; + } + + float getGres(void) + { + switch (Gscale) + { + // Possible gyro scales (and their register bit settings) are: + // 125 DPS (100), 250 DPS (011), 500 DPS (010), 1000 DPS (001), and 2000 DPS (000). + case GFS_125DPS: + gRes = 124.87/32768.0; // per data sheet, not exactly 125!? + break; + case GFS_250DPS: + gRes = 249.75/32768.0; + break; + case GFS_500DPS: + gRes = 499.5/32768.0; + break; + case GFS_1000DPS: + gRes = 999.0/32768.0; + break; + case GFS_2000DPS: + gRes = 1998.0/32768.0; + break; + } + + return gRes; + } + + float getPitch(void) + { + return pitch; + } + + float getRoll(void) + { + return roll; + } + + float getYaw(void) + { + return yaw; + } + + void readAccelData(int16_t * destination) + { + uint8_t rawData[6]; // x/y/z accel register data stored here + + // Read the six raw data registers into data array + readBytes(BMX055_ACC_ADDRESS, BMX055_ACC_D_X_LSB, 6, &rawData[0]); + if((rawData[0] & 0x01) && (rawData[2] & 0x01) && (rawData[4] & 0x01)) + { // Check that all 3 axes have new data + // Turn the MSB and LSB into a signed 12-bit value + destination[0] = (int16_t) (((int16_t)rawData[1] << 8) | rawData[0]) >> 4; + destination[1] = (int16_t) (((int16_t)rawData[3] << 8) | rawData[2]) >> 4; + destination[2] = (int16_t) (((int16_t)rawData[5] << 8) | rawData[4]) >> 4; + } + } + + void readGyroData(int16_t * destination) + { + uint8_t rawData[6]; // x/y/z gyro register data stored here + + // Read the six raw data registers sequentially into data array + readBytes(BMX055_GYRO_ADDRESS, BMX055_GYRO_RATE_X_LSB, 6, &rawData[0]); + // Turn the MSB and LSB into a signed 16-bit value + destination[0] = (int16_t) (((int16_t)rawData[1] << 8) | rawData[0]); + destination[1] = (int16_t) (((int16_t)rawData[3] << 8) | rawData[2]); + destination[2] = (int16_t) (((int16_t)rawData[5] << 8) | rawData[4]); + } + + void readMagData(int16_t * magData) + { + int16_t mdata_x = 0, mdata_y = 0, mdata_z = 0, temp = 0; + uint16_t data_r = 0; + uint8_t rawData[8]; // x/y/z hall magnetic field data, and Hall resistance data + readBytes(BMX055_MAG_ADDRESS, BMX055_MAG_XOUT_LSB, 8, &rawData[0]); // Read the eight raw data registers sequentially into data array + if(rawData[6] & 0x01) { // Check if data ready status bit is set + mdata_x = (int16_t) (((int16_t)rawData[1] << 8) | rawData[0]) >> 3; // 13-bit signed integer for x-axis field + mdata_y = (int16_t) (((int16_t)rawData[3] << 8) | rawData[2]) >> 3; // 13-bit signed integer for y-axis field + mdata_z = (int16_t) (((int16_t)rawData[5] << 8) | rawData[4]) >> 1; // 15-bit signed integer for z-axis field + data_r = (uint16_t) (((uint16_t)rawData[7] << 8) | rawData[6]) >> 2; // 14-bit unsigned integer for Hall resistance + + // calculate temperature compensated 16-bit magnetic fields + temp = ((int16_t)(((uint16_t)((((int32_t)dig_xyz1) << 14)/(data_r != 0 ? data_r : dig_xyz1))) - ((uint16_t)0x4000))); + magData[0] = ((int16_t)((((int32_t)mdata_x) * + ((((((((int32_t)dig_xy2) * ((((int32_t)temp) * ((int32_t)temp)) >> 7)) + + (((int32_t)temp) * ((int32_t)(((int16_t)dig_xy1) << 7)))) >> 9) + + ((int32_t)0x100000)) * ((int32_t)(((int16_t)dig_x2) + ((int16_t)0xA0)))) >> 12)) >> 13)) + + (((int16_t)dig_x1) << 3); + + temp = ((int16_t)(((uint16_t)((((int32_t)dig_xyz1) << 14)/(data_r != 0 ? data_r : dig_xyz1))) - ((uint16_t)0x4000))); + magData[1] = ((int16_t)((((int32_t)mdata_y) * + ((((((((int32_t)dig_xy2) * ((((int32_t)temp) * ((int32_t)temp)) >> 7)) + + (((int32_t)temp) * ((int32_t)(((int16_t)dig_xy1) << 7)))) >> 9) + + ((int32_t)0x100000)) * ((int32_t)(((int16_t)dig_y2) + ((int16_t)0xA0)))) >> 12)) >> 13)) + + (((int16_t)dig_y1) << 3); + magData[2] = (((((int32_t)(mdata_z - dig_z4)) << 15) - ((((int32_t)dig_z3) * ((int32_t)(((int16_t)data_r) - + ((int16_t)dig_xyz1))))>>2))/(dig_z2 + ((int16_t)(((((int32_t)dig_z1) * ((((int16_t)data_r) << 1)))+(1<<15))>>16)))); + } + } + + float getTemperature() + { + uint8_t c = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_D_TEMP); // Read the raw data register + int16_t tempCount = ((int16_t)((int16_t)c << 8)) >> 8 ; // Turn the byte into a signed 8-bit integer + + return ((((float)tempCount) * 0.5) + 23.0); // temperature in degrees Centigrade + } + + void fastcompaccelBMX055(float * dest1) + { + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL, 0x80); // set all accel offset compensation registers to zero + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_SETTING, 0x20); // set offset targets to 0, 0, and +1 g for x, y, z axes + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL, 0x20); // calculate x-axis offset + + uint8_t c = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL); + while(!(c & 0x10)) + { // check if fast calibration complete + c = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL); + wait_ms(10); + } + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL, 0x40); // calculate y-axis offset + + c = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL); + while(!(c & 0x10)) + { // check if fast calibration complete + c = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL); + wait_ms(10); + } + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL, 0x60); // calculate z-axis offset + + c = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL); + while(!(c & 0x10)) + { // check if fast calibration complete + c = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_CTRL); + wait_ms(10); + } + + int8_t compx = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_OFFSET_X); + int8_t compy = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_OFFSET_Y); + int8_t compz = readByte(BMX055_ACC_ADDRESS, BMX055_ACC_OFC_OFFSET_Z); + + dest1[0] = (float) compx/128.; // accleration bias in g + dest1[1] = (float) compy/128.; // accleration bias in g + dest1[2] = (float) compz/128.; // accleration bias in g + } + + void magcalBMX055(float * dest1) + { + uint16_t ii = 0, sample_count = 0; + int32_t mag_bias[3] = {0, 0, 0}; + int16_t mag_max[3] = {0, 0, 0}, mag_min[3] = {0, 0, 0}; + + // ## "Mag Calibration: Wave device in a figure eight until done!" ## + //wait(4); + + sample_count = 48; + for(ii = 0; ii < sample_count; ii++) + { + int16_t mag_temp[3] = {0, 0, 0}; + readMagData(mag_temp); + 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]; + } + wait_ms(105); // at 10 Hz ODR, new mag data is available every 100 ms + } + + 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 + + // save mag biases in G for main program + dest1[0] = (float) mag_bias[0]*mRes; + dest1[1] = (float) mag_bias[1]*mRes; + dest1[2] = (float) mag_bias[2]*mRes; + + // ## "Mag Calibration done!" ## + } + + // Get trim values for magnetometer sensitivity + void trim(void) + { + uint8_t rawData[2]; //placeholder for 2-byte trim data + + dig_x1 = readByte(BMX055_ACC_ADDRESS, BMM050_DIG_X1); + dig_x2 = readByte(BMX055_ACC_ADDRESS, BMM050_DIG_X2); + dig_y1 = readByte(BMX055_ACC_ADDRESS, BMM050_DIG_Y1); + dig_y2 = readByte(BMX055_ACC_ADDRESS, BMM050_DIG_Y2); + dig_xy1 = readByte(BMX055_ACC_ADDRESS, BMM050_DIG_XY1); + dig_xy2 = readByte(BMX055_ACC_ADDRESS, BMM050_DIG_XY2); + readBytes(BMX055_MAG_ADDRESS, BMM050_DIG_Z1_LSB, 2, &rawData[0]); + dig_z1 = (uint16_t) (((uint16_t)rawData[1] << 8) | rawData[0]); + readBytes(BMX055_MAG_ADDRESS, BMM050_DIG_Z2_LSB, 2, &rawData[0]); + dig_z2 = (int16_t) (((int16_t)rawData[1] << 8) | rawData[0]); + readBytes(BMX055_MAG_ADDRESS, BMM050_DIG_Z3_LSB, 2, &rawData[0]); + dig_z3 = (int16_t) (((int16_t)rawData[1] << 8) | rawData[0]); + readBytes(BMX055_MAG_ADDRESS, BMM050_DIG_Z4_LSB, 2, &rawData[0]); + dig_z4 = (int16_t) (((int16_t)rawData[1] << 8) | rawData[0]); + readBytes(BMX055_MAG_ADDRESS, BMM050_DIG_XYZ1_LSB, 2, &rawData[0]); + dig_xyz1 = (uint16_t) (((uint16_t)rawData[1] << 8) | rawData[0]); + } + + /** Initialize device for active mode + * @param Ascale set accel full scale + * @param ACCBW set bandwidth for accelerometer + * @param Gscale set gyro full scale + * @param GODRBW set gyro ODR and bandwidth + * @param Mmode set magnetometer operation mode + * @param MODR set magnetometer data rate + * @see ACCBW, GODRBW and MODR enums + */ + void init(uint8_t mAscale = AFS_2G, uint8_t ACCBW = ABW_16Hz, + uint8_t mGscale = GFS_125DPS, uint8_t GODRBW = G_200Hz23Hz, + uint8_t Mmode = Regular, uint8_t MODR = MODR_10Hz) + { + Ascale = mAscale; + Gscale = mGscale; + + // Configure accelerometer + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_PMU_RANGE, Ascale & 0x0F); // Set accelerometer full scale + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_PMU_BW, (0x08 | ACCBW) & 0x0F); // Set accelerometer bandwidth + writeByte(BMX055_ACC_ADDRESS, BMX055_ACC_D_HBW, 0x00); // Use filtered data + + // Configure Gyro + writeByte(BMX055_GYRO_ADDRESS, BMX055_GYRO_RANGE, Gscale); // set GYRO FS range + writeByte(BMX055_GYRO_ADDRESS, BMX055_GYRO_BW, GODRBW); // set GYRO ODR and Bandwidth + + // Configure magnetometer + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_PWR_CNTL1, 0x82); // Softreset magnetometer, ends up in sleep mode + wait_ms(100); + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_PWR_CNTL1, 0x01); // Wake up magnetometer + wait_ms(100); + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_PWR_CNTL2, MODR << 3); // Normal mode + + // Set up four standard configurations for the magnetometer + switch (Mmode) + { + case lowPower: + // Low-power + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_XY, 0x01); // 3 repetitions (oversampling) + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_Z, 0x02); // 3 repetitions (oversampling) + break; + case Regular: + // Regular + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_XY, 0x04); // 9 repetitions (oversampling) + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_Z, 0x16); // 15 repetitions (oversampling) + break; + case enhancedRegular: + // Enhanced Regular + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_XY, 0x07); // 15 repetitions (oversampling) + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_Z, 0x22); // 27 repetitions (oversampling) + break; + case highAccuracy: + // High Accuracy + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_XY, 0x17); // 47 repetitions (oversampling) + writeByte(BMX055_MAG_ADDRESS, BMX055_MAG_REP_Z, 0x51); // 83 repetitions (oversampling) + break; + } + + // get sensor resolutions, only need to do this once + getAres(); + getGres(); + // magnetometer resolution is 1 microTesla/16 counts or 1/1.6 milliGauss/count + mRes = 1./1.6; + + trim(); // read the magnetometer calibration data + + fastcompaccelBMX055(accelBias); + magcalBMX055(magBias); + // TODO: see magcalBMX055(): 128 samples * 105ms = 13.44s + // So far, magnetometer bias is calculated and subtracted here manually, should construct an algorithm to do it automatically + // like the gyro and accelerometer biases + // magBias[0] = -5.; // User environmental x-axis correction in milliGauss + // magBias[1] = -95.; // User environmental y-axis correction in milliGauss + // magBias[2] = -260.; // User environmental z-axis correction in milliGauss + + } + + // Implementation of Sebastian Madgwick's "...efficient orientation filter for... inertial/magnetic sensor arrays" + // (see http://www.x-io.co.uk/category/open-source/ for examples and more details) + // which fuses acceleration, rotation rate, and magnetic moments to produce a quaternion-based estimate of absolute + // device orientation -- which can be converted to yaw, pitch, and roll. Useful for stabilizing quadcopters, etc. + // The performance of the orientation filter is at least as good as conventional Kalman-based filtering algorithms + // but is much less computationally intensive + void MadgwickQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz) + { + float q1 = q[0], q2 = q[1], q3 = q[2], q4 = q[3]; // short name local variable for readability + float norm; + float hx, hy, _2bx, _2bz; + float s1, s2, s3, s4; + float qDot1, qDot2, qDot3, qDot4; + + // Auxiliary variables to avoid repeated arithmetic + float _2q1mx; + float _2q1my; + float _2q1mz; + float _2q2mx; + float _4bx; + float _4bz; + float _2q1 = 2.0f * q1; + float _2q2 = 2.0f * q2; + float _2q3 = 2.0f * q3; + float _2q4 = 2.0f * q4; + float _2q1q3 = 2.0f * q1 * q3; + float _2q3q4 = 2.0f * q3 * q4; + float q1q1 = q1 * q1; + float q1q2 = q1 * q2; + float q1q3 = q1 * q3; + float q1q4 = q1 * q4; + float q2q2 = q2 * q2; + float q2q3 = q2 * q3; + float q2q4 = q2 * q4; + float q3q3 = q3 * q3; + float q3q4 = q3 * q4; + float q4q4 = q4 * q4; + + // Normalise accelerometer measurement + norm = sqrt(ax * ax + ay * ay + az * az); + if (norm == 0.0f) return; // handle NaN + norm = 1.0f/norm; + ax *= norm; + ay *= norm; + az *= norm; + + // Normalise magnetometer measurement + norm = sqrt(mx * mx + my * my + mz * mz); + if (norm == 0.0f) return; // handle NaN + norm = 1.0f/norm; + mx *= norm; + my *= norm; + mz *= norm; + + // Reference direction of Earth's magnetic field + _2q1mx = 2.0f * q1 * mx; + _2q1my = 2.0f * q1 * my; + _2q1mz = 2.0f * q1 * mz; + _2q2mx = 2.0f * q2 * mx; + hx = mx * q1q1 - _2q1my * q4 + _2q1mz * q3 + mx * q2q2 + _2q2 * my * q3 + _2q2 * mz * q4 - mx * q3q3 - mx * q4q4; + hy = _2q1mx * q4 + my * q1q1 - _2q1mz * q2 + _2q2mx * q3 - my * q2q2 + my * q3q3 + _2q3 * mz * q4 - my * q4q4; + _2bx = sqrt(hx * hx + hy * hy); + _2bz = -_2q1mx * q3 + _2q1my * q2 + mz * q1q1 + _2q2mx * q4 - mz * q2q2 + _2q3 * my * q4 - mz * q3q3 + mz * q4q4; + _4bx = 2.0f * _2bx; + _4bz = 2.0f * _2bz; + + // Gradient decent algorithm corrective step + s1 = -_2q3 * (2.0f * q2q4 - _2q1q3 - ax) + _2q2 * (2.0f * q1q2 + _2q3q4 - ay) - _2bz * q3 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q4 + _2bz * q2) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q3 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); + 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 = 1.0f/norm; + s1 *= norm; + s2 *= norm; + s3 *= norm; + s4 *= norm; + + // Compute rate of change of quaternion + qDot1 = 0.5f * (-q2 * gx - q3 * gy - q4 * gz) - beta * s1; + qDot2 = 0.5f * (q1 * gx + q3 * gz - q4 * gy) - beta * s2; + qDot3 = 0.5f * (q1 * gy - q2 * gz + q4 * gx) - beta * s3; + qDot4 = 0.5f * (q1 * gz + q2 * gy - q3 * gx) - beta * s4; + + // Integrate to yield quaternion + q1 += qDot1 * deltat; + q2 += qDot2 * deltat; + q3 += qDot3 * deltat; + q4 += qDot4 * deltat; + norm = sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); // normalise quaternion + norm = 1.0f/norm; + q[0] = q1 * norm; + q[1] = q2 * norm; + q[2] = q3 * norm; + q[3] = q4 * norm; + + } + + /** Get raw 9-axis motion sensor readings (accel/gyro/compass). + * @param ax 12-bit signed integer container for accelerometer X-axis value + * @param ay 12-bit signed integer container for accelerometer Y-axis value + * @param az 12-bit signed integer container for accelerometer Z-axis value + * @param gx 16-bit signed integer container for gyroscope X-axis value + * @param gy 16-bit signed integer container for gyroscope Y-axis value + * @param gz 16-bit signed integer container for gyroscope Z-axis value + * @param mx 13-bit signed integer container for magnetometer X-axis value + * @param my 13-bit signed integer container for magnetometer Y-axis value + * @param mz 15-bit signed integer container for magnetometer Z-axis value + * @see getAcceleration() + * @see getRotation() + * @see getMag() + */ + void getRaw9( int16_t* ax, int16_t* ay, int16_t* az, + int16_t* gx, int16_t* gy, int16_t* gz, + int16_t* mx, int16_t* my, int16_t* mz) + { + uint8_t rawData[8]; // x/y/z MSB and LSB registers raw data stored here + + // Read the six raw data registers into data array + // Turn the MSB and LSB into a signed 12-bit value + readBytes(BMX055_ACC_ADDRESS, BMX055_ACC_D_X_LSB, 6, rawData); + *ax = (int16_t)(((int16_t)rawData[1] << 8) | rawData[0]) >> 4; + *ay = (int16_t)(((int16_t)rawData[3] << 8) | rawData[2]) >> 4; + *az = (int16_t)(((int16_t)rawData[5] << 8) | rawData[4]) >> 4; + + // Read the six raw data registers sequentially into data array + // Turn the MSB and LSB into a signed 16-bit value + readBytes(BMX055_GYRO_ADDRESS, BMX055_GYRO_RATE_X_LSB, 6, rawData); + *gx = (int16_t)(((int16_t)rawData[1] << 8) | rawData[0]); + *gy = (int16_t)(((int16_t)rawData[3] << 8) | rawData[2]); + *gz = (int16_t)(((int16_t)rawData[5] << 8) | rawData[4]); + + // Read the six raw data registers into data array + // 13-bit signed integer for x-axis and y-axis field + // 15-bit signed integer for z-axis field + readBytes(BMX055_MAG_ADDRESS, BMX055_MAG_XOUT_LSB, 8, rawData); + if(rawData[6] & 0x01) // Check if data ready status bit is set + { + *mx = (int16_t)(((int16_t)rawData[1] << 8) | rawData[0]) >> 3; + *my = (int16_t)(((int16_t)rawData[3] << 8) | rawData[2]) >> 3; + *mz = (int16_t)(((int16_t)rawData[5] << 8) | rawData[4]) >> 1; + } + } + + /** Get raw 9-axis motion sensor readings (accel/gyro/compass). + * @param ax accelerometer X-axis value (g's) + * @param ay accelerometer Y-axis value (g's) + * @param az accelerometer Z-axis value (g's) + * @param gx gyroscope X-axis value (degrees per second) + * @param gy gyroscope Y-axis value (degrees per second) + * @param gz gyroscope Z-axis value (degrees per second) + * @param mx magnetometer X-axis value (milliGauss) + * @param my magnetometer Y-axis value (milliGauss) + * @param mz magnetometer Z-axis value (milliGauss) + * @see getAcceleration() + * @see getRotation() + * @see getMag() + */ + void getMotion9( float* ax, float* ay, float* az, + float* gx, float* gy, float* gz, + float* mx, float* my, float* mz) + { + 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 13/15-bit signed magnetometer sensor output + + // Read the x/y/z raw values + readAccelData(accelCount); + + // Calculate the accleration value into actual g's + // get actual g value, this depends on scale being set + *ax = (float)accelCount[0]*aRes; // + accelBias[0]; + *ay = (float)accelCount[1]*aRes; // + accelBias[1]; + *az = (float)accelCount[2]*aRes; // + accelBias[2]; + + + // Read the x/y/z raw values + readGyroData(gyroCount); + + // Calculate the gyro value into actual degrees per second + // get actual gyro value, this depends on scale being set + *gx = (float)gyroCount[0]*gRes; + *gy = (float)gyroCount[1]*gRes; + *gz = (float)gyroCount[2]*gRes; + + + // Read the x/y/z raw values + readMagData(magCount); + + // Calculate the magnetometer values in milliGauss + // Temperature-compensated magnetic field is in 16 LSB/microTesla + // get actual magnetometer value, this depends on scale being set + *mx = (float)magCount[0]*mRes - magBias[0]; + *my = (float)magCount[1]*mRes - magBias[1]; + *mz = (float)magCount[2]*mRes - magBias[2]; + } + + void runAHRS(float mdeltat, float local_declination = LOCAL_DECLINATION) + { + float ax, ay, az, gx, gy, gz, mx, my, mz; + + getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz); + deltat = mdeltat; + + // Sensors x (y)-axis of the accelerometer is aligned with the -y (x)-axis of the magnetometer; + // the magnetometer z-axis (+ up) is aligned with z-axis (+ up) of accelerometer and gyro! + // We have to make some allowance for this orientation mismatch in feeding the output to the quaternion filter. + // For the BMX-055, we have chosen a magnetic rotation that keeps the sensor forward along the x-axis just like + // in the MPU9250 sensor. This rotation can be modified to allow any convenient orientation convention. + // This is ok by aircraft orientation standards! + // Pass gyro rate as rad/s + //MadgwickQuaternionUpdate(ax, ay, az, gx*M_PI/180.0f, gy*M_PI/180.0f, gz*M_PI/180.0f, -my, mx, mz); + MadgwickQuaternionUpdate(-ay, ax, az, -gy*M_PI/180.0f, gx*M_PI/180.0f, gz*M_PI/180.0f, mx, my, mz); + + + // Define output variables from updated quaternion---these are Tait-Bryan angles, commonly used in aircraft orientation. + // In this coordinate system, the positive z-axis is down toward Earth. + // Yaw is the angle between Sensor x-axis and Earth magnetic North (or true North if corrected for local declination, looking down on the sensor positive yaw is counterclockwise. + // Pitch is angle between sensor x-axis and Earth ground plane, toward the Earth is positive, up toward the sky is negative. + // Roll is angle between sensor y-axis and Earth ground plane, y-axis up is positive roll. + // These arise from the definition of the homogeneous rotation matrix constructed from quaternions. + // Tait-Bryan angles as well as Euler angles are non-commutative; that is, the get the correct orientation the rotations must be + // applied in the correct order which for this configuration is yaw, pitch, and then roll. + // For more see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles which has additional links. + yaw = atan2(2.0f * (q[1] * q[2] + q[0] * q[3]), q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]); + pitch = -asin(2.0f * (q[1] * q[3] - q[0] * q[2])); + roll = atan2(2.0f * (q[0] * q[1] + q[2] * q[3]), q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]); + pitch *= 180.0f / M_PI; + yaw *= 180.0f / M_PI; + yaw -= local_declination; + roll *= 180.0f / M_PI; + } +}; + +#endif /* BMX055_H_ */ \ No newline at end of file