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 * $Id: ml.c 5642 2011-06-14 02:26:20Z mcaramello $
21 *
22 *****************************************************************************/
23
24/**
25 *  @defgroup ML
26 *  @brief  Motion Library APIs.
27 *          The Motion Library processes gyroscopes, accelerometers, and
28 *          compasses to provide a physical model of the movement for the
29 *          sensors.
30 *          The results of this processing may be used to control objects
31 *          within a user interface environment, detect gestures, track 3D
32 *          movement for gaming applications, and analyze the blur created
33 *          due to hand movement while taking a picture.
34 *
35 *  @{
36 *      @file   ml.c
37 *      @brief  The Motion Library APIs.
38 */
39
40/* ------------------ */
41/* - Include Files. - */
42/* ------------------ */
43
44#include <string.h>
45
46#include "ml.h"
47#include "mldl.h"
48#include "mltypes.h"
49#include "mlinclude.h"
50#include "compass.h"
51#include "dmpKey.h"
52#include "dmpDefault.h"
53#include "mlstates.h"
54#include "mlFIFO.h"
55#include "mlFIFOHW.h"
56#include "mlMathFunc.h"
57#include "mlsupervisor.h"
58#include "mlmath.h"
59#include "mlcontrol.h"
60#include "mldl_cfg.h"
61#include "mpu.h"
62#include "accel.h"
63#include "mlos.h"
64#include "mlsl.h"
65#include "mlos.h"
66#include "mlBiasNoMotion.h"
67#include "log.h"
68#undef MPL_LOG_TAG
69#define MPL_LOG_TAG "MPL-ml"
70
71#define ML_MOT_TYPE_NONE 0
72#define ML_MOT_TYPE_NO_MOTION 1
73#define ML_MOT_TYPE_MOTION_DETECTED 2
74
75#define ML_MOT_STATE_MOVING 0
76#define ML_MOT_STATE_NO_MOTION 1
77#define ML_MOT_STATE_BIAS_IN_PROG 2
78
79#define _mlDebug(x)             //{x}
80
81/* Global Variables */
82const unsigned char mlVer[] = { INV_VERSION };
83
84struct inv_params_obj inv_params_obj = {
85    INV_BIAS_UPDATE_FUNC_DEFAULT,   // bias_mode
86    INV_ORIENTATION_MASK_DEFAULT,   // orientation_mask
87    INV_PROCESSED_DATA_CALLBACK_DEFAULT,    // fifo_processed_func
88    INV_ORIENTATION_CALLBACK_DEFAULT,   // orientation_cb_func
89    INV_MOTION_CALLBACK_DEFAULT,    // motion_cb_func
90    INV_STATE_SERIAL_CLOSED     // starting state
91};
92
93struct inv_obj_t inv_obj;
94void *g_mlsl_handle;
95
96typedef struct {
97    // These describe callbacks happening everythime a DMP interrupt is processed
98    int_fast8_t numInterruptProcesses;
99    // Array of function pointers, function being void function taking void
100    inv_obj_func processInterruptCb[MAX_INTERRUPT_PROCESSES];
101
102} tMLXCallbackInterrupt;        // MLX_callback_t
103
104tMLXCallbackInterrupt mlxCallbackInterrupt;
105
106/* --------------- */
107/* -  Functions. - */
108/* --------------- */
109
110inv_error_t inv_freescale_sensor_fusion_16bit(unsigned short orient);
111inv_error_t inv_freescale_sensor_fusion_8bit(unsigned short orient);
112unsigned short inv_orientation_matrix_to_scalar(const signed char *mtx);
113
114/**
115 *  @brief  Open serial connection with the MPU device.
116 *          This is the entry point of the MPL and must be
117 *          called prior to any other function call.
118 *
119 *  @param  port     System handle for 'port' MPU device is found on.
120 *                   The significance of this parameter varies by
121 *                   platform. It is passed as 'port' to MLSLSerialOpen.
122 *
123 *  @return INV_SUCCESS or error code.
124 */
125inv_error_t inv_serial_start(char const *port)
126{
127    INVENSENSE_FUNC_START;
128    inv_error_t result;
129
130    if (inv_get_state() >= INV_STATE_SERIAL_OPENED)
131        return INV_SUCCESS;
132
133    result = inv_state_transition(INV_STATE_SERIAL_OPENED);
134    if (result) {
135        LOG_RESULT_LOCATION(result);
136        return result;
137    }
138
139    result = inv_serial_open(port, &g_mlsl_handle);
140    if (INV_SUCCESS != result) {
141        (void)inv_state_transition(INV_STATE_SERIAL_CLOSED);
142    }
143
144    return result;
145}
146
147/**
148 *  @brief  Close the serial communication.
149 *          This function needs to be called explicitly to shut down
150 *          the communication with the device.  Calling inv_dmp_close()
151 *          won't affect the established serial communication.
152 *  @return INV_SUCCESS; non-zero error code otherwise.
153 */
154inv_error_t inv_serial_stop(void)
155{
156    INVENSENSE_FUNC_START;
157    inv_error_t result = INV_SUCCESS;
158
159    if (inv_get_state() == INV_STATE_SERIAL_CLOSED)
160        return INV_SUCCESS;
161
162    result = inv_state_transition(INV_STATE_SERIAL_CLOSED);
163    if (INV_SUCCESS != result) {
164        MPL_LOGE("State Transition Failure in %s: %d\n", __func__, result);
165    }
166    result = inv_serial_close(g_mlsl_handle);
167    if (INV_SUCCESS != result) {
168        MPL_LOGE("Unable to close Serial Handle %s: %d\n", __func__, result);
169    }
170    return result;
171}
172
173/**
174 *  @brief  Get the serial file handle to the device.
175 *  @return The serial file handle.
176 */
177void *inv_get_serial_handle(void)
178{
179    INVENSENSE_FUNC_START;
180    return g_mlsl_handle;
181}
182
183/**
184 *  @brief  apply the choosen orientation and full scale range
185 *          for gyroscopes, accelerometer, and compass.
186 *  @return INV_SUCCESS if successful, a non-zero code otherwise.
187 */
188inv_error_t inv_apply_calibration(void)
189{
190    INVENSENSE_FUNC_START;
191    signed char gyroCal[9] = { 0 };
192    signed char accelCal[9] = { 0 };
193    signed char magCal[9] = { 0 };
194    float gyroScale = 2000.f;
195    float accelScale = 0.f;
196    float magScale = 0.f;
197
198    inv_error_t result;
199    int ii;
200    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
201
202    for (ii = 0; ii < 9; ii++) {
203        gyroCal[ii] = mldl_cfg->pdata->orientation[ii];
204        accelCal[ii] = mldl_cfg->pdata->accel.orientation[ii];
205        magCal[ii] = mldl_cfg->pdata->compass.orientation[ii];
206    }
207
208    switch (mldl_cfg->full_scale) {
209    case MPU_FS_250DPS:
210        gyroScale = 250.f;
211        break;
212    case MPU_FS_500DPS:
213        gyroScale = 500.f;
214        break;
215    case MPU_FS_1000DPS:
216        gyroScale = 1000.f;
217        break;
218    case MPU_FS_2000DPS:
219        gyroScale = 2000.f;
220        break;
221    default:
222        MPL_LOGE("Unrecognized full scale setting for gyros : %02X\n",
223                 mldl_cfg->full_scale);
224        return INV_ERROR_INVALID_PARAMETER;
225        break;
226    }
227
228    RANGE_FIXEDPOINT_TO_FLOAT(mldl_cfg->accel->range, accelScale);
229    inv_obj.accel_sens = (long)(accelScale * 65536L);
230    /* sensitivity adjustment, typically = 2 (for +/- 2 gee) */
231#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
232    defined CONFIG_MPU_SENSORS_MPU6050B1
233    inv_obj.accel_sens /= 32768 / mldl_cfg->accel_sens_trim;
234#else
235    inv_obj.accel_sens /= 2;
236#endif
237
238    RANGE_FIXEDPOINT_TO_FLOAT(mldl_cfg->compass->range, magScale);
239    inv_obj.compass_sens = (long)(magScale * 32768);
240
241    if (inv_get_state() == INV_STATE_DMP_OPENED) {
242        result = inv_set_gyro_calibration(gyroScale, gyroCal);
243        if (INV_SUCCESS != result) {
244            MPL_LOGE("Unable to set Gyro Calibration\n");
245            return result;
246        }
247        result = inv_set_accel_calibration(accelScale, accelCal);
248        if (INV_SUCCESS != result) {
249            MPL_LOGE("Unable to set Accel Calibration\n");
250            return result;
251        }
252        result = inv_set_compass_calibration(magScale, magCal);
253        if (INV_SUCCESS != result) {
254            MPL_LOGE("Unable to set Mag Calibration\n");
255            return result;
256        }
257    }
258    return INV_SUCCESS;
259}
260
261/**
262 *  @brief  Setup the DMP to handle the accelerometer endianess.
263 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
264 */
265inv_error_t inv_apply_endian_accel(void)
266{
267    INVENSENSE_FUNC_START;
268    unsigned char regs[4] = { 0 };
269    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
270    int endian = mldl_cfg->accel->endian;
271
272    if (mldl_cfg->pdata->accel.bus != EXT_SLAVE_BUS_SECONDARY) {
273        endian = EXT_SLAVE_BIG_ENDIAN;
274    }
275    switch (endian) {
276    case EXT_SLAVE_FS8_BIG_ENDIAN:
277    case EXT_SLAVE_FS16_BIG_ENDIAN:
278    case EXT_SLAVE_LITTLE_ENDIAN:
279        regs[0] = 0;
280        regs[1] = 64;
281        regs[2] = 0;
282        regs[3] = 0;
283        break;
284    case EXT_SLAVE_BIG_ENDIAN:
285    default:
286        regs[0] = 0;
287        regs[1] = 0;
288        regs[2] = 64;
289        regs[3] = 0;
290    }
291
292    return inv_set_mpu_memory(KEY_D_1_236, 4, regs);
293}
294
295/**
296 * @internal
297 * @brief   Initialize MLX data.  This should be called to setup the mlx
298 *          output buffers before any motion processing is done.
299 */
300void inv_init_ml(void)
301{
302    INVENSENSE_FUNC_START;
303    int ii;
304    long tmp[COMPASS_NUM_AXES];
305    // Set all values to zero by default
306    memset(&inv_obj, 0, sizeof(struct inv_obj_t));
307    memset(&mlxCallbackInterrupt, 0, sizeof(tMLXCallbackInterrupt));
308
309    inv_obj.compass_correction[0] = 1073741824L;
310    inv_obj.compass_correction_relative[0] = 1073741824L;
311    inv_obj.compass_disturb_correction[0] = 1073741824L;
312    inv_obj.compass_correction_offset[0] = 1073741824L;
313    inv_obj.relative_quat[0] = 1073741824L;
314
315    //Not used with the ST accelerometer
316    inv_obj.no_motion_threshold = 20;   // noMotionThreshold
317    //Not used with the ST accelerometer
318    inv_obj.motion_duration = 1536; // motionDuration
319
320    inv_obj.motion_state = INV_MOTION;  // Motion state
321
322    inv_obj.bias_update_time = 8000;
323    inv_obj.bias_calc_time = 2000;
324
325    inv_obj.internal_motion_state = ML_MOT_STATE_MOVING;
326
327    inv_obj.start_time = inv_get_tick_count();
328
329    inv_obj.compass_cal[0] = 322122560L;
330    inv_obj.compass_cal[4] = 322122560L;
331    inv_obj.compass_cal[8] = 322122560L;
332    inv_obj.compass_sens = 322122560L;  // Should only change when the sensor full-scale range (FSR) is changed.
333
334    for (ii = 0; ii < COMPASS_NUM_AXES; ii++) {
335        inv_obj.compass_scale[ii] = 65536L;
336        inv_obj.compass_test_scale[ii] = 65536L;
337        inv_obj.compass_bias_error[ii] = P_INIT;
338        inv_obj.init_compass_bias[ii] = 0;
339        inv_obj.compass_asa[ii] = (1L << 30);
340    }
341    if (inv_compass_read_scale(tmp) == INV_SUCCESS) {
342        for (ii = 0; ii < COMPASS_NUM_AXES; ii++)
343            inv_obj.compass_asa[ii] = tmp[ii];
344    }
345    inv_obj.got_no_motion_bias = 0;
346    inv_obj.got_compass_bias = 0;
347    inv_obj.compass_state = SF_UNCALIBRATED;
348    inv_obj.acc_state = SF_STARTUP_SETTLE;
349
350    inv_obj.got_init_compass_bias = 0;
351    inv_obj.resetting_compass = 0;
352
353    inv_obj.external_slave_callback = NULL;
354    inv_obj.compass_accuracy = 0;
355
356    inv_obj.factory_temp_comp = 0;
357    inv_obj.got_coarse_heading = 0;
358
359    inv_obj.compass_bias_ptr[0] = P_INIT;
360    inv_obj.compass_bias_ptr[4] = P_INIT;
361    inv_obj.compass_bias_ptr[8] = P_INIT;
362
363    inv_obj.gyro_bias_err = 1310720;
364
365    inv_obj.accel_lpf_gain = 1073744L;
366    inv_obj.no_motion_accel_threshold = 7000000L;
367}
368
369/**
370 * @internal
371 * @brief   This registers a function to be called for each time the DMP
372 *          generates an an interrupt.
373 *          It will be called after inv_register_fifo_rate_process() which gets called
374 *          every time the FIFO data is processed.
375 *          The FIFO does not have to be on for this callback.
376 * @param func Function to be called when a DMP interrupt occurs.
377 * @return INV_SUCCESS or non-zero error code.
378 */
379inv_error_t inv_register_dmp_interupt_cb(inv_obj_func func)
380{
381    INVENSENSE_FUNC_START;
382    // Make sure we have not filled up our number of allowable callbacks
383    if (mlxCallbackInterrupt.numInterruptProcesses <=
384        MAX_INTERRUPT_PROCESSES - 1) {
385        int kk;
386        // Make sure we haven't registered this function already
387        for (kk = 0; kk < mlxCallbackInterrupt.numInterruptProcesses; ++kk) {
388            if (mlxCallbackInterrupt.processInterruptCb[kk] == func) {
389                return INV_ERROR_INVALID_PARAMETER;
390            }
391        }
392        // Add new callback
393        mlxCallbackInterrupt.processInterruptCb[mlxCallbackInterrupt.
394                                                numInterruptProcesses] = func;
395        mlxCallbackInterrupt.numInterruptProcesses++;
396        return INV_SUCCESS;
397    }
398    return INV_ERROR_MEMORY_EXAUSTED;
399}
400
401/**
402 * @internal
403 * @brief This unregisters a function to be called for each DMP interrupt.
404 * @return INV_SUCCESS or non-zero error code.
405 */
406inv_error_t inv_unregister_dmp_interupt_cb(inv_obj_func func)
407{
408    INVENSENSE_FUNC_START;
409    int kk, jj;
410    // Make sure we haven't registered this function already
411    for (kk = 0; kk < mlxCallbackInterrupt.numInterruptProcesses; ++kk) {
412        if (mlxCallbackInterrupt.processInterruptCb[kk] == func) {
413            // FIXME, we may need a thread block here
414            for (jj = kk + 1; jj < mlxCallbackInterrupt.numInterruptProcesses;
415                 ++jj) {
416                mlxCallbackInterrupt.processInterruptCb[jj - 1] =
417                    mlxCallbackInterrupt.processInterruptCb[jj];
418            }
419            mlxCallbackInterrupt.numInterruptProcesses--;
420            return INV_SUCCESS;
421        }
422    }
423    return INV_ERROR_INVALID_PARAMETER;
424}
425
426/**
427 *  @internal
428 *  @brief  Run the recorded interrupt process callbacks in the event
429 *          of an interrupt.
430 */
431void inv_run_dmp_interupt_cb(void)
432{
433    int kk;
434    for (kk = 0; kk < mlxCallbackInterrupt.numInterruptProcesses; ++kk) {
435        if (mlxCallbackInterrupt.processInterruptCb[kk])
436            mlxCallbackInterrupt.processInterruptCb[kk] (&inv_obj);
437    }
438}
439
440/** @internal
441* Resets the Motion/No Motion state which should be called at startup and resume.
442*/
443inv_error_t inv_reset_motion(void)
444{
445    unsigned char regs[8];
446    inv_error_t result;
447
448    inv_obj.motion_state = INV_MOTION;
449    inv_obj.flags[INV_MOTION_STATE_CHANGE] = INV_MOTION;
450    inv_obj.no_motion_accel_time = inv_get_tick_count();
451#if defined CONFIG_MPU_SENSORS_MPU3050
452    regs[0] = DINAD8 + 2;
453    regs[1] = DINA0C;
454    regs[2] = DINAD8 + 1;
455    result = inv_set_mpu_memory(KEY_CFG_18, 3, regs);
456    if (result) {
457        LOG_RESULT_LOCATION(result);
458        return result;
459    }
460#else
461#endif
462    regs[0] = (unsigned char)((inv_obj.motion_duration >> 8) & 0xff);
463    regs[1] = (unsigned char)(inv_obj.motion_duration & 0xff);
464    result = inv_set_mpu_memory(KEY_D_1_106, 2, regs);
465    if (result) {
466        LOG_RESULT_LOCATION(result);
467        return result;
468    }
469    memset(regs, 0, 8);
470    result = inv_set_mpu_memory(KEY_D_1_96, 8, regs);
471    if (result) {
472        LOG_RESULT_LOCATION(result);
473        return result;
474    }
475
476    result =
477        inv_set_mpu_memory(KEY_D_0_96, 4, inv_int32_to_big8(0x40000000, regs));
478    if (result) {
479        LOG_RESULT_LOCATION(result);
480        return result;
481    }
482
483    inv_set_motion_state(INV_MOTION);
484    return result;
485}
486
487/**
488 * @internal
489 * @brief   inv_start_bias_calc starts the bias calculation on the MPU.
490 */
491void inv_start_bias_calc(void)
492{
493    INVENSENSE_FUNC_START;
494
495    inv_obj.suspend = 1;
496}
497
498/**
499 * @internal
500 * @brief   inv_stop_bias_calc stops the bias calculation on the MPU.
501 */
502void inv_stop_bias_calc(void)
503{
504    INVENSENSE_FUNC_START;
505
506    inv_obj.suspend = 0;
507}
508
509/**
510 *  @brief  inv_update_data fetches data from the fifo and updates the
511 *          motion algorithms.
512 *
513 *  @pre    inv_dmp_open()
514 *          @ifnot MPL_MF
515 *              or inv_open_low_power_pedometer()
516 *              or inv_eis_open_dmp()
517 *          @endif
518 *          and inv_dmp_start() must have been called.
519 *
520 *  @note   Motion algorithm data is constant between calls to inv_update_data
521 *
522 * @return
523 * - INV_SUCCESS
524 * - Non-zero error code
525 */
526inv_error_t inv_update_data(void)
527{
528    INVENSENSE_FUNC_START;
529    inv_error_t result = INV_SUCCESS;
530    int_fast8_t got, ftry;
531    uint_fast8_t mpu_interrupt;
532    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
533
534    if (inv_get_state() != INV_STATE_DMP_STARTED)
535        return INV_ERROR_SM_IMPROPER_STATE;
536
537    // Set the maximum number of FIFO packets we want to process
538    if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) {
539        ftry = 100;             // Large enough to process all packets
540    } else {
541        ftry = 1;
542    }
543
544    // Go and process at most ftry number of packets, probably less than ftry
545    result = inv_read_and_process_fifo(ftry, &got);
546    if (result) {
547        LOG_RESULT_LOCATION(result);
548        return result;
549    }
550
551    // Process all interrupts
552    mpu_interrupt = inv_get_interrupt_trigger(INTSRC_AUX1);
553    if (mpu_interrupt) {
554        inv_clear_interrupt_trigger(INTSRC_AUX1);
555    }
556    // Check if interrupt was from MPU
557    mpu_interrupt = inv_get_interrupt_trigger(INTSRC_MPU);
558    if (mpu_interrupt) {
559        inv_clear_interrupt_trigger(INTSRC_MPU);
560    }
561    // Take care of the callbacks that want to be notified when there was an MPU interrupt
562    if (mpu_interrupt) {
563        inv_run_dmp_interupt_cb();
564    }
565
566    result = inv_get_fifo_status();
567    return result;
568}
569
570/**
571 *  @brief  inv_check_flag returns the value of a flag.
572 *          inv_check_flag can be used to check a number of flags,
573 *          allowing users to poll flags rather than register callback
574 *          functions. If a flag is set to True when inv_check_flag is called,
575 *          the flag is automatically reset.
576 *          The flags are:
577 *          - INV_RAW_DATA_READY
578 *          Indicates that new raw data is available.
579 *          - INV_PROCESSED_DATA_READY
580 *          Indicates that new processed data is available.
581 *          - INV_GOT_GESTURE
582 *          Indicates that a gesture has been detected by the gesture engine.
583 *          - INV_MOTION_STATE_CHANGE
584 *          Indicates that a change has been made from motion to no motion,
585 *          or vice versa.
586 *
587 *  @pre    inv_dmp_open()
588 *          @ifnot MPL_MF
589 *              or inv_open_low_power_pedometer()
590 *              or inv_eis_open_dmp()
591 *          @endif
592 *          and inv_dmp_start() must have been called.
593 *
594 *  @param flag The flag to check.
595 *
596 * @return TRUE or FALSE state of the flag
597 */
598int inv_check_flag(int flag)
599{
600    INVENSENSE_FUNC_START;
601    int flagReturn = inv_obj.flags[flag];
602
603    inv_obj.flags[flag] = 0;
604    return flagReturn;
605}
606
607/**
608 *  @brief  Enable generation of the DMP interrupt when Motion or no-motion
609 *          is detected
610 *  @param on
611 *          Boolean to turn the interrupt on or off.
612 *  @return INV_SUCCESS or non-zero error code.
613 */
614inv_error_t inv_set_motion_interrupt(unsigned char on)
615{
616    INVENSENSE_FUNC_START;
617    inv_error_t result;
618    unsigned char regs[2] = { 0 };
619
620    if (inv_get_state() < INV_STATE_DMP_OPENED)
621        return INV_ERROR_SM_IMPROPER_STATE;
622
623    if (on) {
624        result = inv_get_dl_cfg_int(BIT_DMP_INT_EN);
625        if (result) {
626            LOG_RESULT_LOCATION(result);
627            return result;
628        }
629        inv_obj.interrupt_sources |= INV_INT_MOTION;
630    } else {
631        inv_obj.interrupt_sources &= ~INV_INT_MOTION;
632        if (!inv_obj.interrupt_sources) {
633            result = inv_get_dl_cfg_int(0);
634            if (result) {
635                LOG_RESULT_LOCATION(result);
636                return result;
637            }
638        }
639    }
640
641    if (on) {
642        regs[0] = DINAFE;
643    } else {
644        regs[0] = DINAD8;
645    }
646    result = inv_set_mpu_memory(KEY_CFG_7, 1, regs);
647    if (result) {
648        LOG_RESULT_LOCATION(result);
649        return result;
650    }
651    return result;
652}
653
654/**
655 * Enable generation of the DMP interrupt when a FIFO packet is ready
656 *
657 * @param on Boolean to turn the interrupt on or off
658 *
659 * @return INV_SUCCESS or non-zero error code
660 */
661inv_error_t inv_set_fifo_interrupt(unsigned char on)
662{
663    INVENSENSE_FUNC_START;
664    inv_error_t result;
665    unsigned char regs[2] = { 0 };
666
667    if (inv_get_state() < INV_STATE_DMP_OPENED)
668        return INV_ERROR_SM_IMPROPER_STATE;
669
670    if (on) {
671        result = inv_get_dl_cfg_int(BIT_DMP_INT_EN);
672        if (result) {
673            LOG_RESULT_LOCATION(result);
674            return result;
675        }
676        inv_obj.interrupt_sources |= INV_INT_FIFO;
677    } else {
678        inv_obj.interrupt_sources &= ~INV_INT_FIFO;
679        if (!inv_obj.interrupt_sources) {
680            result = inv_get_dl_cfg_int(0);
681            if (result) {
682                LOG_RESULT_LOCATION(result);
683                return result;
684            }
685        }
686    }
687
688    if (on) {
689        regs[0] = DINAFE;
690    } else {
691        regs[0] = DINAD8;
692    }
693    result = inv_set_mpu_memory(KEY_CFG_6, 1, regs);
694    if (result) {
695        LOG_RESULT_LOCATION(result);
696        return result;
697    }
698    return result;
699}
700
701/**
702 * @brief   Get the current set of DMP interrupt sources.
703 *          These interrupts are generated by the DMP and can be
704 *          routed to the MPU interrupt line via internal
705 *          settings.
706 *
707 *  @pre    inv_dmp_open()
708 *          @ifnot MPL_MF
709 *              or inv_open_low_power_pedometer()
710 *              or inv_eis_open_dmp()
711 *          @endif
712 *          must have been called.
713 *
714 * @return  Currently enabled interrupt sources.  The possible interrupts are:
715 *          - INV_INT_FIFO,
716 *          - INV_INT_MOTION,
717 *          - INV_INT_TAP
718 */
719int inv_get_interrupts(void)
720{
721    INVENSENSE_FUNC_START;
722
723    if (inv_get_state() < INV_STATE_DMP_OPENED)
724        return 0;               // error
725
726    return inv_obj.interrupt_sources;
727}
728
729/**
730 *  @brief  Sets up the Accelerometer calibration and scale factor.
731 *
732 *          Please refer to the provided "9-Axis Sensor Fusion Application
733 *          Note" document provided.  Section 5, "Sensor Mounting Orientation"
734 *          offers a good coverage on the mounting matrices and explains how
735 *          to use them.
736 *
737 *  @pre    inv_dmp_open()
738 *          @ifnot MPL_MF
739 *              or inv_open_low_power_pedometer()
740 *              or inv_eis_open_dmp()
741 *          @endif
742 *          must have been called.
743 *  @pre    inv_dmp_start() must <b>NOT</b> have been called.
744 *
745 *  @see    inv_set_gyro_calibration().
746 *  @see    inv_set_compass_calibration().
747 *
748 *  @param[in]  range
749 *                  The range of the accelerometers in g's. An accelerometer
750 *                  that has a range of +2g's to -2g's should pass in 2.
751 *  @param[in]  orientation
752 *                  A 9 element matrix that represents how the accelerometers
753 *                  are oriented with respect to the device they are mounted
754 *                  in and the reference axis system.
755 *                  A typical set of values are {1, 0, 0, 0, 1, 0, 0, 0, 1}.
756 *                  This example corresponds to a 3 x 3 identity matrix.
757 *
758 *  @return INV_SUCCESS if successful; a non-zero error code otherwise.
759 */
760inv_error_t inv_set_accel_calibration(float range, signed char *orientation)
761{
762    INVENSENSE_FUNC_START;
763    float cal[9];
764    float scale = range / 32768.f;
765    int kk;
766    unsigned long sf;
767    inv_error_t result;
768    unsigned char regs[4] = { 0, 0, 0, 0 };
769    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
770
771    if (inv_get_state() != INV_STATE_DMP_OPENED)
772        return INV_ERROR_SM_IMPROPER_STATE;
773
774    /* Apply zero g-offset values */
775    if (ACCEL_ID_KXSD9 == mldl_cfg->accel->id) {
776        regs[0] = 0x80;
777        regs[1] = 0x0;
778        regs[2] = 0x80;
779        regs[3] = 0x0;
780    }
781
782    if (inv_dmpkey_supported(KEY_D_1_152)) {
783        result = inv_set_mpu_memory(KEY_D_1_152, 4, &regs[0]);
784        if (result) {
785            LOG_RESULT_LOCATION(result);
786            return result;
787        }
788    }
789
790    if (scale == 0) {
791        inv_obj.accel_sens = 0;
792    }
793
794    if (mldl_cfg->accel->id) {
795#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
796    defined CONFIG_MPU_SENSORS_MPU6050B1
797        unsigned char tmp[3] = { DINA0C, DINAC9, DINA2C };
798#else
799        unsigned char tmp[3] = { DINA4C, DINACD, DINA6C };
800#endif
801        struct mldl_cfg *mldl_cfg = inv_get_dl_config();
802        unsigned char regs[3];
803        unsigned short orient;
804
805        for (kk = 0; kk < 9; ++kk) {
806            cal[kk] = scale * orientation[kk];
807            inv_obj.accel_cal[kk] = (long)(cal[kk] * (float)(1L << 30));
808        }
809
810        orient = inv_orientation_matrix_to_scalar(orientation);
811        regs[0] = tmp[orient & 3];
812        regs[1] = tmp[(orient >> 3) & 3];
813        regs[2] = tmp[(orient >> 6) & 3];
814        result = inv_set_mpu_memory(KEY_FCFG_2, 3, regs);
815        if (result) {
816            LOG_RESULT_LOCATION(result);
817            return result;
818        }
819
820        regs[0] = DINA26;
821        regs[1] = DINA46;
822#if defined CONFIG_MPU_SENSORS_MPU3050 || defined CONFIG_MPU_SENSORS_MPU6050A2
823        regs[2] = DINA66;
824#else
825        if (MPL_PROD_KEY(mldl_cfg->product_id, mldl_cfg->product_revision)
826                == MPU_PRODUCT_KEY_B1_E1_5)
827            regs[2] = DINA76;
828        else
829            regs[2] = DINA66;
830#endif
831        if (orient & 4)
832            regs[0] |= 1;
833        if (orient & 0x20)
834            regs[1] |= 1;
835        if (orient & 0x100)
836            regs[2] |= 1;
837
838        result = inv_set_mpu_memory(KEY_FCFG_7, 3, regs);
839        if (result) {
840            LOG_RESULT_LOCATION(result);
841            return result;
842        }
843
844        if (mldl_cfg->accel->id == ACCEL_ID_MMA845X) {
845            result = inv_freescale_sensor_fusion_16bit(orient);
846            if (result) {
847                LOG_RESULT_LOCATION(result);
848                return result;
849            }
850        } else if (mldl_cfg->accel->id == ACCEL_ID_MMA8450) {
851            result = inv_freescale_sensor_fusion_8bit(orient);
852            if (result) {
853                LOG_RESULT_LOCATION(result);
854                return result;
855            }
856        }
857    }
858
859    if (inv_obj.accel_sens != 0) {
860        sf = (1073741824L / inv_obj.accel_sens);
861    } else {
862        sf = 0;
863    }
864    regs[0] = (unsigned char)((sf >> 8) & 0xff);
865    regs[1] = (unsigned char)(sf & 0xff);
866    result = inv_set_mpu_memory(KEY_D_0_108, 2, regs);
867    if (result) {
868        LOG_RESULT_LOCATION(result);
869        return result;
870    }
871
872    return result;
873}
874
875/**
876 *  @brief  Sets up the Gyro calibration and scale factor.
877 *
878 *          Please refer to the provided "9-Axis Sensor Fusion Application
879 *          Note" document provided.  Section 5, "Sensor Mounting Orientation"
880 *          offers a good coverage on the mounting matrices and explains
881 *          how to use them.
882 *
883 *  @pre    inv_dmp_open()
884 *          @ifnot MPL_MF
885 *              or inv_open_low_power_pedometer()
886 *              or inv_eis_open_dmp()
887 *          @endif
888 *          must have been called.
889 *  @pre    inv_dmp_start() must have <b>NOT</b> been called.
890 *
891 *  @see    inv_set_accel_calibration().
892 *  @see    inv_set_compass_calibration().
893 *
894 *  @param[in]  range
895 *                  The range of the gyros in degrees per second. A gyro
896 *                  that has a range of +2000 dps to -2000 dps should pass in
897 *                  2000.
898 *  @param[in] orientation
899 *                  A 9 element matrix that represents how the gyro are oriented
900 *                  with respect to the device they are mounted in. A typical
901 *                  set of values are {1, 0, 0, 0, 1, 0, 0, 0, 1}. This
902 *                  example corresponds to a 3 x 3 identity matrix.
903 *
904 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
905 */
906inv_error_t inv_set_gyro_calibration(float range, signed char *orientation)
907{
908    INVENSENSE_FUNC_START;
909
910    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
911    int kk;
912    float scale;
913    inv_error_t result;
914
915    unsigned char regs[12] = { 0 };
916    unsigned char maxVal = 0;
917    unsigned char tmpPtr = 0;
918    unsigned char tmpSign = 0;
919    unsigned char i = 0;
920    int sf = 0;
921
922    if (inv_get_state() != INV_STATE_DMP_OPENED)
923        return INV_ERROR_SM_IMPROPER_STATE;
924
925    if (mldl_cfg->gyro_sens_trim != 0) {
926        /* adjust the declared range to consider the gyro_sens_trim
927           of this part when different from the default (first dividend) */
928        range *= (32768.f / 250) / mldl_cfg->gyro_sens_trim;
929    }
930
931    scale = range / 32768.f;    // inverse of sensitivity for the given full scale range
932    inv_obj.gyro_sens = (long)(range * 32768L);
933
934    for (kk = 0; kk < 9; ++kk) {
935        inv_obj.gyro_cal[kk] = (long)(scale * orientation[kk] * (1L << 30));
936        inv_obj.gyro_orient[kk] = (long)((double)orientation[kk] * (1L << 30));
937    }
938
939    {
940#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
941    defined CONFIG_MPU_SENSORS_MPU6050B1
942        unsigned char tmpD = DINA4C;
943        unsigned char tmpE = DINACD;
944        unsigned char tmpF = DINA6C;
945#else
946        unsigned char tmpD = DINAC9;
947        unsigned char tmpE = DINA2C;
948        unsigned char tmpF = DINACB;
949#endif
950        regs[3] = DINA36;
951        regs[4] = DINA56;
952        regs[5] = DINA76;
953
954        for (i = 0; i < 3; i++) {
955            maxVal = 0;
956            tmpSign = 0;
957            if (inv_obj.gyro_orient[0 + 3 * i] < 0)
958                tmpSign = 1;
959            if (ABS(inv_obj.gyro_orient[1 + 3 * i]) >
960                ABS(inv_obj.gyro_orient[0 + 3 * i])) {
961                maxVal = 1;
962                if (inv_obj.gyro_orient[1 + 3 * i] < 0)
963                    tmpSign = 1;
964            }
965            if (ABS(inv_obj.gyro_orient[2 + 3 * i]) >
966                ABS(inv_obj.gyro_orient[1 + 3 * i])) {
967                tmpSign = 0;
968                maxVal = 2;
969                if (inv_obj.gyro_orient[2 + 3 * i] < 0)
970                    tmpSign = 1;
971            }
972            if (maxVal == 0) {
973                regs[tmpPtr++] = tmpD;
974                if (tmpSign)
975                    regs[tmpPtr + 2] |= 0x01;
976            } else if (maxVal == 1) {
977                regs[tmpPtr++] = tmpE;
978                if (tmpSign)
979                    regs[tmpPtr + 2] |= 0x01;
980            } else {
981                regs[tmpPtr++] = tmpF;
982                if (tmpSign)
983                    regs[tmpPtr + 2] |= 0x01;
984            }
985        }
986        result = inv_set_mpu_memory(KEY_FCFG_1, 3, regs);
987        if (result) {
988            LOG_RESULT_LOCATION(result);
989            return result;
990        }
991        result = inv_set_mpu_memory(KEY_FCFG_3, 3, &regs[3]);
992        if (result) {
993            LOG_RESULT_LOCATION(result);
994            return result;
995        }
996
997        //sf = (gyroSens) * (0.5 * (pi/180) / 200.0) * 16384
998        inv_obj.gyro_sf =
999            (long)(((long long)inv_obj.gyro_sens * 767603923LL) / 1073741824LL);
1000        result =
1001            inv_set_mpu_memory(KEY_D_0_104, 4,
1002                               inv_int32_to_big8(inv_obj.gyro_sf, regs));
1003        if (result) {
1004            LOG_RESULT_LOCATION(result);
1005            return result;
1006        }
1007
1008        if (inv_obj.gyro_sens != 0) {
1009            sf = (long)((long long)23832619764371LL / inv_obj.gyro_sens);
1010        } else {
1011            sf = 0;
1012        }
1013
1014        result = inv_set_mpu_memory(KEY_D_0_24, 4, inv_int32_to_big8(sf, regs));
1015        if (result) {
1016            LOG_RESULT_LOCATION(result);
1017            return result;
1018        }
1019    }
1020    return INV_SUCCESS;
1021}
1022
1023/**
1024 *  @brief  Sets up the Compass calibration and scale factor.
1025 *
1026 *          Please refer to the provided "9-Axis Sensor Fusion Application
1027 *          Note" document provided.  Section 5, "Sensor Mounting Orientation"
1028 *          offers a good coverage on the mounting matrices and explains
1029 *          how to use them.
1030 *
1031 *  @pre    inv_dmp_open()
1032 *          @ifnot MPL_MF
1033 *              or inv_open_low_power_pedometer()
1034 *              or inv_eis_open_dmp()
1035 *          @endif
1036 *          must have been called.
1037 *  @pre    inv_dmp_start() must have <b>NOT</b> been called.
1038 *
1039 *  @see    inv_set_gyro_calibration().
1040 *  @see    inv_set_accel_calibration().
1041 *
1042 *  @param[in] range
1043 *                  The range of the compass.
1044 *  @param[in] orientation
1045 *                  A 9 element matrix that represents how the compass is
1046 *                  oriented with respect to the device they are mounted in.
1047 *                  A typical set of values are {1, 0, 0, 0, 1, 0, 0, 0, 1}.
1048 *                  This example corresponds to a 3 x 3 identity matrix.
1049 *                  The matrix describes how to go from the chip mounting to
1050 *                  the body of the device.
1051 *
1052 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
1053 */
1054inv_error_t inv_set_compass_calibration(float range, signed char *orientation)
1055{
1056    INVENSENSE_FUNC_START;
1057    float cal[9];
1058    float scale = range / 32768.f;
1059    int kk;
1060    unsigned short compassId = 0;
1061
1062    compassId = inv_get_compass_id();
1063
1064    if ((compassId == COMPASS_ID_YAS529) || (compassId == COMPASS_ID_HMC5883)
1065        || (compassId == COMPASS_ID_LSM303M)) {
1066        scale /= 32.0f;
1067    }
1068
1069    for (kk = 0; kk < 9; ++kk) {
1070        cal[kk] = scale * orientation[kk];
1071        inv_obj.compass_cal[kk] = (long)(cal[kk] * (float)(1L << 30));
1072    }
1073
1074    inv_obj.compass_sens = (long)(scale * 1073741824L);
1075
1076    if (inv_dmpkey_supported(KEY_CPASS_MTX_00)) {
1077        unsigned char reg0[4] = { 0, 0, 0, 0 };
1078        unsigned char regp[4] = { 64, 0, 0, 0 };
1079        unsigned char regn[4] = { 64 + 128, 0, 0, 0 };
1080        unsigned char *reg;
1081        int_fast8_t kk;
1082        unsigned short keyList[9] =
1083            { KEY_CPASS_MTX_00, KEY_CPASS_MTX_01, KEY_CPASS_MTX_02,
1084            KEY_CPASS_MTX_10, KEY_CPASS_MTX_11, KEY_CPASS_MTX_12,
1085            KEY_CPASS_MTX_20, KEY_CPASS_MTX_21, KEY_CPASS_MTX_22
1086        };
1087
1088        for (kk = 0; kk < 9; ++kk) {
1089
1090            if (orientation[kk] == 1)
1091                reg = regp;
1092            else if (orientation[kk] == -1)
1093                reg = regn;
1094            else
1095                reg = reg0;
1096            inv_set_mpu_memory(keyList[kk], 4, reg);
1097        }
1098    }
1099
1100    return INV_SUCCESS;
1101}
1102
1103/**
1104* @internal
1105* @brief Sets the Gyro Dead Zone based upon LPF filter settings and Control setup.
1106*/
1107inv_error_t inv_set_dead_zone(void)
1108{
1109    unsigned char reg;
1110    inv_error_t result;
1111    extern struct control_params cntrl_params;
1112
1113    if (cntrl_params.functions & INV_DEAD_ZONE) {
1114        reg = 0x08;
1115    } else {
1116#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
1117    defined CONFIG_MPU_SENSORS_MPU6050B1
1118        reg = 0;
1119#else
1120        if (inv_params_obj.bias_mode & INV_BIAS_FROM_LPF) {
1121            reg = 0x2;
1122        } else {
1123            reg = 0;
1124        }
1125#endif
1126    }
1127
1128    result = inv_set_mpu_memory(KEY_D_0_163, 1, &reg);
1129    if (result) {
1130        LOG_RESULT_LOCATION(result);
1131        return result;
1132    }
1133    return result;
1134}
1135
1136/**
1137 *  @brief  inv_set_bias_update is used to register which algorithms will be
1138 *          used to automatically reset the gyroscope bias.
1139 *          The engine INV_BIAS_UPDATE must be enabled for these algorithms to
1140 *          run.
1141 *
1142 *  @pre    inv_dmp_open()
1143 *          @ifnot MPL_MF
1144 *              or inv_open_low_power_pedometer()
1145 *              or inv_eis_open_dmp()
1146 *          @endif
1147 *          and inv_dmp_start()
1148 *          must <b>NOT</b> have been called.
1149 *
1150 *  @param  function    A function or bitwise OR of functions that determine
1151 *                      how the gyroscope bias will be automatically updated.
1152 *                      Functions include:
1153 *                      - INV_NONE or 0,
1154 *                      - INV_BIAS_FROM_NO_MOTION,
1155 *                      - INV_BIAS_FROM_GRAVITY,
1156 *                      - INV_BIAS_FROM_TEMPERATURE,
1157                    \ifnot UMPL
1158 *                      - INV_BIAS_FROM_LPF,
1159 *                      - INV_MAG_BIAS_FROM_MOTION,
1160 *                      - INV_MAG_BIAS_FROM_GYRO,
1161 *                      - INV_ALL.
1162 *                   \endif
1163 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
1164 */
1165inv_error_t inv_set_bias_update(unsigned short function)
1166{
1167    INVENSENSE_FUNC_START;
1168    unsigned char regs[4];
1169    long tmp[3] = { 0, 0, 0 };
1170    inv_error_t result = INV_SUCCESS;
1171    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
1172
1173    if (inv_get_state() != INV_STATE_DMP_OPENED)
1174        return INV_ERROR_SM_IMPROPER_STATE;
1175
1176    /* do not allow progressive no motion bias tracker to run -
1177       it's not fully debugged */
1178    function &= ~INV_PROGRESSIVE_NO_MOTION; // FIXME, workaround
1179    MPL_LOGV("forcing disable of PROGRESSIVE_NO_MOTION bias tracker\n");
1180
1181    // You must use EnableFastNoMotion() to get this feature
1182    function &= ~INV_BIAS_FROM_FAST_NO_MOTION;
1183
1184    // You must use DisableFastNoMotion() to turn this feature off
1185    if (inv_params_obj.bias_mode & INV_BIAS_FROM_FAST_NO_MOTION)
1186        function |= INV_BIAS_FROM_FAST_NO_MOTION;
1187
1188    /*--- remove magnetic components from bias tracking
1189          if there is no compass ---*/
1190    if (!inv_compass_present()) {
1191        function &= ~(INV_MAG_BIAS_FROM_GYRO | INV_MAG_BIAS_FROM_MOTION);
1192    } else {
1193        function &= ~(INV_BIAS_FROM_LPF);
1194    }
1195
1196    // Enable function sets bias from no motion
1197    inv_params_obj.bias_mode = function & (~INV_BIAS_FROM_NO_MOTION);
1198
1199    if (function & INV_BIAS_FROM_NO_MOTION) {
1200        inv_enable_bias_no_motion();
1201    } else {
1202        inv_disable_bias_no_motion();
1203    }
1204
1205    if (inv_params_obj.bias_mode & INV_BIAS_FROM_LPF) {
1206        regs[0] = DINA80 + 2;
1207        regs[1] = DINA2D;
1208        regs[2] = DINA55;
1209        regs[3] = DINA7D;
1210    } else {
1211        regs[0] = DINA80 + 7;
1212        regs[1] = DINA2D;
1213        regs[2] = DINA35;
1214        regs[3] = DINA3D;
1215    }
1216    result = inv_set_mpu_memory(KEY_FCFG_5, 4, regs);
1217    if (result) {
1218        LOG_RESULT_LOCATION(result);
1219        return result;
1220    }
1221    result = inv_set_dead_zone();
1222    if (result) {
1223        LOG_RESULT_LOCATION(result);
1224        return result;
1225    }
1226
1227    if ((inv_params_obj.bias_mode & INV_BIAS_FROM_GRAVITY) &&
1228        !inv_compass_present()) {
1229        result = inv_set_gyro_data_source(INV_GYRO_FROM_QUATERNION);
1230        if (result) {
1231            LOG_RESULT_LOCATION(result);
1232            return result;
1233        }
1234    } else {
1235        result = inv_set_gyro_data_source(INV_GYRO_FROM_RAW);
1236        if (result) {
1237            LOG_RESULT_LOCATION(result);
1238            return result;
1239        }
1240    }
1241
1242    inv_obj.factory_temp_comp = 0;  // FIXME, workaround
1243    if ((mldl_cfg->offset_tc[0] != 0) ||
1244        (mldl_cfg->offset_tc[1] != 0) || (mldl_cfg->offset_tc[2] != 0)) {
1245        inv_obj.factory_temp_comp = 1;
1246    }
1247
1248    if (inv_obj.factory_temp_comp == 0) {
1249        if (inv_params_obj.bias_mode & INV_BIAS_FROM_TEMPERATURE) {
1250            result = inv_set_gyro_temp_slope(inv_obj.temp_slope);
1251            if (result) {
1252                LOG_RESULT_LOCATION(result);
1253                return result;
1254            }
1255        } else {
1256            result = inv_set_gyro_temp_slope(tmp);
1257            if (result) {
1258                LOG_RESULT_LOCATION(result);
1259                return result;
1260            }
1261        }
1262    } else {
1263        inv_params_obj.bias_mode &= ~INV_LEARN_BIAS_FROM_TEMPERATURE;
1264        MPL_LOGV("factory temperature compensation coefficients available - "
1265                 "disabling INV_LEARN_BIAS_FROM_TEMPERATURE\n");
1266    }
1267
1268    /*---- hard requirement for using bias tracking BIAS_FROM_GRAVITY, relying on
1269           compass and accel data, is to have accelerometer data delivered in the
1270           FIFO ----*/
1271    if (((inv_params_obj.bias_mode & INV_BIAS_FROM_GRAVITY)
1272         && inv_compass_present())
1273        || (inv_params_obj.bias_mode & INV_MAG_BIAS_FROM_GYRO)
1274        || (inv_params_obj.bias_mode & INV_MAG_BIAS_FROM_MOTION)) {
1275        inv_send_accel(INV_ALL, INV_32_BIT);
1276        inv_send_gyro(INV_ALL, INV_32_BIT);
1277    }
1278
1279    return result;
1280}
1281
1282/**
1283 *  @brief  inv_get_motion_state is used to determine if the device is in
1284 *          a 'motion' or 'no motion' state.
1285 *          inv_get_motion_state returns INV_MOTION of the device is moving,
1286 *          or INV_NO_MOTION if the device is not moving.
1287 *
1288 *  @pre    inv_dmp_open()
1289 *          @ifnot MPL_MF
1290 *              or inv_open_low_power_pedometer()
1291 *              or inv_eis_open_dmp()
1292 *          @endif
1293 *          and inv_dmp_start()
1294 *          must have been called.
1295 *
1296 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
1297 */
1298int inv_get_motion_state(void)
1299{
1300    INVENSENSE_FUNC_START;
1301    return inv_obj.motion_state;
1302}
1303
1304/**
1305 *  @brief  inv_set_no_motion_thresh is used to set the threshold for
1306 *          detecting INV_NO_MOTION
1307 *
1308 *  @pre    inv_dmp_open()
1309 *          @ifnot MPL_MF
1310 *              or inv_open_low_power_pedometer()
1311 *              or inv_eis_open_dmp()
1312 *          @endif
1313 *          must have been called.
1314 *
1315 *  @param  thresh  A threshold scaled in degrees per second.
1316 *
1317 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
1318 */
1319inv_error_t inv_set_no_motion_thresh(float thresh)
1320{
1321    inv_error_t result = INV_SUCCESS;
1322    unsigned char regs[4] = { 0 };
1323    long tmp;
1324    INVENSENSE_FUNC_START;
1325
1326    tmp = (long)(thresh * thresh * 2.045f);
1327    if (tmp < 0) {
1328        return INV_ERROR;
1329    } else if (tmp > 8180000L) {
1330        return INV_ERROR;
1331    }
1332    inv_obj.no_motion_threshold = tmp;
1333
1334    regs[0] = (unsigned char)(tmp >> 24);
1335    regs[1] = (unsigned char)((tmp >> 16) & 0xff);
1336    regs[2] = (unsigned char)((tmp >> 8) & 0xff);
1337    regs[3] = (unsigned char)(tmp & 0xff);
1338
1339    result = inv_set_mpu_memory(KEY_D_1_108, 4, regs);
1340    if (result) {
1341        LOG_RESULT_LOCATION(result);
1342        return result;
1343    }
1344    result = inv_reset_motion();
1345    return result;
1346}
1347
1348/**
1349 *  @brief  inv_set_no_motion_threshAccel is used to set the threshold for
1350 *          detecting INV_NO_MOTION with accelerometers when Gyros have
1351 *          been turned off
1352 *
1353 *  @pre    inv_dmp_open()
1354 *          @ifnot MPL_MF
1355 *              or inv_open_low_power_pedometer()
1356 *              or inv_eis_open_dmp()
1357 *          @endif
1358 *          must have been called.
1359 *
1360 *  @param  thresh  A threshold in g's scaled by 2^32
1361 *
1362 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
1363 */
1364inv_error_t inv_set_no_motion_threshAccel(long thresh)
1365{
1366    INVENSENSE_FUNC_START;
1367
1368    inv_obj.no_motion_accel_threshold = thresh;
1369
1370    return INV_SUCCESS;
1371}
1372
1373/**
1374 *  @brief  inv_set_no_motion_time is used to set the time required for
1375 *          detecting INV_NO_MOTION
1376 *
1377 *  @pre    inv_dmp_open()
1378 *          @ifnot MPL_MF
1379 *              or inv_open_low_power_pedometer()
1380 *              or inv_eis_open_dmp()
1381 *          @endif
1382 *          must have been called.
1383 *
1384 *  @param  time    A time in seconds.
1385 *
1386 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
1387 */
1388inv_error_t inv_set_no_motion_time(float time)
1389{
1390    inv_error_t result = INV_SUCCESS;
1391    unsigned char regs[2] = { 0 };
1392    long tmp;
1393
1394    INVENSENSE_FUNC_START;
1395
1396    tmp = (long)(time * 200);
1397    if (tmp < 0) {
1398        return INV_ERROR;
1399    } else if (tmp > 65535L) {
1400        return INV_ERROR;
1401    }
1402    inv_obj.motion_duration = (unsigned short)tmp;
1403
1404    regs[0] = (unsigned char)((inv_obj.motion_duration >> 8) & 0xff);
1405    regs[1] = (unsigned char)(inv_obj.motion_duration & 0xff);
1406    result = inv_set_mpu_memory(KEY_D_1_106, 2, regs);
1407    if (result) {
1408        LOG_RESULT_LOCATION(result);
1409        return result;
1410    }
1411    result = inv_reset_motion();
1412    return result;
1413}
1414
1415/**
1416 *  @brief  inv_get_version is used to get the ML version.
1417 *
1418 *  @pre    inv_get_version can be called at any time.
1419 *
1420 *  @param  version     inv_get_version writes the ML version
1421 *                      string pointer to version.
1422 *
1423 *  @return INV_SUCCESS if successful or Non-zero error code otherwise.
1424 */
1425inv_error_t inv_get_version(unsigned char **version)
1426{
1427    INVENSENSE_FUNC_START;
1428
1429    *version = (unsigned char *)mlVer;  //fixme we are wiping const
1430
1431    return INV_SUCCESS;
1432}
1433
1434/**
1435 * @brief Check for the presence of the gyro sensor.
1436 *
1437 * This is not a physical check but a logical check and the value can change
1438 * dynamically based on calls to inv_set_mpu_sensors().
1439 *
1440 * @return  TRUE if the gyro is enabled FALSE otherwise.
1441 */
1442int inv_get_gyro_present(void)
1443{
1444    return inv_get_dl_config()->requested_sensors & (INV_X_GYRO | INV_Y_GYRO |
1445                                                     INV_Z_GYRO);
1446}
1447
1448static unsigned short inv_row_2_scale(const signed char *row)
1449{
1450    unsigned short b;
1451
1452    if (row[0] > 0)
1453        b = 0;
1454    else if (row[0] < 0)
1455        b = 4;
1456    else if (row[1] > 0)
1457        b = 1;
1458    else if (row[1] < 0)
1459        b = 5;
1460    else if (row[2] > 0)
1461        b = 2;
1462    else if (row[2] < 0)
1463        b = 6;
1464    else
1465        b = 7;                  // error
1466    return b;
1467}
1468
1469unsigned short inv_orientation_matrix_to_scalar(const signed char *mtx)
1470{
1471    unsigned short scalar;
1472    /*
1473       XYZ  010_001_000 Identity Matrix
1474       XZY  001_010_000
1475       YXZ  010_000_001
1476       YZX  000_010_001
1477       ZXY  001_000_010
1478       ZYX  000_001_010
1479     */
1480
1481    scalar = inv_row_2_scale(mtx);
1482    scalar |= inv_row_2_scale(mtx + 3) << 3;
1483    scalar |= inv_row_2_scale(mtx + 6) << 6;
1484
1485    return scalar;
1486}
1487
1488/* Setups up the Freescale 16-bit accel for Sensor Fusion
1489* @param[in] orient A scalar representation of the orientation
1490*  that describes how to go from the Chip Orientation
1491*  to the Board Orientation often times called the Body Orientation in Invensense Documentation.
1492*  inv_orientation_matrix_to_scalar() will turn the transformation matrix into this scalar.
1493*/
1494inv_error_t inv_freescale_sensor_fusion_16bit(unsigned short orient)
1495{
1496    unsigned char rr[3];
1497    inv_error_t result;
1498
1499    orient = orient & 0xdb;
1500    switch (orient) {
1501    default:
1502        // Typically 0x88
1503        rr[0] = DINACC;
1504        rr[1] = DINACF;
1505        rr[2] = DINA0E;
1506        break;
1507    case 0x50:
1508        rr[0] = DINACE;
1509        rr[1] = DINA0E;
1510        rr[2] = DINACD;
1511        break;
1512    case 0x81:
1513        rr[0] = DINACE;
1514        rr[1] = DINACB;
1515        rr[2] = DINA0E;
1516        break;
1517    case 0x11:
1518        rr[0] = DINACC;
1519        rr[1] = DINA0E;
1520        rr[2] = DINACB;
1521        break;
1522    case 0x42:
1523        rr[0] = DINA0A;
1524        rr[1] = DINACF;
1525        rr[2] = DINACB;
1526        break;
1527    case 0x0a:
1528        rr[0] = DINA0A;
1529        rr[1] = DINACB;
1530        rr[2] = DINACD;
1531        break;
1532    }
1533    result = inv_set_mpu_memory(KEY_FCFG_AZ, 3, rr);
1534    return result;
1535}
1536
1537/* Setups up the Freescale 8-bit accel for Sensor Fusion
1538* @param[in] orient A scalar representation of the orientation
1539*  that describes how to go from the Chip Orientation
1540*  to the Board Orientation often times called the Body Orientation in Invensense Documentation.
1541*  inv_orientation_matrix_to_scalar() will turn the transformation matrix into this scalar.
1542*/
1543inv_error_t inv_freescale_sensor_fusion_8bit(unsigned short orient)
1544{
1545    unsigned char regs[27];
1546    inv_error_t result;
1547    uint_fast8_t kk;
1548
1549    orient = orient & 0xdb;
1550    kk = 0;
1551
1552    regs[kk++] = DINAC3;
1553    regs[kk++] = DINA90 + 14;
1554    regs[kk++] = DINAA0 + 9;
1555    regs[kk++] = DINA3E;
1556    regs[kk++] = DINA5E;
1557    regs[kk++] = DINA7E;
1558
1559    regs[kk++] = DINAC2;
1560    regs[kk++] = DINAA0 + 9;
1561    regs[kk++] = DINA90 + 9;
1562    regs[kk++] = DINAF8 + 2;
1563
1564    switch (orient) {
1565    default:
1566        // Typically 0x88
1567        regs[kk++] = DINACB;
1568
1569        regs[kk++] = DINA54;
1570        regs[kk++] = DINA50;
1571        regs[kk++] = DINA50;
1572        regs[kk++] = DINA50;
1573        regs[kk++] = DINA50;
1574        regs[kk++] = DINA50;
1575        regs[kk++] = DINA50;
1576        regs[kk++] = DINA50;
1577
1578        regs[kk++] = DINACD;
1579        break;
1580    case 0x50:
1581        regs[kk++] = DINACB;
1582
1583        regs[kk++] = DINACF;
1584
1585        regs[kk++] = DINA7C;
1586        regs[kk++] = DINA78;
1587        regs[kk++] = DINA78;
1588        regs[kk++] = DINA78;
1589        regs[kk++] = DINA78;
1590        regs[kk++] = DINA78;
1591        regs[kk++] = DINA78;
1592        regs[kk++] = DINA78;
1593        break;
1594    case 0x81:
1595        regs[kk++] = DINA2C;
1596        regs[kk++] = DINA28;
1597        regs[kk++] = DINA28;
1598        regs[kk++] = DINA28;
1599        regs[kk++] = DINA28;
1600        regs[kk++] = DINA28;
1601        regs[kk++] = DINA28;
1602        regs[kk++] = DINA28;
1603
1604        regs[kk++] = DINACD;
1605
1606        regs[kk++] = DINACB;
1607        break;
1608    case 0x11:
1609        regs[kk++] = DINA2C;
1610        regs[kk++] = DINA28;
1611        regs[kk++] = DINA28;
1612        regs[kk++] = DINA28;
1613        regs[kk++] = DINA28;
1614        regs[kk++] = DINA28;
1615        regs[kk++] = DINA28;
1616        regs[kk++] = DINA28;
1617        regs[kk++] = DINACB;
1618        regs[kk++] = DINACF;
1619        break;
1620    case 0x42:
1621        regs[kk++] = DINACF;
1622        regs[kk++] = DINACD;
1623
1624        regs[kk++] = DINA7C;
1625        regs[kk++] = DINA78;
1626        regs[kk++] = DINA78;
1627        regs[kk++] = DINA78;
1628        regs[kk++] = DINA78;
1629        regs[kk++] = DINA78;
1630        regs[kk++] = DINA78;
1631        regs[kk++] = DINA78;
1632        break;
1633    case 0x0a:
1634        regs[kk++] = DINACD;
1635
1636        regs[kk++] = DINA54;
1637        regs[kk++] = DINA50;
1638        regs[kk++] = DINA50;
1639        regs[kk++] = DINA50;
1640        regs[kk++] = DINA50;
1641        regs[kk++] = DINA50;
1642        regs[kk++] = DINA50;
1643        regs[kk++] = DINA50;
1644
1645        regs[kk++] = DINACF;
1646        break;
1647    }
1648
1649    regs[kk++] = DINA90 + 7;
1650    regs[kk++] = DINAF8 + 3;
1651    regs[kk++] = DINAA0 + 9;
1652    regs[kk++] = DINA0E;
1653    regs[kk++] = DINA0E;
1654    regs[kk++] = DINA0E;
1655
1656    regs[kk++] = DINAF8 + 1;    // filler
1657
1658    result = inv_set_mpu_memory(KEY_FCFG_FSCALE, kk, regs);
1659    if (result) {
1660        LOG_RESULT_LOCATION(result);
1661        return result;
1662    }
1663
1664    return result;
1665}
1666
1667/**
1668 * Controlls each sensor and each axis when the motion processing unit is
1669 * running.  When it is not running, simply records the state for later.
1670 *
1671 * NOTE: In this version only full sensors controll is allowed.  Independent
1672 * axis control will return an error.
1673 *
1674 * @param sensors Bit field of each axis desired to be turned on or off
1675 *
1676 * @return INV_SUCCESS or non-zero error code
1677 */
1678inv_error_t inv_set_mpu_sensors(unsigned long sensors)
1679{
1680    INVENSENSE_FUNC_START;
1681    unsigned char state = inv_get_state();
1682    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
1683    inv_error_t result = INV_SUCCESS;
1684    unsigned short fifoRate;
1685
1686    if (state < INV_STATE_DMP_OPENED)
1687        return INV_ERROR_SM_IMPROPER_STATE;
1688
1689    if (((sensors & INV_THREE_AXIS_ACCEL) != INV_THREE_AXIS_ACCEL) &&
1690        ((sensors & INV_THREE_AXIS_ACCEL) != 0)) {
1691        return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
1692    }
1693    if (((sensors & INV_THREE_AXIS_ACCEL) != 0) &&
1694        (mldl_cfg->pdata->accel.get_slave_descr == 0)) {
1695        return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED;
1696    }
1697
1698    if (((sensors & INV_THREE_AXIS_COMPASS) != INV_THREE_AXIS_COMPASS) &&
1699        ((sensors & INV_THREE_AXIS_COMPASS) != 0)) {
1700        return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
1701    }
1702    if (((sensors & INV_THREE_AXIS_COMPASS) != 0) &&
1703        (mldl_cfg->pdata->compass.get_slave_descr == 0)) {
1704        return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED;
1705    }
1706
1707    if (((sensors & INV_THREE_AXIS_PRESSURE) != INV_THREE_AXIS_PRESSURE) &&
1708        ((sensors & INV_THREE_AXIS_PRESSURE) != 0)) {
1709        return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
1710    }
1711    if (((sensors & INV_THREE_AXIS_PRESSURE) != 0) &&
1712        (mldl_cfg->pdata->pressure.get_slave_descr == 0)) {
1713        return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED;
1714    }
1715
1716    /* DMP was off, and is turning on */
1717    if (sensors & INV_DMP_PROCESSOR &&
1718        !(mldl_cfg->requested_sensors & INV_DMP_PROCESSOR)) {
1719        struct ext_slave_config config;
1720        long odr;
1721        config.key = MPU_SLAVE_CONFIG_ODR_RESUME;
1722        config.len = sizeof(long);
1723        config.apply = (state == INV_STATE_DMP_STARTED);
1724        config.data = &odr;
1725
1726        odr = (inv_mpu_get_sampling_rate_hz(mldl_cfg) * 1000);
1727        result = inv_mpu_config_accel(mldl_cfg,
1728                                      inv_get_serial_handle(),
1729                                      inv_get_serial_handle(), &config);
1730        if (result) {
1731            LOG_RESULT_LOCATION(result);
1732            return result;
1733        }
1734
1735        config.key = MPU_SLAVE_CONFIG_IRQ_RESUME;
1736        odr = MPU_SLAVE_IRQ_TYPE_NONE;
1737        result = inv_mpu_config_accel(mldl_cfg,
1738                                      inv_get_serial_handle(),
1739                                      inv_get_serial_handle(), &config);
1740        if (result) {
1741            LOG_RESULT_LOCATION(result);
1742            return result;
1743        }
1744        inv_init_fifo_hardare();
1745    }
1746
1747    if (inv_obj.mode_change_func) {
1748        result = inv_obj.mode_change_func(mldl_cfg->requested_sensors, sensors);
1749        if (result) {
1750            LOG_RESULT_LOCATION(result);
1751            return result;
1752        }
1753    }
1754
1755    /* Get the fifo rate before changing sensors so if we need to match it */
1756    fifoRate = inv_get_fifo_rate();
1757    mldl_cfg->requested_sensors = sensors;
1758
1759    /* inv_dmp_start will turn the sensors on */
1760    if (state == INV_STATE_DMP_STARTED) {
1761        result = inv_dl_start(sensors);
1762        if (result) {
1763            LOG_RESULT_LOCATION(result);
1764            return result;
1765        }
1766        result = inv_reset_motion();
1767        if (result) {
1768            LOG_RESULT_LOCATION(result);
1769            return result;
1770        }
1771        result = inv_dl_stop(~sensors);
1772        if (result) {
1773            LOG_RESULT_LOCATION(result);
1774            return result;
1775        }
1776    }
1777
1778    inv_set_fifo_rate(fifoRate);
1779
1780    if (!(sensors & INV_DMP_PROCESSOR) && (sensors & INV_THREE_AXIS_ACCEL)) {
1781        struct ext_slave_config config;
1782        long data;
1783
1784        config.len = sizeof(long);
1785        config.key = MPU_SLAVE_CONFIG_IRQ_RESUME;
1786        config.apply = (state == INV_STATE_DMP_STARTED);
1787        config.data = &data;
1788        data = MPU_SLAVE_IRQ_TYPE_DATA_READY;
1789        result = inv_mpu_config_accel(mldl_cfg,
1790                                      inv_get_serial_handle(),
1791                                      inv_get_serial_handle(), &config);
1792        if (result) {
1793            LOG_RESULT_LOCATION(result);
1794            return result;
1795        }
1796    }
1797
1798    return result;
1799}
1800
1801void inv_set_mode_change(inv_error_t(*mode_change_func)
1802                         (unsigned long, unsigned long))
1803{
1804    inv_obj.mode_change_func = mode_change_func;
1805}
1806
1807/**
1808* Mantis setup
1809*/
1810#ifdef CONFIG_MPU_SENSORS_MPU6050B1
1811inv_error_t inv_set_mpu_6050_config()
1812{
1813    long temp;
1814    inv_error_t result;
1815    unsigned char big8[4];
1816    unsigned char atc[4];
1817    long s[3], s2[3];
1818    int kk;
1819    struct mldl_cfg *mldlCfg = inv_get_dl_config();
1820
1821    result = inv_serial_read(inv_get_serial_handle(), inv_get_mpu_slave_addr(),
1822                             0x0d, 4, atc);
1823    if (result) {
1824        LOG_RESULT_LOCATION(result);
1825        return result;
1826    }
1827
1828    temp = atc[3] & 0x3f;
1829    if (temp >= 32)
1830        temp = temp - 64;
1831    temp = (temp << 21) | 0x100000;
1832    temp += (1L << 29);
1833    temp = -temp;
1834    result = inv_set_mpu_memory(KEY_D_ACT0, 4, inv_int32_to_big8(temp, big8));
1835    if (result) {
1836        LOG_RESULT_LOCATION(result);
1837        return result;
1838    }
1839
1840    for (kk = 0; kk < 3; ++kk) {
1841        s[kk] = atc[kk] & 0x3f;
1842        if (s[kk] > 32)
1843            s[kk] = s[kk] - 64;
1844        s[kk] *= 2516582L;
1845    }
1846
1847    for (kk = 0; kk < 3; ++kk) {
1848        s2[kk] = mldlCfg->pdata->orientation[kk * 3] * s[0] +
1849            mldlCfg->pdata->orientation[kk * 3 + 1] * s[1] +
1850            mldlCfg->pdata->orientation[kk * 3 + 2] * s[2];
1851    }
1852    result = inv_set_mpu_memory(KEY_D_ACSX, 4, inv_int32_to_big8(s2[0], big8));
1853    if (result) {
1854        LOG_RESULT_LOCATION(result);
1855        return result;
1856    }
1857    result = inv_set_mpu_memory(KEY_D_ACSY, 4, inv_int32_to_big8(s2[1], big8));
1858    if (result) {
1859        LOG_RESULT_LOCATION(result);
1860        return result;
1861    }
1862    result = inv_set_mpu_memory(KEY_D_ACSZ, 4, inv_int32_to_big8(s2[2], big8));
1863    if (result) {
1864        LOG_RESULT_LOCATION(result);
1865        return result;
1866    }
1867
1868    return result;
1869}
1870#endif
1871
1872/**
1873 * @}
1874 */
1875