From 59acde2467e57965536cbd34ce625ad097ad13f5 Mon Sep 17 00:00:00 2001 From: Kyle Repinski Date: Mon, 12 Oct 2015 18:00:22 -0500 Subject: Add invensense stuff in-tree. Overhauled its .mk files as well as fixed some whitespace problems. Conflicts: libsensors/Android.mk --- invensense/mlsdk/mllite/mldl.c | 1092 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1092 insertions(+) create mode 100644 invensense/mlsdk/mllite/mldl.c (limited to 'invensense/mlsdk/mllite/mldl.c') diff --git a/invensense/mlsdk/mllite/mldl.c b/invensense/mlsdk/mllite/mldl.c new file mode 100644 index 0000000..eddef84 --- /dev/null +++ b/invensense/mlsdk/mllite/mldl.c @@ -0,0 +1,1092 @@ +/* + $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: mldl.c 5653 2011-06-16 21:06:55Z nroyer $ + * + *****************************************************************************/ + +/** + * @defgroup MLDL + * @brief Motion Library - Driver Layer. + * The Motion Library Driver Layer provides the intrface to the + * system drivers that are used by the Motion Library. + * + * @{ + * @file mldl.c + * @brief The Motion Library Driver Layer. + */ + +/* ------------------ */ +/* - Include Files. - */ +/* ------------------ */ + +#include + +#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 "mldl.h" +#include "mldl_cfg.h" +#include "compass.h" +#include "mlsl.h" +#include "mlos.h" +#include "mlinclude.h" +#include "ml.h" +#include "dmpKey.h" +#include "mlFIFOHW.h" +#include "compass.h" +#include "pressure.h" + +#include "log.h" +#undef MPL_LOG_TAG +#define MPL_LOG_TAG "MPL-mldl" + +#define _mldlDebug(x) //{x} + +/* --------------------- */ +/* - Variables. - */ +/* --------------------- */ + +#define MAX_LOAD_WRITE_SIZE (MPU_MEM_BANK_SIZE/2) /* 128 */ + +/*---- structure containing control variables used by MLDL ----*/ +static struct mldl_cfg mldlCfg; +struct ext_slave_descr gAccel; +struct ext_slave_descr gCompass; +struct ext_slave_descr gPressure; +struct mpu_platform_data gPdata; +static void *sMLSLHandle; +int_fast8_t intTrigger[NUM_OF_INTSOURCES]; + +/******************************************************************************* + * Functions for accessing the DMP memory via keys + ******************************************************************************/ + +unsigned short (*sGetAddress) (unsigned short key) = NULL; +static const unsigned char *localDmpMemory = NULL; +static unsigned short localDmpMemorySize = 0; + +/** + * @internal + * @brief Sets the function to use to convert keys to addresses. This + * will changed for each DMP code loaded. + * @param func + * Function used to convert keys to addresses. + * @endif + */ +void inv_set_get_address(unsigned short (*func) (unsigned short key)) +{ + INVENSENSE_FUNC_START; + _mldlDebug(MPL_LOGV("setGetAddress %d", (int)func); + ) + sGetAddress = func; +} + +/** + * @internal + * @brief Check if the feature is supported in the currently loaded + * DMP code basing on the fact that the key is assigned a + * value or not. + * @param key the DMP key + * @return whether the feature associated with the key is supported + * or not. + */ +uint_fast8_t inv_dmpkey_supported(unsigned short key) +{ + unsigned short memAddr; + + if (sGetAddress == NULL) { + MPL_LOGE("%s : sGetAddress is NULL\n", __func__); + return FALSE; + } + + memAddr = sGetAddress(key); + if (memAddr >= 0xffff) { + MPL_LOGV("inv_set_mpu_memory unsupported key\n"); + return FALSE; + } + + return TRUE; +} + +/** + * @internal + * @brief used to get the specified number of bytes from the original + * MPU memory location specified by the key. + * Reads the specified number of bytes from the MPU location + * that was used to program the MPU specified by the key. Each + * set of code specifies a function that changes keys into + * addresses. This function is set with setGetAddress(). + * + * @param key The key to use when looking up the address. + * @param length Number of bytes to read. + * @param buffer Result for data. + * + * @return INV_SUCCESS if the command is successful, INV_ERROR otherwise. The key + * not corresponding to a memory address will result in INV_ERROR. + * @endif + */ +inv_error_t inv_get_mpu_memory_original(unsigned short key, + unsigned short length, + unsigned char *buffer) +{ + unsigned short offset; + + if (sGetAddress == NULL) { + return INV_ERROR_NOT_OPENED; + } + + offset = sGetAddress(key); + if (offset >= localDmpMemorySize || (offset + length) > localDmpMemorySize) { + return INV_ERROR_INVALID_PARAMETER; + } + + memcpy(buffer, &localDmpMemory[offset], length); + + return INV_SUCCESS; +} + +unsigned short inv_dl_get_address(unsigned short key) +{ + unsigned short offset; + if (sGetAddress == NULL) { + return INV_ERROR_NOT_OPENED; + } + + offset = sGetAddress(key); + return offset; +} + +/* ---------------------- */ +/* - Static Functions. - */ +/* ---------------------- */ + +/** + * @brief Open the driver layer and resets the internal + * gyroscope, accelerometer, and compass data + * structures. + * @param mlslHandle + * the serial handle. + * @return INV_SUCCESS if successful, a non-zero error code otherwise. + */ +inv_error_t inv_dl_open(void *mlslHandle) +{ + inv_error_t result; + memset(&mldlCfg, 0, sizeof(mldlCfg)); + memset(intTrigger, INT_CLEAR, sizeof(intTrigger)); + + sMLSLHandle = mlslHandle; + + mldlCfg.addr = 0x68; /* default incase the driver doesn't set it */ + mldlCfg.accel = &gAccel; + mldlCfg.compass = &gCompass; + mldlCfg.pressure = &gPressure; + mldlCfg.pdata = &gPdata; + + result = (inv_error_t) inv_mpu_open(&mldlCfg, sMLSLHandle, + sMLSLHandle, sMLSLHandle, sMLSLHandle); + return result; +} + +/** + * @brief Closes/Cleans up the ML Driver Layer. + * Put the device in sleep mode. + * @return INV_SUCCESS or non-zero error code. + */ +inv_error_t inv_dl_close(void) +{ + INVENSENSE_FUNC_START; + inv_error_t result = INV_SUCCESS; + + result = (inv_error_t) inv_mpu_suspend(&mldlCfg, + sMLSLHandle, + sMLSLHandle, + sMLSLHandle, + sMLSLHandle, + INV_ALL_SENSORS); + + result = (inv_error_t) inv_mpu_close(&mldlCfg, sMLSLHandle, + sMLSLHandle, sMLSLHandle, sMLSLHandle); + /* Clear all previous settings */ + memset(&mldlCfg, 0, sizeof(mldlCfg)); + sMLSLHandle = NULL; + sGetAddress = NULL; + return result; +} + +/** + * @brief Sets the requested_sensors + * + * Accessor to set the requested_sensors field of the mldl_cfg structure. + * Typically set at initialization. + * + * @param sensors + * Bitfield of the sensors that are going to be used. Combination of the + * following: + * - INV_X_GYRO + * - INV_Y_GYRO + * - INV_Z_GYRO + * - INV_DMP_PROCESSOR + * - INV_X_ACCEL + * - INV_Y_ACCEL + * - INV_Z_ACCEL + * - INV_X_COMPASS + * - INV_Y_COMPASS + * - INV_Z_COMPASS + * - INV_X_PRESSURE + * - INV_Y_PRESSURE + * - INV_Z_PRESSURE + * - INV_THREE_AXIS_GYRO + * - INV_THREE_AXIS_ACCEL + * - INV_THREE_AXIS_COMPASS + * - INV_THREE_AXIS_PRESSURE + * + * @return INV_SUCCESS or non-zero error code + */ +inv_error_t inv_init_requested_sensors(unsigned long sensors) +{ + mldlCfg.requested_sensors = sensors; + + return INV_SUCCESS; +} + +/** + * @brief Starts the DMP running + * + * Resumes the sensor if any of the sensor axis or components are requested + * + * @param sensors + * Bitfield of the sensors to turn on. Combination of the following: + * - INV_X_GYRO + * - INV_Y_GYRO + * - INV_Z_GYRO + * - INV_DMP_PROCESSOR + * - INV_X_ACCEL + * - INV_Y_ACCEL + * - INV_Z_ACCEL + * - INV_X_COMPASS + * - INV_Y_COMPASS + * - INV_Z_COMPASS + * - INV_X_PRESSURE + * - INV_Y_PRESSURE + * - INV_Z_PRESSURE + * - INV_THREE_AXIS_GYRO + * - INV_THREE_AXIS_ACCEL + * - INV_THREE_AXIS_COMPASS + * - INV_THREE_AXIS_PRESSURE + * + * @return INV_SUCCESS or non-zero error code + */ +inv_error_t inv_dl_start(unsigned long sensors) +{ + INVENSENSE_FUNC_START; + inv_error_t result = INV_SUCCESS; + + mldlCfg.requested_sensors = sensors; + result = inv_mpu_resume(&mldlCfg, + sMLSLHandle, + sMLSLHandle, + sMLSLHandle, + sMLSLHandle, + sensors); + return result; +} + +/** + * @brief Stops the DMP running and puts it in low power as requested + * + * Suspends each sensor according to the bitfield, if all axis and components + * of the sensor is off. + * + * @param sensors Bitfiled of the sensors to leave on. Combination of the + * following: + * - INV_X_GYRO + * - INV_Y_GYRO + * - INV_Z_GYRO + * - INV_X_ACCEL + * - INV_Y_ACCEL + * - INV_Z_ACCEL + * - INV_X_COMPASS + * - INV_Y_COMPASS + * - INV_Z_COMPASS + * - INV_X_PRESSURE + * - INV_Y_PRESSURE + * - INV_Z_PRESSURE + * - INV_THREE_AXIS_GYRO + * - INV_THREE_AXIS_ACCEL + * - INV_THREE_AXIS_COMPASS + * - INV_THREE_AXIS_PRESSURE + * + * + * @return INV_SUCCESS or non-zero error code + */ +inv_error_t inv_dl_stop(unsigned long sensors) +{ + INVENSENSE_FUNC_START; + inv_error_t result = INV_SUCCESS; + + result = inv_mpu_suspend(&mldlCfg, + sMLSLHandle, + sMLSLHandle, + sMLSLHandle, + sMLSLHandle, + sensors); + return result; +} + +/** + * @brief Get a pointer to the internal data structure + * storing the configuration for the MPU, the accelerometer + * and the compass in use. + * @return a pointer to the data structure of type 'struct mldl_cfg'. + */ +struct mldl_cfg *inv_get_dl_config(void) +{ + return &mldlCfg; +} + +/** + * @brief Query the MPU slave address. + * @return The 7-bit mpu slave address. + */ +unsigned char inv_get_mpu_slave_addr(void) +{ + INVENSENSE_FUNC_START; + return mldlCfg.addr; +} + +/** + * @internal + * @brief MLDLCfgDMP configures the Digital Motion Processor internal to + * the MPU. The DMP can be enabled or disabled and the start address + * can be set. + * + * @param enableRun Enables the DMP processing if set to TRUE. + * @param enableFIFO Enables DMP output to the FIFO if set to TRUE. + * @param startAddress start address + * + * @return Zero if the command is successful, an error code otherwise. +*/ +inv_error_t inv_get_dl_ctrl_dmp(unsigned char enableRun, + unsigned char enableFIFO) +{ + INVENSENSE_FUNC_START; + + mldlCfg.dmp_enable = enableRun; + mldlCfg.fifo_enable = enableFIFO; + mldlCfg.gyro_needs_reset = TRUE; + + return INV_SUCCESS; +} + +/** + * @brief inv_get_dl_cfg_int configures the interrupt function on the specified pin. + * The basic interrupt signal characteristics can be set + * (i.e. active high/low, open drain/push pull, etc.) and the + * triggers can be set. + * Currently only INTPIN_MPU is supported. + * + * @param triggers + * bitmask of triggers to enable for interrupt. + * The available triggers are: + * - BIT_MPU_RDY_EN + * - BIT_DMP_INT_EN + * - BIT_RAW_RDY_EN + * + * @return Zero if the command is successful, an error code otherwise. +*/ +inv_error_t inv_get_dl_cfg_int(unsigned char triggers) +{ + inv_error_t result = INV_SUCCESS; + +#if defined CONFIG_MPU_SENSORS_MPU3050 + /* Mantis has 8 bits of interrupt config bits */ + if (triggers & !(BIT_MPU_RDY_EN | BIT_DMP_INT_EN | BIT_RAW_RDY_EN)) { + return INV_ERROR_INVALID_PARAMETER; + } +#endif + + mldlCfg.int_config = triggers; + if (!mldlCfg.gyro_is_suspended) { + result = inv_serial_single_write(sMLSLHandle, mldlCfg.addr, + MPUREG_INT_CFG, + (mldlCfg.int_config | mldlCfg.pdata-> + int_config)); + } else { + mldlCfg.gyro_needs_reset = TRUE; + } + + return result; +} + +/** + * @brief configures the output sampling rate on the MPU. + * Three parameters control the sampling: + * + * 1) Low pass filter bandwidth, and + * 2) output sampling divider. + * + * The output sampling rate is determined by the divider and the low + * pass filter setting. If the low pass filter is set to + * 'MPUFILTER_256HZ_NOLPF2', then the sample rate going into the + * divider is 8kHz; for all other settings it is 1kHz. + * The 8-bit divider will divide this frequency to get the resulting + * sample frequency. + * For example, if the filter setting is not 256Hz and the divider is + * set to 7, then the sample rate is as follows: + * sample rate = internal sample rate / div = 1kHz / 8 = 125Hz (or 8ms). + * + * The low pass filter selection codes control both the cutoff frequency of + * the internal low pass filter and internal analog sampling rate. The + * latter, in turn, affects the final output sampling rate according to the + * sample rate divider settig. + * 0 -> 256 Hz cutoff BW, 8 kHz analog sample rate, + * 1 -> 188 Hz cutoff BW, 1 kHz analog sample rate, + * 2 -> 98 Hz cutoff BW, 1 kHz analog sample rate, + * 3 -> 42 Hz cutoff BW, 1 kHz analog sample rate, + * 4 -> 20 Hz cutoff BW, 1 kHz analog sample rate, + * 5 -> 10 Hz cutoff BW, 1 kHz analog sample rate, + * 6 -> 5 Hz cutoff BW, 1 kHz analog sample rate, + * 7 -> 2.1 kHz cutoff BW, 8 kHz analog sample rate. + * + * @param lpf low pass filter, 0 to 7. + * @param divider Output sampling rate divider, 0 to 255. + * + * @return ML_SUCESS if successful; a non-zero error code otherwise. +**/ +inv_error_t inv_dl_cfg_sampling(unsigned char lpf, unsigned char divider) +{ + /*---- do range checking ----*/ + if (lpf >= NUM_MPU_FILTER) { + return INV_ERROR_INVALID_PARAMETER; + } + + mldlCfg.lpf = lpf; + mldlCfg.divider = divider; + mldlCfg.gyro_needs_reset = TRUE; + + return INV_SUCCESS; +} + +/** + * @brief set the full scale range for the gyros. + * The full scale selection codes correspond to: + * 0 -> 250 dps, + * 1 -> 500 dps, + * 2 -> 1000 dps, + * 3 -> 2000 dps. + * Full scale range affect the MPU's measurement + * sensitivity. + * + * @param fullScale + * the gyro full scale range in dps. + * + * @return INV_SUCCESS or non-zero error code. +**/ +inv_error_t inv_set_full_scale(float fullScale) +{ + if (fullScale == 250.f) + mldlCfg.full_scale = MPU_FS_250DPS; + else if (fullScale == 500.f) + mldlCfg.full_scale = MPU_FS_500DPS; + else if (fullScale == 1000.f) + mldlCfg.full_scale = MPU_FS_1000DPS; + else if (fullScale == 2000.f) + mldlCfg.full_scale = MPU_FS_2000DPS; + else { // not a valid setting + MPL_LOGE("Invalid full scale range specification for gyros : %f\n", + fullScale); + MPL_LOGE + ("\tAvailable values : +/- 250 dps, +/- 500 dps, +/- 1000 dps, +/- 2000 dps\n"); + return INV_ERROR_INVALID_PARAMETER; + } + mldlCfg.gyro_needs_reset = TRUE; + + return INV_SUCCESS; +} + +/** + * @brief This function sets the external sync for the MPU sampling. + * It can be synchronized on the LSB of any of the gyros, any of the + * external accels, or on the temp readings. + * + * @param extSync External sync selection, 0 to 7. + * @return Zero if the command is successful; an error code otherwise. +**/ +inv_error_t inv_set_external_sync(unsigned char extSync) +{ + INVENSENSE_FUNC_START; + + /*---- do range checking ----*/ + if (extSync >= NUM_MPU_EXT_SYNC) { + return INV_ERROR_INVALID_PARAMETER; + } + mldlCfg.ext_sync = extSync; + mldlCfg.gyro_needs_reset = TRUE; + + return INV_SUCCESS; +} + +inv_error_t inv_set_ignore_system_suspend(unsigned char ignore) +{ + INVENSENSE_FUNC_START; + + mldlCfg.ignore_system_suspend = ignore; + + return INV_SUCCESS; +} + +/** + * @brief inv_clock_source function sets the clock source for the MPU gyro + * processing. + * The source can be any of the following: + * - Internal 8MHz oscillator, + * - PLL with X gyro as reference, + * - PLL with Y gyro as reference, + * - PLL with Z gyro as reference, + * - PLL with external 32.768Mhz reference, or + * - PLL with external 19.2MHz reference + * + * For best accuracy and timing, it is highly recommended to use one + * of the gyros as the clock source; however this gyro must be + * enabled to use its clock (see 'MLDLPowerMgmtMPU()'). + * + * @param clkSource Clock source selection. + * Can be one of: + * - CLK_INTERNAL, + * - CLK_PLLGYROX, + * - CLK_PLLGYROY, + * - CLK_PLLGYROZ, + * - CLK_PLLEXT32K, or + * - CLK_PLLEXT19M. + * + * @return Zero if the command is successful; an error code otherwise. +**/ +inv_error_t inv_clock_source(unsigned char clkSource) +{ + INVENSENSE_FUNC_START; + + /*---- do range checking ----*/ + if (clkSource >= NUM_CLK_SEL) { + return INV_ERROR_INVALID_PARAMETER; + } + + mldlCfg.clk_src = clkSource; + mldlCfg.gyro_needs_reset = TRUE; + + return INV_SUCCESS; +} + +/** + * @brief Set the Temperature Compensation offset. + * @param tc + * a pointer to the temperature compensations offset + * for the 3 gyro axes. + * @return INV_SUCCESS if successful, a non-zero error code otherwise. + */ +inv_error_t inv_set_offsetTC(const unsigned char *tc) +{ + int ii; + inv_error_t result; + + for (ii = 0; ii < ARRAY_SIZE(mldlCfg.offset_tc); ii++) { + mldlCfg.offset_tc[ii] = tc[ii]; + } + + if (!mldlCfg.gyro_is_suspended) { +#ifdef CONFIG_MPU_SENSORS_MPU3050 + result = inv_serial_single_write(sMLSLHandle, mldlCfg.addr, + MPUREG_XG_OFFS_TC, tc[0]); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + result = inv_serial_single_write(sMLSLHandle, mldlCfg.addr, + MPUREG_YG_OFFS_TC, tc[1]); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + result = inv_serial_single_write(sMLSLHandle, mldlCfg.addr, + MPUREG_ZG_OFFS_TC, tc[2]); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } +#else + unsigned char reg; + result = inv_serial_single_write(sMLSLHandle, mldlCfg.addr, + MPUREG_XG_OFFS_TC, + ((mldlCfg.offset_tc[0] << 1) + & BITS_XG_OFFS_TC)); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + reg = ((mldlCfg.offset_tc[1] << 1) & BITS_YG_OFFS_TC); +#ifdef CONFIG_MPU_SENSORS_MPU6050B1 + if (mldlCfg.pdata->level_shifter) + reg |= BIT_I2C_MST_VDDIO; +#endif + result = inv_serial_single_write(sMLSLHandle, mldlCfg.addr, + MPUREG_YG_OFFS_TC, reg); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + result = inv_serial_single_write(sMLSLHandle, mldlCfg.addr, + MPUREG_ZG_OFFS_TC, + ((mldlCfg.offset_tc[2] << 1) + & BITS_ZG_OFFS_TC)); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } +#endif + } else { + mldlCfg.gyro_needs_reset = TRUE; + } + return INV_SUCCESS; +} + +/** + * @brief Set the gyro offset. + * @param offset + * a pointer to the gyro offset for the 3 gyro axes. This is scaled + * as it would be written to the hardware registers. + * @return INV_SUCCESS if successful, a non-zero error code otherwise. + */ +inv_error_t inv_set_offset(const short *offset) +{ + inv_error_t result; + unsigned char regs[7]; + int ii; + long sf; + + sf = (2000L * 131) / mldlCfg.gyro_sens_trim; + for (ii = 0; ii < ARRAY_SIZE(mldlCfg.offset); ii++) { + // Record the bias in the units the register uses + mldlCfg.offset[ii] = offset[ii]; + // Convert the bias to 1 dps = 1<<16 + inv_obj.gyro_bias[ii] = -offset[ii] * sf; + regs[1 + ii * 2] = (unsigned char)(offset[ii] >> 8) & 0xff; + regs[1 + ii * 2 + 1] = (unsigned char)(offset[ii] & 0xff); + } + + if (!mldlCfg.gyro_is_suspended) { + regs[0] = MPUREG_X_OFFS_USRH; + result = inv_serial_write(sMLSLHandle, mldlCfg.addr, 7, regs); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + } else { + mldlCfg.gyro_needs_reset = TRUE; + } + return INV_SUCCESS; +} + +/** + * @internal + * @brief used to get the specified number of bytes in the specified MPU + * memory bank. + * The memory bank is one of the following: + * - MPUMEM_RAM_BANK_0, + * - MPUMEM_RAM_BANK_1, + * - MPUMEM_RAM_BANK_2, or + * - MPUMEM_RAM_BANK_3. + * + * @param bank Memory bank to write. + * @param memAddr Starting address for write. + * @param length Number of bytes to write. + * @param buffer Result for data. + * + * @return zero if the command is successful, an error code otherwise. + * @endif + */ +inv_error_t +inv_get_mpu_memory_one_bank(unsigned char bank, + unsigned char memAddr, + unsigned short length, unsigned char *buffer) +{ + inv_error_t result; + + if ((bank >= MPU_MEM_NUM_RAM_BANKS) || + //(memAddr >= MPU_MEM_BANK_SIZE) || always 0, memAddr is an u_char, therefore limited to 255 + ((memAddr + length) > MPU_MEM_BANK_SIZE) || (NULL == buffer)) { + return INV_ERROR_INVALID_PARAMETER; + } + + if (mldlCfg.gyro_is_suspended) { + memcpy(buffer, &mldlCfg.ram[bank][memAddr], length); + result = INV_SUCCESS; + } else { + result = inv_serial_read_mem(sMLSLHandle, mldlCfg.addr, + ((bank << 8) | memAddr), length, buffer); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + } + + return result; +} + +/** + * @internal + * @brief used to set the specified number of bytes in the specified MPU + * memory bank. + * The memory bank is one of the following: + * - MPUMEM_RAM_BANK_0, + * - MPUMEM_RAM_BANK_1, + * - MPUMEM_RAM_BANK_2, or + * - MPUMEM_RAM_BANK_3. + * + * @param bank Memory bank to write. + * @param memAddr Starting address for write. + * @param length Number of bytes to write. + * @param buffer Result for data. + * + * @return zero if the command is successful, an error code otherwise. + * @endif + */ +inv_error_t inv_set_mpu_memory_one_bank(unsigned char bank, + unsigned short memAddr, + unsigned short length, + const unsigned char *buffer) +{ + inv_error_t result = INV_SUCCESS; + int different; + + if ((bank >= MPU_MEM_NUM_RAM_BANKS) || (memAddr >= MPU_MEM_BANK_SIZE) || + ((memAddr + length) > MPU_MEM_BANK_SIZE) || (NULL == buffer)) { + return INV_ERROR_INVALID_PARAMETER; + } + + different = memcmp(&mldlCfg.ram[bank][memAddr], buffer, length); + memcpy(&mldlCfg.ram[bank][memAddr], buffer, length); + if (!mldlCfg.gyro_is_suspended) { + result = inv_serial_write_mem(sMLSLHandle, mldlCfg.addr, + ((bank << 8) | memAddr), length, buffer); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + } else if (different) { + mldlCfg.gyro_needs_reset = TRUE; + } + + return result; +} + +/** + * @internal + * @brief used to get the specified number of bytes from the MPU location + * specified by the key. + * Reads the specified number of bytes from the MPU location + * specified by the key. Each set of code specifies a function + * that changes keys into addresses. This function is set with + * setGetAddress(). + * + * @param key The key to use when looking up the address. + * @param length Number of bytes to read. + * @param buffer Result for data. + * + * @return INV_SUCCESS if the command is successful, INV_ERROR otherwise. The key + * not corresponding to a memory address will result in INV_ERROR. + * @endif + */ +inv_error_t inv_get_mpu_memory(unsigned short key, + unsigned short length, unsigned char *buffer) +{ + unsigned char bank; + inv_error_t result; + unsigned short memAddr; + + if (sGetAddress == NULL) { + return INV_ERROR_NOT_OPENED; + } + + memAddr = sGetAddress(key); + if (memAddr >= 0xffff) + return INV_ERROR_FEATURE_NOT_IMPLEMENTED; + bank = memAddr >> 8; // Get Bank + memAddr &= 0xff; + + while (memAddr + length > MPU_MEM_BANK_SIZE) { + // We cross a bank in the middle + unsigned short sub_length = MPU_MEM_BANK_SIZE - memAddr; + result = inv_get_mpu_memory_one_bank(bank, (unsigned char)memAddr, + sub_length, buffer); + if (INV_SUCCESS != result) + return result; + bank++; + length -= sub_length; + buffer += sub_length; + memAddr = 0; + } + result = inv_get_mpu_memory_one_bank(bank, (unsigned char)memAddr, + length, buffer); + + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + + return result; +} + +/** + * @internal + * @brief used to set the specified number of bytes from the MPU location + * specified by the key. + * Set the specified number of bytes from the MPU location + * specified by the key. Each set of DMP code specifies a function + * that changes keys into addresses. This function is set with + * setGetAddress(). + * + * @param key The key to use when looking up the address. + * @param length Number of bytes to write. + * @param buffer Result for data. + * + * @return INV_SUCCESS if the command is successful, INV_ERROR otherwise. The key + * not corresponding to a memory address will result in INV_ERROR. + * @endif + */ +inv_error_t inv_set_mpu_memory(unsigned short key, + unsigned short length, + const unsigned char *buffer) +{ + inv_error_t result = INV_SUCCESS; + unsigned short memAddr; + unsigned char bank; + + if (sGetAddress == NULL) { + MPL_LOGE("MLDSetMemoryMPU sGetAddress is NULL\n"); + return INV_ERROR_INVALID_MODULE; + } + memAddr = sGetAddress(key); + + if (memAddr >= 0xffff) { + MPL_LOGE("inv_set_mpu_memory unsupported key\n"); + return INV_ERROR_INVALID_MODULE; // This key not supported + } + + bank = (unsigned char)(memAddr >> 8); + memAddr &= 0xff; + + while (memAddr + length > MPU_MEM_BANK_SIZE) { + // We cross a bank in the middle + unsigned short sub_length = MPU_MEM_BANK_SIZE - memAddr; + + result = inv_set_mpu_memory_one_bank(bank, memAddr, sub_length, buffer); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + + bank++; + length -= sub_length; + buffer += sub_length; + memAddr = 0; + } + result = inv_set_mpu_memory_one_bank(bank, memAddr, length, buffer); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + return result; +} + +/** + * @brief Load the DMP with the given code and configuration. + * @param buffer + * the DMP data. + * @param length + * the length in bytes of the DMP data. + * @param config + * the DMP configuration. + * @return INV_SUCCESS if successful, a non-zero error code otherwise. + */ +inv_error_t inv_load_dmp(const unsigned char *buffer, + unsigned short length, unsigned short config) +{ + INVENSENSE_FUNC_START; + + inv_error_t result = INV_SUCCESS; + unsigned short toWrite; + unsigned short memAddr = 0; + localDmpMemory = buffer; + localDmpMemorySize = length; + + mldlCfg.dmp_cfg1 = (config >> 8); + mldlCfg.dmp_cfg2 = (config & 0xff); + + while (length > 0) { + toWrite = length; + if (toWrite > MAX_LOAD_WRITE_SIZE) + toWrite = MAX_LOAD_WRITE_SIZE; + + result = + inv_set_mpu_memory_one_bank(memAddr >> 8, memAddr & 0xff, toWrite, + buffer); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + + buffer += toWrite; + memAddr += toWrite; + length -= toWrite; + } + + return result; +} + +/** + * @brief Get the silicon revision ID. + * @return The silicon revision ID + * (0 will be read if inv_mpu_open returned an error) + */ +unsigned char inv_get_silicon_rev(void) +{ + return mldlCfg.silicon_revision; +} + +/** + * @brief Get the product revision ID. + * @return The product revision ID + * (0 will be read if inv_mpu_open returned an error) + */ +unsigned char inv_get_product_rev(void) +{ + return mldlCfg.product_revision; +} + +/******************************************************************************* + ******************************************************************************* + ******************************************************************************* + * @todo these belong with an interface to the kernel driver layer + ******************************************************************************* + ******************************************************************************* + ******************************************************************************/ + +/** + * @brief inv_get_interrupt_status returns the interrupt status from the specified + * interrupt pin. + * @param intPin + * Currently only the value INTPIN_MPU is supported. + * @param status + * The available statuses are: + * - BIT_MPU_RDY_EN + * - BIT_DMP_INT_EN + * - BIT_RAW_RDY_EN + * + * @return INV_SUCCESS or a non-zero error code. + */ +inv_error_t inv_get_interrupt_status(unsigned char intPin, + unsigned char *status) +{ + INVENSENSE_FUNC_START; + + inv_error_t result; + + switch (intPin) { + + case INTPIN_MPU: + /*---- return the MPU interrupt status ----*/ + result = inv_serial_read(sMLSLHandle, mldlCfg.addr, + MPUREG_INT_STATUS, 1, status); + break; + + default: + result = INV_ERROR_INVALID_PARAMETER; + break; + } + + return result; +} + +/** + * @brief query the current status of an interrupt source. + * @param srcIndex + * index of the interrupt source. + * Currently the only source supported is INTPIN_MPU. + * + * @return 1 if the interrupt has been triggered. + */ +unsigned char inv_get_interrupt_trigger(unsigned char srcIndex) +{ + INVENSENSE_FUNC_START; + return intTrigger[srcIndex]; +} + +/** + * @brief clear the 'triggered' status for an interrupt source. + * @param srcIndex + * index of the interrupt source. + * Currently only INTPIN_MPU is supported. + */ +void inv_clear_interrupt_trigger(unsigned char srcIndex) +{ + INVENSENSE_FUNC_START; + intTrigger[srcIndex] = 0; +} + +/** + * @brief inv_interrupt_handler function should be called when an interrupt is + * received. The source parameter identifies which interrupt source + * caused the interrupt. Note that this routine should not be called + * directly from the interrupt service routine. + * + * @param intSource MPU, AUX1, AUX2, or timer. Can be one of: INTSRC_MPU, INTSRC_AUX1, + * INTSRC_AUX2, or INT_SRC_TIMER. + * + * @return Zero if the command is successful; an error code otherwise. + */ +inv_error_t inv_interrupt_handler(unsigned char intSource) +{ + INVENSENSE_FUNC_START; + /*---- range check ----*/ + if (intSource >= NUM_OF_INTSOURCES) { + return INV_ERROR; + } + + /*---- save source of interrupt ----*/ + intTrigger[intSource] = INT_TRIGGERED; + +#ifdef ML_USE_DMP_SIM + if (intSource == INTSRC_AUX1 || intSource == INTSRC_TIMER) { + MLSimHWDataInput(); + } +#endif + + return INV_SUCCESS; +} + +/***************************/ + /**@}*//* end of defgroup */ +/***************************/ -- cgit v1.1