2265 lines
70 KiB
C
2265 lines
70 KiB
C
/*
|
|
$License:
|
|
Copyright 2011 InvenSense, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
$
|
|
*/
|
|
/*******************************************************************************
|
|
*
|
|
* $Id: mlFIFO.c 5653 2011-06-16 21:06:55Z nroyer $
|
|
*
|
|
*******************************************************************************/
|
|
|
|
/**
|
|
* @defgroup MLFIFO
|
|
* @brief Motion Library - FIFO Driver.
|
|
* The FIFO API Interface.
|
|
*
|
|
* @{
|
|
* @file mlFIFO.c
|
|
* @brief FIFO Interface.
|
|
**/
|
|
|
|
#include <string.h>
|
|
#include "mpu.h"
|
|
#if defined CONFIG_MPU_SENSORS_MPU6050A2
|
|
# include "mpu6050a2.h"
|
|
#elif defined CONFIG_MPU_SENSORS_MPU6050B1
|
|
# include "mpu6050b1.h"
|
|
#elif defined CONFIG_MPU_SENSORS_MPU3050
|
|
# include "mpu3050.h"
|
|
#else
|
|
#error Invalid or undefined CONFIG_MPU_SENSORS_MPUxxxx
|
|
#endif
|
|
#include "mlFIFO.h"
|
|
#include "mlFIFOHW.h"
|
|
#include "dmpKey.h"
|
|
#include "mlMathFunc.h"
|
|
#include "ml.h"
|
|
#include "mldl.h"
|
|
#include "mldl_cfg.h"
|
|
#include "mlstates.h"
|
|
#include "mlsupervisor.h"
|
|
#include "mlos.h"
|
|
#include "mlmath.h"
|
|
#include "accel.h"
|
|
|
|
#include "log.h"
|
|
#undef MPL_LOG_TAG
|
|
#define MPL_LOG_TAG "MPL-fifo"
|
|
|
|
#define FIFO_DEBUG 0
|
|
|
|
#define REF_QUATERNION (0)
|
|
#define REF_GYROS (REF_QUATERNION + 4)
|
|
#define REF_CONTROL (REF_GYROS + 3)
|
|
#define REF_RAW (REF_CONTROL + 4)
|
|
#define REF_RAW_EXTERNAL (REF_RAW + 8)
|
|
#define REF_ACCEL (REF_RAW_EXTERNAL + 6)
|
|
#define REF_QUANT_ACCEL (REF_ACCEL + 3)
|
|
#define REF_QUATERNION_6AXIS (REF_QUANT_ACCEL + INV_MAX_NUM_ACCEL_SAMPLES)
|
|
#define REF_EIS (REF_QUATERNION_6AXIS + 4)
|
|
#define REF_DMP_PACKET (REF_EIS + 3)
|
|
#define REF_GARBAGE (REF_DMP_PACKET + 1)
|
|
#define REF_LAST (REF_GARBAGE + 1)
|
|
|
|
long fifo_scale[REF_LAST] = {
|
|
(1L << 30), (1L << 30), (1L << 30), (1L << 30), // Quaternion
|
|
// 2^(16+30)/((2^30)*((3.14159265358/180)/200)/2)
|
|
1501974482L, 1501974482L, 1501974482L, // Gyro
|
|
(1L << 30), (1L << 30), (1L << 30), (1L << 30), // Control
|
|
(1L << 14), // Temperature
|
|
(1L << 14), (1L << 14), (1L << 14), // Raw Gyro
|
|
(1L << 14), (1L << 14), (1L << 14), (0), // Raw Accel, plus padding
|
|
(1L << 14), (1L << 14), (1L << 14), // Raw External
|
|
(1L << 14), (1L << 14), (1L << 14), // Raw External
|
|
(1L << 16), (1L << 16), (1L << 16), // Accel
|
|
(1L << 30), (1L << 30), (1L << 30), (1L << 30), // Quant Accel
|
|
(1L << 30), (1L << 30), (1L << 30), (1L << 30), //Quant Accel
|
|
(1L << 30), (1L << 30), (1L << 30), (1L << 30), // Quaternion 6 Axis
|
|
(1L << 30), (1L << 30), (1L << 30), // EIS
|
|
(1L << 30), // Packet
|
|
(1L << 30), // Garbage
|
|
};
|
|
|
|
// The scale factors for tap need to match the number in fifo_scale above.
|
|
// fifo_base_offset below may also need to be changed if this is not 8
|
|
#if INV_MAX_NUM_ACCEL_SAMPLES != 8
|
|
#error INV_MAX_NUM_ACCEL_SAMPLES must be defined to 8
|
|
#endif
|
|
|
|
#define CONFIG_QUAT (0)
|
|
#define CONFIG_GYROS (CONFIG_QUAT + 1)
|
|
#define CONFIG_CONTROL_DATA (CONFIG_GYROS + 1)
|
|
#define CONFIG_TEMPERATURE (CONFIG_CONTROL_DATA + 1)
|
|
#define CONFIG_RAW_DATA (CONFIG_TEMPERATURE + 1)
|
|
#define CONFIG_RAW_EXTERNAL (CONFIG_RAW_DATA + 1)
|
|
#define CONFIG_ACCEL (CONFIG_RAW_EXTERNAL + 1)
|
|
#define CONFIG_DMP_QUANT_ACCEL (CONFIG_ACCEL + 1)
|
|
#define CONFIG_EIS (CONFIG_DMP_QUANT_ACCEL + 1)
|
|
#define CONFIG_DMP_PACKET_NUMBER (CONFIG_EIS + 1)
|
|
#define CONFIG_FOOTER (CONFIG_DMP_PACKET_NUMBER + 1)
|
|
#define NUMFIFOELEMENTS (CONFIG_FOOTER + 1)
|
|
|
|
const int fifo_base_offset[NUMFIFOELEMENTS] = {
|
|
REF_QUATERNION * 4,
|
|
REF_GYROS * 4,
|
|
REF_CONTROL * 4,
|
|
REF_RAW * 4,
|
|
REF_RAW * 4 + 4,
|
|
REF_RAW_EXTERNAL * 4,
|
|
REF_ACCEL * 4,
|
|
REF_QUANT_ACCEL * 4,
|
|
REF_EIS * 4,
|
|
REF_DMP_PACKET * 4,
|
|
REF_GARBAGE * 4
|
|
};
|
|
|
|
struct fifo_obj {
|
|
void (*fifo_process_cb) (void);
|
|
long decoded[REF_LAST];
|
|
long decoded_accel[INV_MAX_NUM_ACCEL_SAMPLES][ACCEL_NUM_AXES];
|
|
int offsets[REF_LAST * 4];
|
|
int cache;
|
|
uint_fast8_t gyro_source;
|
|
unsigned short fifo_rate;
|
|
unsigned short sample_step_size_ms;
|
|
uint_fast16_t fifo_packet_size;
|
|
uint_fast16_t data_config[NUMFIFOELEMENTS];
|
|
unsigned char reference_count[REF_LAST];
|
|
long acc_bias_filt[3];
|
|
float acc_filter_coef;
|
|
long gravity_cache[3];
|
|
};
|
|
|
|
static struct fifo_obj fifo_obj;
|
|
|
|
#define FIFO_CACHE_TEMPERATURE 1
|
|
#define FIFO_CACHE_GYRO 2
|
|
#define FIFO_CACHE_GRAVITY_BODY 4
|
|
#define FIFO_CACHE_ACC_BIAS 8
|
|
|
|
struct fifo_rate_obj {
|
|
// These describe callbacks happening everytime a FIFO block is processed
|
|
int_fast8_t num_cb;
|
|
HANDLE mutex;
|
|
inv_obj_func fifo_process_cb[MAX_HIGH_RATE_PROCESSES];
|
|
int priority[MAX_HIGH_RATE_PROCESSES];
|
|
};
|
|
|
|
struct fifo_rate_obj fifo_rate_obj;
|
|
|
|
/** Sets accuracy to be one of 0, INV_32_BIT, or INV_16_BIT. Looks up old
|
|
* accuracy if needed.
|
|
*/
|
|
static uint_fast16_t inv_set_fifo_accuracy(uint_fast16_t elements,
|
|
uint_fast16_t accuracy,
|
|
uint_fast8_t configOffset)
|
|
{
|
|
if (elements) {
|
|
if (!accuracy)
|
|
accuracy = fifo_obj.data_config[configOffset];
|
|
else if (accuracy & INV_16_BIT)
|
|
if ((fifo_obj.data_config[configOffset] & INV_32_BIT))
|
|
accuracy = INV_32_BIT; // 32-bits takes priority
|
|
else
|
|
accuracy = INV_16_BIT;
|
|
else
|
|
accuracy = INV_32_BIT;
|
|
} else {
|
|
accuracy = 0;
|
|
}
|
|
|
|
return accuracy;
|
|
}
|
|
|
|
/** Adjusts (len) Reference Counts, at offset (refOffset). If increment is 0,
|
|
* the reference counts are subtracted, otherwise they are incremented for each
|
|
* bit set in element. The value returned are the elements that should be sent
|
|
* out as data through the FIFO.
|
|
*/
|
|
static uint_fast16_t inv_set_fifo_reference(uint_fast16_t elements,
|
|
uint_fast16_t increment,
|
|
uint_fast8_t refOffset,
|
|
uint_fast8_t len)
|
|
{
|
|
uint_fast8_t kk;
|
|
|
|
if (increment == 0) {
|
|
for (kk = 0; kk < len; ++kk) {
|
|
if ((elements & 1)
|
|
&& (fifo_obj.reference_count[kk + refOffset] > 0)) {
|
|
fifo_obj.reference_count[kk + refOffset]--;
|
|
}
|
|
elements >>= 1;
|
|
}
|
|
} else {
|
|
for (kk = 0; kk < len; ++kk) {
|
|
if (elements & 1)
|
|
fifo_obj.reference_count[kk + refOffset]++;
|
|
elements >>= 1;
|
|
}
|
|
}
|
|
elements = 0;
|
|
for (kk = 0; kk < len; ++kk) {
|
|
if (fifo_obj.reference_count[kk + refOffset] > 0)
|
|
elements |= (1 << kk);
|
|
}
|
|
return elements;
|
|
}
|
|
|
|
/**
|
|
* @param[in] accuracy INV_16_BIT or INV_32_BIT when constructing data to send
|
|
* out the FIFO, 0 when removing from the FIFO.
|
|
*/
|
|
static inv_error_t inv_construct3_fifo(unsigned char *regs,
|
|
uint_fast16_t elements,
|
|
uint_fast16_t accuracy,
|
|
uint_fast8_t refOffset,
|
|
unsigned short key,
|
|
uint_fast8_t configOffset)
|
|
{
|
|
int_fast8_t kk;
|
|
inv_error_t result;
|
|
|
|
elements = inv_set_fifo_reference(elements, accuracy, refOffset, 3);
|
|
accuracy = inv_set_fifo_accuracy(elements, accuracy, configOffset);
|
|
|
|
if (accuracy & INV_16_BIT) {
|
|
regs[0] = DINAF8 + 2;
|
|
}
|
|
|
|
fifo_obj.data_config[configOffset] = elements | accuracy;
|
|
|
|
for (kk = 0; kk < 3; ++kk) {
|
|
if ((elements & 1) == 0)
|
|
regs[kk + 1] = DINAA0 + 3;
|
|
elements >>= 1;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(key, 4, regs);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* Puts footer on FIFO data.
|
|
*/
|
|
static inv_error_t inv_set_footer(void)
|
|
{
|
|
unsigned char regs = DINA30;
|
|
uint_fast8_t tmp_count;
|
|
int_fast8_t i, j;
|
|
int offset;
|
|
int result;
|
|
int *fifo_offsets_ptr = fifo_obj.offsets;
|
|
|
|
fifo_obj.fifo_packet_size = 0;
|
|
for (i = 0; i < NUMFIFOELEMENTS; i++) {
|
|
tmp_count = 0;
|
|
offset = fifo_base_offset[i];
|
|
for (j = 0; j < 8; j++) {
|
|
if ((fifo_obj.data_config[i] >> j) & 0x0001) {
|
|
#ifndef BIG_ENDIAN
|
|
// Special Case for Byte Ordering on Accel Data
|
|
if ((i == CONFIG_RAW_DATA) && (j > 2)) {
|
|
tmp_count += 2;
|
|
switch (inv_get_dl_config()->accel->endian) {
|
|
case EXT_SLAVE_BIG_ENDIAN:
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
*fifo_offsets_ptr++ = offset + 2;
|
|
break;
|
|
case EXT_SLAVE_LITTLE_ENDIAN:
|
|
*fifo_offsets_ptr++ = offset + 2;
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
break;
|
|
case EXT_SLAVE_FS8_BIG_ENDIAN:
|
|
if (j == 3) {
|
|
// Throw this byte away
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
} else if (j == 4) {
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
*fifo_offsets_ptr++ = offset + 7;
|
|
} else {
|
|
// Throw these byte away
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
}
|
|
break;
|
|
case EXT_SLAVE_FS16_BIG_ENDIAN:
|
|
if (j == 3) {
|
|
// Throw this byte away
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
} else if (j == 4) {
|
|
*fifo_offsets_ptr++ = offset - 2;
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
} else {
|
|
*fifo_offsets_ptr++ = offset - 2;
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
}
|
|
break;
|
|
default:
|
|
return INV_ERROR; // Bad value on ordering
|
|
}
|
|
} else {
|
|
tmp_count += 2;
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
*fifo_offsets_ptr++ = offset + 2;
|
|
if (fifo_obj.data_config[i] & INV_32_BIT) {
|
|
*fifo_offsets_ptr++ = offset + 1;
|
|
*fifo_offsets_ptr++ = offset;
|
|
tmp_count += 2;
|
|
}
|
|
}
|
|
#else
|
|
// Big Endian Platform
|
|
// Special Case for Byte Ordering on Accel Data
|
|
if ((i == CONFIG_RAW_DATA) && (j > 2)) {
|
|
tmp_count += 2;
|
|
switch (inv_get_dl_config()->accel->endian) {
|
|
case EXT_SLAVE_BIG_ENDIAN:
|
|
*fifo_offsets_ptr++ = offset + 2;
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
break;
|
|
case EXT_SLAVE_LITTLE_ENDIAN:
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
*fifo_offsets_ptr++ = offset + 2;
|
|
break;
|
|
case EXT_SLAVE_FS8_BIG_ENDIAN:
|
|
if (j == 3) {
|
|
// Throw this byte away
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
*fifo_offsets_ptr++ = offset;
|
|
} else if (j == 4) {
|
|
*fifo_offsets_ptr++ = offset;
|
|
*fifo_offsets_ptr++ = offset + 4;
|
|
} else {
|
|
// Throw these bytes away
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
}
|
|
break;
|
|
case EXT_SLAVE_FS16_BIG_ENDIAN:
|
|
if (j == 3) {
|
|
// Throw this byte away
|
|
*fifo_offsets_ptr++ =
|
|
fifo_base_offset[CONFIG_FOOTER];
|
|
*fifo_offsets_ptr++ = offset;
|
|
} else if (j == 4) {
|
|
*fifo_offsets_ptr++ = offset - 3;
|
|
*fifo_offsets_ptr++ = offset;
|
|
} else {
|
|
*fifo_offsets_ptr++ = offset - 3;
|
|
*fifo_offsets_ptr++ = offset;
|
|
}
|
|
break;
|
|
default:
|
|
return INV_ERROR; // Bad value on ordering
|
|
}
|
|
} else {
|
|
tmp_count += 2;
|
|
*fifo_offsets_ptr++ = offset;
|
|
*fifo_offsets_ptr++ = offset + 1;
|
|
if (fifo_obj.data_config[i] & INV_32_BIT) {
|
|
*fifo_offsets_ptr++ = offset + 2;
|
|
*fifo_offsets_ptr++ = offset + 3;
|
|
tmp_count += 2;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
offset += 4;
|
|
}
|
|
fifo_obj.fifo_packet_size += tmp_count;
|
|
}
|
|
if (fifo_obj.data_config[CONFIG_FOOTER] == 0 &&
|
|
fifo_obj.fifo_packet_size > 0) {
|
|
// Add footer
|
|
result = inv_set_mpu_memory(KEY_CFG_16, 1, ®s);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
fifo_obj.data_config[CONFIG_FOOTER] = 0x0001 | INV_16_BIT;
|
|
fifo_obj.fifo_packet_size += 2;
|
|
} else if (fifo_obj.data_config[CONFIG_FOOTER] &&
|
|
(fifo_obj.fifo_packet_size == 2)) {
|
|
// Remove Footer
|
|
regs = DINAA0 + 3;
|
|
result = inv_set_mpu_memory(KEY_CFG_16, 1, ®s);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
fifo_obj.data_config[CONFIG_FOOTER] = 0;
|
|
fifo_obj.fifo_packet_size = 0;
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
inv_error_t inv_decode_quantized_accel(void)
|
|
{
|
|
int kk;
|
|
int fifoRate = inv_get_fifo_rate();
|
|
|
|
if (!fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (kk = (INV_MAX_NUM_ACCEL_SAMPLES - (fifoRate + 1));
|
|
kk < INV_MAX_NUM_ACCEL_SAMPLES; kk++) {
|
|
union {
|
|
unsigned int u10:10;
|
|
signed int s10:10;
|
|
} temp;
|
|
|
|
union {
|
|
uint32_t u32;
|
|
int32_t s32;
|
|
} value;
|
|
|
|
value.u32 = fifo_obj.decoded[REF_QUANT_ACCEL + kk];
|
|
// unquantize this samples.
|
|
// They are stored as x * 2^20 + y * 2^10 + z
|
|
// Z
|
|
temp.u10 = value.u32 & 0x3ff;
|
|
value.s32 -= temp.s10;
|
|
fifo_obj.decoded_accel[kk][2] = temp.s10 * 64;
|
|
// Y
|
|
value.s32 = value.s32 / 1024;
|
|
temp.u10 = value.u32 & 0x3ff;
|
|
value.s32 -= temp.s10;
|
|
fifo_obj.decoded_accel[kk][1] = temp.s10 * 64;
|
|
// X
|
|
value.s32 = value.s32 / 1024;
|
|
temp.u10 = value.u32 & 0x3ff;
|
|
fifo_obj.decoded_accel[kk][0] = temp.s10 * 64;
|
|
}
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
static inv_error_t inv_state_change_fifo(unsigned char newState)
|
|
{
|
|
inv_error_t result = INV_SUCCESS;
|
|
unsigned char regs[4];
|
|
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
|
|
|
|
/* Don't reset the fifo on a fifo rate change */
|
|
if ((mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) &&
|
|
(newState != inv_get_state()) && (inv_dmpkey_supported(KEY_D_1_178))) {
|
|
/* Delay output on restart by 50ms due to warm up time of gyros */
|
|
|
|
short delay = (short)-((50 / inv_get_sample_step_size_ms()) + 1);
|
|
inv_init_fifo_hardare();
|
|
inv_int16_to_big8(delay, regs);
|
|
result = inv_set_mpu_memory(KEY_D_1_178, 2, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (INV_STATE_DMP_STARTED == newState) {
|
|
if (inv_dmpkey_supported(KEY_D_1_128)) {
|
|
double tmp;
|
|
tmp = (0x20000000L * M_PI) / (fifo_obj.fifo_rate + 1);
|
|
if (tmp > 0x40000000L)
|
|
tmp = 0x40000000L;
|
|
(void)inv_int32_to_big8((long)tmp, regs);
|
|
result = inv_set_mpu_memory(KEY_D_1_128, sizeof(long), regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
result = inv_reset_fifo();
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* @brief get the FIFO packet size
|
|
* @return the FIFO packet size
|
|
*/
|
|
uint_fast16_t inv_get_fifo_packet_size(void)
|
|
{
|
|
return fifo_obj.fifo_packet_size;
|
|
}
|
|
|
|
/**
|
|
* @brief Initializes all the internal static variables for
|
|
* the FIFO module.
|
|
* @note Should be called by the initialization routine such
|
|
* as inv_dmp_open().
|
|
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_init_fifo_param(void)
|
|
{
|
|
inv_error_t result;
|
|
memset(&fifo_obj, 0, sizeof(struct fifo_obj));
|
|
fifo_obj.decoded[REF_QUATERNION] = 1073741824L; // Set to Identity
|
|
inv_set_linear_accel_filter_coef(0.f);
|
|
fifo_obj.fifo_rate = 20;
|
|
fifo_obj.sample_step_size_ms = 100;
|
|
memset(&fifo_rate_obj, 0, sizeof(struct fifo_rate_obj));
|
|
result = inv_create_mutex(&fifo_rate_obj.mutex);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
result = inv_register_state_callback(inv_state_change_fifo);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Close the FIFO usage.
|
|
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_close_fifo(void)
|
|
{
|
|
inv_error_t result;
|
|
inv_error_t first = INV_SUCCESS;
|
|
result = inv_unregister_state_callback(inv_state_change_fifo);
|
|
ERROR_CHECK_FIRST(first, result);
|
|
result = inv_destroy_mutex(fifo_rate_obj.mutex);
|
|
ERROR_CHECK_FIRST(first, result);
|
|
memset(&fifo_rate_obj, 0, sizeof(struct fifo_rate_obj));
|
|
return first;
|
|
}
|
|
|
|
/**
|
|
* Set the gyro source to output to the fifo
|
|
*
|
|
* @param source The source. One of
|
|
* - INV_GYRO_FROM_RAW
|
|
* - INV_GYRO_FROM_QUATERNION
|
|
*
|
|
* @return INV_SUCCESS or non-zero error code;
|
|
*/
|
|
inv_error_t inv_set_gyro_data_source(uint_fast8_t source)
|
|
{
|
|
if (source != INV_GYRO_FROM_QUATERNION && source != INV_GYRO_FROM_RAW) {
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
fifo_obj.gyro_source = source;
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Reads and processes FIFO data. Also handles callbacks when data is
|
|
* ready.
|
|
* @param numPackets
|
|
* Number of FIFO packets to try to read. You should
|
|
* use a large number here, such as 100, if you want to read all
|
|
* the full packets in the FIFO, which is typical operation.
|
|
* @param processed
|
|
* The number of FIFO packets processed. This may be incremented
|
|
* even if high rate processes later fail.
|
|
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_read_and_process_fifo(int_fast8_t numPackets,
|
|
int_fast8_t * processed)
|
|
{
|
|
int_fast8_t packet;
|
|
inv_error_t result = INV_SUCCESS;
|
|
uint_fast16_t read;
|
|
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
|
|
int kk;
|
|
|
|
if (NULL == processed)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
*processed = 0;
|
|
if (fifo_obj.fifo_packet_size == 0)
|
|
return result; // Nothing to read
|
|
|
|
for (packet = 0; packet < numPackets; ++packet) {
|
|
if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) {
|
|
unsigned char footer_n_data[MAX_FIFO_LENGTH + FIFO_FOOTER_SIZE];
|
|
unsigned char *buf = &footer_n_data[FIFO_FOOTER_SIZE];
|
|
read = inv_get_fifo((uint_fast16_t) fifo_obj.fifo_packet_size,
|
|
footer_n_data);
|
|
if (0 == read ||
|
|
read != fifo_obj.fifo_packet_size - FIFO_FOOTER_SIZE) {
|
|
result = inv_get_fifo_status();
|
|
if (INV_SUCCESS != result) {
|
|
memset(fifo_obj.decoded, 0, sizeof(fifo_obj.decoded));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
result = inv_process_fifo_packet(buf);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
} else if (inv_accel_present()) {
|
|
long data[ACCEL_NUM_AXES];
|
|
result = inv_get_accel_data(data);
|
|
if (result == INV_ERROR_ACCEL_DATA_NOT_READY) {
|
|
return INV_SUCCESS;
|
|
}
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
memset(fifo_obj.decoded, 0, sizeof(fifo_obj.decoded));
|
|
fifo_obj.cache = 0;
|
|
for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
|
|
fifo_obj.decoded[REF_RAW + 4 + kk] =
|
|
inv_q30_mult((data[kk] << 16),
|
|
fifo_scale[REF_RAW + 4 + kk]);
|
|
fifo_obj.decoded[REF_ACCEL + kk] =
|
|
inv_q30_mult((data[kk] << 15), fifo_scale[REF_ACCEL + kk]);
|
|
fifo_obj.decoded[REF_ACCEL + kk] -=
|
|
inv_obj.scaled_accel_bias[kk];
|
|
}
|
|
}
|
|
// The buffer was processed correctly, so increment even if
|
|
// other processes fail later, which will return an error
|
|
*processed = *processed + 1;
|
|
|
|
if ((fifo_obj.fifo_rate < INV_MAX_NUM_ACCEL_SAMPLES) &&
|
|
fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL]) {
|
|
result = inv_decode_quantized_accel();
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (fifo_obj.data_config[CONFIG_QUAT]) {
|
|
result = inv_accel_compass_supervisor();
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
result = inv_pressure_supervisor();
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
// Callbacks now that we have a buffer of data ready
|
|
result = inv_run_fifo_rate_processes();
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief inv_set_fifo_processed_callback is used to set a processed data callback
|
|
* function. inv_set_fifo_processed_callback sets a user defined callback
|
|
* function that triggers when all the decoding has been finished by
|
|
* the motion processing engines. It is called before other bigger
|
|
* processing engines to allow lower latency for the user.
|
|
*
|
|
* @pre inv_dmp_open()
|
|
* @ifnot MPL_MF
|
|
* or inv_open_low_power_pedometer()
|
|
* or inv_eis_open_dmp()
|
|
* @endif
|
|
* and inv_dmp_start()
|
|
* must <b>NOT</b> have been called.
|
|
*
|
|
* @param func A user defined callback function.
|
|
*
|
|
* @return INV_SUCCESS if successful, or non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_set_fifo_processed_callback(void (*func) (void))
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
fifo_obj.fifo_process_cb = func;
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* @brief Process data from the dmp read via the fifo. Takes a buffer
|
|
* filled with bytes read from the DMP FIFO.
|
|
* Currently expects only the 16 bytes of quaternion data.
|
|
* Calculates the motion parameters from that data and stores the
|
|
* results in an internal data structure.
|
|
* @param[in,out] dmpData Pointer to the buffer containing the fifo data.
|
|
* @return INV_SUCCESS or error code.
|
|
**/
|
|
inv_error_t inv_process_fifo_packet(const unsigned char *dmpData)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
int N, kk;
|
|
unsigned char *p;
|
|
|
|
p = (unsigned char *)(&fifo_obj.decoded);
|
|
N = fifo_obj.fifo_packet_size;
|
|
if (N > sizeof(fifo_obj.decoded))
|
|
return INV_ERROR_ASSERTION_FAILURE;
|
|
|
|
memset(&fifo_obj.decoded, 0, sizeof(fifo_obj.decoded));
|
|
|
|
for (kk = 0; kk < N; ++kk) {
|
|
p[fifo_obj.offsets[kk]] = *dmpData++;
|
|
}
|
|
|
|
// If multiplies are much greater cost than if checks, you could check
|
|
// to see if fifo_scale is non-zero first, or equal to (1L<<30)
|
|
for (kk = 0; kk < REF_LAST; ++kk) {
|
|
fifo_obj.decoded[kk] =
|
|
inv_q30_mult(fifo_obj.decoded[kk], fifo_scale[kk]);
|
|
}
|
|
|
|
memcpy(&fifo_obj.decoded[REF_QUATERNION_6AXIS],
|
|
&fifo_obj.decoded[REF_QUATERNION], 4 * sizeof(long));
|
|
|
|
inv_obj.flags[INV_PROCESSED_DATA_READY] = 1;
|
|
fifo_obj.cache = 0;
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/** Converts 16-bit temperature data as read from temperature register
|
|
* into Celcius scaled by 2^16.
|
|
*/
|
|
long inv_decode_temperature(short tempReg)
|
|
{
|
|
#if defined CONFIG_MPU_SENSORS_MPU6050A2
|
|
// Celcius = 35 + (T + 3048.7)/325.9
|
|
return 2906830L + inv_q30_mult((long)tempReg << 16, 3294697L);
|
|
#endif
|
|
#if defined CONFIG_MPU_SENSORS_MPU6050B1
|
|
// Celcius = 35 + (T + 927.4)/360.6
|
|
return 2462307L + inv_q30_mult((long)tempReg << 16, 2977653L);
|
|
#endif
|
|
#if defined CONFIG_MPU_SENSORS_MPU3050
|
|
// Celcius = 35 + (T + 13200)/280
|
|
return 5383314L + inv_q30_mult((long)tempReg << 16, 3834792L);
|
|
#endif
|
|
}
|
|
|
|
/** @internal
|
|
* Returns the temperature in hardware units. The scaling may change from part to part.
|
|
*/
|
|
inv_error_t inv_get_temperature_raw(short *data)
|
|
{
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_TEMPERATURE]) {
|
|
inv_error_t result;
|
|
unsigned char regs[2];
|
|
if ((fifo_obj.cache & FIFO_CACHE_TEMPERATURE) == 0) {
|
|
if (FIFO_DEBUG)
|
|
MPL_LOGI("Fetching the temperature from the registers\n");
|
|
fifo_obj.cache |= FIFO_CACHE_TEMPERATURE;
|
|
result = inv_serial_read(inv_get_serial_handle(),
|
|
inv_get_mpu_slave_addr(), MPUREG_TEMP_OUT_H, 2,
|
|
regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
fifo_obj.decoded[REF_RAW] = ((short)regs[0] << 8) | (regs[1]);
|
|
}
|
|
}
|
|
*data = (short)fifo_obj.decoded[REF_RAW];
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 1-element vector of temperature. It is read from the hardware if it
|
|
* doesn't exist in the FIFO.
|
|
* @param[out] data 1-element vector of temperature
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_temperature(long *data)
|
|
{
|
|
short tr;
|
|
inv_error_t result;
|
|
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
result = inv_get_temperature_raw(&tr);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
data[0] = inv_decode_temperature(tr);
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the Decoded Accel Data.
|
|
* @param data
|
|
* a buffer to store the quantized data.
|
|
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_get_unquantized_accel(long *data)
|
|
{
|
|
int ii, kk;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (ii = 0; ii < INV_MAX_NUM_ACCEL_SAMPLES; ii++) {
|
|
for (kk = 0; kk < ACCEL_NUM_AXES; kk++) {
|
|
data[ii * ACCEL_NUM_AXES + kk] = fifo_obj.decoded_accel[ii][kk];
|
|
}
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the Quantized Accel data algorithm output from the FIFO.
|
|
* @param data
|
|
* a buffer to store the quantized data.
|
|
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_get_quantized_accel(long *data)
|
|
{
|
|
int ii;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (ii = 0; ii < INV_MAX_NUM_ACCEL_SAMPLES; ii++) {
|
|
data[ii] = fifo_obj.decoded[REF_QUANT_ACCEL + ii];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/** This gets raw gyro data. The data is taken from the FIFO if it was put in the FIFO
|
|
* and it is read from the registers if it was not put into the FIFO. The data is
|
|
* cached till the next FIFO processing block time.
|
|
* @param[out] data Length 3, Gyro data
|
|
*/
|
|
inv_error_t inv_get_gyro_sensor(long *data)
|
|
{
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
if ((fifo_obj.data_config[CONFIG_RAW_DATA] & 7) != 7) {
|
|
inv_error_t result;
|
|
unsigned char regs[6];
|
|
if ((fifo_obj.cache & FIFO_CACHE_GYRO) == 0) {
|
|
fifo_obj.cache |= FIFO_CACHE_GYRO;
|
|
result =
|
|
inv_serial_read(inv_get_serial_handle(),
|
|
inv_get_mpu_slave_addr(), MPUREG_GYRO_XOUT_H, 6,
|
|
regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
fifo_obj.decoded[REF_RAW + 1] =
|
|
(((long)regs[0]) << 24) | (((long)regs[1]) << 16);
|
|
fifo_obj.decoded[REF_RAW + 2] =
|
|
(((long)regs[2]) << 24) | (((long)regs[3]) << 16);
|
|
fifo_obj.decoded[REF_RAW + 3] =
|
|
(((long)regs[4]) << 24) | (((long)regs[5]) << 16);
|
|
|
|
// Temperature starts at location 0, Gyro at location 1.
|
|
fifo_obj.decoded[REF_RAW + 1] =
|
|
inv_q30_mult(fifo_obj.decoded[REF_RAW + 1],
|
|
fifo_scale[REF_RAW + 1]);
|
|
fifo_obj.decoded[REF_RAW + 2] =
|
|
inv_q30_mult(fifo_obj.decoded[REF_RAW + 2],
|
|
fifo_scale[REF_RAW + 2]);
|
|
fifo_obj.decoded[REF_RAW + 3] =
|
|
inv_q30_mult(fifo_obj.decoded[REF_RAW + 3],
|
|
fifo_scale[REF_RAW + 3]);
|
|
}
|
|
data[0] = fifo_obj.decoded[REF_RAW + 1];
|
|
data[1] = fifo_obj.decoded[REF_RAW + 2];
|
|
data[2] = fifo_obj.decoded[REF_RAW + 3];
|
|
} else {
|
|
long data2[6];
|
|
inv_get_gyro_and_accel_sensor(data2);
|
|
data[0] = data2[0];
|
|
data[1] = data2[1];
|
|
data[2] = data2[2];
|
|
}
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 6-element vector of gyro and accel data
|
|
* @param[out] data 6-element vector of gyro and accel data
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_gyro_and_accel_sensor(long *data)
|
|
{
|
|
int ii;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_RAW_DATA])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (ii = 0; ii < (GYRO_NUM_AXES + ACCEL_NUM_AXES); ii++) {
|
|
data[ii] = fifo_obj.decoded[REF_RAW + 1 + ii];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 3-element vector of external sensor
|
|
* @param[out] data 3-element vector of external sensor
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_external_sensor_data(long *data, int size)
|
|
{
|
|
#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
|
|
defined CONFIG_MPU_SENSORS_MPU6050B1
|
|
int ii;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_RAW_EXTERNAL])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (ii = 0; ii < size && ii < 6; ii++) {
|
|
data[ii] = fifo_obj.decoded[REF_RAW_EXTERNAL + ii];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
#else
|
|
memset(data, 0, COMPASS_NUM_AXES * sizeof(long));
|
|
return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Sends accelerometer data to the FIFO.
|
|
*
|
|
* @param[in] elements Which of the 3 elements to send. Use INV_ALL for 3 axis
|
|
* or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
|
|
* for a subset.
|
|
*
|
|
* @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
|
|
* bit data. Set to zero to remove it from the FIFO.
|
|
*/
|
|
inv_error_t inv_send_accel(uint_fast16_t elements, uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
unsigned char regs[4] = { DINAF8 + 1, DINA28, DINA30, DINA38 };
|
|
inv_error_t result;
|
|
int kk;
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
result = inv_construct3_fifo(regs, elements, accuracy, REF_ACCEL,
|
|
KEY_CFG_12, CONFIG_ACCEL);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
for (kk = 0; kk < ACCEL_NUM_AXES; kk++) {
|
|
fifo_scale[REF_ACCEL + kk] = 2 * inv_obj.accel_sens;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/**
|
|
* Sends control data to the FIFO. Control data is a 4 length vector of 32-bits.
|
|
*
|
|
* @param[in] elements Which of the 4 elements to send. Use INV_ALL for all
|
|
* or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3, INV_ELEMENT_4 or'd
|
|
* together for a subset.
|
|
*
|
|
* @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
|
|
* bit data. Set to zero to remove it from the FIFO.
|
|
*/
|
|
inv_error_t inv_send_cntrl_data(uint_fast16_t elements, uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
int_fast8_t kk;
|
|
inv_error_t result;
|
|
unsigned char regs[5] = { DINAF8 + 1, DINA20, DINA28, DINA30, DINA38 };
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
elements = inv_set_fifo_reference(elements, accuracy, REF_CONTROL, 4);
|
|
accuracy = inv_set_fifo_accuracy(elements, accuracy, CONFIG_CONTROL_DATA);
|
|
|
|
if (accuracy & INV_16_BIT) {
|
|
regs[0] = DINAF8 + 2;
|
|
}
|
|
|
|
fifo_obj.data_config[CONFIG_CONTROL_DATA] = elements | accuracy;
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
if ((elements & 1) == 0)
|
|
regs[kk + 1] = DINAA0 + 3;
|
|
elements >>= 1;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(KEY_CFG_1, 5, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/**
|
|
* Adds a rolling counter to the fifo packet. When used with the footer
|
|
* the data comes out the first time:
|
|
*
|
|
* @code
|
|
* <data0><data1>...<dataN><PacketNum0><PacketNum1>
|
|
* @endcode
|
|
* for every other packet it is
|
|
*
|
|
* @code
|
|
* <FifoFooter0><FifoFooter1><data0><data1>...<dataN><PacketNum0><PacketNum1>
|
|
* @endcode
|
|
*
|
|
* This allows for scanning of the fifo for packets
|
|
*
|
|
* @return INV_SUCCESS or error code
|
|
*/
|
|
inv_error_t inv_send_packet_number(uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
inv_error_t result;
|
|
unsigned char regs;
|
|
uint_fast16_t elements;
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
elements = inv_set_fifo_reference(1, accuracy, REF_DMP_PACKET, 1);
|
|
if (elements & 1) {
|
|
regs = DINA28;
|
|
fifo_obj.data_config[CONFIG_DMP_PACKET_NUMBER] =
|
|
INV_ELEMENT_1 | INV_16_BIT;
|
|
} else {
|
|
regs = DINAF8 + 3;
|
|
fifo_obj.data_config[CONFIG_DMP_PACKET_NUMBER] = 0;
|
|
}
|
|
result = inv_set_mpu_memory(KEY_CFG_23, 1, ®s);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/**
|
|
* @brief Send the computed gravity vectors into the FIFO.
|
|
* The gravity vectors can be retrieved from the FIFO via
|
|
* inv_get_gravity(), to have the gravitation vector expressed
|
|
* in coordinates relative to the body.
|
|
*
|
|
* Gravity is a derived vector derived from the quaternion.
|
|
* @param elements
|
|
* the gravitation vectors components bitmask.
|
|
* To send all compoents use INV_ALL.
|
|
* @param accuracy
|
|
* The number of bits the gravitation vector is expressed
|
|
* into.
|
|
* Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
|
|
* bit data.
|
|
* Set to zero to remove it from the FIFO.
|
|
*
|
|
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_send_gravity(uint_fast16_t elements, uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
inv_error_t result;
|
|
|
|
result = inv_send_quaternion(accuracy);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/** Sends gyro data to the FIFO. Gyro data is a 3 length vector
|
|
* of 32-bits. Should be called once after inv_dmp_open() and before inv_dmp_start().
|
|
* @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of them
|
|
* or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
|
|
* for a subset.
|
|
* @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
|
|
* bit data. Set to zero to remove it from the FIFO.
|
|
*/
|
|
inv_error_t inv_send_gyro(uint_fast16_t elements, uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
unsigned char regs[4] = { DINAF8 + 1, DINA20, DINA28, DINA30 };
|
|
inv_error_t result;
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
if (fifo_obj.gyro_source == INV_GYRO_FROM_QUATERNION) {
|
|
regs[0] = DINA90 + 5;
|
|
result = inv_set_mpu_memory(KEY_CFG_GYRO_SOURCE, 1, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
regs[0] = DINAF8 + 1;
|
|
regs[1] = DINA20;
|
|
regs[2] = DINA28;
|
|
regs[3] = DINA30;
|
|
} else {
|
|
regs[0] = DINA90 + 10;
|
|
result = inv_set_mpu_memory(KEY_CFG_GYRO_SOURCE, 1, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
regs[0] = DINAF8 + 1;
|
|
regs[1] = DINA28;
|
|
regs[2] = DINA30;
|
|
regs[3] = DINA38;
|
|
}
|
|
result = inv_construct3_fifo(regs, elements, accuracy, REF_GYROS,
|
|
KEY_CFG_9, CONFIG_GYROS);
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/** Sends linear accelerometer data to the FIFO.
|
|
*
|
|
* Linear accelerometer data is a 3 length vector of 32-bits. It is the
|
|
* acceleration in the body frame with gravity removed.
|
|
*
|
|
*
|
|
* @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of
|
|
* them or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
|
|
* for a subset.
|
|
*
|
|
* NOTE: Elements is ignored if the fifo rate is < INV_MAX_NUM_ACCEL_SAMPLES
|
|
* @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
|
|
* bit data. Set to zero to remove it from the FIFO.
|
|
*/
|
|
inv_error_t inv_send_linear_accel(uint_fast16_t elements,
|
|
uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
inv_error_t result;
|
|
unsigned char state = inv_get_state();
|
|
|
|
if (state < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
result = inv_send_gravity(elements, accuracy);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
result = inv_send_accel(elements, accuracy);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/** Sends linear world accelerometer data to the FIFO. Linear world
|
|
* accelerometer data is a 3 length vector of 32-bits. It is the acceleration
|
|
* in the world frame with gravity removed. Should be called once after
|
|
* inv_dmp_open() and before inv_dmp_start().
|
|
*
|
|
* @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of
|
|
* them or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
|
|
* for a subset.
|
|
* @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
|
|
* bit data.
|
|
*/
|
|
inv_error_t inv_send_linear_accel_in_world(uint_fast16_t elements,
|
|
uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
inv_error_t result;
|
|
|
|
result = inv_send_linear_accel(INV_ALL, accuracy);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
result = inv_send_quaternion(accuracy);
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/** Sends quaternion data to the FIFO. Quaternion data is a 4 length vector
|
|
* of 32-bits. Should be called once after inv_dmp_open() and before inv_dmp_start().
|
|
* @param[in] accuracy Set to INV_32_BIT for 32-bit data, or INV_16_BIT for 16
|
|
* bit data.
|
|
*/
|
|
inv_error_t inv_send_quaternion(uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
unsigned char regs[5] = { DINAF8 + 1, DINA20, DINA28,
|
|
DINA30, DINA38
|
|
};
|
|
uint_fast16_t elements, kk;
|
|
inv_error_t result;
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
elements = inv_set_fifo_reference(0xf, accuracy, REF_QUATERNION, 4);
|
|
accuracy = inv_set_fifo_accuracy(elements, accuracy, CONFIG_QUAT);
|
|
|
|
if (accuracy & INV_16_BIT) {
|
|
regs[0] = DINAF8 + 2;
|
|
}
|
|
|
|
fifo_obj.data_config[CONFIG_QUAT] = elements | accuracy;
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
if ((elements & 1) == 0)
|
|
regs[kk + 1] = DINAA0 + 3;
|
|
elements >>= 1;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(KEY_CFG_8, 5, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/** Sends raw data to the FIFO.
|
|
* Should be called once after inv_dmp_open() and before inv_dmp_start().
|
|
* @param[in] elements Which of the 7 elements to send. Use INV_ALL for all of them
|
|
* or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 ... INV_ELEMENT_7 or'd together
|
|
* for a subset. The first element is temperature, the next 3 are gyro data,
|
|
* and the last 3 accel data.
|
|
* @param accuracy
|
|
* The element's accuracy, can be INV_16_BIT, INV_32_BIT, or 0 to turn off.
|
|
* @return 0 if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_send_sensor_data(uint_fast16_t elements, uint_fast16_t accuracy)
|
|
{
|
|
int result;
|
|
#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
|
|
defined CONFIG_MPU_SENSORS_MPU6050B1
|
|
unsigned char regs[7] = { DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
|
|
DINAA0 + 3, DINAA0 + 3, DINAA0 + 3,
|
|
DINAA0 + 3
|
|
};
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
if (accuracy)
|
|
accuracy = INV_16_BIT;
|
|
|
|
elements = inv_set_fifo_reference(elements, accuracy, REF_RAW, 7);
|
|
|
|
if (elements & 1)
|
|
fifo_obj.data_config[CONFIG_TEMPERATURE] = 1 | INV_16_BIT;
|
|
else
|
|
fifo_obj.data_config[CONFIG_TEMPERATURE] = 0;
|
|
if (elements & 0x7e)
|
|
fifo_obj.data_config[CONFIG_RAW_DATA] =
|
|
(0x3f & (elements >> 1)) | INV_16_BIT;
|
|
else
|
|
fifo_obj.data_config[CONFIG_RAW_DATA] = 0;
|
|
|
|
if (elements & INV_ELEMENT_1) {
|
|
regs[0] = DINACA;
|
|
}
|
|
if (elements & INV_ELEMENT_2) {
|
|
regs[1] = DINBC4;
|
|
}
|
|
if (elements & INV_ELEMENT_3) {
|
|
regs[2] = DINACC;
|
|
}
|
|
if (elements & INV_ELEMENT_4) {
|
|
regs[3] = DINBC6;
|
|
}
|
|
if (elements & INV_ELEMENT_5) {
|
|
regs[4] = DINBC0;
|
|
}
|
|
if (elements & INV_ELEMENT_6) {
|
|
regs[5] = DINAC8;
|
|
}
|
|
if (elements & INV_ELEMENT_7) {
|
|
regs[6] = DINBC2;
|
|
}
|
|
result = inv_set_mpu_memory(KEY_CFG_15, 7, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
|
|
#else
|
|
INVENSENSE_FUNC_START;
|
|
unsigned char regs[4] = { DINAA0 + 3,
|
|
DINAA0 + 3,
|
|
DINAA0 + 3,
|
|
DINAA0 + 3
|
|
};
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
if (accuracy)
|
|
accuracy = INV_16_BIT;
|
|
|
|
elements = inv_set_fifo_reference(elements, accuracy, REF_RAW, 7);
|
|
|
|
if (elements & 0x03) {
|
|
elements |= 0x03;
|
|
regs[0] = DINA20;
|
|
}
|
|
if (elements & 0x0C) {
|
|
elements |= 0x0C;
|
|
regs[1] = DINA28;
|
|
}
|
|
if (elements & 0x30) {
|
|
elements |= 0x30;
|
|
regs[2] = DINA30;
|
|
}
|
|
if (elements & 0x40) {
|
|
elements |= 0xC0;
|
|
regs[3] = DINA38;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(KEY_CFG_15, 4, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
if (elements & 0x01)
|
|
fifo_obj.data_config[CONFIG_TEMPERATURE] = 1 | INV_16_BIT;
|
|
else
|
|
fifo_obj.data_config[CONFIG_TEMPERATURE] = 0;
|
|
if (elements & 0xfe)
|
|
fifo_obj.data_config[CONFIG_RAW_DATA] =
|
|
(0x7f & (elements >> 1)) | INV_16_BIT;
|
|
else
|
|
fifo_obj.data_config[CONFIG_RAW_DATA] = 0;
|
|
|
|
return inv_set_footer();
|
|
#endif
|
|
}
|
|
|
|
/** Sends raw external data to the FIFO.
|
|
* Should be called once after inv_dmp_open() and before inv_dmp_start().
|
|
* @param[in] elements Which of the 3 elements to send. Use INV_ALL for all of them
|
|
* or INV_ELEMENT_1, INV_ELEMENT_2, INV_ELEMENT_3 or'd together
|
|
* for a subset.
|
|
* @param[in] accuracy INV_16_BIT to send data, 0 to stop sending this data.
|
|
* Sending and Stop sending are reference counted, so data actually
|
|
* stops when the reference reaches zero.
|
|
*/
|
|
inv_error_t inv_send_external_sensor_data(uint_fast16_t elements,
|
|
uint_fast16_t accuracy)
|
|
{
|
|
#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
|
|
defined CONFIG_MPU_SENSORS_MPU6050B1
|
|
int result;
|
|
unsigned char regs[6] = { DINAA0 + 3, DINAA0 + 3,
|
|
DINAA0 + 3, DINAA0 + 3,
|
|
DINAA0 + 3, DINAA0 + 3 };
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
if (accuracy)
|
|
accuracy = INV_16_BIT;
|
|
|
|
elements = inv_set_fifo_reference(elements, accuracy, REF_RAW_EXTERNAL, 6);
|
|
|
|
if (elements)
|
|
fifo_obj.data_config[CONFIG_RAW_EXTERNAL] = elements | INV_16_BIT;
|
|
else
|
|
fifo_obj.data_config[CONFIG_RAW_EXTERNAL] = 0;
|
|
|
|
if (elements & INV_ELEMENT_1) {
|
|
regs[0] = DINBC2;
|
|
}
|
|
if (elements & INV_ELEMENT_2) {
|
|
regs[1] = DINACA;
|
|
}
|
|
if (elements & INV_ELEMENT_3) {
|
|
regs[2] = DINBC4;
|
|
}
|
|
if (elements & INV_ELEMENT_4) {
|
|
regs[3] = DINBC0;
|
|
}
|
|
if (elements & INV_ELEMENT_5) {
|
|
regs[4] = DINAC8;
|
|
}
|
|
if (elements & INV_ELEMENT_6) {
|
|
regs[5] = DINACC;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(KEY_CFG_EXTERNAL, sizeof(regs), regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
|
|
#else
|
|
return INV_ERROR_FEATURE_NOT_IMPLEMENTED; // Feature not supported
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Send the Quantized Acceleromter data into the FIFO. The data can be
|
|
* retrieved using inv_get_quantized_accel() or inv_get_unquantized_accel().
|
|
*
|
|
* To be useful this should be set to fifo_rate + 1 if less than
|
|
* INV_MAX_NUM_ACCEL_SAMPLES, otherwise it doesn't work.
|
|
*
|
|
* @param elements
|
|
* the components bitmask.
|
|
* To send all compoents use INV_ALL.
|
|
*
|
|
* @param accuracy
|
|
* Use INV_32_BIT for 32-bit data or INV_16_BIT for
|
|
* 16-bit data.
|
|
* Set to zero to remove it from the FIFO.
|
|
*
|
|
* @return INV_SUCCESS if successful, a non-zero error code otherwise.
|
|
*/
|
|
inv_error_t inv_send_quantized_accel(uint_fast16_t elements,
|
|
uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
unsigned char regs[5] = { DINAF8 + 1, DINA20, DINA28,
|
|
DINA30, DINA38
|
|
};
|
|
unsigned char regs2[4] = { DINA20, DINA28,
|
|
DINA30, DINA38
|
|
};
|
|
inv_error_t result;
|
|
int_fast8_t kk;
|
|
int_fast8_t ii;
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
elements = inv_set_fifo_reference(elements, accuracy, REF_QUANT_ACCEL, 8);
|
|
|
|
if (elements) {
|
|
fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL] = (elements) | INV_32_BIT;
|
|
} else {
|
|
fifo_obj.data_config[CONFIG_DMP_QUANT_ACCEL] = 0;
|
|
}
|
|
|
|
for (kk = 0; kk < INV_MAX_NUM_ACCEL_SAMPLES; ++kk) {
|
|
fifo_obj.decoded[REF_QUANT_ACCEL + kk] = 0;
|
|
for (ii = 0; ii < ACCEL_NUM_AXES; ii++) {
|
|
fifo_obj.decoded_accel[kk][ii] = 0;
|
|
}
|
|
}
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
if ((elements & 1) == 0)
|
|
regs[kk + 1] = DINAA0 + 3;
|
|
elements >>= 1;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(KEY_CFG_TAP0, 5, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
if ((elements & 1) == 0)
|
|
regs2[kk] = DINAA0 + 3;
|
|
elements >>= 1;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(KEY_CFG_TAP4, 4, regs2);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
inv_error_t inv_send_eis(uint_fast16_t elements, uint_fast16_t accuracy)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
int_fast8_t kk;
|
|
unsigned char regs[3] = { DINA28, DINA30, DINA38 };
|
|
inv_error_t result;
|
|
|
|
if (inv_get_state() < INV_STATE_DMP_OPENED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
if (accuracy) {
|
|
accuracy = INV_32_BIT;
|
|
}
|
|
|
|
elements = inv_set_fifo_reference(elements, accuracy, REF_EIS, 3);
|
|
accuracy = inv_set_fifo_accuracy(elements, accuracy, CONFIG_EIS);
|
|
|
|
fifo_obj.data_config[CONFIG_EIS] = elements | accuracy;
|
|
|
|
for (kk = 0; kk < 3; ++kk) {
|
|
if ((elements & 1) == 0)
|
|
regs[kk] = DINAA0 + 7;
|
|
elements >>= 1;
|
|
}
|
|
|
|
result = inv_set_mpu_memory(KEY_P_EIS_FIFO_XSHIFT, 3, regs);
|
|
|
|
return inv_set_footer();
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 3-element vector of accelerometer data in body frame.
|
|
*
|
|
* @param[out] data 3-element vector of accelerometer data in body frame.
|
|
* One gee = 2^16.
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_accel(long *data)
|
|
{
|
|
int kk;
|
|
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
|
|
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if ((!fifo_obj.data_config[CONFIG_ACCEL] &&
|
|
(mldl_cfg->requested_sensors & INV_DMP_PROCESSOR))
|
|
||
|
|
(!(mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) &&
|
|
!inv_accel_present()))
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
|
|
data[kk] = fifo_obj.decoded[REF_ACCEL + kk];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 4-element quaternion vector derived from 6-axis or
|
|
* 9-axis if 9-axis was implemented. 6-axis is gyros and accels. 9-axis is
|
|
* gyros, accel and compass.
|
|
*
|
|
* @param[out] data 4-element quaternion vector. One is scaled to 2^30.
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_quaternion(long *data)
|
|
{
|
|
int kk;
|
|
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_QUAT])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
data[kk] = fifo_obj.decoded[REF_QUATERNION + kk];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 4-element quaternion vector derived from 6
|
|
* axis sensors (gyros and accels).
|
|
* @param[out] data
|
|
* 4-element quaternion vector. One is scaled to 2^30.
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_6axis_quaternion(long *data)
|
|
{
|
|
int kk;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_QUAT])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
data[kk] = fifo_obj.decoded[REF_QUATERNION_6AXIS + kk];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
inv_error_t inv_get_relative_quaternion(long *data)
|
|
{
|
|
if (data == NULL)
|
|
return INV_ERROR;
|
|
data[0] = inv_obj.relative_quat[0];
|
|
data[1] = inv_obj.relative_quat[1];
|
|
data[2] = inv_obj.relative_quat[2];
|
|
data[3] = inv_obj.relative_quat[3];
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 3-element vector of gyro data in body frame.
|
|
* @param[out] data
|
|
* 3-element vector of gyro data in body frame
|
|
* with gravity removed. One degree per second = 2^16.
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_gyro(long *data)
|
|
{
|
|
int kk;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (fifo_obj.data_config[CONFIG_GYROS]) {
|
|
for (kk = 0; kk < 3; ++kk) {
|
|
data[kk] = fifo_obj.decoded[REF_GYROS + kk];
|
|
}
|
|
return INV_SUCCESS;
|
|
} else {
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get the 3-element gravity vector from the FIFO expressed
|
|
* in coordinates relative to the body frame.
|
|
* @param data
|
|
* 3-element vector of gravity in body frame.
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_gravity(long *data)
|
|
{
|
|
long quat[4];
|
|
int ii;
|
|
inv_error_t result;
|
|
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_QUAT])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
if ((fifo_obj.cache & FIFO_CACHE_GRAVITY_BODY) == 0) {
|
|
fifo_obj.cache |= FIFO_CACHE_GRAVITY_BODY;
|
|
|
|
// Compute it from Quaternion
|
|
result = inv_get_quaternion(quat);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
data[0] =
|
|
inv_q29_mult(quat[1], quat[3]) - inv_q29_mult(quat[2], quat[0]);
|
|
data[1] =
|
|
inv_q29_mult(quat[2], quat[3]) + inv_q29_mult(quat[1], quat[0]);
|
|
data[2] =
|
|
(inv_q29_mult(quat[3], quat[3]) + inv_q29_mult(quat[0], quat[0])) -
|
|
1073741824L;
|
|
|
|
for (ii = 0; ii < ACCEL_NUM_AXES; ii++) {
|
|
data[ii] >>= 14;
|
|
fifo_obj.gravity_cache[ii] = data[ii];
|
|
}
|
|
} else {
|
|
data[0] = fifo_obj.gravity_cache[0];
|
|
data[1] = fifo_obj.gravity_cache[1];
|
|
data[2] = fifo_obj.gravity_cache[2];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the filter coefficent used for computing the acceleration
|
|
* bias which is used to compute linear acceleration.
|
|
* @param[in] coef Fitler coefficient. 0. means no filter, a small number means
|
|
* a small cutoff frequency with an increasing number meaning
|
|
* an increasing cutoff frequency.
|
|
*/
|
|
inv_error_t inv_set_linear_accel_filter_coef(float coef)
|
|
{
|
|
fifo_obj.acc_filter_coef = coef;
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 3-element vector of accelerometer data in body frame
|
|
* with gravity removed.
|
|
* @param[out] data 3-element vector of accelerometer data in body frame
|
|
* with gravity removed. One g = 2^16.
|
|
* @return 0 on success or an error code. data unchanged on error.
|
|
*/
|
|
inv_error_t inv_get_linear_accel(long *data)
|
|
{
|
|
int kk;
|
|
long grav[3];
|
|
long la[3];
|
|
inv_error_t result;
|
|
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
result = inv_get_gravity(grav);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
result = inv_get_accel(la);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
if ((fifo_obj.cache & FIFO_CACHE_ACC_BIAS) == 0) {
|
|
fifo_obj.cache |= FIFO_CACHE_ACC_BIAS;
|
|
|
|
for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
|
|
long x;
|
|
x = la[kk] - grav[kk];
|
|
fifo_obj.acc_bias_filt[kk] = (long)(x * fifo_obj.acc_filter_coef +
|
|
fifo_obj.acc_bias_filt[kk] *
|
|
(1.f -
|
|
fifo_obj.acc_filter_coef));
|
|
data[kk] = x - fifo_obj.acc_bias_filt[kk];
|
|
}
|
|
} else {
|
|
for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
|
|
data[kk] = la[kk] - grav[kk] - fifo_obj.acc_bias_filt[kk];
|
|
}
|
|
}
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 3-element vector of accelerometer data in world frame
|
|
* with gravity removed.
|
|
* @param[out] data 3-element vector of accelerometer data in world frame
|
|
* with gravity removed. One g = 2^16.
|
|
* @return 0 on success or an error code.
|
|
*/
|
|
inv_error_t inv_get_linear_accel_in_world(long *data)
|
|
{
|
|
int kk;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
if (fifo_obj.data_config[CONFIG_ACCEL] && fifo_obj.data_config[CONFIG_QUAT]) {
|
|
long wtemp[4], qi[4], wtemp2[4];
|
|
wtemp[0] = 0;
|
|
inv_get_linear_accel(&wtemp[1]);
|
|
inv_q_mult(&fifo_obj.decoded[REF_QUATERNION], wtemp, wtemp2);
|
|
inv_q_invert(&fifo_obj.decoded[REF_QUATERNION], qi);
|
|
inv_q_mult(wtemp2, qi, wtemp);
|
|
for (kk = 0; kk < 3; ++kk) {
|
|
data[kk] = wtemp[kk + 1];
|
|
}
|
|
return INV_SUCCESS;
|
|
} else {
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 4-element vector of control data.
|
|
* @param[out] data 4-element vector of control data.
|
|
* @return 0 for succes or an error code.
|
|
*/
|
|
inv_error_t inv_get_cntrl_data(long *data)
|
|
{
|
|
int kk;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_CONTROL_DATA])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
data[kk] = fifo_obj.decoded[REF_CONTROL + kk];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 3-element vector of EIS shfit data
|
|
* @param[out] data 3-element vector of EIS shift data.
|
|
* @return 0 for succes or an error code.
|
|
*/
|
|
inv_error_t inv_get_eis(long *data)
|
|
{
|
|
int kk;
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_EIS])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (kk = 0; kk < 3; ++kk) {
|
|
data[kk] = fifo_obj.decoded[REF_EIS + kk];
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 3-element vector of accelerometer data in body frame.
|
|
* @param[out] data 3-element vector of accelerometer data in body frame in g's.
|
|
* @return 0 for success or an error code.
|
|
*/
|
|
inv_error_t inv_get_accel_float(float *data)
|
|
{
|
|
long lData[3];
|
|
int kk;
|
|
int result;
|
|
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
result = inv_get_accel(lData);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
|
|
for (kk = 0; kk < ACCEL_NUM_AXES; ++kk) {
|
|
data[kk] = lData[kk] / 65536.0f;
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns 4-element quaternion vector.
|
|
* @param[out] data 4-element quaternion vector.
|
|
* @return 0 on success, an error code otherwise.
|
|
*/
|
|
inv_error_t inv_get_quaternion_float(float *data)
|
|
{
|
|
int kk;
|
|
|
|
if (data == NULL)
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!fifo_obj.data_config[CONFIG_QUAT])
|
|
return INV_ERROR_FEATURE_NOT_ENABLED;
|
|
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
data[kk] = fifo_obj.decoded[REF_QUATERNION + kk] / 1073741824.0f;
|
|
}
|
|
|
|
return INV_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Command the MPU to put data in the FIFO at a particular rate.
|
|
*
|
|
* The DMP will add fifo entries every fifoRate + 1 MPU cycles. For
|
|
* example if the MPU is running at 200Hz the following values apply:
|
|
*
|
|
* <TABLE>
|
|
* <TR><TD>fifoRate</TD><TD>DMP Sample Rate</TD><TD>FIFO update frequency</TD></TR>
|
|
* <TR><TD>0</TD><TD>200Hz</TD><TD>200Hz</TD></TR>
|
|
* <TR><TD>1</TD><TD>200Hz</TD><TD>100Hz</TD></TR>
|
|
* <TR><TD>2</TD><TD>200Hz</TD><TD>50Hz</TD></TR>
|
|
* <TR><TD>4</TD><TD>200Hz</TD><TD>40Hz</TD></TR>
|
|
* <TR><TD>9</TD><TD>200Hz</TD><TD>20Hz</TD></TR>
|
|
* <TR><TD>19</TD><TD>200Hz</TD><TD>10Hz</TD></TR>
|
|
* </TABLE>
|
|
*
|
|
* Note: if the DMP is running, (state == INV_STATE_DMP_STARTED)
|
|
* then inv_run_state_callbacks() will be called to allow features
|
|
* that depend upon fundamental constants to be updated.
|
|
*
|
|
* @pre inv_dmp_open()
|
|
* @ifnot MPL_MF
|
|
* or inv_open_low_power_pedometer()
|
|
* or inv_eis_open_dmp()
|
|
* @endif
|
|
* and inv_dmp_start()
|
|
* must <b>NOT</b> have been called.
|
|
*
|
|
* @param fifoRate Divider value - 1. Output rate is
|
|
* (DMP Sample Rate) / (fifoRate + 1).
|
|
*
|
|
* @return INV_SUCCESS if successful, ML error code on any failure.
|
|
*/
|
|
inv_error_t inv_set_fifo_rate(unsigned short fifoRate)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
unsigned char regs[2];
|
|
unsigned char state;
|
|
inv_error_t result = INV_SUCCESS;
|
|
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
|
|
|
|
state = inv_get_state();
|
|
if (state != INV_STATE_DMP_OPENED && state != INV_STATE_DMP_STARTED)
|
|
return INV_ERROR_SM_IMPROPER_STATE;
|
|
|
|
fifo_obj.fifo_rate = fifoRate;
|
|
|
|
if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR) {
|
|
|
|
regs[0] = (unsigned char)((fifoRate >> 8) & 0xff);
|
|
regs[1] = (unsigned char)(fifoRate & 0xff);
|
|
|
|
result = inv_set_mpu_memory(KEY_D_0_22, 2, regs);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
fifo_obj.sample_step_size_ms =
|
|
(unsigned short)(((long)fifoRate + 1) *
|
|
(inv_mpu_get_sampling_period_us
|
|
(mldl_cfg)) / 1000L);
|
|
|
|
if (state == INV_STATE_DMP_STARTED)
|
|
result = inv_run_state_callbacks(state);
|
|
} else if (mldl_cfg->requested_sensors & INV_THREE_AXIS_ACCEL) {
|
|
struct ext_slave_config config;
|
|
long data;
|
|
config.key = MPU_SLAVE_CONFIG_ODR_RESUME;
|
|
config.len = sizeof(long);
|
|
config.apply = (state == INV_STATE_DMP_STARTED);
|
|
config.data = &data;
|
|
data = (1000 * inv_mpu_get_sampling_rate_hz(mldl_cfg)) / (fifoRate + 1);
|
|
|
|
/* Ask for the same frequency */
|
|
result = inv_mpu_config_accel(mldl_cfg,
|
|
inv_get_serial_handle(),
|
|
inv_get_serial_handle(), &config);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
result = inv_mpu_get_accel_config(mldl_cfg,
|
|
inv_get_serial_handle(),
|
|
inv_get_serial_handle(), &config);
|
|
if (result) {
|
|
LOG_RESULT_LOCATION(result);
|
|
return result;
|
|
}
|
|
if(FIFO_DEBUG)
|
|
MPL_LOGI("Actual ODR: %ld Hz\n", data / 1000);
|
|
/* Record the actual frequency granted odr is in mHz */
|
|
fifo_obj.sample_step_size_ms = (unsigned short)((1000L * 1000L) / data);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve the current FIFO update divider - 1.
|
|
* See inv_set_fifo_rate() for how this value is used.
|
|
*
|
|
* The fifo rate when there is no fifo is the equivilent divider when
|
|
* derived from the value set by SetSampleSteSizeMs()
|
|
*
|
|
* @return The value of the fifo rate divider or INV_INVALID_FIFO_RATE on error.
|
|
*/
|
|
unsigned short inv_get_fifo_rate(void)
|
|
{
|
|
return fifo_obj.fifo_rate;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the step size for quaternion type data.
|
|
*
|
|
* Typically the data rate for each FIFO packet. When the gryos are sleeping
|
|
* this value will return the last value set by SetSampleStepSizeMs()
|
|
*
|
|
* @return step size for quaternion type data
|
|
*/
|
|
int_fast16_t inv_get_sample_step_size_ms(void)
|
|
{
|
|
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
|
|
|
|
if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR)
|
|
return (fifo_obj.fifo_rate + 1) *
|
|
(inv_mpu_get_sampling_period_us(mldl_cfg) / 1000);
|
|
else
|
|
return fifo_obj.sample_step_size_ms;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the step size for quaternion type data.
|
|
*
|
|
* Typically the data rate for each FIFO packet. When the gryos are sleeping
|
|
* this value will return the last value set by SetSampleStepSizeMs()
|
|
*
|
|
* @return step size for quaternion type data
|
|
*/
|
|
int_fast16_t inv_get_sample_frequency(void)
|
|
{
|
|
struct mldl_cfg *mldl_cfg = inv_get_dl_config();
|
|
|
|
if (mldl_cfg->requested_sensors & INV_DMP_PROCESSOR)
|
|
return (inv_mpu_get_sampling_rate_hz(mldl_cfg) /
|
|
(fifo_obj.fifo_rate + 1));
|
|
else
|
|
return (1000 / fifo_obj.sample_step_size_ms);
|
|
}
|
|
|
|
/**
|
|
* @brief The gyro data magnitude squared :
|
|
* (1 degree per second)^2 = 2^6 = 2^GYRO_MAG_SQR_SHIFT.
|
|
* @return the computed magnitude squared output of the gyroscope.
|
|
*/
|
|
unsigned long inv_get_gyro_sum_of_sqr(void)
|
|
{
|
|
unsigned long gmag = 0;
|
|
long temp;
|
|
int kk;
|
|
|
|
for (kk = 0; kk < 3; ++kk) {
|
|
temp = fifo_obj.decoded[REF_GYROS + kk] >>
|
|
(16 - (GYRO_MAG_SQR_SHIFT / 2));
|
|
gmag += temp * temp;
|
|
}
|
|
|
|
return gmag;
|
|
}
|
|
|
|
/**
|
|
* @brief The gyro data magnitude squared:
|
|
* (1 g)^2 = 2^16 = 2^ACC_MAG_SQR_SHIFT.
|
|
* @return the computed magnitude squared output of the accelerometer.
|
|
*/
|
|
unsigned long inv_accel_sum_of_sqr(void)
|
|
{
|
|
unsigned long amag = 0;
|
|
long temp;
|
|
int kk;
|
|
long accel[3];
|
|
inv_error_t result;
|
|
|
|
result = inv_get_accel(accel);
|
|
if (INV_SUCCESS != result) {
|
|
return 0;
|
|
}
|
|
|
|
for (kk = 0; kk < 3; ++kk) {
|
|
temp = accel[kk] >> (16 - (ACC_MAG_SQR_SHIFT / 2));
|
|
amag += temp * temp;
|
|
}
|
|
return amag;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
void inv_override_quaternion(float *q)
|
|
{
|
|
int kk;
|
|
for (kk = 0; kk < 4; ++kk) {
|
|
fifo_obj.decoded[REF_QUATERNION + kk] = (long)(q[kk] * (1L << 30));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* @brief This registers a function to be called for each set of
|
|
* gyro/quaternion/rotation matrix/etc output.
|
|
* @param[in] func The callback function to register
|
|
* @param[in] priority The unique priority number of the callback. Lower numbers
|
|
* are called first.
|
|
* @return error code.
|
|
*/
|
|
inv_error_t inv_register_fifo_rate_process(inv_obj_func func, int priority)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
inv_error_t result;
|
|
int kk, nn;
|
|
|
|
result = inv_lock_mutex(fifo_rate_obj.mutex);
|
|
if (INV_SUCCESS != result) {
|
|
return result;
|
|
}
|
|
// Make sure we haven't registered this function already
|
|
// Or used the same priority
|
|
for (kk = 0; kk < fifo_rate_obj.num_cb; ++kk) {
|
|
if ((fifo_rate_obj.fifo_process_cb[kk] == func) ||
|
|
(fifo_rate_obj.priority[kk] == priority)) {
|
|
inv_unlock_mutex(fifo_rate_obj.mutex);
|
|
return INV_ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
// Make sure we have not filled up our number of allowable callbacks
|
|
if (fifo_rate_obj.num_cb <= MAX_HIGH_RATE_PROCESSES - 1) {
|
|
kk = 0;
|
|
if (fifo_rate_obj.num_cb != 0) {
|
|
// set kk to be where this new callback goes in the array
|
|
while ((kk < fifo_rate_obj.num_cb) &&
|
|
(fifo_rate_obj.priority[kk] < priority)) {
|
|
kk++;
|
|
}
|
|
if (kk != fifo_rate_obj.num_cb) {
|
|
// We need to move the others
|
|
for (nn = fifo_rate_obj.num_cb; nn > kk; --nn) {
|
|
fifo_rate_obj.fifo_process_cb[nn] =
|
|
fifo_rate_obj.fifo_process_cb[nn - 1];
|
|
fifo_rate_obj.priority[nn] = fifo_rate_obj.priority[nn - 1];
|
|
}
|
|
}
|
|
}
|
|
// Add new callback
|
|
fifo_rate_obj.fifo_process_cb[kk] = func;
|
|
fifo_rate_obj.priority[kk] = priority;
|
|
fifo_rate_obj.num_cb++;
|
|
} else {
|
|
result = INV_ERROR_MEMORY_EXAUSTED;
|
|
}
|
|
|
|
inv_unlock_mutex(fifo_rate_obj.mutex);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* @brief This unregisters a function to be called for each set of
|
|
* gyro/quaternion/rotation matrix/etc output.
|
|
* @return error code.
|
|
*/
|
|
inv_error_t inv_unregister_fifo_rate_process(inv_obj_func func)
|
|
{
|
|
INVENSENSE_FUNC_START;
|
|
int kk, jj;
|
|
inv_error_t result;
|
|
|
|
result = inv_lock_mutex(fifo_rate_obj.mutex);
|
|
if (INV_SUCCESS != result) {
|
|
return result;
|
|
}
|
|
// Make sure we haven't registered this function already
|
|
result = INV_ERROR_INVALID_PARAMETER;
|
|
for (kk = 0; kk < fifo_rate_obj.num_cb; ++kk) {
|
|
if (fifo_rate_obj.fifo_process_cb[kk] == func) {
|
|
for (jj = kk + 1; jj < fifo_rate_obj.num_cb; ++jj) {
|
|
fifo_rate_obj.fifo_process_cb[jj - 1] =
|
|
fifo_rate_obj.fifo_process_cb[jj];
|
|
fifo_rate_obj.priority[jj - 1] =
|
|
fifo_rate_obj.priority[jj];
|
|
}
|
|
fifo_rate_obj.fifo_process_cb[fifo_rate_obj.num_cb - 1] = NULL;
|
|
fifo_rate_obj.priority[fifo_rate_obj.num_cb - 1] = 0;
|
|
fifo_rate_obj.num_cb--;
|
|
result = INV_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
inv_unlock_mutex(fifo_rate_obj.mutex);
|
|
return result;
|
|
|
|
}
|
|
#ifdef UMPL
|
|
bool bFIFIDataAvailable = FALSE;
|
|
bool isUmplDataInFIFO(void)
|
|
{
|
|
return bFIFIDataAvailable;
|
|
}
|
|
void setUmplDataInFIFOFlag(bool flag)
|
|
{
|
|
bFIFIDataAvailable = flag;
|
|
}
|
|
#endif
|
|
inv_error_t inv_run_fifo_rate_processes(void)
|
|
{
|
|
int kk;
|
|
inv_error_t result, result2;
|
|
|
|
result = inv_lock_mutex(fifo_rate_obj.mutex);
|
|
if (INV_SUCCESS != result) {
|
|
MPL_LOGE("MLOsLockMutex returned %d\n", result);
|
|
return result;
|
|
}
|
|
// User callbacks take priority over the fifo_process_cb callback
|
|
if (fifo_obj.fifo_process_cb)
|
|
fifo_obj.fifo_process_cb();
|
|
|
|
for (kk = 0; kk < fifo_rate_obj.num_cb; ++kk) {
|
|
if (fifo_rate_obj.fifo_process_cb[kk]) {
|
|
result2 = fifo_rate_obj.fifo_process_cb[kk] (&inv_obj);
|
|
if (result == INV_SUCCESS)
|
|
#ifdef UMPL
|
|
setUmplDataInFIFOFlag(TRUE);
|
|
#endif
|
|
result = result2;
|
|
}
|
|
}
|
|
|
|
inv_unlock_mutex(fifo_rate_obj.mutex);
|
|
return result;
|
|
}
|
|
|
|
/*********************/
|
|
/** \}*//* defgroup */
|
|
/*********************/
|