/* $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:$ * *****************************************************************************/ #include "mlBiasNoMotion.h" #include "ml.h" #include "mlinclude.h" #include "mlos.h" #include "mlFIFO.h" #include "dmpKey.h" #include "accel.h" #include "mlMathFunc.h" #include "mldl.h" #include "mlstates.h" #include "mlSetGyroBias.h" #include "log.h" #undef MPL_LOG_TAG #define MPL_LOG_TAG "MPL-BiasNoMot" #define _mlDebug(x) //{x} /** * @brief inv_set_motion_callback is used to register a callback function that * will trigger when a change of motion state is detected. * * @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 func A user defined callback function accepting a * motion_state parameter, the new motion state. * May be one of INV_MOTION or INV_NO_MOTION. * @return INV_SUCCESS if successful or Non-zero error code otherwise. */ inv_error_t inv_set_motion_callback(void (*func) (unsigned short motion_state)) { INVENSENSE_FUNC_START; if ((inv_get_state() != INV_STATE_DMP_OPENED) && (inv_get_state() != INV_STATE_DMP_STARTED)) return INV_ERROR_SM_IMPROPER_STATE; inv_params_obj.motion_cb_func = func; return INV_SUCCESS; } #if defined CONFIG_MPU_SENSORS_MPU6050A2 || \ defined CONFIG_MPU_SENSORS_MPU6050B1 /** Turns on the feature to compute gyro bias from No Motion */ inv_error_t inv_turn_on_bias_from_no_motion() { inv_error_t result; unsigned char regs[3] = { 0x0d, DINA35, 0x5d }; inv_params_obj.bias_mode |= INV_BIAS_FROM_NO_MOTION; result = inv_set_mpu_memory(KEY_CFG_MOTION_BIAS, 3, regs); return result; } /** Turns off the feature to compute gyro bias from No Motion */ inv_error_t inv_turn_off_bias_from_no_motion() { inv_error_t result; unsigned char regs[3] = { DINA90 + 8, DINA90 + 8, DINA90 + 8 }; inv_params_obj.bias_mode &= ~INV_BIAS_FROM_NO_MOTION; result = inv_set_mpu_memory(KEY_CFG_MOTION_BIAS, 3, regs); return result; } #endif inv_error_t inv_update_bias(void) { INVENSENSE_FUNC_START; inv_error_t result; unsigned char regs[12]; short bias[GYRO_NUM_AXES]; if ((inv_params_obj.bias_mode & INV_BIAS_FROM_NO_MOTION) && inv_get_gyro_present()) { regs[0] = DINAA0 + 3; result = inv_set_mpu_memory(KEY_FCFG_6, 1, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } result = inv_get_mpu_memory(KEY_D_1_244, 12, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } inv_convert_bias(regs, bias); regs[0] = DINAA0 + 15; result = inv_set_mpu_memory(KEY_FCFG_6, 1, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } result = inv_set_gyro_bias_in_hw_unit(bias, INV_SGB_NO_MOTION); if (result) { LOG_RESULT_LOCATION(result); return result; } result = inv_serial_read(inv_get_serial_handle(), inv_get_mpu_slave_addr(), MPUREG_TEMP_OUT_H, 2, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } result = inv_set_mpu_memory(KEY_DMP_PREVPTAT, 2, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } inv_obj.got_no_motion_bias = TRUE; } return INV_SUCCESS; } inv_error_t MLAccelMotionDetection(struct inv_obj_t *inv_obj) { long gain; unsigned long timeChange; long rate; inv_error_t result; long accel[3], temp; long long accelMag; unsigned long currentTime; int kk; if (!inv_accel_present()) { return INV_SUCCESS; } currentTime = inv_get_tick_count(); // We always run the accel low pass filter at the highest sample rate possible result = inv_get_accel(accel); if (result != INV_ERROR_FEATURE_NOT_ENABLED) { if (result) { LOG_RESULT_LOCATION(result); return result; } rate = inv_get_fifo_rate() * 5 + 5; if (rate > 200) rate = 200; gain = inv_obj->accel_lpf_gain * rate; timeChange = inv_get_fifo_rate(); accelMag = 0; for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) { inv_obj->accel_lpf[kk] = inv_q30_mult(((1L << 30) - gain), inv_obj->accel_lpf[kk]); inv_obj->accel_lpf[kk] += inv_q30_mult(gain, accel[kk]); temp = accel[0] - inv_obj->accel_lpf[0]; accelMag += (long long)temp *temp; } if (accelMag > inv_obj->no_motion_accel_threshold) { inv_obj->no_motion_accel_time = currentTime; // Check for change of state if (!inv_get_gyro_present()) inv_set_motion_state(INV_MOTION); } else if ((currentTime - inv_obj->no_motion_accel_time) > 5 * inv_obj->motion_duration) { // We have no motion according to accel // Check fsor change of state if (!inv_get_gyro_present()) inv_set_motion_state(INV_NO_MOTION); } } return INV_SUCCESS; } /** * @internal * @brief Manually update the motion/no motion status. This is a * convienence function for implementations that do not wish to use * inv_update_data. * This function can be called periodically to check for the * 'no motion' state and update the internal motion status and bias * calculations. */ inv_error_t MLPollMotionStatus(struct inv_obj_t * inv_obj) { INVENSENSE_FUNC_START; unsigned char regs[3] = { 0 }; unsigned short motionFlag = 0; unsigned long currentTime; inv_error_t result; result = MLAccelMotionDetection(inv_obj); currentTime = inv_get_tick_count(); // If it is not time to poll for a no motion event, return if (((inv_obj->interrupt_sources & INV_INT_MOTION) == 0) && ((currentTime - inv_obj->poll_no_motion) <= 1000)) return INV_SUCCESS; inv_obj->poll_no_motion = currentTime; #if defined CONFIG_MPU_SENSORS_MPU3050 if (inv_get_gyro_present() && ((inv_params_obj.bias_mode & INV_BIAS_FROM_FAST_NO_MOTION) == 0)) { static long repeatBiasUpdateTime = 0; result = inv_get_mpu_memory(KEY_D_1_98, 2, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } motionFlag = (unsigned short)regs[0] * 256 + (unsigned short)regs[1]; _mlDebug(MPL_LOGV("motionFlag from RAM : 0x%04X\n", motionFlag); ) if (motionFlag == inv_obj->motion_duration) { if (inv_obj->motion_state == INV_MOTION) { inv_update_bias(); repeatBiasUpdateTime = inv_get_tick_count(); regs[0] = DINAD8 + 1; regs[1] = DINA0C; regs[2] = DINAD8 + 2; result = inv_set_mpu_memory(KEY_CFG_18, 3, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } regs[0] = 0; regs[1] = 5; result = inv_set_mpu_memory(KEY_D_1_106, 2, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } //Trigger no motion callback inv_set_motion_state(INV_NO_MOTION); } } if (motionFlag == 5) { if (inv_obj->motion_state == INV_NO_MOTION) { 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; } 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; } //Trigger no motion callback inv_set_motion_state(INV_MOTION); } } if (inv_obj->motion_state == INV_NO_MOTION) { if ((inv_get_tick_count() - repeatBiasUpdateTime) > 4000) { inv_update_bias(); repeatBiasUpdateTime = inv_get_tick_count(); } } } #else // CONFIG_MPU_SENSORS_MPU3050 if (inv_get_gyro_present() && ((inv_params_obj.bias_mode & INV_BIAS_FROM_FAST_NO_MOTION) == 0)) { result = inv_get_mpu_memory(KEY_D_1_98, 2, regs); if (result) { LOG_RESULT_LOCATION(result); return result; } motionFlag = (unsigned short)regs[0] * 256 + (unsigned short)regs[1]; _mlDebug(MPL_LOGV("motionFlag from RAM : 0x%04X\n", motionFlag); ) if (motionFlag > 0) { unsigned char biasReg[12]; long biasTmp2[3], biasTmp[3]; int i; if (inv_obj->last_motion != motionFlag) { result = inv_get_mpu_memory(KEY_D_2_96, 12, biasReg); for (i = 0; i < 3; i++) { biasTmp2[i] = inv_big8_to_int32(&biasReg[i * 4]); } // Rotate bias vector by the transpose of the orientation matrix for (i = 0; i < 3; ++i) { biasTmp[i] = inv_q30_mult(biasTmp2[0], inv_obj->gyro_orient[i]) + inv_q30_mult(biasTmp2[1], inv_obj->gyro_orient[i + 3]) + inv_q30_mult(biasTmp2[2], inv_obj->gyro_orient[i + 6]); } inv_obj->gyro_bias[0] = inv_q30_mult(biasTmp[0], 1501974482L); inv_obj->gyro_bias[1] = inv_q30_mult(biasTmp[1], 1501974482L); inv_obj->gyro_bias[2] = inv_q30_mult(biasTmp[2], 1501974482L); } inv_set_motion_state(INV_NO_MOTION); } else { // We are in a motion state inv_set_motion_state(INV_MOTION); } inv_obj->last_motion = motionFlag; } #endif // CONFIG_MPU_SENSORS_MPU3050 return INV_SUCCESS; } inv_error_t inv_enable_bias_no_motion(void) { inv_error_t result; inv_params_obj.bias_mode |= INV_BIAS_FROM_NO_MOTION; result = inv_register_fifo_rate_process(MLPollMotionStatus, INV_PRIORITY_BIAS_NO_MOTION); #if defined CONFIG_MPU_SENSORS_MPU6050A2 || \ defined CONFIG_MPU_SENSORS_MPU6050B1 if (result) { LOG_RESULT_LOCATION(result); return result; } result = inv_turn_on_bias_from_no_motion(); #endif return result; } inv_error_t inv_disable_bias_no_motion(void) { inv_error_t result; inv_params_obj.bias_mode &= ~INV_BIAS_FROM_NO_MOTION; result = inv_unregister_fifo_rate_process(MLPollMotionStatus); #if defined CONFIG_MPU_SENSORS_MPU6050A2 || \ defined CONFIG_MPU_SENSORS_MPU6050B1 if (result) { LOG_RESULT_LOCATION(result); return result; } result = inv_turn_off_bias_from_no_motion(); #endif return result; }