3270 lines
106 KiB
C
3270 lines
106 KiB
C
/*
|
|
* Copyright (C) 2018 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.
|
|
*/
|
|
|
|
#include <algos/time_sync.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <timer.h>
|
|
#include <sensors.h>
|
|
#include <heap.h>
|
|
#include <spi.h>
|
|
#include <slab.h>
|
|
#include <limits.h>
|
|
#include <atomic.h>
|
|
#include <plat/rtc.h>
|
|
#include <plat/gpio.h>
|
|
#include <plat/exti.h>
|
|
#include <plat/syscfg.h>
|
|
#include <seos.h>
|
|
#include <gpio.h>
|
|
#include <isr.h>
|
|
#include <halIntf.h>
|
|
#include <hostIntf.h>
|
|
#include <nanohubPacket.h>
|
|
#include <cpu/cpuMath.h>
|
|
#include <nanohub_math.h>
|
|
#include <variant/sensType.h>
|
|
#include <variant/variant.h>
|
|
#include <common/math/macros.h>
|
|
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
#include <calibration/accelerometer/accel_cal.h>
|
|
#endif
|
|
#ifdef GYRO_CAL_ENABLED
|
|
#include <calibration/gyroscope/gyro_cal.h>
|
|
#endif
|
|
|
|
#define INFO_PRINT(fmt, ...) do { \
|
|
osLog(LOG_INFO, "%s " fmt, "[ICM40600]", ##__VA_ARGS__); \
|
|
} while (0);
|
|
|
|
#define ERROR_PRINT(fmt, ...) do { \
|
|
osLog(LOG_ERROR, "%s " fmt, "[ICM40600] ERROR:", ##__VA_ARGS__); \
|
|
} while (0);
|
|
|
|
#define DEBUG_PRINT(fmt, ...) do { \
|
|
if (DBG_ENABLE) { \
|
|
INFO_PRINT(fmt, ##__VA_ARGS__); \
|
|
} \
|
|
} while (0);
|
|
|
|
#define DEBUG_PRINT_IF(cond, fmt, ...) do { \
|
|
if ((cond) && DBG_ENABLE) { \
|
|
INFO_PRINT(fmt, ##__VA_ARGS__); \
|
|
} \
|
|
} while (0);
|
|
|
|
#define DBG_ENABLE 0
|
|
#define DBG_STATE 0
|
|
#define DBG_TIMESTAMP 0
|
|
|
|
#define ICM40600_APP_ID APP_ID_MAKE(NANOHUB_VENDOR_INVENSENSE, 0x2)
|
|
#define ICM40600_APP_VERSION 1
|
|
|
|
#define ICM40600_SPI_WRITE 0x00
|
|
#define ICM40600_SPI_READ 0x80
|
|
|
|
// SPI speeds officialy supported: 5MHz (low speed), 17MHz (high speed)
|
|
#define ICM40600_SPI_LOW_SPEED_HZ 5000000
|
|
#define ICM40600_SPI_HIGH_SPEED_HZ 17000000
|
|
#define ICM40600_SPI_DEFAULT_SPEED_HZ ICM40600_SPI_LOW_SPEED_HZ
|
|
#define ICM40600_SPI_MODE 3
|
|
|
|
#ifndef ICM40600_SPI_BUS_ID
|
|
#define ICM40600_SPI_BUS_ID 1
|
|
#endif
|
|
#ifndef ICM40600_INT1_PIN
|
|
#define ICM40600_INT1_PIN GPIO_PB(6)
|
|
#endif
|
|
#ifndef ICM40600_INT1_IRQ
|
|
#define ICM40600_INT1_IRQ EXTI9_5_IRQn
|
|
#endif
|
|
#define ICM40600_SPI_SPEED_HZ ICM40600_SPI_HIGH_SPEED_HZ
|
|
|
|
// set SPI speed register value
|
|
#if ICM40600_SPI_SPEED_HZ == ICM40600_SPI_LOW_SPEED_HZ
|
|
#define ICM40600_SPI_SPEED_REG_VALUE BIT_SPI_SPEED_5MHZ
|
|
#elif ICM40600_SPI_SPEED_HZ == ICM40600_SPI_HIGH_SPEED_HZ
|
|
#define ICM40600_SPI_SPEED_REG_VALUE BIT_SPI_SPEED_17MHZ
|
|
#else
|
|
#error "Invalid ICM40600_SPI_SPEED_HZ setting: valid values are 5MHz or 17MHz"
|
|
#endif
|
|
|
|
/*
|
|
**********************
|
|
* Chip configuration
|
|
**********************
|
|
*/
|
|
/* Full-scale range */
|
|
// Default accel range is 8g
|
|
#ifndef ICM40600_ACC_RANGE_G
|
|
#define ICM40600_ACC_RANGE_G 8
|
|
#endif
|
|
// Default gyro range is 2000dps
|
|
#ifndef ICM40600_GYR_RANGE_DPS
|
|
#define ICM40600_GYR_RANGE_DPS 2000
|
|
#endif
|
|
// 0 -> +-16g, 1 -> +-8g, 2 -> +-4g, 3 -> +-2g, 4 -> +-1g
|
|
#if ICM40600_ACC_RANGE_G == 16
|
|
#define ICM40600_ACC_FS 0
|
|
#elif ICM40600_ACC_RANGE_G == 8
|
|
#define ICM40600_ACC_FS 1
|
|
#elif ICM40600_ACC_RANGE_G == 4
|
|
#define ICM40600_ACC_FS 2
|
|
#else
|
|
#error "Invalid ICM40600_ACC_RANGE_G setting: valid values are 16, 8, 4 (no HiFi)"
|
|
#endif
|
|
// 0 -> +-2000dps, 1 -> +-1000dps, 2 -> +-500dps, 3 -> +-250dps, 4 -> +-125dps, 5 -> +-62.5dps, 6 -> +-31.25dps, 7 -> +-15.6225dps
|
|
#if ICM40600_GYR_RANGE_DPS == 2000
|
|
#define ICM40600_GYR_FS 0
|
|
#elif ICM40600_GYR_RANGE_DPS == 1000
|
|
#define ICM40600_GYR_FS 1
|
|
#else
|
|
#error "Invalid ICM40600_GYR_RANGE_DPS setting: valid values are 2000, 1000"
|
|
#endif
|
|
// Bandwidth for low-pass filters, using ODR/2 FIR
|
|
#define ICM40600_ACC_BW_IND BIT_ACCEL_UI_BW_2_FIR
|
|
#define ICM40600_GYR_BW_IND BIT_GYRO_UI_BW_2_FIR
|
|
|
|
/* INT1 configuration */
|
|
// Polarity: 0 -> Active Low, 1 -> Active High
|
|
#define INT1_POLARITY 1
|
|
// Drive circuit: 0 -> Open Drain, 1 -> Push-Pull, fixed for INT1 do not change!
|
|
#define INT1_DRIVE_CIRCUIT 1
|
|
// Mode: 0 -> Pulse, 1 -> Latch
|
|
#define INT1_MODE 0
|
|
|
|
/* Wake-on-Motion/No-Motion */
|
|
#define ICM40600_WOM_THRESHOLD_MG 160 // 160mg @ 25Hz
|
|
#define ICM40600_WOM_COMPUTE(val_mg) ((256 * val_mg) / 1000)
|
|
// No-Motion duration: 5s
|
|
#define ICM40600_NOM_DURATION_NS (5 * 1000000000ULL)
|
|
|
|
/*
|
|
**********************
|
|
* Factory calibration
|
|
**********************
|
|
*/
|
|
#define CALIBRATION_ODR 7 // 200Hz (support both LPM and LNM)
|
|
#define CALIBRATION_ACC_FS 0 // +-16g (= resolution of offset register)
|
|
#define CALIBRATION_GYR_FS 1 // +-1000dps (= resolution of offset register)
|
|
#define CALIBRATION_ACC_1G 2048 // LSB/g @+-16g
|
|
#define CALIBRATION_ACC_BW_IND BIT_ACCEL_UI_BW_4_IIR
|
|
#define CALIBRATION_GYR_BW_IND BIT_GYRO_UI_BW_4_IIR
|
|
|
|
#define CALIBRATION_READ_INTERVAL_US (200 * 1000) // 200ms (200/5ms=40 packets -> 40 * 16 = 640 bytes)
|
|
#define CALIBRATION_SAMPLE_NB 200 // 200/40packets = 5 loops
|
|
#define RETRY_CNT_CALIBRATION 10 // > 5 loops
|
|
|
|
/*
|
|
**********************
|
|
* Selftest
|
|
**********************
|
|
*/
|
|
#define SELF_TEST_ODR 6 // 1000Hz
|
|
#define SELF_TEST_ACC_FS 3 // +-2g
|
|
#define SELF_TEST_GYR_FS 3 // +-250dps
|
|
#define SELF_TEST_ACC_BW_IND BIT_ACCEL_UI_BW_10_IIR
|
|
#define SELF_TEST_GYR_BW_IND BIT_GYRO_UI_BW_10_IIR
|
|
#define SELF_TEST_MIN_ACC_MG 225 // mg
|
|
#define SELF_TEST_MAX_ACC_MG 675 // mg
|
|
#define SELF_TEST_MIN_GYR_DPS 60 // dps
|
|
#define SELF_TEST_MAX_GYR_OFF_DPS 20 // dps
|
|
#define SELF_TEST_ACC_SHIFT_DELTA 500 // = 0.5
|
|
#define SELF_TEST_GYR_SHIFT_DELTA 500 // = 0.5
|
|
|
|
#define SELF_TEST_ACC_SCALE 32768 / (16 >> SELF_TEST_ACC_FS) / 1000
|
|
#define SELF_TEST_GYR_SCALE 32768 / (2000 >> SELF_TEST_GYR_FS)
|
|
|
|
#define SELF_TEST_READ_INTERVAL_US (20 * 1000) // 20ms (20/1ms=20 packets -> 20 * 16 = 320 bytes)
|
|
#define SELF_TEST_SAMPLE_NB 200 // 200/20packets = 10 loops
|
|
#define RETRY_CNT_SELF_TEST 20 // > 10 loops
|
|
#define SELF_TEST_PRECISION 1000
|
|
#define SELF_TEST_SETTLE_TIME_MS 25
|
|
|
|
#define SELF_TEST_MIN_ACC (SELF_TEST_MIN_ACC_MG * SELF_TEST_ACC_SCALE * SELF_TEST_PRECISION)
|
|
#define SELF_TEST_MAX_ACC (SELF_TEST_MAX_ACC_MG * SELF_TEST_ACC_SCALE * SELF_TEST_PRECISION)
|
|
#define SELF_TEST_MIN_GYR (SELF_TEST_MIN_GYR_DPS * SELF_TEST_GYR_SCALE * SELF_TEST_PRECISION)
|
|
#define SELF_TEST_MAX_GYR (SELF_TEST_MAX_GYR_DPS * SELF_TEST_GYR_SCALE * SELF_TEST_PRECISION)
|
|
#define SELF_TEST_MAX_GYR_OFFSET (SELF_TEST_MAX_GYR_OFF_DPS * SELF_TEST_GYR_SCALE * SELF_TEST_PRECISION)
|
|
|
|
/*
|
|
****************
|
|
* register map *
|
|
****************
|
|
*/
|
|
/* Bank 0 */
|
|
#define REG_DEVICE_CONFIG 0x11
|
|
#define REG_SPI_SPEED 0x13
|
|
#define REG_INT_CONFIG 0x14
|
|
#define REG_FIFO_CONFIG 0x16
|
|
#define REG_INT_STATUS 0x2D
|
|
#define REG_FIFO_BYTE_COUNT1 0x2E
|
|
#define REG_FIFO_BYTE_COUNT2 0x2F
|
|
#define REG_FIFO_DATA 0x30
|
|
#define REG_SIGNAL_PATH_RESET 0x4B
|
|
#define REG_INTF_CONFIG0 0x4C
|
|
#define REG_PWR_MGMT_0 0x4E
|
|
#define REG_GYRO_CONFIG0 0x4F
|
|
#define REG_ACCEL_CONFIG0 0x50
|
|
#define REG_GYRO_CONFIG1 0x51
|
|
#define REG_GYRO_ACCEL_CONFIG0 0x52
|
|
#define REG_ACCEL_CONFIG1 0x53
|
|
#define REG_ACCEL_WOM_X_THR 0x54
|
|
#define REG_ACCEL_WOM_Y_THR 0x55
|
|
#define REG_ACCEL_WOM_Z_THR 0x56
|
|
#define REG_SMD_CONFIG 0x57
|
|
#define REG_INT_STATUS2 0x59
|
|
#define REG_TMST_CONFIG 0x5A
|
|
#define REG_FIFO_CONFIG1 0x5F
|
|
#define REG_FIFO_CONFIG2 0x60
|
|
#define REG_FIFO_CONFIG3 0x61
|
|
#define REG_INT_CONFIG0 0x63
|
|
#define REG_INT_CONFIG1 0x64
|
|
#define REG_INT_SOURCE0 0x65
|
|
#define REG_INT_SOURCE1 0x66
|
|
#define REG_SELF_TEST_CONFIG 0x70
|
|
#define REG_WHO_AM_I 0x75
|
|
#define REG_REG_BANK_SEL 0x76
|
|
#define REG_GOS_USER0 0x77
|
|
#define REG_GOS_USER1 0x78
|
|
#define REG_GOS_USER2 0x79
|
|
#define REG_GOS_USER3 0x7A
|
|
#define REG_GOS_USER4 0x7B
|
|
#define REG_GOS_USER5 0x7C
|
|
#define REG_GOS_USER6 0x7D
|
|
#define REG_GOS_USER7 0x7E
|
|
#define REG_GOS_USER8 0x7F
|
|
/* Bank 1 */
|
|
#define REG_XG_ST_DATA 0x5F
|
|
#define REG_YG_ST_DATA 0x60
|
|
#define REG_ZG_ST_DATA 0x61
|
|
/* Bank 2 */
|
|
#define REG_XA_ST_DATA 0x3B
|
|
#define REG_YA_ST_DATA 0x3C
|
|
#define REG_ZA_ST_DATA 0x3D
|
|
|
|
/* REG_WHO_AM_I */
|
|
#define WHO_AM_I_ICM40604 0x32
|
|
#define WHO_AM_I_ICM40605 0x33
|
|
/* REG_REG_BANK_SEL */
|
|
#define BIT_BANK_SEL_0 0x00
|
|
#define BIT_BANK_SEL_1 0x01
|
|
#define BIT_BANK_SEL_2 0x02
|
|
/* REG_DEVICE_CONFIG */
|
|
#define BIT_SOFT_RESET 0x01
|
|
/* REG_SPI_SPEED */
|
|
#define BIT_SPI_SPEED_5MHZ 0x03
|
|
#define BIT_SPI_SPEED_17MHZ 0x05
|
|
/* REG_GYRO_CONFIG0/REG_ACCEL_CONFIG0 */
|
|
#define SHIFT_GYRO_FS_SEL 5
|
|
#define SHIFT_ACCEL_FS_SEL 5
|
|
#define SHIFT_ODR_CONF 0
|
|
/* REG_INT_CONFIG */
|
|
#define SHIFT_INT1_POLARITY 0
|
|
#define SHIFT_INT1_DRIVE_CIRCUIT 1
|
|
#define SHIFT_INT1_MODE 2
|
|
/* REG_PWR_MGMT_0 */
|
|
#define BIT_TEMP_DIS 0x20
|
|
#define BIT_IDLE 0x10
|
|
#define BIT_GYRO_MODE_OFF 0x00
|
|
#define BIT_GYRO_MODE_STBY 0x04
|
|
#define BIT_GYRO_MODE_LN 0x0C
|
|
#define BIT_ACCEL_MODE_OFF 0x00
|
|
#define BIT_ACCEL_MODE_LN 0x03
|
|
/* REG_SIGNAL_PATH_RESET */
|
|
#define BIT_FIFO_FLUSH 0x02
|
|
/* REG_INTF_CONFIG0 */
|
|
#define BIT_FIFO_COUNT_REC 0x40
|
|
#define BIT_COUNT_BIG_ENDIAN 0x20
|
|
#define BIT_SENS_DATA_BIG_ENDIAN 0x10
|
|
#define BIT_UI_SIFS_DISABLE_SPI 0x02
|
|
#define BIT_UI_SIFS_DISABLE_I2C 0x03
|
|
/* REG_FIFO_CONFIG1 */
|
|
#define BIT_FIFO_ACCEL_EN 0x01
|
|
#define BIT_FIFO_GYRO_EN 0x02
|
|
#define BIT_FIFO_TEMP_EN 0x04
|
|
#define BIT_FIFO_TMST_FSYNC_EN 0x08
|
|
#define BIT_FIFO_HIRES_EN 0x10
|
|
#define BIT_FIFO_WM_TH 0x20
|
|
#define BIT_FIFO_RESUME_PART_RD 0x40
|
|
/* REG_INT_CONFIG1 */
|
|
#define BIT_INT_ASY_RST_DISABLE 0x10
|
|
/* REG_INT_SOURCE0 */
|
|
#define BIT_INT_UI_AGC_RDY_INT1_EN 0x01
|
|
#define BIT_INT_FIFO_FULL_INT1_EN 0x02
|
|
#define BIT_INT_FIFO_THS_INT1_EN 0x04
|
|
#define BIT_INT_UI_DRDY_INT1_EN 0x08
|
|
#define BIT_INT_RESET_DONE_INT1_EN 0x10
|
|
#define BIT_INT_PLL_RDY_INT1_EN 0x20
|
|
#define BIT_INT_UI_FSYNC_INT1_EN 0x40
|
|
/* REG_INT_SOURCE1 */
|
|
#define BIT_INT_WOM_X_INT1_EN 0x01
|
|
#define BIT_INT_WOM_Y_INT1_EN 0x02
|
|
#define BIT_INT_WOM_Z_INT1_EN 0x04
|
|
#define BIT_INT_SMD_INT1_EN 0x08
|
|
#define BIT_INT_WOM_XYZ_INT1_EN (BIT_INT_WOM_X_INT1_EN | BIT_INT_WOM_Y_INT1_EN | BIT_INT_WOM_Z_INT1_EN)
|
|
/* REG_SELF_TEST_CONFIG */
|
|
#define BIT_SELF_TEST_REGULATOR_EN 0x40
|
|
#define BIT_TEST_AZ_EN 0x20
|
|
#define BIT_TEST_AY_EN 0x10
|
|
#define BIT_TEST_AX_EN 0x08
|
|
#define BIT_TEST_GZ_EN 0x04
|
|
#define BIT_TEST_GY_EN 0x02
|
|
#define BIT_TEST_GX_EN 0x01
|
|
/* REG_INT_STATUS */
|
|
#define BIT_INT_STATUS_AGC_RDY 0x01
|
|
#define BIT_INT_STATUS_FIFO_FULL 0x02
|
|
#define BIT_INT_STATUS_FIFO_THS 0x04
|
|
#define BIT_INT_STATUS_DRDY 0x08
|
|
#define BIT_INT_STATUS_RESET_DONE 0x10
|
|
#define BIT_INT_STATUS_PLL_DRY 0x20
|
|
#define BIT_INT_STATUS_UI_FSYNC 0x40
|
|
/* REG_INT_STATUS2 */
|
|
#define BIT_INT_STATUS_WOM_X 0x01
|
|
#define BIT_INT_STATUS_WOM_Y 0x02
|
|
#define BIT_INT_STATUS_WOM_Z 0x04
|
|
#define BIT_INT_STATUS_SMD 0x08
|
|
#define BIT_INT_STATUS_WOM_XYZ (BIT_INT_STATUS_WOM_X | BIT_INT_STATUS_WOM_Y | BIT_INT_STATUS_WOM_Z)
|
|
/* REG_FIFO_CONFIG */
|
|
#define BIT_FIFO_MODE_BYPASS 0x00
|
|
#define BIT_FIFO_MODE_STREAM 0x40
|
|
#define BIT_FIFO_MODE_STOP_FULL 0x80
|
|
/* REG_GYRO_ACCEL_CONFIG0 */
|
|
#define BIT_ACCEL_UI_BW_2_FIR 0x00
|
|
#define BIT_ACCEL_UI_BW_4_IIR 0x10
|
|
#define BIT_ACCEL_UI_BW_5_IIR 0x20
|
|
#define BIT_ACCEL_UI_BW_8_IIR 0x30
|
|
#define BIT_ACCEL_UI_BW_10_IIR 0x40
|
|
#define BIT_ACCEL_UI_BW_16_IIR 0x50
|
|
#define BIT_ACCEL_UI_BW_20_IIR 0x60
|
|
#define BIT_ACCEL_UI_BW_40_IIR 0x70
|
|
#define BIT_GYRO_UI_BW_2_FIR 0x00
|
|
#define BIT_GYRO_UI_BW_4_IIR 0x01
|
|
#define BIT_GYRO_UI_BW_5_IIR 0x02
|
|
#define BIT_GYRO_UI_BW_8_IIR 0x03
|
|
#define BIT_GYRO_UI_BW_10_IIR 0x04
|
|
#define BIT_GYRO_UI_BW_16_IIR 0x05
|
|
#define BIT_GYRO_UI_BW_20_IIR 0x06
|
|
#define BIT_GYRO_UI_BW_40_IIR 0x07
|
|
/* fifo data packet header */
|
|
#define BIT_FIFO_HEAD_MSG 0x80
|
|
#define BIT_FIFO_HEAD_ACCEL 0x40
|
|
#define BIT_FIFO_HEAD_GYRO 0x20
|
|
#define BIT_FIFO_HEAD_TMSP_ODR 0x08
|
|
#define BIT_FIFO_HEAD_TMSP_NO_ODR 0x04
|
|
#define BIT_FIFO_HEAD_TMSP_FSYNC 0x0C
|
|
#define BIT_FIFO_HEAD_ODR_ACCEL 0x02
|
|
#define BIT_FIFO_HEAD_ODR_GYRO 0x01
|
|
/* REG_SMD_CONFIG */
|
|
#define BIT_WOM_INT_MODE_OR 0x00
|
|
#define BIT_WOM_INT_MODE_AND 0x08
|
|
#define BIT_WOM_MODE_INITIAL 0x00
|
|
#define BIT_WOM_MODE_PREV 0x04
|
|
#define BIT_SMD_MODE_OFF 0x00
|
|
#define BIT_SMD_MODE_OLD 0x01
|
|
#define BIT_SMD_MODE_SHORT 0x02
|
|
#define BIT_SMD_MODE_LONG 0x03
|
|
/* REG_TMST_CONFIG */
|
|
#define BIT_EN_DREG_FIFO_D2A 0x20
|
|
#define BIT_TMST_TO_REGS_EN 0x10
|
|
#define BIT_TMST_RESOL 0x08
|
|
#define BIT_TMST_DELTA_EN 0x04
|
|
#define BIT_TMST_FSYNC_EN 0x02
|
|
#define BIT_TMST_EN 0x01
|
|
|
|
/* FIFO data definitions */
|
|
#define FIFO_PACKET_SIZE 16
|
|
#define FIFO_MAX_PACKET_NB 65
|
|
#define FIFO_MIN_PACKET_NB 62
|
|
|
|
/* sensor startup time */
|
|
#define ICM40600_GYRO_START_TIME_MS 40
|
|
#define ICM40600_ACCEL_START_TIME_MS 10
|
|
|
|
/* temperature sensor */
|
|
#define TEMP_SCALE (128.0f / 115.49f) // scale, #9447
|
|
#define TEMP_OFFSET 25 // 25 degC offset
|
|
|
|
#define SPI_WRITE_0(addr, data) spiQueueWrite(addr, data, 2)
|
|
#define SPI_WRITE_1(addr, data, delay) spiQueueWrite(addr, data, delay)
|
|
#define GET_SPI_WRITE_MACRO(_1,_2,_3,NAME,...) NAME
|
|
#define SPI_WRITE(...) GET_SPI_WRITE_MACRO(__VA_ARGS__, SPI_WRITE_1, SPI_WRITE_0)(__VA_ARGS__)
|
|
|
|
#define SPI_READ_0(addr, size, buf) spiQueueRead(addr, size, buf, 0)
|
|
#define SPI_READ_1(addr, size, buf, delay) spiQueueRead(addr, size, buf, delay)
|
|
#define GET_SPI_READ_MACRO(_1,_2,_3,_4,NAME,...) NAME
|
|
#define SPI_READ(...) GET_SPI_READ_MACRO(__VA_ARGS__, SPI_READ_1, SPI_READ_0)(__VA_ARGS__)
|
|
|
|
#define EVT_SENSOR_MAG_DATA_RDY sensorGetMyEventType(SENS_TYPE_MAG)
|
|
#define EVT_SENSOR_NO_MOTION sensorGetMyEventType(SENS_TYPE_NO_MOTION)
|
|
#define EVT_SENSOR_ANY_MOTION sensorGetMyEventType(SENS_TYPE_ANY_MOTION)
|
|
|
|
#define MAX_NUM_COMMS_EVENT_SAMPLES 15
|
|
|
|
#define SPI_PACKET_SIZE 30
|
|
#define FIFO_READ_SIZE (FIFO_MAX_PACKET_NB * FIFO_PACKET_SIZE)
|
|
#define BUF_MARGIN 32 // some extra buffer for additional reg RW when a FIFO read happens
|
|
#define SPI_BUF_SIZE (FIFO_READ_SIZE + BUF_MARGIN)
|
|
|
|
#define ACC_FS_RANGE (16 >> ICM40600_ACC_FS)
|
|
#define GYR_FS_RANGE (2000 >> ICM40600_GYR_FS)
|
|
#define GRAVITY_NORM 9.80665f
|
|
#define kScale_acc (GRAVITY_NORM * ACC_FS_RANGE / 32768.0f)
|
|
#define kScale_gyr (NANO_PI / 180.0f * GYR_FS_RANGE / 32768.0f)
|
|
|
|
#define RATE_TO_HZ SENSOR_HZ(1.0f)
|
|
#define DECIMATION_HIGH_RATE SENSOR_HZ(1000.0f)
|
|
#define DECIMATION_LOW_RATE SENSOR_HZ(25.0f)
|
|
#define NO_DECIMATION_MAX_RATE SENSOR_HZ(200.0f) // will set ODR to DECIMATION_HIGH_RATE if more than this rate
|
|
#define NO_DECIMATION_MIN_RATE SENSOR_HZ(25.0f) // will set ODR to DECIMATION_LOW_RATE if less than this rate
|
|
#define MAX_BATCH_SIZE (((FIFO_MIN_PACKET_NB * 90) / 100) * FIFO_PACKET_SIZE) // 90% of FIFO size
|
|
|
|
/* skip first some samples */
|
|
#define ACC_SKIP_SAMPLE_NB 0
|
|
#define GYR_SKIP_SAMPLE_NB 3
|
|
|
|
#define CHIP_TIME_RES_US 1
|
|
#define CHIP_TIME_OFFSET_US 64000000ULL // 64sec
|
|
#define MIN_INCREMENT_TIME_NS 1000000ULL // 1ms for 1000Hz
|
|
|
|
enum SensorIndex {
|
|
FIRST_CONT_SENSOR = 0,
|
|
ACC = FIRST_CONT_SENSOR,
|
|
GYR,
|
|
NUM_CONT_SENSOR,
|
|
FIRST_ONESHOT_SENSOR = NUM_CONT_SENSOR,
|
|
WOM = FIRST_ONESHOT_SENSOR,
|
|
NOMO,
|
|
NUM_OF_SENSOR,
|
|
};
|
|
|
|
enum SensorEvents {
|
|
NO_EVT = -1,
|
|
EVT_SPI_DONE = EVT_APP_START + 1,
|
|
EVT_SENSOR_INTERRUPT_1,
|
|
};
|
|
|
|
enum IntState {
|
|
INT_READ_STATUS,
|
|
INT_PROCESS_STATUS,
|
|
INT_READ_FIFO_DATA,
|
|
INT_DONE,
|
|
};
|
|
|
|
enum InitState {
|
|
RESET_ICM40600,
|
|
INIT_ICM40600,
|
|
INIT_DONE,
|
|
};
|
|
|
|
enum CalibrationState {
|
|
CALIBRATION_START,
|
|
CALIBRATION_READ_STATUS,
|
|
CALIBRATION_READ_DATA,
|
|
CALIBRATION_SET_OFFSET,
|
|
CALIBRATION_DONE
|
|
};
|
|
|
|
enum SelfTestState {
|
|
TEST_START,
|
|
TEST_READ_STATUS,
|
|
TEST_READ_DATA,
|
|
TEST_READ_OTP,
|
|
TEST_REPORT,
|
|
TEST_DONE
|
|
};
|
|
|
|
enum SensorState {
|
|
// keep this in sync with getStateName
|
|
SENSOR_BOOT,
|
|
SENSOR_VERIFY_ID,
|
|
SENSOR_INITIALIZING,
|
|
SENSOR_IDLE,
|
|
SENSOR_POWERING_UP,
|
|
SENSOR_POWERING_DOWN,
|
|
SENSOR_CONFIG_CHANGING,
|
|
SENSOR_INT_1_HANDLING,
|
|
SENSOR_CALIBRATING,
|
|
SENSOR_TESTING,
|
|
SENSOR_SAVE_CALIBRATION,
|
|
SENSOR_NUM_OF_STATE
|
|
};
|
|
#if DBG_STATE
|
|
#define PRI_STATE "s"
|
|
static const char * getStateName(int32_t s) {
|
|
// keep this in sync with SensorState
|
|
static const char* const l[] = {"BOOT", "VERIFY_ID", "INIT", "IDLE", "PWR_UP",
|
|
"PWR_DN", "CFG_CHANGE", "INT1", "CALIB", "TEST", "SAVE_CALIB"};
|
|
if (s >= 0 && s < SENSOR_NUM_OF_STATE) {
|
|
return l[s];
|
|
}
|
|
return "???";
|
|
#else
|
|
#define PRI_STATE PRIi32
|
|
static int32_t getStateName(int32_t s) {
|
|
return s;
|
|
#endif
|
|
}
|
|
|
|
#if DBG_TIMESTAMP
|
|
struct StatisticsSet {
|
|
uint32_t sync_reset_count;
|
|
uint32_t sync_count;
|
|
uint32_t sync_adjust_plus;
|
|
uint32_t sync_adjust_minus;
|
|
uint32_t sync_truncate;
|
|
};
|
|
#endif
|
|
|
|
struct ConfigStat {
|
|
uint64_t latency;
|
|
uint32_t rate;
|
|
bool enable;
|
|
};
|
|
|
|
struct CalibrationData {
|
|
struct HostHubRawPacket header;
|
|
struct SensorAppEventHeader data_header;
|
|
int32_t xBias;
|
|
int32_t yBias;
|
|
int32_t zBias;
|
|
} __attribute__((packed));
|
|
|
|
struct TestResultData {
|
|
struct HostHubRawPacket header;
|
|
struct SensorAppEventHeader data_header;
|
|
} __attribute__((packed));
|
|
|
|
struct ICM40600Sensor {
|
|
struct ConfigStat pConfig; // pending config status request
|
|
struct TripleAxisDataEvent *data_evt;
|
|
uint32_t handle;
|
|
uint32_t rate;
|
|
uint64_t latency;
|
|
uint64_t prev_rtc_time;
|
|
int16_t offset[3];
|
|
int16_t data[3];
|
|
bool updated;
|
|
bool powered; // activate status
|
|
bool configed; // configure status
|
|
bool wait_for_odr;
|
|
enum SensorIndex idx;
|
|
uint8_t flush;
|
|
uint8_t decimator;
|
|
uint8_t data_cnt;
|
|
uint8_t skip_sample_cnt;
|
|
};
|
|
|
|
struct FifoPacketData {
|
|
int16_t accel[3];
|
|
int16_t gyro[3];
|
|
uint16_t timestamp;
|
|
bool odr_accel;
|
|
bool odr_gyro;
|
|
bool valid_accel;
|
|
bool valid_gyro;
|
|
int8_t temp;
|
|
};
|
|
|
|
struct ICM40600FactoryCal {
|
|
int32_t data[3];
|
|
int32_t data_count;
|
|
};
|
|
|
|
struct ICM40600SelfTest {
|
|
int32_t data[3];
|
|
int32_t data_st_on[3];
|
|
int32_t data_st_off[3];
|
|
int32_t data_count;
|
|
bool st_mode;
|
|
uint8_t otp_st_data_gyro[3];
|
|
uint8_t otp_st_data_accel[3];
|
|
};
|
|
|
|
struct ICM40600Config {
|
|
uint32_t accel_rate;
|
|
uint32_t gyro_rate;
|
|
uint32_t wom_threshold;
|
|
uint32_t fifo_rate;
|
|
uint16_t fifo_watermark;
|
|
uint8_t fifo_sample_size;
|
|
bool accel_on;
|
|
bool gyro_on;
|
|
bool wom_on;
|
|
};
|
|
|
|
struct ICM40600Task {
|
|
uint32_t tid;
|
|
struct ICM40600Sensor sensors[NUM_OF_SENSOR];
|
|
|
|
// spi and interrupt
|
|
spi_cs_t cs;
|
|
struct SpiMode mode;
|
|
struct SpiPacket packets[SPI_PACKET_SIZE];
|
|
struct SpiDevice *spiDev;
|
|
struct Gpio *Int1;
|
|
IRQn_Type Irq1;
|
|
struct ChainedIsr Isr1;
|
|
bool Int1_EN;
|
|
|
|
// spi buffers
|
|
uint8_t *dataBuffer[3];
|
|
uint8_t txrxBuffer[SPI_BUF_SIZE];
|
|
|
|
// states
|
|
volatile uint8_t state; //task state, type enum SensorState, do NOT change this directly
|
|
enum InitState init_state;
|
|
enum IntState int_state;
|
|
enum CalibrationState calibration_state;
|
|
enum SelfTestState selftest_state;
|
|
|
|
// pending config
|
|
bool pending_int[1];
|
|
bool pending_flush;
|
|
bool pending_config[NUM_OF_SENSOR];
|
|
bool pending_calibration_save;
|
|
|
|
struct ICM40600Config config;
|
|
uint32_t noMotionTimerHandle;
|
|
uint16_t fifo_count;
|
|
bool powered;
|
|
bool flush;
|
|
|
|
// calibration
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
struct AccelCal accel_cal;
|
|
#endif
|
|
#ifdef GYRO_CAL_ENABLED
|
|
struct GyroCal gyro_cal;
|
|
#endif
|
|
|
|
// timestamping
|
|
time_sync_t gSensorTime2RTC;
|
|
uint64_t data_timestamp;
|
|
uint64_t chip_time_us;
|
|
uint64_t last_sync_time;
|
|
uint64_t last_sync_data_ts;
|
|
uint16_t chip_timestamp;
|
|
bool fifo_start_sync;
|
|
|
|
// temperature data from chip in degC
|
|
float chip_temperature;
|
|
|
|
// spi rw
|
|
struct SlabAllocator *mDataSlab;
|
|
uint16_t mWbufCnt;
|
|
uint8_t mRegCnt;
|
|
uint8_t mRetryLeft;
|
|
bool spiInUse;
|
|
|
|
struct ICM40600FactoryCal factory_cal;
|
|
|
|
struct ICM40600SelfTest self_test;
|
|
|
|
#if DBG_TIMESTAMP
|
|
struct StatisticsSet statistics_set;
|
|
#endif
|
|
};
|
|
|
|
static uint32_t AccRates[] = {
|
|
SENSOR_HZ(25.0f/8.0f),
|
|
SENSOR_HZ(25.0f/4.0f),
|
|
SENSOR_HZ(25.0f/2.0f),
|
|
SENSOR_HZ(25.0f),
|
|
SENSOR_HZ(50.0f),
|
|
SENSOR_HZ(100.0f),
|
|
SENSOR_HZ(200.0f),
|
|
SENSOR_HZ(500.0f),
|
|
0,
|
|
};
|
|
|
|
static uint32_t GyrRates[] = {
|
|
SENSOR_HZ(25.0f/8.0f),
|
|
SENSOR_HZ(25.0f/4.0f),
|
|
SENSOR_HZ(25.0f/2.0f),
|
|
SENSOR_HZ(25.0f),
|
|
SENSOR_HZ(50.0f),
|
|
SENSOR_HZ(100.0f),
|
|
SENSOR_HZ(200.0f),
|
|
SENSOR_HZ(500.0f),
|
|
0,
|
|
};
|
|
|
|
static struct ICM40600Task mTask;
|
|
|
|
#define DEC_INFO(name, type, axis, inter, samples) \
|
|
.sensorName = name, \
|
|
.sensorType = type, \
|
|
.numAxis = axis, \
|
|
.interrupt = inter, \
|
|
.minSamples = samples
|
|
|
|
#define DEC_INFO_RATE(name, rates, type, axis, inter, samples) \
|
|
DEC_INFO(name, type, axis, inter, samples), \
|
|
.supportedRates = rates
|
|
|
|
#define DEC_INFO_RATE_RAW(name, rates, type, axis, inter, samples, raw, scale) \
|
|
DEC_INFO(name, type, axis, inter, samples), \
|
|
.supportedRates = rates, \
|
|
.flags1 = SENSOR_INFO_FLAGS1_RAW, \
|
|
.rawType = raw, \
|
|
.rawScale = scale
|
|
|
|
#define DEC_INFO_RATE_BIAS(name, rates, type, axis, inter, samples, bias) \
|
|
DEC_INFO(name, type, axis, inter, samples), \
|
|
.supportedRates = rates, \
|
|
.flags1 = SENSOR_INFO_FLAGS1_BIAS, \
|
|
.biasType = bias
|
|
|
|
#define DEC_INFO_RATE_RAW_BIAS(name, rates, type, axis, inter, samples, raw, scale, bias) \
|
|
DEC_INFO_RATE_RAW(name, rates, type, axis, inter, samples, raw, scale), \
|
|
.flags1 = SENSOR_INFO_FLAGS1_RAW | SENSOR_INFO_FLAGS1_BIAS, \
|
|
.biasType = bias
|
|
|
|
typedef struct ICM40600Task _Task;
|
|
#define TASK _Task* const _task
|
|
|
|
// To get rid of static variables all task functions should have a task structure pointer input.
|
|
// This is an intermediate step.
|
|
#define TDECL() TASK = &mTask; (void)_task
|
|
|
|
// Access task variables without explicitly specify the task structure pointer.
|
|
#define T(v) (_task->v)
|
|
|
|
// Atomic get state
|
|
#define GET_STATE() (atomicReadByte(&(_task->state)))
|
|
|
|
// Atomic set state, this set the state to arbitrary value, use with caution
|
|
#define SET_STATE(s) do{\
|
|
DEBUG_PRINT_IF(DBG_STATE, "set state %" PRI_STATE "\n", getStateName(s));\
|
|
atomicWriteByte(&(_task->state), (s));\
|
|
}while(0)
|
|
|
|
// Atomic switch state from IDLE to desired state.
|
|
static bool trySwitchState_(TASK, enum SensorState newState) {
|
|
#if DBG_STATE
|
|
bool ret = atomicCmpXchgByte(&T(state), SENSOR_IDLE, newState);
|
|
uint8_t prevState = ret ? SENSOR_IDLE : GET_STATE();
|
|
DEBUG_PRINT("switch state %" PRI_STATE "->%" PRI_STATE ", %s\n",
|
|
getStateName(prevState), getStateName(newState), ret ? "ok" : "failed");
|
|
return ret;
|
|
#else
|
|
return atomicCmpXchgByte(&T(state), SENSOR_IDLE, newState);
|
|
#endif
|
|
}
|
|
// Short-hand
|
|
#define trySwitchState(s) trySwitchState_(_task, (s))
|
|
|
|
static const struct SensorInfo mSensorInfo[NUM_OF_SENSOR] =
|
|
{
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
{ DEC_INFO_RATE_RAW_BIAS("Accelerometer", AccRates, SENS_TYPE_ACCEL, NUM_AXIS_THREE,
|
|
NANOHUB_INT_NONWAKEUP, 3000, SENS_TYPE_ACCEL_RAW, 1.0 / kScale_acc, SENS_TYPE_ACCEL_BIAS) },
|
|
#else
|
|
{ DEC_INFO_RATE_RAW("Accelerometer", AccRates, SENS_TYPE_ACCEL, NUM_AXIS_THREE,
|
|
NANOHUB_INT_NONWAKEUP, 3000, SENS_TYPE_ACCEL_RAW, 1.0 / kScale_acc) },
|
|
#endif
|
|
#ifdef GYRO_CAL_ENABLED
|
|
{ DEC_INFO_RATE_BIAS("Gyroscope", GyrRates, SENS_TYPE_GYRO, NUM_AXIS_THREE,
|
|
NANOHUB_INT_NONWAKEUP, 20, SENS_TYPE_GYRO_BIAS) },
|
|
#else
|
|
{ DEC_INFO_RATE("Gyroscope", GyrRates, SENS_TYPE_GYRO, NUM_AXIS_THREE,
|
|
NANOHUB_INT_NONWAKEUP, 20) },
|
|
#endif
|
|
{ DEC_INFO("Wake-on-Motion", SENS_TYPE_ANY_MOTION, NUM_AXIS_EMBEDDED, NANOHUB_INT_NONWAKEUP, 20) },
|
|
{ DEC_INFO("No-Motion", SENS_TYPE_NO_MOTION, NUM_AXIS_EMBEDDED, NANOHUB_INT_NONWAKEUP, 20) },
|
|
};
|
|
|
|
static void time_init(TASK)
|
|
{
|
|
time_sync_init(&T(gSensorTime2RTC));
|
|
}
|
|
|
|
static bool sensortime_to_rtc_time(TASK, uint64_t sensor_time, uint64_t *rtc_time_ns)
|
|
{
|
|
return time_sync_estimate_time1(&T(gSensorTime2RTC), sensor_time, rtc_time_ns);
|
|
}
|
|
|
|
static void map_sensortime_to_rtc_time(TASK, uint64_t sensor_time, uint64_t rtc_time_ns)
|
|
{
|
|
#if DBG_TIMESTAMP
|
|
T(statistics_set).sync_count++;
|
|
#endif
|
|
time_sync_add(&T(gSensorTime2RTC), rtc_time_ns, sensor_time);
|
|
}
|
|
|
|
static void invalidate_sensortime_to_rtc_time(TASK)
|
|
{
|
|
#if DBG_TIMESTAMP
|
|
T(statistics_set).sync_reset_count++;
|
|
#endif
|
|
time_sync_reset(&T(gSensorTime2RTC));
|
|
}
|
|
|
|
static void dataEvtFree(void *ptr)
|
|
{
|
|
TDECL();
|
|
struct TripleAxisDataEvent *ev = (struct TripleAxisDataEvent *)ptr;
|
|
|
|
slabAllocatorFree(T(mDataSlab), ev);
|
|
}
|
|
|
|
static void spiQueueWrite(uint8_t addr, uint8_t data, uint32_t delay)
|
|
{
|
|
TDECL();
|
|
|
|
if (T(spiInUse)) {
|
|
ERROR_PRINT("SPI in use, cannot queue write\n");
|
|
return;
|
|
}
|
|
T(packets[T(mRegCnt)]).size = 2;
|
|
T(packets[T(mRegCnt)]).txBuf = &T(txrxBuffer[T(mWbufCnt)]);
|
|
T(packets[T(mRegCnt)]).rxBuf = &T(txrxBuffer[T(mWbufCnt)]);
|
|
T(packets[T(mRegCnt)]).delay = delay * 1000;
|
|
T(txrxBuffer[T(mWbufCnt++)]) = ICM40600_SPI_WRITE | addr;
|
|
T(txrxBuffer[T(mWbufCnt++)]) = data;
|
|
T(mRegCnt)++;
|
|
}
|
|
|
|
/*
|
|
* need to be sure size of buf is larger than read size
|
|
*/
|
|
static void spiQueueRead(uint8_t addr, size_t size, uint8_t **buf, uint32_t delay)
|
|
{
|
|
TDECL();
|
|
|
|
if (T(spiInUse)) {
|
|
ERROR_PRINT("SPI in use, cannot queue read %d %d\n", (int)addr, (int)size);
|
|
return;
|
|
}
|
|
|
|
*buf = &T(txrxBuffer[T(mWbufCnt)]);
|
|
T(packets[T(mRegCnt)]).size = size + 1; // first byte will not contain valid data
|
|
T(packets[T(mRegCnt)]).txBuf = &T(txrxBuffer[T(mWbufCnt)]);
|
|
T(packets[T(mRegCnt)]).rxBuf = *buf;
|
|
T(packets[T(mRegCnt)]).delay = delay * 1000;
|
|
T(txrxBuffer[T(mWbufCnt)++]) = ICM40600_SPI_READ | addr;
|
|
T(mWbufCnt) += size;
|
|
T(mRegCnt)++;
|
|
}
|
|
|
|
static void spiBatchTxRx(struct SpiMode *mode,
|
|
SpiCbkF callback, void *cookie, const char * src)
|
|
{
|
|
TDECL();
|
|
|
|
if (T(mRegCnt) == 0) {
|
|
callback(cookie, 0);
|
|
return;
|
|
}
|
|
|
|
if (T(mWbufCnt) > SPI_BUF_SIZE) {
|
|
ERROR_PRINT("NO enough SPI buffer space, dropping transaction.\n");
|
|
return;
|
|
}
|
|
if (T(mRegCnt) > SPI_PACKET_SIZE) {
|
|
ERROR_PRINT("spiBatchTxRx too many packets!\n");
|
|
return;
|
|
}
|
|
|
|
T(spiInUse) = true;
|
|
T(mWbufCnt) = 0;
|
|
|
|
// Reset variables before issuing SPI transaction.
|
|
// SPI may finish before spiMasterRxTx finish
|
|
uint8_t regCount = T(mRegCnt);
|
|
T(mRegCnt) = 0;
|
|
|
|
if (spiMasterRxTx(T(spiDev), T(cs), T(packets), regCount, mode, callback, cookie) < 0) {
|
|
ERROR_PRINT("spiMasterRxTx failed!\n");
|
|
}
|
|
}
|
|
|
|
static bool icm40600Isr1(struct ChainedIsr *isr)
|
|
{
|
|
TASK = container_of(isr, struct ICM40600Task, Isr1);
|
|
|
|
if (!extiIsPendingGpio(T(Int1))) {
|
|
return false;
|
|
}
|
|
|
|
/* better to use atomic read for the pending flag but not serious even not atomic */
|
|
if (!T(pending_int[0])) {
|
|
osEnqueuePrivateEvt(EVT_SENSOR_INTERRUPT_1, _task, NULL, T(tid));
|
|
}
|
|
extiClearPendingGpio(T(Int1));
|
|
return true;
|
|
}
|
|
|
|
static void sensorSpiCallback(void *cookie, int err)
|
|
{
|
|
TDECL();
|
|
|
|
T(spiInUse) = false;
|
|
|
|
if (!osEnqueuePrivateEvt(EVT_SPI_DONE, cookie, NULL, T(tid)))
|
|
ERROR_PRINT("sensorSpiCallback: osEnqueuePrivateEvt() failed\n");
|
|
}
|
|
|
|
static void noMotionCallback(uint32_t timerId, void *data)
|
|
{
|
|
const struct ICM40600Sensor *sensor = (const struct ICM40600Sensor *)data;
|
|
|
|
if (!sensor->configed)
|
|
return;
|
|
|
|
osEnqueueEvt(EVT_SENSOR_NO_MOTION, NULL, NULL);
|
|
}
|
|
|
|
static void setOffsetReg(TASK)
|
|
{
|
|
uint8_t val;
|
|
const int16_t * const acc_offset = T(sensors[ACC]).offset;
|
|
const int16_t * const gyr_offset = T(sensors[GYR]).offset;
|
|
|
|
DEBUG_PRINT("Set ACC hw offset %d %d %d\n",
|
|
-acc_offset[0], -acc_offset[1], -acc_offset[2]);
|
|
DEBUG_PRINT("Set GYR hw offset %d %d %d\n",
|
|
-gyr_offset[0], -gyr_offset[1], -gyr_offset[2]);
|
|
|
|
// GX_L
|
|
val = (-gyr_offset[0]) & 0xff;
|
|
SPI_WRITE(REG_GOS_USER0, val);
|
|
// GY_H / GX_H
|
|
val = (-gyr_offset[1] >> 4) & 0xf0;
|
|
val |= ((-gyr_offset[0] >> 8) & 0x0f);
|
|
SPI_WRITE(REG_GOS_USER1, val);
|
|
// GY_L
|
|
val = (-gyr_offset[1]) & 0xff;
|
|
SPI_WRITE(REG_GOS_USER2, val);
|
|
// GZ_L
|
|
val = (-gyr_offset[2]) & 0xff;
|
|
SPI_WRITE(REG_GOS_USER3, val);
|
|
// AX_H / GZ_H
|
|
val = (-acc_offset[0] >> 4) & 0xf0;
|
|
val |= ((-gyr_offset[2] >> 8) & 0x0f);
|
|
SPI_WRITE(REG_GOS_USER4, val);
|
|
// AX_H / GZ_H
|
|
val = (-acc_offset[0] >> 4) & 0xf0;
|
|
val |= ((-gyr_offset[2] >> 8) & 0x0f);
|
|
SPI_WRITE(REG_GOS_USER4, val);
|
|
// AX_L
|
|
val = (-acc_offset[0]) & 0xff;
|
|
SPI_WRITE(REG_GOS_USER5, val);
|
|
// AY_L
|
|
val = (-acc_offset[1]) & 0xff;
|
|
SPI_WRITE(REG_GOS_USER6, val);
|
|
// AZ_H / AY_H
|
|
val = (-acc_offset[2] >> 4) & 0xf0;
|
|
val |= ((-acc_offset[1] >> 8) & 0x0f);
|
|
SPI_WRITE(REG_GOS_USER7, val);
|
|
// AZ_L
|
|
val = (-acc_offset[2]) & 0xff;
|
|
SPI_WRITE(REG_GOS_USER8, val);
|
|
}
|
|
|
|
static void resetOffsetReg(TASK, enum SensorIndex idx)
|
|
{
|
|
uint8_t val;
|
|
|
|
DEBUG_PRINT("Reset offset registers sensor=%d\n", idx);
|
|
|
|
if (idx == ACC) {
|
|
val = (-T(sensors[GYR]).offset[2] >> 8) & 0x0f;
|
|
SPI_WRITE(REG_GOS_USER4, val); // AX_H / GZ_H
|
|
SPI_WRITE(REG_GOS_USER5, 0x00); // AX_L
|
|
SPI_WRITE(REG_GOS_USER6, 0x00); // AY_L
|
|
SPI_WRITE(REG_GOS_USER7, 0x00); // AZ_H / AY_H
|
|
SPI_WRITE(REG_GOS_USER8, 0x00); // AZ_L
|
|
} else if (idx == GYR) {
|
|
SPI_WRITE(REG_GOS_USER0, 0x00); // GX_L
|
|
SPI_WRITE(REG_GOS_USER1, 0x00); // GY_H / GX_H
|
|
SPI_WRITE(REG_GOS_USER2, 0x00); // GY_L
|
|
SPI_WRITE(REG_GOS_USER3, 0x00); // GZ_L
|
|
val = (-T(sensors[ACC]).offset[0] >> 4) & 0xf0;
|
|
SPI_WRITE(REG_GOS_USER4, val); // AX_H / GZ_H
|
|
}
|
|
}
|
|
|
|
static bool saveCalibration(TASK)
|
|
{
|
|
if (trySwitchState(SENSOR_SAVE_CALIBRATION)) {
|
|
setOffsetReg(_task);
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, _task, __FUNCTION__);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool accCfgData(void *data, void *cookie)
|
|
{
|
|
TDECL();
|
|
struct ICM40600Sensor *sensor = &T(sensors[ACC]);
|
|
struct CfgData {
|
|
int32_t hw[3]; // chip frame (hw unit @FSR=factory calibration)
|
|
float sw[3]; // body frame
|
|
};
|
|
struct CfgData *values = data;
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
// offset register is 12bit
|
|
if (values->hw[i] > 2047) {
|
|
sensor->offset[i] = 2047;
|
|
} else if (values->hw[i] < -2048) {
|
|
sensor->offset[i] = -2048;
|
|
} else {
|
|
sensor->offset[i] = values->hw[i];
|
|
}
|
|
}
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
accelCalBiasSet(&T(accel_cal), values->sw[0], values->sw[1], values->sw[2]);
|
|
#endif
|
|
if (!saveCalibration(_task)) {
|
|
T(pending_calibration_save) = true;
|
|
}
|
|
INFO_PRINT("accCfgData hw bias: data=%d, %d, %d\n",
|
|
sensor->offset[0], sensor->offset[1], sensor->offset[2]);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool gyrCfgData(void *data, void *cookie)
|
|
{
|
|
TDECL();
|
|
struct ICM40600Sensor *sensor = &T(sensors[GYR]);
|
|
const struct AppToSensorHalDataPayload *p = data;
|
|
int i;
|
|
|
|
if (p->type == HALINTF_TYPE_GYRO_CAL_BIAS && p->size == sizeof(struct GyroCalBias)) {
|
|
const struct GyroCalBias *bias = p->gyroCalBias;
|
|
for (i = 0; i < 3; i++) {
|
|
// offset register is 12bit
|
|
if (bias->hardwareBias[i] > 2047) {
|
|
sensor->offset[i] = 2047;
|
|
} else if (bias->hardwareBias[i] < -2048) {
|
|
sensor->offset[i] = -2048;
|
|
} else {
|
|
sensor->offset[i] = bias->hardwareBias[i];
|
|
}
|
|
}
|
|
#ifdef GYRO_CAL_ENABLED
|
|
gyroCalSetBias(&T(gyro_cal), bias->softwareBias[0], bias->softwareBias[1],
|
|
bias->softwareBias[2], sensorGetTime());
|
|
#endif
|
|
if (!saveCalibration(_task)) {
|
|
T(pending_calibration_save) = true;
|
|
}
|
|
INFO_PRINT("gyrCfgData hw bias: data=%d, %d, %d\n",
|
|
sensor->offset[0], sensor->offset[1], sensor->offset[2]);
|
|
} else if (p->type == HALINTF_TYPE_GYRO_OTC_DATA && p->size == sizeof(struct GyroOtcData)) {
|
|
// Over-temperature gyro calibration not supported
|
|
} else {
|
|
ERROR_PRINT("Unknown gyro config data type 0x%04x, size %d\n", p->type, p->size);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool validateFifoData(const int16_t data[3])
|
|
{
|
|
bool ret = true;
|
|
|
|
if ((data[0] == -32768) && (data[1] == -32768) && (data[2] == -32768))
|
|
ret = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void getFifoData(const uint8_t *data, int idx, struct FifoPacketData *out)
|
|
{
|
|
/* invalidate all flags */
|
|
out->valid_accel = false;
|
|
out->valid_gyro = false;
|
|
|
|
/* Header : 1 byte
|
|
* Accel : 6 byte
|
|
* Gyro : 6 byte
|
|
* Temp : 1 byte
|
|
* Timestamp : 2 byte */
|
|
if (data[idx] & BIT_FIFO_HEAD_ACCEL) {
|
|
out->accel[0] = (data[idx + 2] << 8) | data[idx + 1];
|
|
out->accel[1] = (data[idx + 4] << 8) | data[idx + 3];
|
|
out->accel[2] = (data[idx + 6] << 8) | data[idx + 5];
|
|
out->valid_accel = validateFifoData(out->accel);
|
|
}
|
|
out->odr_accel = (data[idx] & BIT_FIFO_HEAD_ODR_ACCEL) ? true : false;
|
|
|
|
if (data[idx] & BIT_FIFO_HEAD_GYRO) {
|
|
out->gyro[0] = (data[idx + 8] << 8) | data[idx + 7];
|
|
out->gyro[1] = (data[idx + 10] << 8) | data[idx + 9];
|
|
out->gyro[2] = (data[idx + 12] << 8) | data[idx + 11];
|
|
out->valid_gyro = validateFifoData(out->gyro);
|
|
}
|
|
out->odr_gyro = (data[idx] & BIT_FIFO_HEAD_ODR_GYRO) ? true : false;
|
|
|
|
out->temp = (int8_t)data[idx + 13];
|
|
out->timestamp = (data[idx + 15] << 8) | data[idx + 14];
|
|
}
|
|
|
|
static bool calSelftestGetOneData(enum SensorIndex sensor_idx, const uint8_t *data, int idx, int16_t raw_data[3])
|
|
{
|
|
struct FifoPacketData fifo_data;
|
|
bool ret = false;
|
|
|
|
getFifoData(data, idx, &fifo_data);
|
|
|
|
switch (sensor_idx) {
|
|
case ACC:
|
|
if (fifo_data.valid_accel) {
|
|
raw_data[0] = fifo_data.accel[0];
|
|
raw_data[1] = fifo_data.accel[1];
|
|
raw_data[2] = fifo_data.accel[2];
|
|
ret = true;
|
|
}
|
|
break;
|
|
case GYR:
|
|
if (fifo_data.valid_gyro) {
|
|
raw_data[0] = fifo_data.gyro[0];
|
|
raw_data[1] = fifo_data.gyro[1];
|
|
raw_data[2] = fifo_data.gyro[2];
|
|
ret = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void calSelftestFifoEnable(TASK, enum SensorIndex idx, bool en, uint32_t delay)
|
|
{
|
|
uint8_t val;
|
|
|
|
if (en) {
|
|
// enable FIFO output
|
|
T(config).fifo_sample_size = FIFO_PACKET_SIZE;
|
|
val = BIT_FIFO_ACCEL_EN | BIT_FIFO_GYRO_EN |
|
|
BIT_FIFO_TEMP_EN | BIT_FIFO_TMST_FSYNC_EN;
|
|
SPI_WRITE(REG_FIFO_CONFIG, BIT_FIFO_MODE_STREAM);
|
|
SPI_WRITE(REG_FIFO_CONFIG1, val, delay); // with wait
|
|
} else {
|
|
// disable FIFO output
|
|
T(config).fifo_sample_size = 0;
|
|
SPI_WRITE(REG_FIFO_CONFIG, BIT_FIFO_MODE_BYPASS);
|
|
SPI_WRITE(REG_FIFO_CONFIG1, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Factory calibration
|
|
*/
|
|
static void sendCalibrationResult(uint8_t status, uint8_t sensorType,
|
|
int32_t xBias, int32_t yBias, int32_t zBias)
|
|
{
|
|
struct CalibrationData *data = heapAlloc(sizeof(struct CalibrationData));
|
|
if (!data) {
|
|
ERROR_PRINT("Couldn't alloc cal result pkt");
|
|
return;
|
|
}
|
|
|
|
data->header.appId = ICM40600_APP_ID;
|
|
data->header.dataLen = (sizeof(struct CalibrationData) - sizeof(struct HostHubRawPacket));
|
|
data->data_header.msgId = SENSOR_APP_MSG_ID_CAL_RESULT;
|
|
data->data_header.sensorType = sensorType;
|
|
data->data_header.status = status;
|
|
|
|
data->xBias = xBias;
|
|
data->yBias = yBias;
|
|
data->zBias = zBias;
|
|
|
|
if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree))
|
|
ERROR_PRINT("Couldn't send cal result evt");
|
|
}
|
|
|
|
static void calibrationInit(TASK, enum SensorIndex idx)
|
|
{
|
|
uint8_t val;
|
|
|
|
// Disable Interrupt
|
|
SPI_WRITE(REG_INT_SOURCE0, 0);
|
|
SPI_WRITE(REG_INT_SOURCE1, 0);
|
|
|
|
// reset offset registers
|
|
resetOffsetReg(_task, idx);
|
|
|
|
// stop FIFO
|
|
calSelftestFifoEnable(_task, idx, false, 0);
|
|
|
|
// set rate
|
|
SPI_WRITE(REG_GYRO_CONFIG0, (CALIBRATION_GYR_FS << SHIFT_GYRO_FS_SEL) |
|
|
(CALIBRATION_ODR << SHIFT_ODR_CONF));
|
|
SPI_WRITE(REG_ACCEL_CONFIG0, (CALIBRATION_ACC_FS << SHIFT_ACCEL_FS_SEL) |
|
|
(CALIBRATION_ODR << SHIFT_ODR_CONF));
|
|
|
|
// set filter
|
|
SPI_WRITE(REG_GYRO_ACCEL_CONFIG0, CALIBRATION_ACC_BW_IND | CALIBRATION_GYR_BW_IND);
|
|
|
|
// turn on sensors
|
|
switch (idx) {
|
|
case ACC:
|
|
val = BIT_ACCEL_MODE_LN;
|
|
break;
|
|
case GYR:
|
|
val = BIT_GYRO_MODE_LN;
|
|
break;
|
|
default:
|
|
val = 0;
|
|
break;
|
|
}
|
|
SPI_WRITE(REG_PWR_MGMT_0, val, 200 * 1000);
|
|
|
|
calSelftestFifoEnable(_task, idx, true, CALIBRATION_READ_INTERVAL_US);
|
|
}
|
|
|
|
static void calibrationDeinit(TASK, enum SensorIndex idx)
|
|
{
|
|
calSelftestFifoEnable(_task, idx, false, 0);
|
|
|
|
SPI_WRITE(REG_GYRO_ACCEL_CONFIG0, ICM40600_ACC_BW_IND | ICM40600_GYR_BW_IND);
|
|
SPI_WRITE(REG_PWR_MGMT_0, 0, 200); // 9136
|
|
|
|
// make register accesses happen when sensor is enabled next time
|
|
T(config).gyro_rate = 0;
|
|
T(config).accel_rate = 0;
|
|
T(config).fifo_rate = 0;
|
|
T(config).fifo_watermark = 0;
|
|
T(config).accel_on = false;
|
|
T(config).gyro_on = false;
|
|
T(config).wom_on = false;
|
|
}
|
|
|
|
static void calibrationHandling(TASK, enum SensorIndex idx)
|
|
{
|
|
const uint8_t *buf, *data;
|
|
uint8_t int_status;
|
|
uint16_t fifo_count;
|
|
int i, t;
|
|
bool r;
|
|
int16_t raw_data[3] = { 0, 0, 0 };
|
|
|
|
if (idx != ACC && idx != GYR) {
|
|
ERROR_PRINT("Invalid sensor index\n");
|
|
return;
|
|
}
|
|
|
|
switch (T(calibration_state)) {
|
|
case CALIBRATION_START:
|
|
T(mRetryLeft) = RETRY_CNT_CALIBRATION;
|
|
calibrationInit(_task, idx);
|
|
for (i = 0; i < 3; i++) {
|
|
T(factory_cal).data[i] = 0;
|
|
}
|
|
SPI_READ(REG_INT_STATUS, 1, &T(dataBuffer[0]));
|
|
SPI_READ(REG_FIFO_BYTE_COUNT1, 2, &T(dataBuffer[1]));
|
|
T(calibration_state) = CALIBRATION_READ_STATUS;
|
|
T(factory_cal).data_count = CALIBRATION_SAMPLE_NB;
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
case CALIBRATION_READ_STATUS:
|
|
buf = T(dataBuffer[0]);
|
|
int_status = buf[1];
|
|
buf = T(dataBuffer[1]);
|
|
fifo_count = buf[2] << 8 | buf[1];
|
|
fifo_count = fifo_count - fifo_count % T(config).fifo_sample_size;
|
|
T(fifo_count) = fifo_count;
|
|
DEBUG_PRINT("fifo_count = %d\n", fifo_count);
|
|
if (int_status & BIT_INT_STATUS_FIFO_FULL) {
|
|
ERROR_PRINT("fifo overflow\n");
|
|
calibrationDeinit(_task, idx);
|
|
T(calibration_state) = CALIBRATION_DONE;
|
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_ERROR, mSensorInfo[idx].sensorType, 0, 0, 0);
|
|
} else {
|
|
T(calibration_state) = CALIBRATION_READ_DATA;
|
|
SPI_READ(REG_FIFO_DATA, fifo_count, &T(dataBuffer[0]));
|
|
}
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
case CALIBRATION_READ_DATA:
|
|
buf = T(dataBuffer[0]);
|
|
data = &buf[1];
|
|
for (i = 0; i < T(fifo_count); i += T(config).fifo_sample_size) {
|
|
r = calSelftestGetOneData(idx, data, i, raw_data);
|
|
if (r == false) {
|
|
ERROR_PRINT("invalid data packet\n");
|
|
calibrationDeinit(_task, idx);
|
|
T(calibration_state) = CALIBRATION_DONE;
|
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_ERROR, mSensorInfo[idx].sensorType, 0, 0, 0);
|
|
break;
|
|
}
|
|
for (t = 0; t < 3; t++) {
|
|
T(factory_cal).data[t] += raw_data[t];
|
|
}
|
|
T(factory_cal).data_count--;
|
|
if (T(factory_cal).data_count == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (T(factory_cal).data_count > 0) {
|
|
if (--T(mRetryLeft) == 0) {
|
|
ERROR_PRINT("calibration timeout\n");
|
|
calibrationDeinit(_task, idx);
|
|
T(calibration_state) = CALIBRATION_DONE;
|
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_ERROR, mSensorInfo[idx].sensorType, 0, 0, 0);
|
|
} else {
|
|
calSelftestFifoEnable(_task, idx, false, 0); // toggle FIFO to reset
|
|
calSelftestFifoEnable(_task, idx, true, CALIBRATION_READ_INTERVAL_US);
|
|
SPI_READ(REG_INT_STATUS, 1, &T(dataBuffer[0]));
|
|
SPI_READ(REG_FIFO_BYTE_COUNT1, 2, &T(dataBuffer[1]));
|
|
T(calibration_state) = CALIBRATION_READ_STATUS;
|
|
}
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
}
|
|
T(calibration_state) = CALIBRATION_SET_OFFSET;
|
|
// fall-through
|
|
case CALIBRATION_SET_OFFSET:
|
|
DEBUG_PRINT("calibration total: %ld, %ld, %ld, data count=%d\n",
|
|
T(factory_cal).data[0],
|
|
T(factory_cal).data[1],
|
|
T(factory_cal).data[2],
|
|
CALIBRATION_SAMPLE_NB);
|
|
for (i = 0; i < 3; i++) {
|
|
T(factory_cal).data[i] /= CALIBRATION_SAMPLE_NB;
|
|
}
|
|
DEBUG_PRINT("average: %ld, %ld, %ld\n",
|
|
T(factory_cal).data[0],
|
|
T(factory_cal).data[1],
|
|
T(factory_cal).data[2]);
|
|
if (idx == ACC) {
|
|
// assume the largest data axis shows +1 or -1 gee
|
|
t = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
if (abs(T(factory_cal).data[i]) > abs(T(factory_cal).data[t]))
|
|
t = i;
|
|
}
|
|
if (T(factory_cal).data[t] > 0) {
|
|
DEBUG_PRINT("assume axis %d is %d\n", t, CALIBRATION_ACC_1G);
|
|
T(factory_cal).data[t] -= CALIBRATION_ACC_1G;
|
|
} else {
|
|
DEBUG_PRINT("assume axis %d is %d\n", t, -CALIBRATION_ACC_1G);
|
|
T(factory_cal).data[t] += CALIBRATION_ACC_1G;
|
|
}
|
|
}
|
|
// set bias to offset registers
|
|
for (i = 0; i < 3; i++) {
|
|
T(sensors[idx]).offset[i] = T(factory_cal).data[i];
|
|
}
|
|
setOffsetReg(_task);
|
|
calibrationDeinit(_task, idx);
|
|
|
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_SUCCESS,
|
|
mSensorInfo[idx].sensorType,
|
|
T(factory_cal).data[0],
|
|
T(factory_cal).data[1],
|
|
T(factory_cal).data[2]);
|
|
INFO_PRINT("reported: %ld, %ld, %ld\n",
|
|
T(factory_cal).data[0],
|
|
T(factory_cal).data[1],
|
|
T(factory_cal).data[2]);
|
|
T(calibration_state) = CALIBRATION_DONE;
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool accCalibration(void *cookie)
|
|
{
|
|
TDECL();
|
|
|
|
INFO_PRINT("Accel Calibration\n");
|
|
|
|
if (!T(powered) && trySwitchState(SENSOR_CALIBRATING)) {
|
|
T(calibration_state) = CALIBRATION_START;
|
|
calibrationHandling(_task, ACC);
|
|
return true;
|
|
} else {
|
|
ERROR_PRINT("Cannot run calibration because sensor is busy\n");
|
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_BUSY, SENS_TYPE_ACCEL, 0, 0, 0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool gyrCalibration(void *cookie)
|
|
{
|
|
TDECL();
|
|
|
|
INFO_PRINT("Gyro Calibration\n");
|
|
|
|
if (!T(powered) && trySwitchState(SENSOR_CALIBRATING)) {
|
|
T(calibration_state) = CALIBRATION_START;
|
|
calibrationHandling(_task, GYR);
|
|
return true;
|
|
} else {
|
|
ERROR_PRINT("Cannot run calibration because sensor is busy\n");
|
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_BUSY, SENS_TYPE_GYRO, 0, 0, 0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Selftest
|
|
*/
|
|
static void sendTestResult(uint8_t status, uint8_t sensorType)
|
|
{
|
|
struct TestResultData *data = heapAlloc(sizeof(struct TestResultData));
|
|
if (!data) {
|
|
ERROR_PRINT("Couldn't alloc test result packet");
|
|
return;
|
|
}
|
|
|
|
data->header.appId = ICM40600_APP_ID;
|
|
data->header.dataLen = (sizeof(struct TestResultData) - sizeof(struct HostHubRawPacket));
|
|
data->data_header.msgId = SENSOR_APP_MSG_ID_TEST_RESULT;
|
|
data->data_header.sensorType = sensorType;
|
|
data->data_header.status = status;
|
|
|
|
if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree))
|
|
ERROR_PRINT("Couldn't send test result packet");
|
|
}
|
|
|
|
static const uint16_t SelfTestEquation[256] = {
|
|
2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
|
|
2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
|
|
3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
|
|
3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
|
|
3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
|
|
3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
|
|
4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
|
|
4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
|
|
4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
|
|
5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
|
|
5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
|
|
6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
|
|
6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
|
|
7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
|
|
7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
|
|
8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
|
|
9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
|
|
10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
|
|
10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
|
|
11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
|
|
12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
|
|
13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
|
|
15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
|
|
16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
|
|
17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
|
|
19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
|
|
20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
|
|
22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
|
|
24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
|
|
26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
|
|
28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
|
|
30903, 31212, 31524, 31839, 32157, 32479, 32804
|
|
};
|
|
|
|
static void selfTestInit(TASK, enum SensorIndex idx)
|
|
{
|
|
uint8_t val;
|
|
|
|
// Disable Interrupt
|
|
SPI_WRITE(REG_INT_SOURCE0, 0);
|
|
SPI_WRITE(REG_INT_SOURCE1, 0);
|
|
|
|
// reset offset registers
|
|
resetOffsetReg(_task, idx);
|
|
|
|
// stop FIFO
|
|
calSelftestFifoEnable(_task, idx, false, 0);
|
|
|
|
// self-test mode set
|
|
val = 0;
|
|
if (T(self_test).st_mode) {
|
|
if (idx == ACC) {
|
|
val |= (BIT_TEST_AX_EN | BIT_TEST_AY_EN | BIT_TEST_AZ_EN);
|
|
val |= BIT_SELF_TEST_REGULATOR_EN;
|
|
} else if (idx == GYR) {
|
|
val |= (BIT_TEST_GX_EN | BIT_TEST_GY_EN | BIT_TEST_GZ_EN);
|
|
}
|
|
}
|
|
SPI_WRITE(REG_SELF_TEST_CONFIG, val);
|
|
|
|
// set rate
|
|
SPI_WRITE(REG_GYRO_CONFIG0, (SELF_TEST_GYR_FS << SHIFT_GYRO_FS_SEL) |
|
|
(SELF_TEST_ODR << SHIFT_ODR_CONF));
|
|
SPI_WRITE(REG_ACCEL_CONFIG0, (SELF_TEST_ACC_FS << SHIFT_ACCEL_FS_SEL) |
|
|
(SELF_TEST_ODR << SHIFT_ODR_CONF));
|
|
|
|
// set filter
|
|
val = 0;
|
|
if (idx == ACC) {
|
|
val |= SELF_TEST_ACC_BW_IND;
|
|
} else if (idx == GYR) {
|
|
val |= SELF_TEST_GYR_BW_IND;
|
|
}
|
|
SPI_WRITE(REG_GYRO_ACCEL_CONFIG0, val);
|
|
|
|
// turn on sensors
|
|
val = 0;
|
|
if (idx == ACC) {
|
|
val |= BIT_ACCEL_MODE_LN;
|
|
} else if (idx == GYR) {
|
|
val |= BIT_GYRO_MODE_LN;
|
|
}
|
|
SPI_WRITE(REG_PWR_MGMT_0, val, 200 * 1000);
|
|
|
|
calSelftestFifoEnable(_task, idx, true, SELF_TEST_READ_INTERVAL_US);
|
|
}
|
|
|
|
static void selfTestDeinit(TASK, enum SensorIndex idx)
|
|
{
|
|
calSelftestFifoEnable(_task, idx, false, 0);
|
|
|
|
SPI_WRITE(REG_SELF_TEST_CONFIG, 0);
|
|
SPI_WRITE(REG_GYRO_ACCEL_CONFIG0, ICM40600_ACC_BW_IND | ICM40600_GYR_BW_IND);
|
|
SPI_WRITE(REG_PWR_MGMT_0, 0, 200); // 9136
|
|
|
|
// make register accesses happen when sensor is enabled next time
|
|
T(config).gyro_rate = 0;
|
|
T(config).accel_rate = 0;
|
|
T(config).fifo_rate = 0;
|
|
T(config).fifo_watermark = 0;
|
|
T(config).accel_on = false;
|
|
T(config).gyro_on = false;
|
|
T(config).wom_on = false;
|
|
}
|
|
|
|
static bool checkAccelSelftest(TASK)
|
|
{
|
|
int32_t st_res;
|
|
uint32_t st_otp[3];
|
|
int i;
|
|
int32_t ratio;
|
|
bool pass = true;
|
|
bool otp_value_zero = false;
|
|
|
|
/* calculate ST_OTP */
|
|
for (i = 0; i < 3; i++) {
|
|
if (T(self_test.otp_st_data_accel[i] != 0))
|
|
st_otp[i] = SelfTestEquation[T(self_test).otp_st_data_accel[i] - 1];
|
|
else
|
|
otp_value_zero = true;
|
|
}
|
|
|
|
if (!otp_value_zero) {
|
|
/* criteria a */
|
|
for (i = 0; i < 3; i++) {
|
|
st_res = T(self_test).data_st_on[i] - T(self_test).data_st_off[i];
|
|
ratio = abs(st_res / st_otp[i] - SELF_TEST_PRECISION);
|
|
if (ratio >= SELF_TEST_ACC_SHIFT_DELTA) {
|
|
INFO_PRINT("error accel[%d] : st_res = %ld, st_otp = %ld\n", i, st_res, st_otp[i]);
|
|
pass = false;
|
|
}
|
|
}
|
|
} else {
|
|
/* criteria b */
|
|
for (i = 0; i < 3; i++) {
|
|
st_res = abs(T(self_test).data_st_on[i] - T(self_test).data_st_off[i]);
|
|
if (st_res < SELF_TEST_MIN_ACC || st_res > SELF_TEST_MAX_ACC) {
|
|
INFO_PRINT("error accel[%d] : st_res = %ld, min = %d, max = %d\n", i, st_res, SELF_TEST_MIN_ACC, SELF_TEST_MAX_ACC);
|
|
pass = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pass;
|
|
}
|
|
|
|
static bool checkGyroSelftest(TASK)
|
|
{
|
|
int32_t st_res;
|
|
uint32_t st_otp[3];
|
|
int i;
|
|
bool pass = true;
|
|
bool otp_value_zero = false;
|
|
|
|
/* calculate ST_OTP */
|
|
for (i = 0; i < 3; i++) {
|
|
if (T(self_test).otp_st_data_gyro[i] != 0)
|
|
st_otp[i] = SelfTestEquation[T(self_test).otp_st_data_gyro[i] - 1];
|
|
else
|
|
otp_value_zero = true;
|
|
}
|
|
|
|
if (!otp_value_zero) {
|
|
/* criteria a */
|
|
for (i = 0; i < 3; i++) {
|
|
st_res = T(self_test).data_st_on[i] - T(self_test).data_st_off[i];
|
|
if (st_res <= st_otp[i] * SELF_TEST_GYR_SHIFT_DELTA) {
|
|
INFO_PRINT("error gyro[%d] : st_res = %ld, st_otp = %ld\n", i, st_res, st_otp[i]);
|
|
pass = false;
|
|
}
|
|
}
|
|
} else {
|
|
/* criteria b */
|
|
for (i = 0; i < 3; i++) {
|
|
st_res = abs(T(self_test).data_st_on[i] - T(self_test).data_st_off[i]);
|
|
if (st_res < SELF_TEST_MIN_GYR) {
|
|
INFO_PRINT("error gyro[%d] : st_res = %ld, min = %d\n", i, st_res, SELF_TEST_MIN_GYR);
|
|
pass = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pass) {
|
|
/* criteria c */
|
|
for (i = 0; i < 3; i++) {
|
|
if (abs(T(self_test).data_st_off[i]) > SELF_TEST_MAX_GYR_OFFSET) {
|
|
INFO_PRINT("error gyro[%d] = %d, max = %d\n", i, abs(T(self_test).data_st_off[i]), SELF_TEST_MAX_GYR_OFFSET);
|
|
pass = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pass;
|
|
}
|
|
|
|
static void selfTestHandling(TASK, enum SensorIndex idx)
|
|
{
|
|
const uint8_t *buf, *data;
|
|
uint8_t int_status;
|
|
uint16_t fifo_count;
|
|
int i, t;
|
|
bool r;
|
|
int16_t raw_data[3] = { 0, 0, 0 };
|
|
bool pass;
|
|
|
|
if (idx != ACC && idx != GYR) {
|
|
ERROR_PRINT("Invalid sensor index\n");
|
|
return;
|
|
}
|
|
|
|
switch (T(selftest_state)) {
|
|
case TEST_START:
|
|
T(mRetryLeft) = RETRY_CNT_SELF_TEST;
|
|
selfTestInit(_task, idx);
|
|
for (i = 0; i < 3; i++) {
|
|
T(self_test).data[i] = 0;
|
|
}
|
|
SPI_READ(REG_INT_STATUS, 1, &T(dataBuffer[0]));
|
|
SPI_READ(REG_FIFO_BYTE_COUNT1, 2, &T(dataBuffer[1]));
|
|
T(selftest_state) = TEST_READ_STATUS;
|
|
T(self_test).data_count = SELF_TEST_SAMPLE_NB;
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
case TEST_READ_STATUS:
|
|
buf = T(dataBuffer[0]);
|
|
int_status = buf[1];
|
|
buf = T(dataBuffer[1]);
|
|
fifo_count = buf[2] << 8 | buf[1];
|
|
fifo_count = fifo_count - fifo_count % T(config).fifo_sample_size;
|
|
T(fifo_count) = fifo_count;
|
|
DEBUG_PRINT("fifo_count = %d\n", fifo_count);
|
|
if (int_status & BIT_INT_STATUS_FIFO_FULL) {
|
|
ERROR_PRINT("fifo overflow\n");
|
|
selfTestDeinit(_task, idx);
|
|
T(selftest_state) = TEST_DONE;
|
|
sendTestResult(SENSOR_APP_EVT_STATUS_ERROR, mSensorInfo[idx].sensorType);
|
|
} else {
|
|
T(selftest_state) = TEST_READ_DATA;
|
|
SPI_READ(REG_FIFO_DATA, fifo_count, &T(dataBuffer[0]));
|
|
}
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
case TEST_READ_DATA:
|
|
buf = T(dataBuffer[0]);
|
|
data = &buf[1];
|
|
for (i = 0; i < T(fifo_count); i += T(config).fifo_sample_size) {
|
|
r = calSelftestGetOneData(idx, data, i, raw_data);
|
|
if (r == false) {
|
|
ERROR_PRINT("invalid data packet\n");
|
|
selfTestDeinit(_task, idx);
|
|
T(selftest_state) = TEST_DONE;
|
|
sendTestResult(SENSOR_APP_EVT_STATUS_ERROR, mSensorInfo[idx].sensorType);
|
|
break;
|
|
}
|
|
for (t = 0; t < 3; t++) {
|
|
T(self_test).data[t] += raw_data[t];
|
|
}
|
|
T(self_test).data_count--;
|
|
if (T(self_test).data_count == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (T(self_test).data_count > 0) {
|
|
if (--T(mRetryLeft) == 0) {
|
|
ERROR_PRINT("selftest timeout\n");
|
|
selfTestDeinit(_task, idx);
|
|
T(selftest_state) = TEST_DONE;
|
|
sendTestResult(SENSOR_APP_EVT_STATUS_ERROR, mSensorInfo[idx].sensorType);
|
|
} else {
|
|
calSelftestFifoEnable(_task, idx, false, 0); // toggle FIFO to reset
|
|
calSelftestFifoEnable(_task, idx, true, SELF_TEST_READ_INTERVAL_US);
|
|
SPI_READ(REG_INT_STATUS, 1, &T(dataBuffer[0]));
|
|
SPI_READ(REG_FIFO_BYTE_COUNT1, 2, &T(dataBuffer[1]));
|
|
T(selftest_state) = TEST_READ_STATUS;
|
|
}
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
}
|
|
T(selftest_state) = TEST_READ_OTP;
|
|
// fall-through
|
|
case TEST_READ_OTP:
|
|
for (i = 0; i < 3; i++) {
|
|
T(self_test).data[i] = T(self_test).data[i] / SELF_TEST_SAMPLE_NB * SELF_TEST_PRECISION;
|
|
}
|
|
INFO_PRINT("st_mode=%d average (scaled) : %ld, %ld, %ld\n",
|
|
T(self_test).st_mode,
|
|
T(self_test).data[0],
|
|
T(self_test).data[1],
|
|
T(self_test).data[2]);
|
|
|
|
selfTestDeinit(_task, idx);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (T(self_test).st_mode) {
|
|
T(self_test).data_st_on[i] = T(self_test).data[i];
|
|
} else {
|
|
T(self_test).data_st_off[i] = T(self_test).data[i];
|
|
}
|
|
}
|
|
|
|
// Run again with self-test mode on
|
|
if (!T(self_test).st_mode) {
|
|
T(self_test).st_mode = true;
|
|
T(selftest_state) = TEST_START;
|
|
} else {
|
|
INFO_PRINT("st mode on : %ld %ld %ld\n",
|
|
T(self_test).data_st_on[0],
|
|
T(self_test).data_st_on[1],
|
|
T(self_test).data_st_on[2]);
|
|
INFO_PRINT("st mode off : %ld %ld %ld\n",
|
|
T(self_test).data_st_off[0],
|
|
T(self_test).data_st_off[1],
|
|
T(self_test).data_st_off[2]);
|
|
|
|
// read OTP
|
|
if (idx == ACC) {
|
|
SPI_WRITE(REG_REG_BANK_SEL, BIT_BANK_SEL_2);
|
|
SPI_READ(REG_XA_ST_DATA, 3, &T(dataBuffer[0]));
|
|
} else if (idx == GYR) {
|
|
SPI_WRITE(REG_REG_BANK_SEL, BIT_BANK_SEL_1);
|
|
SPI_READ(REG_XG_ST_DATA, 3, &T(dataBuffer[0]));
|
|
}
|
|
SPI_WRITE(REG_REG_BANK_SEL, BIT_BANK_SEL_0);
|
|
|
|
T(selftest_state) = TEST_REPORT;
|
|
}
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
case TEST_REPORT:
|
|
buf = T(dataBuffer[0]);
|
|
if (idx == ACC) {
|
|
T(self_test).otp_st_data_accel[0] = buf[1];
|
|
T(self_test).otp_st_data_accel[1] = buf[2];
|
|
T(self_test).otp_st_data_accel[2] = buf[3];
|
|
INFO_PRINT("otp accel : %d %d %d\n",
|
|
T(self_test).otp_st_data_accel[0],
|
|
T(self_test).otp_st_data_accel[1],
|
|
T(self_test).otp_st_data_accel[2]);
|
|
} else if (idx == GYR) {
|
|
T(self_test).otp_st_data_gyro[0] = buf[1];
|
|
T(self_test).otp_st_data_gyro[1] = buf[2];
|
|
T(self_test).otp_st_data_gyro[2] = buf[3];
|
|
INFO_PRINT("otp gyro : %d %d %d\n",
|
|
T(self_test).otp_st_data_gyro[0],
|
|
T(self_test).otp_st_data_gyro[1],
|
|
T(self_test).otp_st_data_gyro[2]);
|
|
}
|
|
|
|
pass = false;
|
|
if (idx == ACC) {
|
|
pass = checkAccelSelftest(_task);
|
|
} else if (idx == GYR) {
|
|
pass = checkGyroSelftest(_task);
|
|
}
|
|
if (pass) {
|
|
sendTestResult(SENSOR_APP_EVT_STATUS_SUCCESS,
|
|
mSensorInfo[idx].sensorType);
|
|
} else {
|
|
sendTestResult(SENSOR_APP_EVT_STATUS_ERROR,
|
|
mSensorInfo[idx].sensorType);
|
|
}
|
|
|
|
T(selftest_state) = TEST_DONE;
|
|
selfTestDeinit(_task, idx);
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, &T(sensors[idx]), __FUNCTION__);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool accSelfTest(void *cookie)
|
|
{
|
|
TDECL();
|
|
|
|
INFO_PRINT("Accel Selftest\n");
|
|
|
|
if (!T(powered) && trySwitchState(SENSOR_TESTING)) {
|
|
T(self_test).st_mode = false;
|
|
T(selftest_state) = TEST_START;
|
|
selfTestHandling(_task, ACC);
|
|
return true;
|
|
} else {
|
|
ERROR_PRINT("cannot test accel because sensor is busy\n");
|
|
sendTestResult(SENSOR_APP_EVT_STATUS_BUSY, SENS_TYPE_ACCEL);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool gyrSelfTest(void *cookie)
|
|
{
|
|
TDECL();
|
|
|
|
INFO_PRINT("Gyro Selftest\n");
|
|
|
|
if (!T(powered) && trySwitchState(SENSOR_TESTING)) {
|
|
T(self_test).st_mode = false;
|
|
T(selftest_state) = TEST_START;
|
|
selfTestHandling(_task, GYR);
|
|
return true;
|
|
} else {
|
|
ERROR_PRINT("cannot test accel because sensor is busy\n");
|
|
sendTestResult(SENSOR_APP_EVT_STATUS_BUSY, SENS_TYPE_GYRO);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool sensorFirmwareUpload(void *cookie)
|
|
{
|
|
TDECL();
|
|
int i = (long int)cookie;
|
|
|
|
sensorSignalInternalEvt(T(sensors[i]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool enableInterrupt(struct Gpio *pin, IRQn_Type irq, struct ChainedIsr *isr)
|
|
{
|
|
gpioConfigInput(pin, GPIO_SPEED_LOW, GPIO_PULL_NONE);
|
|
syscfgSetExtiPort(pin);
|
|
extiEnableIntGpio(pin, EXTI_TRIGGER_RISING);
|
|
extiChainIsr(irq, isr);
|
|
return true;
|
|
}
|
|
|
|
static bool disableInterrupt(struct Gpio *pin, IRQn_Type irq, struct ChainedIsr *isr)
|
|
{
|
|
extiUnchainIsr(irq, isr);
|
|
extiDisableIntGpio(pin);
|
|
return true;
|
|
}
|
|
|
|
static void configInt1(TASK, bool on)
|
|
{
|
|
enum SensorIndex i;
|
|
bool powered = false;
|
|
|
|
if (on && !T(Int1_EN)) {
|
|
enableInterrupt(T(Int1), T(Irq1), &T(Isr1));
|
|
T(Int1_EN) = true;
|
|
} else if (!on && T(Int1_EN)) {
|
|
for (i = 0; i < NUM_OF_SENSOR; i++) {
|
|
if (T(sensors[i]).powered) {
|
|
powered = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!powered) {
|
|
disableInterrupt(T(Int1), T(Irq1), &T(Isr1));
|
|
T(Int1_EN) = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint8_t computeOdrConf(uint32_t rate)
|
|
{
|
|
switch (rate) {
|
|
case SENSOR_HZ(1000.0f):
|
|
return 6;
|
|
case SENSOR_HZ(200.0f):
|
|
return 7;
|
|
case SENSOR_HZ(100.0f):
|
|
return 8;
|
|
case SENSOR_HZ(50.0f):
|
|
return 9;
|
|
case SENSOR_HZ(25.0f):
|
|
default:
|
|
return 10;
|
|
}
|
|
}
|
|
|
|
static void computeConfig(TASK, struct ICM40600Config *config)
|
|
{
|
|
uint64_t latency;
|
|
uint64_t val;
|
|
uint32_t latency_ms;
|
|
int i;
|
|
|
|
// copy current parameters
|
|
*config = T(config);
|
|
|
|
// compute sensors on
|
|
if (T(sensors[ACC]).configed || T(sensors[WOM]).configed || T(sensors[NOMO]).configed) {
|
|
config->accel_on = true;
|
|
} else {
|
|
config->accel_on = false;
|
|
}
|
|
config->gyro_on = T(sensors[GYR]).configed;
|
|
if (T(sensors[WOM]).configed || T(sensors[NOMO]).configed) {
|
|
config->wom_on = true;
|
|
} else {
|
|
config->wom_on = false;
|
|
}
|
|
|
|
// compute accel and gyro rates
|
|
if (config->accel_on) {
|
|
if (T(sensors[ACC]).configed) {
|
|
if (T(sensors[ACC]).rate > NO_DECIMATION_MAX_RATE) {
|
|
config->accel_rate = DECIMATION_HIGH_RATE;
|
|
} else if (T(sensors[ACC]).rate < NO_DECIMATION_MIN_RATE) {
|
|
config->accel_rate = DECIMATION_LOW_RATE;
|
|
} else {
|
|
config->accel_rate = T(sensors[ACC]).rate;
|
|
}
|
|
} else {
|
|
config->accel_rate = NO_DECIMATION_MIN_RATE;
|
|
}
|
|
}
|
|
if (config->gyro_on) {
|
|
if (T(sensors[GYR]).configed) {
|
|
if (T(sensors[GYR]).rate > NO_DECIMATION_MAX_RATE) {
|
|
config->gyro_rate = DECIMATION_HIGH_RATE;
|
|
} else if (T(sensors[GYR]).rate < NO_DECIMATION_MIN_RATE) {
|
|
config->gyro_rate = DECIMATION_LOW_RATE;
|
|
} else {
|
|
config->gyro_rate = T(sensors[GYR]).rate;
|
|
}
|
|
} else {
|
|
config->gyro_rate = NO_DECIMATION_MIN_RATE;
|
|
}
|
|
}
|
|
|
|
// compute wom threshold
|
|
if (config->wom_on) {
|
|
config->wom_threshold = ICM40600_WOM_THRESHOLD_MG / (config->accel_rate / NO_DECIMATION_MIN_RATE);
|
|
}
|
|
|
|
// compute fifo configuration
|
|
if (T(sensors[ACC]).configed || T(sensors[GYR]).configed) {
|
|
config->fifo_sample_size = FIFO_PACKET_SIZE;
|
|
} else {
|
|
config->fifo_sample_size = 0;
|
|
}
|
|
|
|
// compute fifo rate and latency/watermark
|
|
config->fifo_rate = 0;
|
|
latency = SENSOR_LATENCY_NODATA;
|
|
for (i = FIRST_CONT_SENSOR; i < NUM_CONT_SENSOR; ++i) {
|
|
if (T(sensors[i]).configed) {
|
|
// look for the highest rate
|
|
if (T(sensors[i]).rate > config->fifo_rate) {
|
|
config->fifo_rate = T(sensors[i]).rate;
|
|
}
|
|
// look for the shortest latency
|
|
if (T(sensors[i]).latency < latency) {
|
|
latency = T(sensors[i]).latency;
|
|
}
|
|
}
|
|
}
|
|
if (config->fifo_rate > NO_DECIMATION_MAX_RATE) {
|
|
config->fifo_rate = DECIMATION_HIGH_RATE;
|
|
} else if (config->fifo_rate < NO_DECIMATION_MIN_RATE) {
|
|
config->fifo_rate = DECIMATION_LOW_RATE;
|
|
}
|
|
|
|
// add 0.5ms for rounding
|
|
latency_ms = cpuMathU64DivByU16(cpuMathU64DivByU16(latency + 500000, 1000), 1000);
|
|
val = (uint64_t)latency_ms * (uint64_t)config->fifo_rate;
|
|
config->fifo_watermark = cpuMathU64DivByU16(cpuMathU64DivByU16(val, 1000), RATE_TO_HZ);
|
|
config->fifo_watermark *= config->fifo_sample_size;
|
|
if (config->fifo_watermark < config->fifo_sample_size) {
|
|
config->fifo_watermark = config->fifo_sample_size;
|
|
}
|
|
if (config->fifo_watermark > MAX_BATCH_SIZE) {
|
|
config->fifo_watermark = MAX_BATCH_SIZE - (MAX_BATCH_SIZE % config->fifo_sample_size);
|
|
}
|
|
}
|
|
|
|
static void updateConfig(TASK, const struct ICM40600Config *config)
|
|
{
|
|
uint32_t delay_us;
|
|
uint8_t val, val2;
|
|
|
|
// Disable Interrupt
|
|
SPI_WRITE(REG_INT_SOURCE0, 0);
|
|
SPI_WRITE(REG_INT_SOURCE1, 0);
|
|
|
|
// set rate and WoM threshold
|
|
if (config->gyro_rate != T(config).gyro_rate) {
|
|
val = computeOdrConf(config->gyro_rate);
|
|
SPI_WRITE(REG_GYRO_CONFIG0, (ICM40600_GYR_FS << SHIFT_GYRO_FS_SEL) |
|
|
(val << SHIFT_ODR_CONF));
|
|
}
|
|
if (config->accel_rate != T(config).accel_rate) {
|
|
val = computeOdrConf(config->accel_rate);
|
|
SPI_WRITE(REG_ACCEL_CONFIG0, (ICM40600_ACC_FS << SHIFT_ACCEL_FS_SEL) |
|
|
(val << SHIFT_ODR_CONF));
|
|
}
|
|
if (config->wom_threshold != T(config).wom_threshold) {
|
|
val = ICM40600_WOM_COMPUTE(config->wom_threshold);
|
|
SPI_WRITE(REG_ACCEL_WOM_X_THR, val);
|
|
SPI_WRITE(REG_ACCEL_WOM_Y_THR, val);
|
|
SPI_WRITE(REG_ACCEL_WOM_Z_THR, val);
|
|
}
|
|
|
|
// set WM
|
|
if (config->fifo_watermark != T(config).fifo_watermark) {
|
|
SPI_WRITE(REG_FIFO_CONFIG2, config->fifo_watermark & 0xFF);
|
|
SPI_WRITE(REG_FIFO_CONFIG3, (config->fifo_watermark >> 8) & 0xFF);
|
|
}
|
|
|
|
// turn on/off accel and gyro if any change
|
|
if (config->gyro_on != T(config).gyro_on || config->accel_on != T(config).accel_on) {
|
|
val = 0;
|
|
if (config->gyro_on) {
|
|
val |= BIT_GYRO_MODE_LN;
|
|
}
|
|
if (config->accel_on) {
|
|
val |= BIT_ACCEL_MODE_LN;
|
|
}
|
|
// delay needed if gyro or accel turning on
|
|
if ((config->gyro_on && !T(config).gyro_on) || (config->accel_on && !T(config).accel_on)) {
|
|
delay_us = 200; // 9136
|
|
} else {
|
|
delay_us = 0;
|
|
}
|
|
SPI_WRITE(REG_PWR_MGMT_0, val, delay_us);
|
|
}
|
|
|
|
// turn on/off WOM
|
|
if (config->wom_on != T(config).wom_on) {
|
|
if (config->wom_on) {
|
|
val = BIT_WOM_INT_MODE_OR | BIT_WOM_MODE_PREV | BIT_SMD_MODE_OLD;
|
|
} else {
|
|
val = 0;
|
|
}
|
|
SPI_WRITE(REG_SMD_CONFIG, val);
|
|
}
|
|
|
|
// turn on/off FIFO
|
|
if (config->fifo_sample_size != T(config).fifo_sample_size) {
|
|
if (config->fifo_sample_size != 0) {
|
|
// enabling FIFO
|
|
val = BIT_FIFO_MODE_STREAM;
|
|
val2 = BIT_FIFO_ACCEL_EN | BIT_FIFO_GYRO_EN | BIT_FIFO_TEMP_EN |
|
|
BIT_FIFO_TMST_FSYNC_EN | BIT_FIFO_WM_TH;
|
|
// reset chip time
|
|
T(chip_time_us) = 0;
|
|
T(chip_timestamp) = 0;
|
|
T(fifo_start_sync) = true;
|
|
invalidate_sensortime_to_rtc_time(_task);
|
|
} else {
|
|
// disabling FIFO
|
|
val = BIT_FIFO_MODE_BYPASS;
|
|
val2 = 0;
|
|
}
|
|
SPI_WRITE(REG_FIFO_CONFIG, val);
|
|
SPI_WRITE(REG_FIFO_CONFIG1, val2);
|
|
if (val == BIT_FIFO_MODE_BYPASS) {
|
|
SPI_READ(REG_FIFO_BYTE_COUNT1, 2, &T(dataBuffer[0])); // 9052
|
|
}
|
|
}
|
|
|
|
// enable/disable FIFO data interrupt
|
|
if (config->fifo_sample_size != 0) {
|
|
val = BIT_INT_FIFO_THS_INT1_EN;
|
|
} else {
|
|
val = 0;
|
|
}
|
|
SPI_WRITE(REG_INT_SOURCE0, val);
|
|
|
|
// enable/disable WOM interrupt (only when FIFO disabled or batch mode)
|
|
if (config->wom_on && (config->fifo_sample_size == 0 || config->fifo_watermark > config->fifo_sample_size)) {
|
|
val = BIT_INT_WOM_XYZ_INT1_EN;
|
|
} else {
|
|
val = 0;
|
|
}
|
|
SPI_WRITE(REG_INT_SOURCE1, val);
|
|
}
|
|
|
|
static void applyConfig(TASK, const struct ICM40600Config *config)
|
|
{
|
|
uint32_t duration_us;
|
|
uint32_t decimator;
|
|
int i;
|
|
|
|
// setup wait for odr change
|
|
if (config->accel_rate != T(config).accel_rate) {
|
|
T(sensors[ACC]).wait_for_odr = true;
|
|
}
|
|
if (config->gyro_rate != T(config).gyro_rate) {
|
|
T(sensors[GYR]).wait_for_odr = true;
|
|
}
|
|
|
|
// setup first samples skip
|
|
if (config->gyro_on && !T(config).gyro_on) {
|
|
// gyro turning on
|
|
duration_us = (1000000 * RATE_TO_HZ) / config->gyro_rate;
|
|
if (duration_us * GYR_SKIP_SAMPLE_NB > ICM40600_GYRO_START_TIME_MS * 1000) {
|
|
T(sensors[GYR]).skip_sample_cnt = GYR_SKIP_SAMPLE_NB;
|
|
} else {
|
|
T(sensors[GYR]).skip_sample_cnt = (ICM40600_GYRO_START_TIME_MS * 1000) / duration_us;
|
|
if ((ICM40600_GYRO_START_TIME_MS * 1000) % duration_us) {
|
|
T(sensors[GYR]).skip_sample_cnt++;
|
|
}
|
|
}
|
|
}
|
|
if (config->accel_on && !T(config).accel_on) {
|
|
// accel turning on
|
|
duration_us = (1000000 * RATE_TO_HZ) / config->accel_rate;
|
|
if (duration_us * ACC_SKIP_SAMPLE_NB > ICM40600_ACCEL_START_TIME_MS * 1000) {
|
|
T(sensors[ACC]).skip_sample_cnt = ACC_SKIP_SAMPLE_NB;
|
|
} else {
|
|
T(sensors[ACC]).skip_sample_cnt = (ICM40600_ACCEL_START_TIME_MS * 1000) / duration_us;
|
|
if ((ICM40600_ACCEL_START_TIME_MS * 1000) % duration_us) {
|
|
T(sensors[ACC]).skip_sample_cnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update all sensors decimators
|
|
for (i = 0; i <= GYR; i++) {
|
|
if (!T(sensors[i]).configed || T(sensors[i]).rate == 0) {
|
|
decimator = 1;
|
|
} else {
|
|
if (i == ACC) {
|
|
decimator = config->accel_rate / T(sensors[i]).rate;
|
|
} else if (i == GYR) {
|
|
decimator = config->gyro_rate / T(sensors[i]).rate;
|
|
}
|
|
}
|
|
if (decimator != T(sensors[i]).decimator) {
|
|
T(sensors[i]).decimator = decimator;
|
|
T(sensors[i]).data_cnt = 0;
|
|
}
|
|
}
|
|
|
|
// setup NOMO timer
|
|
if (T(sensors[NOMO]).configed && !T(noMotionTimerHandle)) {
|
|
T(noMotionTimerHandle) = timTimerSet(ICM40600_NOM_DURATION_NS, 0, 100, noMotionCallback, &T(sensors[NOMO]), false);
|
|
} else if (!T(sensors[NOMO]).configed && T(noMotionTimerHandle)) {
|
|
timTimerCancel(T(noMotionTimerHandle));
|
|
T(noMotionTimerHandle) = 0;
|
|
}
|
|
|
|
// update config
|
|
T(config) = *config;
|
|
|
|
DEBUG_PRINT("config: accel(%d, %luHz/%u), gyro(%d, %luHz/%u), wom(%d, %lu), "
|
|
"fifo(%u/%u, %luHz)\n",
|
|
T(config).accel_on, T(config).accel_rate / RATE_TO_HZ, T(sensors[ACC]).decimator,
|
|
T(config).gyro_on, T(config).gyro_rate / RATE_TO_HZ, T(sensors[GYR]).decimator,
|
|
T(config).wom_on, T(config).wom_threshold,
|
|
T(config).fifo_sample_size, T(config).fifo_watermark, T(config).fifo_rate / RATE_TO_HZ);
|
|
}
|
|
|
|
static void configSensor(TASK)
|
|
{
|
|
struct ICM40600Config config;
|
|
|
|
computeConfig(_task, &config);
|
|
updateConfig(_task, &config);
|
|
applyConfig(_task, &config);
|
|
}
|
|
|
|
static void sensorTurnOn(TASK)
|
|
{
|
|
/* enable chip timestamp */
|
|
SPI_WRITE(REG_TMST_CONFIG, BIT_EN_DREG_FIFO_D2A | BIT_TMST_EN);
|
|
|
|
T(powered) = true;
|
|
#if DBG_TIMESTAMP
|
|
memset(&T(statistics_set), 0, sizeof(struct StatisticsSet));
|
|
#endif
|
|
DEBUG_PRINT("chip on\n");
|
|
}
|
|
|
|
static void sensorTurnOff(TASK)
|
|
{
|
|
/* disable chip timestamp */
|
|
SPI_WRITE(REG_TMST_CONFIG, BIT_EN_DREG_FIFO_D2A);
|
|
|
|
T(powered) = false;
|
|
#if DBG_TIMESTAMP
|
|
DEBUG_PRINT("time sync stats: reset: %ld, sync: %ld, adjust+: %ld, adjust-: %ld, truncate: %ld\n",
|
|
T(statistics_set).sync_reset_count,
|
|
T(statistics_set).sync_count,
|
|
T(statistics_set).sync_adjust_plus,
|
|
T(statistics_set).sync_adjust_minus,
|
|
T(statistics_set).sync_truncate);
|
|
#endif
|
|
DEBUG_PRINT("chip off\n");
|
|
}
|
|
|
|
static void sensorPower(TASK, int sensorType, bool on)
|
|
{
|
|
bool chip_on;
|
|
int i;
|
|
|
|
if (on) {
|
|
// turn on chip if needed
|
|
if (!T(powered)) {
|
|
sensorTurnOn(_task);
|
|
}
|
|
} else {
|
|
// change chip configuration
|
|
configSensor(_task);
|
|
// turn chip off if needed
|
|
chip_on = false;
|
|
for (i = 0; i < NUM_OF_SENSOR; ++i) {
|
|
if (T(sensors[i]).powered) {
|
|
chip_on = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!chip_on) {
|
|
sensorTurnOff(_task);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool sensorSetPower(bool on, void *cookie)
|
|
{
|
|
TDECL();
|
|
int sensor_type = (int)cookie;
|
|
struct ICM40600Sensor *sensor = &T(sensors[sensor_type]);
|
|
|
|
DEBUG_PRINT("%s: on=%d, state=%" PRI_STATE "\n", mSensorInfo[sensor_type].sensorName, on, getStateName(GET_STATE()));
|
|
|
|
if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
|
|
sensor->powered = on;
|
|
if (!on) {
|
|
sensor->configed = false;
|
|
}
|
|
configInt1(_task, on);
|
|
sensorPower(_task, sensor_type, on);
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, sensor, __FUNCTION__);
|
|
} else {
|
|
T(pending_config[sensor_type]) = true;
|
|
sensor->pConfig.enable = on;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sensorSetRate(uint32_t rate, uint64_t latency, void *cookie)
|
|
{
|
|
TDECL();
|
|
int sensor_type = (int)cookie;
|
|
struct ICM40600Sensor *sensor = &T(sensors[sensor_type]);
|
|
|
|
DEBUG_PRINT("%s: rate=%lu, latency=%llu, state=%" PRI_STATE "\n",
|
|
mSensorInfo[sensor_type].sensorName, rate, latency, getStateName(GET_STATE()));
|
|
|
|
if (trySwitchState(SENSOR_CONFIG_CHANGING)) {
|
|
sensor->rate = rate;
|
|
sensor->latency = latency;
|
|
sensor->configed = true;
|
|
configSensor(_task);
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, sensor, __FUNCTION__);
|
|
} else {
|
|
T(pending_config[sensor_type]) = true;
|
|
sensor->pConfig.enable = sensor->powered;
|
|
sensor->pConfig.rate = rate;
|
|
sensor->pConfig.latency = latency;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void sendFlushEvt(void)
|
|
{
|
|
TDECL();
|
|
uint32_t evtType = 0;
|
|
int i;
|
|
|
|
for (i = FIRST_CONT_SENSOR; i < NUM_CONT_SENSOR; i++) {
|
|
while (T(sensors[i]).flush > 0) {
|
|
evtType = sensorGetMyEventType(mSensorInfo[i].sensorType);
|
|
osEnqueueEvt(evtType, SENSOR_DATA_EVENT_FLUSH, NULL);
|
|
T(sensors[i]).flush--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void int1Evt(TASK, bool flush);
|
|
|
|
static bool flushSensor(void *cookie)
|
|
{
|
|
TDECL();
|
|
int sensor_idx = (int)cookie;
|
|
uint32_t evtType;
|
|
|
|
DEBUG_PRINT("%s flush\n", mSensorInfo[sensor_idx].sensorName);
|
|
|
|
if (sensor_idx >= FIRST_CONT_SENSOR && sensor_idx < NUM_CONT_SENSOR) {
|
|
T(sensors[sensor_idx]).flush++;
|
|
int1Evt(_task, true);
|
|
return true;
|
|
}
|
|
if (sensor_idx >= FIRST_ONESHOT_SENSOR && sensor_idx < NUM_OF_SENSOR) {
|
|
evtType = sensorGetMyEventType(mSensorInfo[sensor_idx].sensorType);
|
|
osEnqueueEvt(evtType, SENSOR_DATA_EVENT_FLUSH, NULL);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool flushData(struct ICM40600Sensor *sensor, uint32_t eventId)
|
|
{
|
|
bool success = false;
|
|
|
|
if (sensor->data_evt) {
|
|
success = osEnqueueEvtOrFree(eventId, sensor->data_evt, dataEvtFree);
|
|
sensor->data_evt = NULL;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static void flushAllData(void)
|
|
{
|
|
TDECL();
|
|
int i;
|
|
|
|
for (i = FIRST_CONT_SENSOR; i < NUM_CONT_SENSOR; i++) {
|
|
flushData(&T(sensors[i]),
|
|
EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(mSensorInfo[i].sensorType));
|
|
}
|
|
}
|
|
|
|
static bool allocateDataEvt(struct ICM40600Sensor *mSensor, uint64_t rtc_time)
|
|
{
|
|
TDECL();
|
|
|
|
mSensor->data_evt = slabAllocatorAlloc(T(mDataSlab));
|
|
if (mSensor->data_evt == NULL) {
|
|
// slab allocation failed
|
|
ERROR_PRINT("slabAllocatorAlloc() failed\n");
|
|
return false;
|
|
}
|
|
|
|
// delta time for the first sample is sample count
|
|
memset(&mSensor->data_evt->samples[0].firstSample, 0x00, sizeof(struct SensorFirstSample));
|
|
mSensor->data_evt->referenceTime = rtc_time;
|
|
mSensor->prev_rtc_time = rtc_time;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void parseOnePacket(TASK, const uint8_t *data, int idx)
|
|
{
|
|
struct FifoPacketData fifo_data;
|
|
uint16_t diff;
|
|
|
|
getFifoData(data, idx, &fifo_data);
|
|
|
|
/* wait for odr */
|
|
if (T(sensors[ACC]).wait_for_odr) {
|
|
if (fifo_data.odr_accel) {
|
|
T(sensors[ACC]).wait_for_odr = false;
|
|
} else {
|
|
fifo_data.valid_accel = false;
|
|
}
|
|
}
|
|
if (T(sensors[GYR]).wait_for_odr) {
|
|
if (fifo_data.odr_gyro) {
|
|
T(sensors[GYR]).wait_for_odr = false;
|
|
} else {
|
|
fifo_data.valid_gyro = false;
|
|
}
|
|
}
|
|
|
|
/* drop first some samples */
|
|
if (fifo_data.valid_accel) {
|
|
if (T(sensors[ACC]).skip_sample_cnt > 0) {
|
|
fifo_data.valid_accel = false;
|
|
T(sensors[ACC]).skip_sample_cnt--;
|
|
}
|
|
}
|
|
if (fifo_data.valid_gyro) {
|
|
if (T(sensors[GYR]).skip_sample_cnt > 0) {
|
|
fifo_data.valid_gyro = false;
|
|
T(sensors[GYR]).skip_sample_cnt--;
|
|
}
|
|
}
|
|
|
|
/* update sensors data */
|
|
if (fifo_data.valid_accel) {
|
|
ICM40600_TO_ANDROID_COORDINATE(fifo_data.accel[0], fifo_data.accel[1], fifo_data.accel[2]);
|
|
if (T(sensors[ACC]).configed) {
|
|
T(sensors[ACC]).data[0] = fifo_data.accel[0];
|
|
T(sensors[ACC]).data[1] = fifo_data.accel[1];
|
|
T(sensors[ACC]).data[2] = fifo_data.accel[2];
|
|
T(sensors[ACC]).updated = true;
|
|
T(sensors[ACC]).data_cnt++;
|
|
}
|
|
}
|
|
if (fifo_data.valid_gyro) {
|
|
ICM40600_TO_ANDROID_COORDINATE(fifo_data.gyro[0], fifo_data.gyro[1], fifo_data.gyro[2]);
|
|
if (T(sensors[GYR]).configed) {
|
|
T(sensors[GYR]).data[0] = fifo_data.gyro[0];
|
|
T(sensors[GYR]).data[1] = fifo_data.gyro[1];
|
|
T(sensors[GYR]).data[2] = fifo_data.gyro[2];
|
|
T(sensors[GYR]).updated = true;
|
|
T(sensors[GYR]).data_cnt++;
|
|
}
|
|
}
|
|
|
|
// update temperature
|
|
T(chip_temperature) = fifo_data.temp * TEMP_SCALE + TEMP_OFFSET;
|
|
|
|
// count up chip time
|
|
if (T(chip_time_us) == 0) {
|
|
T(chip_time_us) = (uint64_t)fifo_data.timestamp * CHIP_TIME_RES_US + CHIP_TIME_OFFSET_US;
|
|
} else {
|
|
// unsigned difference handle counter roll-up
|
|
diff = fifo_data.timestamp - T(chip_timestamp);
|
|
T(chip_time_us) += diff * CHIP_TIME_RES_US;
|
|
}
|
|
T(chip_timestamp) = fifo_data.timestamp;
|
|
}
|
|
|
|
static void pushSensorData(TASK, struct ICM40600Sensor *mSensor, uint64_t rtc_time)
|
|
{
|
|
float x, y, z;
|
|
float offset[3];
|
|
bool new_offset_update = false;
|
|
struct TripleAxisDataPoint *sample;
|
|
uint32_t delta_time;
|
|
|
|
switch (mSensor->idx) {
|
|
case ACC:
|
|
// scale data to android units
|
|
x = mSensor->data[0] * kScale_acc;
|
|
y = mSensor->data[1] * kScale_acc;
|
|
z = mSensor->data[2] * kScale_acc;
|
|
// run and apply calibration on sensor data
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
accelCalRun(&T(accel_cal), rtc_time, x, y, z, T(chip_temperature));
|
|
accelCalBiasRemove(&T(accel_cal), &x, &y, &z);
|
|
# ifdef ACCEL_CAL_DBG_ENABLED
|
|
accelCalDebPrint(&T(accel_cal), T(chip_temperature));
|
|
# endif
|
|
#endif
|
|
#ifdef GYRO_CAL_ENABLED
|
|
gyroCalUpdateAccel(&T(gyro_cal), rtc_time, x, y, z);
|
|
#endif
|
|
break;
|
|
case GYR:
|
|
// scale data to android units
|
|
x = mSensor->data[0] * kScale_gyr;
|
|
y = mSensor->data[1] * kScale_gyr;
|
|
z = mSensor->data[2] * kScale_gyr;
|
|
// run and apply calibration on sensor data
|
|
#ifdef GYRO_CAL_ENABLED
|
|
gyroCalUpdateGyro(&T(gyro_cal), rtc_time, x, y, z, T(chip_temperature));
|
|
gyroCalRemoveBias(&T(gyro_cal), x, y, z, &x, &y, &z);
|
|
new_offset_update = gyroCalNewBiasAvailable(&T(gyro_cal));
|
|
if (new_offset_update) {
|
|
float gyro_offset_temperature_celsius;
|
|
gyroCalGetBias(&T(gyro_cal), &offset[0], &offset[1], &offset[2],
|
|
&gyro_offset_temperature_celsius);
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (mSensor->data_evt == NULL) {
|
|
if (!allocateDataEvt(mSensor, rtc_time))
|
|
return;
|
|
}
|
|
|
|
if (mSensor->data_evt->samples[0].firstSample.numSamples >= MAX_NUM_COMMS_EVENT_SAMPLES) {
|
|
ERROR_PRINT("samples bad index\n");
|
|
return;
|
|
}
|
|
|
|
// handle bias sending
|
|
if (new_offset_update) {
|
|
if (mSensor->data_evt->samples[0].firstSample.numSamples > 0) {
|
|
// flush existing samples so the bias appears after them.
|
|
flushData(mSensor, EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(mSensorInfo[mSensor->idx].sensorType));
|
|
if (!allocateDataEvt(mSensor, rtc_time)) {
|
|
return;
|
|
}
|
|
}
|
|
mSensor->data_evt->samples[0].firstSample.biasCurrent = true;
|
|
mSensor->data_evt->samples[0].firstSample.biasPresent = 1;
|
|
mSensor->data_evt->samples[0].firstSample.biasSample = mSensor->data_evt->samples[0].firstSample.numSamples;
|
|
sample = &mSensor->data_evt->samples[mSensor->data_evt->samples[0].firstSample.numSamples++];
|
|
// Updates the offset in HAL.
|
|
sample->x = offset[0];
|
|
sample->y = offset[1];
|
|
sample->z = offset[2];
|
|
flushData(mSensor, sensorGetMyEventType(mSensorInfo[mSensor->idx].biasType));
|
|
DEBUG_PRINT("send new gyro bias\n");
|
|
if (!allocateDataEvt(mSensor, rtc_time)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
sample = &mSensor->data_evt->samples[mSensor->data_evt->samples[0].firstSample.numSamples++];
|
|
|
|
// the first deltatime is for sample size
|
|
if (mSensor->data_evt->samples[0].firstSample.numSamples > 1) {
|
|
delta_time = rtc_time - mSensor->prev_rtc_time;
|
|
delta_time = delta_time < 0 ? 0 : delta_time;
|
|
sample->deltaTime = delta_time;
|
|
mSensor->prev_rtc_time = rtc_time;
|
|
}
|
|
|
|
sample->x = x;
|
|
sample->y = y;
|
|
sample->z = z;
|
|
|
|
if (mSensor->data_evt->samples[0].firstSample.numSamples == MAX_NUM_COMMS_EVENT_SAMPLES) {
|
|
flushAllData();
|
|
}
|
|
}
|
|
|
|
static void computeTimeSync(TASK, const uint8_t *data, uint32_t count, uint64_t data_timestamp)
|
|
{
|
|
const uint64_t now = sensorGetTime();
|
|
uint64_t chip_time_us;
|
|
uint32_t sample_index;
|
|
uint32_t duration_us;
|
|
struct FifoPacketData packet;
|
|
uint16_t sample_nb;
|
|
uint16_t chip_timestamp;
|
|
uint16_t diff;
|
|
bool first_sync = false;
|
|
bool sync = false;
|
|
|
|
/* scan fifo data for the latest chip timestamp */
|
|
chip_time_us = T(chip_time_us);
|
|
chip_timestamp = T(chip_timestamp);
|
|
sample_index = 0;
|
|
sample_nb = 0;
|
|
while (count >= T(config).fifo_sample_size) {
|
|
sample_nb++;
|
|
getFifoData(data, sample_index, &packet);
|
|
sample_index += T(config).fifo_sample_size;
|
|
count -= T(config).fifo_sample_size;
|
|
// count up chip time
|
|
if (chip_time_us == 0) {
|
|
// first chip timestamp
|
|
chip_time_us = (uint64_t)packet.timestamp * CHIP_TIME_RES_US + CHIP_TIME_OFFSET_US;
|
|
} else {
|
|
// unsigned difference handle counter roll-up
|
|
diff = packet.timestamp - chip_timestamp;
|
|
chip_time_us += diff * CHIP_TIME_RES_US;
|
|
}
|
|
chip_timestamp = packet.timestamp;
|
|
}
|
|
|
|
/* first time sync after FIFO enable */
|
|
if (T(fifo_start_sync)) {
|
|
if (T(config).fifo_rate != 0) {
|
|
duration_us = (1000000 * RATE_TO_HZ) / T(config).fifo_rate;
|
|
} else {
|
|
duration_us = 0;
|
|
}
|
|
// use estimated duration
|
|
map_sensortime_to_rtc_time(_task,
|
|
chip_time_us - duration_us * sample_nb,
|
|
data_timestamp - (uint64_t)duration_us * 1000ULL * sample_nb);
|
|
T(fifo_start_sync) = false;
|
|
T(last_sync_time) = now;
|
|
first_sync = true;
|
|
}
|
|
|
|
/* periodical time sync */
|
|
if ((now - T(last_sync_time)) > MSEC_TO_NANOS(100)) {
|
|
// every 100ms
|
|
uint64_t estimated_rtc;
|
|
uint64_t min_rtc, max_rtc;
|
|
uint64_t limit_ns, adjust_ns;
|
|
uint64_t updated_data_timestamp = data_timestamp;
|
|
bool valid = sensortime_to_rtc_time(_task, chip_time_us, &estimated_rtc);
|
|
// check if appropriate to inject to time sync algorithm
|
|
// adjust rtc time if necessary not to make large jitters
|
|
if (valid) {
|
|
limit_ns = U64_DIV_BY_CONST_U16((data_timestamp - T(last_sync_data_ts)) * 10, 1000); // 1% (100ms * 1% = 1ms)
|
|
adjust_ns = U64_DIV_BY_CONST_U16((data_timestamp - T(last_sync_data_ts)) * 1, 10000); // 0.01% (100ms * 0.01% = 10us)
|
|
min_rtc = data_timestamp - limit_ns; // actual ts - x
|
|
max_rtc = data_timestamp + limit_ns; // actual ts + x
|
|
if ((estimated_rtc >= min_rtc) && (estimated_rtc <= max_rtc)) {
|
|
sync = true;
|
|
} else if (estimated_rtc < min_rtc) {
|
|
sync = true;
|
|
updated_data_timestamp = updated_data_timestamp - adjust_ns;
|
|
#if DBG_TIMESTAMP
|
|
T(statistics_set).sync_adjust_minus++;
|
|
#endif
|
|
} else if (estimated_rtc > max_rtc) {
|
|
sync = true;
|
|
updated_data_timestamp = updated_data_timestamp + adjust_ns;
|
|
#if DBG_TIMESTAMP
|
|
T(statistics_set).sync_adjust_plus++;
|
|
#endif
|
|
}
|
|
// limit the adjustment
|
|
if (sync) {
|
|
if (updated_data_timestamp > (data_timestamp + MSEC_TO_NANOS(1))) {
|
|
updated_data_timestamp = data_timestamp + MSEC_TO_NANOS(1);
|
|
}
|
|
}
|
|
}
|
|
if (sync) {
|
|
data_timestamp = updated_data_timestamp;
|
|
}
|
|
}
|
|
|
|
/* do time sync */
|
|
if (first_sync || sync) {
|
|
map_sensortime_to_rtc_time(_task, chip_time_us, data_timestamp);
|
|
T(last_sync_time) = now;
|
|
T(last_sync_data_ts) = data_timestamp;
|
|
}
|
|
}
|
|
|
|
static void dispatchData(TASK, const uint8_t *data, uint32_t count)
|
|
{
|
|
uint64_t ts = 0;
|
|
uint32_t sample_index = 0;
|
|
int i;
|
|
|
|
if (count == 0) {
|
|
return;
|
|
}
|
|
|
|
/* do time sync if no flush or fist time starting FIFO */
|
|
if (T(fifo_start_sync) || !T(flush)) {
|
|
computeTimeSync(_task, data, count, T(data_timestamp));
|
|
}
|
|
|
|
/* process FIFO data */
|
|
while (count >= T(config).fifo_sample_size) {
|
|
for (i = FIRST_CONT_SENSOR; i < NUM_CONT_SENSOR; ++i) {
|
|
T(sensors[i]).updated = false;
|
|
}
|
|
parseOnePacket(_task, data, sample_index);
|
|
sample_index += T(config).fifo_sample_size;
|
|
count -= T(config).fifo_sample_size;
|
|
// compute timestamp
|
|
if (!sensortime_to_rtc_time(_task, T(chip_time_us), &ts)) {
|
|
continue;
|
|
}
|
|
// add data
|
|
for (i = FIRST_CONT_SENSOR; i < NUM_CONT_SENSOR; ++i) {
|
|
if (!T(sensors[i]).configed || !T(sensors[i]).updated) {
|
|
continue;
|
|
}
|
|
if (T(sensors[i]).data_cnt % T(sensors[i]).decimator == 0) {
|
|
if (ts <= T(sensors[i]).prev_rtc_time) {
|
|
ts = T(sensors[i]).prev_rtc_time + MIN_INCREMENT_TIME_NS;
|
|
#if DBG_TIMESTAMP
|
|
T(statistics_set).sync_truncate++;
|
|
#endif
|
|
}
|
|
pushSensorData(_task, &T(sensors[i]), ts);
|
|
}
|
|
}
|
|
}
|
|
|
|
flushAllData();
|
|
}
|
|
|
|
static void int1Handling(TASK)
|
|
{
|
|
uint8_t int_status;
|
|
uint8_t int_status2;
|
|
uint16_t fifo_count = 0;
|
|
union EmbeddedDataPoint trigger_axies;
|
|
const uint8_t *buf;
|
|
|
|
switch (T(int_state)) {
|
|
case INT_READ_STATUS:
|
|
T(int_state) = INT_PROCESS_STATUS;
|
|
// read INT status1, status2 and fifo_count registers
|
|
SPI_READ(REG_INT_STATUS, 1, &T(dataBuffer[0]));
|
|
SPI_READ(REG_INT_STATUS2, 1, &T(dataBuffer[1]));
|
|
SPI_READ(REG_FIFO_BYTE_COUNT1, 2, &T(dataBuffer[2]));
|
|
T(data_timestamp) = sensorGetTime();
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, _task, __FUNCTION__);
|
|
break;
|
|
case INT_PROCESS_STATUS:
|
|
buf = T(dataBuffer[0]);
|
|
int_status = buf[1];
|
|
buf = T(dataBuffer[1]);
|
|
int_status2 = buf[1];
|
|
buf = T(dataBuffer[2]);
|
|
fifo_count = buf[2] << 8 | buf[1];
|
|
// INT status handling
|
|
if (int_status2 & BIT_INT_STATUS_WOM_XYZ) {
|
|
if (T(sensors[WOM]).configed) {
|
|
trigger_axies.idata = ((int_status2 & BIT_INT_STATUS_WOM_X) >> 0) | ((int_status2 & BIT_INT_STATUS_WOM_Y) >> 1) | ((int_status2 & BIT_INT_STATUS_WOM_Z) >> 2);
|
|
osEnqueueEvt(EVT_SENSOR_ANY_MOTION, trigger_axies.vptr, NULL);
|
|
}
|
|
if (T(sensors[NOMO]).configed && T(noMotionTimerHandle)) {
|
|
timTimerCancel(T(noMotionTimerHandle));
|
|
T(noMotionTimerHandle) = timTimerSet(ICM40600_NOM_DURATION_NS, 0, 100, noMotionCallback, (void *)&T(sensors[NOMO]), false);
|
|
}
|
|
}
|
|
// FIFO overflow
|
|
if (int_status & BIT_INT_STATUS_FIFO_FULL) {
|
|
ERROR_PRINT("fifo overflow\n");
|
|
}
|
|
// FIFO WM status handling
|
|
if ((int_status & BIT_INT_STATUS_FIFO_THS) || T(flush)) {
|
|
T(int_state) = INT_READ_FIFO_DATA;
|
|
} else {
|
|
T(int_state) = INT_DONE;
|
|
T(fifo_count) = 0;
|
|
sensorSpiCallback(_task, 0);
|
|
break;
|
|
}
|
|
// fall-through
|
|
case INT_READ_FIFO_DATA:
|
|
T(int_state) = INT_DONE;
|
|
// compute fifo count and delete partial sample
|
|
fifo_count -= fifo_count % T(config).fifo_sample_size;
|
|
// read FIFO data
|
|
T(fifo_count) = fifo_count;
|
|
if (fifo_count > 0) {
|
|
SPI_READ(REG_FIFO_DATA, fifo_count, &T(dataBuffer[0]));
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, _task, __FUNCTION__);
|
|
} else {
|
|
sensorSpiCallback(_task, 0);
|
|
}
|
|
break;
|
|
default:
|
|
T(int_state) = INT_DONE;
|
|
T(fifo_count) = 0;
|
|
sensorSpiCallback(_task, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void int1Evt(TASK, bool flush)
|
|
{
|
|
if (trySwitchState(SENSOR_INT_1_HANDLING)) {
|
|
T(flush) = flush;
|
|
T(int_state) = INT_READ_STATUS;
|
|
int1Handling(_task);
|
|
} else {
|
|
if (flush) {
|
|
T(pending_flush) = true;
|
|
} else {
|
|
T(pending_int[0]) = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define DEC_OPS(power, firmware, rate, flush) \
|
|
.sensorPower = power, \
|
|
.sensorFirmwareUpload = firmware, \
|
|
.sensorSetRate = rate, \
|
|
.sensorFlush = flush
|
|
|
|
#define DEC_OPS_CAL_CFG_TEST(power, firmware, rate, flush, cal, cfg, test) \
|
|
DEC_OPS(power, firmware, rate, flush), \
|
|
.sensorCalibrate = cal, \
|
|
.sensorCfgData = cfg, \
|
|
.sensorSelfTest = test,
|
|
|
|
#define DEC_OPS_CFG(power, firmware, rate, flush, cfg) \
|
|
DEC_OPS(power, firmware, rate, flush), \
|
|
.sensorCfgData = cfg
|
|
|
|
static const struct SensorOps mSensorOps[NUM_OF_SENSOR] =
|
|
{
|
|
{ DEC_OPS_CAL_CFG_TEST(sensorSetPower, sensorFirmwareUpload, sensorSetRate, flushSensor, accCalibration,
|
|
accCfgData, accSelfTest) },
|
|
{ DEC_OPS_CAL_CFG_TEST(sensorSetPower, sensorFirmwareUpload, sensorSetRate, flushSensor, gyrCalibration,
|
|
gyrCfgData, gyrSelfTest) },
|
|
{ DEC_OPS(sensorSetPower, sensorFirmwareUpload, sensorSetRate, flushSensor) },
|
|
{ DEC_OPS(sensorSetPower, sensorFirmwareUpload, sensorSetRate, flushSensor) },
|
|
};
|
|
|
|
static void configEvent(struct ICM40600Sensor *mSensor, struct ConfigStat *ConfigData)
|
|
{
|
|
TDECL();
|
|
int i;
|
|
|
|
for (i = 0; &T(sensors[i]) != mSensor; i++) ;
|
|
|
|
if (ConfigData->enable == 0 && mSensor->powered) {
|
|
mSensorOps[i].sensorPower(false, (void *)i);
|
|
} else if (ConfigData->enable == 1 && !mSensor->powered) {
|
|
mSensorOps[i].sensorPower(true, (void *)i);
|
|
} else {
|
|
mSensorOps[i].sensorSetRate(ConfigData->rate, ConfigData->latency, (void *)i);
|
|
}
|
|
}
|
|
|
|
static void processPendingEvt(TASK)
|
|
{
|
|
enum SensorIndex i;
|
|
|
|
if (T(pending_int[0])) {
|
|
T(pending_int[0]) = false;
|
|
int1Evt(_task, false);
|
|
return;
|
|
}
|
|
|
|
if (T(pending_flush)) {
|
|
T(pending_flush) = false;
|
|
int1Evt(_task, true);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < NUM_OF_SENSOR; i++) {
|
|
if (T(pending_config[i])) {
|
|
T(pending_config[i]) = false;
|
|
configEvent(&T(sensors[i]), &T(sensors[i]).pConfig);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (T(pending_calibration_save)) {
|
|
T(pending_calibration_save) = !saveCalibration(_task);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void sensorInit(TASK)
|
|
{
|
|
const uint8_t *buf;
|
|
|
|
switch (T(init_state)) {
|
|
case RESET_ICM40600:
|
|
// reset chip and turn on
|
|
SPI_WRITE(REG_DEVICE_CONFIG, BIT_SOFT_RESET, 100 * 1000);
|
|
SPI_READ(REG_INT_STATUS, 1, &T(dataBuffer[0]));
|
|
T(powered) = false;
|
|
sensorTurnOn(_task);
|
|
// set SPI speed register
|
|
SPI_WRITE(REG_SPI_SPEED, ICM40600_SPI_SPEED_REG_VALUE);
|
|
T(init_state) = INIT_ICM40600;
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, _task, __FUNCTION__);
|
|
break;
|
|
case INIT_ICM40600:
|
|
buf = T(dataBuffer[0]);
|
|
if (!(buf[1] & BIT_INT_STATUS_RESET_DONE)) {
|
|
ERROR_PRINT("chip reset failed!\n");
|
|
}
|
|
// reconfigure SPI speed
|
|
T(mode).speed = ICM40600_SPI_SPEED_HZ;
|
|
// configure interface
|
|
// i2c disabled
|
|
// fifo_count: little endian
|
|
// sensor data: little endian
|
|
SPI_WRITE(REG_INTF_CONFIG0, BIT_UI_SIFS_DISABLE_I2C);
|
|
// configure interrupt
|
|
SPI_WRITE(REG_INT_CONFIG, (INT1_POLARITY << SHIFT_INT1_POLARITY) |
|
|
(INT1_DRIVE_CIRCUIT << SHIFT_INT1_DRIVE_CIRCUIT) |
|
|
(INT1_MODE << SHIFT_INT1_MODE));
|
|
SPI_WRITE(REG_INT_CONFIG1, BIT_INT_ASY_RST_DISABLE); // 9139
|
|
// static config of sensors
|
|
SPI_WRITE(REG_GYRO_ACCEL_CONFIG0, ICM40600_ACC_BW_IND | ICM40600_GYR_BW_IND);
|
|
SPI_WRITE(REG_GYRO_CONFIG1, 0x1A); // default reset value
|
|
SPI_WRITE(REG_ACCEL_CONFIG1, 0x15); // default reset value
|
|
// turn off FIFO and sensors
|
|
SPI_WRITE(REG_FIFO_CONFIG, BIT_FIFO_MODE_BYPASS);
|
|
SPI_WRITE(REG_FIFO_CONFIG1, 0);
|
|
SPI_WRITE(REG_FIFO_CONFIG2, 0);
|
|
SPI_WRITE(REG_FIFO_CONFIG3, 0);
|
|
SPI_WRITE(REG_SMD_CONFIG, BIT_SMD_MODE_OFF);
|
|
SPI_WRITE(REG_PWR_MGMT_0, BIT_GYRO_MODE_OFF | BIT_ACCEL_MODE_OFF, 200); // 9136
|
|
sensorTurnOff(_task);
|
|
T(init_state) = INIT_DONE;
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, _task, __FUNCTION__);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void handleSpiDoneEvt(const void* evtData)
|
|
{
|
|
TDECL();
|
|
struct ICM40600Sensor *mSensor;
|
|
bool returnIdle = false;
|
|
uint32_t i;
|
|
const uint8_t *buf;
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
bool accelCalNewBiasAvailable;
|
|
struct TripleAxisDataPoint *sample;
|
|
float accelCalBiasX, accelCalBiasY, accelCalBiasZ;
|
|
bool fallThrough;
|
|
#endif
|
|
|
|
switch (GET_STATE()) {
|
|
case SENSOR_BOOT:
|
|
SET_STATE(SENSOR_VERIFY_ID);
|
|
SPI_READ(REG_WHO_AM_I, 1, &T(dataBuffer[0]));
|
|
spiBatchTxRx(&T(mode), sensorSpiCallback, _task, __FUNCTION__);
|
|
break;
|
|
case SENSOR_VERIFY_ID:
|
|
buf = T(dataBuffer[0]);
|
|
if (buf[1] != WHO_AM_I_ICM40604 && buf[1] != WHO_AM_I_ICM40605) {
|
|
ERROR_PRINT("chip not found (id=0x%x)\n", buf[1]);
|
|
break;
|
|
}
|
|
INFO_PRINT("chip found (id=0x%x)\n", buf[1]);
|
|
SET_STATE(SENSOR_INITIALIZING);
|
|
T(init_state) = RESET_ICM40600;
|
|
sensorInit(_task);
|
|
break;
|
|
case SENSOR_INITIALIZING:
|
|
if (T(init_state) == INIT_DONE) {
|
|
for (i = 0; i < NUM_OF_SENSOR; i++) {
|
|
sensorRegisterInitComplete(T(sensors[i]).handle);
|
|
}
|
|
returnIdle = true;
|
|
} else {
|
|
sensorInit(_task);
|
|
}
|
|
break;
|
|
case SENSOR_POWERING_UP:
|
|
mSensor = (struct ICM40600Sensor *)evtData;
|
|
sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, 1, 0);
|
|
returnIdle = true;
|
|
break;
|
|
case SENSOR_POWERING_DOWN:
|
|
mSensor = (struct ICM40600Sensor *)evtData;
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
if (mSensor->idx == ACC) {
|
|
// https://source.android.com/devices/sensors/sensor-types.html
|
|
// "The bias and scale calibration must only be updated while the sensor is deactivated,
|
|
// so as to avoid causing jumps in values during streaming."
|
|
accelCalNewBiasAvailable = accelCalUpdateBias(&T(accel_cal), &accelCalBiasX, &accelCalBiasY, &accelCalBiasZ);
|
|
// notify HAL about new accel bias calibration
|
|
if (accelCalNewBiasAvailable) {
|
|
fallThrough = true;
|
|
if (mSensor->data_evt->samples[0].firstSample.numSamples > 0) {
|
|
// flush existing samples so the bias appears after them
|
|
flushData(mSensor, EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(mSensorInfo[ACC].sensorType));
|
|
// try to allocate another data event and break if unsuccessful
|
|
if (!allocateDataEvt(mSensor, sensorGetTime())) {
|
|
fallThrough = false;
|
|
}
|
|
}
|
|
if (fallThrough) {
|
|
mSensor->data_evt->samples[0].firstSample.biasCurrent = true;
|
|
mSensor->data_evt->samples[0].firstSample.biasPresent = 1;
|
|
mSensor->data_evt->samples[0].firstSample.biasSample =
|
|
mSensor->data_evt->samples[0].firstSample.numSamples;
|
|
sample = &mSensor->data_evt->samples[mSensor->data_evt->samples[0].firstSample.numSamples++];
|
|
sample->x = accelCalBiasX;
|
|
sample->y = accelCalBiasY;
|
|
sample->z = accelCalBiasZ;
|
|
flushData(mSensor, sensorGetMyEventType(mSensorInfo[ACC].biasType));
|
|
DEBUG_PRINT("send new accel bias\n");
|
|
allocateDataEvt(mSensor, sensorGetTime());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, 0, 0);
|
|
returnIdle = true;
|
|
break;
|
|
case SENSOR_INT_1_HANDLING:
|
|
if (T(int_state) == INT_DONE) {
|
|
buf = T(dataBuffer[0]);
|
|
dispatchData(_task, &buf[1], T(fifo_count));
|
|
if (T(flush)) {
|
|
sendFlushEvt();
|
|
}
|
|
returnIdle = true;
|
|
} else {
|
|
int1Handling(_task);
|
|
}
|
|
break;
|
|
case SENSOR_CONFIG_CHANGING:
|
|
mSensor = (struct ICM40600Sensor *)evtData;
|
|
sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_RATE_CHG, mSensor->rate, mSensor->latency);
|
|
returnIdle = true;
|
|
break;
|
|
case SENSOR_CALIBRATING:
|
|
mSensor = (struct ICM40600Sensor *)evtData;
|
|
if (mSensor->idx == ACC) {
|
|
if (T(calibration_state) == CALIBRATION_DONE) {
|
|
INFO_PRINT("Accel Calibration done\n");
|
|
returnIdle = true;
|
|
} else {
|
|
calibrationHandling(_task, ACC);
|
|
}
|
|
} else if (mSensor->idx == GYR) {
|
|
if (T(calibration_state) == CALIBRATION_DONE) {
|
|
INFO_PRINT("Gyro Calibration done\n");
|
|
returnIdle = true;
|
|
} else {
|
|
calibrationHandling(_task, GYR);
|
|
}
|
|
}
|
|
break;
|
|
case SENSOR_TESTING:
|
|
mSensor = (struct ICM40600Sensor *)evtData;
|
|
if (mSensor->idx == ACC) {
|
|
if (T(selftest_state) == TEST_DONE) {
|
|
INFO_PRINT("Accel Selftest done\n");
|
|
returnIdle = true;
|
|
} else {
|
|
selfTestHandling(_task, ACC);
|
|
}
|
|
} else if (mSensor->idx == GYR) {
|
|
if (T(selftest_state) == TEST_DONE) {
|
|
INFO_PRINT("Gyro Selftest done\n");
|
|
returnIdle = true;
|
|
} else {
|
|
selfTestHandling(_task, GYR);
|
|
}
|
|
}
|
|
break;
|
|
case SENSOR_SAVE_CALIBRATION:
|
|
returnIdle = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (returnIdle) {
|
|
SET_STATE(SENSOR_IDLE);
|
|
processPendingEvt(_task);
|
|
}
|
|
}
|
|
|
|
#ifdef GYRO_CAL_ENABLED
|
|
static void processMagEvents(TASK, const struct TripleAxisDataEvent *evtData)
|
|
{
|
|
const struct SensorFirstSample * const first = &evtData->samples[0].firstSample;
|
|
const struct TripleAxisDataPoint *sample;
|
|
uint64_t ts = evtData->referenceTime;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < first->numSamples; ++i) {
|
|
sample = &evtData->samples[i];
|
|
if (i > 0) {
|
|
ts += sample->deltaTime;
|
|
}
|
|
gyroCalUpdateMag(&T(gyro_cal), ts, sample->x, sample->y, sample->z);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void handleEvent(uint32_t evtType, const void* evtData)
|
|
{
|
|
TDECL();
|
|
|
|
switch (evtType) {
|
|
case EVT_APP_START:
|
|
SET_STATE(SENSOR_BOOT);
|
|
osEventUnsubscribe(T(tid), EVT_APP_START);
|
|
#ifdef GYRO_CAL_ENABLED
|
|
osEventSubscribe(T(tid), EVT_SENSOR_MAG_DATA_RDY);
|
|
#endif
|
|
// fall through
|
|
case EVT_SPI_DONE:
|
|
handleSpiDoneEvt(evtData);
|
|
break;
|
|
case EVT_SENSOR_INTERRUPT_1:
|
|
int1Evt(_task, false);
|
|
break;
|
|
#ifdef GYRO_CAL_ENABLED
|
|
case EVT_SENSOR_MAG_DATA_RDY:
|
|
if (evtData != SENSOR_DATA_EVENT_FLUSH) {
|
|
processMagEvents(_task, (const struct TripleAxisDataEvent *)evtData);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void initSensorStruct(struct ICM40600Sensor *sensor, enum SensorIndex idx)
|
|
{
|
|
sensor->data_evt = NULL;
|
|
sensor->rate = 0;
|
|
sensor->latency = 0;
|
|
sensor->decimator = 1;
|
|
sensor->data_cnt = 0;
|
|
sensor->prev_rtc_time = 0;
|
|
sensor->skip_sample_cnt = 0;
|
|
sensor->data[0] = 0;
|
|
sensor->data[1] = 0;
|
|
sensor->data[2] = 0;
|
|
sensor->offset[0] = 0;
|
|
sensor->offset[1] = 0;
|
|
sensor->offset[2] = 0;
|
|
sensor->updated = false;
|
|
sensor->powered = false;
|
|
sensor->configed = false;
|
|
sensor->wait_for_odr = false;
|
|
sensor->idx = idx;
|
|
sensor->flush = 0;
|
|
}
|
|
|
|
static bool startTask(uint32_t task_id)
|
|
{
|
|
TDECL();
|
|
enum SensorIndex i;
|
|
size_t slabSize;
|
|
|
|
time_init(_task);
|
|
|
|
T(tid) = task_id;
|
|
|
|
T(Int1) = gpioRequest(ICM40600_INT1_PIN);
|
|
T(Irq1) = ICM40600_INT1_IRQ;
|
|
T(Isr1).func = icm40600Isr1;
|
|
T(Int1_EN) = false;
|
|
T(pending_int[0]) = false;
|
|
T(pending_flush) = false;
|
|
T(pending_calibration_save) = false;
|
|
|
|
T(mode).speed = ICM40600_SPI_DEFAULT_SPEED_HZ;
|
|
T(mode).bitsPerWord = 8;
|
|
T(mode).cpol = SPI_CPOL_IDLE_HI;
|
|
T(mode).cpha = SPI_CPHA_TRAILING_EDGE;
|
|
T(mode).nssChange = true;
|
|
T(mode).format = SPI_FORMAT_MSB_FIRST;
|
|
T(cs) = GPIO_PB(12);
|
|
T(config).accel_rate = 0;
|
|
T(config).gyro_rate = 0;
|
|
T(config).fifo_rate = 0;
|
|
T(config).wom_threshold = 0;
|
|
T(config).fifo_watermark = 0;
|
|
T(config).fifo_sample_size = 0;
|
|
T(config).accel_on = false;
|
|
T(config).gyro_on = false;
|
|
T(config).wom_on = false;
|
|
T(noMotionTimerHandle) = 0;
|
|
T(fifo_count) = 0;
|
|
T(powered) = false;
|
|
T(flush) = false;
|
|
T(data_timestamp) = 0;
|
|
T(chip_time_us) = 0;
|
|
T(last_sync_time) = 0;
|
|
T(last_sync_data_ts) = 0;
|
|
T(chip_timestamp) = 0;
|
|
T(fifo_start_sync) = false;
|
|
T(chip_temperature) = TEMP_OFFSET;
|
|
|
|
spiMasterRequest(ICM40600_SPI_BUS_ID, &T(spiDev));
|
|
|
|
for (i = 0; i < NUM_OF_SENSOR; i++) {
|
|
initSensorStruct(&T(sensors[i]), i);
|
|
T(sensors[i]).handle = sensorRegister(&mSensorInfo[i], &mSensorOps[i], (void *)i, false);
|
|
T(pending_config[i]) = false;
|
|
}
|
|
|
|
osEventSubscribe(T(tid), EVT_APP_START);
|
|
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
// Init Accel Cal
|
|
accelCalInit(&T(accel_cal),
|
|
800000000, // Stillness Time in ns (0.8s)
|
|
5, // Minimum Sample Number
|
|
0.00025, // Threshold
|
|
15, // nx bucket count
|
|
15, // nxb bucket count
|
|
15, // ny bucket count
|
|
15, // nyb bucket count
|
|
15, // nz bucket count
|
|
15, // nzb bucket count
|
|
15); // nle bucket count
|
|
#endif
|
|
#ifdef GYRO_CAL_ENABLED
|
|
// Init Gyro Cal
|
|
gyroCalInit(&T(gyro_cal),
|
|
SEC_TO_NANOS(5.0f), // Min stillness period = 5.0 seconds
|
|
SEC_TO_NANOS(5.9f), // Max stillness period = 6.0 seconds (NOTE 1)
|
|
0, 0, 0, // Initial bias offset calibration
|
|
0, // Time stamp of initial bias calibration
|
|
SEC_TO_NANOS(1.5f), // Analysis window length = 1.5 seconds
|
|
7.5e-5f, // Gyroscope variance threshold [rad/sec]^2
|
|
1.5e-5f, // Gyroscope confidence delta [rad/sec]^2
|
|
4.5e-3f, // Accelerometer variance threshold [m/sec^2]^2
|
|
9.0e-4f, // Accelerometer confidence delta [m/sec^2]^2
|
|
5.0f, // Magnetometer variance threshold [uT]^2
|
|
1.0f, // Magnetometer confidence delta [uT]^2
|
|
0.95f, // Stillness threshold [0,1]
|
|
40.0f * MDEG_TO_RAD, // Stillness mean variation limit [rad/sec]
|
|
1.5f, // Max temperature delta during stillness [C]
|
|
true); // Gyro calibration enable
|
|
// NOTE 1: This parameter is set to 5.9 seconds to achieve a max stillness
|
|
// period of 6.0 seconds and avoid buffer boundary conditions that could push
|
|
// the max stillness to the next multiple of the analysis window length
|
|
// (i.e., 7.5 seconds).
|
|
#endif
|
|
|
|
slabSize = sizeof(struct TripleAxisDataEvent) +
|
|
MAX_NUM_COMMS_EVENT_SAMPLES * sizeof(struct TripleAxisDataPoint);
|
|
// TODO: need to investigate slab items number
|
|
T(mDataSlab) = slabAllocatorNew(slabSize, 4, 20);
|
|
if (!T(mDataSlab)) {
|
|
ERROR_PRINT("slabAllocatorNew() failed\n");
|
|
return false;
|
|
}
|
|
T(mWbufCnt) = 0;
|
|
T(mRegCnt) = 0;
|
|
T(spiInUse) = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void endTask(void)
|
|
{
|
|
TDECL();
|
|
|
|
#ifdef ACCEL_CAL_ENABLED
|
|
accelCalDestroy(&T(accel_cal));
|
|
#endif
|
|
slabAllocatorDestroy(T(mDataSlab));
|
|
spiMasterRelease(T(spiDev));
|
|
|
|
// disable and release interrupt.
|
|
disableInterrupt(T(Int1), T(Irq1), &T(Isr1));
|
|
gpioRelease(T(Int1));
|
|
}
|
|
|
|
INTERNAL_APP_INIT(ICM40600_APP_ID, ICM40600_APP_VERSION, startTask, endTask, handleEvent);
|