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: mlFIFO.c 5653 2011-06-16 21:06:55Z nroyer $
21 *
22 *******************************************************************************/
23
24/**
25 *   @defgroup MLFIFO
26 *   @brief Motion Library - FIFO Driver.
27 *          The FIFO API Interface.
28 *
29 *   @{
30 *       @file mlFIFO.c
31 *       @brief FIFO Interface.
32**/
33
34#include <string.h>
35#include "mpu.h"
36#if defined CONFIG_MPU_SENSORS_MPU6050A2
37#    include "mpu6050a2.h"
38#elif defined CONFIG_MPU_SENSORS_MPU6050B1
39#    include "mpu6050b1.h"
40#elif defined CONFIG_MPU_SENSORS_MPU3050
41#  include "mpu3050.h"
42#else
43#error Invalid or undefined CONFIG_MPU_SENSORS_MPUxxxx
44#endif
45#include "mlFIFO.h"
46#include "mlFIFOHW.h"
47#include "dmpKey.h"
48#include "mlMathFunc.h"
49#include "ml.h"
50#include "mldl.h"
51#include "mldl_cfg.h"
52#include "mlstates.h"
53#include "mlsupervisor.h"
54#include "mlos.h"
55#include "mlmath.h"
56#include "accel.h"
57
58#include "log.h"
59#undef MPL_LOG_TAG
60#define MPL_LOG_TAG "MPL-fifo"
61
62#define FIFO_DEBUG 0
63
64#define REF_QUATERNION             (0)
65#define REF_GYROS                  (REF_QUATERNION + 4)
66#define REF_CONTROL                (REF_GYROS + 3)
67#define REF_RAW                    (REF_CONTROL + 4)
68#define REF_RAW_EXTERNAL           (REF_RAW + 8)
69#define REF_ACCEL                  (REF_RAW_EXTERNAL + 6)
70#define REF_QUANT_ACCEL            (REF_ACCEL + 3)
71#define REF_QUATERNION_6AXIS       (REF_QUANT_ACCEL + INV_MAX_NUM_ACCEL_SAMPLES)
72#define REF_EIS                    (REF_QUATERNION_6AXIS + 4)
73#define REF_DMP_PACKET             (REF_EIS + 3)
74#define REF_GARBAGE                (REF_DMP_PACKET + 1)
75#define REF_LAST                   (REF_GARBAGE + 1)
76
77long fifo_scale[REF_LAST] = {
78    (1L << 30), (1L << 30), (1L << 30), (1L << 30), // Quaternion
79    // 2^(16+30)/((2^30)*((3.14159265358/180)/200)/2)
80    1501974482L, 1501974482L, 1501974482L,  // Gyro
81    (1L << 30), (1L << 30), (1L << 30), (1L << 30), // Control
82    (1L << 14),                 // Temperature
83    (1L << 14), (1L << 14), (1L << 14), // Raw Gyro
84    (1L << 14), (1L << 14), (1L << 14), (0),    // Raw Accel, plus padding
85    (1L << 14), (1L << 14), (1L << 14), // Raw External
86    (1L << 14), (1L << 14), (1L << 14), // Raw External
87    (1L << 16), (1L << 16), (1L << 16), // Accel
88    (1L << 30), (1L << 30), (1L << 30), (1L << 30), // Quant Accel
89    (1L << 30), (1L << 30), (1L << 30), (1L << 30), //Quant Accel
90    (1L << 30), (1L << 30), (1L << 30), (1L << 30), // Quaternion 6 Axis
91    (1L << 30), (1L << 30), (1L << 30), // EIS
92    (1L << 30),                 // Packet
93    (1L << 30),                 // Garbage
94};
95
96// The scale factors for tap need to match the number in fifo_scale above.
97// fifo_base_offset below may also need to be changed if this is not 8
98#if INV_MAX_NUM_ACCEL_SAMPLES != 8
99#error  INV_MAX_NUM_ACCEL_SAMPLES must be defined to 8
100#endif
101
102#define CONFIG_QUAT                (0)
103#define CONFIG_GYROS               (CONFIG_QUAT + 1)
104#define CONFIG_CONTROL_DATA        (CONFIG_GYROS + 1)
105#define CONFIG_TEMPERATURE         (CONFIG_CONTROL_DATA + 1)
106#define CONFIG_RAW_DATA            (CONFIG_TEMPERATURE + 1)
107#define CONFIG_RAW_EXTERNAL        (CONFIG_RAW_DATA + 1)
108#define CONFIG_ACCEL               (CONFIG_RAW_EXTERNAL + 1)
109#define CONFIG_DMP_QUANT_ACCEL     (CONFIG_ACCEL + 1)
110#define CONFIG_EIS                 (CONFIG_DMP_QUANT_ACCEL + 1)
111#define CONFIG_DMP_PACKET_NUMBER   (CONFIG_EIS + 1)
112#define CONFIG_FOOTER              (CONFIG_DMP_PACKET_NUMBER + 1)
113#define NUMFIFOELEMENTS            (CONFIG_FOOTER + 1)
114
115const int fifo_base_offset[NUMFIFOELEMENTS] = {
116    REF_QUATERNION * 4,
117    REF_GYROS * 4,
118    REF_CONTROL * 4,
119    REF_RAW * 4,
120    REF_RAW * 4 + 4,
121    REF_RAW_EXTERNAL * 4,
122    REF_ACCEL * 4,
123    REF_QUANT_ACCEL * 4,
124    REF_EIS * 4,
125    REF_DMP_PACKET * 4,
126    REF_GARBAGE * 4
127};
128
129struct fifo_obj {
130    void (*fifo_process_cb) (void);
131    long decoded[REF_LAST];
132    long decoded_accel[INV_MAX_NUM_ACCEL_SAMPLES][ACCEL_NUM_AXES];
133    int offsets[REF_LAST * 4];
134    int cache;
135    uint_fast8_t gyro_source;
136    unsigned short fifo_rate;
137    unsigned short sample_step_size_ms;
138    uint_fast16_t fifo_packet_size;
139    uint_fast16_t data_config[NUMFIFOELEMENTS];
140    unsigned char reference_count[REF_LAST];
141    long acc_bias_filt[3];
142    float acc_filter_coef;
143    long gravity_cache[3];
144};
145
146static struct fifo_obj fifo_obj;
147
148#define FIFO_CACHE_TEMPERATURE 1
149#define FIFO_CACHE_GYRO 2
150#define FIFO_CACHE_GRAVITY_BODY 4
151#define FIFO_CACHE_ACC_BIAS 8
152
153struct fifo_rate_obj {
154    // These describe callbacks happening everytime a FIFO block is processed
155    int_fast8_t num_cb;
156    HANDLE mutex;
157    inv_obj_func fifo_process_cb[MAX_HIGH_RATE_PROCESSES];
158    int priority[MAX_HIGH_RATE_PROCESSES];
159};
160
161struct fifo_rate_obj fifo_rate_obj;
162
163/** Sets accuracy to be one of 0, INV_32_BIT, or INV_16_BIT. Looks up old
164 *  accuracy if needed.
165 */
166static uint_fast16_t inv_set_fifo_accuracy(uint_fast16_t elements,
167                                           uint_fast16_t accuracy,
168                                           uint_fast8_t configOffset)
169{
170    if (elements) {
171        if (!accuracy)
172            accuracy = fifo_obj.data_config[configOffset];
173        else if (accuracy & INV_16_BIT)
174            if ((fifo_obj.data_config[configOffset] & INV_32_BIT))
175                accuracy = INV_32_BIT;  // 32-bits takes priority
176            else
177                accuracy = INV_16_BIT;
178        else
179            accuracy = INV_32_BIT;
180    } else {
181        accuracy = 0;
182    }
183
184    return accuracy;
185}
186
187/** Adjusts (len) Reference Counts, at offset (refOffset). If increment is 0,
188 * the reference counts are subtracted, otherwise they are incremented for each
189 * bit set in element. The value returned are the elements that should be sent
190 * out as data through the FIFO.
191*/
192static uint_fast16_t inv_set_fifo_reference(uint_fast16_t elements,
193                                            uint_fast16_t increment,
194                                            uint_fast8_t refOffset,
195                                            uint_fast8_t len)
196{
197    uint_fast8_t kk;
198
199    if (increment == 0) {
200        for (kk = 0; kk < len; ++kk) {
201            if ((elements & 1)
202                && (fifo_obj.reference_count[kk + refOffset] > 0)) {
203                fifo_obj.reference_count[kk + refOffset]--;
204            }
205            elements >>= 1;
206        }
207    } else {
208        for (kk = 0; kk < len; ++kk) {
209            if (elements & 1)
210                fifo_obj.reference_count[kk + refOffset]++;
211            elements >>= 1;
212        }
213    }
214    elements = 0;
215    for (kk = 0; kk < len; ++kk) {
216        if (fifo_obj.reference_count[kk + refOffset] > 0)
217            elements |= (1 << kk);
218    }
219    return elements;
220}
221
222/**
223 * @param[in] accuracy INV_16_BIT or INV_32_BIT when constructing data to send
224 *  out the FIFO, 0 when removing from the FIFO.
225 */
226static inv_error_t inv_construct3_fifo(unsigned char *regs,
227                                       uint_fast16_t elements,
228                                       uint_fast16_t accuracy,
229                                       uint_fast8_t refOffset,
230                                       unsigned short key,
231                                       uint_fast8_t configOffset)
232{
233    int_fast8_t kk;
234    inv_error_t result;
235
236    elements = inv_set_fifo_reference(elements, accuracy, refOffset, 3);
237    accuracy = inv_set_fifo_accuracy(elements, accuracy, configOffset);
238
239    if (accuracy & INV_16_BIT) {
240        regs[0] = DINAF8 + 2;
241    }
242
243    fifo_obj.data_config[configOffset] = elements | accuracy;
244
245    for (kk = 0; kk < 3; ++kk) {
246        if ((elements & 1) == 0)
247            regs[kk + 1] = DINAA0 + 3;
248        elements >>= 1;
249    }
250
251    result = inv_set_mpu_memory(key, 4, regs);
252
253    return result;
254}
255
256/**
257 * @internal
258 * Puts footer on FIFO data.
259 */
260static inv_error_t inv_set_footer(void)
261{
262    unsigned char regs = DINA30;
263    uint_fast8_t tmp_count;
264    int_fast8_t i, j;
265    int offset;
266    int result;
267    int *fifo_offsets_ptr = fifo_obj.offsets;
268
269    fifo_obj.fifo_packet_size = 0;
270    for (i = 0; i < NUMFIFOELEMENTS; i++) {
271        tmp_count = 0;
272        offset = fifo_base_offset[i];
273        for (j = 0; j < 8; j++) {
274            if ((fifo_obj.data_config[i] >> j) & 0x0001) {
275#ifndef BIG_ENDIAN
276                // Special Case for Byte Ordering on Accel Data
277                if ((i == CONFIG_RAW_DATA) && (j > 2)) {
278                    tmp_count += 2;
279                    switch (inv_get_dl_config()->accel->endian) {
280                    case EXT_SLAVE_BIG_ENDIAN:
281                        *fifo_offsets_ptr++ = offset + 3;
282                        *fifo_offsets_ptr++ = offset + 2;
283                        break;
284                    case EXT_SLAVE_LITTLE_ENDIAN:
285                        *fifo_offsets_ptr++ = offset + 2;
286                        *fifo_offsets_ptr++ = offset + 3;
287                        break;
288                    case EXT_SLAVE_FS8_BIG_ENDIAN:
289                        if (j == 3) {
290                            // Throw this byte away
291                            *fifo_offsets_ptr++ =
292                                fifo_base_offset[CONFIG_FOOTER];
293                            *fifo_offsets_ptr++ = offset + 3;
294                        } else if (j == 4) {
295                            *fifo_offsets_ptr++ = offset + 3;
296                            *fifo_offsets_ptr++ = offset + 7;
297                        } else {
298                            // Throw these byte away
299                            *fifo_offsets_ptr++ =
300                                fifo_base_offset[CONFIG_FOOTER];
301                            *fifo_offsets_ptr++ =
302                                fifo_base_offset[CONFIG_FOOTER];
303                        }
304                        break;
305                    case EXT_SLAVE_FS16_BIG_ENDIAN:
306                        if (j == 3) {
307                            // Throw this byte away
308                            *fifo_offsets_ptr++ =
309                                fifo_base_offset[CONFIG_FOOTER];
310                            *fifo_offsets_ptr++ = offset + 3;
311                        } else if (j == 4) {
312                            *fifo_offsets_ptr++ = offset - 2;
313                            *fifo_offsets_ptr++ = offset + 3;
314                        } else {
315                            *fifo_offsets_ptr++ = offset - 2;
316                            *fifo_offsets_ptr++ = offset + 3;
317                        }
318                        break;
319                    default:
320                        return INV_ERROR;    // Bad value on ordering
321                    }
322                } else {
323                    tmp_count += 2;
324                    *fifo_offsets_ptr++ = offset + 3;
325                    *fifo_offsets_ptr++ = offset + 2;
326                    if (fifo_obj.data_config[i] & INV_32_BIT) {
327                        *fifo_offsets_ptr++ = offset + 1;
328                        *fifo_offsets_ptr++ = offset;
329                        tmp_count += 2;
330                    }
331                }
332#else
333                // Big Endian Platform
334                // Special Case for Byte Ordering on Accel Data
335                if ((i == CONFIG_RAW_DATA) && (j > 2)) {
336                    tmp_count += 2;
337                    switch (inv_get_dl_config()->accel->endian) {
338                    case EXT_SLAVE_BIG_ENDIAN:
339                        *fifo_offsets_ptr++ = offset + 2;
340                        *fifo_offsets_ptr++ = offset + 3;
341                        break;
342                    case EXT_SLAVE_LITTLE_ENDIAN:
343                        *fifo_offsets_ptr++ = offset + 3;
344                        *fifo_offsets_ptr++ = offset + 2;
345                        break;
346                    case EXT_SLAVE_FS8_BIG_ENDIAN:
347                        if (j == 3) {
348                            // Throw this byte away
349                            *fifo_offsets_ptr++ =
350                                fifo_base_offset[CONFIG_FOOTER];
351                            *fifo_offsets_ptr++ = offset;
352                        } else if (j == 4) {
353                            *fifo_offsets_ptr++ = offset;
354                            *fifo_offsets_ptr++ = offset + 4;
355                        } else {
356                            // Throw these bytes away
357                            *fifo_offsets_ptr++ =
358                                fifo_base_offset[CONFIG_FOOTER];
359                            *fifo_offsets_ptr++ =
360                                fifo_base_offset[CONFIG_FOOTER];
361                        }
362                        break;
363                    case EXT_SLAVE_FS16_BIG_ENDIAN:
364                        if (j == 3) {
365                            // Throw this byte away
366                            *fifo_offsets_ptr++ =
367                                fifo_base_offset[CONFIG_FOOTER];
368                            *fifo_offsets_ptr++ = offset;
369                        } else if (j == 4) {
370                            *fifo_offsets_ptr++ = offset - 3;
371                            *fifo_offsets_ptr++ = offset;
372                        } else {
373                            *fifo_offsets_ptr++ = offset - 3;
374                            *fifo_offsets_ptr++ = offset;
375                        }
376                        break;
377                    default:
378                        return INV_ERROR;    // Bad value on ordering
379                    }
380                } else {
381                    tmp_count += 2;
382                    *fifo_offsets_ptr++ = offset;
383                    *fifo_offsets_ptr++ = offset + 1;
384                    if (fifo_obj.data_config[i] & INV_32_BIT) {
385                        *fifo_offsets_ptr++ = offset + 2;
386                        *fifo_offsets_ptr++ = offset + 3;
387                        tmp_count += 2;
388                    }
389                }
390
391#endif
392            }
393            offset += 4;
394        }
395        fifo_obj.fifo_packet_size += tmp_count;
396    }
397    if (fifo_obj.data_config[CONFIG_FOOTER] == 0 &&
398        fifo_obj.fifo_packet_size > 0) {
399        // Add footer
400        result = inv_set_mpu_memory(KEY_CFG_16, 1, &regs);
401        if (result) {
402            LOG_RESULT_LOCATION(result);
403            return result;
404        }
405        fifo_obj.data_config[CONFIG_FOOTER] = 0x0001 | INV_16_BIT;
406        fifo_obj.fifo_packet_size += 2;
407    } else if (fifo_obj.data_config[CONFIG_FOOTER] &&
408               (fifo_obj.fifo_packet_size == 2)) {
409        // Remove Footer
410        regs = DINAA0 + 3;
411        result = inv_set_mpu_memory(KEY_CFG_16, 1, &regs);
412        if (result) {
413            LOG_RESULT_LOCATION(result);
414            return result;
415        }
416        fifo_obj.data_config[CONFIG_FOOTER] = 0;
417        fifo_obj.fifo_packet_size = 0;
418    }
419
420    return INV_SUCCESS;
421}
422
423inv_error_t inv_decode_quantized_accel(void)
424{
425    int kk;
426    int fifoRate = inv_get_fifo_rate();
427
428    if (!fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL])
429        return INV_ERROR_FEATURE_NOT_ENABLED;
430
431    for (kk = (INV_MAX_NUM_ACCEL_SAMPLES - (fifoRate + 1));
432         kk < INV_MAX_NUM_ACCEL_SAMPLES; kk++) {
433        union {
434            unsigned int u10:10;
435            signed int s10:10;
436        } temp;
437
438        union {
439            uint32_t u32;
440            int32_t s32;
441        } value;
442
443        value.u32 = fifo_obj.decoded[REF_QUANT_ACCEL + kk];
444        // unquantize this samples.
445        // They are stored as x * 2^20 + y * 2^10 + z
446        // Z
447        temp.u10 = value.u32 & 0x3ff;
448        value.s32 -= temp.s10;
449        fifo_obj.decoded_accel[kk][2] = temp.s10 * 64;
450        // Y
451        value.s32 = value.s32 / 1024;
452        temp.u10 = value.u32 & 0x3ff;
453        value.s32 -= temp.s10;
454        fifo_obj.decoded_accel[kk][1] = temp.s10 * 64;
455        // X
456        value.s32 = value.s32 / 1024;
457        temp.u10 = value.u32 & 0x3ff;
458        fifo_obj.decoded_accel[kk][0] = temp.s10 * 64;
459    }
460    return INV_SUCCESS;
461}
462
463static inv_error_t inv_state_change_fifo(unsigned char newState)
464{
465    inv_error_t result = INV_SUCCESS;
466    unsigned char regs[4];
467    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
468
469    /* Don't reset the fifo on a fifo rate change */
470    if ((mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) &&
471        (newState != inv_get_state()) && (inv_dmpkey_supported(KEY_D_1_178))) {
472        /* Delay output on restart by 50ms due to warm up time of gyros */
473
474        short delay = (short)-((50 / inv_get_sample_step_size_ms()) + 1);
475        inv_init_fifo_hardare();
476        inv_int16_to_big8(delay, regs);
477        result = inv_set_mpu_memory(KEY_D_1_178, 2, regs);
478        if (result) {
479            LOG_RESULT_LOCATION(result);
480            return result;
481        }
482    }
483
484    if (INV_STATE_DMP_STARTED == newState) {
485        if (inv_dmpkey_supported(KEY_D_1_128)) {
486            double tmp;
487            tmp = (0x20000000L * M_PI) / (fifo_obj.fifo_rate + 1);
488            if (tmp > 0x40000000L)
489                tmp = 0x40000000L;
490            (void)inv_int32_to_big8((long)tmp, regs);
491            result = inv_set_mpu_memory(KEY_D_1_128, sizeof(long), regs);
492            if (result) {
493                LOG_RESULT_LOCATION(result);
494                return result;
495            }
496            result = inv_reset_fifo();
497            if (result) {
498                LOG_RESULT_LOCATION(result);
499                return result;
500            }
501        }
502    }
503    return result;
504}
505
506/**
507 * @internal
508 * @brief get the FIFO packet size
509 * @return the FIFO packet size
510 */
511uint_fast16_t inv_get_fifo_packet_size(void)
512{
513    return fifo_obj.fifo_packet_size;
514}
515
516/**
517 *  @brief  Initializes all the internal static variables for
518 *          the FIFO module.
519 *  @note   Should be called by the initialization routine such
520 *          as inv_dmp_open().
521 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
522 */
523inv_error_t inv_init_fifo_param(void)
524{
525    inv_error_t result;
526    memset(&fifo_obj, 0, sizeof(struct fifo_obj));
527    fifo_obj.decoded[REF_QUATERNION] = 1073741824L; // Set to Identity
528    inv_set_linear_accel_filter_coef(0.f);
529    fifo_obj.fifo_rate = 20;
530    fifo_obj.sample_step_size_ms = 100;
531    memset(&fifo_rate_obj, 0, sizeof(struct fifo_rate_obj));
532    result = inv_create_mutex(&fifo_rate_obj.mutex);
533    if (result) {
534        LOG_RESULT_LOCATION(result);
535        return result;
536    }
537    result = inv_register_state_callback(inv_state_change_fifo);
538    if (result) {
539        LOG_RESULT_LOCATION(result);
540        return result;
541    }
542    return result;
543}
544
545/**
546 *  @brief  Close the FIFO usage.
547 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
548 */
549inv_error_t inv_close_fifo(void)
550{
551    inv_error_t result;
552    inv_error_t first = INV_SUCCESS;
553    result = inv_unregister_state_callback(inv_state_change_fifo);
554    ERROR_CHECK_FIRST(first, result);
555    result = inv_destroy_mutex(fifo_rate_obj.mutex);
556    ERROR_CHECK_FIRST(first, result);
557    memset(&fifo_rate_obj, 0, sizeof(struct fifo_rate_obj));
558    return first;
559}
560
561/**
562 * Set the gyro source to output to the fifo
563 *
564 * @param source The source.  One of
565 * - INV_GYRO_FROM_RAW
566 * - INV_GYRO_FROM_QUATERNION
567 *
568 * @return INV_SUCCESS or non-zero error code;
569 */
570inv_error_t inv_set_gyro_data_source(uint_fast8_t source)
571{
572    if (source != INV_GYRO_FROM_QUATERNION && source != INV_GYRO_FROM_RAW) {
573        return INV_ERROR_INVALID_PARAMETER;
574    }
575
576    fifo_obj.gyro_source = source;
577    return INV_SUCCESS;
578}
579
580/**
581 *  @brief  Reads and processes FIFO data. Also handles callbacks when data is
582 *          ready.
583 *  @param  numPackets
584 *              Number of FIFO packets to try to read. You should
585 *              use a large number here, such as 100, if you want to read all
586 *              the full packets in the FIFO, which is typical operation.
587 *  @param  processed
588 *              The number of FIFO packets processed. This may be incremented
589 *              even if high rate processes later fail.
590 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
591 */
592inv_error_t inv_read_and_process_fifo(int_fast8_t numPackets,
593                                      int_fast8_t * processed)
594{
595    int_fast8_t packet;
596    inv_error_t result = INV_SUCCESS;
597    uint_fast16_t read;
598    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
599    int kk;
600
601    if (NULL == processed)
602        return INV_ERROR_INVALID_PARAMETER;
603
604    *processed = 0;
605    if (fifo_obj.fifo_packet_size == 0)
606        return result;          // Nothing to read
607
608    for (packet = 0; packet < numPackets; ++packet) {
609        if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) {
610            unsigned char footer_n_data[MAX_FIFO_LENGTH + FIFO_FOOTER_SIZE];
611            unsigned char *buf = &footer_n_data[FIFO_FOOTER_SIZE];
612            read = inv_get_fifo((uint_fast16_t) fifo_obj.fifo_packet_size,
613                                footer_n_data);
614            if (0 == read ||
615                read != fifo_obj.fifo_packet_size - FIFO_FOOTER_SIZE) {
616                result = inv_get_fifo_status();
617                if (INV_SUCCESS != result) {
618                    memset(fifo_obj.decoded, 0, sizeof(fifo_obj.decoded));
619                }
620                return result;
621            }
622
623            result = inv_process_fifo_packet(buf);
624            if (result) {
625                LOG_RESULT_LOCATION(result);
626                return result;
627            }
628        } else if (inv_accel_present()) {
629            long data[ACCEL_NUM_AXES];
630            result = inv_get_accel_data(data);
631            if (result == INV_ERROR_ACCEL_DATA_NOT_READY) {
632                return INV_SUCCESS;
633            }
634            if (result) {
635                LOG_RESULT_LOCATION(result);
636                return result;
637            }
638
639            memset(fifo_obj.decoded, 0, sizeof(fifo_obj.decoded));
640            fifo_obj.cache = 0;
641            for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
642                fifo_obj.decoded[REF_RAW + 4 + kk] =
643                    inv_q30_mult((data[kk] << 16),
644                                 fifo_scale[REF_RAW + 4 + kk]);
645                fifo_obj.decoded[REF_ACCEL + kk] =
646                    inv_q30_mult((data[kk] << 15), fifo_scale[REF_ACCEL + kk]);
647                fifo_obj.decoded[REF_ACCEL + kk] -=
648                    inv_obj.scaled_accel_bias[kk];
649            }
650        }
651        // The buffer was processed correctly, so increment even if
652        // other processes fail later, which will return an error
653        *processed = *processed + 1;
654
655        if ((fifo_obj.fifo_rate < INV_MAX_NUM_ACCEL_SAMPLES) &&
656            fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL]) {
657            result = inv_decode_quantized_accel();
658            if (result) {
659                LOG_RESULT_LOCATION(result);
660                return result;
661            }
662        }
663
664        if (fifo_obj.data_config[CONFIG_QUAT]) {
665            result = inv_accel_compass_supervisor();
666            if (result) {
667                LOG_RESULT_LOCATION(result);
668                return result;
669            }
670        }
671
672        result = inv_pressure_supervisor();
673        if (result) {
674            LOG_RESULT_LOCATION(result);
675            return result;
676        }
677
678        // Callbacks now that we have a buffer of data ready
679        result = inv_run_fifo_rate_processes();
680        if (result) {
681            LOG_RESULT_LOCATION(result);
682            return result;
683        }
684
685    }
686    return result;
687}
688
689/**
690 *  @brief  inv_set_fifo_processed_callback is used to set a processed data callback
691 *          function.  inv_set_fifo_processed_callback sets a user defined callback
692 *          function that triggers when all the decoding has been finished by
693 *          the motion processing engines. It is called before other bigger
694 *          processing engines to allow lower latency for the user.
695 *
696 *  @pre    inv_dmp_open()
697 *          @ifnot MPL_MF
698 *              or inv_open_low_power_pedometer()
699 *              or inv_eis_open_dmp()
700 *          @endif
701 *          and inv_dmp_start()
702 *          must <b>NOT</b> have been called.
703 *
704 *  @param  func    A user defined callback function.
705 *
706 *  @return INV_SUCCESS if successful, or non-zero error code otherwise.
707 */
708inv_error_t inv_set_fifo_processed_callback(void (*func) (void))
709{
710    INVENSENSE_FUNC_START;
711
712    if (inv_get_state() < INV_STATE_DMP_OPENED)
713        return INV_ERROR_SM_IMPROPER_STATE;
714
715    fifo_obj.fifo_process_cb = func;
716
717    return INV_SUCCESS;
718}
719
720/**
721 * @internal
722 * @brief   Process data from the dmp read via the fifo.  Takes a buffer
723 *          filled with bytes read from the DMP FIFO.
724 *          Currently expects only the 16 bytes of quaternion data.
725 *          Calculates the motion parameters from that data and stores the
726 *          results in an internal data structure.
727 * @param[in,out]   dmpData     Pointer to the buffer containing the fifo data.
728 * @return  INV_SUCCESS or error code.
729**/
730inv_error_t inv_process_fifo_packet(const unsigned char *dmpData)
731{
732    INVENSENSE_FUNC_START;
733    int N, kk;
734    unsigned char *p;
735
736    p = (unsigned char *)(&fifo_obj.decoded);
737    N = fifo_obj.fifo_packet_size;
738    if (N > sizeof(fifo_obj.decoded))
739        return INV_ERROR_ASSERTION_FAILURE;
740
741    memset(&fifo_obj.decoded, 0, sizeof(fifo_obj.decoded));
742
743    for (kk = 0; kk < N; ++kk) {
744        p[fifo_obj.offsets[kk]] = *dmpData++;
745    }
746
747    // If multiplies are much greater cost than if checks, you could check
748    // to see if fifo_scale is non-zero first, or equal to (1L<<30)
749    for (kk = 0; kk < REF_LAST; ++kk) {
750        fifo_obj.decoded[kk] =
751            inv_q30_mult(fifo_obj.decoded[kk], fifo_scale[kk]);
752    }
753
754    memcpy(&fifo_obj.decoded[REF_QUATERNION_6AXIS],
755           &fifo_obj.decoded[REF_QUATERNION], 4 * sizeof(long));
756
757    inv_obj.flags[INV_PROCESSED_DATA_READY] = 1;
758    fifo_obj.cache = 0;
759
760    return INV_SUCCESS;
761}
762
763/** Converts 16-bit temperature data as read from temperature register
764* into Celcius scaled by 2^16.
765*/
766long inv_decode_temperature(short tempReg)
767{
768#if defined CONFIG_MPU_SENSORS_MPU6050A2
769    // Celcius = 35 + (T + 3048.7)/325.9
770    return 2906830L + inv_q30_mult((long)tempReg << 16, 3294697L);
771#endif
772#if defined CONFIG_MPU_SENSORS_MPU6050B1
773    // Celcius = 35 + (T + 927.4)/360.6
774    return 2462307L + inv_q30_mult((long)tempReg << 16, 2977653L);
775#endif
776#if defined CONFIG_MPU_SENSORS_MPU3050
777    // Celcius = 35 + (T + 13200)/280
778    return 5383314L + inv_q30_mult((long)tempReg << 16, 3834792L);
779#endif
780}
781
782/**  @internal
783* Returns the temperature in hardware units. The scaling may change from part to part.
784*/
785inv_error_t inv_get_temperature_raw(short *data)
786{
787    if (data == NULL)
788        return INV_ERROR_INVALID_PARAMETER;
789
790    if (!fifo_obj.data_config[CONFIG_TEMPERATURE]) {
791        inv_error_t result;
792        unsigned char regs[2];
793        if ((fifo_obj.cache & FIFO_CACHE_TEMPERATURE) == 0) {
794            if (FIFO_DEBUG)
795                MPL_LOGI("Fetching the temperature from the registers\n");
796            fifo_obj.cache |= FIFO_CACHE_TEMPERATURE;
797            result = inv_serial_read(inv_get_serial_handle(),
798                                inv_get_mpu_slave_addr(), MPUREG_TEMP_OUT_H, 2,
799                                regs);
800            if (result) {
801                LOG_RESULT_LOCATION(result);
802                return result;
803            }
804            fifo_obj.decoded[REF_RAW] = ((short)regs[0] << 8) | (regs[1]);
805        }
806    }
807    *data = (short)fifo_obj.decoded[REF_RAW];
808    return INV_SUCCESS;
809}
810
811/**
812 *  @brief      Returns 1-element vector of temperature. It is read from the hardware if it
813 *              doesn't exist in the FIFO.
814 *  @param[out] data    1-element vector of temperature
815 *  @return     0 on success or an error code.
816 */
817inv_error_t inv_get_temperature(long *data)
818{
819    short tr;
820    inv_error_t result;
821
822    if (data == NULL)
823        return INV_ERROR_INVALID_PARAMETER;
824    result = inv_get_temperature_raw(&tr);
825    if (result) {
826        LOG_RESULT_LOCATION(result);
827        return result;
828    }
829    data[0] = inv_decode_temperature(tr);
830    return INV_SUCCESS;
831}
832
833/**
834 *  @brief  Get the Decoded Accel Data.
835 *  @param  data
836 *              a buffer to store the quantized data.
837 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
838 */
839inv_error_t inv_get_unquantized_accel(long *data)
840{
841    int ii, kk;
842    if (data == NULL)
843        return INV_ERROR_INVALID_PARAMETER;
844
845    if (!fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL])
846        return INV_ERROR_FEATURE_NOT_ENABLED;
847
848    for (ii = 0; ii < INV_MAX_NUM_ACCEL_SAMPLES; ii++) {
849        for (kk = 0; kk < ACCEL_NUM_AXES; kk++) {
850            data[ii * ACCEL_NUM_AXES + kk] = fifo_obj.decoded_accel[ii][kk];
851        }
852    }
853
854    return INV_SUCCESS;
855}
856
857/**
858 *  @brief  Get the Quantized Accel data algorithm output from the FIFO.
859 *  @param  data
860 *              a buffer to store the quantized data.
861 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
862 */
863inv_error_t inv_get_quantized_accel(long *data)
864{
865    int ii;
866    if (data == NULL)
867        return INV_ERROR_INVALID_PARAMETER;
868
869    if (!fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL])
870        return INV_ERROR_FEATURE_NOT_ENABLED;
871
872    for (ii = 0; ii < INV_MAX_NUM_ACCEL_SAMPLES; ii++) {
873        data[ii] = fifo_obj.decoded[REF_QUANT_ACCEL + ii];
874    }
875
876    return INV_SUCCESS;
877}
878
879/** This gets raw gyro data. The data is taken from the FIFO if it was put in the FIFO
880*  and it is read from the registers if it was not put into the FIFO. The data is
881*  cached till the next FIFO processing block time.
882* @param[out] data Length 3, Gyro data
883*/
884inv_error_t inv_get_gyro_sensor(long *data)
885{
886    if (data == NULL)
887        return INV_ERROR_INVALID_PARAMETER;
888    if ((fifo_obj.data_config[CONFIG_RAW_DATA] & 7) != 7) {
889        inv_error_t result;
890        unsigned char regs[6];
891        if ((fifo_obj.cache & FIFO_CACHE_GYRO) == 0) {
892            fifo_obj.cache |= FIFO_CACHE_GYRO;
893            result =
894                inv_serial_read(inv_get_serial_handle(),
895                                inv_get_mpu_slave_addr(), MPUREG_GYRO_XOUT_H, 6,
896                                regs);
897            if (result) {
898                LOG_RESULT_LOCATION(result);
899                return result;
900            }
901            fifo_obj.decoded[REF_RAW + 1] =
902                (((long)regs[0]) << 24) | (((long)regs[1]) << 16);
903            fifo_obj.decoded[REF_RAW + 2] =
904                (((long)regs[2]) << 24) | (((long)regs[3]) << 16);
905            fifo_obj.decoded[REF_RAW + 3] =
906                (((long)regs[4]) << 24) | (((long)regs[5]) << 16);
907
908            // Temperature starts at location 0, Gyro at location 1.
909            fifo_obj.decoded[REF_RAW + 1] =
910                inv_q30_mult(fifo_obj.decoded[REF_RAW + 1],
911                             fifo_scale[REF_RAW + 1]);
912            fifo_obj.decoded[REF_RAW + 2] =
913                inv_q30_mult(fifo_obj.decoded[REF_RAW + 2],
914                             fifo_scale[REF_RAW + 2]);
915            fifo_obj.decoded[REF_RAW + 3] =
916                inv_q30_mult(fifo_obj.decoded[REF_RAW + 3],
917                             fifo_scale[REF_RAW + 3]);
918        }
919        data[0] = fifo_obj.decoded[REF_RAW + 1];
920        data[1] = fifo_obj.decoded[REF_RAW + 2];
921        data[2] = fifo_obj.decoded[REF_RAW + 3];
922    } else {
923        long data2[6];
924        inv_get_gyro_and_accel_sensor(data2);
925        data[0] = data2[0];
926        data[1] = data2[1];
927        data[2] = data2[2];
928    }
929    return INV_SUCCESS;
930}
931
932/**
933 *  @brief      Returns 6-element vector of gyro and accel data
934 *  @param[out] data    6-element vector of gyro and accel data
935 *  @return     0 on success or an error code.
936 */
937inv_error_t inv_get_gyro_and_accel_sensor(long *data)
938{
939    int ii;
940    if (data == NULL)
941        return INV_ERROR_INVALID_PARAMETER;
942
943    if (!fifo_obj.data_config[CONFIG_RAW_DATA])
944        return INV_ERROR_FEATURE_NOT_ENABLED;
945
946    for (ii = 0; ii < (GYRO_NUM_AXES + ACCEL_NUM_AXES); ii++) {
947        data[ii] = fifo_obj.decoded[REF_RAW + 1 + ii];
948    }
949
950    return INV_SUCCESS;
951}
952
953/**
954 *  @brief      Returns 3-element vector of external sensor
955 *  @param[out] data    3-element vector of external sensor
956 *  @return     0 on success or an error code.
957 */
958inv_error_t inv_get_external_sensor_data(long *data, int size)
959{
960#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
961	defined CONFIG_MPU_SENSORS_MPU6050B1
962    int ii;
963    if (data == NULL)
964        return INV_ERROR_INVALID_PARAMETER;
965
966    if (!fifo_obj.data_config[CONFIG_RAW_EXTERNAL])
967        return INV_ERROR_FEATURE_NOT_ENABLED;
968
969    for (ii = 0; ii < size && ii < 6; ii++) {
970        data[ii] = fifo_obj.decoded[REF_RAW_EXTERNAL + ii];
971    }
972
973    return INV_SUCCESS;
974#else
975    memset(data, 0, COMPASS_NUM_AXES * sizeof(long));
976    return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
977#endif
978}
979
980/**
981 *  Sends accelerometer data to the FIFO.
982 *
983 *  @param[in] elements Which of the 3 elements to send. Use INV_ALL for 3 axis
984 *            or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
985 *            for a subset.
986 *
987 * @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
988 *            bit data. Set to zero to remove it from the FIFO.
989 */
990inv_error_t inv_send_accel(uint_fast16_t elements, uint_fast16_t accuracy)
991{
992    INVENSENSE_FUNC_START;
993    unsigned char regs[4] = { DINAF8 + 1, DINA28, DINA30, DINA38 };
994    inv_error_t result;
995    int kk;
996
997    if (inv_get_state() < INV_STATE_DMP_OPENED)
998        return INV_ERROR_SM_IMPROPER_STATE;
999
1000    result = inv_construct3_fifo(regs, elements, accuracy, REF_ACCEL,
1001                                 KEY_CFG_12, CONFIG_ACCEL);
1002    if (result) {
1003        LOG_RESULT_LOCATION(result);
1004        return result;
1005    }
1006
1007    for (kk = 0; kk < ACCEL_NUM_AXES; kk++) {
1008        fifo_scale[REF_ACCEL + kk] = 2 * inv_obj.accel_sens;
1009    }
1010
1011    return inv_set_footer();
1012}
1013
1014/**
1015 * Sends control data to the FIFO. Control data is a 4 length vector of 32-bits.
1016 *
1017 *  @param[in] elements Which of the 4 elements to send. Use INV_ALL for all
1018 *            or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3, INV_ELEMENT_4 or'd
1019 *             together for a subset.
1020 *
1021 *  @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
1022 *             bit data. Set to zero to remove it from the FIFO.
1023 */
1024inv_error_t inv_send_cntrl_data(uint_fast16_t elements, uint_fast16_t accuracy)
1025{
1026    INVENSENSE_FUNC_START;
1027    int_fast8_t kk;
1028    inv_error_t result;
1029    unsigned char regs[5] = { DINAF8 + 1, DINA20, DINA28, DINA30, DINA38 };
1030
1031    if (inv_get_state() < INV_STATE_DMP_OPENED)
1032        return INV_ERROR_SM_IMPROPER_STATE;
1033
1034    elements = inv_set_fifo_reference(elements, accuracy, REF_CONTROL, 4);
1035    accuracy = inv_set_fifo_accuracy(elements, accuracy, CONFIG_CONTROL_DATA);
1036
1037    if (accuracy & INV_16_BIT) {
1038        regs[0] = DINAF8 + 2;
1039    }
1040
1041    fifo_obj.data_config[CONFIG_CONTROL_DATA] = elements | accuracy;
1042
1043    for (kk = 0; kk < 4; ++kk) {
1044        if ((elements & 1) == 0)
1045            regs[kk + 1] = DINAA0 + 3;
1046        elements >>= 1;
1047    }
1048
1049    result = inv_set_mpu_memory(KEY_CFG_1, 5, regs);
1050    if (result) {
1051        LOG_RESULT_LOCATION(result);
1052        return result;
1053    }
1054
1055    return inv_set_footer();
1056}
1057
1058/**
1059 * Adds a rolling counter to the fifo packet.  When used with the footer
1060 * the data comes out the first time:
1061 *
1062 * @code
1063 * <data0><data1>...<dataN><PacketNum0><PacketNum1>
1064 * @endcode
1065 * for every other packet it is
1066 *
1067 * @code
1068 * <FifoFooter0><FifoFooter1><data0><data1>...<dataN><PacketNum0><PacketNum1>
1069 * @endcode
1070 *
1071 * This allows for scanning of the fifo for packets
1072 *
1073 * @return INV_SUCCESS or error code
1074 */
1075inv_error_t inv_send_packet_number(uint_fast16_t accuracy)
1076{
1077    INVENSENSE_FUNC_START;
1078    inv_error_t result;
1079    unsigned char regs;
1080    uint_fast16_t elements;
1081
1082    if (inv_get_state() < INV_STATE_DMP_OPENED)
1083        return INV_ERROR_SM_IMPROPER_STATE;
1084
1085    elements = inv_set_fifo_reference(1, accuracy, REF_DMP_PACKET, 1);
1086    if (elements & 1) {
1087        regs = DINA28;
1088        fifo_obj.data_config[CONFIG_DMP_PACKET_NUMBER] =
1089            INV_ELEMENT_1 | INV_16_BIT;
1090    } else {
1091        regs = DINAF8 + 3;
1092        fifo_obj.data_config[CONFIG_DMP_PACKET_NUMBER] = 0;
1093    }
1094    result = inv_set_mpu_memory(KEY_CFG_23, 1, &regs);
1095    if (result) {
1096        LOG_RESULT_LOCATION(result);
1097        return result;
1098    }
1099
1100    return inv_set_footer();
1101}
1102
1103/**
1104 *  @brief  Send the computed gravity vectors into the FIFO.
1105 *          The gravity vectors can be retrieved from the FIFO via
1106 *          inv_get_gravity(), to have the gravitation vector expressed
1107 *          in coordinates relative to the body.
1108 *
1109 *  Gravity is a derived vector derived from the quaternion.
1110 *  @param  elements
1111 *              the gravitation vectors components bitmask.
1112 *              To send all compoents use INV_ALL.
1113 *  @param  accuracy
1114 *              The number of bits the gravitation vector is expressed
1115 *              into.
1116 *              Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
1117 *              bit data.
1118 *              Set to zero to remove it from the FIFO.
1119 *
1120 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
1121 */
1122inv_error_t inv_send_gravity(uint_fast16_t elements, uint_fast16_t accuracy)
1123{
1124    INVENSENSE_FUNC_START;
1125    inv_error_t result;
1126
1127    result = inv_send_quaternion(accuracy);
1128    if (result) {
1129        LOG_RESULT_LOCATION(result);
1130        return result;
1131    }
1132
1133    return inv_set_footer();
1134}
1135
1136/** Sends gyro data to the FIFO. Gyro data is a 3 length vector
1137 *  of 32-bits. Should be called once after inv_dmp_open() and before inv_dmp_start().
1138 *  @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of them
1139 *            or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
1140 *            for a subset.
1141 *  @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
1142 *             bit data. Set to zero to remove it from the FIFO.
1143 */
1144inv_error_t inv_send_gyro(uint_fast16_t elements, uint_fast16_t accuracy)
1145{
1146    INVENSENSE_FUNC_START;
1147    unsigned char regs[4] = { DINAF8 + 1, DINA20, DINA28, DINA30 };
1148    inv_error_t result;
1149
1150    if (inv_get_state() < INV_STATE_DMP_OPENED)
1151        return INV_ERROR_SM_IMPROPER_STATE;
1152
1153    if (fifo_obj.gyro_source == INV_GYRO_FROM_QUATERNION) {
1154        regs[0] = DINA90 + 5;
1155        result = inv_set_mpu_memory(KEY_CFG_GYRO_SOURCE, 1, regs);
1156        if (result) {
1157            LOG_RESULT_LOCATION(result);
1158            return result;
1159        }
1160        regs[0] = DINAF8 + 1;
1161        regs[1] = DINA20;
1162        regs[2] = DINA28;
1163        regs[3] = DINA30;
1164    } else {
1165        regs[0] = DINA90 + 10;
1166        result = inv_set_mpu_memory(KEY_CFG_GYRO_SOURCE, 1, regs);
1167        if (result) {
1168            LOG_RESULT_LOCATION(result);
1169            return result;
1170        }
1171        regs[0] = DINAF8 + 1;
1172        regs[1] = DINA28;
1173        regs[2] = DINA30;
1174        regs[3] = DINA38;
1175    }
1176    result = inv_construct3_fifo(regs, elements, accuracy, REF_GYROS,
1177                                 KEY_CFG_9, CONFIG_GYROS);
1178
1179    return inv_set_footer();
1180}
1181
1182/** Sends linear accelerometer data to the FIFO.
1183 *
1184 *  Linear accelerometer data is a 3 length vector of 32-bits. It is the
1185 *  acceleration in the body frame with gravity removed.
1186 *
1187 *
1188 *  @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of
1189 *            them or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
1190 *            for a subset.
1191 *
1192 *  NOTE: Elements is ignored if the fifo rate is < INV_MAX_NUM_ACCEL_SAMPLES
1193 *  @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
1194 *             bit data. Set to zero to remove it from the FIFO.
1195 */
1196inv_error_t inv_send_linear_accel(uint_fast16_t elements,
1197                                  uint_fast16_t accuracy)
1198{
1199    INVENSENSE_FUNC_START;
1200    inv_error_t result;
1201    unsigned char state = inv_get_state();
1202
1203    if (state < INV_STATE_DMP_OPENED)
1204        return INV_ERROR_SM_IMPROPER_STATE;
1205
1206    result = inv_send_gravity(elements, accuracy);
1207    if (result) {
1208        LOG_RESULT_LOCATION(result);
1209        return result;
1210    }
1211    result = inv_send_accel(elements, accuracy);
1212    if (result) {
1213        LOG_RESULT_LOCATION(result);
1214        return result;
1215    }
1216
1217    return inv_set_footer();
1218}
1219
1220/** Sends linear world accelerometer data to the FIFO. Linear world
1221 *  accelerometer data is a 3 length vector of 32-bits. It is the acceleration
1222 *  in the world frame with gravity removed. Should be called once after
1223 *  inv_dmp_open() and before inv_dmp_start().
1224 *
1225 *  @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of
1226 *             them or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
1227 *             for a subset.
1228 *  @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
1229 *             bit data.
1230 */
1231inv_error_t inv_send_linear_accel_in_world(uint_fast16_t elements,
1232                                           uint_fast16_t accuracy)
1233{
1234    INVENSENSE_FUNC_START;
1235    inv_error_t result;
1236
1237    result = inv_send_linear_accel(INV_ALL, accuracy);
1238    if (result) {
1239        LOG_RESULT_LOCATION(result);
1240        return result;
1241    }
1242    result = inv_send_quaternion(accuracy);
1243
1244    return inv_set_footer();
1245}
1246
1247/** Sends quaternion data to the FIFO. Quaternion data is a 4 length vector
1248 *   of 32-bits. Should be called once after inv_dmp_open() and before inv_dmp_start().
1249 * @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
1250 *            bit data.
1251 */
1252inv_error_t inv_send_quaternion(uint_fast16_t accuracy)
1253{
1254    INVENSENSE_FUNC_START;
1255    unsigned char regs[5] = { DINAF8 + 1, DINA20, DINA28,
1256        DINA30, DINA38
1257    };
1258    uint_fast16_t elements, kk;
1259    inv_error_t result;
1260
1261    if (inv_get_state() < INV_STATE_DMP_OPENED)
1262        return INV_ERROR_SM_IMPROPER_STATE;
1263
1264    elements = inv_set_fifo_reference(0xf, accuracy, REF_QUATERNION, 4);
1265    accuracy = inv_set_fifo_accuracy(elements, accuracy, CONFIG_QUAT);
1266
1267    if (accuracy & INV_16_BIT) {
1268        regs[0] = DINAF8 + 2;
1269    }
1270
1271    fifo_obj.data_config[CONFIG_QUAT] = elements | accuracy;
1272
1273    for (kk = 0; kk < 4; ++kk) {
1274        if ((elements & 1) == 0)
1275            regs[kk + 1] = DINAA0 + 3;
1276        elements >>= 1;
1277    }
1278
1279    result = inv_set_mpu_memory(KEY_CFG_8, 5, regs);
1280    if (result) {
1281        LOG_RESULT_LOCATION(result);
1282        return result;
1283    }
1284
1285    return inv_set_footer();
1286}
1287
1288/** Sends raw data to the FIFO.
1289 *  Should be called once after inv_dmp_open() and before inv_dmp_start().
1290 *  @param[in] elements Which of the 7 elements to send. Use INV_ALL for all of them
1291 *            or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 ... INV_ELEMENT_7 or'd together
1292 *            for a subset. The first element is temperature, the next 3 are gyro data,
1293 *            and the last 3 accel data.
1294 *  @param  accuracy
1295 *              The element's accuracy, can be INV_16_BIT, INV_32_BIT, or 0 to turn off.
1296 *  @return 0 if successful, a non-zero error code otherwise.
1297 */
1298inv_error_t inv_send_sensor_data(uint_fast16_t elements, uint_fast16_t accuracy)
1299{
1300    int result;
1301#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
1302	defined CONFIG_MPU_SENSORS_MPU6050B1
1303    unsigned char regs[7] = { DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
1304        DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
1305        DINAA0 + 3
1306    };
1307
1308    if (inv_get_state() < INV_STATE_DMP_OPENED)
1309        return INV_ERROR_SM_IMPROPER_STATE;
1310
1311    if (accuracy)
1312        accuracy = INV_16_BIT;
1313
1314    elements = inv_set_fifo_reference(elements, accuracy, REF_RAW, 7);
1315
1316    if (elements & 1)
1317        fifo_obj.data_config[CONFIG_TEMPERATURE] = 1 | INV_16_BIT;
1318    else
1319        fifo_obj.data_config[CONFIG_TEMPERATURE] = 0;
1320    if (elements & 0x7e)
1321        fifo_obj.data_config[CONFIG_RAW_DATA] =
1322            (0x3f & (elements >> 1)) | INV_16_BIT;
1323    else
1324        fifo_obj.data_config[CONFIG_RAW_DATA] = 0;
1325
1326    if (elements & INV_ELEMENT_1) {
1327        regs[0] = DINACA;
1328    }
1329    if (elements & INV_ELEMENT_2) {
1330        regs[1] = DINBC4;
1331    }
1332    if (elements & INV_ELEMENT_3) {
1333        regs[2] = DINACC;
1334    }
1335    if (elements & INV_ELEMENT_4) {
1336        regs[3] = DINBC6;
1337    }
1338    if (elements & INV_ELEMENT_5) {
1339        regs[4] = DINBC0;
1340    }
1341    if (elements & INV_ELEMENT_6) {
1342        regs[5] = DINAC8;
1343    }
1344    if (elements & INV_ELEMENT_7) {
1345        regs[6] = DINBC2;
1346    }
1347    result = inv_set_mpu_memory(KEY_CFG_15, 7, regs);
1348    if (result) {
1349        LOG_RESULT_LOCATION(result);
1350        return result;
1351    }
1352
1353    return inv_set_footer();
1354
1355#else
1356    INVENSENSE_FUNC_START;
1357    unsigned char regs[4] = { DINAA0 + 3,
1358        DINAA0 + 3,
1359        DINAA0 + 3,
1360        DINAA0 + 3
1361    };
1362
1363    if (inv_get_state() < INV_STATE_DMP_OPENED)
1364        return INV_ERROR_SM_IMPROPER_STATE;
1365
1366    if (accuracy)
1367        accuracy = INV_16_BIT;
1368
1369    elements = inv_set_fifo_reference(elements, accuracy, REF_RAW, 7);
1370
1371    if (elements & 0x03) {
1372        elements |= 0x03;
1373        regs[0] = DINA20;
1374    }
1375    if (elements & 0x0C) {
1376        elements |= 0x0C;
1377        regs[1] = DINA28;
1378    }
1379    if (elements & 0x30) {
1380        elements |= 0x30;
1381        regs[2] = DINA30;
1382    }
1383    if (elements & 0x40) {
1384        elements |= 0xC0;
1385        regs[3] = DINA38;
1386    }
1387
1388    result = inv_set_mpu_memory(KEY_CFG_15, 4, regs);
1389    if (result) {
1390        LOG_RESULT_LOCATION(result);
1391        return result;
1392    }
1393
1394    if (elements & 0x01)
1395        fifo_obj.data_config[CONFIG_TEMPERATURE] = 1 | INV_16_BIT;
1396    else
1397        fifo_obj.data_config[CONFIG_TEMPERATURE] = 0;
1398    if (elements & 0xfe)
1399        fifo_obj.data_config[CONFIG_RAW_DATA] =
1400            (0x7f & (elements >> 1)) | INV_16_BIT;
1401    else
1402        fifo_obj.data_config[CONFIG_RAW_DATA] = 0;
1403
1404    return inv_set_footer();
1405#endif
1406}
1407
1408/** Sends raw external data to the FIFO.
1409 *  Should be called once after inv_dmp_open() and before inv_dmp_start().
1410 *  @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of them
1411 *            or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
1412 *            for a subset.
1413 *  @param[in] accuracy INV_16_BIT to send data, 0 to stop sending this data.
1414 *            Sending and Stop sending are reference counted, so data actually
1415 *            stops when the reference reaches zero.
1416 */
1417inv_error_t inv_send_external_sensor_data(uint_fast16_t elements,
1418                                          uint_fast16_t accuracy)
1419{
1420#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
1421	defined CONFIG_MPU_SENSORS_MPU6050B1
1422    int result;
1423    unsigned char regs[6] = { DINAA0 + 3, DINAA0 + 3,
1424                              DINAA0 + 3, DINAA0 + 3,
1425                              DINAA0 + 3, DINAA0 + 3 };
1426
1427    if (inv_get_state() < INV_STATE_DMP_OPENED)
1428        return INV_ERROR_SM_IMPROPER_STATE;
1429
1430    if (accuracy)
1431        accuracy = INV_16_BIT;
1432
1433    elements = inv_set_fifo_reference(elements, accuracy, REF_RAW_EXTERNAL, 6);
1434
1435    if (elements)
1436        fifo_obj.data_config[CONFIG_RAW_EXTERNAL] = elements | INV_16_BIT;
1437    else
1438        fifo_obj.data_config[CONFIG_RAW_EXTERNAL] = 0;
1439
1440    if (elements & INV_ELEMENT_1) {
1441        regs[0] = DINBC2;
1442    }
1443    if (elements & INV_ELEMENT_2) {
1444        regs[1] = DINACA;
1445    }
1446    if (elements & INV_ELEMENT_3) {
1447        regs[2] = DINBC4;
1448    }
1449    if (elements & INV_ELEMENT_4) {
1450        regs[3] = DINBC0;
1451    }
1452    if (elements & INV_ELEMENT_5) {
1453        regs[4] = DINAC8;
1454    }
1455    if (elements & INV_ELEMENT_6) {
1456        regs[5] = DINACC;
1457    }
1458
1459    result = inv_set_mpu_memory(KEY_CFG_EXTERNAL, sizeof(regs), regs);
1460    if (result) {
1461        LOG_RESULT_LOCATION(result);
1462        return result;
1463    }
1464
1465    return inv_set_footer();
1466
1467#else
1468    return INV_ERROR_FEATURE_NOT_IMPLEMENTED;    // Feature not supported
1469#endif
1470}
1471
1472/**
1473 *  @brief  Send the Quantized Acceleromter data into the FIFO.  The data can be
1474 *          retrieved using inv_get_quantized_accel() or inv_get_unquantized_accel().
1475 *
1476 *  To be useful this should be set to fifo_rate + 1 if less than
1477 *  INV_MAX_NUM_ACCEL_SAMPLES, otherwise it doesn't work.
1478 *
1479 *  @param  elements
1480 *              the components bitmask.
1481 *              To send all compoents use INV_ALL.
1482 *
1483 *  @param  accuracy
1484 *              Use INV_32_BIT for 32-bit data or INV_16_BIT for
1485 *              16-bit data.
1486 *              Set to zero to remove it from the FIFO.
1487 *
1488 *  @return INV_SUCCESS if successful, a non-zero error code otherwise.
1489 */
1490inv_error_t inv_send_quantized_accel(uint_fast16_t elements,
1491                                     uint_fast16_t accuracy)
1492{
1493    INVENSENSE_FUNC_START;
1494    unsigned char regs[5] = { DINAF8 + 1, DINA20, DINA28,
1495        DINA30, DINA38
1496    };
1497    unsigned char regs2[4] = { DINA20, DINA28,
1498        DINA30, DINA38
1499    };
1500    inv_error_t result;
1501    int_fast8_t kk;
1502    int_fast8_t ii;
1503
1504    if (inv_get_state() < INV_STATE_DMP_OPENED)
1505        return INV_ERROR_SM_IMPROPER_STATE;
1506
1507    elements = inv_set_fifo_reference(elements, accuracy, REF_QUANT_ACCEL, 8);
1508
1509    if (elements) {
1510        fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL] = (elements) | INV_32_BIT;
1511    } else {
1512        fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL] = 0;
1513    }
1514
1515    for (kk = 0; kk < INV_MAX_NUM_ACCEL_SAMPLES; ++kk) {
1516        fifo_obj.decoded[REF_QUANT_ACCEL + kk] = 0;
1517        for (ii = 0; ii < ACCEL_NUM_AXES; ii++) {
1518            fifo_obj.decoded_accel[kk][ii] = 0;
1519        }
1520    }
1521
1522    for (kk = 0; kk < 4; ++kk) {
1523        if ((elements & 1) == 0)
1524            regs[kk + 1] = DINAA0 + 3;
1525        elements >>= 1;
1526    }
1527
1528    result = inv_set_mpu_memory(KEY_CFG_TAP0, 5, regs);
1529    if (result) {
1530        LOG_RESULT_LOCATION(result);
1531        return result;
1532    }
1533
1534    for (kk = 0; kk < 4; ++kk) {
1535        if ((elements & 1) == 0)
1536            regs2[kk] = DINAA0 + 3;
1537        elements >>= 1;
1538    }
1539
1540    result = inv_set_mpu_memory(KEY_CFG_TAP4, 4, regs2);
1541    if (result) {
1542        LOG_RESULT_LOCATION(result);
1543        return result;
1544    }
1545
1546    return inv_set_footer();
1547}
1548
1549inv_error_t inv_send_eis(uint_fast16_t elements, uint_fast16_t accuracy)
1550{
1551    INVENSENSE_FUNC_START;
1552    int_fast8_t kk;
1553    unsigned char regs[3] = { DINA28, DINA30, DINA38 };
1554    inv_error_t result;
1555
1556    if (inv_get_state() < INV_STATE_DMP_OPENED)
1557        return INV_ERROR_SM_IMPROPER_STATE;
1558
1559    if (accuracy) {
1560        accuracy = INV_32_BIT;
1561    }
1562
1563    elements = inv_set_fifo_reference(elements, accuracy, REF_EIS, 3);
1564    accuracy = inv_set_fifo_accuracy(elements, accuracy, CONFIG_EIS);
1565
1566    fifo_obj.data_config[CONFIG_EIS] = elements | accuracy;
1567
1568    for (kk = 0; kk < 3; ++kk) {
1569        if ((elements & 1) == 0)
1570            regs[kk] = DINAA0 + 7;
1571        elements >>= 1;
1572    }
1573
1574    result = inv_set_mpu_memory(KEY_P_EIS_FIFO_XSHIFT, 3, regs);
1575
1576    return inv_set_footer();
1577}
1578
1579/**
1580 * @brief       Returns 3-element vector of accelerometer data in body frame.
1581 *
1582 * @param[out]  data    3-element vector of accelerometer data in body frame.
1583 *                      One gee = 2^16.
1584 *  @return     0 on success or an error code.
1585 */
1586inv_error_t inv_get_accel(long *data)
1587{
1588    int kk;
1589    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
1590
1591    if (data == NULL)
1592        return INV_ERROR_INVALID_PARAMETER;
1593
1594    if ((!fifo_obj.data_config[CONFIG_ACCEL] &&
1595         (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR))
1596        ||
1597        (!(mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) &&
1598         !inv_accel_present()))
1599        return INV_ERROR_FEATURE_NOT_ENABLED;
1600
1601    for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
1602        data[kk] = fifo_obj.decoded[REF_ACCEL + kk];
1603    }
1604
1605    return INV_SUCCESS;
1606}
1607
1608/**
1609 *  @brief      Returns 4-element quaternion vector derived from 6-axis or
1610 *  9-axis if 9-axis was implemented. 6-axis is gyros and accels. 9-axis is
1611 *  gyros, accel and compass.
1612 *
1613 *  @param[out] data    4-element quaternion vector. One is scaled to 2^30.
1614 *  @return     0 on success or an error code.
1615 */
1616inv_error_t inv_get_quaternion(long *data)
1617{
1618    int kk;
1619
1620    if (data == NULL)
1621        return INV_ERROR_INVALID_PARAMETER;
1622
1623    if (!fifo_obj.data_config[CONFIG_QUAT])
1624        return INV_ERROR_FEATURE_NOT_ENABLED;
1625
1626    for (kk = 0; kk < 4; ++kk) {
1627        data[kk] = fifo_obj.decoded[REF_QUATERNION + kk];
1628    }
1629
1630    return INV_SUCCESS;
1631}
1632
1633/**
1634 *  @brief      Returns 4-element quaternion vector derived from 6
1635 *              axis sensors (gyros and accels).
1636 *  @param[out] data
1637 *                  4-element quaternion vector. One is scaled to 2^30.
1638 *  @return     0 on success or an error code.
1639 */
1640inv_error_t inv_get_6axis_quaternion(long *data)
1641{
1642    int kk;
1643    if (data == NULL)
1644        return INV_ERROR_INVALID_PARAMETER;
1645
1646    if (!fifo_obj.data_config[CONFIG_QUAT])
1647        return INV_ERROR_FEATURE_NOT_ENABLED;
1648
1649    for (kk = 0; kk < 4; ++kk) {
1650        data[kk] = fifo_obj.decoded[REF_QUATERNION_6AXIS + kk];
1651    }
1652
1653    return INV_SUCCESS;
1654}
1655
1656inv_error_t inv_get_relative_quaternion(long *data)
1657{
1658    if (data == NULL)
1659        return INV_ERROR;
1660    data[0] = inv_obj.relative_quat[0];
1661    data[1] = inv_obj.relative_quat[1];
1662    data[2] = inv_obj.relative_quat[2];
1663    data[3] = inv_obj.relative_quat[3];
1664    return INV_SUCCESS;
1665}
1666
1667/**
1668 *  @brief  Returns 3-element vector of gyro data in body frame.
1669 *  @param[out] data
1670 *              3-element vector of gyro data in body frame
1671 *              with gravity removed. One degree per second = 2^16.
1672 *  @return 0 on success or an error code.
1673 */
1674inv_error_t inv_get_gyro(long *data)
1675{
1676    int kk;
1677    if (data == NULL)
1678        return INV_ERROR_INVALID_PARAMETER;
1679
1680    if (fifo_obj.data_config[CONFIG_GYROS]) {
1681        for (kk = 0; kk < 3; ++kk) {
1682            data[kk] = fifo_obj.decoded[REF_GYROS + kk];
1683        }
1684        return INV_SUCCESS;
1685    } else {
1686        return INV_ERROR_FEATURE_NOT_ENABLED;
1687    }
1688}
1689
1690/**
1691 *  @brief  Get the 3-element gravity vector from the FIFO expressed
1692 *          in coordinates relative to the body frame.
1693 *  @param  data
1694 *              3-element vector of gravity in body frame.
1695 *  @return 0 on success or an error code.
1696 */
1697inv_error_t inv_get_gravity(long *data)
1698{
1699    long quat[4];
1700    int ii;
1701    inv_error_t result;
1702
1703    if (data == NULL)
1704        return INV_ERROR_INVALID_PARAMETER;
1705
1706    if (!fifo_obj.data_config[CONFIG_QUAT])
1707        return INV_ERROR_FEATURE_NOT_ENABLED;
1708
1709    if ((fifo_obj.cache & FIFO_CACHE_GRAVITY_BODY) == 0) {
1710        fifo_obj.cache |= FIFO_CACHE_GRAVITY_BODY;
1711
1712        // Compute it from Quaternion
1713        result = inv_get_quaternion(quat);
1714        if (result) {
1715            LOG_RESULT_LOCATION(result);
1716            return result;
1717        }
1718
1719        data[0] =
1720            inv_q29_mult(quat[1], quat[3]) - inv_q29_mult(quat[2], quat[0]);
1721        data[1] =
1722            inv_q29_mult(quat[2], quat[3]) + inv_q29_mult(quat[1], quat[0]);
1723        data[2] =
1724            (inv_q29_mult(quat[3], quat[3]) + inv_q29_mult(quat[0], quat[0])) -
1725            1073741824L;
1726
1727        for (ii = 0; ii < ACCEL_NUM_AXES; ii++) {
1728            data[ii] >>= 14;
1729            fifo_obj.gravity_cache[ii] = data[ii];
1730        }
1731    } else {
1732        data[0] = fifo_obj.gravity_cache[0];
1733        data[1] = fifo_obj.gravity_cache[1];
1734        data[2] = fifo_obj.gravity_cache[2];
1735    }
1736
1737    return INV_SUCCESS;
1738}
1739
1740/**
1741* @brief        Sets the filter coefficent used for computing the acceleration
1742*               bias which is used to compute linear acceleration.
1743* @param[in] coef   Fitler coefficient. 0. means no filter, a small number means
1744*                   a small cutoff frequency with an increasing number meaning
1745*                   an increasing cutoff frequency.
1746*/
1747inv_error_t inv_set_linear_accel_filter_coef(float coef)
1748{
1749    fifo_obj.acc_filter_coef = coef;
1750    return INV_SUCCESS;
1751}
1752
1753/**
1754 *  @brief      Returns 3-element vector of accelerometer data in body frame
1755 *              with gravity removed.
1756 *  @param[out] data    3-element vector of accelerometer data in body frame
1757 *                      with gravity removed. One g = 2^16.
1758 *  @return     0 on success or an error code. data unchanged on error.
1759 */
1760inv_error_t inv_get_linear_accel(long *data)
1761{
1762    int kk;
1763    long grav[3];
1764    long la[3];
1765    inv_error_t result;
1766
1767    if (data == NULL)
1768        return INV_ERROR_INVALID_PARAMETER;
1769
1770    result = inv_get_gravity(grav);
1771    if (result) {
1772        LOG_RESULT_LOCATION(result);
1773        return result;
1774    }
1775    result = inv_get_accel(la);
1776    if (result) {
1777        LOG_RESULT_LOCATION(result);
1778        return result;
1779    }
1780
1781    if ((fifo_obj.cache & FIFO_CACHE_ACC_BIAS) == 0) {
1782        fifo_obj.cache |= FIFO_CACHE_ACC_BIAS;
1783
1784        for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
1785            long x;
1786            x = la[kk] - grav[kk];
1787            fifo_obj.acc_bias_filt[kk] = (long)(x * fifo_obj.acc_filter_coef +
1788                                                fifo_obj.acc_bias_filt[kk] *
1789                                                (1.f -
1790                                                 fifo_obj.acc_filter_coef));
1791            data[kk] = x - fifo_obj.acc_bias_filt[kk];
1792        }
1793    } else {
1794        for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
1795            data[kk] = la[kk] - grav[kk] - fifo_obj.acc_bias_filt[kk];
1796        }
1797    }
1798    return INV_SUCCESS;
1799}
1800
1801/**
1802 *  @brief      Returns 3-element vector of accelerometer data in world frame
1803 *              with gravity removed.
1804 *  @param[out] data    3-element vector of accelerometer data in world frame
1805 *                      with gravity removed. One g = 2^16.
1806 *  @return     0 on success or an error code.
1807 */
1808inv_error_t inv_get_linear_accel_in_world(long *data)
1809{
1810    int kk;
1811    if (data == NULL)
1812        return INV_ERROR_INVALID_PARAMETER;
1813    if (fifo_obj.data_config[CONFIG_ACCEL] && fifo_obj.data_config[CONFIG_QUAT]) {
1814        long wtemp[4], qi[4], wtemp2[4];
1815        wtemp[0] = 0;
1816        inv_get_linear_accel(&wtemp[1]);
1817        inv_q_mult(&fifo_obj.decoded[REF_QUATERNION], wtemp, wtemp2);
1818        inv_q_invert(&fifo_obj.decoded[REF_QUATERNION], qi);
1819        inv_q_mult(wtemp2, qi, wtemp);
1820        for (kk = 0; kk < 3; ++kk) {
1821            data[kk] = wtemp[kk + 1];
1822        }
1823        return INV_SUCCESS;
1824    } else {
1825        return INV_ERROR_FEATURE_NOT_ENABLED;
1826    }
1827}
1828
1829/**
1830 *  @brief      Returns 4-element vector of control data.
1831 *  @param[out] data    4-element vector of control data.
1832 *  @return     0 for succes or an error code.
1833 */
1834inv_error_t inv_get_cntrl_data(long *data)
1835{
1836    int kk;
1837    if (data == NULL)
1838        return INV_ERROR_INVALID_PARAMETER;
1839
1840    if (!fifo_obj.data_config[CONFIG_CONTROL_DATA])
1841        return INV_ERROR_FEATURE_NOT_ENABLED;
1842
1843    for (kk = 0; kk < 4; ++kk) {
1844        data[kk] = fifo_obj.decoded[REF_CONTROL + kk];
1845    }
1846
1847    return INV_SUCCESS;
1848
1849}
1850
1851/**
1852 *  @brief      Returns 3-element vector of EIS shfit data
1853 *  @param[out] data    3-element vector of EIS shift data.
1854 *  @return     0 for succes or an error code.
1855 */
1856inv_error_t inv_get_eis(long *data)
1857{
1858    int kk;
1859    if (data == NULL)
1860        return INV_ERROR_INVALID_PARAMETER;
1861
1862    if (!fifo_obj.data_config[CONFIG_EIS])
1863        return INV_ERROR_FEATURE_NOT_ENABLED;
1864
1865    for (kk = 0; kk < 3; ++kk) {
1866        data[kk] = fifo_obj.decoded[REF_EIS + kk];
1867    }
1868
1869    return INV_SUCCESS;
1870
1871}
1872
1873/**
1874 *  @brief      Returns 3-element vector of accelerometer data in body frame.
1875 *  @param[out] data    3-element vector of accelerometer data in body frame in g's.
1876 *  @return     0 for success or an error code.
1877 */
1878inv_error_t inv_get_accel_float(float *data)
1879{
1880    long lData[3];
1881    int kk;
1882    int result;
1883
1884    if (data == NULL)
1885        return INV_ERROR_INVALID_PARAMETER;
1886
1887    result = inv_get_accel(lData);
1888    if (result) {
1889        LOG_RESULT_LOCATION(result);
1890        return result;
1891    }
1892
1893    for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
1894        data[kk] = lData[kk] / 65536.0f;
1895    }
1896
1897    return INV_SUCCESS;
1898}
1899
1900/**
1901 *  @brief      Returns 4-element quaternion vector.
1902 *  @param[out] data    4-element quaternion vector.
1903 *  @return     0 on success, an error code otherwise.
1904 */
1905inv_error_t inv_get_quaternion_float(float *data)
1906{
1907    int kk;
1908
1909    if (data == NULL)
1910        return INV_ERROR_INVALID_PARAMETER;
1911
1912    if (!fifo_obj.data_config[CONFIG_QUAT])
1913        return INV_ERROR_FEATURE_NOT_ENABLED;
1914
1915    for (kk = 0; kk < 4; ++kk) {
1916        data[kk] = fifo_obj.decoded[REF_QUATERNION + kk] / 1073741824.0f;
1917    }
1918
1919    return INV_SUCCESS;
1920}
1921
1922/**
1923 * @brief   Command the MPU to put data in the FIFO at a particular rate.
1924 *
1925 *          The DMP will add fifo entries every fifoRate + 1 MPU cycles.  For
1926 *          example if the MPU is running at 200Hz the following values apply:
1927 *
1928 *          <TABLE>
1929 *          <TR><TD>fifoRate</TD><TD>DMP Sample Rate</TD><TD>FIFO update frequency</TD></TR>
1930 *          <TR><TD>0</TD><TD>200Hz</TD><TD>200Hz</TD></TR>
1931 *          <TR><TD>1</TD><TD>200Hz</TD><TD>100Hz</TD></TR>
1932 *          <TR><TD>2</TD><TD>200Hz</TD><TD>50Hz</TD></TR>
1933 *          <TR><TD>4</TD><TD>200Hz</TD><TD>40Hz</TD></TR>
1934 *          <TR><TD>9</TD><TD>200Hz</TD><TD>20Hz</TD></TR>
1935 *          <TR><TD>19</TD><TD>200Hz</TD><TD>10Hz</TD></TR>
1936 *          </TABLE>
1937 *
1938 *          Note: if the DMP is running, (state == INV_STATE_DMP_STARTED)
1939 *          then inv_run_state_callbacks() will be called to allow features
1940 *          that depend upon fundamental constants to be updated.
1941 *
1942 *  @pre    inv_dmp_open()
1943 *          @ifnot MPL_MF
1944 *              or inv_open_low_power_pedometer()
1945 *              or inv_eis_open_dmp()
1946 *          @endif
1947 *          and inv_dmp_start()
1948 *          must <b>NOT</b> have been called.
1949 *
1950 * @param   fifoRate    Divider value - 1.  Output rate is
1951 *          (DMP Sample Rate) / (fifoRate + 1).
1952 *
1953 * @return  INV_SUCCESS if successful, ML error code on any failure.
1954 */
1955inv_error_t inv_set_fifo_rate(unsigned short fifoRate)
1956{
1957    INVENSENSE_FUNC_START;
1958    unsigned char regs[2];
1959    unsigned char state;
1960    inv_error_t result = INV_SUCCESS;
1961    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
1962
1963    state = inv_get_state();
1964    if (state != INV_STATE_DMP_OPENED && state != INV_STATE_DMP_STARTED)
1965        return INV_ERROR_SM_IMPROPER_STATE;
1966
1967    fifo_obj.fifo_rate = fifoRate;
1968
1969    if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) {
1970
1971        regs[0] = (unsigned char)((fifoRate >> 8) & 0xff);
1972        regs[1] = (unsigned char)(fifoRate & 0xff);
1973
1974        result = inv_set_mpu_memory(KEY_D_0_22, 2, regs);
1975        if (result) {
1976            LOG_RESULT_LOCATION(result);
1977            return result;
1978        }
1979        fifo_obj.sample_step_size_ms =
1980            (unsigned short)(((long)fifoRate + 1) *
1981                             (inv_mpu_get_sampling_period_us
1982                              (mldl_cfg)) / 1000L);
1983
1984        if (state == INV_STATE_DMP_STARTED)
1985            result = inv_run_state_callbacks(state);
1986    } else if (mldl_cfg->requested_sensors & INV_THREE_AXIS_ACCEL) {
1987        struct ext_slave_config config;
1988        long data;
1989        config.key = MPU_SLAVE_CONFIG_ODR_RESUME;
1990        config.len = sizeof(long);
1991        config.apply = (state == INV_STATE_DMP_STARTED);
1992        config.data = &data;
1993        data = (1000 * inv_mpu_get_sampling_rate_hz(mldl_cfg)) / (fifoRate + 1);
1994
1995        /* Ask for the same frequency */
1996        result = inv_mpu_config_accel(mldl_cfg,
1997                                      inv_get_serial_handle(),
1998                                      inv_get_serial_handle(), &config);
1999        if (result) {
2000            LOG_RESULT_LOCATION(result);
2001            return result;
2002        }
2003        result = inv_mpu_get_accel_config(mldl_cfg,
2004                                          inv_get_serial_handle(),
2005                                          inv_get_serial_handle(), &config);
2006        if (result) {
2007            LOG_RESULT_LOCATION(result);
2008            return result;
2009        }
2010        if(FIFO_DEBUG)
2011            MPL_LOGI("Actual ODR: %ld Hz\n", data / 1000);
2012        /* Record the actual frequency granted odr is in mHz */
2013        fifo_obj.sample_step_size_ms = (unsigned short)((1000L * 1000L) / data);
2014    }
2015    return result;
2016}
2017
2018/**
2019 * @brief   Retrieve the current FIFO update divider - 1.
2020 *          See inv_set_fifo_rate() for how this value is used.
2021 *
2022 *          The fifo rate when there is no fifo is the equivilent divider when
2023 *          derived from the value set by SetSampleSteSizeMs()
2024 *
2025 * @return  The value of the fifo rate divider or INV_INVALID_FIFO_RATE on error.
2026 */
2027unsigned short inv_get_fifo_rate(void)
2028{
2029    return fifo_obj.fifo_rate;
2030}
2031
2032/**
2033 * @brief   Returns the step size for quaternion type data.
2034 *
2035 * Typically the data rate for each FIFO packet. When the gryos are sleeping
2036 * this value will return the last value set by SetSampleStepSizeMs()
2037 *
2038 * @return  step size for quaternion type data
2039 */
2040int_fast16_t inv_get_sample_step_size_ms(void)
2041{
2042    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
2043
2044    if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR)
2045        return (fifo_obj.fifo_rate + 1) *
2046            (inv_mpu_get_sampling_period_us(mldl_cfg) / 1000);
2047    else
2048        return fifo_obj.sample_step_size_ms;
2049}
2050
2051/**
2052 * @brief   Returns the step size for quaternion type data.
2053 *
2054 * Typically the data rate for each FIFO packet. When the gryos are sleeping
2055 * this value will return the last value set by SetSampleStepSizeMs()
2056 *
2057 * @return  step size for quaternion type data
2058 */
2059int_fast16_t inv_get_sample_frequency(void)
2060{
2061    struct mldl_cfg *mldl_cfg = inv_get_dl_config();
2062
2063    if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR)
2064        return (inv_mpu_get_sampling_rate_hz(mldl_cfg) /
2065                (fifo_obj.fifo_rate + 1));
2066    else
2067        return (1000 / fifo_obj.sample_step_size_ms);
2068}
2069
2070/**
2071 *  @brief  The gyro data magnitude squared :
2072 *          (1 degree per second)^2 = 2^6 = 2^GYRO_MAG_SQR_SHIFT.
2073 *  @return the computed magnitude squared output of the gyroscope.
2074 */
2075unsigned long inv_get_gyro_sum_of_sqr(void)
2076{
2077    unsigned long gmag = 0;
2078    long temp;
2079    int kk;
2080
2081    for (kk = 0; kk < 3; ++kk) {
2082        temp = fifo_obj.decoded[REF_GYROS + kk] >>
2083            (16 - (GYRO_MAG_SQR_SHIFT / 2));
2084        gmag += temp * temp;
2085    }
2086
2087    return gmag;
2088}
2089
2090/**
2091 *  @brief  The gyro data magnitude squared:
2092 *          (1 g)^2 = 2^16 = 2^ACC_MAG_SQR_SHIFT.
2093 *  @return the computed magnitude squared output of the accelerometer.
2094 */
2095unsigned long inv_accel_sum_of_sqr(void)
2096{
2097    unsigned long amag = 0;
2098    long temp;
2099    int kk;
2100    long accel[3];
2101    inv_error_t result;
2102
2103    result = inv_get_accel(accel);
2104    if (INV_SUCCESS != result) {
2105        return 0;
2106    }
2107
2108    for (kk = 0; kk < 3; ++kk) {
2109        temp = accel[kk] >> (16 - (ACC_MAG_SQR_SHIFT / 2));
2110        amag += temp * temp;
2111    }
2112    return amag;
2113}
2114
2115/**
2116 *  @internal
2117 */
2118void inv_override_quaternion(float *q)
2119{
2120    int kk;
2121    for (kk = 0; kk < 4; ++kk) {
2122        fifo_obj.decoded[REF_QUATERNION + kk] = (long)(q[kk] * (1L << 30));
2123    }
2124}
2125
2126/**
2127 * @internal
2128 * @brief   This registers a function to be called for each set of
2129 *          gyro/quaternion/rotation matrix/etc output.
2130 * @param[in] func The callback function to register
2131 * @param[in] priority The unique priority number of the callback. Lower numbers
2132 *            are called first.
2133 * @return  error code.
2134 */
2135inv_error_t inv_register_fifo_rate_process(inv_obj_func func, int priority)
2136{
2137    INVENSENSE_FUNC_START;
2138    inv_error_t result;
2139    int kk, nn;
2140
2141    result = inv_lock_mutex(fifo_rate_obj.mutex);
2142    if (INV_SUCCESS != result) {
2143        return result;
2144    }
2145    // Make sure we haven't registered this function already
2146    // Or used the same priority
2147    for (kk = 0; kk < fifo_rate_obj.num_cb; ++kk) {
2148        if ((fifo_rate_obj.fifo_process_cb[kk] == func) ||
2149            (fifo_rate_obj.priority[kk] == priority)) {
2150            inv_unlock_mutex(fifo_rate_obj.mutex);
2151            return INV_ERROR_INVALID_PARAMETER;
2152        }
2153    }
2154
2155    // Make sure we have not filled up our number of allowable callbacks
2156    if (fifo_rate_obj.num_cb <= MAX_HIGH_RATE_PROCESSES - 1) {
2157        kk = 0;
2158        if (fifo_rate_obj.num_cb != 0) {
2159            // set kk to be where this new callback goes in the array
2160            while ((kk < fifo_rate_obj.num_cb) &&
2161                   (fifo_rate_obj.priority[kk] < priority)) {
2162                kk++;
2163            }
2164            if (kk != fifo_rate_obj.num_cb) {
2165                // We need to move the others
2166                for (nn = fifo_rate_obj.num_cb; nn > kk; --nn) {
2167                    fifo_rate_obj.fifo_process_cb[nn] =
2168                        fifo_rate_obj.fifo_process_cb[nn - 1];
2169                    fifo_rate_obj.priority[nn] = fifo_rate_obj.priority[nn - 1];
2170                }
2171            }
2172        }
2173        // Add new callback
2174        fifo_rate_obj.fifo_process_cb[kk] = func;
2175        fifo_rate_obj.priority[kk] = priority;
2176        fifo_rate_obj.num_cb++;
2177    } else {
2178        result = INV_ERROR_MEMORY_EXAUSTED;
2179    }
2180
2181    inv_unlock_mutex(fifo_rate_obj.mutex);
2182    return result;
2183}
2184
2185/**
2186 * @internal
2187 * @brief   This unregisters a function to be called for each set of
2188 *          gyro/quaternion/rotation matrix/etc output.
2189 * @return  error code.
2190 */
2191inv_error_t inv_unregister_fifo_rate_process(inv_obj_func func)
2192{
2193    INVENSENSE_FUNC_START;
2194    int kk, jj;
2195    inv_error_t result;
2196
2197    result = inv_lock_mutex(fifo_rate_obj.mutex);
2198    if (INV_SUCCESS != result) {
2199        return result;
2200    }
2201    // Make sure we haven't registered this function already
2202    result = INV_ERROR_INVALID_PARAMETER;
2203    for (kk = 0; kk < fifo_rate_obj.num_cb; ++kk) {
2204        if (fifo_rate_obj.fifo_process_cb[kk] == func) {
2205            for (jj = kk + 1; jj < fifo_rate_obj.num_cb; ++jj) {
2206                fifo_rate_obj.fifo_process_cb[jj - 1] =
2207                    fifo_rate_obj.fifo_process_cb[jj];
2208                fifo_rate_obj.priority[jj - 1] =
2209                    fifo_rate_obj.priority[jj];
2210            }
2211            fifo_rate_obj.fifo_process_cb[fifo_rate_obj.num_cb - 1] = NULL;
2212            fifo_rate_obj.priority[fifo_rate_obj.num_cb - 1] = 0;
2213            fifo_rate_obj.num_cb--;
2214            result = INV_SUCCESS;
2215            break;
2216        }
2217    }
2218
2219    inv_unlock_mutex(fifo_rate_obj.mutex);
2220    return result;
2221
2222}
2223#ifdef UMPL
2224bool bFIFIDataAvailable = FALSE;
2225bool isUmplDataInFIFO(void)
2226{
2227    return bFIFIDataAvailable;
2228}
2229void setUmplDataInFIFOFlag(bool flag)
2230{
2231    bFIFIDataAvailable = flag;
2232}
2233#endif
2234inv_error_t inv_run_fifo_rate_processes(void)
2235{
2236    int kk;
2237    inv_error_t result, result2;
2238
2239    result = inv_lock_mutex(fifo_rate_obj.mutex);
2240    if (INV_SUCCESS != result) {
2241        MPL_LOGE("MLOsLockMutex returned %d\n", result);
2242        return result;
2243    }
2244    // User callbacks take priority over the fifo_process_cb callback
2245    if (fifo_obj.fifo_process_cb)
2246        fifo_obj.fifo_process_cb();
2247
2248    for (kk = 0; kk < fifo_rate_obj.num_cb; ++kk) {
2249        if (fifo_rate_obj.fifo_process_cb[kk]) {
2250            result2 = fifo_rate_obj.fifo_process_cb[kk] (&inv_obj);
2251            if (result == INV_SUCCESS)
2252#ifdef UMPL
2253	 setUmplDataInFIFOFlag(TRUE);
2254#endif
2255                result = result2;
2256        }
2257    }
2258
2259    inv_unlock_mutex(fifo_rate_obj.mutex);
2260    return result;
2261}
2262
2263/*********************/
2264         /** \}*//* defgroup */
2265/*********************/
2266