/*
$License:
Copyright 2011 InvenSense, Inc.
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.
$
*/
/******************************************************************************
*
* $Id: ml.c 5642 2011-06-14 02:26:20Z mcaramello $
*
*****************************************************************************/
/**
* @defgroup ML
* @brief Motion Library APIs.
* The Motion Library processes gyroscopes, accelerometers, and
* compasses to provide a physical model of the movement for the
* sensors.
* The results of this processing may be used to control objects
* within a user interface environment, detect gestures, track 3D
* movement for gaming applications, and analyze the blur created
* due to hand movement while taking a picture.
*
* @{
* @file ml.c
* @brief The Motion Library APIs.
*/
/* ------------------ */
/* - Include Files. - */
/* ------------------ */
#include <string.h>
#include "ml.h"
#include "mldl.h"
#include "mltypes.h"
#include "mlinclude.h"
#include "compass.h"
#include "dmpKey.h"
#include "dmpDefault.h"
#include "mlstates.h"
#include "mlFIFO.h"
#include "mlFIFOHW.h"
#include "mlMathFunc.h"
#include "mlsupervisor.h"
#include "mlmath.h"
#include "mlcontrol.h"
#include "mldl_cfg.h"
#include "mpu.h"
#include "accel.h"
#include "mlos.h"
#include "mlsl.h"
#include "mlos.h"
#include "mlBiasNoMotion.h"
#include "log.h"
#undef MPL_LOG_TAG
#define MPL_LOG_TAG "MPL-ml"
#define ML_MOT_TYPE_NONE 0
#define ML_MOT_TYPE_NO_MOTION 1
#define ML_MOT_TYPE_MOTION_DETECTED 2
#define ML_MOT_STATE_MOVING 0
#define ML_MOT_STATE_NO_MOTION 1
#define ML_MOT_STATE_BIAS_IN_PROG 2
#define _mlDebug(x) //{x}
/* Global Variables */
const unsigned char mlVer[] = { INV_VERSION };
struct inv_params_obj inv_params_obj = {
INV_BIAS_UPDATE_FUNC_DEFAULT, // bias_mode
INV_ORIENTATION_MASK_DEFAULT, // orientation_mask
INV_PROCESSED_DATA_CALLBACK_DEFAULT, // fifo_processed_func
INV_ORIENTATION_CALLBACK_DEFAULT, // orientation_cb_func
INV_MOTION_CALLBACK_DEFAULT, // motion_cb_func
INV_STATE_SERIAL_CLOSED // starting state
};
struct inv_obj_t inv_obj;
void *g_mlsl_handle;
typedef struct {
// These describe callbacks happening everythime a DMP interrupt is processed
int_fast8_t numInterruptProcesses;
// Array of function pointers, function being void function taking void
inv_obj_func processInterruptCb[MAX_INTERRUPT_PROCESSES];
} tMLXCallbackInterrupt; // MLX_callback_t
tMLXCallbackInterrupt mlxCallbackInterrupt;
/* --------------- */
/* - Functions. - */
/* --------------- */
inv_error_t inv_freescale_sensor_fusion_16bit(unsigned short orient);
inv_error_t inv_freescale_sensor_fusion_8bit(unsigned short orient);
unsigned short inv_orientation_matrix_to_scalar(const signed char *mtx);
/**
* @brief Open serial connection with the MPU device.
* This is the entry point of the MPL and must be
* called prior to any other function call.
*
* @param port System handle for 'port' MPU device is found on.
* The significance of this parameter varies by
* platform. It is passed as 'port' to MLSLSerialOpen.
*
* @return INV_SUCCESS or error code.
*/
inv_error_t inv_serial_start(char const *port)
{
INVENSENSE_FUNC_START;
inv_error_t result;
if (inv_get_state() >= INV_STATE_SERIAL_OPENED)
return INV_SUCCESS;
result = inv_state_transition(INV_STATE_SERIAL_OPENED);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_serial_open(port, &g_mlsl_handle);
if (INV_SUCCESS != result) {
(void)inv_state_transition(INV_STATE_SERIAL_CLOSED);
}
return result;
}
/**
* @brief Close the serial communication.
* This function needs to be called explicitly to shut down
* the communication with the device. Calling inv_dmp_close()
* won't affect the established serial communication.
* @return INV_SUCCESS; non-zero error code otherwise.
*/
inv_error_t inv_serial_stop(void)
{
INVENSENSE_FUNC_START;
inv_error_t result = INV_SUCCESS;
if (inv_get_state() == INV_STATE_SERIAL_CLOSED)
return INV_SUCCESS;
result = inv_state_transition(INV_STATE_SERIAL_CLOSED);
if (INV_SUCCESS != result) {
MPL_LOGE("State Transition Failure in %s: %d\n", __func__, result);
}
result = inv_serial_close(g_mlsl_handle);
if (INV_SUCCESS != result) {
MPL_LOGE("Unable to close Serial Handle %s: %d\n", __func__, result);
}
return result;
}
/**
* @brief Get the serial file handle to the device.
* @return The serial file handle.
*/
void *inv_get_serial_handle(void)
{
INVENSENSE_FUNC_START;
return g_mlsl_handle;
}
/**
* @brief apply the choosen orientation and full scale range
* for gyroscopes, accelerometer, and compass.
* @return INV_SUCCESS if successful, a non-zero code otherwise.
*/
inv_error_t inv_apply_calibration(void)
{
INVENSENSE_FUNC_START;
signed char gyroCal[9] = { 0 };
signed char accelCal[9] = { 0 };
signed char magCal[9] = { 0 };
float gyroScale = 2000.f;
float accelScale = 0.f;
float magScale = 0.f;
inv_error_t result;
int ii;
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
for (ii = 0; ii < 9; ii++) {
gyroCal[ii] = mldl_cfg->pdata->orientation[ii];
accelCal[ii] = mldl_cfg->pdata->accel.orientation[ii];
magCal[ii] = mldl_cfg->pdata->compass.orientation[ii];
}
switch (mldl_cfg->full_scale) {
case MPU_FS_250DPS:
gyroScale = 250.f;
break;
case MPU_FS_500DPS:
gyroScale = 500.f;
break;
case MPU_FS_1000DPS:
gyroScale = 1000.f;
break;
case MPU_FS_2000DPS:
gyroScale = 2000.f;
break;
default:
MPL_LOGE("Unrecognized full scale setting for gyros : %02X\n",
mldl_cfg->full_scale);
return INV_ERROR_INVALID_PARAMETER;
break;
}
RANGE_FIXEDPOINT_TO_FLOAT(mldl_cfg->accel->range, accelScale);
inv_obj.accel_sens = (long)(accelScale * 65536L);
/* sensitivity adjustment, typically = 2 (for +/- 2 gee) */
#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
defined CONFIG_MPU_SENSORS_MPU6050B1
inv_obj.accel_sens /= 32768 / mldl_cfg->accel_sens_trim;
#else
inv_obj.accel_sens /= 2;
#endif
RANGE_FIXEDPOINT_TO_FLOAT(mldl_cfg->compass->range, magScale);
inv_obj.compass_sens = (long)(magScale * 32768);
if (inv_get_state() == INV_STATE_DMP_OPENED) {
result = inv_set_gyro_calibration(gyroScale, gyroCal);
if (INV_SUCCESS != result) {
MPL_LOGE("Unable to set Gyro Calibration\n");
return result;
}
result = inv_set_accel_calibration(accelScale, accelCal);
if (INV_SUCCESS != result) {
MPL_LOGE("Unable to set Accel Calibration\n");
return result;
}
result = inv_set_compass_calibration(magScale, magCal);
if (INV_SUCCESS != result) {
MPL_LOGE("Unable to set Mag Calibration\n");
return result;
}
}
return INV_SUCCESS;
}
/**
* @brief Setup the DMP to handle the accelerometer endianess.
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
*/
inv_error_t inv_apply_endian_accel(void)
{
INVENSENSE_FUNC_START;
unsigned char regs[4] = { 0 };
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
int endian = mldl_cfg->accel->endian;
if (mldl_cfg->pdata->accel.bus != EXT_SLAVE_BUS_SECONDARY) {
endian = EXT_SLAVE_BIG_ENDIAN;
}
switch (endian) {
case EXT_SLAVE_FS8_BIG_ENDIAN:
case EXT_SLAVE_FS16_BIG_ENDIAN:
case EXT_SLAVE_LITTLE_ENDIAN:
regs[0] = 0;
regs[1] = 64;
regs[2] = 0;
regs[3] = 0;
break;
case EXT_SLAVE_BIG_ENDIAN:
default:
regs[0] = 0;
regs[1] = 0;
regs[2] = 64;
regs[3] = 0;
}
return inv_set_mpu_memory(KEY_D_1_236, 4, regs);
}
/**
* @internal
* @brief Initialize MLX data. This should be called to setup the mlx
* output buffers before any motion processing is done.
*/
void inv_init_ml(void)
{
INVENSENSE_FUNC_START;
int ii;
long tmp[COMPASS_NUM_AXES];
// Set all values to zero by default
memset(&inv_obj, 0, sizeof(struct inv_obj_t));
memset(&mlxCallbackInterrupt, 0, sizeof(tMLXCallbackInterrupt));
inv_obj.compass_correction[0] = 1073741824L;
inv_obj.compass_correction_relative[0] = 1073741824L;
inv_obj.compass_disturb_correction[0] = 1073741824L;
inv_obj.compass_correction_offset[0] = 1073741824L;
inv_obj.relative_quat[0] = 1073741824L;
//Not used with the ST accelerometer
inv_obj.no_motion_threshold = 20; // noMotionThreshold
//Not used with the ST accelerometer
inv_obj.motion_duration = 1536; // motionDuration
inv_obj.motion_state = INV_MOTION; // Motion state
inv_obj.bias_update_time = 8000;
inv_obj.bias_calc_time = 2000;
inv_obj.internal_motion_state = ML_MOT_STATE_MOVING;
inv_obj.start_time = inv_get_tick_count();
inv_obj.compass_cal[0] = 322122560L;
inv_obj.compass_cal[4] = 322122560L;
inv_obj.compass_cal[8] = 322122560L;
inv_obj.compass_sens = 322122560L; // Should only change when the sensor full-scale range (FSR) is changed.
for (ii = 0; ii < COMPASS_NUM_AXES; ii++) {
inv_obj.compass_scale[ii] = 65536L;
inv_obj.compass_test_scale[ii] = 65536L;
inv_obj.compass_bias_error[ii] = P_INIT;
inv_obj.init_compass_bias[ii] = 0;
inv_obj.compass_asa[ii] = (1L << 30);
}
if (inv_compass_read_scale(tmp) == INV_SUCCESS) {
for (ii = 0; ii < COMPASS_NUM_AXES; ii++)
inv_obj.compass_asa[ii] = tmp[ii];
}
inv_obj.got_no_motion_bias = 0;
inv_obj.got_compass_bias = 0;
inv_obj.compass_state = SF_UNCALIBRATED;
inv_obj.acc_state = SF_STARTUP_SETTLE;
inv_obj.got_init_compass_bias = 0;
inv_obj.resetting_compass = 0;
inv_obj.external_slave_callback = NULL;
inv_obj.compass_accuracy = 0;
inv_obj.factory_temp_comp = 0;
inv_obj.got_coarse_heading = 0;
inv_obj.compass_bias_ptr[0] = P_INIT;
inv_obj.compass_bias_ptr[4] = P_INIT;
inv_obj.compass_bias_ptr[8] = P_INIT;
inv_obj.gyro_bias_err = 1310720;
inv_obj.accel_lpf_gain = 1073744L;
inv_obj.no_motion_accel_threshold = 7000000L;
}
/**
* @internal
* @brief This registers a function to be called for each time the DMP
* generates an an interrupt.
* It will be called after inv_register_fifo_rate_process() which gets called
* every time the FIFO data is processed.
* The FIFO does not have to be on for this callback.
* @param func Function to be called when a DMP interrupt occurs.
* @return INV_SUCCESS or non-zero error code.
*/
inv_error_t inv_register_dmp_interupt_cb(inv_obj_func func)
{
INVENSENSE_FUNC_START;
// Make sure we have not filled up our number of allowable callbacks
if (mlxCallbackInterrupt.numInterruptProcesses <=
MAX_INTERRUPT_PROCESSES - 1) {
int kk;
// Make sure we haven't registered this function already
for (kk = 0; kk < mlxCallbackInterrupt.numInterruptProcesses; ++kk) {
if (mlxCallbackInterrupt.processInterruptCb[kk] == func) {
return INV_ERROR_INVALID_PARAMETER;
}
}
// Add new callback
mlxCallbackInterrupt.processInterruptCb[mlxCallbackInterrupt.
numInterruptProcesses] = func;
mlxCallbackInterrupt.numInterruptProcesses++;
return INV_SUCCESS;
}
return INV_ERROR_MEMORY_EXAUSTED;
}
/**
* @internal
* @brief This unregisters a function to be called for each DMP interrupt.
* @return INV_SUCCESS or non-zero error code.
*/
inv_error_t inv_unregister_dmp_interupt_cb(inv_obj_func func)
{
INVENSENSE_FUNC_START;
int kk, jj;
// Make sure we haven't registered this function already
for (kk = 0; kk < mlxCallbackInterrupt.numInterruptProcesses; ++kk) {
if (mlxCallbackInterrupt.processInterruptCb[kk] == func) {
// FIXME, we may need a thread block here
for (jj = kk + 1; jj < mlxCallbackInterrupt.numInterruptProcesses;
++jj) {
mlxCallbackInterrupt.processInterruptCb[jj - 1] =
mlxCallbackInterrupt.processInterruptCb[jj];
}
mlxCallbackInterrupt.numInterruptProcesses--;
return INV_SUCCESS;
}
}
return INV_ERROR_INVALID_PARAMETER;
}
/**
* @internal
* @brief Run the recorded interrupt process callbacks in the event
* of an interrupt.
*/
void inv_run_dmp_interupt_cb(void)
{
int kk;
for (kk = 0; kk < mlxCallbackInterrupt.numInterruptProcesses; ++kk) {
if (mlxCallbackInterrupt.processInterruptCb[kk])
mlxCallbackInterrupt.processInterruptCb[kk] (&inv_obj);
}
}
/** @internal
* Resets the Motion/No Motion state which should be called at startup and resume.
*/
inv_error_t inv_reset_motion(void)
{
unsigned char regs[8];
inv_error_t result;
inv_obj.motion_state = INV_MOTION;
inv_obj.flags[INV_MOTION_STATE_CHANGE] = INV_MOTION;
inv_obj.no_motion_accel_time = inv_get_tick_count();
#if defined CONFIG_MPU_SENSORS_MPU3050
regs[0] = DINAD8 + 2;
regs[1] = DINA0C;
regs[2] = DINAD8 + 1;
result = inv_set_mpu_memory(KEY_CFG_18, 3, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
#else
#endif
regs[0] = (unsigned char)((inv_obj.motion_duration >> 8) & 0xff);
regs[1] = (unsigned char)(inv_obj.motion_duration & 0xff);
result = inv_set_mpu_memory(KEY_D_1_106, 2, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
memset(regs, 0, 8);
result = inv_set_mpu_memory(KEY_D_1_96, 8, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result =
inv_set_mpu_memory(KEY_D_0_96, 4, inv_int32_to_big8(0x40000000, regs));
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
inv_set_motion_state(INV_MOTION);
return result;
}
/**
* @internal
* @brief inv_start_bias_calc starts the bias calculation on the MPU.
*/
void inv_start_bias_calc(void)
{
INVENSENSE_FUNC_START;
inv_obj.suspend = 1;
}
/**
* @internal
* @brief inv_stop_bias_calc stops the bias calculation on the MPU.
*/
void inv_stop_bias_calc(void)
{
INVENSENSE_FUNC_START;
inv_obj.suspend = 0;
}
/**
* @brief inv_update_data fetches data from the fifo and updates the
* motion algorithms.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* and inv_dmp_start() must have been called.
*
* @note Motion algorithm data is constant between calls to inv_update_data
*
* @return
* - INV_SUCCESS
* - Non-zero error code
*/
inv_error_t inv_update_data(void)
{
INVENSENSE_FUNC_START;
inv_error_t result = INV_SUCCESS;
int_fast8_t got, ftry;
uint_fast8_t mpu_interrupt;
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
if (inv_get_state() != INV_STATE_DMP_STARTED)
return INV_ERROR_SM_IMPROPER_STATE;
// Set the maximum number of FIFO packets we want to process
if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) {
ftry = 100; // Large enough to process all packets
} else {
ftry = 1;
}
// Go and process at most ftry number of packets, probably less than ftry
result = inv_read_and_process_fifo(ftry, &got);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
// Process all interrupts
mpu_interrupt = inv_get_interrupt_trigger(INTSRC_AUX1);
if (mpu_interrupt) {
inv_clear_interrupt_trigger(INTSRC_AUX1);
}
// Check if interrupt was from MPU
mpu_interrupt = inv_get_interrupt_trigger(INTSRC_MPU);
if (mpu_interrupt) {
inv_clear_interrupt_trigger(INTSRC_MPU);
}
// Take care of the callbacks that want to be notified when there was an MPU interrupt
if (mpu_interrupt) {
inv_run_dmp_interupt_cb();
}
result = inv_get_fifo_status();
return result;
}
/**
* @brief inv_check_flag returns the value of a flag.
* inv_check_flag can be used to check a number of flags,
* allowing users to poll flags rather than register callback
* functions. If a flag is set to True when inv_check_flag is called,
* the flag is automatically reset.
* The flags are:
* - INV_RAW_DATA_READY
* Indicates that new raw data is available.
* - INV_PROCESSED_DATA_READY
* Indicates that new processed data is available.
* - INV_GOT_GESTURE
* Indicates that a gesture has been detected by the gesture engine.
* - INV_MOTION_STATE_CHANGE
* Indicates that a change has been made from motion to no motion,
* or vice versa.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* and inv_dmp_start() must have been called.
*
* @param flag The flag to check.
*
* @return TRUE or FALSE state of the flag
*/
int inv_check_flag(int flag)
{
INVENSENSE_FUNC_START;
int flagReturn = inv_obj.flags[flag];
inv_obj.flags[flag] = 0;
return flagReturn;
}
/**
* @brief Enable generation of the DMP interrupt when Motion or no-motion
* is detected
* @param on
* Boolean to turn the interrupt on or off.
* @return INV_SUCCESS or non-zero error code.
*/
inv_error_t inv_set_motion_interrupt(unsigned char on)
{
INVENSENSE_FUNC_START;
inv_error_t result;
unsigned char regs[2] = { 0 };
if (inv_get_state() < INV_STATE_DMP_OPENED)
return INV_ERROR_SM_IMPROPER_STATE;
if (on) {
result = inv_get_dl_cfg_int(BIT_DMP_INT_EN);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
inv_obj.interrupt_sources |= INV_INT_MOTION;
} else {
inv_obj.interrupt_sources &= ~INV_INT_MOTION;
if (!inv_obj.interrupt_sources) {
result = inv_get_dl_cfg_int(0);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
}
if (on) {
regs[0] = DINAFE;
} else {
regs[0] = DINAD8;
}
result = inv_set_mpu_memory(KEY_CFG_7, 1, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
return result;
}
/**
* Enable generation of the DMP interrupt when a FIFO packet is ready
*
* @param on Boolean to turn the interrupt on or off
*
* @return INV_SUCCESS or non-zero error code
*/
inv_error_t inv_set_fifo_interrupt(unsigned char on)
{
INVENSENSE_FUNC_START;
inv_error_t result;
unsigned char regs[2] = { 0 };
if (inv_get_state() < INV_STATE_DMP_OPENED)
return INV_ERROR_SM_IMPROPER_STATE;
if (on) {
result = inv_get_dl_cfg_int(BIT_DMP_INT_EN);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
inv_obj.interrupt_sources |= INV_INT_FIFO;
} else {
inv_obj.interrupt_sources &= ~INV_INT_FIFO;
if (!inv_obj.interrupt_sources) {
result = inv_get_dl_cfg_int(0);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
}
if (on) {
regs[0] = DINAFE;
} else {
regs[0] = DINAD8;
}
result = inv_set_mpu_memory(KEY_CFG_6, 1, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
return result;
}
/**
* @brief Get the current set of DMP interrupt sources.
* These interrupts are generated by the DMP and can be
* routed to the MPU interrupt line via internal
* settings.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* must have been called.
*
* @return Currently enabled interrupt sources. The possible interrupts are:
* - INV_INT_FIFO,
* - INV_INT_MOTION,
* - INV_INT_TAP
*/
int inv_get_interrupts(void)
{
INVENSENSE_FUNC_START;
if (inv_get_state() < INV_STATE_DMP_OPENED)
return 0; // error
return inv_obj.interrupt_sources;
}
/**
* @brief Sets up the Accelerometer calibration and scale factor.
*
* Please refer to the provided "9-Axis Sensor Fusion Application
* Note" document provided. Section 5, "Sensor Mounting Orientation"
* offers a good coverage on the mounting matrices and explains how
* to use them.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* must have been called.
* @pre inv_dmp_start() must <b>NOT</b> have been called.
*
* @see inv_set_gyro_calibration().
* @see inv_set_compass_calibration().
*
* @param[in] range
* The range of the accelerometers in g's. An accelerometer
* that has a range of +2g's to -2g's should pass in 2.
* @param[in] orientation
* A 9 element matrix that represents how the accelerometers
* are oriented with respect to the device they are mounted
* in and the reference axis system.
* A typical set of values are {1, 0, 0, 0, 1, 0, 0, 0, 1}.
* This example corresponds to a 3 x 3 identity matrix.
*
* @return INV_SUCCESS if successful; a non-zero error code otherwise.
*/
inv_error_t inv_set_accel_calibration(float range, signed char *orientation)
{
INVENSENSE_FUNC_START;
float cal[9];
float scale = range / 32768.f;
int kk;
unsigned long sf;
inv_error_t result;
unsigned char regs[4] = { 0, 0, 0, 0 };
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
if (inv_get_state() != INV_STATE_DMP_OPENED)
return INV_ERROR_SM_IMPROPER_STATE;
/* Apply zero g-offset values */
if (ACCEL_ID_KXSD9 == mldl_cfg->accel->id) {
regs[0] = 0x80;
regs[1] = 0x0;
regs[2] = 0x80;
regs[3] = 0x0;
}
if (inv_dmpkey_supported(KEY_D_1_152)) {
result = inv_set_mpu_memory(KEY_D_1_152, 4, ®s[0]);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
if (scale == 0) {
inv_obj.accel_sens = 0;
}
if (mldl_cfg->accel->id) {
#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
defined CONFIG_MPU_SENSORS_MPU6050B1
unsigned char tmp[3] = { DINA0C, DINAC9, DINA2C };
#else
unsigned char tmp[3] = { DINA4C, DINACD, DINA6C };
#endif
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
unsigned char regs[3];
unsigned short orient;
for (kk = 0; kk < 9; ++kk) {
cal[kk] = scale * orientation[kk];
inv_obj.accel_cal[kk] = (long)(cal[kk] * (float)(1L << 30));
}
orient = inv_orientation_matrix_to_scalar(orientation);
regs[0] = tmp[orient & 3];
regs[1] = tmp[(orient >> 3) & 3];
regs[2] = tmp[(orient >> 6) & 3];
result = inv_set_mpu_memory(KEY_FCFG_2, 3, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
regs[0] = DINA26;
regs[1] = DINA46;
#if defined CONFIG_MPU_SENSORS_MPU3050 || defined CONFIG_MPU_SENSORS_MPU6050A2
regs[2] = DINA66;
#else
if (MPL_PROD_KEY(mldl_cfg->product_id, mldl_cfg->product_revision)
== MPU_PRODUCT_KEY_B1_E1_5)
regs[2] = DINA76;
else
regs[2] = DINA66;
#endif
if (orient & 4)
regs[0] |= 1;
if (orient & 0x20)
regs[1] |= 1;
if (orient & 0x100)
regs[2] |= 1;
result = inv_set_mpu_memory(KEY_FCFG_7, 3, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
if (mldl_cfg->accel->id == ACCEL_ID_MMA845X) {
result = inv_freescale_sensor_fusion_16bit(orient);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
} else if (mldl_cfg->accel->id == ACCEL_ID_MMA8450) {
result = inv_freescale_sensor_fusion_8bit(orient);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
}
if (inv_obj.accel_sens != 0) {
sf = (1073741824L / inv_obj.accel_sens);
} else {
sf = 0;
}
regs[0] = (unsigned char)((sf >> 8) & 0xff);
regs[1] = (unsigned char)(sf & 0xff);
result = inv_set_mpu_memory(KEY_D_0_108, 2, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
return result;
}
/**
* @brief Sets up the Gyro calibration and scale factor.
*
* Please refer to the provided "9-Axis Sensor Fusion Application
* Note" document provided. Section 5, "Sensor Mounting Orientation"
* offers a good coverage on the mounting matrices and explains
* how to use them.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* must have been called.
* @pre inv_dmp_start() must have <b>NOT</b> been called.
*
* @see inv_set_accel_calibration().
* @see inv_set_compass_calibration().
*
* @param[in] range
* The range of the gyros in degrees per second. A gyro
* that has a range of +2000 dps to -2000 dps should pass in
* 2000.
* @param[in] orientation
* A 9 element matrix that represents how the gyro are oriented
* with respect to the device they are mounted in. A typical
* set of values are {1, 0, 0, 0, 1, 0, 0, 0, 1}. This
* example corresponds to a 3 x 3 identity matrix.
*
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
inv_error_t inv_set_gyro_calibration(float range, signed char *orientation)
{
INVENSENSE_FUNC_START;
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
int kk;
float scale;
inv_error_t result;
unsigned char regs[12] = { 0 };
unsigned char maxVal = 0;
unsigned char tmpPtr = 0;
unsigned char tmpSign = 0;
unsigned char i = 0;
int sf = 0;
if (inv_get_state() != INV_STATE_DMP_OPENED)
return INV_ERROR_SM_IMPROPER_STATE;
if (mldl_cfg->gyro_sens_trim != 0) {
/* adjust the declared range to consider the gyro_sens_trim
of this part when different from the default (first dividend) */
range *= (32768.f / 250) / mldl_cfg->gyro_sens_trim;
}
scale = range / 32768.f; // inverse of sensitivity for the given full scale range
inv_obj.gyro_sens = (long)(range * 32768L);
for (kk = 0; kk < 9; ++kk) {
inv_obj.gyro_cal[kk] = (long)(scale * orientation[kk] * (1L << 30));
inv_obj.gyro_orient[kk] = (long)((double)orientation[kk] * (1L << 30));
}
{
#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
defined CONFIG_MPU_SENSORS_MPU6050B1
unsigned char tmpD = DINA4C;
unsigned char tmpE = DINACD;
unsigned char tmpF = DINA6C;
#else
unsigned char tmpD = DINAC9;
unsigned char tmpE = DINA2C;
unsigned char tmpF = DINACB;
#endif
regs[3] = DINA36;
regs[4] = DINA56;
regs[5] = DINA76;
for (i = 0; i < 3; i++) {
maxVal = 0;
tmpSign = 0;
if (inv_obj.gyro_orient[0 + 3 * i] < 0)
tmpSign = 1;
if (ABS(inv_obj.gyro_orient[1 + 3 * i]) >
ABS(inv_obj.gyro_orient[0 + 3 * i])) {
maxVal = 1;
if (inv_obj.gyro_orient[1 + 3 * i] < 0)
tmpSign = 1;
}
if (ABS(inv_obj.gyro_orient[2 + 3 * i]) >
ABS(inv_obj.gyro_orient[1 + 3 * i])) {
tmpSign = 0;
maxVal = 2;
if (inv_obj.gyro_orient[2 + 3 * i] < 0)
tmpSign = 1;
}
if (maxVal == 0) {
regs[tmpPtr++] = tmpD;
if (tmpSign)
regs[tmpPtr + 2] |= 0x01;
} else if (maxVal == 1) {
regs[tmpPtr++] = tmpE;
if (tmpSign)
regs[tmpPtr + 2] |= 0x01;
} else {
regs[tmpPtr++] = tmpF;
if (tmpSign)
regs[tmpPtr + 2] |= 0x01;
}
}
result = inv_set_mpu_memory(KEY_FCFG_1, 3, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_set_mpu_memory(KEY_FCFG_3, 3, ®s[3]);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
//sf = (gyroSens) * (0.5 * (pi/180) / 200.0) * 16384
inv_obj.gyro_sf =
(long)(((long long)inv_obj.gyro_sens * 767603923LL) / 1073741824LL);
result =
inv_set_mpu_memory(KEY_D_0_104, 4,
inv_int32_to_big8(inv_obj.gyro_sf, regs));
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
if (inv_obj.gyro_sens != 0) {
sf = (long)((long long)23832619764371LL / inv_obj.gyro_sens);
} else {
sf = 0;
}
result = inv_set_mpu_memory(KEY_D_0_24, 4, inv_int32_to_big8(sf, regs));
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
return INV_SUCCESS;
}
/**
* @brief Sets up the Compass calibration and scale factor.
*
* Please refer to the provided "9-Axis Sensor Fusion Application
* Note" document provided. Section 5, "Sensor Mounting Orientation"
* offers a good coverage on the mounting matrices and explains
* how to use them.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* must have been called.
* @pre inv_dmp_start() must have <b>NOT</b> been called.
*
* @see inv_set_gyro_calibration().
* @see inv_set_accel_calibration().
*
* @param[in] range
* The range of the compass.
* @param[in] orientation
* A 9 element matrix that represents how the compass is
* oriented with respect to the device they are mounted in.
* A typical set of values are {1, 0, 0, 0, 1, 0, 0, 0, 1}.
* This example corresponds to a 3 x 3 identity matrix.
* The matrix describes how to go from the chip mounting to
* the body of the device.
*
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
inv_error_t inv_set_compass_calibration(float range, signed char *orientation)
{
INVENSENSE_FUNC_START;
float cal[9];
float scale = range / 32768.f;
int kk;
unsigned short compassId = 0;
compassId = inv_get_compass_id();
if ((compassId == COMPASS_ID_YAS529) || (compassId == COMPASS_ID_HMC5883)
|| (compassId == COMPASS_ID_LSM303M)) {
scale /= 32.0f;
}
for (kk = 0; kk < 9; ++kk) {
cal[kk] = scale * orientation[kk];
inv_obj.compass_cal[kk] = (long)(cal[kk] * (float)(1L << 30));
}
inv_obj.compass_sens = (long)(scale * 1073741824L);
if (inv_dmpkey_supported(KEY_CPASS_MTX_00)) {
unsigned char reg0[4] = { 0, 0, 0, 0 };
unsigned char regp[4] = { 64, 0, 0, 0 };
unsigned char regn[4] = { 64 + 128, 0, 0, 0 };
unsigned char *reg;
int_fast8_t kk;
unsigned short keyList[9] =
{ KEY_CPASS_MTX_00, KEY_CPASS_MTX_01, KEY_CPASS_MTX_02,
KEY_CPASS_MTX_10, KEY_CPASS_MTX_11, KEY_CPASS_MTX_12,
KEY_CPASS_MTX_20, KEY_CPASS_MTX_21, KEY_CPASS_MTX_22
};
for (kk = 0; kk < 9; ++kk) {
if (orientation[kk] == 1)
reg = regp;
else if (orientation[kk] == -1)
reg = regn;
else
reg = reg0;
inv_set_mpu_memory(keyList[kk], 4, reg);
}
}
return INV_SUCCESS;
}
/**
* @internal
* @brief Sets the Gyro Dead Zone based upon LPF filter settings and Control setup.
*/
inv_error_t inv_set_dead_zone(void)
{
unsigned char reg;
inv_error_t result;
extern struct control_params cntrl_params;
if (cntrl_params.functions & INV_DEAD_ZONE) {
reg = 0x08;
} else {
#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
defined CONFIG_MPU_SENSORS_MPU6050B1
reg = 0;
#else
if (inv_params_obj.bias_mode & INV_BIAS_FROM_LPF) {
reg = 0x2;
} else {
reg = 0;
}
#endif
}
result = inv_set_mpu_memory(KEY_D_0_163, 1, ®);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
return result;
}
/**
* @brief inv_set_bias_update is used to register which algorithms will be
* used to automatically reset the gyroscope bias.
* The engine INV_BIAS_UPDATE must be enabled for these algorithms to
* run.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* and inv_dmp_start()
* must <b>NOT</b> have been called.
*
* @param function A function or bitwise OR of functions that determine
* how the gyroscope bias will be automatically updated.
* Functions include:
* - INV_NONE or 0,
* - INV_BIAS_FROM_NO_MOTION,
* - INV_BIAS_FROM_GRAVITY,
* - INV_BIAS_FROM_TEMPERATURE,
\ifnot UMPL
* - INV_BIAS_FROM_LPF,
* - INV_MAG_BIAS_FROM_MOTION,
* - INV_MAG_BIAS_FROM_GYRO,
* - INV_ALL.
* \endif
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
inv_error_t inv_set_bias_update(unsigned short function)
{
INVENSENSE_FUNC_START;
unsigned char regs[4];
long tmp[3] = { 0, 0, 0 };
inv_error_t result = INV_SUCCESS;
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
if (inv_get_state() != INV_STATE_DMP_OPENED)
return INV_ERROR_SM_IMPROPER_STATE;
/* do not allow progressive no motion bias tracker to run -
it's not fully debugged */
function &= ~INV_PROGRESSIVE_NO_MOTION; // FIXME, workaround
MPL_LOGV("forcing disable of PROGRESSIVE_NO_MOTION bias tracker\n");
// You must use EnableFastNoMotion() to get this feature
function &= ~INV_BIAS_FROM_FAST_NO_MOTION;
// You must use DisableFastNoMotion() to turn this feature off
if (inv_params_obj.bias_mode & INV_BIAS_FROM_FAST_NO_MOTION)
function |= INV_BIAS_FROM_FAST_NO_MOTION;
/*--- remove magnetic components from bias tracking
if there is no compass ---*/
if (!inv_compass_present()) {
function &= ~(INV_MAG_BIAS_FROM_GYRO | INV_MAG_BIAS_FROM_MOTION);
} else {
function &= ~(INV_BIAS_FROM_LPF);
}
// Enable function sets bias from no motion
inv_params_obj.bias_mode = function & (~INV_BIAS_FROM_NO_MOTION);
if (function & INV_BIAS_FROM_NO_MOTION) {
inv_enable_bias_no_motion();
} else {
inv_disable_bias_no_motion();
}
if (inv_params_obj.bias_mode & INV_BIAS_FROM_LPF) {
regs[0] = DINA80 + 2;
regs[1] = DINA2D;
regs[2] = DINA55;
regs[3] = DINA7D;
} else {
regs[0] = DINA80 + 7;
regs[1] = DINA2D;
regs[2] = DINA35;
regs[3] = DINA3D;
}
result = inv_set_mpu_memory(KEY_FCFG_5, 4, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_set_dead_zone();
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
if ((inv_params_obj.bias_mode & INV_BIAS_FROM_GRAVITY) &&
!inv_compass_present()) {
result = inv_set_gyro_data_source(INV_GYRO_FROM_QUATERNION);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
} else {
result = inv_set_gyro_data_source(INV_GYRO_FROM_RAW);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
inv_obj.factory_temp_comp = 0; // FIXME, workaround
if ((mldl_cfg->offset_tc[0] != 0) ||
(mldl_cfg->offset_tc[1] != 0) || (mldl_cfg->offset_tc[2] != 0)) {
inv_obj.factory_temp_comp = 1;
}
if (inv_obj.factory_temp_comp == 0) {
if (inv_params_obj.bias_mode & INV_BIAS_FROM_TEMPERATURE) {
result = inv_set_gyro_temp_slope(inv_obj.temp_slope);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
} else {
result = inv_set_gyro_temp_slope(tmp);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
} else {
inv_params_obj.bias_mode &= ~INV_LEARN_BIAS_FROM_TEMPERATURE;
MPL_LOGV("factory temperature compensation coefficients available - "
"disabling INV_LEARN_BIAS_FROM_TEMPERATURE\n");
}
/*---- hard requirement for using bias tracking BIAS_FROM_GRAVITY, relying on
compass and accel data, is to have accelerometer data delivered in the
FIFO ----*/
if (((inv_params_obj.bias_mode & INV_BIAS_FROM_GRAVITY)
&& inv_compass_present())
|| (inv_params_obj.bias_mode & INV_MAG_BIAS_FROM_GYRO)
|| (inv_params_obj.bias_mode & INV_MAG_BIAS_FROM_MOTION)) {
inv_send_accel(INV_ALL, INV_32_BIT);
inv_send_gyro(INV_ALL, INV_32_BIT);
}
return result;
}
/**
* @brief inv_get_motion_state is used to determine if the device is in
* a 'motion' or 'no motion' state.
* inv_get_motion_state returns INV_MOTION of the device is moving,
* or INV_NO_MOTION if the device is not moving.
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* and inv_dmp_start()
* must have been called.
*
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
int inv_get_motion_state(void)
{
INVENSENSE_FUNC_START;
return inv_obj.motion_state;
}
/**
* @brief inv_set_no_motion_thresh is used to set the threshold for
* detecting INV_NO_MOTION
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* must have been called.
*
* @param thresh A threshold scaled in degrees per second.
*
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
inv_error_t inv_set_no_motion_thresh(float thresh)
{
inv_error_t result = INV_SUCCESS;
unsigned char regs[4] = { 0 };
long tmp;
INVENSENSE_FUNC_START;
tmp = (long)(thresh * thresh * 2.045f);
if (tmp < 0) {
return INV_ERROR;
} else if (tmp > 8180000L) {
return INV_ERROR;
}
inv_obj.no_motion_threshold = tmp;
regs[0] = (unsigned char)(tmp >> 24);
regs[1] = (unsigned char)((tmp >> 16) & 0xff);
regs[2] = (unsigned char)((tmp >> 8) & 0xff);
regs[3] = (unsigned char)(tmp & 0xff);
result = inv_set_mpu_memory(KEY_D_1_108, 4, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_reset_motion();
return result;
}
/**
* @brief inv_set_no_motion_threshAccel is used to set the threshold for
* detecting INV_NO_MOTION with accelerometers when Gyros have
* been turned off
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* must have been called.
*
* @param thresh A threshold in g's scaled by 2^32
*
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
inv_error_t inv_set_no_motion_threshAccel(long thresh)
{
INVENSENSE_FUNC_START;
inv_obj.no_motion_accel_threshold = thresh;
return INV_SUCCESS;
}
/**
* @brief inv_set_no_motion_time is used to set the time required for
* detecting INV_NO_MOTION
*
* @pre inv_dmp_open()
* @ifnot MPL_MF
* or inv_open_low_power_pedometer()
* or inv_eis_open_dmp()
* @endif
* must have been called.
*
* @param time A time in seconds.
*
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
inv_error_t inv_set_no_motion_time(float time)
{
inv_error_t result = INV_SUCCESS;
unsigned char regs[2] = { 0 };
long tmp;
INVENSENSE_FUNC_START;
tmp = (long)(time * 200);
if (tmp < 0) {
return INV_ERROR;
} else if (tmp > 65535L) {
return INV_ERROR;
}
inv_obj.motion_duration = (unsigned short)tmp;
regs[0] = (unsigned char)((inv_obj.motion_duration >> 8) & 0xff);
regs[1] = (unsigned char)(inv_obj.motion_duration & 0xff);
result = inv_set_mpu_memory(KEY_D_1_106, 2, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_reset_motion();
return result;
}
/**
* @brief inv_get_version is used to get the ML version.
*
* @pre inv_get_version can be called at any time.
*
* @param version inv_get_version writes the ML version
* string pointer to version.
*
* @return INV_SUCCESS if successful or Non-zero error code otherwise.
*/
inv_error_t inv_get_version(unsigned char **version)
{
INVENSENSE_FUNC_START;
*version = (unsigned char *)mlVer; //fixme we are wiping const
return INV_SUCCESS;
}
/**
* @brief Check for the presence of the gyro sensor.
*
* This is not a physical check but a logical check and the value can change
* dynamically based on calls to inv_set_mpu_sensors().
*
* @return TRUE if the gyro is enabled FALSE otherwise.
*/
int inv_get_gyro_present(void)
{
return inv_get_dl_config()->requested_sensors & (INV_X_GYRO | INV_Y_GYRO |
INV_Z_GYRO);
}
static unsigned short inv_row_2_scale(const signed char *row)
{
unsigned short b;
if (row[0] > 0)
b = 0;
else if (row[0] < 0)
b = 4;
else if (row[1] > 0)
b = 1;
else if (row[1] < 0)
b = 5;
else if (row[2] > 0)
b = 2;
else if (row[2] < 0)
b = 6;
else
b = 7; // error
return b;
}
unsigned short inv_orientation_matrix_to_scalar(const signed char *mtx)
{
unsigned short scalar;
/*
XYZ 010_001_000 Identity Matrix
XZY 001_010_000
YXZ 010_000_001
YZX 000_010_001
ZXY 001_000_010
ZYX 000_001_010
*/
scalar = inv_row_2_scale(mtx);
scalar |= inv_row_2_scale(mtx + 3) << 3;
scalar |= inv_row_2_scale(mtx + 6) << 6;
return scalar;
}
/* Setups up the Freescale 16-bit accel for Sensor Fusion
* @param[in] orient A scalar representation of the orientation
* that describes how to go from the Chip Orientation
* to the Board Orientation often times called the Body Orientation in Invensense Documentation.
* inv_orientation_matrix_to_scalar() will turn the transformation matrix into this scalar.
*/
inv_error_t inv_freescale_sensor_fusion_16bit(unsigned short orient)
{
unsigned char rr[3];
inv_error_t result;
orient = orient & 0xdb;
switch (orient) {
default:
// Typically 0x88
rr[0] = DINACC;
rr[1] = DINACF;
rr[2] = DINA0E;
break;
case 0x50:
rr[0] = DINACE;
rr[1] = DINA0E;
rr[2] = DINACD;
break;
case 0x81:
rr[0] = DINACE;
rr[1] = DINACB;
rr[2] = DINA0E;
break;
case 0x11:
rr[0] = DINACC;
rr[1] = DINA0E;
rr[2] = DINACB;
break;
case 0x42:
rr[0] = DINA0A;
rr[1] = DINACF;
rr[2] = DINACB;
break;
case 0x0a:
rr[0] = DINA0A;
rr[1] = DINACB;
rr[2] = DINACD;
break;
}
result = inv_set_mpu_memory(KEY_FCFG_AZ, 3, rr);
return result;
}
/* Setups up the Freescale 8-bit accel for Sensor Fusion
* @param[in] orient A scalar representation of the orientation
* that describes how to go from the Chip Orientation
* to the Board Orientation often times called the Body Orientation in Invensense Documentation.
* inv_orientation_matrix_to_scalar() will turn the transformation matrix into this scalar.
*/
inv_error_t inv_freescale_sensor_fusion_8bit(unsigned short orient)
{
unsigned char regs[27];
inv_error_t result;
uint_fast8_t kk;
orient = orient & 0xdb;
kk = 0;
regs[kk++] = DINAC3;
regs[kk++] = DINA90 + 14;
regs[kk++] = DINAA0 + 9;
regs[kk++] = DINA3E;
regs[kk++] = DINA5E;
regs[kk++] = DINA7E;
regs[kk++] = DINAC2;
regs[kk++] = DINAA0 + 9;
regs[kk++] = DINA90 + 9;
regs[kk++] = DINAF8 + 2;
switch (orient) {
default:
// Typically 0x88
regs[kk++] = DINACB;
regs[kk++] = DINA54;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINACD;
break;
case 0x50:
regs[kk++] = DINACB;
regs[kk++] = DINACF;
regs[kk++] = DINA7C;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
break;
case 0x81:
regs[kk++] = DINA2C;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINACD;
regs[kk++] = DINACB;
break;
case 0x11:
regs[kk++] = DINA2C;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINA28;
regs[kk++] = DINACB;
regs[kk++] = DINACF;
break;
case 0x42:
regs[kk++] = DINACF;
regs[kk++] = DINACD;
regs[kk++] = DINA7C;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
regs[kk++] = DINA78;
break;
case 0x0a:
regs[kk++] = DINACD;
regs[kk++] = DINA54;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINA50;
regs[kk++] = DINACF;
break;
}
regs[kk++] = DINA90 + 7;
regs[kk++] = DINAF8 + 3;
regs[kk++] = DINAA0 + 9;
regs[kk++] = DINA0E;
regs[kk++] = DINA0E;
regs[kk++] = DINA0E;
regs[kk++] = DINAF8 + 1; // filler
result = inv_set_mpu_memory(KEY_FCFG_FSCALE, kk, regs);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
return result;
}
/**
* Controlls each sensor and each axis when the motion processing unit is
* running. When it is not running, simply records the state for later.
*
* NOTE: In this version only full sensors controll is allowed. Independent
* axis control will return an error.
*
* @param sensors Bit field of each axis desired to be turned on or off
*
* @return INV_SUCCESS or non-zero error code
*/
inv_error_t inv_set_mpu_sensors(unsigned long sensors)
{
INVENSENSE_FUNC_START;
unsigned char state = inv_get_state();
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
inv_error_t result = INV_SUCCESS;
unsigned short fifoRate;
if (state < INV_STATE_DMP_OPENED)
return INV_ERROR_SM_IMPROPER_STATE;
if (((sensors & INV_THREE_AXIS_ACCEL) != INV_THREE_AXIS_ACCEL) &&
((sensors & INV_THREE_AXIS_ACCEL) != 0)) {
return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
}
if (((sensors & INV_THREE_AXIS_ACCEL) != 0) &&
(mldl_cfg->pdata->accel.get_slave_descr == 0)) {
return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED;
}
if (((sensors & INV_THREE_AXIS_COMPASS) != INV_THREE_AXIS_COMPASS) &&
((sensors & INV_THREE_AXIS_COMPASS) != 0)) {
return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
}
if (((sensors & INV_THREE_AXIS_COMPASS) != 0) &&
(mldl_cfg->pdata->compass.get_slave_descr == 0)) {
return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED;
}
if (((sensors & INV_THREE_AXIS_PRESSURE) != INV_THREE_AXIS_PRESSURE) &&
((sensors & INV_THREE_AXIS_PRESSURE) != 0)) {
return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
}
if (((sensors & INV_THREE_AXIS_PRESSURE) != 0) &&
(mldl_cfg->pdata->pressure.get_slave_descr == 0)) {
return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED;
}
/* DMP was off, and is turning on */
if (sensors & INV_DMP_PROCESSOR &&
!(mldl_cfg->requested_sensors & INV_DMP_PROCESSOR)) {
struct ext_slave_config config;
long odr;
config.key = MPU_SLAVE_CONFIG_ODR_RESUME;
config.len = sizeof(long);
config.apply = (state == INV_STATE_DMP_STARTED);
config.data = &odr;
odr = (inv_mpu_get_sampling_rate_hz(mldl_cfg) * 1000);
result = inv_mpu_config_accel(mldl_cfg,
inv_get_serial_handle(),
inv_get_serial_handle(), &config);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
config.key = MPU_SLAVE_CONFIG_IRQ_RESUME;
odr = MPU_SLAVE_IRQ_TYPE_NONE;
result = inv_mpu_config_accel(mldl_cfg,
inv_get_serial_handle(),
inv_get_serial_handle(), &config);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
inv_init_fifo_hardare();
}
if (inv_obj.mode_change_func) {
result = inv_obj.mode_change_func(mldl_cfg->requested_sensors, sensors);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
/* Get the fifo rate before changing sensors so if we need to match it */
fifoRate = inv_get_fifo_rate();
mldl_cfg->requested_sensors = sensors;
/* inv_dmp_start will turn the sensors on */
if (state == INV_STATE_DMP_STARTED) {
result = inv_dl_start(sensors);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_reset_motion();
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_dl_stop(~sensors);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
inv_set_fifo_rate(fifoRate);
if (!(sensors & INV_DMP_PROCESSOR) && (sensors & INV_THREE_AXIS_ACCEL)) {
struct ext_slave_config config;
long data;
config.len = sizeof(long);
config.key = MPU_SLAVE_CONFIG_IRQ_RESUME;
config.apply = (state == INV_STATE_DMP_STARTED);
config.data = &data;
data = MPU_SLAVE_IRQ_TYPE_DATA_READY;
result = inv_mpu_config_accel(mldl_cfg,
inv_get_serial_handle(),
inv_get_serial_handle(), &config);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
}
return result;
}
void inv_set_mode_change(inv_error_t(*mode_change_func)
(unsigned long, unsigned long))
{
inv_obj.mode_change_func = mode_change_func;
}
/**
* Mantis setup
*/
#ifdef CONFIG_MPU_SENSORS_MPU6050B1
inv_error_t inv_set_mpu_6050_config()
{
long temp;
inv_error_t result;
unsigned char big8[4];
unsigned char atc[4];
long s[3], s2[3];
int kk;
struct mldl_cfg *mldlCfg = inv_get_dl_config();
result = inv_serial_read(inv_get_serial_handle(), inv_get_mpu_slave_addr(),
0x0d, 4, atc);
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
temp = atc[3] & 0x3f;
if (temp >= 32)
temp = temp - 64;
temp = (temp << 21) | 0x100000;
temp += (1L << 29);
temp = -temp;
result = inv_set_mpu_memory(KEY_D_ACT0, 4, inv_int32_to_big8(temp, big8));
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
for (kk = 0; kk < 3; ++kk) {
s[kk] = atc[kk] & 0x3f;
if (s[kk] > 32)
s[kk] = s[kk] - 64;
s[kk] *= 2516582L;
}
for (kk = 0; kk < 3; ++kk) {
s2[kk] = mldlCfg->pdata->orientation[kk * 3] * s[0] +
mldlCfg->pdata->orientation[kk * 3 + 1] * s[1] +
mldlCfg->pdata->orientation[kk * 3 + 2] * s[2];
}
result = inv_set_mpu_memory(KEY_D_ACSX, 4, inv_int32_to_big8(s2[0], big8));
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_set_mpu_memory(KEY_D_ACSY, 4, inv_int32_to_big8(s2[1], big8));
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
result = inv_set_mpu_memory(KEY_D_ACSZ, 4, inv_int32_to_big8(s2[2], big8));
if (result) {
LOG_RESULT_LOCATION(result);
return result;
}
return result;
}
#endif
/**
* @}
*/