1/*
2 $License:
3   Copyright 2011 InvenSense, Inc.
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16  $
17 */
18
19/******************************************************************************
20 *
21 * $Id:$
22 *
23 *****************************************************************************/
24
25#include "mlBiasNoMotion.h"
26#include "ml.h"
27#include "mlinclude.h"
28#include "mlos.h"
29#include "mlFIFO.h"
30#include "dmpKey.h"
31#include "accel.h"
32#include "mlMathFunc.h"
33#include "mldl.h"
34#include "mlstates.h"
35#include "mlSetGyroBias.h"
36
37#include "log.h"
38#undef MPL_LOG_TAG
39#define MPL_LOG_TAG "MPL-BiasNoMot"
40
41
42#define _mlDebug(x)             //{x}
43
44/**
45 *  @brief  inv_set_motion_callback is used to register a callback function that
46 *          will trigger when a change of motion state is detected.
47 *
48 *  @pre    inv_dmp_open()
49 *          @ifnot MPL_MF
50 *              or inv_open_low_power_pedometer()
51 *              or inv_eis_open_dmp()
52 *          @endif
53 *          and inv_dmp_start()
54 *          must <b>NOT</b> have been called.
55 *
56 *  @param  func    A user defined callback function accepting a
57 *                  motion_state parameter, the new motion state.
58 *                  May be one of INV_MOTION or INV_NO_MOTION.
59 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
60 */
61inv_error_t inv_set_motion_callback(void (*func) (unsigned short motion_state))
62{
63    INVENSENSE_FUNC_START;
64
65    if ((inv_get_state() != INV_STATE_DMP_OPENED) &&
66        (inv_get_state() != INV_STATE_DMP_STARTED))
67        return INV_ERROR_SM_IMPROPER_STATE;
68
69    inv_params_obj.motion_cb_func = func;
70
71    return INV_SUCCESS;
72}
73
74#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
75    defined CONFIG_MPU_SENSORS_MPU6050B1
76/** Turns on the feature to compute gyro bias from No Motion */
77inv_error_t inv_turn_on_bias_from_no_motion()
78{
79    inv_error_t result;
80    unsigned char regs[3] = { 0x0d, DINA35, 0x5d };
81    inv_params_obj.bias_mode |= INV_BIAS_FROM_NO_MOTION;
82    result = inv_set_mpu_memory(KEY_CFG_MOTION_BIAS, 3, regs);
83    return result;
84}
85
86/** Turns off the feature to compute gyro bias from No Motion
87*/
88inv_error_t inv_turn_off_bias_from_no_motion()
89{
90    inv_error_t result;
91    unsigned char regs[3] = { DINA90 + 8, DINA90 + 8, DINA90 + 8 };
92    inv_params_obj.bias_mode &= ~INV_BIAS_FROM_NO_MOTION;
93    result = inv_set_mpu_memory(KEY_CFG_MOTION_BIAS, 3, regs);
94    return result;
95}
96#endif
97
98inv_error_t inv_update_bias(void)
99{
100    INVENSENSE_FUNC_START;
101    inv_error_t result;
102    unsigned char regs[12];
103    short bias[GYRO_NUM_AXES];
104
105    if ((inv_params_obj.bias_mode & INV_BIAS_FROM_NO_MOTION)
106        && inv_get_gyro_present()) {
107
108        regs[0] = DINAA0 + 3;
109        result = inv_set_mpu_memory(KEY_FCFG_6, 1, regs);
110        if (result) {
111            LOG_RESULT_LOCATION(result);
112            return result;
113        }
114
115        result = inv_get_mpu_memory(KEY_D_1_244, 12, regs);
116        if (result) {
117            LOG_RESULT_LOCATION(result);
118            return result;
119        }
120
121        inv_convert_bias(regs, bias);
122
123        regs[0] = DINAA0 + 15;
124        result = inv_set_mpu_memory(KEY_FCFG_6, 1, regs);
125        if (result) {
126            LOG_RESULT_LOCATION(result);
127            return result;
128        }
129
130        result = inv_set_gyro_bias_in_hw_unit(bias, INV_SGB_NO_MOTION);
131        if (result) {
132            LOG_RESULT_LOCATION(result);
133            return result;
134        }
135
136        result =
137            inv_serial_read(inv_get_serial_handle(), inv_get_mpu_slave_addr(),
138                            MPUREG_TEMP_OUT_H, 2, regs);
139        if (result) {
140            LOG_RESULT_LOCATION(result);
141            return result;
142        }
143        result = inv_set_mpu_memory(KEY_DMP_PREVPTAT, 2, regs);
144        if (result) {
145            LOG_RESULT_LOCATION(result);
146            return result;
147        }
148
149        inv_obj.got_no_motion_bias = TRUE;
150    }
151    return INV_SUCCESS;
152}
153
154inv_error_t MLAccelMotionDetection(struct inv_obj_t *inv_obj)
155{
156    long gain;
157    unsigned long timeChange;
158    long rate;
159    inv_error_t result;
160    long accel[3], temp;
161    long long accelMag;
162    unsigned long currentTime;
163    int kk;
164
165    if (!inv_accel_present()) {
166        return INV_SUCCESS;
167    }
168
169    currentTime = inv_get_tick_count();
170
171    // We always run the accel low pass filter at the highest sample rate possible
172    result = inv_get_accel(accel);
173    if (result != INV_ERROR_FEATURE_NOT_ENABLED) {
174        if (result) {
175            LOG_RESULT_LOCATION(result);
176            return result;
177        }
178        rate = inv_get_fifo_rate() * 5 + 5;
179        if (rate > 200)
180            rate = 200;
181
182        gain = inv_obj->accel_lpf_gain * rate;
183        timeChange = inv_get_fifo_rate();
184
185        accelMag = 0;
186        for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
187            inv_obj->accel_lpf[kk] =
188                inv_q30_mult(((1L << 30) - gain), inv_obj->accel_lpf[kk]);
189            inv_obj->accel_lpf[kk] += inv_q30_mult(gain, accel[kk]);
190            temp = accel[0] - inv_obj->accel_lpf[0];
191            accelMag += (long long)temp *temp;
192        }
193
194        if (accelMag > inv_obj->no_motion_accel_threshold) {
195            inv_obj->no_motion_accel_time = currentTime;
196
197            // Check for change of state
198            if (!inv_get_gyro_present())
199                inv_set_motion_state(INV_MOTION);
200
201        } else if ((currentTime - inv_obj->no_motion_accel_time) >
202                   5 * inv_obj->motion_duration) {
203            // We have no motion according to accel
204            // Check fsor change of state
205            if (!inv_get_gyro_present())
206                inv_set_motion_state(INV_NO_MOTION);
207        }
208    }
209    return INV_SUCCESS;
210}
211
212/**
213 * @internal
214 * @brief   Manually update the motion/no motion status.  This is a
215 *          convienence function for implementations that do not wish to use
216 *          inv_update_data.
217 *          This function can be called periodically to check for the
218 *          'no motion' state and update the internal motion status and bias
219 *          calculations.
220 */
221inv_error_t MLPollMotionStatus(struct inv_obj_t * inv_obj)
222{
223    INVENSENSE_FUNC_START;
224    unsigned char regs[3] = { 0 };
225    unsigned short motionFlag = 0;
226    unsigned long currentTime;
227    inv_error_t result;
228
229    result = MLAccelMotionDetection(inv_obj);
230
231    currentTime = inv_get_tick_count();
232
233    // If it is not time to poll for a no motion event, return
234    if (((inv_obj->interrupt_sources & INV_INT_MOTION) == 0) &&
235        ((currentTime - inv_obj->poll_no_motion) <= 1000))
236        return INV_SUCCESS;
237
238    inv_obj->poll_no_motion = currentTime;
239
240#if defined CONFIG_MPU_SENSORS_MPU3050
241    if (inv_get_gyro_present()
242        && ((inv_params_obj.bias_mode & INV_BIAS_FROM_FAST_NO_MOTION) == 0)) {
243        static long repeatBiasUpdateTime = 0;
244
245        result = inv_get_mpu_memory(KEY_D_1_98, 2, regs);
246        if (result) {
247            LOG_RESULT_LOCATION(result);
248            return result;
249        }
250
251        motionFlag = (unsigned short)regs[0] * 256 + (unsigned short)regs[1];
252
253        _mlDebug(MPL_LOGV("motionFlag from RAM : 0x%04X\n", motionFlag);
254            )
255            if (motionFlag == inv_obj->motion_duration) {
256            if (inv_obj->motion_state == INV_MOTION) {
257                inv_update_bias();
258                repeatBiasUpdateTime = inv_get_tick_count();
259
260                regs[0] = DINAD8 + 1;
261                regs[1] = DINA0C;
262                regs[2] = DINAD8 + 2;
263                result = inv_set_mpu_memory(KEY_CFG_18, 3, regs);
264                if (result) {
265                    LOG_RESULT_LOCATION(result);
266                    return result;
267                }
268
269                regs[0] = 0;
270                regs[1] = 5;
271                result = inv_set_mpu_memory(KEY_D_1_106, 2, regs);
272                if (result) {
273                    LOG_RESULT_LOCATION(result);
274                    return result;
275                }
276
277                //Trigger no motion callback
278                inv_set_motion_state(INV_NO_MOTION);
279            }
280        }
281        if (motionFlag == 5) {
282            if (inv_obj->motion_state == INV_NO_MOTION) {
283                regs[0] = DINAD8 + 2;
284                regs[1] = DINA0C;
285                regs[2] = DINAD8 + 1;
286                result = inv_set_mpu_memory(KEY_CFG_18, 3, regs);
287                if (result) {
288                    LOG_RESULT_LOCATION(result);
289                    return result;
290                }
291
292                regs[0] =
293                    (unsigned char)((inv_obj->motion_duration >> 8) & 0xff);
294                regs[1] = (unsigned char)(inv_obj->motion_duration & 0xff);
295                result = inv_set_mpu_memory(KEY_D_1_106, 2, regs);
296                if (result) {
297                    LOG_RESULT_LOCATION(result);
298                    return result;
299                }
300
301                //Trigger no motion callback
302                inv_set_motion_state(INV_MOTION);
303            }
304        }
305        if (inv_obj->motion_state == INV_NO_MOTION) {
306            if ((inv_get_tick_count() - repeatBiasUpdateTime) > 4000) {
307                inv_update_bias();
308                repeatBiasUpdateTime = inv_get_tick_count();
309            }
310        }
311    }
312#else                           // CONFIG_MPU_SENSORS_MPU3050
313    if (inv_get_gyro_present()
314        && ((inv_params_obj.bias_mode & INV_BIAS_FROM_FAST_NO_MOTION) == 0)) {
315        result = inv_get_mpu_memory(KEY_D_1_98, 2, regs);
316        if (result) {
317            LOG_RESULT_LOCATION(result);
318            return result;
319        }
320
321        motionFlag = (unsigned short)regs[0] * 256 + (unsigned short)regs[1];
322
323        _mlDebug(MPL_LOGV("motionFlag from RAM : 0x%04X\n", motionFlag);
324            )
325            if (motionFlag > 0) {
326            unsigned char biasReg[12];
327            long biasTmp2[3], biasTmp[3];
328            int i;
329
330            if (inv_obj->last_motion != motionFlag) {
331                result = inv_get_mpu_memory(KEY_D_2_96, 12, biasReg);
332
333                for (i = 0; i < 3; i++) {
334                    biasTmp2[i] = inv_big8_to_int32(&biasReg[i * 4]);
335                }
336                // Rotate bias vector by the transpose of the orientation matrix
337                for (i = 0; i < 3; ++i) {
338                    biasTmp[i] =
339                        inv_q30_mult(biasTmp2[0],
340                                     inv_obj->gyro_orient[i]) +
341                        inv_q30_mult(biasTmp2[1],
342                                     inv_obj->gyro_orient[i + 3]) +
343                        inv_q30_mult(biasTmp2[2], inv_obj->gyro_orient[i + 6]);
344                }
345                inv_obj->gyro_bias[0] = inv_q30_mult(biasTmp[0], 1501974482L);
346                inv_obj->gyro_bias[1] = inv_q30_mult(biasTmp[1], 1501974482L);
347                inv_obj->gyro_bias[2] = inv_q30_mult(biasTmp[2], 1501974482L);
348            }
349            inv_set_motion_state(INV_NO_MOTION);
350        } else {
351            // We are in a motion state
352            inv_set_motion_state(INV_MOTION);
353        }
354        inv_obj->last_motion = motionFlag;
355
356    }
357#endif                          // CONFIG_MPU_SENSORS_MPU3050
358    return INV_SUCCESS;
359}
360
361inv_error_t inv_enable_bias_no_motion(void)
362{
363    inv_error_t result;
364    inv_params_obj.bias_mode |= INV_BIAS_FROM_NO_MOTION;
365    result =
366        inv_register_fifo_rate_process(MLPollMotionStatus,
367                                       INV_PRIORITY_BIAS_NO_MOTION);
368#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
369    defined CONFIG_MPU_SENSORS_MPU6050B1
370    if (result) {
371        LOG_RESULT_LOCATION(result);
372        return result;
373    }
374    result = inv_turn_on_bias_from_no_motion();
375#endif
376    return result;
377}
378
379inv_error_t inv_disable_bias_no_motion(void)
380{
381    inv_error_t result;
382    inv_params_obj.bias_mode &= ~INV_BIAS_FROM_NO_MOTION;
383    result = inv_unregister_fifo_rate_process(MLPollMotionStatus);
384#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
385    defined CONFIG_MPU_SENSORS_MPU6050B1
386    if (result) {
387        LOG_RESULT_LOCATION(result);
388        return result;
389    }
390    result = inv_turn_off_bias_from_no_motion();
391#endif
392    return result;
393}
394