| /* |
| $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; |
| } |
| } |
| |
| if (!(sensors & INV_DMP_PROCESSOR) && (sensors & INV_THREE_AXIS_ACCEL)) { |
| struct ext_slave_config config; |
| long data; |
| inv_set_fifo_rate(fifoRate); |
| |
| 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 |
| |
| /** |
| * @} |
| */ |