You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1130 lines
32 KiB
1130 lines
32 KiB
/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- |
|
/* |
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
-- Coded by Victor Mayoral Vilches -- |
|
|
|
The MPU9150 is a sensor composed by a MPU6050 with a AK8975 on the auxiliary bus. |
|
Please check the following links for datasheets and documentation: |
|
- http://www.invensense.com/mems/gyro/documents/PS-MPU-9150A-00v4_3.pdf |
|
- http://www.invensense.com/mems/gyro/documents/RM-MPU-9150A-00v4_2.pdf |
|
|
|
Note that this is an experimental driver. It is not used by any |
|
actively maintained board and should be considered untested and |
|
unmaintained |
|
*/ |
|
|
|
#include <AP_HAL.h> |
|
#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX |
|
|
|
#include <AP_Math.h> |
|
#include "AP_InertialSensor_MPU9150.h" |
|
#include <stdio.h> |
|
#include <unistd.h> |
|
#include <time.h> |
|
|
|
const extern AP_HAL::HAL& hal; |
|
|
|
/////// |
|
/* Hardware registers needed by driver. */ |
|
struct gyro_reg_s { |
|
uint8_t who_am_i; |
|
uint8_t rate_div; |
|
uint8_t lpf; |
|
uint8_t prod_id; |
|
uint8_t user_ctrl; |
|
uint8_t fifo_en; |
|
uint8_t gyro_cfg; |
|
uint8_t accel_cfg; |
|
uint8_t motion_thr; |
|
uint8_t motion_dur; |
|
uint8_t fifo_count_h; |
|
uint8_t fifo_r_w; |
|
uint8_t raw_gyro; |
|
uint8_t raw_accel; |
|
uint8_t temp; |
|
uint8_t int_enable; |
|
uint8_t dmp_int_status; |
|
uint8_t int_status; |
|
uint8_t pwr_mgmt_1; |
|
uint8_t pwr_mgmt_2; |
|
uint8_t int_pin_cfg; |
|
uint8_t mem_r_w; |
|
uint8_t accel_offs; |
|
uint8_t i2c_mst; |
|
uint8_t bank_sel; |
|
uint8_t mem_start_addr; |
|
uint8_t prgm_start_h; |
|
uint8_t raw_compass; |
|
uint8_t yg_offs_tc; |
|
uint8_t s0_addr; |
|
uint8_t s0_reg; |
|
uint8_t s0_ctrl; |
|
uint8_t s1_addr; |
|
uint8_t s1_reg; |
|
uint8_t s1_ctrl; |
|
uint8_t s4_ctrl; |
|
uint8_t s0_do; |
|
uint8_t s1_do; |
|
uint8_t i2c_delay_ctrl; |
|
}; |
|
|
|
/* Information specific to a particular device. */ |
|
struct hw_s { |
|
uint8_t addr; |
|
uint16_t max_fifo; |
|
uint8_t num_reg; |
|
uint16_t temp_sens; |
|
int16_t temp_offset; |
|
uint16_t bank_size; |
|
uint16_t compass_fsr; |
|
}; |
|
|
|
/* Information for self-test. */ |
|
struct test_s { |
|
uint32_t gyro_sens; |
|
uint32_t accel_sens; |
|
uint8_t reg_rate_div; |
|
uint8_t reg_lpf; |
|
uint8_t reg_gyro_fsr; |
|
uint8_t reg_accel_fsr; |
|
uint16_t wait_ms; |
|
uint8_t packet_thresh; |
|
float min_dps; |
|
float max_dps; |
|
float max_gyro_var; |
|
float min_g; |
|
float max_g; |
|
float max_accel_var; |
|
}; |
|
|
|
/* Gyro driver state variables. */ |
|
struct gyro_state_s { |
|
const struct gyro_reg_s *reg; |
|
const struct hw_s *hw; |
|
const struct test_s *test; |
|
}; |
|
|
|
/* Filter configurations. The values correspond to the DLPF_CFG register. |
|
Note that the gyro and accel frequencies are slightly different. |
|
(DLPF_CFG register, RM-MPU-9150A00.pdf, pg. 13) |
|
*/ |
|
enum lpf_e { |
|
INV_FILTER_256HZ_NOLPF2 = 0, |
|
INV_FILTER_188HZ = 1, |
|
INV_FILTER_98HZ = 2, |
|
INV_FILTER_42HZ = 3, |
|
INV_FILTER_20HZ = 4, |
|
INV_FILTER_10HZ = 5, |
|
INV_FILTER_5HZ = 6, |
|
INV_FILTER_2100HZ_NOLPF = 7, |
|
NUM_FILTER |
|
}; |
|
|
|
/* Full scale ranges. FS_SEL register. |
|
(RM-MPU-9150A00.pdf, pg. 14) |
|
*/ |
|
enum gyro_fsr_e { |
|
INV_FSR_250DPS = 0, |
|
INV_FSR_500DPS = 1, |
|
INV_FSR_1000DPS = 2, |
|
INV_FSR_2000DPS = 3, |
|
NUM_GYRO_FSR |
|
}; |
|
|
|
/* Full scale ranges. AFS_SEL register. |
|
(RM-MPU-9150A00.pdf, pg. 15) |
|
*/ |
|
enum accel_fsr_e { |
|
INV_FSR_2G = 0, |
|
INV_FSR_4G = 1, |
|
INV_FSR_8G = 2, |
|
INV_FSR_16G = 4, |
|
NUM_ACCEL_FSR |
|
}; |
|
|
|
/* Clock sources. */ |
|
enum clock_sel_e { |
|
INV_CLK_INTERNAL = 0, |
|
INV_CLK_PLL, |
|
NUM_CLK |
|
}; |
|
|
|
#define MPU9150_ADDRESS 0x68 |
|
|
|
#define INV_X_GYRO (0x40) |
|
#define INV_Y_GYRO (0x20) |
|
#define INV_Z_GYRO (0x10) |
|
#define INV_XYZ_GYRO (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO) |
|
#define INV_XYZ_ACCEL (0x08) |
|
#define INV_XYZ_COMPASS (0x01) |
|
|
|
#define MPU_INT_STATUS_DATA_READY (0x0001) |
|
#define MPU_INT_STATUS_DMP (0x0002) |
|
#define MPU_INT_STATUS_PLL_READY (0x0004) |
|
#define MPU_INT_STATUS_I2C_MST (0x0008) |
|
#define MPU_INT_STATUS_FIFO_OVERFLOW (0x0010) |
|
#define MPU_INT_STATUS_ZMOT (0x0020) |
|
#define MPU_INT_STATUS_MOT (0x0040) |
|
#define MPU_INT_STATUS_FREE_FALL (0x0080) |
|
#define MPU_INT_STATUS_DMP_0 (0x0100) |
|
#define MPU_INT_STATUS_DMP_1 (0x0200) |
|
#define MPU_INT_STATUS_DMP_2 (0x0400) |
|
#define MPU_INT_STATUS_DMP_3 (0x0800) |
|
#define MPU_INT_STATUS_DMP_4 (0x1000) |
|
#define MPU_INT_STATUS_DMP_5 (0x2000) |
|
|
|
#define BIT_I2C_MST_VDDIO (0x80) |
|
#define BIT_FIFO_EN (0x40) |
|
#define BIT_DMP_EN (0x80) |
|
#define BIT_FIFO_RST (0x04) |
|
#define BIT_DMP_RST (0x08) |
|
#define BIT_FIFO_OVERFLOW (0x10) |
|
#define BIT_DATA_RDY_EN (0x01) |
|
#define BIT_DMP_INT_EN (0x02) |
|
#define BIT_MOT_INT_EN (0x40) |
|
#define BITS_FSR (0x18) |
|
#define BITS_LPF (0x07) |
|
#define BITS_HPF (0x07) |
|
#define BITS_CLK (0x07) |
|
#define BIT_FIFO_SIZE_1024 (0x40) |
|
#define BIT_FIFO_SIZE_2048 (0x80) |
|
#define BIT_FIFO_SIZE_4096 (0xC0) |
|
#define BIT_RESET (0x80) |
|
#define BIT_SLEEP (0x40) |
|
#define BIT_S0_DELAY_EN (0x01) |
|
#define BIT_S2_DELAY_EN (0x04) |
|
#define BITS_SLAVE_LENGTH (0x0F) |
|
#define BIT_SLAVE_BYTE_SW (0x40) |
|
#define BIT_SLAVE_GROUP (0x10) |
|
#define BIT_SLAVE_EN (0x80) |
|
#define BIT_I2C_READ (0x80) |
|
#define BITS_I2C_MASTER_DLY (0x1F) |
|
#define BIT_AUX_IF_EN (0x20) |
|
#define BIT_ACTL (0x80) |
|
#define BIT_LATCH_EN (0x20) |
|
#define BIT_ANY_RD_CLR (0x10) |
|
#define BIT_BYPASS_EN (0x02) |
|
#define BITS_WOM_EN (0xC0) |
|
#define BIT_LPA_CYCLE (0x20) |
|
#define BIT_STBY_XA (0x20) |
|
#define BIT_STBY_YA (0x10) |
|
#define BIT_STBY_ZA (0x08) |
|
#define BIT_STBY_XG (0x04) |
|
#define BIT_STBY_YG (0x02) |
|
#define BIT_STBY_ZG (0x01) |
|
#define BIT_STBY_XYZA (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA) |
|
#define BIT_STBY_XYZG (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) |
|
// AK8975_SECONDARY |
|
#define SUPPORTS_AK89xx_HIGH_SENS (0x00) |
|
#define AK89xx_FSR (9830) |
|
#define MAX_COMPASS_SAMPLE_RATE (100) |
|
|
|
// Gyroscope scale (uncertain where the 0.01745 value comes from) |
|
#define MPU9150_GYRO_SCALE_2000 (0.0174532f / 16.4f) |
|
#define MPU9150_GYRO_SCALE_1000 (0.0174532f / 32.8f) |
|
#define MPU9150_GYRO_SCALE_500 (0.0174532f / 65.5f) |
|
#define MPU9150_GYRO_SCALE_250 (0.0174532f / 131f) |
|
|
|
// Accelerometer scale adjustment |
|
#define MPU9150_ACCEL_SCALE_16G (GRAVITY_MSS / 2048.0f) |
|
#define MPU9150_ACCEL_SCALE_8G (GRAVITY_MSS / 4096.0f) |
|
#define MPU9150_ACCEL_SCALE_4G (GRAVITY_MSS / 8192.0f) |
|
#define MPU9150_ACCEL_SCALE_2G (GRAVITY_MSS / 16384.0f) |
|
|
|
|
|
const struct gyro_reg_s reg = { |
|
/* .who_am_i */ 0x75, |
|
/* .rate_div */ 0x19, |
|
/* .lpf */ 0x1A, |
|
/* .prod_id */ 0x0C, |
|
/* .user_ctrl */ 0x6A, |
|
/* .fifo_en */ 0x23, |
|
/* .gyro_cfg */ 0x1B, |
|
/* .accel_cfg */ 0x1C, |
|
/* .motion_thr */ 0x1F, |
|
/* .motion_dur */ 0x20, |
|
/* .fifo_count_h */ 0x72, |
|
/* .fifo_r_w */ 0x74, |
|
/* .raw_gyro */ 0x43, |
|
/* .raw_accel */ 0x3B, |
|
/* .temp */ 0x41, |
|
/* .int_enable */ 0x38, |
|
/* .dmp_int_status*/ 0x39, |
|
/* .int_status */ 0x3A, |
|
/* .pwr_mgmt_1 */ 0x6B, |
|
/* .pwr_mgmt_2 */ 0x6C, |
|
/* .int_pin_cfg */ 0x37, |
|
/* .mem_r_w */ 0x6F, |
|
/* .accel_offs */ 0x06, |
|
/* .i2c_mst */ 0x24, |
|
/* .bank_sel */ 0x6D, |
|
/* .mem_start_addr*/ 0x6E, |
|
/* .prgm_start_h */ 0x70, |
|
/* .raw_compass */ 0x49, |
|
/* .yg_offs_tc */ 0x01, |
|
/* .s0_addr */ 0x25, |
|
/* .s0_reg */ 0x26, |
|
/* .s0_ctrl */ 0x27, |
|
/* .s1_addr */ 0x28, |
|
/* .s1_reg */ 0x29, |
|
/* .s1_ctrl */ 0x2A, |
|
/* .s4_ctrl */ 0x34, |
|
/* .s0_do */ 0x63, |
|
/* .s1_do */ 0x64, |
|
/* .i2c_delay_ctrl*/ 0x67 |
|
}; |
|
|
|
const struct hw_s hw = { |
|
/* .addr */ 0x68, |
|
/* .max_fifo */ 1024, |
|
/* .num_reg */ 118, |
|
/* .temp_sens */ 340, |
|
/* .temp_offset */ -521, |
|
/* .bank_size */ 256, |
|
/* .compass_fsr */ AK89xx_FSR |
|
|
|
}; |
|
|
|
const struct test_s test = { |
|
/* .gyro_sens */ 32768/250, |
|
/* .accel_sens */ 32768/16, |
|
/* .reg_rate_div */ 0, /* 1kHz. */ |
|
/* .reg_lpf */ 1, /* 188Hz. */ |
|
/* .reg_gyro_fsr */ 0, /* 250dps. */ |
|
/* .reg_accel_fsr */ 0x18, /* 16g. */ |
|
/* .wait_ms */ 50, |
|
/* .packet_thresh */ 5, /* 5% */ |
|
/* .min_dps */ 10.f, |
|
/* .max_dps */ 105.f, |
|
/* .max_gyro_var */ 0.14f, |
|
/* .min_g */ 0.3f, |
|
/* .max_g */ 0.95f, |
|
/* .max_accel_var */ 0.14f |
|
}; |
|
|
|
static struct gyro_state_s st = { |
|
/* .reg */ ®, |
|
/* .hw */ &hw, |
|
/* .test */ &test |
|
|
|
}; |
|
|
|
/** |
|
* @brief Constructor |
|
*/ |
|
AP_InertialSensor_MPU9150::AP_InertialSensor_MPU9150(AP_InertialSensor &imu) : |
|
AP_InertialSensor_Backend(imu), |
|
_have_sample_available(false), |
|
_accel_filter(800, 10), |
|
_gyro_filter(800, 10) |
|
{ |
|
} |
|
|
|
|
|
/* |
|
detect the sensor |
|
*/ |
|
AP_InertialSensor_Backend *AP_InertialSensor_MPU9150::detect(AP_InertialSensor &_imu) |
|
{ |
|
AP_InertialSensor_MPU9150 *sensor = new AP_InertialSensor_MPU9150(_imu); |
|
if (sensor == NULL) { |
|
return NULL; |
|
} |
|
if (!sensor->_init_sensor()) { |
|
delete sensor; |
|
return NULL; |
|
} |
|
return sensor; |
|
} |
|
|
|
/* |
|
set the accel filter frequency |
|
*/ |
|
void AP_InertialSensor_MPU9150::_set_accel_filter_frequency(uint8_t filter_hz) |
|
{ |
|
_accel_filter.set_cutoff_frequency(800, filter_hz); |
|
} |
|
|
|
/* |
|
set the gyro filter frequency |
|
*/ |
|
void AP_InertialSensor_MPU9150::_set_gyro_filter_frequency(uint8_t filter_hz) |
|
{ |
|
_gyro_filter.set_cutoff_frequency(800, filter_hz); |
|
} |
|
|
|
/** |
|
* Init method |
|
*/ |
|
bool AP_InertialSensor_MPU9150::_init_sensor(void) |
|
{ |
|
// Sensors pushed to the FIFO. |
|
uint8_t sensors; |
|
|
|
// get pointer to i2c bus semaphore |
|
AP_HAL::Semaphore* i2c_sem = hal.i2c->get_semaphore(); |
|
|
|
// take i2c bus sempahore |
|
if (!i2c_sem->take(HAL_SEMAPHORE_BLOCK_FOREVER)){ |
|
return false; |
|
} |
|
|
|
// Init the sensor |
|
// Reset the device |
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->pwr_mgmt_1, |
|
BIT_RESET); |
|
hal.scheduler->delay(100); |
|
|
|
// Wake up the chip |
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->pwr_mgmt_1, |
|
0x00); |
|
|
|
// Check product revision |
|
// This registers are not documented in the register map. |
|
uint8_t buff[6]; |
|
if (hal.i2c->readRegisters(st.hw->addr, st.reg->accel_offs, 6, buff) != 0) { |
|
hal.console->printf("AP_InertialSensor_MPU9150: couldn't read the registers to determine revision"); |
|
goto failed; |
|
} |
|
// uint8_t rev; |
|
// rev = ((buff[5] & 0x01) << 2) | ((buff[3] & 0x01) << 1) | |
|
// (buff[1] & 0x01); |
|
|
|
// Do not do the checking, for some reason the MPU-9150 returns 0 |
|
// if (rev) { |
|
// if (rev == 1){ |
|
// /* Congrats, these parts are better. */ |
|
// ; |
|
// } |
|
// else if (rev == 2){ |
|
// ; |
|
// } |
|
// else { |
|
// hal.scheduler->panic(PSTR("AP_InertialSensor_MPU9150: Unsupported software product rev.\n")); |
|
// goto failed; |
|
// } |
|
// } else { |
|
// hal.scheduler->panic(PSTR("Product ID read as 0 indicates device is either incompatible or an MPU3050.\n")); |
|
// goto failed; |
|
// } |
|
|
|
// Set gyro full-scale range [250, 500, 1000, 2000] |
|
if (mpu_set_gyro_fsr(2000)){ |
|
hal.console->printf("AP_InertialSensor_MPU9150: mpu_set_gyro_fsr.\n"); |
|
goto failed; |
|
} |
|
// Set the accel full-scale range |
|
if (mpu_set_accel_fsr(2)){ |
|
hal.console->printf("AP_InertialSensor_MPU9150: mpu_set_accel_fsr.\n"); |
|
goto failed; |
|
} |
|
// Set digital low pass filter to 256Hz (DLPF disabled) |
|
if (mpu_set_lpf(256)) { |
|
hal.console->printf("AP_InertialSensor_MPU9150: mpu_set_lpf.\n"); |
|
goto failed; |
|
} |
|
// Set sampling rate (value must be between 4Hz and 1KHz) |
|
if (mpu_set_sample_rate(800)){ |
|
hal.console->printf("AP_InertialSensor_MPU9150: mpu_set_sample_rate.\n"); |
|
goto failed; |
|
} |
|
// Select which sensors are pushed to FIFO. |
|
sensors = INV_XYZ_ACCEL| INV_XYZ_GYRO; |
|
if (mpu_configure_fifo(sensors)){ |
|
hal.console->printf("AP_InertialSensor_MPU9150: mpu_configure_fifo.\n"); |
|
goto failed; |
|
} |
|
|
|
// For now the compass is not used. |
|
// TODO adjust the functions to the ArduPilot API |
|
|
|
// setup_compass(); |
|
// if (mpu_set_compass_sample_rate(10, 400)) |
|
// return -1; |
|
|
|
mpu_set_sensors(sensors); |
|
|
|
// Set the filter frecuency |
|
_set_accel_filter_frequency(_accel_filter_cutoff()); |
|
_set_gyro_filter_frequency(_gyro_filter_cutoff()); |
|
|
|
// give back i2c semaphore |
|
i2c_sem->give(); |
|
|
|
_gyro_instance = _imu.register_gyro(); |
|
_accel_instance = _imu.register_accel(); |
|
|
|
// start the timer process to read samples |
|
hal.scheduler->register_timer_process(FUNCTOR_BIND_MEMBER(&AP_InertialSensor_MPU9150::_accumulate, void)); |
|
|
|
return true; |
|
|
|
failed: |
|
// give back i2c semaphore |
|
i2c_sem->give(); |
|
return false; |
|
} |
|
|
|
/** |
|
* @brief Set the gyro full-scale range. |
|
* @param[in] fsr Desired full-scale range. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_set_gyro_fsr(uint16_t fsr) |
|
{ |
|
uint8_t data; |
|
|
|
switch (fsr) { |
|
case 250: |
|
data = INV_FSR_250DPS << 3; |
|
break; |
|
case 500: |
|
data = INV_FSR_500DPS << 3; |
|
break; |
|
case 1000: |
|
data = INV_FSR_1000DPS << 3; |
|
break; |
|
case 2000: |
|
data = INV_FSR_2000DPS << 3; |
|
break; |
|
default: |
|
return -1; |
|
} |
|
|
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->gyro_cfg, |
|
data); |
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Set the accel full-scale range. |
|
* @param[in] fsr Desired full-scale range. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_set_accel_fsr(uint8_t fsr) |
|
{ |
|
uint8_t data; |
|
|
|
switch (fsr) { |
|
case 2: |
|
data = INV_FSR_2G << 3; |
|
break; |
|
case 4: |
|
data = INV_FSR_4G << 3; |
|
break; |
|
case 8: |
|
data = INV_FSR_8G << 3; |
|
break; |
|
case 16: |
|
data = INV_FSR_16G << 3; |
|
break; |
|
default: |
|
return -1; |
|
} |
|
|
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->accel_cfg, |
|
data); |
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Set digital low pass filter. |
|
* The following LPF settings are supported: 188, 98, 42, 20, 10, 5. |
|
* @param[in] lpf Desired LPF setting. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_set_lpf(uint16_t lpf) |
|
{ |
|
uint8_t data; |
|
|
|
if (lpf == 0) { |
|
data = INV_FILTER_256HZ_NOLPF2; |
|
} else if (lpf >= 256){ |
|
data = INV_FILTER_256HZ_NOLPF2; |
|
} else if (lpf >= 188){ |
|
data = INV_FILTER_188HZ; |
|
} |
|
else if (lpf >= 98){ |
|
data = INV_FILTER_98HZ; |
|
} |
|
else if (lpf >= 42){ |
|
data = INV_FILTER_42HZ; |
|
} |
|
else if (lpf >= 20){ |
|
data = INV_FILTER_20HZ; |
|
} |
|
else if (lpf >= 10){ |
|
data = INV_FILTER_10HZ; |
|
} |
|
else { |
|
data = INV_FILTER_5HZ; |
|
} |
|
|
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->lpf, |
|
data); |
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Set sampling rate. |
|
* Sampling rate must be between 4Hz and 1kHz. |
|
* @param[in] rate Desired sampling rate (Hz). |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_set_sample_rate(uint16_t rate) |
|
{ |
|
uint8_t data; |
|
// uint16_t sample_rate; |
|
|
|
if (rate < 4){ |
|
rate = 4; |
|
} |
|
else if (rate > 1000){ |
|
rate = 1000; |
|
} |
|
|
|
data = 1000 / rate - 1; |
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->rate_div, |
|
data); |
|
|
|
// sample_rate = 1000 / (1 + data); |
|
// mpu_set_compass_sample_rate(min(sample_rate, MAX_COMPASS_SAMPLE_RATE), rate); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Set compass sampling rate. |
|
* The compass on the auxiliary I2C bus is read by the MPU hardware at a |
|
* maximum of 100Hz. The actual rate can be set to a fraction of the gyro |
|
* sampling rate. |
|
* |
|
* \n WARNING: The new rate may be different than what was requested. Call |
|
* mpu_get_compass_sample_rate to check the actual setting. |
|
* @param[in] rate Desired compass sampling rate (Hz). |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_set_compass_sample_rate(uint16_t rate, uint16_t chip_sample_rate) |
|
{ |
|
uint8_t div; |
|
if (!rate || rate > MAX_COMPASS_SAMPLE_RATE){ |
|
return -1; |
|
} |
|
|
|
div = chip_sample_rate / rate - 1; |
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->s4_ctrl, |
|
div); |
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Select which sensors are pushed to FIFO. |
|
* @e sensors can contain a combination of the following flags: |
|
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO |
|
* \n INV_XYZ_GYRO |
|
* \n INV_XYZ_ACCEL |
|
* @param[in] sensors Mask of sensors to push to FIFO. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_configure_fifo(uint8_t sensors) |
|
{ |
|
int16_t result = 0; |
|
|
|
/* Compass data isn't going into the FIFO. Stop trying. */ |
|
sensors &= ~INV_XYZ_COMPASS; |
|
|
|
// Enable or disable the interrupts |
|
// set_int_enable(1); |
|
set_int_enable(0); |
|
if (sensors) { |
|
if (mpu_reset_fifo(sensors)) { |
|
return -1; |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
/** |
|
* @brief Enable/disable data ready interrupt. |
|
* If the DMP is on, the DMP interrupt is enabled. Otherwise, the data ready |
|
* interrupt is used. |
|
* @param[in] enable 1 to enable interrupt. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::set_int_enable(uint8_t enable) |
|
{ |
|
uint8_t tmp; |
|
|
|
if (enable){ |
|
tmp = BIT_DATA_RDY_EN; |
|
} |
|
else { |
|
tmp = 0x00; |
|
} |
|
hal.i2c->writeRegister(st.hw->addr, |
|
st.reg->int_enable, |
|
tmp); |
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Reset FIFO read/write pointers. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_reset_fifo(uint8_t sensors) |
|
{ |
|
uint8_t data; |
|
|
|
data = 0; |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->int_enable, data); |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->fifo_en, data); |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->user_ctrl, data); |
|
data = BIT_FIFO_RST; |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->user_ctrl, data); |
|
|
|
data = BIT_FIFO_EN; |
|
// data = BIT_FIFO_EN | BIT_AUX_IF_EN; |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->user_ctrl, data); |
|
hal.scheduler->delay(50); |
|
|
|
// interrupts for the DMP |
|
// data = BIT_DATA_RDY_EN; |
|
data = 0; |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->int_enable, data); |
|
// enable FIFO |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->fifo_en, sensors); |
|
return 0; |
|
} |
|
|
|
|
|
#if 0 |
|
/* This initialization is similar to the one in ak8975.c. |
|
TODO: Use the ArduPilot APIs (write, read, ...), remove the st.chip_cfg cache vars |
|
*/ |
|
static int AP_InertialSensor_MPU9150::setup_compass(void) |
|
{ |
|
uint8_t data[4], akm_addr; |
|
|
|
mpu_set_bypass(1); |
|
|
|
/* Find compass. Possible addresses range from 0x0C to 0x0F. */ |
|
for (akm_addr = 0x0C; akm_addr <= 0x0F; akm_addr++) { |
|
int result; |
|
result = i2c_read(akm_addr, AKM_REG_WHOAMI, 1, data); |
|
if (!result && (data[0] == AKM_WHOAMI)) |
|
break; |
|
} |
|
|
|
if (akm_addr > 0x0F) { |
|
/* TODO: Handle this case in all compass-related functions. */ |
|
log_e("Compass not found.\n"); |
|
return -1; |
|
} |
|
|
|
st.chip_cfg.compass_addr = akm_addr; |
|
|
|
data[0] = AKM_POWER_DOWN; |
|
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, data)) |
|
return -1; |
|
delay_ms(1); |
|
|
|
data[0] = AKM_FUSE_ROM_ACCESS; |
|
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, data)) |
|
return -1; |
|
delay_ms(1); |
|
|
|
/* Get sensitivity adjustment data from fuse ROM. */ |
|
if (i2c_read(st.chip_cfg.compass_addr, AKM_REG_ASAX, 3, data)) |
|
return -1; |
|
st.chip_cfg.mag_sens_adj[0] = (long)data[0] + 128; |
|
st.chip_cfg.mag_sens_adj[1] = (long)data[1] + 128; |
|
st.chip_cfg.mag_sens_adj[2] = (long)data[2] + 128; |
|
|
|
data[0] = AKM_POWER_DOWN; |
|
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, data)) |
|
return -1; |
|
delay_ms(1); |
|
|
|
mpu_set_bypass(0); |
|
|
|
/* Set up master mode, master clock, and ES bit. */ |
|
data[0] = 0x40; |
|
if (i2c_write(st.hw->addr, st.reg->i2c_mst, 1, data)) |
|
return -1; |
|
|
|
/* Slave 0 reads from AKM data registers. */ |
|
data[0] = BIT_I2C_READ | st.chip_cfg.compass_addr; |
|
if (i2c_write(st.hw->addr, st.reg->s0_addr, 1, data)) |
|
return -1; |
|
|
|
/* Compass reads start at this register. */ |
|
data[0] = AKM_REG_ST1; |
|
if (i2c_write(st.hw->addr, st.reg->s0_reg, 1, data)) |
|
return -1; |
|
|
|
/* Enable slave 0, 8-byte reads. */ |
|
data[0] = BIT_SLAVE_EN | 8; |
|
if (i2c_write(st.hw->addr, st.reg->s0_ctrl, 1, data)) |
|
return -1; |
|
|
|
/* Slave 1 changes AKM measurement mode. */ |
|
data[0] = st.chip_cfg.compass_addr; |
|
if (i2c_write(st.hw->addr, st.reg->s1_addr, 1, data)) |
|
return -1; |
|
|
|
/* AKM measurement mode register. */ |
|
data[0] = AKM_REG_CNTL; |
|
if (i2c_write(st.hw->addr, st.reg->s1_reg, 1, data)) |
|
return -1; |
|
|
|
/* Enable slave 1, 1-byte writes. */ |
|
data[0] = BIT_SLAVE_EN | 1; |
|
if (i2c_write(st.hw->addr, st.reg->s1_ctrl, 1, data)) |
|
return -1; |
|
|
|
/* Set slave 1 data. */ |
|
data[0] = AKM_SINGLE_MEASUREMENT; |
|
if (i2c_write(st.hw->addr, st.reg->s1_do, 1, data)) |
|
return -1; |
|
|
|
/* Trigger slave 0 and slave 1 actions at each sample. */ |
|
data[0] = 0x03; |
|
if (i2c_write(st.hw->addr, st.reg->i2c_delay_ctrl, 1, data)) |
|
return -1; |
|
|
|
/* For the MPU9150, the auxiliary I2C bus needs to be set to VDD. */ |
|
data[0] = BIT_I2C_MST_VDDIO; |
|
if (i2c_write(st.hw->addr, st.reg->yg_offs_tc, 1, data)) |
|
return -1; |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
/** |
|
* @brief Turn specific sensors on/off. |
|
* @e sensors can contain a combination of the following flags: |
|
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO |
|
* \n INV_XYZ_GYRO |
|
* \n INV_XYZ_ACCEL |
|
* \n INV_XYZ_COMPASS |
|
* @param[in] sensors Mask of sensors to wake. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_set_sensors(uint8_t sensors) |
|
{ |
|
uint8_t data; |
|
// uint8_t user_ctrl; |
|
|
|
if (sensors & INV_XYZ_GYRO){ |
|
data = INV_CLK_PLL; |
|
} |
|
else if (sensors){ |
|
data = 0; |
|
} |
|
else { |
|
data = BIT_SLEEP; |
|
} |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->pwr_mgmt_1, data); |
|
data = 0; |
|
if (!(sensors & INV_X_GYRO)){ |
|
data |= BIT_STBY_XG; |
|
} |
|
if (!(sensors & INV_Y_GYRO)){ |
|
data |= BIT_STBY_YG; |
|
} |
|
if (!(sensors & INV_Z_GYRO)){ |
|
data |= BIT_STBY_ZG; |
|
} |
|
if (!(sensors & INV_XYZ_ACCEL)){ |
|
data |= BIT_STBY_XYZA; |
|
} |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->pwr_mgmt_2, data); |
|
|
|
#if 0 |
|
if (sensors && (sensors != INV_XYZ_ACCEL)){ |
|
/* Latched interrupts only used in LP accel mode. */ |
|
mpu_set_int_latched(0); |
|
} |
|
#endif |
|
|
|
// // handle the compass, not implemented for now |
|
// #ifdef AK89xx_SECONDARY |
|
// #ifdef AK89xx_BYPASS |
|
// if (sensors & INV_XYZ_COMPASS) |
|
// mpu_set_bypass(1); |
|
// else |
|
// mpu_set_bypass(0); |
|
// #else |
|
// if (i2c_read(st.hw->addr, st.reg->user_ctrl, 1, &user_ctrl)) |
|
// return -1; |
|
// /* Handle AKM power management. */ |
|
// if (sensors & INV_XYZ_COMPASS) { |
|
// data = AKM_SINGLE_MEASUREMENT; |
|
// user_ctrl |= BIT_AUX_IF_EN; |
|
// } else { |
|
// data = AKM_POWER_DOWN; |
|
// user_ctrl &= ~BIT_AUX_IF_EN; |
|
// } |
|
// if (st.chip_cfg.dmp_on) |
|
// user_ctrl |= BIT_DMP_EN; |
|
// else |
|
// user_ctrl &= ~BIT_DMP_EN; |
|
// if (i2c_write(st.hw->addr, st.reg->s1_do, 1, &data)) |
|
// return -1; |
|
// /* Enable/disable I2C master mode. */ |
|
// if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &user_ctrl)) |
|
// return -1; |
|
// #endif |
|
|
|
hal.scheduler->delay(50); |
|
return 0; |
|
} |
|
|
|
#if 0 |
|
/** |
|
* TODO: Remove the st.chip_cfg cache variables |
|
* |
|
* @brief Enable latched interrupts. |
|
* Any MPU register will clear the interrupt. |
|
* @param[in] enable 1 to enable, 0 to disable. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_set_int_latched(uint8_t enable) |
|
{ |
|
uint8_t tmp; |
|
if (st.chip_cfg.latched_int == enable){ |
|
return 0; |
|
} |
|
|
|
if (enable){ |
|
tmp = BIT_LATCH_EN | BIT_ANY_RD_CLR; |
|
} |
|
else { |
|
tmp = 0; |
|
} |
|
if (st.chip_cfg.bypass_mode){ |
|
tmp |= BIT_BYPASS_EN; |
|
} |
|
if (st.chip_cfg.active_low_int){ |
|
tmp |= BIT_ACTL; |
|
} |
|
hal.i2c->writeRegister(st.hw->addr,st.reg->int_pin_cfg, tmp); |
|
st.chip_cfg.latched_int = enable; |
|
return 0; |
|
} |
|
#endif |
|
|
|
|
|
|
|
/** |
|
* @brief Get one packet from the FIFO. |
|
* If @e sensors does not contain a particular sensor, disregard the data |
|
* returned to that pointer. |
|
* \n @e sensors can contain a combination of the following flags: |
|
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO |
|
* \n INV_XYZ_GYRO |
|
* \n INV_XYZ_ACCEL |
|
* \n If the FIFO has no new data, @e sensors will be zero. |
|
* \n If the FIFO is disabled, @e sensors will be zero and this function will |
|
* return a non-zero error code. |
|
* @param[out] gyro Gyro data in hardware units. |
|
* @param[out] accel Accel data in hardware units. |
|
* @param[out] timestamp Timestamp in milliseconds. |
|
* @param[out] sensors Mask of sensors read from FIFO. |
|
* @param[out] more Number of remaining packets. |
|
* @return 0 if successful. |
|
*/ |
|
int16_t AP_InertialSensor_MPU9150::mpu_read_fifo(int16_t *gyro, int16_t *accel, uint32_t *timestamp, |
|
uint8_t *sensors, uint8_t *more) |
|
{ |
|
/* Assumes maximum packet size is gyro (6) + accel (6). */ |
|
uint8_t data[12]; |
|
uint8_t packet_size = 0; |
|
uint16_t fifo_count, index = 0; |
|
uint8_t sensors_aux = INV_XYZ_ACCEL| INV_XYZ_GYRO; |
|
|
|
sensors[0] = 0; |
|
// We assume we want gyro and accel values |
|
packet_size = 12; |
|
|
|
// fifo_count_h register contains the number of samples in the FIFO |
|
hal.i2c->readRegisters(st.hw->addr, st.reg->fifo_count_h, 2, data); |
|
|
|
fifo_count = (data[0] << 8) | data[1]; |
|
if (fifo_count < packet_size){ |
|
return 0; |
|
} |
|
// hal.console->printf_P(PTR("FIFO count: %hd\n", fifo_count)); |
|
if (fifo_count > (st.hw->max_fifo >> 1)) { |
|
/* FIFO is 50% full, better check overflow bit. */ |
|
hal.i2c->readRegister(st.hw->addr, st.reg->int_status, data); |
|
if (data[0] & BIT_FIFO_OVERFLOW) { |
|
mpu_reset_fifo(sensors_aux); |
|
return -2; |
|
} |
|
} |
|
|
|
*timestamp = hal.scheduler->millis(); |
|
// read the data |
|
hal.i2c->readRegisters(st.hw->addr, st.reg->fifo_r_w, packet_size, data); |
|
more[0] = fifo_count / packet_size - 1; |
|
sensors[0] = 0; |
|
|
|
if (index != packet_size) { |
|
accel[0] = (data[index+0] << 8) | data[index+1]; |
|
accel[1] = (data[index+2] << 8) | data[index+3]; |
|
accel[2] = (data[index+4] << 8) | data[index+5]; |
|
sensors[0] |= INV_XYZ_ACCEL; |
|
index += 6; |
|
} |
|
if (index != packet_size) { |
|
gyro[0] = (data[index+0] << 8) | data[index+1]; |
|
sensors[0] |= INV_X_GYRO; |
|
index += 2; |
|
} |
|
if (index != packet_size) { |
|
gyro[1] = (data[index+0] << 8) | data[index+1]; |
|
sensors[0] |= INV_Y_GYRO; |
|
index += 2; |
|
} |
|
if (index != packet_size) { |
|
gyro[2] = (data[index+0] << 8) | data[index+1]; |
|
sensors[0] |= INV_Z_GYRO; |
|
index += 2; |
|
} |
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Accumulate values from accels and gyros. |
|
* |
|
* This method is called periodically by the scheduler. |
|
*/ |
|
void AP_InertialSensor_MPU9150::_accumulate(void) |
|
{ |
|
// get pointer to i2c bus semaphore |
|
AP_HAL::Semaphore* i2c_sem = hal.i2c->get_semaphore(); |
|
|
|
// take i2c bus sempahore |
|
if (!i2c_sem->take_nonblocking()){ |
|
return; |
|
} |
|
|
|
// Read accelerometer FIFO to find out how many samples are available |
|
/* Assumes maximum packet size is gyro (6) + accel (6). */ |
|
uint8_t data[12]; |
|
uint8_t packet_size = 12; |
|
uint16_t fifo_count, index = 0; |
|
int16_t accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z; |
|
|
|
// fifo_count_h register contains the number of samples in the FIFO |
|
hal.i2c->readRegisters(st.hw->addr, st.reg->fifo_count_h, 2, data); |
|
fifo_count = (data[0] << 8) | data[1]; |
|
if (fifo_count < packet_size){ |
|
// give back i2c semaphore |
|
i2c_sem->give(); |
|
return; |
|
} |
|
|
|
// hal.console->printf_P(PTR("FIFO count: %hd\n", fifo_count)); |
|
if (fifo_count > (st.hw->max_fifo >> 1)) { |
|
/* FIFO is 50% full, better check overflow bit. */ |
|
hal.i2c->readRegister(st.hw->addr, st.reg->int_status, data); |
|
if (data[0] & BIT_FIFO_OVERFLOW) { |
|
mpu_reset_fifo(INV_XYZ_ACCEL| INV_XYZ_GYRO); |
|
i2c_sem->give(); |
|
return; |
|
} |
|
} |
|
|
|
// read the samples |
|
for (uint16_t i=0; i< fifo_count; i++) { |
|
// read the data |
|
// TODO check whether it's possible to read all the packages in a single call |
|
hal.i2c->readRegisters(st.hw->addr, st.reg->fifo_r_w, packet_size, data); |
|
// TODO, remove all the checking since it's being configured this way. |
|
if (index != packet_size) { |
|
accel_x = (int16_t) (data[index+0] << 8) | data[index+1]; |
|
accel_y = (int16_t) (data[index+2] << 8) | data[index+3]; |
|
accel_z = (int16_t) (data[index+4] << 8) | data[index+5]; |
|
index += 6; |
|
} |
|
if (index != packet_size) { |
|
gyro_x = (int16_t) (data[index+0] << 8) | data[index+1]; |
|
index += 2; |
|
} |
|
if (index != packet_size) { |
|
gyro_y = (int16_t) (data[index+0] << 8) | data[index+1]; |
|
index += 2; |
|
} |
|
if (index != packet_size) { |
|
gyro_z = (int16_t) (data[index+0] << 8) | data[index+1]; |
|
index += 2; |
|
} |
|
// reset the index |
|
index = 0; |
|
|
|
// TODO Revisit why AP_InertialSensor_L3G4200D uses a minus sign in the y and z component. Maybe this |
|
// is because the sensor is placed in the bottom side of the board? |
|
_accel_filtered = _accel_filter.apply(Vector3f(accel_x, accel_y, accel_z)); |
|
|
|
_gyro_filtered = _gyro_filter.apply(Vector3f(gyro_x, gyro_y, gyro_z)); |
|
|
|
_have_sample_available = true; |
|
} |
|
|
|
// give back i2c semaphore |
|
i2c_sem->give(); |
|
} |
|
|
|
bool AP_InertialSensor_MPU9150::update(void) |
|
{ |
|
Vector3f accel, gyro; |
|
|
|
hal.scheduler->suspend_timer_procs(); |
|
accel = _accel_filtered; |
|
gyro = _gyro_filtered; |
|
_have_sample_available = false; |
|
hal.scheduler->resume_timer_procs(); |
|
|
|
accel *= MPU9150_ACCEL_SCALE_2G; |
|
_publish_accel(_accel_instance, accel); |
|
|
|
gyro *= MPU9150_GYRO_SCALE_2000; |
|
_publish_gyro(_gyro_instance, gyro); |
|
|
|
if (_last_accel_filter_hz != _accel_filter_cutoff()) { |
|
_set_accel_filter_frequency(_accel_filter_cutoff()); |
|
_last_accel_filter_hz = _accel_filter_cutoff(); |
|
} |
|
|
|
if (_last_gyro_filter_hz != _gyro_filter_cutoff()) { |
|
_set_gyro_filter_frequency(_gyro_filter_cutoff()); |
|
_last_gyro_filter_hz = _gyro_filter_cutoff(); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
#endif // CONFIG_HAL_BOARD
|
|
|