Compare commits

...

1 Commits

Author SHA1 Message Date
Daniel Agar 52e98f99f4 sensors/vehicle_imu: use accel & gyro FIFO if available 2022-09-21 21:06:09 -04:00
164 changed files with 6052 additions and 5902 deletions
+1 -2
View File
@@ -903,12 +903,11 @@ void printTopics() {
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener rate_ctrl_status" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener safety" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_accel" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_accel_fifo" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_imu_fifo" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_baro" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_combined" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_gyro" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_gyro_fft" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_gyro_fifo" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_mag" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_preflight_mag" || true'
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-*` --cmd "listener sensor_selection" || true'
+1 -2
View File
@@ -144,7 +144,6 @@ set(msg_files
rtl_time_estimate.msg
satellite_info.msg
sensor_accel.msg
sensor_accel_fifo.msg
sensor_baro.msg
sensor_combined.msg
sensor_correction.msg
@@ -152,8 +151,8 @@ set(msg_files
sensor_gps.msg
sensor_gyro.msg
sensor_gyro_fft.msg
sensor_gyro_fifo.msg
sensor_hygrometer.msg
sensor_imu_fifo.msg
sensor_mag.msg
sensor_optical_flow.msg
sensor_preflight_mag.msg
+2 -4
View File
@@ -7,12 +7,10 @@ float32 x # acceleration in the FRD board frame X-axis in m/s^2
float32 y # acceleration in the FRD board frame Y-axis in m/s^2
float32 z # acceleration in the FRD board frame Z-axis in m/s^2
float32 range # TODO
float32 temperature # temperature in degrees Celsius
uint32 error_count
uint8[3] clip_counter # clip count per axis in the sample period
uint8 samples # number of raw samples that went into this message
uint8 ORB_QUEUE_LENGTH = 8
-13
View File
@@ -1,13 +0,0 @@
uint64 timestamp # time since system start (microseconds)
uint64 timestamp_sample
uint32 device_id # unique device ID for the sensor that does not change between power cycles
float32 dt # delta time between samples (microseconds)
float32 scale
uint8 samples # number of valid samples
int16[32] x # acceleration in the FRD board frame X-axis in m/s^2
int16[32] y # acceleration in the FRD board frame Y-axis in m/s^2
int16[32] z # acceleration in the FRD board frame Z-axis in m/s^2
+2 -4
View File
@@ -7,12 +7,10 @@ float32 x # angular velocity in the FRD board frame X-axis in ra
float32 y # angular velocity in the FRD board frame Y-axis in rad/s
float32 z # angular velocity in the FRD board frame Z-axis in rad/s
float32 range # dynamic range in rad/s
float32 temperature # temperature in degrees Celsius
uint32 error_count
uint8[3] clip_counter # clip count per axis in the sample period
uint8 samples # number of raw samples that went into this message
uint8 ORB_QUEUE_LENGTH = 8
-15
View File
@@ -1,15 +0,0 @@
uint64 timestamp # time since system start (microseconds)
uint64 timestamp_sample
uint32 device_id # unique device ID for the sensor that does not change between power cycles
float32 dt # delta time between samples (microseconds)
float32 scale
uint8 samples # number of valid samples
int16[32] x # angular velocity in the FRD board frame X-axis in rad/s
int16[32] y # angular velocity in the FRD board frame Y-axis in rad/s
int16[32] z # angular velocity in the FRD board frame Z-axis in rad/s
uint8 ORB_QUEUE_LENGTH = 4
+26
View File
@@ -0,0 +1,26 @@
uint64 timestamp # time since system start (microseconds)
uint64 timestamp_sample
uint32 device_id # unique device ID for the sensor that does not change between power cycles
float32 dt # delta time between samples (microseconds)
uint8 samples # number of valid samples
float32 accel_scale
float32 gyro_scale
uint8 FIFO_SIZE = 24
int16[24] accel_x # gyro in the FRD board frame X-axis
int16[24] accel_y # gyro in the FRD board frame Y-axis
int16[24] accel_z # gyro in the FRD board frame Z-axis
int16[24] gyro_x # gyro in the FRD board frame X-axis
int16[24] gyro_y # gyro in the FRD board frame Y-axis
int16[24] gyro_z # gyro in the FRD board frame Z-axis
float32 temperature # temperature in degrees Celsius
uint32 error_count
uint8 ORB_QUEUE_LENGTH = 8
@@ -49,7 +49,7 @@ logger off
sensors status
listener sensor_gyro
listener sensor_gyro_fifo
listener sensor_imu_fifo
listener sensor_gyro_fft
perf
+27 -8
View File
@@ -56,8 +56,7 @@ using namespace time_literals;
ADIS16477::ADIS16477(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation),
_rotation(config.rotation),
_sample_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": read")),
_bad_transfers(perf_alloc(PC_COUNT, MODULE_NAME": bad transfers")),
_drdy_gpio(config.drdy_gpio)
@@ -67,8 +66,8 @@ ADIS16477::ADIS16477(const I2CSPIDriverConfig &config) :
px4_arch_configgpio(GPIO_SPI1_RESET_ADIS16477);
#endif // GPIO_SPI1_RESET_ADIS16477
_px4_accel.set_scale(1.25f * CONSTANTS_ONE_G / 1000.0f); // accel 1.25 mg/LSB
_px4_gyro.set_scale(math::radians(0.025f)); // gyro 0.025 °/sec/LSB
_accel_scale = 1.25f * CONSTANTS_ONE_G / 1000.0f; // accel 1.25 mg/LSB
_gyro_scale = math::radians(0.025f); // gyro 0.025 °/sec/LSB
}
ADIS16477::~ADIS16477()
@@ -366,11 +365,31 @@ ADIS16477::measure()
// temperature 1 LSB = 0.1°C
const float temperature = adis_report.temp * 0.1f;
_px4_accel.set_temperature(temperature);
_px4_gyro.set_temperature(temperature);
_px4_accel.update(timestamp_sample, adis_report.accel_x, adis_report.accel_y, adis_report.accel_z);
_px4_gyro.update(timestamp_sample, adis_report.gyro_x, adis_report.gyro_y, adis_report.gyro_z);
// sensor_accel
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
sensor_accel.x = adis_report.accel_x;
sensor_accel.y = adis_report.accel_y;
sensor_accel.z = adis_report.accel_z;
sensor_accel.range = 16.f * CONSTANTS_ONE_G;
sensor_accel.temperature = temperature;
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
// sensor_gyro
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
sensor_gyro.x = adis_report.gyro_x;
sensor_gyro.y = adis_report.gyro_y;
sensor_gyro.z = adis_report.gyro_z;
sensor_gyro.range = math::radians(2000.f);
sensor_gyro.temperature = temperature;
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
perf_end(_sample_perf);
-5
View File
@@ -41,8 +41,6 @@
#include <drivers/device/spi.h>
#include <geo/geo.h>
#include <lib/conversion/rotation.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <perf/perf_counter.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/i2c_spi_buses.h>
@@ -66,9 +64,6 @@ protected:
private:
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
perf_counter_t _sample_perf;
perf_counter_t _bad_transfers;
-2
View File
@@ -40,6 +40,4 @@ px4_add_module(
adis16477_main.cpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
)
+34 -17
View File
@@ -73,8 +73,7 @@ using namespace time_literals;
ADIS16497::ADIS16497(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation),
_rotation(config.rotation),
_sample_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": read")),
_bad_transfers(perf_alloc(PC_COUNT, MODULE_NAME": bad transfers")),
_drdy_gpio(config.drdy_gpio)
@@ -308,23 +307,23 @@ ADIS16497::self_test()
bool
ADIS16497::set_measurement_range(uint16_t model)
{
_px4_accel.set_scale(1.25f * CONSTANTS_ONE_G / 1000.0f); // 1.25 mg/LSB
_px4_accel.set_range(40.0f * CONSTANTS_ONE_G); // 40g
_accel_scale = 1.25f * CONSTANTS_ONE_G / 1000.0f; // 1.25 mg/LSB
_accel_range = 40.0f * CONSTANTS_ONE_G; // 40g
switch (model) {
case RANG_MDL_1BMLZ:
_px4_gyro.set_scale(math::radians(0.00625f)); // 0.00625 °/sec/LSB
_px4_gyro.set_range(math::radians(125.0f)); // 125 °/s
_gyro_scale = math::radians(0.00625f); // 0.00625 °/sec/LSB
_gyro_range = math::radians(125.0f); // 125 °/s
break;
case RANG_MDL_2BMLZ:
_px4_gyro.set_scale(math::radians(0.025f)); // 0.025 °/sec/LSB
_px4_gyro.set_range(math::radians(450.0f)); // 450 °/s
_gyro_scale = math::radians(0.025f); // 0.025 °/sec/LSB
_gyro_range = math::radians(450.0f); // 450 °/s
break;
case RANG_MDL_3BMLZ:
_px4_gyro.set_scale(math::radians(0.1f)); // 0.1 °/sec/LSB
_px4_gyro.set_range(math::radians(2000.0f)); // 2000 °/s
_gyro_scale = math::radians(0.1f); // 0.1 °/sec/LSB
_gyro_range = math::radians(2000.0f); // 2000 °/s
break;
default:
@@ -470,26 +469,44 @@ ADIS16497::measure()
}
const uint64_t error_count = perf_event_count(_bad_transfers);
_px4_accel.set_error_count(error_count);
_px4_gyro.set_error_count(error_count);
const float temperature = (int16_t(adis_report.TEMP_OUT) * 0.0125f) + 25.0f; // 1 LSB = 0.0125°C, 0x0000 at 25°C
_px4_accel.set_temperature(temperature);
_px4_gyro.set_temperature(temperature);
// TODO check data counter here to see if we're missing samples/getting repeated samples
{
float xraw_f = (int32_t(adis_report.X_ACCEL_OUT) << 16 | adis_report.X_ACCEL_LOW) / 65536.0f;
float yraw_f = (int32_t(adis_report.Y_ACCEL_OUT) << 16 | adis_report.Y_ACCEL_LOW) / 65536.0f;
float zraw_f = (int32_t(adis_report.Z_ACCEL_OUT) << 16 | adis_report.Z_ACCEL_LOW) / 65536.0f;
_px4_accel.update(timestamp_sample, xraw_f, yraw_f, zraw_f);
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
sensor_accel.x = xraw_f;
sensor_accel.y = yraw_f;
sensor_accel.z = zraw_f;
sensor_accel.range = 16.f * CONSTANTS_ONE_G;
sensor_accel.temperature = temperature;
sensor_accel.error_count = error_count;
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
}
{
float xraw_f = (int32_t(adis_report.X_GYRO_OUT) << 16 | adis_report.X_GYRO_LOW) / 65536.0f;
float yraw_f = (int32_t(adis_report.Y_GYRO_OUT) << 16 | adis_report.Y_GYRO_LOW) / 65536.0f;
float zraw_f = (int32_t(adis_report.Z_GYRO_OUT) << 16 | adis_report.Z_GYRO_LOW) / 65536.0f;
_px4_gyro.update(timestamp_sample, xraw_f, yraw_f, zraw_f);
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
sensor_gyro.x = xraw_f;
sensor_gyro.y = yraw_f;
sensor_gyro.z = zraw_f;
sensor_gyro.range = math::radians(2000.f);
sensor_gyro.temperature = temperature;
sensor_gyro.error_count = error_count;
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
}
perf_end(_sample_perf);
-5
View File
@@ -41,8 +41,6 @@
#include <drivers/device/spi.h>
#include <geo/geo.h>
#include <lib/conversion/rotation.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <perf/perf_counter.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/i2c_spi_buses.h>
@@ -102,9 +100,6 @@ protected:
private:
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
perf_counter_t _sample_perf;
perf_counter_t _bad_transfers;
+1 -2
View File
@@ -40,6 +40,5 @@ px4_add_module(
ADIS16497.hpp
adis16497_main.cpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2021-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -99,9 +99,8 @@ ADIS16448::ADIS16448(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio), // TODO: DRDY disabled
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation),
_px4_mag(get_device_id(), config.rotation)
_px4_mag(get_device_id(), config.rotation),
_rotation(config.rotation)
{
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
@@ -392,15 +391,10 @@ void ADIS16448::RunImpl()
if (publish_data) {
const uint32_t error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf);
_px4_accel.set_error_count(error_count);
_px4_gyro.set_error_count(error_count);
// temperature 0.07386°C/LSB, 31°C = 0x000
const float temperature = (convert12BitToINT16(buffer.TEMP_OUT) * 0.07386f) + 31.f;
_px4_accel.set_temperature(temperature);
_px4_gyro.set_temperature(temperature);
bool imu_updated = false;
// sensor's frame is +x forward, +y left, +z up
@@ -430,8 +424,29 @@ void ADIS16448::RunImpl()
}
if (imu_updated) {
_px4_accel.update(timestamp_sample, accel_x, accel_y, accel_z);
_px4_gyro.update(timestamp_sample, gyro_x, gyro_y, gyro_z);
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
sensor_accel.x = accel_x;
sensor_accel.y = accel_y;
sensor_accel.z = accel_z;
sensor_accel.range = 16.f * CONSTANTS_ONE_G;
sensor_accel.temperature = temperature;
sensor_accel.error_count = error_count;
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
sensor_gyro.x = gyro_x;
sensor_gyro.y = gyro_y;
sensor_gyro.z = gyro_z;
sensor_gyro.range = math::radians(2000.f);
sensor_gyro.temperature = temperature;
sensor_gyro.error_count = error_count;
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
}
// DIAG_STAT bit 7: New data, xMAGN_OUT/BARO_OUT
@@ -534,12 +549,12 @@ bool ADIS16448::Configure()
}
}
_px4_accel.set_scale(0.833f * 1e-3f * CONSTANTS_ONE_G); // 0.833 mg/LSB
_px4_gyro.set_scale(math::radians(0.04f)); // 0.04 °/sec/LSB
_accel_scale = 0.833f * 1e-3f * CONSTANTS_ONE_G; // 0.833 mg/LSB
_gyro_scale = math::radians(0.04f); // 0.04 °/sec/LSB
_px4_mag.set_scale(142.9f * 1e-6f); // μgauss/LSB
_px4_accel.set_range(18.f * CONSTANTS_ONE_G);
_px4_gyro.set_range(math::radians(1000.f));
_accel_range = 18.f * CONSTANTS_ONE_G;
_gyro_range = math::radians(1000.f);
return success;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2021-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -44,16 +44,17 @@
#include <drivers/drv_hrt.h>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/drivers/magnetometer/PX4Magnetometer.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_baro.h>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_accel.h>
#include <uORB/topics/sensor_baro.h>
#include <uORB/topics/sensor_gyro.h>
using namespace Analog_Devices_ADIS16448;
class ADIS16448 : public device::SPI, public I2CSPIDriver<ADIS16448>
@@ -98,11 +99,17 @@ private:
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
PX4Magnetometer _px4_mag;
uORB::PublicationMulti<sensor_accel_s> _sensor_accel_pub{ORB_ID(sensor_accel)};
uORB::PublicationMulti<sensor_baro_s> _sensor_baro_pub{ORB_ID(sensor_baro)};
uORB::PublicationMulti<sensor_gyro_s> _sensor_gyro_pub{ORB_ID(sensor_gyro)};
const enum Rotation _rotation;
float _accel_scale{0.f};
float _accel_range{0.f};
float _gyro_scale{0.f};
float _gyro_range{0.f};
perf_counter_t _reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": reset")};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
@@ -43,8 +43,6 @@ px4_add_module(
adis16448_main.cpp
Analog_Devices_ADIS16448_registers.hpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
drivers_magnetometer
px4_work_queue
)
@@ -44,8 +44,7 @@ ADIS16470::ADIS16470(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
@@ -296,8 +295,6 @@ void ADIS16470::RunImpl()
// temperature 1 LSB = 0.1°C
const float temperature = buffer.TEMP_OUT * 0.1f;
_px4_accel.set_temperature(temperature);
_px4_gyro.set_temperature(temperature);
int16_t accel_x = buffer.X_ACCL_OUT;
@@ -306,10 +303,17 @@ void ADIS16470::RunImpl()
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel_y = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel_z = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
_px4_accel.update(timestamp_sample, accel_x, accel_y, accel_z);
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
sensor_accel.x = accel_x;
sensor_accel.y = math::negate(accel_y);
sensor_accel.z = math::negate(accel_z);
sensor_accel.range = 16.f * CONSTANTS_ONE_G;
sensor_accel.temperature = temperature;
sensor_accel.error_count = error_count;
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
int16_t gyro_x = buffer.X_GYRO_OUT;
@@ -317,9 +321,17 @@ void ADIS16470::RunImpl()
int16_t gyro_z = buffer.Z_GYRO_OUT;
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro_y = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro_z = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
_px4_gyro.update(timestamp_sample, gyro_x, gyro_y, gyro_z);
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
sensor_gyro.x = gyro_x;
sensor_gyro.y = math::negate(gyro_y);
sensor_gyro.z = math::negate(gyro_z);
sensor_gyro.range = math::radians(2000.f);
sensor_gyro.temperature = temperature;
sensor_gyro.error_count = error_count;
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
success = true;
@@ -376,12 +388,12 @@ bool ADIS16470::Configure()
}
// accel: ±40 g, 800 LSB/g (16-bit format)
_px4_accel.set_range(40.f * CONSTANTS_ONE_G);
_px4_accel.set_scale(CONSTANTS_ONE_G / 800.f); // scaling 800 LSB/g -> m/s^2 per LSB
//_accel_range = 40.f * CONSTANTS_ONE_G;
_accel_scale = CONSTANTS_ONE_G / 800.f; // scaling 800 LSB/g -> m/s^2 per LSB
// gyro: ±2000 °/sec, 10 LSB/°/sec (16-bit format)
_px4_gyro.set_range(math::radians(2000.f));
_px4_gyro.set_scale(math::radians(1.f / 10.f)); // scaling 10 LSB/°/sec -> rad/s per LSB
//_gyro_range = math::radians(2000.f);
_gyro_scale = math::radians(1.f / 10.f); // scaling 10 LSB/°/sec -> rad/s per LSB
return success;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2021-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "Analog_Devices_ADIS16470_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace Analog_Devices_ADIS16470;
class ADIS16470 : public device::SPI, public I2CSPIDriver<ADIS16470>
@@ -94,8 +95,11 @@ private:
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
float _accel_scale{0.f};
float _gyro_scale{0.f};
perf_counter_t _reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": reset")};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
@@ -35,13 +35,12 @@ px4_add_module(
MODULE drivers__imu__analog_devices__adis16470
MAIN adis16470
COMPILE_FLAGS
-Wno-error
SRCS
ADIS16470.cpp
ADIS16470.hpp
adis16470_main.cpp
Analog_Devices_ADIS16470_registers.hpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -42,13 +42,13 @@ namespace Bosch::BMI055::Accelerometer
BMI055_Accelerometer::BMI055_Accelerometer(const I2CSPIDriverConfig &config) :
BMI055(config),
_px4_accel(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_accel: DRDY missed");
}
ConfigureSampleRate(_px4_accel.get_max_rate_hz());
ConfigureSampleRate(RATE);
}
BMI055_Accelerometer::~BMI055_Accelerometer()
@@ -121,8 +121,8 @@ void BMI055_Accelerometer::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -162,62 +162,78 @@ void BMI055_Accelerometer::RunImpl()
break;
case STATE::FIFO_READ: {
hrt_abstime timestamp_sample = 0;
hrt_abstime timestamp_sample = now;
uint8_t samples = 0;
if (_data_ready_interrupt_enabled) {
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if ((drdy_timestamp_sample != 0) && (now < drdy_timestamp_sample + _fifo_empty_interval_us)) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_samples;
} else {
perf_count(_drdy_missed_perf);
}
// push backup schedule back
ScheduleDelayed(_fifo_empty_interval_us * 2);
ScheduleDelayed(_fifo_empty_interval_us * 3);
}
// always check current FIFO status/count
bool success = false;
const uint8_t FIFO_STATUS = RegisterRead(Register::FIFO_STATUS);
if (samples == 0) {
// always check current FIFO status/count
const uint8_t FIFO_STATUS = RegisterRead(Register::FIFO_STATUS);
if (FIFO_STATUS & FIFO_STATUS_BIT::fifo_overrun) {
FIFOReset();
perf_count(_fifo_overflow_perf);
} else {
const uint8_t fifo_frame_counter = FIFO_STATUS & FIFO_STATUS_BIT::fifo_frame_counter;
if (fifo_frame_counter > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
if (FIFO_STATUS & FIFO_STATUS_BIT::fifo_overrun) {
FIFOReset();
perf_count(_fifo_overflow_perf);
} else if (fifo_frame_counter == 0) {
perf_count(_fifo_empty_perf);
} else {
const uint8_t fifo_frame_counter = FIFO_STATUS & FIFO_STATUS_BIT::fifo_frame_counter;
} else if (fifo_frame_counter >= 1) {
if (fifo_frame_counter > FIFO_MAX_SAMPLES) {
FIFOReset();
perf_count(_fifo_overflow_perf);
uint8_t samples = fifo_frame_counter;
} else if (fifo_frame_counter == 0) {
perf_count(_fifo_empty_perf);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_samples + 1) {
// sample timestamp set from data ready already corresponds to _fifo_samples
if (timestamp_sample == 0) {
timestamp_sample = now - static_cast<int>(FIFO_SAMPLE_DT);
} else if (fifo_frame_counter >= 1) {
samples = fifo_frame_counter;
if (samples > _fifo_samples) {
// grab desired number of samples, but reschedule next cycle sooner
const int extra_samples = samples - _fifo_samples;
samples = _fifo_samples;
if (_fifo_samples > extra_samples) {
// reschedule to run when a total of _fifo_gyro_samples should be available in the FIFO
const uint32_t reschedule_delay_us = (_fifo_samples - extra_samples) * static_cast<int>(FIFO_SAMPLE_DT);
ScheduleOnInterval(_fifo_empty_interval_us, reschedule_delay_us);
} else {
// otherwise reschedule to run immediately
ScheduleOnInterval(_fifo_empty_interval_us);
}
} else if (samples < _fifo_samples) {
// reschedule next cycle to catch the desired number of samples
ScheduleOnInterval(_fifo_empty_interval_us, (_fifo_samples - samples) * static_cast<int>(FIFO_SAMPLE_DT));
}
samples--;
}
}
}
if (FIFORead((timestamp_sample == 0) ? now : timestamp_sample, samples)) {
success = true;
bool success = false;
if (_failure_count > 0) {
_failure_count--;
}
if (samples == _fifo_samples) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
@@ -227,13 +243,14 @@ void BMI055_Accelerometer::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_cfg[_checked_register])) {
_last_config_check_timestamp = now;
_checked_register = (_checked_register + 1) % size_register_cfg;
@@ -241,6 +258,7 @@ void BMI055_Accelerometer::RunImpl()
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
PX4_DEBUG("Force reset because register 0x%02hhX check failed ", (uint8_t)_register_cfg[_checked_register].reg);
Reset();
}
@@ -263,23 +281,23 @@ void BMI055_Accelerometer::ConfigureAccel()
switch (PMU_RANGE) {
case range_2g_set:
_px4_accel.set_scale(CONSTANTS_ONE_G / 1024.f); // 1024 LSB/g, 0.98mg/LSB
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G / 1024.f; // 1024 LSB/g, 0.98mg/LSB
_accel_range = 2.f * CONSTANTS_ONE_G;
break;
case range_4g_set:
_px4_accel.set_scale(CONSTANTS_ONE_G / 512.f); // 512 LSB/g, 1.95mg/LSB
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G / 512.f; // 512 LSB/g, 1.95mg/LSB
_accel_range = 4.f * CONSTANTS_ONE_G;
break;
case range_8g_set:
_px4_accel.set_scale(CONSTANTS_ONE_G / 256.f); // 256 LSB/g, 3.91mg/LSB
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G / 256.f; // 256 LSB/g, 3.91mg/LSB
_accel_range = 8.f * CONSTANTS_ONE_G;
break;
case range_16g_set:
_px4_accel.set_scale(CONSTANTS_ONE_G / 128.f); // 128 LSB/g, 7.81mg/LSB
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G / 128.f; // 128 LSB/g, 7.81mg/LSB
_accel_range = 16.f * CONSTANTS_ONE_G;
break;
}
}
@@ -349,7 +367,7 @@ bool BMI055_Accelerometer::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0);
}
bool BMI055_Accelerometer::DataReadyInterruptDisable()
@@ -358,7 +376,7 @@ bool BMI055_Accelerometer::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
bool BMI055_Accelerometer::RegisterCheck(const register_config_t &reg_cfg)
@@ -407,40 +425,66 @@ void BMI055_Accelerometer::RegisterSetAndClearBits(Register reg, uint8_t setbits
bool BMI055_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 1, FIFO::SIZE);
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_DATA) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + (samples * FIFO::DATA)
const size_t transfer_size = 1 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = samples;
accel.dt = FIFO_SAMPLE_DT;
if (samples > 0) {
for (int i = 0; i < samples; i++) {
const FIFO::DATA &fifo_sample = buffer.f[i];
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
// acc_x_msb<11:4> + acc_x_lsb<3:0>
const int16_t accel_x = combine(fifo_sample.ACCD_X_MSB, fifo_sample.ACCD_X_LSB) >> 4;
const int16_t accel_y = combine(fifo_sample.ACCD_Y_MSB, fifo_sample.ACCD_Y_LSB) >> 4;
const int16_t accel_z = combine(fifo_sample.ACCD_Z_MSB, fifo_sample.ACCD_Z_LSB) >> 4;
float accel_sum[3] {};
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[i] = accel_x;
accel.y[i] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[i] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
for (int i = 0; i < samples; i++) {
const FIFO::DATA &fifo_sample = buffer.f[i];
// acc_x_msb<11:4> + acc_x_lsb<3:0>
int16_t accel_x = combine(fifo_sample.ACCD_X_MSB, fifo_sample.ACCD_X_LSB) >> 4;
int16_t accel_y = combine(fifo_sample.ACCD_Y_MSB, fifo_sample.ACCD_Y_LSB) >> 4;
int16_t accel_z = combine(fifo_sample.ACCD_Z_MSB, fifo_sample.ACCD_Z_LSB) >> 4;
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
// accel_x = accel_x;
accel_y = math::negate(accel_y);
accel_z = math::negate(accel_z);
accel_sum[0] += accel_x;
accel_sum[1] += accel_y;
accel_sum[2] += accel_z;
}
sensor_accel.x = (accel_sum[0] / samples) * _accel_scale;
sensor_accel.y = (accel_sum[1] / samples) * _accel_scale;
sensor_accel.z = (accel_sum[2] / samples) * _accel_scale;
rotate_3f(_rotation, sensor_accel.x, sensor_accel.y, sensor_accel.z);
sensor_accel.range = _accel_range;
sensor_accel.temperature = _temperature;
sensor_accel.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
return true;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_accel.updateFIFO(accel);
return true;
return false;
}
void BMI055_Accelerometer::FIFOReset()
@@ -472,7 +516,7 @@ void BMI055_Accelerometer::UpdateTemperature()
float temperature = static_cast<int8_t>(RegisterRead(Register::ACCD_TEMP)) * 0.5f + 23.f;
if (PX4_ISFINITE(temperature)) {
_px4_accel.set_temperature(temperature);
_temperature = temperature;
} else {
perf_count(_bad_transfer_perf);
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,10 +35,11 @@
#include "BMI055.hpp"
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include "Bosch_BMI055_Accelerometer_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_accel.h>
namespace Bosch::BMI055::Accelerometer
{
@@ -58,15 +59,7 @@ private:
static constexpr uint32_t RATE{2000}; // 2000 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]))};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_DATA) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (1 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
struct register_config_t {
Register reg;
@@ -97,7 +90,13 @@ private:
void UpdateTemperature();
PX4Accelerometer _px4_accel;
uORB::PublicationMulti<sensor_accel_s> _sensor_accel_pub{ORB_ID(sensor_accel)};
const enum Rotation _rotation;
float _accel_scale{0.f};
float _accel_range{0.f};
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad transfer")};
+102 -66
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,8 +33,6 @@
#include "BMI055_Gyroscope.hpp"
#include <px4_platform/board_dma_alloc.h>
using namespace time_literals;
namespace Bosch::BMI055::Gyroscope
@@ -42,13 +40,13 @@ namespace Bosch::BMI055::Gyroscope
BMI055_Gyroscope::BMI055_Gyroscope(const I2CSPIDriverConfig &config) :
BMI055(config),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_gyro: DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
ConfigureSampleRate(RATE);
}
BMI055_Gyroscope::~BMI055_Gyroscope()
@@ -121,8 +119,8 @@ void BMI055_Gyroscope::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -162,62 +160,78 @@ void BMI055_Gyroscope::RunImpl()
break;
case STATE::FIFO_READ: {
hrt_abstime timestamp_sample = 0;
hrt_abstime timestamp_sample = now;
uint8_t samples = 0;
if (_data_ready_interrupt_enabled) {
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if ((drdy_timestamp_sample != 0) && (now < drdy_timestamp_sample + _fifo_empty_interval_us)) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_samples;
} else {
perf_count(_drdy_missed_perf);
}
// push backup schedule back
ScheduleDelayed(_fifo_empty_interval_us * 2);
ScheduleDelayed(_fifo_empty_interval_us * 3);
}
// always check current FIFO status/count
bool success = false;
const uint8_t FIFO_STATUS = RegisterRead(Register::FIFO_STATUS);
if (samples == 0) {
// always check current FIFO status/count
const uint8_t FIFO_STATUS = RegisterRead(Register::FIFO_STATUS);
if (FIFO_STATUS & FIFO_STATUS_BIT::fifo_overrun) {
FIFOReset();
perf_count(_fifo_overflow_perf);
} else {
const uint8_t fifo_frame_counter = FIFO_STATUS & FIFO_STATUS_BIT::fifo_frame_counter;
if (fifo_frame_counter > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
if (FIFO_STATUS & FIFO_STATUS_BIT::fifo_overrun) {
FIFOReset();
perf_count(_fifo_overflow_perf);
} else if (fifo_frame_counter == 0) {
perf_count(_fifo_empty_perf);
} else {
const uint8_t fifo_frame_counter = FIFO_STATUS & FIFO_STATUS_BIT::fifo_frame_counter;
} else if (fifo_frame_counter >= 1) {
if (fifo_frame_counter > FIFO_MAX_SAMPLES) {
FIFOReset();
perf_count(_fifo_overflow_perf);
uint8_t samples = fifo_frame_counter;
} else if (fifo_frame_counter == 0) {
perf_count(_fifo_empty_perf);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_samples + 1) {
// sample timestamp set from data ready already corresponds to _fifo_samples
if (timestamp_sample == 0) {
timestamp_sample = now - static_cast<int>(FIFO_SAMPLE_DT);
} else if (fifo_frame_counter >= 1) {
samples = fifo_frame_counter;
if (samples > _fifo_samples) {
// grab desired number of samples, but reschedule next cycle sooner
const int extra_samples = samples - _fifo_samples;
samples = _fifo_samples;
if (_fifo_samples > extra_samples) {
// reschedule to run when a total of _fifo_gyro_samples should be available in the FIFO
const uint32_t reschedule_delay_us = (_fifo_samples - extra_samples) * static_cast<int>(FIFO_SAMPLE_DT);
ScheduleOnInterval(_fifo_empty_interval_us, reschedule_delay_us);
} else {
// otherwise reschedule to run immediately
ScheduleOnInterval(_fifo_empty_interval_us);
}
} else if (samples < _fifo_samples) {
// reschedule next cycle to catch the desired number of samples
ScheduleOnInterval(_fifo_empty_interval_us, (_fifo_samples - samples) * static_cast<int>(FIFO_SAMPLE_DT));
}
samples--;
}
}
}
if (FIFORead((timestamp_sample == 0) ? now : timestamp_sample, samples)) {
success = true;
bool success = false;
if (_failure_count > 0) {
_failure_count--;
}
if (samples == _fifo_samples) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
@@ -227,13 +241,14 @@ void BMI055_Gyroscope::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_cfg[_checked_register])) {
_last_config_check_timestamp = now;
_checked_register = (_checked_register + 1) % size_register_cfg;
@@ -241,6 +256,7 @@ void BMI055_Gyroscope::RunImpl()
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
PX4_DEBUG("Force reset because register 0x%02hhX check failed ", (uint8_t)_register_cfg[_checked_register].reg);
Reset();
}
}
@@ -256,28 +272,28 @@ void BMI055_Gyroscope::ConfigureGyro()
switch (RANGE) {
case gyro_range_2000_dps:
_px4_gyro.set_scale(math::radians(1.f / 16.384f));
_px4_gyro.set_range(math::radians(2000.f));
_gyro_scale = math::radians(1.f / 16.384f);
_gyro_range = math::radians(2000.f);
break;
case gyro_range_1000_dps:
_px4_gyro.set_scale(math::radians(1.f / 32.768f));
_px4_gyro.set_range(math::radians(1000.f));
_gyro_scale = math::radians(1.f / 32.768f);
_gyro_range = math::radians(1000.f);
break;
case gyro_range_500_dps:
_px4_gyro.set_scale(math::radians(1.f / 65.536f));
_px4_gyro.set_range(math::radians(500.f));
_gyro_scale = math::radians(1.f / 65.536f);
_gyro_range = math::radians(500.f);
break;
case gyro_range_250_dps:
_px4_gyro.set_scale(math::radians(1.f / 131.072f));
_px4_gyro.set_range(math::radians(250.f));
_gyro_scale = math::radians(1.f / 131.072f);
_gyro_range = math::radians(250.f);
break;
case gyro_range_125_dps:
_px4_gyro.set_scale(math::radians(1.f / 262.144f));
_px4_gyro.set_range(math::radians(125.f));
_gyro_scale = math::radians(1.f / 262.144f);
_gyro_range = math::radians(125.f);
break;
}
}
@@ -347,7 +363,7 @@ bool BMI055_Gyroscope::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool BMI055_Gyroscope::DataReadyInterruptDisable()
@@ -356,7 +372,7 @@ bool BMI055_Gyroscope::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
bool BMI055_Gyroscope::RegisterCheck(const register_config_t &reg_cfg)
@@ -405,37 +421,57 @@ void BMI055_Gyroscope::RegisterSetAndClearBits(Register reg, uint8_t setbits, ui
bool BMI055_Gyroscope::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 1, FIFO::SIZE);
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_DATA) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + samples * FIFO::DATA
const size_t transfer_size = 1 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
float gyro_sum[3] {};
for (int i = 0; i < samples; i++) {
const FIFO::DATA &fifo_sample = buffer.f[i];
const int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
const int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
const int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
//gyro.x[i] = gyro_x;
gyro_y = math::negate(gyro_y);
gyro_z = math::negate(gyro_z);
gyro_sum[0] += gyro_x;
gyro_sum[1] += gyro_y;
gyro_sum[2] += gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
sensor_gyro.x = gyro_sum[0] * _gyro_scale / samples;
sensor_gyro.y = gyro_sum[1] * _gyro_scale / samples;
sensor_gyro.z = gyro_sum[2] * _gyro_scale / samples;
_px4_gyro.updateFIFO(gyro);
rotate_3f(_rotation, sensor_gyro.x, sensor_gyro.y, sensor_gyro.z);
sensor_gyro.range = _gyro_range;
sensor_gyro.temperature = NAN;
sensor_gyro.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
return true;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,10 +35,11 @@
#include "BMI055.hpp"
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include "Bosch_BMI055_Gyroscope_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_gyro.h>
namespace Bosch::BMI055::Gyroscope
{
@@ -58,15 +59,7 @@ private:
static constexpr uint32_t RATE{2000}; // 2000 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]))};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_DATA) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (1 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
struct register_config_t {
Register reg;
@@ -95,7 +88,11 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_gyro_s> _sensor_gyro_pub{ORB_ID(sensor_gyro)};
const enum Rotation _rotation;
float _gyro_scale{0.f};
float _gyro_range{0.f};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad transfer")};
+1 -2
View File
@@ -35,6 +35,7 @@ px4_add_module(
MODULE drivers__imu__bosch__bmi055
MAIN bmi055
COMPILE_FLAGS
-O0
SRCS
Bosch_BMI055_Accelerometer_Registers.hpp
Bosch_BMI055_Gyroscope_Registers.hpp
@@ -48,7 +49,5 @@ px4_add_module(
bmi055_main.cpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
@@ -42,13 +42,13 @@ namespace Bosch::BMI085::Accelerometer
BMI085_Accelerometer::BMI085_Accelerometer(const I2CSPIDriverConfig &config) :
BMI085(config),
_px4_accel(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_accel: DRDY missed");
}
ConfigureSampleRate(_px4_accel.get_max_rate_hz());
ConfigureSampleRate(RATE);
}
BMI085_Accelerometer::~BMI085_Accelerometer()
@@ -280,23 +280,23 @@ void BMI085_Accelerometer::ConfigureAccel()
switch (ACC_RANGE) {
case acc_range_2g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f;
_accel_range = 2.f * CONSTANTS_ONE_G;
break;
case acc_range_4g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f;
_accel_range = 4.f * CONSTANTS_ONE_G;
break;
case acc_range_8g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f;
_accel_range = 8.f * CONSTANTS_ONE_G;
break;
case acc_range_16g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1)) / 32768.f;
_accel_range = 16.f * CONSTANTS_ONE_G;
break;
}
}
@@ -452,7 +452,7 @@ uint16_t BMI085_Accelerometer::FIFOReadCount()
const uint8_t FIFO_LENGTH_0 = fifo_len_buf[2]; // fifo_byte_counter[7:0]
const uint8_t FIFO_LENGTH_1 = fifo_len_buf[3] & 0x3F; // fifo_byte_counter[13:8]
return combine(FIFO_LENGTH_1, FIFO_LENGTH_0);
return (FIFO_LENGTH_1 << 8) + FIFO_LENGTH_0;
}
bool BMI085_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
@@ -465,7 +465,7 @@ bool BMI085_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
return false;
}
const size_t fifo_byte_counter = combine(buffer.FIFO_LENGTH_1 & 0x3F, buffer.FIFO_LENGTH_0);
const size_t fifo_byte_counter = ((buffer.FIFO_LENGTH_1 & 0x3F) << 8) + buffer.FIFO_LENGTH_0;
// An empty FIFO corresponds to 0x8000
if (fifo_byte_counter == 0x8000) {
@@ -477,15 +477,17 @@ bool BMI085_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
return false;
}
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
// first find all sensor data frames in the buffer
uint8_t *data_buffer = (uint8_t *)&buffer.f[0];
unsigned fifo_buffer_index = 0; // start of buffer
float accel_sum[3] {};
int accel_samples = 0;
while (fifo_buffer_index < math::min(fifo_byte_counter, transfer_size - 4)) {
// look for header signature (first 6 bits) followed by two bits indicating the status of INT1 and INT2
switch (data_buffer[fifo_buffer_index] & 0xFC) {
@@ -494,16 +496,23 @@ bool BMI085_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
// Frame length: 7 bytes (1 byte header + 6 bytes payload)
FIFO::DATA *fifo_sample = (FIFO::DATA *)&data_buffer[fifo_buffer_index];
const int16_t accel_x = combine(fifo_sample->ACC_X_MSB, fifo_sample->ACC_X_LSB);
const int16_t accel_y = combine(fifo_sample->ACC_Y_MSB, fifo_sample->ACC_Y_LSB);
const int16_t accel_z = combine(fifo_sample->ACC_Z_MSB, fifo_sample->ACC_Z_LSB);
int16_t accel_x = combine(fifo_sample->ACC_X_MSB, fifo_sample->ACC_X_LSB);
int16_t accel_y = combine(fifo_sample->ACC_Y_MSB, fifo_sample->ACC_Y_LSB);
int16_t accel_z = combine(fifo_sample->ACC_Z_MSB, fifo_sample->ACC_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = math::negate(accel_y);
accel.z[accel.samples] = math::negate(accel_z);
accel.samples++;
// accel_x = accel_x;
accel_y = math::negate(accel_y);
accel_z = math::negate(accel_z);
rotate_3i(_rotation, accel_x, accel_y, accel_z);
accel_sum[0] += accel_x;
accel_sum[1] += accel_y;
accel_sum[2] += accel_z;
accel_samples++;
fifo_buffer_index += 7; // move forward to next record
}
@@ -543,11 +552,19 @@ bool BMI085_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
}
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel_samples > 0) {
sensor_accel.x = (accel_sum[0] / accel_samples) * _accel_scale;
sensor_accel.y = (accel_sum[1] / accel_samples) * _accel_scale;
sensor_accel.z = (accel_sum[2] / accel_samples) * _accel_scale;
sensor_accel.range = _accel_range;
sensor_accel.temperature = _temperature;
sensor_accel.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
return true;
}
@@ -594,7 +611,7 @@ void BMI085_Accelerometer::UpdateTemperature()
float temperature = (Temp_int11 * 0.125f) + 23.f; // Temp_int11 * 0.125°C/LSB + 23°C
if (PX4_ISFINITE(temperature)) {
_px4_accel.set_temperature(temperature);
_temperature = temperature;
} else {
perf_count(_bad_transfer_perf);
@@ -35,10 +35,11 @@
#include "BMI085.hpp"
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include "Bosch_BMI085_Accelerometer_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_accel.h>
namespace Bosch::BMI085::Accelerometer
{
@@ -58,7 +59,7 @@ private:
static constexpr uint32_t RATE{1600}; // 1600 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]))};
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
// Transfer data
struct FIFOTransferBuffer {
@@ -101,7 +102,13 @@ private:
void UpdateTemperature();
PX4Accelerometer _px4_accel;
uORB::PublicationMulti<sensor_accel_s> _sensor_accel_pub{ORB_ID(sensor_accel)};
const enum Rotation _rotation;
float _accel_scale{0.f};
float _accel_range{0.f};
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad transfer")};
@@ -33,8 +33,6 @@
#include "BMI085_Gyroscope.hpp"
#include <px4_platform/board_dma_alloc.h>
using namespace time_literals;
namespace Bosch::BMI085::Gyroscope
@@ -42,13 +40,13 @@ namespace Bosch::BMI085::Gyroscope
BMI085_Gyroscope::BMI085_Gyroscope(const I2CSPIDriverConfig &config) :
BMI085(config),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_gyro: DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
ConfigureSampleRate(RATE);
}
BMI085_Gyroscope::~BMI085_Gyroscope()
@@ -257,28 +255,28 @@ void BMI085_Gyroscope::ConfigureGyro()
switch (GYRO_RANGE) {
case gyro_range_2000_dps:
_px4_gyro.set_scale(math::radians(1.f / 16.384f));
_px4_gyro.set_range(math::radians(2000.f));
_gyro_scale = math::radians(1.f / 16.384f);
_gyro_range = math::radians(2000.f);
break;
case gyro_range_1000_dps:
_px4_gyro.set_scale(math::radians(1.f / 32.768f));
_px4_gyro.set_range(math::radians(1000.f));
_gyro_scale = math::radians(1.f / 32.768f);
_gyro_range = math::radians(1000.f);
break;
case gyro_range_500_dps:
_px4_gyro.set_scale(math::radians(1.f / 65.536f));
_px4_gyro.set_range(math::radians(500.f));
_gyro_scale = math::radians(1.f / 65.536f);
_gyro_range = math::radians(500.f);
break;
case gyro_range_250_dps:
_px4_gyro.set_scale(math::radians(1.f / 131.072f));
_px4_gyro.set_range(math::radians(250.f));
_gyro_scale = math::radians(1.f / 131.072f);
_gyro_range = math::radians(250.f);
break;
case gyro_range_125_dps:
_px4_gyro.set_scale(math::radians(1.f / 262.144f));
_px4_gyro.set_range(math::radians(125.f));
_gyro_scale = math::radians(1.f / 262.144f);
_gyro_range = math::radians(125.f);
break;
}
}
@@ -414,29 +412,43 @@ bool BMI085_Gyroscope::FIFORead(const hrt_abstime &timestamp_sample, uint8_t sam
return false;
}
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
float gyro_sum[3] {};
for (int i = 0; i < samples; i++) {
const FIFO::DATA &fifo_sample = buffer.f[i];
const int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
const int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
const int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
//gyro.x[i] = gyro_x;
gyro_y = math::negate(gyro_y);
gyro_z = math::negate(gyro_z);
rotate_3i(_rotation, gyro_x, gyro_y, gyro_z);
gyro_sum[0] += gyro_x * _gyro_scale;
gyro_sum[1] += gyro_y * _gyro_scale;
gyro_sum[2] += gyro_z * _gyro_scale;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
sensor_gyro.x = gyro_sum[0] / samples;
sensor_gyro.y = gyro_sum[1] / samples;
sensor_gyro.z = gyro_sum[2] / samples;
_px4_gyro.updateFIFO(gyro);
sensor_gyro.range = _gyro_range;
sensor_gyro.temperature = NAN;
sensor_gyro.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
return true;
}
@@ -35,10 +35,11 @@
#include "BMI085.hpp"
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include "Bosch_BMI085_Gyroscope_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_gyro.h>
namespace Bosch::BMI085::Gyroscope
{
@@ -58,7 +59,7 @@ private:
static constexpr uint32_t RATE{2000}; // 2000 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]))};
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
// Transfer data
struct FIFOTransferBuffer {
@@ -95,7 +96,11 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_gyro_s> _sensor_gyro_pub{ORB_ID(sensor_gyro)};
const enum Rotation _rotation;
float _gyro_scale{0.f};
float _gyro_range{0.f};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad transfer")};
@@ -48,7 +48,5 @@ px4_add_module(
bmi085_main.cpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
+1 -1
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
+1 -1
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -42,13 +42,13 @@ namespace Bosch::BMI088::Accelerometer
BMI088_Accelerometer::BMI088_Accelerometer(const I2CSPIDriverConfig &config) :
BMI088(config),
_px4_accel(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_accel: DRDY missed");
}
ConfigureSampleRate(_px4_accel.get_max_rate_hz());
ConfigureSampleRate(RATE);
}
BMI088_Accelerometer::~BMI088_Accelerometer()
@@ -286,23 +286,23 @@ void BMI088_Accelerometer::ConfigureAccel()
switch (ACC_RANGE) {
case acc_range_3g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(3.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 3.f * CONSTANTS_ONE_G;
break;
case acc_range_6g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(6.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 6.f * CONSTANTS_ONE_G;
break;
case acc_range_12g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(12.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 12.f * CONSTANTS_ONE_G;
break;
case acc_range_24g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(24.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 24.f * CONSTANTS_ONE_G;
break;
}
}
@@ -458,7 +458,7 @@ uint16_t BMI088_Accelerometer::FIFOReadCount()
const uint8_t FIFO_LENGTH_0 = fifo_len_buf[2]; // fifo_byte_counter[7:0]
const uint8_t FIFO_LENGTH_1 = fifo_len_buf[3] & 0x3F; // fifo_byte_counter[13:8]
return combine(FIFO_LENGTH_1, FIFO_LENGTH_0);
return (FIFO_LENGTH_1 << 8) + FIFO_LENGTH_0;
}
bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
@@ -471,7 +471,7 @@ bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
return false;
}
const size_t fifo_byte_counter = combine(buffer.FIFO_LENGTH_1 & 0x3F, buffer.FIFO_LENGTH_0);
const size_t fifo_byte_counter = ((buffer.FIFO_LENGTH_1 & 0x3F) << 8) + buffer.FIFO_LENGTH_0;
// An empty FIFO corresponds to 0x8000
if (fifo_byte_counter == 0x8000) {
@@ -483,15 +483,17 @@ bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
return false;
}
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
// first find all sensor data frames in the buffer
uint8_t *data_buffer = (uint8_t *)&buffer.f[0];
unsigned fifo_buffer_index = 0; // start of buffer
float accel_sum[3] {};
int accel_samples = 0;
while (fifo_buffer_index < math::min(fifo_byte_counter, transfer_size - 4)) {
// look for header signature (first 6 bits) followed by two bits indicating the status of INT1 and INT2
switch (data_buffer[fifo_buffer_index] & 0xFC) {
@@ -500,16 +502,23 @@ bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
// Frame length: 7 bytes (1 byte header + 6 bytes payload)
FIFO::DATA *fifo_sample = (FIFO::DATA *)&data_buffer[fifo_buffer_index];
const int16_t accel_x = combine(fifo_sample->ACC_X_MSB, fifo_sample->ACC_X_LSB);
const int16_t accel_y = combine(fifo_sample->ACC_Y_MSB, fifo_sample->ACC_Y_LSB);
const int16_t accel_z = combine(fifo_sample->ACC_Z_MSB, fifo_sample->ACC_Z_LSB);
int16_t accel_x = combine(fifo_sample->ACC_X_MSB, fifo_sample->ACC_X_LSB);
int16_t accel_y = combine(fifo_sample->ACC_Y_MSB, fifo_sample->ACC_Y_LSB);
int16_t accel_z = combine(fifo_sample->ACC_Z_MSB, fifo_sample->ACC_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
// accel_x = accel_x;
accel_y = math::negate(accel_y);
accel_z = math::negate(accel_z);
rotate_3i(_rotation, accel_x, accel_y, accel_z);
accel_sum[0] += accel_x;
accel_sum[1] += accel_y;
accel_sum[2] += accel_z;
accel_samples++;
fifo_buffer_index += 7; // move forward to next record
}
@@ -549,11 +558,19 @@ bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
}
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel_samples > 0) {
sensor_accel.x = (accel_sum[0] / accel_samples) * _accel_scale;
sensor_accel.y = (accel_sum[1] / accel_samples) * _accel_scale;
sensor_accel.z = (accel_sum[2] / accel_samples) * _accel_scale;
sensor_accel.range = _accel_range;
sensor_accel.temperature = _temperature;
sensor_accel.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
return true;
}
@@ -600,7 +617,7 @@ void BMI088_Accelerometer::UpdateTemperature()
float temperature = (Temp_int11 * 0.125f) + 23.f; // Temp_int11 * 0.125°C/LSB + 23°C
if (PX4_ISFINITE(temperature)) {
_px4_accel.set_temperature(temperature);
_temperature = temperature;
} else {
perf_count(_bad_transfer_perf);
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,10 +35,11 @@
#include "BMI088.hpp"
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include "Bosch_BMI088_Accelerometer_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_accel.h>
namespace Bosch::BMI088::Accelerometer
{
@@ -58,7 +59,7 @@ private:
static constexpr uint32_t RATE{1600}; // 1600 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]))};
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
// Transfer data
struct FIFOTransferBuffer {
@@ -101,7 +102,13 @@ private:
void UpdateTemperature();
PX4Accelerometer _px4_accel;
uORB::PublicationMulti<sensor_accel_s> _sensor_accel_pub{ORB_ID(sensor_accel)};
const enum Rotation _rotation;
float _accel_scale{0.f};
float _accel_range{0.f};
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad transfer")};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,8 +33,6 @@
#include "BMI088_Gyroscope.hpp"
#include <px4_platform/board_dma_alloc.h>
using namespace time_literals;
namespace Bosch::BMI088::Gyroscope
@@ -42,13 +40,13 @@ namespace Bosch::BMI088::Gyroscope
BMI088_Gyroscope::BMI088_Gyroscope(const I2CSPIDriverConfig &config) :
BMI088(config),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_gyro: DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
ConfigureSampleRate(RATE);
}
BMI088_Gyroscope::~BMI088_Gyroscope()
@@ -257,28 +255,28 @@ void BMI088_Gyroscope::ConfigureGyro()
switch (GYRO_RANGE) {
case gyro_range_2000_dps:
_px4_gyro.set_scale(math::radians(1.f / 16.384f));
_px4_gyro.set_range(math::radians(2000.f));
_gyro_scale = math::radians(1.f / 16.384f);
_gyro_range = math::radians(2000.f);
break;
case gyro_range_1000_dps:
_px4_gyro.set_scale(math::radians(1.f / 32.768f));
_px4_gyro.set_range(math::radians(1000.f));
_gyro_scale = math::radians(1.f / 32.768f);
_gyro_range = math::radians(1000.f);
break;
case gyro_range_500_dps:
_px4_gyro.set_scale(math::radians(1.f / 65.536f));
_px4_gyro.set_range(math::radians(500.f));
_gyro_scale = math::radians(1.f / 65.536f);
_gyro_range = math::radians(500.f);
break;
case gyro_range_250_dps:
_px4_gyro.set_scale(math::radians(1.f / 131.072f));
_px4_gyro.set_range(math::radians(250.f));
_gyro_scale = math::radians(1.f / 131.072f);
_gyro_range = math::radians(250.f);
break;
case gyro_range_125_dps:
_px4_gyro.set_scale(math::radians(1.f / 262.144f));
_px4_gyro.set_range(math::radians(125.f));
_gyro_scale = math::radians(1.f / 262.144f);
_gyro_range = math::radians(125.f);
break;
}
}
@@ -414,29 +412,43 @@ bool BMI088_Gyroscope::FIFORead(const hrt_abstime &timestamp_sample, uint8_t sam
return false;
}
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
float gyro_sum[3] {};
for (int i = 0; i < samples; i++) {
const FIFO::DATA &fifo_sample = buffer.f[i];
const int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
const int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
const int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
//gyro.x[i] = gyro_x;
gyro_y = math::negate(gyro_y);
gyro_z = math::negate(gyro_z);
rotate_3i(_rotation, gyro_x, gyro_y, gyro_z);
gyro_sum[0] += gyro_x * _gyro_scale;
gyro_sum[1] += gyro_y * _gyro_scale;
gyro_sum[2] += gyro_z * _gyro_scale;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
sensor_gyro.x = gyro_sum[0] / samples;
sensor_gyro.y = gyro_sum[1] / samples;
sensor_gyro.z = gyro_sum[2] / samples;
_px4_gyro.updateFIFO(gyro);
sensor_gyro.range = _gyro_range;
sensor_gyro.temperature = NAN;
sensor_gyro.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
return true;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,10 +35,11 @@
#include "BMI088.hpp"
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include "Bosch_BMI088_Gyroscope_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_gyro.h>
namespace Bosch::BMI088::Gyroscope
{
@@ -58,7 +59,7 @@ private:
static constexpr uint32_t RATE{2000}; // 2000 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]))};
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
// Transfer data
struct FIFOTransferBuffer {
@@ -95,7 +96,11 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_gyro_s> _sensor_gyro_pub{ORB_ID(sensor_gyro)};
const enum Rotation _rotation;
float _gyro_scale{0.f};
float _gyro_range{0.f};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad transfer")};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
+1 -3
View File
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2019-2020 PX4 Development Team. All rights reserved.
# Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -48,7 +48,5 @@ px4_add_module(
bmi088_main.cpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
+2 -5
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -76,10 +76,7 @@ int BMI088::init()
return ret;
}
int res = Reset() ? 0 : -1;
_state = STATE::SELFTEST;
return res;
return Reset() ? 0 : -1;
}
bool BMI088::Reset()
+2 -9
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -65,23 +65,16 @@ protected:
hrt_abstime _last_config_check_timestamp{0};
hrt_abstime _temperature_update_timestamp{0};
int _failure_count{0};
int _overflow_data_size_count{0};
int _overflow_fifo_max_samples_count{0};
int _fifo_read_error_count{0};
int _empty_count{0};
int _total_failure_count{0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
bool _data_ready_interrupt_enabled{false};
enum class STATE : uint8_t {
SELFTEST,
RESET,
WAIT_FOR_RESET,
CONFIGURE,
FIFO_READ,
} _state{STATE::SELFTEST};
} _state{STATE::RESET};
uint16_t _fifo_empty_interval_us{2500}; // 2500 us / 400 Hz transfer interval
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -39,15 +39,16 @@ using namespace time_literals;
namespace Bosch::BMI088::Accelerometer
{
BMI088_Accelerometer::BMI088_Accelerometer(const I2CSPIDriverConfig &config) :
BMI088(config),
_px4_accel(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_accel: DRDY missed");
}
ConfigureSampleRate(1600);
ConfigureSampleRate(RATE);
}
BMI088_Accelerometer::~BMI088_Accelerometer()
@@ -80,32 +81,21 @@ void BMI088_Accelerometer::print_status()
perf_print_counter(_drdy_missed_perf);
}
uint8_t BMI088_Accelerometer::RegisterRead(Register reg)
{
uint8_t add = static_cast<uint8_t>(reg);
uint8_t cmd[2] = {add, 0};
transfer(&cmd[0], 1, &cmd[1], 1);
return cmd[1];
}
uint8_t BMI088_Accelerometer::RegisterWrite(Register reg, uint8_t value)
{
uint8_t add = static_cast<uint8_t>(reg);
uint8_t cmd[2] = { add, value};
return transfer(cmd, sizeof(cmd), nullptr, 0);
}
int BMI088_Accelerometer::probe()
{
const uint8_t ACC_CHIP_ID = RegisterRead(Register::ACC_CHIP_ID);
if (ACC_CHIP_ID != ID) {
if (ACC_CHIP_ID == ID_088) {
DEVICE_DEBUG("BMI088 Accel");
} else if (ACC_CHIP_ID == ID_090L) {
DEVICE_DEBUG("BMI090L Accel");
} else {
DEVICE_DEBUG("unexpected ACC_CHIP_ID 0x%02x", ACC_CHIP_ID);
return PX4_ERROR;
}
PX4_WARN("Probe success, ACC_CHIP_ID: 0x%02x", ACC_CHIP_ID);
return PX4_OK;
}
@@ -114,27 +104,17 @@ void BMI088_Accelerometer::RunImpl()
const hrt_abstime now = hrt_absolute_time();
switch (_state) {
case STATE::SELFTEST:
//PX4_WARN("Selftest state");
//SelfTest();
_state = STATE::RESET;
ScheduleDelayed(10_ms);
break;
case STATE::RESET:
// ACC_SOFTRESET: Writing a value of 0xB6 to this register resets the sensor
RegisterWrite(Register::ACC_SOFTRESET, 0xB6);
_reset_timestamp = now;
_failure_count = 0;
_state = STATE::WAIT_FOR_RESET;
ScheduleDelayed(1_ms); // Following a delay of 1 ms, all configuration settings are overwritten with their reset value.
break;
case STATE::WAIT_FOR_RESET:
if (RegisterRead(Register::ACC_CHIP_ID) == ID) {
if ((RegisterRead(Register::ACC_CHIP_ID) == ID_088) || (RegisterRead(Register::ACC_CHIP_ID) == ID_090L)) {
// ACC_PWR_CONF: Power on sensor
RegisterWrite(Register::ACC_PWR_CONF, 0);
@@ -191,36 +171,125 @@ void BMI088_Accelerometer::RunImpl()
break;
case STATE::FIFO_READ: {
SimpleFIFORead(now);
hrt_abstime timestamp_sample = now;
uint8_t samples = 0;
if (_data_ready_interrupt_enabled) {
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_samples;
} else {
perf_count(_drdy_missed_perf);
}
// push backup schedule back
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
if (samples == 0) {
// check current FIFO count
const uint16_t fifo_byte_counter = FIFOReadCount();
if (fifo_byte_counter >= FIFO::SIZE) {
FIFOReset();
perf_count(_fifo_overflow_perf);
} else if ((fifo_byte_counter == 0) || (fifo_byte_counter == 0x8000)) {
// An empty FIFO corresponds to 0x8000
perf_count(_fifo_empty_perf);
} else {
samples = fifo_byte_counter / sizeof(FIFO::DATA);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_samples + 1) {
timestamp_sample -= static_cast<int>(FIFO_SAMPLE_DT);
samples--;
}
if (samples > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
FIFOReset();
perf_count(_fifo_overflow_perf);
samples = 0;
}
}
}
bool success = false;
if (samples >= 1) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
if (!success) {
_failure_count++;
// full reset if things are failing consistently
if (_failure_count > 10) {
Reset();
return;
}
}
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_cfg[_checked_register])) {
_last_config_check_timestamp = now;
_checked_register = (_checked_register + 1) % size_register_cfg;
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
Reset();
}
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature_update_timestamp = now;
}
}
}
break;
}
}
void BMI088_Accelerometer::ConfigureAccel()
{
//PX4_WARN("ConfigureAccel");
const uint8_t ACC_RANGE = RegisterRead(Register::ACC_RANGE) & (Bit1 | Bit0);
switch (ACC_RANGE) {
case acc_range_3g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(3.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 3.f * CONSTANTS_ONE_G;
break;
case acc_range_6g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(6.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 6.f * CONSTANTS_ONE_G;
break;
case acc_range_12g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(12.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 12.f * CONSTANTS_ONE_G;
break;
case acc_range_24g:
_px4_accel.set_scale(CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f);
_px4_accel.set_range(24.f * CONSTANTS_ONE_G);
_accel_scale = CONSTANTS_ONE_G * (powf(2, ACC_RANGE + 1) * 1.5f) / 32768.f;
_accel_range = 24.f * CONSTANTS_ONE_G;
break;
}
}
@@ -231,15 +300,11 @@ void BMI088_Accelerometer::ConfigureSampleRate(int sample_rate)
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
PX4_WARN("_fifo_empty_interval_us %d", _fifo_empty_interval_us);
_fifo_samples = math::min((float)_fifo_empty_interval_us / (1e6f / RATE), (float)FIFO_MAX_SAMPLES);
PX4_WARN("_fifo_samples %d", _fifo_samples);
// recompute FIFO empty interval (us) with actual sample limit
_fifo_empty_interval_us = _fifo_samples * (1e6f / RATE);
PX4_WARN("_fifo_empty_interval_us %d", _fifo_empty_interval_us);
//PX4_WARN("_fifo_samples %d", _fifo_samples);
ConfigureFIFOWatermark(_fifo_samples);
}
@@ -265,7 +330,6 @@ void BMI088_Accelerometer::ConfigureFIFOWatermark(uint8_t samples)
bool BMI088_Accelerometer::Configure()
{
// first set and clear all configured register bits
for (const auto &reg_cfg : _register_cfg) {
RegisterSetAndClearBits(reg_cfg.reg, reg_cfg.set_bits, reg_cfg.clear_bits);
@@ -293,6 +357,7 @@ int BMI088_Accelerometer::DataReadyInterruptCallback(int irq, void *context, voi
void BMI088_Accelerometer::DataReady()
{
_drdy_timestamp_sample.store(hrt_absolute_time());
ScheduleNow();
}
@@ -334,6 +399,20 @@ bool BMI088_Accelerometer::RegisterCheck(const register_config_t &reg_cfg)
return success;
}
uint8_t BMI088_Accelerometer::RegisterRead(Register reg)
{
uint8_t cmd = static_cast<uint8_t>(reg);
uint8_t value = 0;
transfer(&cmd, 1, &value, 1);
return value;
}
void BMI088_Accelerometer::RegisterWrite(Register reg, uint8_t value)
{
uint8_t cmd[2] { (uint8_t)reg, value };
transfer(cmd, sizeof(cmd), nullptr, 0);
}
void BMI088_Accelerometer::RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t clearbits)
{
const uint8_t orig_val = RegisterRead(reg);
@@ -349,10 +428,9 @@ uint16_t BMI088_Accelerometer::FIFOReadCount()
{
// FIFO length registers FIFO_LENGTH_1 and FIFO_LENGTH_0 contain the 14 bit FIFO byte
uint8_t fifo_len_buf[2] {};
fifo_len_buf[0] = static_cast<uint8_t>(Register::FIFO_LENGTH_0) | DIR_READ;
// fifo_len_buf[1] dummy byte
uint8_t cmd = static_cast<uint8_t>(Register::FIFO_LENGTH_0);
if (transfer(&fifo_len_buf[0], 1, &fifo_len_buf[0], 2) != PX4_OK) {
if (transfer(&cmd, 1, fifo_len_buf, sizeof(fifo_len_buf)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
@@ -360,21 +438,20 @@ uint16_t BMI088_Accelerometer::FIFOReadCount()
const uint8_t FIFO_LENGTH_0 = fifo_len_buf[0]; // fifo_byte_counter[7:0]
const uint8_t FIFO_LENGTH_1 = fifo_len_buf[1] & 0x3F; // fifo_byte_counter[13:8]
return combine(FIFO_LENGTH_1, FIFO_LENGTH_0);
return (FIFO_LENGTH_1 << 8) + FIFO_LENGTH_0;
}
bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 4, FIFO::SIZE);
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 2, FIFO::SIZE);
if (transfer((uint8_t *)&buffer, 1, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
if (transfer(&buffer.cmd, 1, (uint8_t *)&buffer.FIFO_LENGTH_0, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
//PX4_WARN("Accel transfer success");
const size_t fifo_byte_counter = combine(buffer.FIFO_LENGTH_1 & 0x3F, buffer.FIFO_LENGTH_0);
const size_t fifo_byte_counter = ((buffer.FIFO_LENGTH_1 & 0x3F) << 8) + buffer.FIFO_LENGTH_0;
// An empty FIFO corresponds to 0x8000
if (fifo_byte_counter == 0x8000) {
@@ -386,15 +463,17 @@ bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
return false;
}
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
// first find all sensor data frames in the buffer
uint8_t *data_buffer = (uint8_t *)&buffer.f[0];
unsigned fifo_buffer_index = 0; // start of buffer
float accel_sum[3] {};
int accel_samples = 0;
while (fifo_buffer_index < math::min(fifo_byte_counter, transfer_size - 4)) {
// look for header signature (first 6 bits) followed by two bits indicating the status of INT1 and INT2
switch (data_buffer[fifo_buffer_index] & 0xFC) {
@@ -403,16 +482,23 @@ bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
// Frame length: 7 bytes (1 byte header + 6 bytes payload)
FIFO::DATA *fifo_sample = (FIFO::DATA *)&data_buffer[fifo_buffer_index];
const int16_t accel_x = combine(fifo_sample->ACC_X_MSB, fifo_sample->ACC_X_LSB);
const int16_t accel_y = combine(fifo_sample->ACC_Y_MSB, fifo_sample->ACC_Y_LSB);
const int16_t accel_z = combine(fifo_sample->ACC_Z_MSB, fifo_sample->ACC_Z_LSB);
int16_t accel_x = combine(fifo_sample->ACC_X_MSB, fifo_sample->ACC_X_LSB);
int16_t accel_y = combine(fifo_sample->ACC_Y_MSB, fifo_sample->ACC_Y_LSB);
int16_t accel_z = combine(fifo_sample->ACC_Z_MSB, fifo_sample->ACC_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
// accel_x = accel_x;
accel_y = math::negate(accel_y);
accel_z = math::negate(accel_z);
rotate_3i(_rotation, accel_x, accel_y, accel_z);
accel_sum[0] += accel_x;
accel_sum[1] += accel_y;
accel_sum[2] += accel_z;
accel_samples++;
fifo_buffer_index += 7; // move forward to next record
}
@@ -452,130 +538,25 @@ bool BMI088_Accelerometer::FIFORead(const hrt_abstime &timestamp_sample, uint8_t
}
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel_samples > 0) {
sensor_accel.x = (accel_sum[0] / accel_samples) * _accel_scale;
sensor_accel.y = (accel_sum[1] / accel_samples) * _accel_scale;
sensor_accel.z = (accel_sum[2] / accel_samples) * _accel_scale;
sensor_accel.range = _accel_range;
sensor_accel.temperature = _temperature;
sensor_accel.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
return true;
}
return false;
}
bool BMI088_Accelerometer::SimpleFIFORead(const hrt_abstime &timestamp_sample)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
int fifo_fill_level = 0;
uint8_t data_o[2] = { 0, 0 };
uint8_t data_i[1] = {static_cast<uint8_t>(Register::FIFO_LENGTH_0)};
data_i[0] = static_cast<uint8_t>(Register::FIFO_LENGTH_0);
transfer(&data_i[0], 1, &data_o[0], 2);
fifo_fill_level = data_o[0] + (data_o[1] << 8);
if (fifo_fill_level & 0x8000) {
return false;
}
int n_frames_to_read = 6;
// don't read more than 6 frames at a time
if (fifo_fill_level > n_frames_to_read * 7) {
fifo_fill_level = n_frames_to_read * 7;
}
if (fifo_fill_level == 0) {
return false;
}
uint8_t data[fifo_fill_level];
data[0] = static_cast<uint8_t>(Register::FIFO_DATA);
if (transfer(&data[0], 1, &data[0], fifo_fill_level) != PX4_OK) {
return false;
}
const uint8_t *p = &data[0];
while (fifo_fill_level >= 7) {
uint8_t frame_len = 2;
switch (p[0] & 0xFC) {
case 0x84: {
// accel frame
frame_len = 7;
const uint8_t *d = p + 1;
int16_t xyz[3] {
int16_t(uint16_t(d[0] | (d[1] << 8))),
int16_t(uint16_t(d[2] | (d[3] << 8))),
int16_t(uint16_t(d[4] | (d[5] << 8)))
};
const int16_t tX[3] = {1, 0, 0};
const int16_t tY[3] = {0, -1, 0};
const int16_t tZ[3] = {0, 0, -1};
float x = 0;
float y = 0;
float z = 0;
x = xyz[0] * tX[0] + xyz[1] * tX[1] + xyz[2] * tX[2];
y = xyz[0] * tY[0] + xyz[1] * tY[1] + xyz[2] * tY[2];
z = xyz[0] * tZ[0] + xyz[1] * tZ[1] + xyz[2] * tZ[2];
accel.x[accel.samples] = x;
accel.y[accel.samples] = y;
accel.z[accel.samples] = z;
accel.samples++;
break;
}
case 0x40:
// skip frame
frame_len = 2;
break;
case 0x44:
// sensortime frame
frame_len = 4;
break;
case 0x48:
// fifo config frame
frame_len = 2;
break;
case 0x50:
// sample drop frame
frame_len = 2;
break;
}
p += frame_len;
fifo_fill_level -= frame_len;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
//PX4_WARN("accel.samples: %d", accel.samples);
_px4_accel.updateFIFO(accel);
return true;
}
return true;
}
void BMI088_Accelerometer::FIFOReset()
{
perf_count(_fifo_reset_perf);
@@ -590,17 +571,17 @@ void BMI088_Accelerometer::FIFOReset()
void BMI088_Accelerometer::UpdateTemperature()
{
// stored in an 11-bit value in 2s complement format
uint8_t temperature_buf[4] {};
temperature_buf[0] = static_cast<uint8_t>(Register::TEMP_MSB) | ACC_I2C_ADDR_PRIMARY;
uint8_t temperature_buf[2] {};
uint8_t cmd = static_cast<uint8_t>(Register::TEMP_MSB);
// temperature_buf[1] dummy byte
if (transfer(&temperature_buf[0], 1, &temperature_buf[0], sizeof(temperature_buf)) != PX4_OK) {
if (transfer(&cmd, 1, &temperature_buf[0], sizeof(temperature_buf)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
}
const uint8_t TEMP_MSB = temperature_buf[2];
const uint8_t TEMP_LSB = temperature_buf[3];
const uint8_t TEMP_MSB = temperature_buf[0];
const uint8_t TEMP_LSB = temperature_buf[1];
// Datasheet 5.3.7: Register 0x22 0x23: Temperature sensor data
uint16_t Temp_uint11 = (TEMP_MSB * 8) + (TEMP_LSB / 32);
@@ -616,308 +597,11 @@ void BMI088_Accelerometer::UpdateTemperature()
float temperature = (Temp_int11 * 0.125f) + 23.f; // Temp_int11 * 0.125°C/LSB + 23°C
if (PX4_ISFINITE(temperature)) {
_px4_accel.set_temperature(temperature);
_temperature = temperature;
} else {
perf_count(_bad_transfer_perf);
}
}
bool BMI088_Accelerometer::SelfTest()
{
PX4_WARN("Running self-test with datasheet recomended steps(page 17)");
// Reset
PX4_WARN("Reseting the sensor");
if (RegisterWrite(Register::ACC_SOFTRESET, 0xB6) == PX4_OK) {
PX4_WARN("Reset success");
}
usleep(100000);
PX4_WARN("Accel on");
if (RegisterWrite(Register::ACC_PWR_CTRL, 0x04) == PX4_OK) {
PX4_WARN("Accel on success");
}
usleep(100000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
Configure();
usleep(1000000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
const uint8_t ACC_CHIP_ID = RegisterRead(Register::ACC_CHIP_ID);
PX4_WARN("ACC_CHIP_ID: 0x%02x", ACC_CHIP_ID);
usleep(30000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
if (RegisterWrite(Register::ACC_PWR_CONF, 0) == PX4_OK) {
PX4_WARN("Start sensor success");
PX4_WARN("ACC_PWR_CONF(0): 0x%02x", RegisterRead(Register::ACC_PWR_CONF));
}
usleep(2000000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
if (RegisterWrite(Register::ACC_RANGE, 0x03) == PX4_OK) {
PX4_WARN("Range set success");
PX4_WARN("ACC_RANGE(0x03): 0x%02x", RegisterRead(Register::ACC_RANGE));
}
usleep(100000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
if (RegisterWrite(Register::ACC_CONF, 0xA7) == PX4_OK) {
PX4_WARN("Conf set success");
PX4_WARN("ACC_CONF(0xA7): 0x%02x", RegisterRead(Register::ACC_CONF));
}
usleep(100000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
// Positive sel-test polarity
if (RegisterWrite(Register::ACC_SELF_TEST, 0x0D) == PX4_OK) {
PX4_WARN("Self-test positive mode set success");
PX4_WARN("ACC_SELF_TEST(0x0D): 0x%02x", RegisterRead(Register::ACC_SELF_TEST));
}
usleep(100000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
float *accel_mss = ReadAccelDataFIFO();
PX4_WARN("Positive value");
PX4_WARN("X %f", (double)accel_mss[0]);
PX4_WARN("Y %f", (double)accel_mss[1]);
PX4_WARN("Z %f", (double)accel_mss[2]);
// Negative sel-test polarity
if (RegisterWrite(Register::ACC_SELF_TEST, 0x09) == PX4_OK) {
PX4_WARN("Self-test negative mode set success");
PX4_WARN("ACC_SELF_TEST(0x09): 0x%02x", RegisterRead(Register::ACC_SELF_TEST));
}
usleep(600000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
float *accel_mss2 = ReadAccelDataFIFO();
PX4_WARN("Negative value");
PX4_WARN("X %f", (double)accel_mss2[0]);
PX4_WARN("Y %f", (double)accel_mss2[1]);
PX4_WARN("Z %f", (double)accel_mss2[2]);
// Calculate difference between positive and negative sef-test response
float diff_x = accel_mss[0] - accel_mss2[0];
float diff_y = accel_mss[1] - accel_mss2[1];
float diff_z = accel_mss[2] - accel_mss2[2];
PX4_WARN("Diff value");
PX4_WARN("diff_x %f", (double)diff_x);
PX4_WARN("diff_y %f", (double)diff_y);
PX4_WARN("diff_z %f", (double)diff_z);
if (diff_x >= 1000) {
PX4_WARN("X Axis self-test success");
}
if (diff_y >= 1000) {
PX4_WARN("Y Axis self-test success");
}
if (diff_z >= 500) {
PX4_WARN("Z Axis self-test success");
}
// Disable self-test
RegisterWrite(Register::ACC_SELF_TEST, 0x00);
usleep(60000);
PX4_WARN("Sensor ErrReg: 0x%02x", CheckSensorErrReg());
// Reset
//PX4_WARN("Reseting the sensor again");
//RegisterWrite(Register::ACC_SOFTRESET, 0xB6);
//usleep(100000);
return true;
}
float *BMI088_Accelerometer::ReadAccelData()
{
uint8_t cmd[1] = {0x12};
uint8_t buf[6] = {0, 0, 0, 0, 0, 0};
uint8_t *buffer = buf;
int16_t accel[3];
if (transfer(&cmd[0], 1, buffer, sizeof(buf)) == PX4_OK) {
PX4_WARN("ReadAccelData transfer success");
}
for (uint8_t i = 0; i < sizeof(buf); i++) {
PX4_WARN("buf[%d]: %f", i, (double)buf[i]);
}
accel[0] = (buf[1] << 8) | buf[0];
accel[1] = (buf[3] << 8) | buf[2];
accel[2] = (buf[5] << 8) | buf[4];
float *accel_mss = new float[3];
accel_mss[0] = (float) accel[0] / 32768.0f * 1000.0f * powf(2.0f, 24.0f + 1.0f) * 1.50f;
accel_mss[1] = (float) accel[1] / 32768.0f * 1000.0f * powf(2.0f, 24.0f + 1.0f) * 1.50f;
accel_mss[2] = (float) accel[2] / 32768.0f * 1000.0f * powf(2.0f, 24.0f + 1.0f) * 1.50f;
return accel_mss;
}
float *BMI088_Accelerometer::ReadAccelDataFIFO()
{
float *accel_mg = new float[3];
struct FIFO::bmi08x_sensor_data bmi08x_accel;
uint8_t buffer[50] = {0};
PX4_WARN("FIFO mode is stop-at-full");
/* Desired FIFO mode is stop-at-full: set bit #0 to 1 in 0x48. Bit #1 must always be one! */
buffer[0] = 0x00 | 0x02;
RegisterWrite(Register::FIFO_CONFIG_0, buffer[0]);
PX4_WARN("FIFO_CONFIG_0(0x%02x): 0x%02x", buffer[0], RegisterRead(Register::FIFO_CONFIG_0));
PX4_WARN("Downsampling factor 2**4 = 16");
/* Downsampling factor 2**4 = 16: write 4 into bit #4-6 of reg. 0x45. Bit #7 must always be one! */
buffer[0] = 0x10 | 0x80;
RegisterWrite(Register::FIFO_DOWN_SAMPLING, buffer[0]);
PX4_WARN("FIFO_DOWN_SAMPLING(0x%02x): 0x%02x", buffer[0], RegisterRead(Register::FIFO_DOWN_SAMPLING));
/* Set water mark to 42 bytes (aka 6 frames, each 7 bytes: 1 byte header + 6 bytes accel data) */
// uint16_t wml = 42;
// buffer[0] = (uint8_t) wml & 0xff;
// buffer[1] = (uint8_t) (wml >> 8) & 0xff;
// uint8_t add = static_cast<uint8_t>(Register::FIFO_WTM_0);
// uint8_t cmd[3] = { add, buffer[0], buffer[1]};
// transfer(cmd, sizeof(cmd), nullptr, 0);
// PX4_WARN("FIFO_WTM_0(0x%02x): 0x%02x",cmd[0], RegisterRead(Register::FIFO_WTM_0));
/* Enable the actual FIFO functionality: write 0x50 to 0x49. Bit #4 must always be one! */
buffer[0] = 0x10 | 0x40;
RegisterWrite(Register::FIFO_CONFIG_1, buffer[0]);
PX4_WARN("FIFO_CONFIG_1(0x%02x): 0x%02x", buffer[0], RegisterRead(Register::FIFO_CONFIG_1));
usleep(1000000);
int fifo_fill_level = 0;
uint8_t data_o[2] = { 0, 0 };
uint8_t data_i[1] = {static_cast<uint8_t>(Register::FIFO_LENGTH_0)};
data_i[0] = static_cast<uint8_t>(Register::FIFO_LENGTH_0);
transfer(&data_i[0], 1, &data_o[0], 2);
fifo_fill_level = data_o[0] + 256 * data_o[1];
PX4_WARN("fifo_fill_level %d", fifo_fill_level);
// while(fifo_fill_level < wml)
// {
// transfer(&data_i[0], 1, &data_o[0], 2);
// fifo_fill_level = data_o[0] + 256 * data_o[1];
// PX4_WARN("fifo_fill_level %d", fifo_fill_level);
// }
uint8_t custom_size = 42;
uint8_t buffer_data[custom_size] = {0};
buffer[0] = static_cast<uint8_t>(Register::FIFO_DATA);
bmi08x_accel.x = 10;
PX4_WARN("bmi08x_accel %d", bmi08x_accel.x);
transfer(&buffer[0], 1, &buffer_data[0], custom_size);
/* This is a super-simple FIFO parsing loop, hoping it will only find valid accel data packets */
for (int i = 1; i < custom_size;) {
/* Header of acceleration sensor data frame: 100001xxb, where x is INT1/INT2 tag, ignored here */
if (buffer_data[i] == (0x84 & 0x8c)) {
UnpackSensorData(&bmi08x_accel, &buffer_data[i + 1]);
PX4_WARN("Frame: %03d ax:%f ay:%f az:%f", i / 6, (double)bmi08x_accel.x, (double)bmi08x_accel.y,
(double)bmi08x_accel.z);
accel_mg[0] = bmi08x_accel.x;
accel_mg[1] = bmi08x_accel.y;
accel_mg[2] = bmi08x_accel.z;
float *data_in_mg = SensorDataTomg(accel_mg);
PX4_WARN("Frame mg: %03d ax:%f ay:%f az:%f", i / 6, (double)data_in_mg[0], (double)data_in_mg[1],
(double)data_in_mg[2]);
i += 7;
} else {
i++;
}
}
return accel_mg;
}
uint8_t BMI088_Accelerometer::CheckSensorErrReg()
{
return RegisterRead(Register::ACC_ERR_REG);
}
void BMI088_Accelerometer::UnpackSensorData(struct FIFO::bmi08x_sensor_data *sens_data, uint8_t *buffer)
{
uint16_t data_lsb;
uint16_t data_msb;
uint16_t start_idx = 0;
/* Gyro raw x data */
data_lsb = buffer[start_idx++];
data_msb = buffer[start_idx++];
sens_data->x = (int16_t)((data_msb << 8) | data_lsb);
/* Gyro raw y data */
data_lsb = buffer[start_idx++];
data_msb = buffer[start_idx++];
sens_data->y = (int16_t)((data_msb << 8) | data_lsb);
/* Gyro raw z data */
data_lsb = buffer[start_idx++];
data_msb = buffer[start_idx++];
sens_data->z = (int16_t)((data_msb << 8) | data_lsb);
}
float *BMI088_Accelerometer::SensorDataTomg(float *data)
{
data[0] = (float) data[0] / 32768.0f * 1000.0f * powf(2.0f, 24.0f + 1.0f) * 1.50f;
data[1] = (float) data[1] / 32768.0f * 1000.0f * powf(2.0f, 24.0f + 1.0f) * 1.50f;
data[2] = (float) data[2] / 32768.0f * 1000.0f * powf(2.0f, 24.0f + 1.0f) * 1.50f;
return data;
}
bool BMI088_Accelerometer::NormalRead(const hrt_abstime &timestamp_sample)
{
const int16_t tX[3] = {1, 0, 0};
const int16_t tY[3] = {0, -1, 0};
const int16_t tZ[3] = {0, 0, -1};
float x = 0;
float y = 0;
float z = 0;
uint8_t buffer[6] = {0};
uint8_t cmd[1] = {static_cast<uint8_t>(Register::ACC_READ)};
transfer(&cmd[0], 1, &buffer[0], 6);
uint8_t RATE_X_LSB = buffer[0];
uint8_t RATE_X_MSB = buffer[1];
uint8_t RATE_Y_LSB = buffer[2];
uint8_t RATE_Y_MSB = buffer[3];
uint8_t RATE_Z_LSB = buffer[4];
uint8_t RATE_Z_MSB = buffer[5];
const int16_t accel_x = combine(RATE_X_MSB, RATE_X_LSB);
const int16_t accel_y = combine(RATE_Y_MSB, RATE_Y_LSB);
const int16_t accel_z = combine(RATE_Z_MSB, RATE_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
x = accel_x * tX[0] + accel_y * tX[1] + accel_z * tX[2];
y = accel_x * tY[0] + accel_y * tY[1] + accel_z * tY[2];
z = accel_x * tZ[0] + accel_y * tZ[1] + accel_z * tZ[2];
//PX4_WARN("x: %f | y: %f | z: %f", (double)x, (double)y ,(double)z);
_px4_accel.update(timestamp_sample, x, y, z);
return true;
}
} // namespace Bosch::BMI088::Accelerometer
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,10 +35,11 @@
#include "BMI088.hpp"
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include "Bosch_BMI088_Accelerometer_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_accel.h>
namespace Bosch::BMI088::Accelerometer
{
@@ -55,11 +56,10 @@ private:
void exit_and_cleanup() override;
// Sensor Configuration
// static constexpr uint32_t RATE{1600}; // 1600 Hz
static constexpr uint32_t RATE{1600}; // 1600 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]))};
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
// Transfer data
struct FIFOTransferBuffer {
@@ -69,10 +69,6 @@ private:
uint8_t FIFO_LENGTH_1{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// Transfer data without length
struct FIFOTransferBufferWithoutLength {
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (4 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
@@ -97,7 +93,7 @@ private:
bool RegisterCheck(const register_config_t &reg_cfg);
uint8_t RegisterRead(Register reg);
uint8_t RegisterWrite(Register reg, uint8_t value);
void RegisterWrite(Register reg, uint8_t value);
void RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t clearbits);
uint16_t FIFOReadCount();
@@ -105,16 +101,14 @@ private:
void FIFOReset();
void UpdateTemperature();
void UnpackSensorData(struct FIFO::bmi08x_sensor_data *sens_data, uint8_t *buffer);
bool SelfTest();
float *ReadAccelData();
float *ReadAccelDataFIFO();
float *SensorDataTomg(float *data);
uint8_t CheckSensorErrReg();
bool SimpleFIFORead(const hrt_abstime &timestamp_sample);
bool NormalRead(const hrt_abstime &timestamp_sample);
PX4Accelerometer _px4_accel;
uORB::PublicationMulti<sensor_accel_s> _sensor_accel_pub{ORB_ID(sensor_accel)};
const enum Rotation _rotation;
float _accel_scale{0.f};
float _accel_range{0.f};
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_accel: bad transfer")};
@@ -129,7 +123,7 @@ private:
static constexpr uint8_t size_register_cfg{10};
register_config_t _register_cfg[size_register_cfg] {
// Register | Set bits, Clear bits
{ Register::ACC_PWR_CONF, 0, ACC_PWR_CONF_BIT::acc_pwr_save }, //
{ Register::ACC_PWR_CONF, 0, ACC_PWR_CONF_BIT::acc_pwr_save },
{ Register::ACC_PWR_CTRL, ACC_PWR_CTRL_BIT::acc_enable, 0 },
{ Register::ACC_CONF, ACC_CONF_BIT::acc_bwp_Normal | ACC_CONF_BIT::acc_odr_1600, Bit1 | Bit0 },
{ Register::ACC_RANGE, ACC_RANGE_BIT::acc_range_24g, 0 },
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,8 +33,6 @@
#include "BMI088_Gyroscope.hpp"
#include <px4_platform/board_dma_alloc.h>
using namespace time_literals;
namespace Bosch::BMI088::Gyroscope
@@ -42,13 +40,13 @@ namespace Bosch::BMI088::Gyroscope
BMI088_Gyroscope::BMI088_Gyroscope(const I2CSPIDriverConfig &config) :
BMI088(config),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME"_gyro: DRDY missed");
}
ConfigureSampleRate(2000);
ConfigureSampleRate(RATE);
}
BMI088_Gyroscope::~BMI088_Gyroscope()
@@ -98,13 +96,6 @@ void BMI088_Gyroscope::RunImpl()
const hrt_abstime now = hrt_absolute_time();
switch (_state) {
case STATE::SELFTEST:
//SelfTest();
_state = STATE::RESET;
ScheduleDelayed(1_ms);
break;
case STATE::RESET:
// GYRO_SOFTRESET: Writing a value of 0xB6 to this register resets the sensor.
// Following a delay of 30 ms, all configuration settings are overwritten with their reset value.
@@ -170,8 +161,90 @@ void BMI088_Gyroscope::RunImpl()
break;
case STATE::FIFO_READ: {
SimpleFIFORead(now);
hrt_abstime timestamp_sample = 0;
if (_data_ready_interrupt_enabled) {
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
} else {
perf_count(_drdy_missed_perf);
}
// push backup schedule back
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
// always check current FIFO status/count
bool success = false;
const uint8_t FIFO_STATUS = RegisterRead(Register::FIFO_STATUS);
if (FIFO_STATUS & FIFO_STATUS_BIT::Fifo_overrun) {
FIFOReset();
perf_count(_fifo_overflow_perf);
} else {
const uint8_t fifo_frame_counter = FIFO_STATUS & FIFO_STATUS_BIT::Fifo_frame_counter;
if (fifo_frame_counter > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
FIFOReset();
perf_count(_fifo_overflow_perf);
} else if (fifo_frame_counter == 0) {
perf_count(_fifo_empty_perf);
} else if (fifo_frame_counter >= 1) {
uint8_t samples = fifo_frame_counter;
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_samples + 1) {
// sample timestamp set from data ready already corresponds to _fifo_samples
if (timestamp_sample == 0) {
timestamp_sample = now - static_cast<int>(FIFO_SAMPLE_DT);
}
samples--;
}
if (FIFORead((timestamp_sample == 0) ? now : timestamp_sample, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
}
if (!success) {
_failure_count++;
// full reset if things are failing consistently
if (_failure_count > 10) {
Reset();
return;
}
}
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_cfg[_checked_register])) {
_last_config_check_timestamp = now;
_checked_register = (_checked_register + 1) % size_register_cfg;
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
Reset();
}
}
}
break;
}
}
@@ -182,28 +255,28 @@ void BMI088_Gyroscope::ConfigureGyro()
switch (GYRO_RANGE) {
case gyro_range_2000_dps:
_px4_gyro.set_scale(math::radians(1.f / 16.384f));
_px4_gyro.set_range(math::radians(2000.f));
_gyro_scale = math::radians(1.f / 16.384f);
_gyro_range = math::radians(2000.f);
break;
case gyro_range_1000_dps:
_px4_gyro.set_scale(math::radians(1.f / 32.768f));
_px4_gyro.set_range(math::radians(1000.f));
_gyro_scale = math::radians(1.f / 32.768f);
_gyro_range = math::radians(1000.f);
break;
case gyro_range_500_dps:
_px4_gyro.set_scale(math::radians(1.f / 65.536f));
_px4_gyro.set_range(math::radians(500.f));
_gyro_scale = math::radians(1.f / 65.536f);
_gyro_range = math::radians(500.f);
break;
case gyro_range_250_dps:
_px4_gyro.set_scale(math::radians(1.f / 131.072f));
_px4_gyro.set_range(math::radians(250.f));
_gyro_scale = math::radians(1.f / 131.072f);
_gyro_range = math::radians(250.f);
break;
case gyro_range_125_dps:
_px4_gyro.set_scale(math::radians(1.f / 262.144f));
_px4_gyro.set_range(math::radians(125.f));
_gyro_scale = math::radians(1.f / 262.144f);
_gyro_range = math::radians(125.f);
break;
}
}
@@ -306,16 +379,15 @@ bool BMI088_Gyroscope::RegisterCheck(const register_config_t &reg_cfg)
uint8_t BMI088_Gyroscope::RegisterRead(Register reg)
{
uint8_t add = static_cast<uint8_t>(reg);
uint8_t cmd[2] = {add, 0};
transfer(&cmd[0], 1, &cmd[1], 1);
return cmd[1];
uint8_t cmd = static_cast<uint8_t>(reg);
uint8_t value = 0;
transfer(&cmd, 1, &value, 1);
return value;
}
void BMI088_Gyroscope::RegisterWrite(Register reg, uint8_t value)
{
uint8_t add = static_cast<uint8_t>(reg);
uint8_t cmd[2] = {add, value};
uint8_t cmd[2] { (uint8_t)reg, value };
transfer(cmd, sizeof(cmd), nullptr, 0);
}
@@ -333,37 +405,50 @@ void BMI088_Gyroscope::RegisterSetAndClearBits(Register reg, uint8_t setbits, ui
bool BMI088_Gyroscope::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 1, FIFO::SIZE);
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
//PX4_WARN("Estimated transfer size: %d", transfer_size);
if (transfer((uint8_t *)&buffer, 1, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
if (transfer((uint8_t *)&buffer.cmd, 1, (uint8_t *)&buffer.f, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = timestamp_sample;
sensor_gyro.device_id = get_device_id();
float gyro_sum[3] {};
for (int i = 0; i < samples; i++) {
const FIFO::DATA &fifo_sample = buffer.f[i];
const int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
const int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
const int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
int16_t gyro_x = combine(fifo_sample.RATE_X_MSB, fifo_sample.RATE_X_LSB);
int16_t gyro_y = combine(fifo_sample.RATE_Y_MSB, fifo_sample.RATE_Y_LSB);
int16_t gyro_z = combine(fifo_sample.RATE_Z_MSB, fifo_sample.RATE_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
//gyro.x[i] = gyro_x;
gyro_y = math::negate(gyro_y);
gyro_z = math::negate(gyro_z);
rotate_3i(_rotation, gyro_x, gyro_y, gyro_z);
gyro_sum[0] += gyro_x * _gyro_scale;
gyro_sum[1] += gyro_y * _gyro_scale;
gyro_sum[2] += gyro_z * _gyro_scale;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
sensor_gyro.x = gyro_sum[0] / samples;
sensor_gyro.y = gyro_sum[1] / samples;
sensor_gyro.z = gyro_sum[2] / samples;
_px4_gyro.updateFIFO(gyro);
sensor_gyro.range = _gyro_range;
sensor_gyro.temperature = NAN;
sensor_gyro.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
return true;
}
@@ -390,125 +475,4 @@ void BMI088_Gyroscope::FIFOReset()
}
}
bool BMI088_Gyroscope::SelfTest()
{
//Datasheet page 17 self test
//Set bit0 to enable built in self test
RegisterWrite(Register::SELF_TEST, 0x01);
usleep(10000);
uint8_t res = 0;
uint8_t test_res = false;
while (true) {
res = RegisterRead(Register::SELF_TEST);
if ((res & 0x02) == 0x02) {
if ((res & 0x04) == 0x00) {
PX4_WARN("Gyro Self-test success");
test_res = true;
} else {
PX4_WARN("Gyro Self-test error");
}
break;
}
}
RegisterWrite(Register::SELF_TEST, 0x00);
return test_res;
}
bool BMI088_Gyroscope::NormalRead(const hrt_abstime &timestamp_sample)
{
float x = 0;
float y = 0;
float z = 0;
uint8_t buffer[6] = {0};
uint8_t cmd[1] = {static_cast<uint8_t>(Register::READ_GYRO)};
transfer(&cmd[0], 1, &buffer[0], 6);
uint8_t RATE_X_LSB = buffer[0];
uint8_t RATE_X_MSB = buffer[1];
uint8_t RATE_Y_LSB = buffer[2];
uint8_t RATE_Y_MSB = buffer[3];
uint8_t RATE_Z_LSB = buffer[4];
uint8_t RATE_Z_MSB = buffer[5];
const int16_t gyro_x = combine(RATE_X_MSB, RATE_X_LSB);
const int16_t gyro_y = combine(RATE_Y_MSB, RATE_Y_LSB);
const int16_t gyro_z = combine(RATE_Z_MSB, RATE_Z_LSB);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
x = gyro_x;
y = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
z = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
_px4_gyro.update(timestamp_sample, x, y, z);
return true;
}
bool BMI088_Gyroscope::SimpleFIFORead(const hrt_abstime &timestamp_sample)
{
uint8_t n_frames;
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = 0;
gyro.dt = FIFO_SAMPLE_DT;
uint8_t data_i[1] = {static_cast<uint8_t>(Register::FIFO_STATUS)};
transfer(&data_i[0], 1, &n_frames, 1);
n_frames &= 0x7F;
int n_frames_to_read = 6;
// don't read more than 8 frames at a time
if (n_frames > n_frames_to_read) {
n_frames = n_frames_to_read;
}
if (n_frames == 0) {
return false;
}
uint8_t data[6 * n_frames];
data[0] = static_cast<uint8_t>(Register::FIFO_DATA);
if (transfer(&data[0], 1, &data[0], 6 * n_frames) != PX4_OK) {
//PX4_WARN("transfer(&data[0], 1, &data[0], fifo_fill_level) != PX4_OK");
return false;
}
for (uint8_t i = 0; i < n_frames; i++) {
const uint8_t *d = &data[i * 6];
int16_t xyz[3] {
int16_t(uint16_t(d[0] | d[1] << 8)),
int16_t(uint16_t(d[2] | d[3] << 8)),
int16_t(uint16_t(d[4] | d[5] << 8))
};
gyro.x[i] = xyz[0];
gyro.y[i] = (xyz[1] == INT16_MIN) ? INT16_MAX : -xyz[1];
gyro.z[i] = (xyz[2] == INT16_MIN) ? INT16_MAX : -xyz[2];
gyro.samples++;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (gyro.samples > 0) {
//PX4_WARN("accel.samples: %d", accel.samples);
_px4_gyro.updateFIFO(gyro);
return true;
}
return true;
}
} // namespace Bosch::BMI088::Gyroscope
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,10 +35,11 @@
#include "BMI088.hpp"
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include "Bosch_BMI088_Gyroscope_Registers.hpp"
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_gyro.h>
namespace Bosch::BMI088::Gyroscope
{
@@ -55,10 +56,10 @@ private:
void exit_and_cleanup() override;
// Sensor Configuration
static constexpr uint32_t RATE{400}; // 2000 Hz
static constexpr uint32_t RATE{2000}; // 2000 Hz
static constexpr float FIFO_SAMPLE_DT{1e6f / RATE};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]))};
static constexpr int32_t FIFO_MAX_SAMPLES{FIFO::SIZE / sizeof(FIFO::DATA)};
// Transfer data
struct FIFOTransferBuffer {
@@ -95,10 +96,11 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool SelfTest();
bool NormalRead(const hrt_abstime &timestamp_sample);
bool SimpleFIFORead(const hrt_abstime &timestamp_sample);
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_gyro_s> _sensor_gyro_pub{ORB_ID(sensor_gyro)};
const enum Rotation _rotation;
float _gyro_scale{0.f};
float _gyro_range{0.f};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME"_gyro: bad transfer")};
@@ -114,13 +116,13 @@ private:
register_config_t _register_cfg[size_register_cfg] {
// Register | Set bits, Clear bits
{ Register::GYRO_RANGE, GYRO_RANGE_BIT::gyro_range_2000_dps, 0 },
{ Register::GYRO_BANDWIDTH, 0, GYRO_BANDWIDTH_BIT::gyro_bw_2000_Hz},
{ Register::GYRO_BANDWIDTH, 0, GYRO_BANDWIDTH_BIT::gyro_bw_532_Hz },
{ Register::GYRO_INT_CTRL, GYRO_INT_CTRL_BIT::fifo_en, 0 },
{ Register::INT3_INT4_IO_CONF, 0, INT3_INT4_IO_CONF_BIT::Int3_od | INT3_INT4_IO_CONF_BIT::Int3_lvl },
{ Register::INT3_INT4_IO_MAP, INT3_INT4_IO_MAP_BIT::Int3_fifo, 0 },
{ Register::FIFO_WM_ENABLE, FIFO_WM_ENABLE_BIT::fifo_wm_enable, 0 },
{ Register::FIFO_CONFIG_0, 0, 0 }, // fifo_water_mark_level_trigger_retain<6:0>
{ Register::FIFO_CONFIG_1, FIFO_CONFIG_1_BIT::FIFO_MODE_STREAM, 0 },
{ Register::FIFO_CONFIG_1, FIFO_CONFIG_1_BIT::FIFO_MODE, 0 },
};
};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2019 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -48,15 +48,14 @@ static constexpr uint8_t Bit7 = (1 << 7);
static constexpr uint32_t SPI_SPEED = 10 * 1000 * 1000; // 10MHz SPI serial interface
static constexpr uint32_t I2C_400_SPEED = 400 * 1000; // 400kHz I2C interface
static constexpr uint32_t I2C_200_SPEED = 200 * 1000; // 200kHz I2C interface
static constexpr uint32_t I2C_100_SPEED = 100 * 1000; // 100kHz I2C interface
static constexpr uint32_t I2C_SPEED = 400 * 1000; // 400kHz I2C interface
static constexpr uint8_t DIR_READ = 0x80;
static constexpr uint8_t ID = 0x1E;
static constexpr uint8_t ID_088 = 0x1E;
static constexpr uint8_t ID_090L = 0x1A;
static constexpr uint8_t ACC_I2C_ADDR_PRIMARY = 0x18;
static constexpr uint8_t ACC_I2C_ADDR_PRIMARY = 0x18;
static constexpr uint8_t ACC_I2C_ADDR_SECONDARY = 0x19;
enum class Register : uint8_t {
@@ -74,7 +73,7 @@ enum class Register : uint8_t {
ACC_CONF = 0x40,
ACC_RANGE = 0x41,
FIFO_DOWN_SAMPLING = 0x45,
FIFO_DOWNS = 0x45,
FIFO_WTM_0 = 0x46,
FIFO_WTM_1 = 0x47,
FIFO_CONFIG_0 = 0x48,
@@ -87,24 +86,15 @@ enum class Register : uint8_t {
ACC_PWR_CONF = 0x7C,
ACC_PWR_CTRL = 0x7D,
ACC_SOFTRESET = 0x7E,
ACC_SELF_TEST = 0x6D,
ACC_I2C_ADDR_PRIMARY_REG = 0x6D,
ACC_I2C_ADDR_SECONDARY_REG = 0x19,
ACC_READ = 0x12
};
// ACC_CONF
enum ACC_CONF_BIT : uint8_t {
// [7:4] acc_bwp
acc_bwp_Normal = Bit7 | Bit5, // Filter setting normal
// [7:4] acc_bwp
acc_bwp_osr_4 = Bit7, // OSR4
// [3:0] acc_odr
acc_odr_1600 = Bit3 | Bit2, // ODR 1600 Hz
// [3:0] acc_odr
acc_odr_12_5 = Bit2 | Bit0, // ODR 12.5 Hz
// [3:0] acc_odr
acc_odr_100 = Bit3, // ODR 100 Hz
};
// ACC_RANGE
@@ -158,7 +148,7 @@ enum ACC_PWR_CTRL_BIT : uint8_t {
namespace FIFO
{
static constexpr size_t SIZE = 1024;
// 1. Acceleration sensor data frame - Frame length: 7 bytes (1 byte header + 6 bytes payload)
// Payload: the next bytes contain the sensor data in the same order as defined in the register map (addresses 0x12 0x17).
@@ -176,8 +166,6 @@ struct DATA {
uint8_t ACC_Z_LSB;
uint8_t ACC_Z_MSB;
};
static constexpr size_t SIZE = 1024;
//static constexpr size_t SIZE = sizeof(DATA) * 10;
static_assert(sizeof(DATA) == 7);
enum header : uint8_t {
@@ -187,15 +175,6 @@ enum header : uint8_t {
FIFO_input_config_frame = 0b01001000,
sample_drop_frame = 0b01010000,
};
struct bmi08x_sensor_data {
/*! X-axis sensor data */
int16_t x;
/*! Y-axis sensor data */
int16_t y;
/*! Z-axis sensor data */
int16_t z;
};
} // namespace FIFO
} // namespace Bosch::BMI088::Accelerometer
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -48,9 +48,7 @@ static constexpr uint8_t Bit7 = (1 << 7);
static constexpr uint32_t SPI_SPEED = 10 * 1000 * 1000; // 10MHz SPI serial interface
static constexpr uint32_t I2C_400_SPEED = 400 * 1000; // 400kHz I2C interface
static constexpr uint32_t I2C_200_SPEED = 200 * 1000; // 200kHz I2C interface
static constexpr uint32_t I2C_100_SPEED = 100 * 1000; // 100kHz I2C interface
static constexpr uint32_t I2C_SPEED = 400 * 1000; // 400kHz I2C interface
static constexpr uint8_t DIR_READ = 0x80;
@@ -76,8 +74,6 @@ enum class Register : uint8_t {
FIFO_CONFIG_0 = 0x3D,
FIFO_CONFIG_1 = 0x3E,
FIFO_DATA = 0x3F,
SELF_TEST = 0x3C,
READ_GYRO = 0x02,
};
// FIFO_STATUS
@@ -97,9 +93,7 @@ enum GYRO_RANGE_BIT : uint8_t {
// GYRO_BANDWIDTH
enum GYRO_BANDWIDTH_BIT : uint8_t {
gyro_bw_100_Hz = Bit2 | Bit1 | Bit0,
gyro_bw_200_Hz = Bit4,
gyro_bw_2000_Hz = 0x00,
gyro_bw_532_Hz = Bit2 | Bit1 | Bit0
};
// GYRO_INT_CTRL
@@ -131,7 +125,6 @@ enum FIFO_WM_ENABLE_BIT : uint8_t {
// FIFO_CONFIG_1
enum FIFO_CONFIG_1_BIT : uint8_t {
FIFO_MODE = Bit6,
FIFO_MODE_STREAM = Bit7,
};
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2019-2020 PX4 Development Team. All rights reserved.
# Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -48,7 +48,5 @@ px4_add_module(
bmi088_i2c_main.cpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020, 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -60,19 +60,20 @@ extern "C" int bmi088_i2c_main(int argc, char *argv[])
uint16_t type = 0;
const char *name = MODULE_NAME;
while ((ch = cli.getOpt(argc, argv, "AGR:")) != EOF) {
switch (ch) {
case 'A':
type = DRV_ACC_DEVTYPE_BMI088;
name = MODULE_NAME "_accel";
cli.i2c_address = 0x18;
cli.default_i2c_frequency = 400 * 1000;
break;
case 'G':
type = DRV_GYR_DEVTYPE_BMI088;
name = MODULE_NAME "_gyro";
cli.i2c_address = 0x69;
cli.default_i2c_frequency = 400 * 1000;
break;
case 'R':
@@ -92,17 +93,14 @@ extern "C" int bmi088_i2c_main(int argc, char *argv[])
if (!strcmp(verb, "start")) {
return ThisDriver::module_start(cli, iterator);
}
if (!strcmp(verb, "stop")) {
} else if (!strcmp(verb, "stop")) {
return ThisDriver::module_stop(iterator);
}
if (!strcmp(verb, "status")) {
} else if (!strcmp(verb, "status")) {
return ThisDriver::module_status(iterator);
}
PX4_WARN("print_usage1");
ThisDriver::print_usage();
return -1;
}
+16 -6
View File
@@ -68,7 +68,7 @@ using namespace time_literals;
FXAS21002C::FXAS21002C(device::Device *interface, const I2CSPIDriverConfig &config) :
I2CSPIDriver(config),
_interface(interface),
_px4_gyro(_interface->get_device_id(), config.rotation),
_rotation(config.rotation),
_sample_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": read")),
_errors(perf_alloc(PC_COUNT, MODULE_NAME": err")),
_bad_registers(perf_alloc(PC_COUNT, MODULE_NAME": bad register")),
@@ -186,7 +186,7 @@ int FXAS21002C::set_range(unsigned max_dps)
set_standby(_current_rate, true);
_px4_gyro.set_scale(new_range_scale_dps_digit / 180.0f * M_PI_F);
_gyro_scale = new_range_scale_dps_digit / 180.0f * M_PI_F);
modify_reg(FXAS21002C_CTRL_REG0, CTRL_REG0_FS_MASK, bits);
set_standby(_current_rate, false);
@@ -378,9 +378,6 @@ void FXAS21002C::RunImpl()
}
// report the error count as the number of bad register reads. This allows the higher level
_px4_gyro.set_error_count(perf_event_count(_bad_registers));
_px4_gyro.update(timestamp_sample, x_raw, y_raw, z_raw);
if (hrt_elapsed_time(&_last_temperature_update) > 100_ms) {
/*
* The TEMP register contains an 8-bit 2's complement temperature value with a range
@@ -389,10 +386,23 @@ void FXAS21002C::RunImpl()
* mode and actively measuring the angular rate.
*/
const float temperature = read_reg(FXAS21002C_TEMP) * 1.0f;
_px4_gyro.set_temperature(temperature);
_last_temperature = temperature;
_last_temperature_update = timestamp_sample;
}
// sensor_gyro
sensor_gyro_s sensor_gyro{};
sensor_gyro.timestamp_sample = time_now_us;
sensor_gyro.device_id = 1;
sensor_gyro.x = gyro(0);
sensor_gyro.y = gyro(1);
sensor_gyro.z = gyro(2);
sensor_gyro.range = math::radians(2000.f);
sensor_gyro.temperature = NAN;
sensor_gyro.error_count = perf_event_count(_bad_registers);
sensor_gyro.timestamp = hrt_absolute_time();
_sensor_gyro_pub.publish(sensor_gyro);
/* stop the perf counter */
perf_end(_sample_perf);
}
@@ -40,7 +40,6 @@
#pragma once
#include <drivers/device/Device.hpp>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <perf/perf_counter.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/i2c_spi_buses.h>
@@ -211,7 +210,6 @@ protected:
private:
device::Device *_interface;
PX4Gyroscope _px4_gyro;
unsigned _current_rate{800};
+14 -10
View File
@@ -56,18 +56,13 @@ const uint8_t FXOS8701CQ::_checked_registers[FXOS8701C_NUM_CHECKED_REGISTERS] =
FXOS8701CQ::FXOS8701CQ(device::Device *interface, const I2CSPIDriverConfig &config) :
I2CSPIDriver(config),
_interface(interface),
_px4_accel(interface->get_device_id(), config.rotation),
#if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
_px4_mag(interface->get_device_id(), config.rotation),
_mag_sample_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": mag read")),
#endif
_accel_sample_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": acc read")),
_bad_registers(perf_alloc(PC_COUNT, MODULE_NAME": bad reg")),
_accel_duplicates(perf_alloc(PC_COUNT, MODULE_NAME": acc dupe"))
{
#if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
_px4_mag.set_scale(0.001f);
#endif
}
FXOS8701CQ::~FXOS8701CQ()
@@ -195,7 +190,7 @@ FXOS8701CQ::accel_set_range(unsigned max_g)
modify_reg(FXOS8701CQ_XYZ_DATA_CFG, XYZ_DATA_CFG_FS_MASK, setbits);
_px4_accel.set_scale(accel_range_scale);
_accel_scale = accel_range_scale;
return OK;
}
@@ -335,9 +330,6 @@ void FXOS8701CQ::RunImpl()
}
// report the error count as the sum of the number of bad register reads and bad values.
_px4_accel.set_error_count(perf_event_count(_bad_registers));
_px4_accel.update(timestamp_sample, x, y, z);
if (hrt_elapsed_time(&_last_temperature_update) > 100_ms) {
/*
* Eight-bit 2s complement sensor temperature value with 0.96 °C/LSB sensitivity.
@@ -349,9 +341,21 @@ void FXOS8701CQ::RunImpl()
_last_temperature_update = timestamp_sample;
float temperature = (read_reg(FXOS8701CQ_TEMP)) * 0.96f;
_px4_accel.set_temperature(temperature);
_last_temperature = temperature;
}
sensor_accel_s sensor_accel{};
sensor_accel.timestamp_sample = timestamp_sample;
sensor_accel.device_id = get_device_id();
sensor_accel.x = x;
sensor_accel.y = y;
sensor_accel.z = z;
sensor_accel.range = 16.f * CONSTANTS_ONE_G;
sensor_accel.temperature = _last_temperature;
sensor_accel.error_count = perf_event_count(_bad_registers);
sensor_accel.timestamp = hrt_absolute_time();
_sensor_accel_pub.publish(sensor_accel);
#if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
@@ -41,7 +41,6 @@
#include <drivers/device/i2c.h>
#include <drivers/device/spi.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/i2c_spi_buses.h>
@@ -231,9 +230,6 @@ private:
*/
int accel_set_samplerate(unsigned frequency);
PX4Accelerometer _px4_accel;
#if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
PX4Magnetometer _px4_mag;
hrt_abstime _mag_last_measure{0};
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2019-2021 PX4 Development Team. All rights reserved.
# Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -35,13 +35,12 @@ px4_add_module(
MODULE drivers__imu__invensense__icm20602
MAIN icm20602
COMPILE_FLAGS
${MAX_CUSTOM_OPT_LEVEL}
SRCS
icm20602_main.cpp
ICM20602.cpp
ICM20602.hpp
icm20602_main.cpp
InvenSense_ICM20602_registers.hpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
+107 -207
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2019-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "ICM20602.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ ICM20602::ICM20602(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM20602::~ICM20602()
@@ -131,14 +135,19 @@ bool ICM20602::StoreCheckedRegisterValue(Register reg)
int ICM20602::probe()
{
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
if (whoami != WHOAMI) {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
return PX4_ERROR;
if (whoami == WHOAMI) {
return PX4_OK;
} else {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
}
}
return PX4_OK;
return PX4_ERROR;
}
void ICM20602::RunImpl()
@@ -196,8 +205,8 @@ void ICM20602::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -244,7 +253,7 @@ void ICM20602::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_gyro_samples;
@@ -310,13 +319,14 @@ void ICM20602::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_cfg[_checked_register])) {
_last_config_check_timestamp = now;
_checked_register = (_checked_register + 1) % size_register_cfg;
@@ -324,6 +334,7 @@ void ICM20602::RunImpl()
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
PX4_DEBUG("Force reset because register 0x%02hhX check failed ", (uint8_t)_register_cfg[_checked_register].reg);
Reset();
}
}
@@ -333,65 +344,10 @@ void ICM20602::RunImpl()
}
}
void ICM20602::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::ACCEL_CONFIG) & (Bit4 | Bit3); // [4:3] ACCEL_FS_SEL[1:0]
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_2G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 16384.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
}
}
void ICM20602::ConfigureGyro()
{
const uint8_t FS_SEL = RegisterRead(Register::GYRO_CONFIG) & (Bit4 | Bit3); // [4:3] FS_SEL[1:0]
float range_dps = 0.f;
switch (FS_SEL) {
case FS_SEL_250_DPS:
range_dps = 250.f;
break;
case FS_SEL_500_DPS:
range_dps = 500.f;
break;
case FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void ICM20602::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt * SAMPLES_PER_TRANSFER
const float min_interval = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
@@ -438,9 +394,6 @@ bool ICM20602::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -463,7 +416,7 @@ bool ICM20602::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool ICM20602::DataReadyInterruptDisable()
@@ -472,7 +425,7 @@ bool ICM20602::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
bool ICM20602::RegisterCheck(const register_config_t &reg_cfg)
@@ -521,32 +474,46 @@ void ICM20602::RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t cl
uint16_t ICM20602::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ;
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM20602::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 3, FIFO::SIZE);
// FIFO transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 3 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
if ((fifo_count_bytes >= FIFO::SIZE) || (fifo_count_samples > FIFO_MAX_SAMPLES)) {
const uint8_t valid_samples = math::min(samples, fifo_count_samples);
if ((fifo_count_bytes >= FIFO::SIZE) || (valid_samples > FIFO_MAX_SAMPLES)) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
@@ -556,17 +523,45 @@ bool ICM20602::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
const uint8_t valid_samples = math::min(samples, fifo_count_samples);
if (valid_samples > 0) {
// use raw temperature to first validate FIFO transfer
if (ProcessTemperature(buffer.f, valid_samples)) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = valid_samples;
sensor_imu_fifo.accel_scale = ACCEL_SCALE;
sensor_imu_fifo.gyro_scale = GYRO_SCALE;
if (ProcessAccel(timestamp_sample, buffer.f, valid_samples)) {
return true;
}
float temperature_sum{0};
for (int i = 0; i < valid_samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
sensor_imu_fifo.accel_x[i] = combine(buffer.f[i].ACCEL_XOUT_H, buffer.f[i].ACCEL_XOUT_L);
sensor_imu_fifo.accel_y[i] = math::negate(combine(buffer.f[i].ACCEL_YOUT_H, buffer.f[i].ACCEL_YOUT_L));
sensor_imu_fifo.accel_z[i] = math::negate(combine(buffer.f[i].ACCEL_ZOUT_H, buffer.f[i].ACCEL_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
sensor_imu_fifo.gyro_x[i] = combine(buffer.f[i].GYRO_XOUT_H, buffer.f[i].GYRO_XOUT_L);
sensor_imu_fifo.gyro_y[i] = math::negate(combine(buffer.f[i].GYRO_YOUT_H, buffer.f[i].GYRO_YOUT_L));
sensor_imu_fifo.gyro_z[i] = math::negate(combine(buffer.f[i].GYRO_ZOUT_H, buffer.f[i].GYRO_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
temperature_sum += combine(buffer.f[i].TEMP_OUT_H, buffer.f[i].TEMP_OUT_L);
}
// use average temperature reading
const float temperature_avg = temperature_sum / valid_samples;
const float temperature_C = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
sensor_imu_fifo.temperature = temperature_C;
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf)
+ perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
@@ -594,124 +589,29 @@ void ICM20602::FIFOReset()
}
}
static bool fifo_accel_equal(const FIFO::DATA &f0, const FIFO::DATA &f1)
float ICM20602::ReadTemperature()
{
return (memcmp(&f0.ACCEL_XOUT_H, &f1.ACCEL_XOUT_H, 6) == 0);
}
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ};
uint8_t TEMP_OUT_H{0};
uint8_t TEMP_OUT_L{0};
} buffer{};
bool ICM20602::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
bool bad_data = false;
// accel data is doubled in FIFO, but might be shifted
int accel_first_sample = 1;
if (samples >= 4) {
if (fifo_accel_equal(fifo[0], fifo[1]) && fifo_accel_equal(fifo[2], fifo[3])) {
// [A0, A1, A2, A3]
// A0==A1, A2==A3
accel_first_sample = 1;
} else if (fifo_accel_equal(fifo[1], fifo[2])) {
// [A0, A1, A2, A3]
// A0, A1==A2, A3
accel_first_sample = 0;
} else {
// no matching accel samples is an error
bad_data = true;
perf_count(_bad_transfer_perf);
}
}
for (int i = accel_first_sample; i < samples; i = i + SAMPLES_PER_TRANSFER) {
int16_t accel_x = combine(fifo[i].ACCEL_XOUT_H, fifo[i].ACCEL_XOUT_L);
int16_t accel_y = combine(fifo[i].ACCEL_YOUT_H, fifo[i].ACCEL_YOUT_L);
int16_t accel_z = combine(fifo[i].ACCEL_ZOUT_H, fifo[i].ACCEL_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
return !bad_data;
}
void ICM20602::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_XOUT_H, fifo[i].GYRO_XOUT_L);
const int16_t gyro_y = combine(fifo[i].GYRO_YOUT_H, fifo[i].GYRO_YOUT_L);
const int16_t gyro_z = combine(fifo[i].GYRO_ZOUT_H, fifo[i].GYRO_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
bool ICM20602::ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples)
{
int16_t temperature[FIFO_MAX_SAMPLES];
float temperature_sum{0};
for (int i = 0; i < samples; i++) {
const int16_t t = combine(fifo[i].TEMP_OUT_H, fifo[i].TEMP_OUT_L);
temperature_sum += t;
temperature[i] = t;
}
const float temperature_avg = temperature_sum / samples;
for (int i = 0; i < samples; i++) {
// temperature changing wildly is an indication of a transfer error
if (fabsf(temperature[i] - temperature_avg) > 1000) {
perf_count(_bad_transfer_perf);
return false;
}
}
// use average temperature reading
const float temperature_C = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(temperature_C)
&& (temperature_C >= TEMPERATURE_SENSOR_MIN)
&& (temperature_C <= TEMPERATURE_SENSOR_MAX)) {
_px4_accel.set_temperature(temperature_C);
_px4_gyro.set_temperature(temperature_C);
return true;
} else {
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return NAN;;
}
return false;
const int16_t TEMP_OUT = combine(buffer.TEMP_OUT_H, buffer.TEMP_OUT_L);
const float TEMP_degC = (TEMP_OUT / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)
&& (TEMP_degC >= TEMPERATURE_SENSOR_MIN)
&& (TEMP_degC <= TEMPERATURE_SENSOR_MAX)) {
return TEMP_degC;
}
return NAN;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2019-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM20602_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM20602;
class ICM20602 : public device::SPI, public I2CSPIDriver<ICM20602>
@@ -71,22 +72,13 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f};
static constexpr int32_t SAMPLES_PER_TRANSFER{2}; // ensure at least 1 new accel sample per transfer
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; // 8000 Hz gyro
static constexpr float ACCEL_RATE{GYRO_RATE / SAMPLES_PER_TRANSFER}; // 4000 Hz accel
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (3 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr float ACCEL_SCALE{CONSTANTS_ONE_G / 2048.f}; // ACCEL_FS_SEL_16G:
static constexpr float GYRO_SCALE{math::radians(2000.f / 32768.f)}; // FS_SEL_2000_DPS:
struct register_config_t {
Register reg;
@@ -99,8 +91,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
void ConfigureFIFOWatermark(uint8_t samples);
@@ -120,14 +110,13 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
bool ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples);
float ReadTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
@@ -41,7 +41,5 @@ px4_add_module(
icm20608g_main.cpp
InvenSense_ICM20608G_registers.hpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
+102 -178
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2019-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "ICM20608G.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ ICM20608G::ICM20608G(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM20608G::~ICM20608G()
@@ -108,7 +112,7 @@ void ICM20608G::print_status()
bool ICM20608G::StoreCheckedRegisterValue(Register reg)
{
// 3 retries
for (int i = 0; i < 3; i++) {
for (int retry = 0; retry < 3; retry++) {
uint8_t read1 = RegisterRead(reg);
uint8_t read2 = RegisterRead(reg);
@@ -131,14 +135,19 @@ bool ICM20608G::StoreCheckedRegisterValue(Register reg)
int ICM20608G::probe()
{
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
if (whoami != WHOAMI) {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
return PX4_ERROR;
if (whoami == WHOAMI) {
return PX4_OK;
} else {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
}
}
return PX4_OK;
return PX4_ERROR;
}
void ICM20608G::RunImpl()
@@ -187,8 +196,8 @@ void ICM20608G::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -196,6 +205,8 @@ void ICM20608G::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
_temperature = ReadTemperature();
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
@@ -234,7 +245,7 @@ void ICM20608G::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
} else {
@@ -271,7 +282,7 @@ void ICM20608G::RunImpl()
FIFOReset();
perf_count(_fifo_overflow_perf);
} else if (samples >= SAMPLES_PER_TRANSFER) {
} else if (samples >= 1) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -287,6 +298,7 @@ void ICM20608G::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
@@ -301,13 +313,14 @@ void ICM20608G::RunImpl()
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
PX4_DEBUG("Force reset because register 0x%02hhX check failed ", (uint8_t)_register_cfg[_checked_register].reg);
Reset();
}
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature = ReadTemperature();
_temperature_update_timestamp = now;
}
}
@@ -317,65 +330,10 @@ void ICM20608G::RunImpl()
}
}
void ICM20608G::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::ACCEL_CONFIG) & (Bit4 | Bit3); // [4:3] ACCEL_FS_SEL[1:0]
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_2G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 16384.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
}
}
void ICM20608G::ConfigureGyro()
{
const uint8_t FS_SEL = RegisterRead(Register::GYRO_CONFIG) & (Bit4 | Bit3); // [4:3] FS_SEL[1:0]
float range_dps = 0.f;
switch (FS_SEL) {
case FS_SEL_250_DPS:
range_dps = 250.f;
break;
case FS_SEL_500_DPS:
range_dps = 500.f;
break;
case FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void ICM20608G::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt * SAMPLES_PER_TRANSFER
const float min_interval = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
@@ -400,9 +358,6 @@ bool ICM20608G::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -429,7 +384,7 @@ bool ICM20608G::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool ICM20608G::DataReadyInterruptDisable()
@@ -438,7 +393,7 @@ bool ICM20608G::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
bool ICM20608G::RegisterCheck(const register_config_t &reg_cfg)
@@ -487,31 +442,75 @@ void ICM20608G::RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t c
uint16_t ICM20608G::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ;
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM20608G::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 1, FIFO::SIZE);
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + samples (FIFO::DATA)
const size_t transfer_size = 1 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
const uint8_t valid_samples = samples;
ProcessGyro(timestamp_sample, buffer.f, samples);
return ProcessAccel(timestamp_sample, buffer.f, samples);
if (valid_samples > 0) {
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = valid_samples;
sensor_imu_fifo.accel_scale = ACCEL_SCALE;
sensor_imu_fifo.gyro_scale = GYRO_SCALE;
for (int i = 0; i < valid_samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
sensor_imu_fifo.accel_x[i] = combine(buffer.f[i].ACCEL_XOUT_H, buffer.f[i].ACCEL_XOUT_L);
sensor_imu_fifo.accel_y[i] = math::negate(combine(buffer.f[i].ACCEL_YOUT_H, buffer.f[i].ACCEL_YOUT_L));
sensor_imu_fifo.accel_z[i] = math::negate(combine(buffer.f[i].ACCEL_ZOUT_H, buffer.f[i].ACCEL_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
sensor_imu_fifo.gyro_x[i] = combine(buffer.f[i].GYRO_XOUT_H, buffer.f[i].GYRO_XOUT_L);
sensor_imu_fifo.gyro_y[i] = math::negate(combine(buffer.f[i].GYRO_YOUT_H, buffer.f[i].GYRO_YOUT_L));
sensor_imu_fifo.gyro_z[i] = math::negate(combine(buffer.f[i].GYRO_ZOUT_H, buffer.f[i].GYRO_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
sensor_imu_fifo.temperature = (hrt_elapsed_time(&_temperature_update_timestamp) < 5_s) ? _temperature : NAN;
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf)
+ perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
}
void ICM20608G::FIFOReset()
@@ -537,105 +536,30 @@ void ICM20608G::FIFOReset()
}
}
static bool fifo_accel_equal(const FIFO::DATA &f0, const FIFO::DATA &f1)
float ICM20608G::ReadTemperature()
{
return (memcmp(&f0.ACCEL_XOUT_H, &f1.ACCEL_XOUT_H, 6) == 0);
}
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ};
uint8_t TEMP_OUT_H{0};
uint8_t TEMP_OUT_L{0};
} buffer{};
bool ICM20608G::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
bool bad_data = false;
// accel data is doubled in FIFO, but might be shifted
int accel_first_sample = 1;
if (samples >= 4) {
if (fifo_accel_equal(fifo[0], fifo[1]) && fifo_accel_equal(fifo[2], fifo[3])) {
// [A0, A1, A2, A3]
// A0==A1, A2==A3
accel_first_sample = 1;
} else if (fifo_accel_equal(fifo[1], fifo[2])) {
// [A0, A1, A2, A3]
// A0, A1==A2, A3
accel_first_sample = 0;
} else {
// no matching accel samples is an error
bad_data = true;
perf_count(_bad_transfer_perf);
}
}
for (int i = accel_first_sample; i < samples; i = i + SAMPLES_PER_TRANSFER) {
int16_t accel_x = combine(fifo[i].ACCEL_XOUT_H, fifo[i].ACCEL_XOUT_L);
int16_t accel_y = combine(fifo[i].ACCEL_YOUT_H, fifo[i].ACCEL_YOUT_L);
int16_t accel_z = combine(fifo[i].ACCEL_ZOUT_H, fifo[i].ACCEL_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
return !bad_data;
}
void ICM20608G::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_XOUT_H, fifo[i].GYRO_XOUT_L);
const int16_t gyro_y = combine(fifo[i].GYRO_YOUT_H, fifo[i].GYRO_YOUT_L);
const int16_t gyro_z = combine(fifo[i].GYRO_ZOUT_H, fifo[i].GYRO_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void ICM20608G::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ;
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
return NAN;
}
const int16_t TEMP_OUT = combine(temperature_buf[1], temperature_buf[2]);
const int16_t TEMP_OUT = combine(buffer.TEMP_OUT_H, buffer.TEMP_OUT_L);
const float TEMP_degC = (TEMP_OUT / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
if (PX4_ISFINITE(TEMP_degC)
&& (TEMP_degC >= TEMPERATURE_SENSOR_MIN)
&& (TEMP_degC <= TEMPERATURE_SENSOR_MAX)) {
return TEMP_degC;
}
return NAN;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2019-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM20608G_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM20608G;
class ICM20608G : public device::SPI, public I2CSPIDriver<ICM20608G>
@@ -71,20 +72,13 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f};
static constexpr int32_t SAMPLES_PER_TRANSFER{2}; // ensure at least 1 new accel sample per transfer
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; // 8000 Hz gyro
static constexpr float ACCEL_RATE{GYRO_RATE / SAMPLES_PER_TRANSFER}; // 4000 Hz accel
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0])), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (1 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr float ACCEL_SCALE{CONSTANTS_ONE_G / 2048.f}; // ACCEL_FS_SEL_16G
static constexpr float GYRO_SCALE{math::radians(2000.f / 32768.f)}; // FS_SEL
struct register_config_t {
Register reg;
@@ -97,8 +91,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
static int DataReadyInterruptCallback(int irq, void *context, void *arg);
@@ -117,14 +109,15 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
float ReadTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2019-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2019-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -42,6 +42,8 @@
#include <cstdint>
namespace InvenSense_ICM20608G
{
// TODO: move to a central header
static constexpr uint8_t Bit0 = (1 << 0);
static constexpr uint8_t Bit1 = (1 << 1);
@@ -52,8 +54,6 @@ static constexpr uint8_t Bit5 = (1 << 5);
static constexpr uint8_t Bit6 = (1 << 6);
static constexpr uint8_t Bit7 = (1 << 7);
namespace InvenSense_ICM20608G
{
static constexpr uint32_t SPI_SPEED = 8 * 1000 * 1000; // 8MHz SPI serial interface
static constexpr uint8_t DIR_READ = 0x80;
@@ -61,8 +61,11 @@ static constexpr uint8_t WHOAMI = 0xAF;
static constexpr float TEMPERATURE_SENSITIVITY = 326.8f; // LSB/C
static constexpr float TEMPERATURE_OFFSET = 25.f; // C
static constexpr float TEMPERATURE_SENSOR_MIN = -40.f; // °C
static constexpr float TEMPERATURE_SENSOR_MAX = 85.f; // °C
enum class Register : uint8_t {
CONFIG = 0x1A,
GYRO_CONFIG = 0x1B,
ACCEL_CONFIG = 0x1C,
@@ -34,6 +34,7 @@ px4_add_module(
MODULE drivers__imu__invensense__icm20649
MAIN icm20649
COMPILE_FLAGS
${MAX_CUSTOM_OPT_LEVEL}
SRCS
ICM20649.cpp
ICM20649.hpp
@@ -41,6 +42,4 @@ px4_add_module(
InvenSense_ICM20649_registers.hpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
)
+90 -268
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "ICM20649.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -43,15 +45,12 @@ static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
ICM20649::ICM20649(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM20649::~ICM20649()
@@ -61,7 +60,6 @@ ICM20649::~ICM20649()
perf_free(_fifo_empty_perf);
perf_free(_fifo_overflow_perf);
perf_free(_fifo_reset_perf);
perf_free(_drdy_missed_perf);
}
int ICM20649::init()
@@ -79,18 +77,11 @@ int ICM20649::init()
bool ICM20649::Reset()
{
_state = STATE::RESET;
DataReadyInterruptDisable();
ScheduleClear();
ScheduleNow();
return true;
}
void ICM20649::exit_and_cleanup()
{
DataReadyInterruptDisable();
I2CSPIDriverBase::exit_and_cleanup();
}
void ICM20649::print_status()
{
I2CSPIDriverBase::print_status();
@@ -102,13 +93,13 @@ void ICM20649::print_status()
perf_print_counter(_fifo_empty_perf);
perf_print_counter(_fifo_overflow_perf);
perf_print_counter(_fifo_reset_perf);
perf_print_counter(_drdy_missed_perf);
}
int ICM20649::probe()
{
for (int i = 0; i < 3; i++) {
uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
const uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
if (whoami == WHOAMI) {
return PX4_OK;
@@ -175,19 +166,12 @@ void ICM20649::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
_temperature = ReadTemperature();
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
FIFOReset();
@@ -207,23 +191,6 @@ void ICM20649::RunImpl()
break;
case STATE::FIFO_READ: {
hrt_abstime timestamp_sample = now;
if (_data_ready_interrupt_enabled) {
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
} else {
perf_count(_drdy_missed_perf);
}
// push backup schedule back
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
// always check current FIFO count
bool success = false;
const uint16_t fifo_count = FIFOReadCount();
@@ -241,7 +208,7 @@ void ICM20649::RunImpl()
if (samples > _fifo_gyro_samples) {
// grab desired number of samples, but reschedule next cycle sooner
int extra_samples = samples - _fifo_gyro_samples;
const int extra_samples = samples - _fifo_gyro_samples;
samples = _fifo_gyro_samples;
if (_fifo_gyro_samples > extra_samples) {
@@ -260,7 +227,7 @@ void ICM20649::RunImpl()
}
if (samples == _fifo_gyro_samples) {
if (FIFORead(timestamp_sample, samples)) {
if (FIFORead(now, samples)) {
success = true;
if (_failure_count > 0) {
@@ -275,13 +242,14 @@ void ICM20649::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_bank0_cfg[_checked_register_bank0])
&& RegisterCheck(_register_bank2_cfg[_checked_register_bank2])
) {
@@ -298,7 +266,7 @@ void ICM20649::RunImpl()
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature = ReadTemperature();
_temperature_update_timestamp = now;
}
}
@@ -308,65 +276,10 @@ void ICM20649::RunImpl()
}
}
void ICM20649::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::BANK_2::ACCEL_CONFIG) & (Bit2 | Bit1); // 2:1 ACCEL_FS_SEL[1:0]
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_30G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 1024.f);
_px4_accel.set_range(30.f * CONSTANTS_ONE_G);
break;
}
}
void ICM20649::ConfigureGyro()
{
const uint8_t GYRO_FS_SEL = RegisterRead(Register::BANK_2::GYRO_CONFIG_1) & (Bit2 | Bit1); // 2:1 GYRO_FS_SEL[1:0]
float range_dps = 0.f;
switch (GYRO_FS_SEL) {
case GYRO_FS_SEL_500_DPS:
range_dps = 500.f;
break;
case GYRO_FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case GYRO_FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
case GYRO_FS_SEL_4000_DPS:
range_dps = 4000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void ICM20649::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt * SAMPLES_PER_TRANSFER
const float min_interval = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
@@ -415,57 +328,9 @@ bool ICM20649::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
int ICM20649::DataReadyInterruptCallback(int irq, void *context, void *arg)
{
static_cast<ICM20649 *>(arg)->DataReady();
return 0;
}
void ICM20649::DataReady()
{
// at least the required number of samples in the FIFO
if (++_drdy_count >= _fifo_gyro_samples) {
_drdy_timestamp_sample.store(hrt_absolute_time());
_drdy_count -= _fifo_gyro_samples;
ScheduleNow();
}
}
bool ICM20649::DataReadyInterruptConfigure()
{
// TODO: enable data ready interrupt
return false;
#if 0
if (_drdy_gpio == 0) {
return false;
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
#endif
}
bool ICM20649::DataReadyInterruptDisable()
{
// TODO: enable data ready interrupt
return false;
#if 0
if (_drdy_gpio == 0) {
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
#endif
}
template <typename T>
bool ICM20649::RegisterCheck(const T &reg_cfg)
{
@@ -520,54 +385,89 @@ uint16_t ICM20649::FIFOReadCount()
{
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ;
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM20649::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 3, FIFO::SIZE);
// FIFO transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 3 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
const uint8_t valid_samples = math::min(samples, fifo_count_samples);
if (fifo_count_bytes >= FIFO::SIZE) {
if ((fifo_count_bytes >= FIFO::SIZE) || (valid_samples > FIFO_MAX_SAMPLES)) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
if (fifo_count_samples == 0) {
} else if (fifo_count_samples == 0) {
perf_count(_fifo_empty_perf);
return false;
}
const uint16_t valid_samples = math::min(samples, fifo_count_samples);
if (valid_samples > 0) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = valid_samples;
sensor_imu_fifo.accel_scale = ACCEL_SCALE;
sensor_imu_fifo.gyro_scale = GYRO_SCALE;
if (ProcessAccel(timestamp_sample, buffer.f, valid_samples)) {
return true;
for (int i = 0; i < valid_samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
sensor_imu_fifo.accel_x[i] = combine(buffer.f[i].ACCEL_XOUT_H, buffer.f[i].ACCEL_XOUT_L);
sensor_imu_fifo.accel_y[i] = math::negate(combine(buffer.f[i].ACCEL_YOUT_H, buffer.f[i].ACCEL_YOUT_L));
sensor_imu_fifo.accel_z[i] = math::negate(combine(buffer.f[i].ACCEL_ZOUT_H, buffer.f[i].ACCEL_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
sensor_imu_fifo.gyro_x[i] = combine(buffer.f[i].GYRO_XOUT_H, buffer.f[i].GYRO_XOUT_L);
sensor_imu_fifo.gyro_y[i] = math::negate(combine(buffer.f[i].GYRO_YOUT_H, buffer.f[i].GYRO_YOUT_L));
sensor_imu_fifo.gyro_z[i] = math::negate(combine(buffer.f[i].GYRO_ZOUT_H, buffer.f[i].GYRO_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
sensor_imu_fifo.temperature = (hrt_elapsed_time(&_temperature_update_timestamp) < 5_s) ? _temperature : NAN;
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf)
+ perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
@@ -580,112 +480,34 @@ void ICM20649::FIFOReset()
// FIFO_RST: reset FIFO
RegisterSetBits(Register::BANK_0::FIFO_RST, FIFO_RST_BIT::FIFO_RESET);
RegisterClearBits(Register::BANK_0::FIFO_RST, FIFO_RST_BIT::FIFO_RESET);
// reset while FIFO is disabled
_drdy_count = 0;
_drdy_timestamp_sample.store(0);
}
static bool fifo_accel_equal(const FIFO::DATA &f0, const FIFO::DATA &f1)
float ICM20649::ReadTemperature()
{
return (memcmp(&f0.ACCEL_XOUT_H, &f1.ACCEL_XOUT_H, 6) == 0);
}
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::TEMP_OUT_H) | DIR_READ};
uint8_t TEMP_OUT_H{0};
uint8_t TEMP_OUT_L{0};
} buffer{};
bool ICM20649::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
bool bad_data = false;
// accel data is doubled in FIFO, but might be shifted
int accel_first_sample = 1;
if (samples >= 4) {
if (fifo_accel_equal(fifo[0], fifo[1]) && fifo_accel_equal(fifo[2], fifo[3])) {
// [A0, A1, A2, A3]
// A0==A1, A2==A3
accel_first_sample = 1;
} else if (fifo_accel_equal(fifo[1], fifo[2])) {
// [A0, A1, A2, A3]
// A0, A1==A2, A3
accel_first_sample = 0;
} else {
// no matching accel samples is an error
bad_data = true;
perf_count(_bad_transfer_perf);
}
}
for (int i = accel_first_sample; i < samples; i = i + SAMPLES_PER_TRANSFER) {
int16_t accel_x = combine(fifo[i].ACCEL_XOUT_H, fifo[i].ACCEL_XOUT_L);
int16_t accel_y = combine(fifo[i].ACCEL_YOUT_H, fifo[i].ACCEL_YOUT_L);
int16_t accel_z = combine(fifo[i].ACCEL_ZOUT_H, fifo[i].ACCEL_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
return !bad_data;
}
void ICM20649::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_XOUT_H, fifo[i].GYRO_XOUT_L);
const int16_t gyro_y = combine(fifo[i].GYRO_YOUT_H, fifo[i].GYRO_YOUT_L);
const int16_t gyro_z = combine(fifo[i].GYRO_ZOUT_H, fifo[i].GYRO_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void ICM20649::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::BANK_0::TEMP_OUT_H) | DIR_READ;
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
return NAN;
}
const int16_t TEMP_OUT = combine(temperature_buf[1], temperature_buf[2]);
const int16_t TEMP_OUT = combine(buffer.TEMP_OUT_H, buffer.TEMP_OUT_L);
const float TEMP_degC = (TEMP_OUT / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
if (PX4_ISFINITE(TEMP_degC)
&& (TEMP_degC >= TEMPERATURE_SENSOR_MIN)
&& (TEMP_degC <= TEMPERATURE_SENSOR_MAX)) {
return TEMP_degC;
}
return NAN;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM20649_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM20649;
class ICM20649 : public device::SPI, public I2CSPIDriver<ICM20649>
@@ -67,26 +68,15 @@ public:
void print_status() override;
private:
void exit_and_cleanup() override;
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 9000.f};
static constexpr int32_t SAMPLES_PER_TRANSFER{2}; // ensure at least 1 new accel sample per transfer
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; // 9000 Hz gyro
static constexpr float ACCEL_RATE{GYRO_RATE / SAMPLES_PER_TRANSFER}; // 4500 Hz accel
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (3 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr float ACCEL_SCALE{CONSTANTS_ONE_G / 1024.f}; // ACCEL_FS_SEL_30G:
static constexpr float GYRO_SCALE{math::radians(4000.f / 32768.f)}; // GYRO_FS_SEL_4000_DPS:
struct register_bank0_config_t {
Register::BANK_0 reg;
@@ -105,19 +95,12 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
void SelectRegisterBank(enum REG_BANK_SEL_BIT bank, bool force = false);
void SelectRegisterBank(Register::BANK_0 reg) { SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0); }
void SelectRegisterBank(Register::BANK_2 reg) { SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_2); }
static int DataReadyInterruptCallback(int irq, void *context, void *arg);
void DataReady();
bool DataReadyInterruptConfigure();
bool DataReadyInterruptDisable();
template <typename T> bool RegisterCheck(const T &reg_cfg);
template <typename T> uint8_t RegisterRead(T reg);
template <typename T> void RegisterWrite(T reg, uint8_t value);
@@ -129,21 +112,19 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
float ReadTemperature();
const spi_drdy_gpio_t _drdy_gpio;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
const enum Rotation _rotation;
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")};
perf_counter_t _fifo_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO overflow")};
perf_counter_t _fifo_reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO reset")};
perf_counter_t _drdy_missed_perf{nullptr};
hrt_abstime _reset_timestamp{0};
hrt_abstime _last_config_check_timestamp{0};
@@ -152,10 +133,6 @@ private:
enum REG_BANK_SEL_BIT _last_register_bank {REG_BANK_SEL_BIT::USER_BANK_0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
int32_t _drdy_count{0};
bool _data_ready_interrupt_enabled{false};
enum class STATE : uint8_t {
RESET,
WAIT_FOR_RESET,
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -61,6 +61,8 @@ static constexpr uint8_t WHOAMI = 0xE1;
static constexpr float TEMPERATURE_SENSITIVITY = 333.87f; // LSB/C
static constexpr float TEMPERATURE_OFFSET = 21.f; // C
static constexpr float TEMPERATURE_SENSOR_MIN = -40.f; // °C
static constexpr float TEMPERATURE_SENSOR_MAX = 85.f; // °C
namespace Register
{
@@ -41,7 +41,5 @@ px4_add_module(
icm20689_main.cpp
InvenSense_ICM20689_registers.hpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
+102 -178
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "ICM20689.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ ICM20689::ICM20689(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM20689::~ICM20689()
@@ -108,7 +112,7 @@ void ICM20689::print_status()
bool ICM20689::StoreCheckedRegisterValue(Register reg)
{
// 3 retries
for (int i = 0; i < 3; i++) {
for (int retry = 0; retry < 3; retry++) {
uint8_t read1 = RegisterRead(reg);
uint8_t read2 = RegisterRead(reg);
@@ -131,14 +135,19 @@ bool ICM20689::StoreCheckedRegisterValue(Register reg)
int ICM20689::probe()
{
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
if (whoami != WHOAMI) {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
return PX4_ERROR;
if (whoami == WHOAMI) {
return PX4_OK;
} else {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
}
}
return PX4_OK;
return PX4_ERROR;
}
void ICM20689::RunImpl()
@@ -187,8 +196,8 @@ void ICM20689::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -196,6 +205,8 @@ void ICM20689::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
_temperature = ReadTemperature();
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
@@ -234,7 +245,7 @@ void ICM20689::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
} else {
@@ -271,7 +282,7 @@ void ICM20689::RunImpl()
FIFOReset();
perf_count(_fifo_overflow_perf);
} else if (samples >= SAMPLES_PER_TRANSFER) {
} else if (samples >= 1) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -287,6 +298,7 @@ void ICM20689::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
@@ -301,13 +313,14 @@ void ICM20689::RunImpl()
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
PX4_DEBUG("Force reset because register 0x%02hhX check failed ", (uint8_t)_register_cfg[_checked_register].reg);
Reset();
}
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature = ReadTemperature();
_temperature_update_timestamp = now;
}
}
@@ -317,65 +330,10 @@ void ICM20689::RunImpl()
}
}
void ICM20689::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::ACCEL_CONFIG) & (Bit4 | Bit3); // [4:3] ACCEL_FS_SEL[1:0]
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_2G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 16384.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
}
}
void ICM20689::ConfigureGyro()
{
const uint8_t FS_SEL = RegisterRead(Register::GYRO_CONFIG) & (Bit4 | Bit3); // [4:3] FS_SEL[1:0]
float range_dps = 0.f;
switch (FS_SEL) {
case FS_SEL_250_DPS:
range_dps = 250.f;
break;
case FS_SEL_500_DPS:
range_dps = 500.f;
break;
case FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void ICM20689::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt * SAMPLES_PER_TRANSFER
const float min_interval = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
@@ -400,9 +358,6 @@ bool ICM20689::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -429,7 +384,7 @@ bool ICM20689::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool ICM20689::DataReadyInterruptDisable()
@@ -438,7 +393,7 @@ bool ICM20689::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
bool ICM20689::RegisterCheck(const register_config_t &reg_cfg)
@@ -487,31 +442,75 @@ void ICM20689::RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t cl
uint16_t ICM20689::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ;
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM20689::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 1, FIFO::SIZE);
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + samples (FIFO::DATA)
const size_t transfer_size = 1 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
const uint8_t valid_samples = samples;
ProcessGyro(timestamp_sample, buffer.f, samples);
return ProcessAccel(timestamp_sample, buffer.f, samples);
if (valid_samples > 0) {
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = valid_samples;
sensor_imu_fifo.accel_scale = ACCEL_SCALE;
sensor_imu_fifo.gyro_scale = GYRO_SCALE;
for (int i = 0; i < valid_samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
sensor_imu_fifo.accel_x[i] = combine(buffer.f[i].ACCEL_XOUT_H, buffer.f[i].ACCEL_XOUT_L);
sensor_imu_fifo.accel_y[i] = math::negate(combine(buffer.f[i].ACCEL_YOUT_H, buffer.f[i].ACCEL_YOUT_L));
sensor_imu_fifo.accel_z[i] = math::negate(combine(buffer.f[i].ACCEL_ZOUT_H, buffer.f[i].ACCEL_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
sensor_imu_fifo.gyro_x[i] = combine(buffer.f[i].GYRO_XOUT_H, buffer.f[i].GYRO_XOUT_L);
sensor_imu_fifo.gyro_y[i] = math::negate(combine(buffer.f[i].GYRO_YOUT_H, buffer.f[i].GYRO_YOUT_L));
sensor_imu_fifo.gyro_z[i] = math::negate(combine(buffer.f[i].GYRO_ZOUT_H, buffer.f[i].GYRO_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
sensor_imu_fifo.temperature = (hrt_elapsed_time(&_temperature_update_timestamp) < 5_s) ? _temperature : NAN;
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf)
+ perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
}
void ICM20689::FIFOReset()
@@ -537,105 +536,30 @@ void ICM20689::FIFOReset()
}
}
static bool fifo_accel_equal(const FIFO::DATA &f0, const FIFO::DATA &f1)
float ICM20689::ReadTemperature()
{
return (memcmp(&f0.ACCEL_XOUT_H, &f1.ACCEL_XOUT_H, 6) == 0);
}
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ};
uint8_t TEMP_OUT_H{0};
uint8_t TEMP_OUT_L{0};
} buffer{};
bool ICM20689::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
bool bad_data = false;
// accel data is doubled in FIFO, but might be shifted
int accel_first_sample = 1;
if (samples >= 4) {
if (fifo_accel_equal(fifo[0], fifo[1]) && fifo_accel_equal(fifo[2], fifo[3])) {
// [A0, A1, A2, A3]
// A0==A1, A2==A3
accel_first_sample = 1;
} else if (fifo_accel_equal(fifo[1], fifo[2])) {
// [A0, A1, A2, A3]
// A0, A1==A2, A3
accel_first_sample = 0;
} else {
// no matching accel samples is an error
bad_data = true;
perf_count(_bad_transfer_perf);
}
}
for (int i = accel_first_sample; i < samples; i = i + SAMPLES_PER_TRANSFER) {
int16_t accel_x = combine(fifo[i].ACCEL_XOUT_H, fifo[i].ACCEL_XOUT_L);
int16_t accel_y = combine(fifo[i].ACCEL_YOUT_H, fifo[i].ACCEL_YOUT_L);
int16_t accel_z = combine(fifo[i].ACCEL_ZOUT_H, fifo[i].ACCEL_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
return !bad_data;
}
void ICM20689::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_XOUT_H, fifo[i].GYRO_XOUT_L);
const int16_t gyro_y = combine(fifo[i].GYRO_YOUT_H, fifo[i].GYRO_YOUT_L);
const int16_t gyro_z = combine(fifo[i].GYRO_ZOUT_H, fifo[i].GYRO_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void ICM20689::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ;
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
return NAN;
}
const int16_t TEMP_OUT = combine(temperature_buf[1], temperature_buf[2]);
const int16_t TEMP_OUT = combine(buffer.TEMP_OUT_H, buffer.TEMP_OUT_L);
const float TEMP_degC = (TEMP_OUT / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
if (PX4_ISFINITE(TEMP_degC)
&& (TEMP_degC >= TEMPERATURE_SENSOR_MIN)
&& (TEMP_degC <= TEMPERATURE_SENSOR_MAX)) {
return TEMP_degC;
}
return NAN;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM20689_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM20689;
class ICM20689 : public device::SPI, public I2CSPIDriver<ICM20689>
@@ -71,20 +72,13 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f};
static constexpr int32_t SAMPLES_PER_TRANSFER{2}; // ensure at least 1 new accel sample per transfer
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; // 8000 Hz gyro
static constexpr float ACCEL_RATE{GYRO_RATE / SAMPLES_PER_TRANSFER}; // 4000 Hz accel
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0])), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (1 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr float ACCEL_SCALE{CONSTANTS_ONE_G / 2048.f}; // ACCEL_FS_SEL
static constexpr float GYRO_SCALE{math::radians(2000.f / 32768.f)}; // FS_SEL
struct register_config_t {
Register reg;
@@ -97,8 +91,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
static int DataReadyInterruptCallback(int irq, void *context, void *arg);
@@ -117,14 +109,15 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
float ReadTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,6 +43,8 @@
#include <cstdint>
#include <cstddef>
namespace InvenSense_ICM20689
{
// TODO: move to a central header
static constexpr uint8_t Bit0 = (1 << 0);
static constexpr uint8_t Bit1 = (1 << 1);
@@ -53,8 +55,6 @@ static constexpr uint8_t Bit5 = (1 << 5);
static constexpr uint8_t Bit6 = (1 << 6);
static constexpr uint8_t Bit7 = (1 << 7);
namespace InvenSense_ICM20689
{
static constexpr uint32_t SPI_SPEED = 8 * 1000 * 1000; // 8MHz SPI serial interface
static constexpr uint8_t DIR_READ = 0x80;
@@ -62,8 +62,11 @@ static constexpr uint8_t WHOAMI = 0x98;
static constexpr float TEMPERATURE_SENSITIVITY = 326.8f; // LSB/C
static constexpr float TEMPERATURE_OFFSET = 25.f; // C
static constexpr float TEMPERATURE_SENSOR_MIN = -40.f; // °C
static constexpr float TEMPERATURE_SENSOR_MAX = 85.f; // °C
enum class Register : uint8_t {
CONFIG = 0x1A,
GYRO_CONFIG = 0x1B,
ACCEL_CONFIG = 0x1C,
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2020 PX4 Development Team. All rights reserved.
# Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -34,6 +34,7 @@ px4_add_module(
MODULE drivers__imu__invensense__icm20948
MAIN icm20948
COMPILE_FLAGS
-Wno-error
SRCS
AKM_AK09916_registers.hpp
ICM20948.cpp
@@ -44,8 +45,6 @@ px4_add_module(
InvenSense_ICM20948_registers.hpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
drivers_magnetometer
)
+69 -170
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "ICM20948.hpp"
#include <lib/parameters/param.h>
#include "AKM_AK09916_registers.hpp"
using namespace time_literals;
@@ -46,14 +48,16 @@ ICM20948::ICM20948(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
bool enable_magnetometer = config.custom1 == 1;
@@ -136,8 +140,9 @@ void ICM20948::print_status()
int ICM20948::probe()
{
for (int i = 0; i < 3; i++) {
uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
const uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
if (whoami == WHOAMI) {
return PX4_OK;
@@ -249,7 +254,7 @@ void ICM20948::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
} else {
@@ -311,6 +316,7 @@ void ICM20948::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
@@ -346,65 +352,10 @@ void ICM20948::RunImpl()
}
}
void ICM20948::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::BANK_2::ACCEL_CONFIG) & (Bit2 | Bit1); // 2:1 ACCEL_FS_SEL[1:0]
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_2G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 16384.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
}
}
void ICM20948::ConfigureGyro()
{
const uint8_t GYRO_FS_SEL = RegisterRead(Register::BANK_2::GYRO_CONFIG_1) & (Bit2 | Bit1); // 2:1 GYRO_FS_SEL[1:0]
float range_dps = 0.f;
switch (GYRO_FS_SEL) {
case GYRO_FS_SEL_250_DPS:
range_dps = 250.f;
break;
case GYRO_FS_SEL_500_DPS:
range_dps = 500.f;
break;
case GYRO_FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case GYRO_FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void ICM20948::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt * SAMPLES_PER_TRANSFER
const float min_interval = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
@@ -462,9 +413,6 @@ bool ICM20948::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -567,54 +515,89 @@ uint16_t ICM20948::FIFOReadCount()
{
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ;
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM20948::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 3, FIFO::SIZE);
// FIFO transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 3 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
const uint8_t valid_samples = math::min(samples, fifo_count_samples);
if (fifo_count_bytes >= FIFO::SIZE) {
if ((fifo_count_bytes >= FIFO::SIZE) || (valid_samples > FIFO_MAX_SAMPLES)) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
if (fifo_count_samples == 0) {
} else if (fifo_count_samples == 0) {
perf_count(_fifo_empty_perf);
return false;
}
const uint16_t valid_samples = math::min(samples, fifo_count_samples);
if (valid_samples > 0) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = valid_samples;
sensor_imu_fifo.accel_scale = ACCEL_SCALE;
sensor_imu_fifo.gyro_scale = GYRO_SCALE;
if (ProcessAccel(timestamp_sample, buffer.f, valid_samples)) {
return true;
for (int i = 0; i < valid_samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
sensor_imu_fifo.accel_x[i] = combine(buffer.f[i].ACCEL_XOUT_H, buffer.f[i].ACCEL_XOUT_L);
sensor_imu_fifo.accel_y[i] = math::negate(combine(buffer.f[i].ACCEL_YOUT_H, buffer.f[i].ACCEL_YOUT_L));
sensor_imu_fifo.accel_z[i] = math::negate(combine(buffer.f[i].ACCEL_ZOUT_H, buffer.f[i].ACCEL_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
sensor_imu_fifo.gyro_x[i] = combine(buffer.f[i].GYRO_XOUT_H, buffer.f[i].GYRO_XOUT_L);
sensor_imu_fifo.gyro_y[i] = math::negate(combine(buffer.f[i].GYRO_YOUT_H, buffer.f[i].GYRO_YOUT_L));
sensor_imu_fifo.gyro_z[i] = math::negate(combine(buffer.f[i].GYRO_ZOUT_H, buffer.f[i].GYRO_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
sensor_imu_fifo.temperature = (hrt_elapsed_time(&_temperature_update_timestamp) < 5_s) ? _temperature : NAN;
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf)
+ perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
@@ -633,89 +616,6 @@ void ICM20948::FIFOReset()
_drdy_timestamp_sample.store(0);
}
static bool fifo_accel_equal(const FIFO::DATA &f0, const FIFO::DATA &f1)
{
return (memcmp(&f0.ACCEL_XOUT_H, &f1.ACCEL_XOUT_H, 6) == 0);
}
bool ICM20948::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
bool bad_data = false;
// accel data is doubled in FIFO, but might be shifted
int accel_first_sample = 1;
if (samples >= 4) {
if (fifo_accel_equal(fifo[0], fifo[1]) && fifo_accel_equal(fifo[2], fifo[3])) {
// [A0, A1, A2, A3]
// A0==A1, A2==A3
accel_first_sample = 1;
} else if (fifo_accel_equal(fifo[1], fifo[2])) {
// [A0, A1, A2, A3]
// A0, A1==A2, A3
accel_first_sample = 0;
} else {
// no matching accel samples is an error
bad_data = true;
perf_count(_bad_transfer_perf);
}
}
for (int i = accel_first_sample; i < samples; i = i + SAMPLES_PER_TRANSFER) {
int16_t accel_x = combine(fifo[i].ACCEL_XOUT_H, fifo[i].ACCEL_XOUT_L);
int16_t accel_y = combine(fifo[i].ACCEL_YOUT_H, fifo[i].ACCEL_YOUT_L);
int16_t accel_z = combine(fifo[i].ACCEL_ZOUT_H, fifo[i].ACCEL_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
return !bad_data;
}
void ICM20948::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_XOUT_H, fifo[i].GYRO_XOUT_L);
const int16_t gyro_y = combine(fifo[i].GYRO_YOUT_H, fifo[i].GYRO_YOUT_L);
const int16_t gyro_z = combine(fifo[i].GYRO_ZOUT_H, fifo[i].GYRO_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void ICM20948::UpdateTemperature()
{
// read current temperature
@@ -732,8 +632,7 @@ void ICM20948::UpdateTemperature()
const float TEMP_degC = (TEMP_OUT / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
_temperature = TEMP_degC;
}
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM20948_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
#include "ICM20948_AK09916.hpp"
using namespace InvenSense_ICM20948;
@@ -73,22 +74,13 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 9000.f};
static constexpr int32_t SAMPLES_PER_TRANSFER{2}; // ensure at least 1 new accel sample per transfer
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; // 9000 Hz gyro
static constexpr float ACCEL_RATE{GYRO_RATE / SAMPLES_PER_TRANSFER}; // 4500 Hz accel
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (3 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr float ACCEL_SCALE{CONSTANTS_ONE_G / 2048.f}; // ACCEL_FS_SEL_16G:
static constexpr float GYRO_SCALE{math::radians(2000.f / 32768.f)}; // GYRO_FS_SEL_2000_DPS:
struct register_bank0_config_t {
Register::BANK_0 reg;
@@ -113,8 +105,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
void SelectRegisterBank(enum REG_BANK_SEL_BIT bank, bool force = false);
@@ -138,12 +128,16 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
const spi_drdy_gpio_t _drdy_gpio;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
float _temperature{NAN};
// I2C AUX interface (slave 1 - 4)
AKM_AK09916::ICM20948_AK09916 *_slave_ak09916_magnetometer{nullptr};
friend class AKM_AK09916::ICM20948_AK09916;
@@ -152,9 +146,6 @@ private:
void I2CSlaveExternalSensorDataEnable(uint8_t slave_i2c_addr, uint8_t reg, uint8_t size);
bool I2CSlaveExternalSensorDataRead(uint8_t *buffer, uint8_t length);
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")};
@@ -41,6 +41,4 @@ px4_add_module(
InvenSense_ICM40609D_registers.hpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
)
+228 -216
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "ICM40609D.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ ICM40609D::ICM40609D(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM40609D::~ICM40609D()
@@ -61,6 +65,7 @@ ICM40609D::~ICM40609D()
perf_free(_fifo_empty_perf);
perf_free(_fifo_overflow_perf);
perf_free(_fifo_reset_perf);
perf_free(_fifo_timestamp_error_perf);
perf_free(_drdy_missed_perf);
}
@@ -102,12 +107,14 @@ void ICM40609D::print_status()
perf_print_counter(_fifo_empty_perf);
perf_print_counter(_fifo_overflow_perf);
perf_print_counter(_fifo_reset_perf);
perf_print_counter(_fifo_timestamp_error_perf);
perf_print_counter(_drdy_missed_perf);
}
int ICM40609D::probe()
{
for (int i = 0; i < 3; i++) {
// 3 retries
for (int retry = 0; retry < 3; retry++) {
uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
if (whoami == WHOAMI) {
@@ -161,8 +168,8 @@ void ICM40609D::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -170,21 +177,9 @@ void ICM40609D::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
FIFOReset();
// if configure succeeded then reset the FIFO
_state = STATE::FIFO_RESET;
ScheduleDelayed(1_ms);
} else {
// CONFIGURE not complete
@@ -201,6 +196,24 @@ void ICM40609D::RunImpl()
break;
case STATE::FIFO_RESET:
_state = STATE::FIFO_READ;
FIFOReset();
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
break;
case STATE::FIFO_READ: {
hrt_abstime timestamp_sample = now;
uint8_t samples = 0;
@@ -209,7 +222,7 @@ void ICM40609D::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_gyro_samples;
@@ -221,6 +234,8 @@ void ICM40609D::RunImpl()
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
bool success = false;
if (samples == 0) {
// check current FIFO count
const uint16_t fifo_count = FIFOReadCount();
@@ -233,27 +248,42 @@ void ICM40609D::RunImpl()
perf_count(_fifo_empty_perf);
} else {
// FIFO count (size in bytes)
samples = (fifo_count / sizeof(FIFO::DATA));
// FIFO count (size in bytes) should be a multiple of the FIFO::DATA structure
samples = fifo_count / sizeof(FIFO::DATA);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_gyro_samples + 1) {
timestamp_sample -= static_cast<int>(FIFO_SAMPLE_DT);
samples--;
if (samples > _fifo_gyro_samples) {
// grab desired number of samples, but reschedule next cycle sooner
const int extra_samples = samples - _fifo_gyro_samples;
samples = _fifo_gyro_samples;
if (_fifo_gyro_samples > extra_samples) {
// reschedule to run when a total of _fifo_gyro_samples should be available in the FIFO
const uint32_t reschedule_delay_us = (_fifo_gyro_samples - extra_samples) * static_cast<int>(FIFO_SAMPLE_DT);
ScheduleOnInterval(_fifo_empty_interval_us, reschedule_delay_us);
} else {
// otherwise reschedule to run immediately
ScheduleOnInterval(_fifo_empty_interval_us);
}
} else if (samples < _fifo_gyro_samples) {
// reschedule next cycle to catch the desired number of samples
ScheduleOnInterval(_fifo_empty_interval_us, (_fifo_gyro_samples - samples) * static_cast<int>(FIFO_SAMPLE_DT));
}
if (samples > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
FIFOReset();
perf_count(_fifo_overflow_perf);
samples = 0;
if (samples == _fifo_gyro_samples) {
if (FIFORead(now, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
}
}
bool success = false;
if (samples >= 1) {
if (samples == _fifo_gyro_samples) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -268,13 +298,14 @@ void ICM40609D::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_bank0_cfg[_checked_register_bank0])
) {
_last_config_check_timestamp = now;
@@ -285,13 +316,6 @@ void ICM40609D::RunImpl()
perf_count(_bad_register_perf);
Reset();
}
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature_update_timestamp = now;
}
}
}
@@ -299,75 +323,16 @@ void ICM40609D::RunImpl()
}
}
void ICM40609D::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::BANK_0::ACCEL_CONFIG0) & (Bit7 | Bit6 | Bit5); // 7:5 ACCEL_FS_SEL
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_32G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 1024.f);
_px4_accel.set_range(32.f * CONSTANTS_ONE_G);
break;
}
}
void ICM40609D::ConfigureGyro()
{
const uint8_t GYRO_FS_SEL = RegisterRead(Register::BANK_0::GYRO_CONFIG0) & (Bit7 | Bit6 | Bit5); // 7:5 GYRO_FS_SEL
float range_dps = 0.f;
switch (GYRO_FS_SEL) {
case GYRO_FS_SEL_125_DPS:
range_dps = 125.f;
break;
case GYRO_FS_SEL_250_DPS:
range_dps = 250.f;
break;
case GYRO_FS_SEL_500_DPS:
range_dps = 500.f;
break;
case GYRO_FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case GYRO_FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void ICM40609D::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / RATE), (float)FIFO_MAX_SAMPLES));
// recompute FIFO empty interval (us) with actual gyro sample limit
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / GYRO_RATE);
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / RATE);
ConfigureFIFOWatermark(_fifo_gyro_samples);
}
@@ -418,9 +383,6 @@ bool ICM40609D::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -443,7 +405,7 @@ bool ICM40609D::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool ICM40609D::DataReadyInterruptDisable()
@@ -452,7 +414,7 @@ bool ICM40609D::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
template <typename T>
@@ -507,23 +469,36 @@ void ICM40609D::RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbit
uint16_t ICM40609D::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ;
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM40609D::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 4, FIFO::SIZE);
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + INT_STATUS + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 4 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
@@ -537,7 +512,7 @@ bool ICM40609D::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
if (fifo_count_bytes >= FIFO::SIZE) {
perf_count(_fifo_overflow_perf);
@@ -552,40 +527,151 @@ bool ICM40609D::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = 0;
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 8192.f; // highres accel data 8192 LSB/g
sensor_imu_fifo.gyro_scale = math::radians(1.f / 131.f); // highres gyro data 131 LSB/dps
// check FIFO header in every sample
uint8_t valid_samples = 0;
float temperature_sum = 0;
float timestamp_interval_sum = 0;
int timestamp_interval_sum_count = 0;
bool accel_scale_16bit = false; // 18-bits of accelerometer data
bool gyro_scale_16bit = false; // 20-bits of gyroscope data
for (int i = 0; i < math::min(samples, fifo_count_samples); i++) {
bool valid = true;
const FIFO::DATA &fifo = buffer.f[i];
// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8b_0110_10xx
const uint8_t FIFO_HEADER = buffer.f[i].FIFO_Header;
const uint8_t FIFO_HEADER = fifo.FIFO_Header;
if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG) {
// FIFO sample empty if HEADER_MSG set
valid = false;
const bool HEADER_MSG = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG; // FIFO is empty
const bool HEADER_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL;
const bool HEADER_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO;
// 3:2 HEADER_TIMESTAMP_FSYNC
const bool HEADER_ODR_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL; // ODR for accel is different
const bool HEADER_ODR_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO; // ODR for gyro is different
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL)) {
// accel bit not set
valid = false;
if (!HEADER_MSG && HEADER_ACCEL && HEADER_GYRO && HEADER_20 && !HEADER_ODR_ACCEL && !HEADER_ODR_GYRO) {
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO)) {
// gyro bit not set
valid = false;
}
// 20 bit data scaled to 16 bit
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 2048.f;
sensor_imu_fifo.accel_x[valid_samples] = combine(fifo.ACCEL_DATA_X1, fifo.ACCEL_DATA_X0);
sensor_imu_fifo.accel_y[valid_samples] = combine(fifo.ACCEL_DATA_Y1, fifo.ACCEL_DATA_Y0);
sensor_imu_fifo.accel_z[valid_samples] = combine(fifo.ACCEL_DATA_Z1, fifo.ACCEL_DATA_Z0);
sensor_imu_fifo.gyro_x[i] = combine(fifo.GYRO_DATA_X1, fifo.GYRO_DATA_X0);
sensor_imu_fifo.gyro_y[i] = combine(fifo.GYRO_DATA_Y1, fifo.GYRO_DATA_Y0);
sensor_imu_fifo.gyro_z[i] = combine(fifo.GYRO_DATA_Z1, fifo.GYRO_DATA_Z0);
// temperature
const int16_t TEMP_DATA = combine(fifo.TEMP_DATA1, fifo.TEMP_DATA0);
// sample invalid if -32768
if (TEMP_DATA != -32768) {
temperature_sum += TEMP_DATA;
} else {
break;
}
// HEADER_TIMESTAMP_FSYNC - 0b10: Packet contains ODR Timestamp
if (FIFO_HEADER & Bit3) {
const uint16_t timestamp = (fifo.TimeStamp_h << 8) + fifo.TimeStamp_l;
if (_timestamp_prev != 0) {
// If TMST_RES = 0 (corresponding to timestamp resolution of 1µs), timestamp interval reported in FIFO requires scaling by a factor of 32/30.
// Document Number: DS-000347 Revision: 1.5 Page 59 of 110
static constexpr float FIFO_DT_SCALE = 32.f / 30.f;
float dt = 0;
if (timestamp > _timestamp_prev) {
dt = static_cast<float>(timestamp - _timestamp_prev) * FIFO_DT_SCALE;
} else if (timestamp < _timestamp_prev) {
// uint16_t rollover
uint32_t timestamp_new = UINT16_MAX + timestamp;
dt = static_cast<float>(timestamp_new - _timestamp_prev) * FIFO_DT_SCALE;
}
timestamp_interval_sum += dt;
timestamp_interval_sum_count++;
// check dt is within +=2% of expected value
if ((dt < (FIFO_SAMPLE_DT * 0.98f)) || (dt > (FIFO_SAMPLE_DT * 1.02f))) {
perf_count(_fifo_timestamp_error_perf);
}
}
_timestamp_prev = timestamp;
}
if (valid) {
valid_samples++;
} else {
perf_count(_bad_transfer_perf);
break;
}
}
if (valid_samples > 0) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
ProcessAccel(timestamp_sample, buffer.f, valid_samples);
sensor_imu_fifo.samples = valid_samples;
for (int i = 0; i < sensor_imu_fifo.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
// sensor_imu_fifo.accel_x[i]
sensor_imu_fifo.accel_y[i] = math::negate(sensor_imu_fifo.accel_y[i]);
sensor_imu_fifo.accel_z[i] = math::negate(sensor_imu_fifo.accel_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
// sensor_imu_fifo.gyro_x[i]
sensor_imu_fifo.gyro_y[i] = math::negate(sensor_imu_fifo.gyro_y[i]);
sensor_imu_fifo.gyro_z[i] = math::negate(sensor_imu_fifo.gyro_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
const float temperature_avg = temperature_sum / valid_samples;
// use average temperature reading
const float TEMP_degC = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
sensor_imu_fifo.temperature = TEMP_degC;
} else {
perf_count(_bad_transfer_perf);
return false;
}
if (timestamp_interval_sum > 0) {
const float dt_avg = (timestamp_interval_sum / timestamp_interval_sum_count);
// check dt is within +=1% of expected value
if ((dt_avg < (FIFO_SAMPLE_DT * 0.99f)) || (dt_avg > (FIFO_SAMPLE_DT * 1.01f))) {
PX4_ERR("DT error %.6f", (double)dt_avg);
perf_count(_fifo_timestamp_error_perf);
} else {
sensor_imu_fifo.dt = dt_avg;
}
}
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
@@ -601,80 +687,6 @@ void ICM40609D::FIFOReset()
// reset while FIFO is disabled
_drdy_timestamp_sample.store(0);
}
void ICM40609D::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
int16_t accel_x = combine(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0);
int16_t accel_y = combine(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0);
int16_t accel_z = combine(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
}
void ICM40609D::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0);
const int16_t gyro_y = combine(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0);
const int16_t gyro_z = combine(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void ICM40609D::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::BANK_0::TEMP_DATA1) | DIR_READ;
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
}
const int16_t TEMP_DATA = combine(temperature_buf[1], temperature_buf[2]);
// Temperature in Degrees Centigrade
const float TEMP_degC = (TEMP_DATA / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
}
_timestamp_prev = 0;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM40609D_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM40609D;
class ICM40609D : public device::SPI, public I2CSPIDriver<ICM40609D>
@@ -71,22 +72,10 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f}; // 8000 Hz accel & gyro ODR configured
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float ACCEL_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float RATE{1e6f / FIFO_SAMPLE_DT};
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0])), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (4 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
struct register_bank0_config_t {
Register::BANK_0 reg;
@@ -99,8 +88,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
void ConfigureFIFOWatermark(uint8_t samples);
@@ -123,20 +110,18 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
void ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")};
perf_counter_t _fifo_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO overflow")};
perf_counter_t _fifo_reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO reset")};
perf_counter_t _fifo_timestamp_error_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO timestamp error")};
perf_counter_t _drdy_missed_perf{nullptr};
hrt_abstime _reset_timestamp{0};
@@ -144,6 +129,8 @@ private:
hrt_abstime _temperature_update_timestamp{0};
int _failure_count{0};
uint16_t _timestamp_prev{0};
enum REG_BANK_SEL_BIT _last_register_bank {REG_BANK_SEL_BIT::USER_BANK_0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
@@ -153,11 +140,12 @@ private:
RESET,
WAIT_FOR_RESET,
CONFIGURE,
FIFO_RESET,
FIFO_READ,
} _state{STATE::RESET};
uint16_t _fifo_empty_interval_us{1250}; // default 1250 us / 800 Hz transfer interval
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / GYRO_RATE))};
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / RATE))};
uint8_t _checked_register_bank0{0};
static constexpr uint8_t size_register_bank0_cfg{10};
@@ -41,6 +41,4 @@ px4_add_module(
InvenSense_ICM42605_registers.hpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
)
+228 -216
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "ICM42605.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ ICM42605::ICM42605(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM42605::~ICM42605()
@@ -61,6 +65,7 @@ ICM42605::~ICM42605()
perf_free(_fifo_empty_perf);
perf_free(_fifo_overflow_perf);
perf_free(_fifo_reset_perf);
perf_free(_fifo_timestamp_error_perf);
perf_free(_drdy_missed_perf);
}
@@ -102,12 +107,14 @@ void ICM42605::print_status()
perf_print_counter(_fifo_empty_perf);
perf_print_counter(_fifo_overflow_perf);
perf_print_counter(_fifo_reset_perf);
perf_print_counter(_fifo_timestamp_error_perf);
perf_print_counter(_drdy_missed_perf);
}
int ICM42605::probe()
{
for (int i = 0; i < 3; i++) {
// 3 retries
for (int retry = 0; retry < 3; retry++) {
uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
if (whoami == WHOAMI) {
@@ -162,8 +169,8 @@ void ICM42605::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -171,21 +178,9 @@ void ICM42605::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
FIFOReset();
// if configure succeeded then reset the FIFO
_state = STATE::FIFO_RESET;
ScheduleDelayed(1_ms);
} else {
// CONFIGURE not complete
@@ -202,6 +197,24 @@ void ICM42605::RunImpl()
break;
case STATE::FIFO_RESET:
_state = STATE::FIFO_READ;
FIFOReset();
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
break;
case STATE::FIFO_READ: {
hrt_abstime timestamp_sample = now;
uint8_t samples = 0;
@@ -210,7 +223,7 @@ void ICM42605::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_gyro_samples;
@@ -222,6 +235,8 @@ void ICM42605::RunImpl()
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
bool success = false;
if (samples == 0) {
// check current FIFO count
const uint16_t fifo_count = FIFOReadCount();
@@ -234,27 +249,42 @@ void ICM42605::RunImpl()
perf_count(_fifo_empty_perf);
} else {
// FIFO count (size in bytes)
samples = (fifo_count / sizeof(FIFO::DATA));
// FIFO count (size in bytes) should be a multiple of the FIFO::DATA structure
samples = fifo_count / sizeof(FIFO::DATA);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_gyro_samples + 1) {
timestamp_sample -= static_cast<int>(FIFO_SAMPLE_DT);
samples--;
if (samples > _fifo_gyro_samples) {
// grab desired number of samples, but reschedule next cycle sooner
const int extra_samples = samples - _fifo_gyro_samples;
samples = _fifo_gyro_samples;
if (_fifo_gyro_samples > extra_samples) {
// reschedule to run when a total of _fifo_gyro_samples should be available in the FIFO
const uint32_t reschedule_delay_us = (_fifo_gyro_samples - extra_samples) * static_cast<int>(FIFO_SAMPLE_DT);
ScheduleOnInterval(_fifo_empty_interval_us, reschedule_delay_us);
} else {
// otherwise reschedule to run immediately
ScheduleOnInterval(_fifo_empty_interval_us);
}
} else if (samples < _fifo_gyro_samples) {
// reschedule next cycle to catch the desired number of samples
ScheduleOnInterval(_fifo_empty_interval_us, (_fifo_gyro_samples - samples) * static_cast<int>(FIFO_SAMPLE_DT));
}
if (samples > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
FIFOReset();
perf_count(_fifo_overflow_perf);
samples = 0;
if (samples == _fifo_gyro_samples) {
if (FIFORead(now, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
}
}
bool success = false;
if (samples >= 1) {
if (samples == _fifo_gyro_samples) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -269,13 +299,14 @@ void ICM42605::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_bank0_cfg[_checked_register_bank0])
) {
_last_config_check_timestamp = now;
@@ -286,13 +317,6 @@ void ICM42605::RunImpl()
perf_count(_bad_register_perf);
Reset();
}
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature_update_timestamp = now;
}
}
}
@@ -300,75 +324,16 @@ void ICM42605::RunImpl()
}
}
void ICM42605::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::BANK_0::ACCEL_CONFIG0) & (Bit7 | Bit6 | Bit5); // 7:5 ACCEL_FS_SEL
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_2G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 16384.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
}
}
void ICM42605::ConfigureGyro()
{
const uint8_t GYRO_FS_SEL = RegisterRead(Register::BANK_0::GYRO_CONFIG0) & (Bit7 | Bit6 | Bit5); // 7:5 GYRO_FS_SEL
float range_dps = 0.f;
switch (GYRO_FS_SEL) {
case GYRO_FS_SEL_125_DPS:
range_dps = 125.f;
break;
case GYRO_FS_SEL_250_DPS:
range_dps = 250.f;
break;
case GYRO_FS_SEL_500_DPS:
range_dps = 500.f;
break;
case GYRO_FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case GYRO_FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void ICM42605::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / RATE), (float)FIFO_MAX_SAMPLES));
// recompute FIFO empty interval (us) with actual gyro sample limit
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / GYRO_RATE);
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / RATE);
ConfigureFIFOWatermark(_fifo_gyro_samples);
}
@@ -419,9 +384,6 @@ bool ICM42605::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -444,7 +406,7 @@ bool ICM42605::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool ICM42605::DataReadyInterruptDisable()
@@ -453,7 +415,7 @@ bool ICM42605::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
template <typename T>
@@ -508,23 +470,36 @@ void ICM42605::RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbits
uint16_t ICM42605::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ;
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM42605::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 4, FIFO::SIZE);
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + INT_STATUS + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 4 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
@@ -538,7 +513,7 @@ bool ICM42605::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
if (fifo_count_bytes >= FIFO::SIZE) {
perf_count(_fifo_overflow_perf);
@@ -553,40 +528,151 @@ bool ICM42605::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = 0;
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 8192.f; // highres accel data 8192 LSB/g
sensor_imu_fifo.gyro_scale = math::radians(1.f / 131.f); // highres gyro data 131 LSB/dps
// check FIFO header in every sample
uint8_t valid_samples = 0;
float temperature_sum = 0;
float timestamp_interval_sum = 0;
int timestamp_interval_sum_count = 0;
bool accel_scale_16bit = false; // 18-bits of accelerometer data
bool gyro_scale_16bit = false; // 20-bits of gyroscope data
for (int i = 0; i < math::min(samples, fifo_count_samples); i++) {
bool valid = true;
const FIFO::DATA &fifo = buffer.f[i];
// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8b_0110_10xx
const uint8_t FIFO_HEADER = buffer.f[i].FIFO_Header;
const uint8_t FIFO_HEADER = fifo.FIFO_Header;
if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG) {
// FIFO sample empty if HEADER_MSG set
valid = false;
const bool HEADER_MSG = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG; // FIFO is empty
const bool HEADER_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL;
const bool HEADER_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO;
// 3:2 HEADER_TIMESTAMP_FSYNC
const bool HEADER_ODR_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL; // ODR for accel is different
const bool HEADER_ODR_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO; // ODR for gyro is different
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL)) {
// accel bit not set
valid = false;
if (!HEADER_MSG && HEADER_ACCEL && HEADER_GYRO && HEADER_20 && !HEADER_ODR_ACCEL && !HEADER_ODR_GYRO) {
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO)) {
// gyro bit not set
valid = false;
}
// 20 bit data scaled to 16 bit
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 2048.f;
sensor_imu_fifo.accel_x[valid_samples] = combine(fifo.ACCEL_DATA_X1, fifo.ACCEL_DATA_X0);
sensor_imu_fifo.accel_y[valid_samples] = combine(fifo.ACCEL_DATA_Y1, fifo.ACCEL_DATA_Y0);
sensor_imu_fifo.accel_z[valid_samples] = combine(fifo.ACCEL_DATA_Z1, fifo.ACCEL_DATA_Z0);
sensor_imu_fifo.gyro_x[i] = combine(fifo.GYRO_DATA_X1, fifo.GYRO_DATA_X0);
sensor_imu_fifo.gyro_y[i] = combine(fifo.GYRO_DATA_Y1, fifo.GYRO_DATA_Y0);
sensor_imu_fifo.gyro_z[i] = combine(fifo.GYRO_DATA_Z1, fifo.GYRO_DATA_Z0);
// temperature
const int16_t TEMP_DATA = combine(fifo.TEMP_DATA1, fifo.TEMP_DATA0);
// sample invalid if -32768
if (TEMP_DATA != -32768) {
temperature_sum += TEMP_DATA;
} else {
break;
}
// HEADER_TIMESTAMP_FSYNC - 0b10: Packet contains ODR Timestamp
if (FIFO_HEADER & Bit3) {
const uint16_t timestamp = (fifo.TimeStamp_h << 8) + fifo.TimeStamp_l;
if (_timestamp_prev != 0) {
// If TMST_RES = 0 (corresponding to timestamp resolution of 1µs), timestamp interval reported in FIFO requires scaling by a factor of 32/30.
// Document Number: DS-000347 Revision: 1.5 Page 59 of 110
static constexpr float FIFO_DT_SCALE = 32.f / 30.f;
float dt = 0;
if (timestamp > _timestamp_prev) {
dt = static_cast<float>(timestamp - _timestamp_prev) * FIFO_DT_SCALE;
} else if (timestamp < _timestamp_prev) {
// uint16_t rollover
uint32_t timestamp_new = UINT16_MAX + timestamp;
dt = static_cast<float>(timestamp_new - _timestamp_prev) * FIFO_DT_SCALE;
}
timestamp_interval_sum += dt;
timestamp_interval_sum_count++;
// check dt is within +=2% of expected value
if ((dt < (FIFO_SAMPLE_DT * 0.98f)) || (dt > (FIFO_SAMPLE_DT * 1.02f))) {
perf_count(_fifo_timestamp_error_perf);
}
}
_timestamp_prev = timestamp;
}
if (valid) {
valid_samples++;
} else {
perf_count(_bad_transfer_perf);
break;
}
}
if (valid_samples > 0) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
ProcessAccel(timestamp_sample, buffer.f, valid_samples);
sensor_imu_fifo.samples = valid_samples;
for (int i = 0; i < sensor_imu_fifo.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
// sensor_imu_fifo.accel_x[i]
sensor_imu_fifo.accel_y[i] = math::negate(sensor_imu_fifo.accel_y[i]);
sensor_imu_fifo.accel_z[i] = math::negate(sensor_imu_fifo.accel_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
// sensor_imu_fifo.gyro_x[i]
sensor_imu_fifo.gyro_y[i] = math::negate(sensor_imu_fifo.gyro_y[i]);
sensor_imu_fifo.gyro_z[i] = math::negate(sensor_imu_fifo.gyro_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
const float temperature_avg = temperature_sum / valid_samples;
// use average temperature reading
const float TEMP_degC = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
sensor_imu_fifo.temperature = TEMP_degC;
} else {
perf_count(_bad_transfer_perf);
return false;
}
if (timestamp_interval_sum > 0) {
const float dt_avg = (timestamp_interval_sum / timestamp_interval_sum_count);
// check dt is within +=1% of expected value
if ((dt_avg < (FIFO_SAMPLE_DT * 0.99f)) || (dt_avg > (FIFO_SAMPLE_DT * 1.01f))) {
PX4_ERR("DT error %.6f", (double)dt_avg);
perf_count(_fifo_timestamp_error_perf);
} else {
sensor_imu_fifo.dt = dt_avg;
}
}
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
@@ -602,80 +688,6 @@ void ICM42605::FIFOReset()
// reset while FIFO is disabled
_drdy_timestamp_sample.store(0);
}
void ICM42605::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
int16_t accel_x = combine(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0);
int16_t accel_y = combine(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0);
int16_t accel_z = combine(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
}
void ICM42605::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0);
const int16_t gyro_y = combine(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0);
const int16_t gyro_z = combine(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void ICM42605::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::BANK_0::TEMP_DATA1) | DIR_READ;
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
}
const int16_t TEMP_DATA = combine(temperature_buf[1], temperature_buf[2]);
// Temperature in Degrees Centigrade
const float TEMP_degC = (TEMP_DATA / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
}
_timestamp_prev = 0;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM42605_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM42605;
class ICM42605 : public device::SPI, public I2CSPIDriver<ICM42605>
@@ -71,22 +72,10 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f}; // 8000 Hz accel & gyro ODR configured
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float ACCEL_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float RATE{1e6f / FIFO_SAMPLE_DT};
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (4 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
struct register_bank0_config_t {
Register::BANK_0 reg;
@@ -99,8 +88,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
void ConfigureFIFOWatermark(uint8_t samples);
@@ -123,20 +110,18 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
void ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")};
perf_counter_t _fifo_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO overflow")};
perf_counter_t _fifo_reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO reset")};
perf_counter_t _fifo_timestamp_error_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO timestamp error")};
perf_counter_t _drdy_missed_perf{nullptr};
hrt_abstime _reset_timestamp{0};
@@ -144,6 +129,8 @@ private:
hrt_abstime _temperature_update_timestamp{0};
int _failure_count{0};
uint16_t _timestamp_prev{0};
enum REG_BANK_SEL_BIT _last_register_bank {REG_BANK_SEL_BIT::USER_BANK_0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
@@ -153,11 +140,12 @@ private:
RESET,
WAIT_FOR_RESET,
CONFIGURE,
FIFO_RESET,
FIFO_READ,
} _state{STATE::RESET};
uint16_t _fifo_empty_interval_us{1250}; // default 1250 us / 800 Hz transfer interval
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / GYRO_RATE))};
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / RATE))};
uint8_t _checked_register_bank0{0};
static constexpr uint8_t size_register_bank0_cfg{10};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -242,7 +242,7 @@ enum FIFO_HEADER_BIT : uint8_t {
HEADER_MSG = Bit7, // 1: FIFO is empty
HEADER_ACCEL = Bit6,
HEADER_GYRO = Bit5,
HEADER_20 = Bit4,
HEADER_TIMESTAMP_FSYNC = Bit3 | Bit2,
HEADER_ODR_ACCEL = Bit1,
HEADER_ODR_GYRO = Bit0,
@@ -41,6 +41,4 @@ px4_add_module(
InvenSense_ICM42670P_registers.hpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
)
+231 -180
View File
@@ -33,6 +33,8 @@
#include "ICM42670P.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ ICM42670P::ICM42670P(const I2CSPIDriverConfig &config):
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM42670P::~ICM42670P()
@@ -61,6 +65,7 @@ ICM42670P::~ICM42670P()
perf_free(_fifo_empty_perf);
perf_free(_fifo_overflow_perf);
perf_free(_fifo_reset_perf);
perf_free(_fifo_timestamp_error_perf);
perf_free(_drdy_missed_perf);
}
@@ -102,19 +107,25 @@ void ICM42670P::print_status()
perf_print_counter(_fifo_empty_perf);
perf_print_counter(_fifo_overflow_perf);
perf_print_counter(_fifo_reset_perf);
perf_print_counter(_fifo_timestamp_error_perf);
perf_print_counter(_drdy_missed_perf);
}
int ICM42670P::probe()
{
const uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
if (whoami != WHOAMI) {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
return PX4_ERROR;
if (whoami == WHOAMI) {
return PX4_OK;
} else {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
}
}
return PX4_OK;
return PX4_ERROR;
}
void ICM42670P::RunImpl()
@@ -149,8 +160,8 @@ void ICM42670P::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -158,21 +169,9 @@ void ICM42670P::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
FIFOReset();
// if configure succeeded then reset the FIFO
_state = STATE::FIFO_RESET;
ScheduleDelayed(1_ms);
} else {
// CONFIGURE not complete
@@ -189,6 +188,24 @@ void ICM42670P::RunImpl()
break;
case STATE::FIFO_RESET:
_state = STATE::FIFO_READ;
FIFOReset();
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
break;
case STATE::FIFO_READ: {
hrt_abstime timestamp_sample = now;
uint8_t samples = 0;
@@ -197,7 +214,7 @@ void ICM42670P::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_gyro_samples;
@@ -209,6 +226,8 @@ void ICM42670P::RunImpl()
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
bool success = false;
if (samples == 0) {
// check current FIFO count
const uint16_t fifo_count = FIFOReadCount();
@@ -221,27 +240,42 @@ void ICM42670P::RunImpl()
perf_count(_fifo_empty_perf);
} else {
// FIFO count (size in bytes)
samples = (fifo_count / sizeof(FIFO::DATA));
// FIFO count (size in bytes) should be a multiple of the FIFO::DATA structure
samples = fifo_count / sizeof(FIFO::DATA);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_gyro_samples + 1) {
timestamp_sample -= static_cast<int>(FIFO_SAMPLE_DT);
samples--;
if (samples > _fifo_gyro_samples) {
// grab desired number of samples, but reschedule next cycle sooner
const int extra_samples = samples - _fifo_gyro_samples;
samples = _fifo_gyro_samples;
if (_fifo_gyro_samples > extra_samples) {
// reschedule to run when a total of _fifo_gyro_samples should be available in the FIFO
const uint32_t reschedule_delay_us = (_fifo_gyro_samples - extra_samples) * static_cast<int>(FIFO_SAMPLE_DT);
ScheduleOnInterval(_fifo_empty_interval_us, reschedule_delay_us);
} else {
// otherwise reschedule to run immediately
ScheduleOnInterval(_fifo_empty_interval_us);
}
} else if (samples < _fifo_gyro_samples) {
// reschedule next cycle to catch the desired number of samples
ScheduleOnInterval(_fifo_empty_interval_us, (_fifo_gyro_samples - samples) * static_cast<int>(FIFO_SAMPLE_DT));
}
if (samples > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
FIFOReset();
perf_count(_fifo_overflow_perf);
samples = 0;
if (samples == _fifo_gyro_samples) {
if (FIFORead(now, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
}
}
bool success = false;
if (samples >= 1) {
if (samples == _fifo_gyro_samples) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -256,13 +290,14 @@ void ICM42670P::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_bank0_cfg[_checked_register_bank0])
&& RegisterCheck(_register_mreg1_cfg[_checked_register_mreg1])
) {
@@ -275,13 +310,6 @@ void ICM42670P::RunImpl()
perf_count(_bad_register_perf);
Reset();
}
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature_update_timestamp = now;
}
}
}
@@ -295,10 +323,10 @@ void ICM42670P::ConfigureSampleRate(int sample_rate)
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / RATE), (float)FIFO_MAX_SAMPLES));
// recompute FIFO empty interval (us) with actual gyro sample limit
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / GYRO_RATE);
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / RATE);
ConfigureFIFOWatermark(_fifo_gyro_samples);
}
@@ -346,12 +374,6 @@ bool ICM42670P::Configure()
}
}
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
_px4_gyro.set_scale(math::radians(2000.f / 32768.f));
_px4_gyro.set_range(math::radians(2000.f));
return success;
}
@@ -374,7 +396,7 @@ bool ICM42670P::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool ICM42670P::DataReadyInterruptDisable()
@@ -383,7 +405,7 @@ bool ICM42670P::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
template <typename T>
@@ -457,7 +479,6 @@ void ICM42670P::RegisterWrite(Register::MREG1 reg, uint8_t value)
px4_udelay(10);
}
template <typename T>
void ICM42670P::RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbits)
{
@@ -472,22 +493,35 @@ void ICM42670P::RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbit
uint16_t ICM42670P::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ;
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool ICM42670P::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 6, FIFO::SIZE);
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + INT_STATUS + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 4 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
@@ -500,7 +534,7 @@ bool ICM42670P::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
if (fifo_count_bytes >= FIFO::SIZE) {
perf_count(_fifo_overflow_perf);
@@ -515,52 +549,151 @@ bool ICM42670P::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = 0;
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 8192.f; // highres accel data 8192 LSB/g
sensor_imu_fifo.gyro_scale = math::radians(1.f / 131.f); // highres gyro data 131 LSB/dps
// check FIFO header in every sample
uint8_t valid_samples = 0;
float temperature_sum = 0;
float timestamp_interval_sum = 0;
int timestamp_interval_sum_count = 0;
bool accel_scale_16bit = false; // 18-bits of accelerometer data
bool gyro_scale_16bit = false; // 20-bits of gyroscope data
for (int i = 0; i < math::min(samples, fifo_count_samples); i++) {
bool valid = true;
const FIFO::DATA &fifo = buffer.f[i];
// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8b_0110_10xx
const uint8_t FIFO_HEADER = buffer.f[i].FIFO_Header;
const uint8_t FIFO_HEADER = fifo.FIFO_Header;
if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG) {
// FIFO sample empty if HEADER_MSG set
valid = false;
const bool HEADER_MSG = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG; // FIFO is empty
const bool HEADER_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL;
const bool HEADER_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO;
// 3:2 HEADER_TIMESTAMP_FSYNC
const bool HEADER_ODR_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL; // ODR for accel is different
const bool HEADER_ODR_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO; // ODR for gyro is different
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL)) {
// accel bit not set
valid = false;
if (!HEADER_MSG && HEADER_ACCEL && HEADER_GYRO && !HEADER_ODR_ACCEL && !HEADER_ODR_GYRO) {
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO)) {
// gyro bit not set
valid = false;
// 20 bit data scaled to 16 bit
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 2048.f;
} else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_20) {
// Packet does not contain a new and valid extended 20-bit data
valid = false;
sensor_imu_fifo.accel_x[valid_samples] = combine(fifo.ACCEL_DATA_X1, fifo.ACCEL_DATA_X0);
sensor_imu_fifo.accel_y[valid_samples] = combine(fifo.ACCEL_DATA_Y1, fifo.ACCEL_DATA_Y0);
sensor_imu_fifo.accel_z[valid_samples] = combine(fifo.ACCEL_DATA_Z1, fifo.ACCEL_DATA_Z0);
} else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL) {
// accel ODR changed
valid = false;
sensor_imu_fifo.gyro_x[i] = combine(fifo.GYRO_DATA_X1, fifo.GYRO_DATA_X0);
sensor_imu_fifo.gyro_y[i] = combine(fifo.GYRO_DATA_Y1, fifo.GYRO_DATA_Y0);
sensor_imu_fifo.gyro_z[i] = combine(fifo.GYRO_DATA_Z1, fifo.GYRO_DATA_Z0);
} else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO) {
// gyro ODR changed
valid = false;
}
// temperature
const int16_t TEMP_DATA = combine(fifo.TEMP_DATA1, fifo.TEMP_DATA0);
// sample invalid if -32768
if (TEMP_DATA != -32768) {
temperature_sum += TEMP_DATA;
} else {
break;
}
// HEADER_TIMESTAMP_FSYNC - 0b10: Packet contains ODR Timestamp
if (FIFO_HEADER & Bit3) {
const uint16_t timestamp = (fifo.TimeStamp_h << 8) + fifo.TimeStamp_l;
if (_timestamp_prev != 0) {
// If TMST_RES = 0 (corresponding to timestamp resolution of 1µs), timestamp interval reported in FIFO requires scaling by a factor of 32/30.
// Document Number: DS-000347 Revision: 1.5 Page 59 of 110
static constexpr float FIFO_DT_SCALE = 32.f / 30.f;
float dt = 0;
if (timestamp > _timestamp_prev) {
dt = static_cast<float>(timestamp - _timestamp_prev) * FIFO_DT_SCALE;
} else if (timestamp < _timestamp_prev) {
// uint16_t rollover
uint32_t timestamp_new = UINT16_MAX + timestamp;
dt = static_cast<float>(timestamp_new - _timestamp_prev) * FIFO_DT_SCALE;
}
timestamp_interval_sum += dt;
timestamp_interval_sum_count++;
// check dt is within +=2% of expected value
if ((dt < (FIFO_SAMPLE_DT * 0.98f)) || (dt > (FIFO_SAMPLE_DT * 1.02f))) {
perf_count(_fifo_timestamp_error_perf);
}
}
_timestamp_prev = timestamp;
}
if (valid) {
valid_samples++;
} else {
perf_count(_bad_transfer_perf);
break;
}
}
if (valid_samples > 0) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
ProcessAccel(timestamp_sample, buffer.f, valid_samples);
sensor_imu_fifo.samples = valid_samples;
for (int i = 0; i < sensor_imu_fifo.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
// sensor_imu_fifo.accel_x[i]
sensor_imu_fifo.accel_y[i] = math::negate(sensor_imu_fifo.accel_y[i]);
sensor_imu_fifo.accel_z[i] = math::negate(sensor_imu_fifo.accel_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
// sensor_imu_fifo.gyro_x[i]
sensor_imu_fifo.gyro_y[i] = math::negate(sensor_imu_fifo.gyro_y[i]);
sensor_imu_fifo.gyro_z[i] = math::negate(sensor_imu_fifo.gyro_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
const float temperature_avg = temperature_sum / valid_samples;
// use average temperature reading
const float TEMP_degC = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
sensor_imu_fifo.temperature = TEMP_degC;
} else {
perf_count(_bad_transfer_perf);
return false;
}
if (timestamp_interval_sum > 0) {
const float dt_avg = (timestamp_interval_sum / timestamp_interval_sum_count);
// check dt is within +=1% of expected value
if ((dt_avg < (FIFO_SAMPLE_DT * 0.99f)) || (dt_avg > (FIFO_SAMPLE_DT * 1.01f))) {
PX4_ERR("DT error %.6f", (double)dt_avg);
perf_count(_fifo_timestamp_error_perf);
} else {
sensor_imu_fifo.dt = dt_avg;
}
}
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
@@ -571,93 +704,11 @@ void ICM42670P::FIFOReset()
{
perf_count(_fifo_reset_perf);
// FIFO flush requires the following programming sequence:
// Write FIFO_FLUSH = 1
// Wait for 1.5 µs
// Read FIFO_FLUSH, it should now be 0
// SIGNAL_PATH_RESET: FIFO flush
RegisterSetBits(Register::BANK_0::SIGNAL_PATH_RESET, SIGNAL_PATH_RESET_BIT::FIFO_FLUSH);
px4_udelay(2); // Wait for 1.5 µs
const uint8_t SIGNAL_PATH_RESET = RegisterRead(Register::BANK_0::SIGNAL_PATH_RESET);
if ((SIGNAL_PATH_RESET & SIGNAL_PATH_RESET_BIT::FIFO_FLUSH) != 0) {
PX4_DEBUG("SIGNAL_PATH_RESET FIFO_FLUSH failed");
}
// reset while FIFO is disabled
_drdy_timestamp_sample.store(0);
}
void ICM42670P::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = samples;
accel.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
int16_t accel_x = combine(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0);
int16_t accel_y = combine(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0);
int16_t accel_z = combine(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[i] = accel_x;
accel.y[i] = math::negate(accel_y);
accel.z[i] = math::negate(accel_z);
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_accel.updateFIFO(accel);
}
void ICM42670P::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0);
const int16_t gyro_y = combine(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0);
const int16_t gyro_z = combine(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = math::negate(gyro_y);
gyro.z[i] = math::negate(gyro_z);
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void ICM42670P::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::BANK_0::TEMP_DATA1) | DIR_READ;
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
}
const int16_t TEMP_DATA = combine(temperature_buf[1], temperature_buf[2]);
// Temperature in Degrees Centigrade
const float TEMP_degC = (TEMP_DATA / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
}
_timestamp_prev = 0;
}
@@ -43,13 +43,15 @@
#include "InvenSense_ICM42670P_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM42670P;
class ICM42670P : public device::SPI, public I2CSPIDriver<ICM42670P>
@@ -70,24 +72,10 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 1600.f}; // 1600 Hz accel & gyro ODR configured
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float ACCEL_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float RATE{1e6f / FIFO_SAMPLE_DT};
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t INT_STATUS2{0};
uint8_t INT_STATUS3{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (6 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
struct register_bank0_config_t {
Register::BANK_0 reg;
@@ -129,20 +117,18 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
void ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")};
perf_counter_t _fifo_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO overflow")};
perf_counter_t _fifo_reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO reset")};
perf_counter_t _fifo_timestamp_error_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO timestamp error")};
perf_counter_t _drdy_missed_perf{nullptr};
hrt_abstime _reset_timestamp{0};
@@ -150,6 +136,8 @@ private:
hrt_abstime _temperature_update_timestamp{0};
int _failure_count{0};
uint16_t _timestamp_prev{0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
bool _data_ready_interrupt_enabled{false};
@@ -157,11 +145,12 @@ private:
RESET,
WAIT_FOR_RESET,
CONFIGURE,
FIFO_RESET,
FIFO_READ,
} _state{STATE::RESET};
uint16_t _fifo_empty_interval_us{1250}; // default 1250 us / 800 Hz transfer interval
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / GYRO_RATE))};
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / RATE))};
uint8_t _checked_register_bank0{0};
static constexpr uint8_t size_register_bank0_cfg{10};
@@ -231,7 +231,7 @@ enum FIFO_HEADER_BIT : uint8_t {
HEADER_MSG = Bit7, // 1: FIFO is empty
HEADER_ACCEL = Bit6, // 1: Packet is sized so that accel data have location in the packet, FIFO_ACCEL_EN must be 1
HEADER_GYRO = Bit5, // 1: Packet is sized so that gyro data have location in the packet, FIFO_GYRO_EN must be1
HEADER_20 = Bit4, // 1: Packet has a new and valid sample of extended 20-bit data for gyro and/or accel
HEADER_TIMESTAMP_FSYNC = Bit3 | Bit2,
HEADER_ODR_ACCEL = Bit1, // 1: The ODR for accel is different for this accel data packet compared to the previous accel packet
HEADER_ODR_GYRO = Bit0, // 1: The ODR for gyro is different for this gyro data packet compared to the previous gyro packet
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2020 PX4 Development Team. All rights reserved.
# Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -42,6 +42,4 @@ px4_add_module(
InvenSense_ICM42688P_registers.hpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
)
+307 -298
View File
@@ -33,6 +33,8 @@
#include "ICM42688P.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ ICM42688P::ICM42688P(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
ICM42688P::~ICM42688P()
@@ -61,6 +65,7 @@ ICM42688P::~ICM42688P()
perf_free(_fifo_empty_perf);
perf_free(_fifo_overflow_perf);
perf_free(_fifo_reset_perf);
perf_free(_fifo_timestamp_error_perf);
perf_free(_drdy_missed_perf);
}
@@ -102,12 +107,14 @@ void ICM42688P::print_status()
perf_print_counter(_fifo_empty_perf);
perf_print_counter(_fifo_overflow_perf);
perf_print_counter(_fifo_reset_perf);
perf_print_counter(_fifo_timestamp_error_perf);
perf_print_counter(_drdy_missed_perf);
}
int ICM42688P::probe()
{
for (int i = 0; i < 3; i++) {
// 3 retries
for (int retry = 0; retry < 3; retry++) {
uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
if (whoami == WHOAMI) {
@@ -162,8 +169,8 @@ void ICM42688P::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -216,7 +223,7 @@ void ICM42688P::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_gyro_samples;
@@ -228,6 +235,8 @@ void ICM42688P::RunImpl()
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
bool success = false;
if (samples == 0) {
// check current FIFO count
const uint16_t fifo_count = FIFOReadCount();
@@ -240,27 +249,42 @@ void ICM42688P::RunImpl()
perf_count(_fifo_empty_perf);
} else {
// FIFO count (size in bytes)
samples = (fifo_count / sizeof(FIFO::DATA));
// FIFO count (size in bytes) should be a multiple of the FIFO::DATA structure
samples = fifo_count / sizeof(FIFO::DATA);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_gyro_samples + 1) {
timestamp_sample -= static_cast<int>(FIFO_SAMPLE_DT);
samples--;
if (samples > _fifo_gyro_samples) {
// grab desired number of samples, but reschedule next cycle sooner
const int extra_samples = samples - _fifo_gyro_samples;
samples = _fifo_gyro_samples;
if (_fifo_gyro_samples > extra_samples) {
// reschedule to run when a total of _fifo_gyro_samples should be available in the FIFO
const uint32_t reschedule_delay_us = (_fifo_gyro_samples - extra_samples) * static_cast<int>(FIFO_SAMPLE_DT);
ScheduleOnInterval(_fifo_empty_interval_us, reschedule_delay_us);
} else {
// otherwise reschedule to run immediately
ScheduleOnInterval(_fifo_empty_interval_us);
}
} else if (samples < _fifo_gyro_samples) {
// reschedule next cycle to catch the desired number of samples
ScheduleOnInterval(_fifo_empty_interval_us, (_fifo_gyro_samples - samples) * static_cast<int>(FIFO_SAMPLE_DT));
}
if (samples > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
FIFOReset();
perf_count(_fifo_overflow_perf);
samples = 0;
if (samples == _fifo_gyro_samples) {
if (FIFORead(now, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
}
}
bool success = false;
if (samples >= 1) {
if (samples == _fifo_gyro_samples) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -275,13 +299,14 @@ void ICM42688P::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_bank0_cfg[_checked_register_bank0])
&& RegisterCheck(_register_bank1_cfg[_checked_register_bank1])
&& RegisterCheck(_register_bank2_cfg[_checked_register_bank2])
@@ -309,10 +334,10 @@ void ICM42688P::ConfigureSampleRate(int sample_rate)
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / RATE), (float)FIFO_MAX_SAMPLES));
// recompute FIFO empty interval (us) with actual gyro sample limit
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / GYRO_RATE);
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / RATE);
ConfigureFIFOWatermark(_fifo_gyro_samples);
}
@@ -362,6 +387,7 @@ bool ICM42688P::Configure()
RegisterSetAndClearBits(reg_cfg.reg, reg_cfg.set_bits, reg_cfg.clear_bits);
}
// now check that all are configured
bool success = true;
@@ -383,11 +409,6 @@ bool ICM42688P::Configure()
}
}
// 20-bits data format used
// the only FSR settings that are operational are ±2000dps for gyroscope and ±16g for accelerometer
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
_px4_gyro.set_range(math::radians(2000.f));
return success;
}
@@ -410,7 +431,7 @@ bool ICM42688P::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool ICM42688P::DataReadyInterruptDisable()
@@ -419,7 +440,7 @@ bool ICM42688P::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
template <typename T>
@@ -474,114 +495,20 @@ void ICM42688P::RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbit
uint16_t ICM42688P::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ;
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
}
bool ICM42688P::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 4, FIFO::SIZE);
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
if (buffer.INT_STATUS & INT_STATUS_BIT::FIFO_FULL_INT) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
if (fifo_count_bytes >= FIFO::SIZE) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
if (fifo_count_samples == 0) {
perf_count(_fifo_empty_perf);
return false;
}
// check FIFO header in every sample
uint8_t valid_samples = 0;
for (int i = 0; i < math::min(samples, fifo_count_samples); i++) {
bool valid = true;
// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8b_0110_10xx
const uint8_t FIFO_HEADER = buffer.f[i].FIFO_Header;
if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG) {
// FIFO sample empty if HEADER_MSG set
valid = false;
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL)) {
// accel bit not set
valid = false;
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO)) {
// gyro bit not set
valid = false;
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_20)) {
// Packet does not contain a new and valid extended 20-bit data
valid = false;
} else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL) {
// accel ODR changed
valid = false;
} else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO) {
// gyro ODR changed
valid = false;
}
if (valid) {
valid_samples++;
} else {
perf_count(_bad_transfer_perf);
break;
}
}
if (valid_samples > 0) {
if (ProcessTemperature(buffer.f, valid_samples)) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
ProcessAccel(timestamp_sample, buffer.f, valid_samples);
return true;
}
}
return false;
}
void ICM42688P::FIFOReset()
{
perf_count(_fifo_reset_perf);
// SIGNAL_PATH_RESET: FIFO flush
RegisterSetBits(Register::BANK_0::SIGNAL_PATH_RESET, SIGNAL_PATH_RESET_BIT::FIFO_FLUSH);
// reset while FIFO is disabled
_drdy_timestamp_sample.store(0);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
static constexpr int32_t reassemble_20bit(const uint32_t a, const uint32_t b, const uint32_t c)
@@ -601,206 +528,288 @@ static constexpr int32_t reassemble_20bit(const uint32_t a, const uint32_t b, co
return static_cast<int32_t>(x);
}
void ICM42688P::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
bool ICM42688P::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// 18-bits of accelerometer data
bool scale_20bit = false;
// cmd + INT_STATUS + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 4 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
// first pass
for (int i = 0; i < samples; i++) {
// 20 bit hires mode
// Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte)
// Accel data is 18 bit ()
int32_t accel_x = reassemble_20bit(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0,
fifo[i].Ext_Accel_X_Gyro_X & 0xF0 >> 4);
int32_t accel_y = reassemble_20bit(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0,
fifo[i].Ext_Accel_Y_Gyro_Y & 0xF0 >> 4);
int32_t accel_z = reassemble_20bit(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0,
fifo[i].Ext_Accel_Z_Gyro_Z & 0xF0 >> 4);
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// sample invalid if -524288
if (accel_x != -524288 && accel_y != -524288 && accel_z != -524288) {
// check if any values are going to exceed int16 limits
static constexpr int16_t max_accel = INT16_MAX;
static constexpr int16_t min_accel = INT16_MIN;
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
if (accel_x >= max_accel || accel_x <= min_accel) {
scale_20bit = true;
if (buffer.INT_STATUS & INT_STATUS_BIT::FIFO_FULL_INT) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
if (fifo_count_bytes >= FIFO::SIZE) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
if (fifo_count_samples == 0) {
perf_count(_fifo_empty_perf);
return false;
}
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = 0;
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 8192.f; // highres accel data 8192 LSB/g
sensor_imu_fifo.gyro_scale = math::radians(1.f / 131.f); // highres gyro data 131 LSB/dps
// check FIFO header in every sample
uint8_t valid_samples = 0;
float temperature_sum = 0;
float timestamp_interval_sum = 0;
int timestamp_interval_sum_count = 0;
bool accel_scale_16bit = false; // 18-bits of accelerometer data
bool gyro_scale_16bit = false; // 20-bits of gyroscope data
for (int i = 0; i < math::min(samples, fifo_count_samples); i++) {
const FIFO::DATA &fifo = buffer.f[i];
// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8b_0110_10xx
const uint8_t FIFO_HEADER = fifo.FIFO_Header;
const bool HEADER_MSG = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG; // FIFO is empty
const bool HEADER_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL;
const bool HEADER_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO;
const bool HEADER_20 = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_20; // valid sample of extended 20-bit data
// 3:2 HEADER_TIMESTAMP_FSYNC
const bool HEADER_ODR_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL; // ODR for accel is different
const bool HEADER_ODR_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO; // ODR for gyro is different
if (!HEADER_MSG && HEADER_ACCEL && HEADER_GYRO && HEADER_20 && !HEADER_ODR_ACCEL && !HEADER_ODR_GYRO) {
if (!accel_scale_16bit) {
// Accel: 20 bit hires mode, Accel data is 18 bit
// Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte)
int32_t accel_x = reassemble_20bit(fifo.ACCEL_DATA_X1, fifo.ACCEL_DATA_X0, fifo.Ext_Accel_X_Gyro_X & 0xF0 >> 4);
int32_t accel_y = reassemble_20bit(fifo.ACCEL_DATA_Y1, fifo.ACCEL_DATA_Y0, fifo.Ext_Accel_Y_Gyro_Y & 0xF0 >> 4);
int32_t accel_z = reassemble_20bit(fifo.ACCEL_DATA_Z1, fifo.ACCEL_DATA_Z0, fifo.Ext_Accel_Z_Gyro_Z & 0xF0 >> 4);
// sample invalid if -524288
if (accel_x == -524288 || accel_y == -524288 || accel_z == -524288) {
break;
}
// shift by 2 (2 least significant bits are always 0)
accel_x = accel_x / 4;
accel_y = accel_y / 4;
accel_z = accel_z / 4;
// check if any values are going to exceed int16 limits
if ((accel_x >= INT16_MAX || accel_x <= INT16_MIN)
|| (accel_y >= INT16_MAX || accel_y <= INT16_MIN)
|| (accel_z >= INT16_MAX || accel_z <= INT16_MIN)) {
accel_scale_16bit = true;
// 20 bit data scaled to 16 bit
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 2048.f;
// rescale any existing data
for (int j = 0; j < valid_samples + 1; j++) {
sensor_imu_fifo.accel_x[valid_samples] = combine(buffer.f[j].ACCEL_DATA_X1, buffer.f[j].ACCEL_DATA_X0);
sensor_imu_fifo.accel_y[valid_samples] = combine(buffer.f[j].ACCEL_DATA_Y1, buffer.f[j].ACCEL_DATA_Y0);
sensor_imu_fifo.accel_z[valid_samples] = combine(buffer.f[j].ACCEL_DATA_Z1, buffer.f[j].ACCEL_DATA_Z0);
}
} else {
sensor_imu_fifo.accel_x[valid_samples] = accel_x;
sensor_imu_fifo.accel_y[valid_samples] = accel_y;
sensor_imu_fifo.accel_z[valid_samples] = accel_z;
}
} else {
sensor_imu_fifo.accel_x[valid_samples] = combine(fifo.ACCEL_DATA_X1, fifo.ACCEL_DATA_X0);
sensor_imu_fifo.accel_y[valid_samples] = combine(fifo.ACCEL_DATA_Y1, fifo.ACCEL_DATA_Y0);
sensor_imu_fifo.accel_z[valid_samples] = combine(fifo.ACCEL_DATA_Z1, fifo.ACCEL_DATA_Z0);
}
if (accel_y >= max_accel || accel_y <= min_accel) {
scale_20bit = true;
if (!gyro_scale_16bit) {
// Gyro: 20 bit hires mode
// Gyro [19:12] + Gyro [11:4] + Gyro [3:0] (bottom 4 bits of 20 bit extension byte)
int32_t gyro_x = reassemble_20bit(fifo.GYRO_DATA_X1, fifo.GYRO_DATA_X0, fifo.Ext_Accel_X_Gyro_X & 0x0F);
int32_t gyro_y = reassemble_20bit(fifo.GYRO_DATA_Y1, fifo.GYRO_DATA_Y0, fifo.Ext_Accel_Y_Gyro_Y & 0x0F);
int32_t gyro_z = reassemble_20bit(fifo.GYRO_DATA_Z1, fifo.GYRO_DATA_Z0, fifo.Ext_Accel_Z_Gyro_Z & 0x0F);
// shift by 1 (least significant bit is always 0)
gyro_x = gyro_x / 2;
gyro_y = gyro_y / 2;
gyro_z = gyro_z / 2;
// check if any gyro values are going to exceed int16 limits
if ((gyro_x >= INT16_MAX || gyro_x <= INT16_MIN)
|| (gyro_y >= INT16_MAX || gyro_y <= INT16_MIN)
|| (gyro_z >= INT16_MAX || gyro_z <= INT16_MIN)) {
gyro_scale_16bit = true;
// 20 bit data scaled to 16 bit
sensor_imu_fifo.gyro_scale = math::radians(2000.f / 32768.f);
// rescale any existing data
for (int j = 0; j < valid_samples + 1; j++) {
sensor_imu_fifo.gyro_x[j] = combine(buffer.f[j].GYRO_DATA_X1, buffer.f[j].GYRO_DATA_X0);
sensor_imu_fifo.gyro_y[j] = combine(buffer.f[j].GYRO_DATA_Y1, buffer.f[j].GYRO_DATA_Y0);
sensor_imu_fifo.gyro_z[j] = combine(buffer.f[j].GYRO_DATA_Z1, buffer.f[j].GYRO_DATA_Z0);
}
} else {
sensor_imu_fifo.gyro_x[valid_samples] = gyro_x;
sensor_imu_fifo.gyro_y[valid_samples] = gyro_y;
sensor_imu_fifo.gyro_z[valid_samples] = gyro_z;
}
} else {
sensor_imu_fifo.gyro_x[i] = combine(fifo.GYRO_DATA_X1, fifo.GYRO_DATA_X0);
sensor_imu_fifo.gyro_y[i] = combine(fifo.GYRO_DATA_Y1, fifo.GYRO_DATA_Y0);
sensor_imu_fifo.gyro_z[i] = combine(fifo.GYRO_DATA_Z1, fifo.GYRO_DATA_Z0);
}
if (accel_z >= max_accel || accel_z <= min_accel) {
scale_20bit = true;
// temperature
const int16_t TEMP_DATA = combine(fifo.TEMP_DATA1, fifo.TEMP_DATA0);
// sample invalid if -32768
if (TEMP_DATA != -32768) {
temperature_sum += TEMP_DATA;
} else {
break;
}
// shift by 2 (2 least significant bits are always 0)
accel.x[accel.samples] = accel_x / 4;
accel.y[accel.samples] = accel_y / 4;
accel.z[accel.samples] = accel_z / 4;
accel.samples++;
}
}
if (!scale_20bit) {
// if highres enabled accel data is always 8192 LSB/g
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
// HEADER_TIMESTAMP_FSYNC - 0b10: Packet contains ODR Timestamp
if (FIFO_HEADER & Bit3) {
const uint16_t timestamp = (fifo.TimeStamp_h << 8) + fifo.TimeStamp_l;
} else {
// 20 bit data scaled to 16 bit (2^4)
for (int i = 0; i < samples; i++) {
// 20 bit hires mode
// Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte)
// Accel data is 18 bit ()
int16_t accel_x = combine(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0);
int16_t accel_y = combine(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0);
int16_t accel_z = combine(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0);
if (_timestamp_prev != 0) {
// If TMST_RES = 0 (corresponding to timestamp resolution of 1µs), timestamp interval reported in FIFO requires scaling by a factor of 32/30.
// Document Number: DS-000347 Revision: 1.5 Page 59 of 110
static constexpr float FIFO_DT_SCALE = 32.f / 30.f;
accel.x[i] = accel_x;
accel.y[i] = accel_y;
accel.z[i] = accel_z;
}
float dt = 0;
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
}
if (timestamp > _timestamp_prev) {
dt = static_cast<float>(timestamp - _timestamp_prev) * FIFO_DT_SCALE;
// correct frame for publication
for (int i = 0; i < accel.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[i] = accel.x[i];
accel.y[i] = (accel.y[i] == INT16_MIN) ? INT16_MAX : -accel.y[i];
accel.z[i] = (accel.z[i] == INT16_MIN) ? INT16_MAX : -accel.z[i];
}
} else if (timestamp < _timestamp_prev) {
// uint16_t rollover
uint32_t timestamp_new = UINT16_MAX + timestamp;
dt = static_cast<float>(timestamp_new - _timestamp_prev) * FIFO_DT_SCALE;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
timestamp_interval_sum += dt;
timestamp_interval_sum_count++;
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
}
// check dt is within +=2% of expected value
if ((dt < (FIFO_SAMPLE_DT * 0.98f)) || (dt > (FIFO_SAMPLE_DT * 1.02f))) {
perf_count(_fifo_timestamp_error_perf);
}
}
void ICM42688P::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = 0;
gyro.dt = FIFO_SAMPLE_DT;
_timestamp_prev = timestamp;
}
// 20-bits of gyroscope data
bool scale_20bit = false;
// first pass
for (int i = 0; i < samples; i++) {
// 20 bit hires mode
// Gyro [19:12] + Gyro [11:4] + Gyro [3:0] (bottom 4 bits of 20 bit extension byte)
int32_t gyro_x = reassemble_20bit(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0, fifo[i].Ext_Accel_X_Gyro_X & 0x0F);
int32_t gyro_y = reassemble_20bit(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0, fifo[i].Ext_Accel_Y_Gyro_Y & 0x0F);
int32_t gyro_z = reassemble_20bit(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0, fifo[i].Ext_Accel_Z_Gyro_Z & 0x0F);
// check if any values are going to exceed int16 limits
static constexpr int16_t max_gyro = INT16_MAX;
static constexpr int16_t min_gyro = INT16_MIN;
if (gyro_x >= max_gyro || gyro_x <= min_gyro) {
scale_20bit = true;
}
if (gyro_y >= max_gyro || gyro_y <= min_gyro) {
scale_20bit = true;
}
if (gyro_z >= max_gyro || gyro_z <= min_gyro) {
scale_20bit = true;
}
gyro.x[gyro.samples] = gyro_x / 2;
gyro.y[gyro.samples] = gyro_y / 2;
gyro.z[gyro.samples] = gyro_z / 2;
gyro.samples++;
}
if (!scale_20bit) {
// if highres enabled gyro data is always 131 LSB/dps
_px4_gyro.set_scale(math::radians(1.f / 131.f));
} else {
// 20 bit data scaled to 16 bit (2^4)
for (int i = 0; i < samples; i++) {
gyro.x[i] = combine(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0);
gyro.y[i] = combine(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0);
gyro.z[i] = combine(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0);
}
_px4_gyro.set_scale(math::radians(2000.f / 32768.f));
}
// correct frame for publication
for (int i = 0; i < gyro.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro.x[i];
gyro.y[i] = (gyro.y[i] == INT16_MIN) ? INT16_MAX : -gyro.y[i];
gyro.z[i] = (gyro.z[i] == INT16_MIN) ? INT16_MAX : -gyro.z[i];
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (gyro.samples > 0) {
_px4_gyro.updateFIFO(gyro);
}
}
bool ICM42688P::ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples)
{
int16_t temperature[FIFO_MAX_SAMPLES];
float temperature_sum{0};
int valid_samples = 0;
for (int i = 0; i < samples; i++) {
const int16_t t = combine(fifo[i].TEMP_DATA1, fifo[i].TEMP_DATA0);
// sample invalid if -32768
if (t != -32768) {
temperature_sum += t;
temperature[valid_samples] = t;
valid_samples++;
}
}
if (valid_samples > 0) {
const float temperature_avg = temperature_sum / valid_samples;
for (int i = 0; i < valid_samples; i++) {
// temperature changing wildly is an indication of a transfer error
if (fabsf(temperature[i] - temperature_avg) > 1000) {
perf_count(_bad_transfer_perf);
return false;
}
sensor_imu_fifo.samples = valid_samples;
for (int i = 0; i < sensor_imu_fifo.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
// sensor_imu_fifo.accel_x[i]
sensor_imu_fifo.accel_y[i] = math::negate(sensor_imu_fifo.accel_y[i]);
sensor_imu_fifo.accel_z[i] = math::negate(sensor_imu_fifo.accel_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
// sensor_imu_fifo.gyro_x[i]
sensor_imu_fifo.gyro_y[i] = math::negate(sensor_imu_fifo.gyro_y[i]);
sensor_imu_fifo.gyro_z[i] = math::negate(sensor_imu_fifo.gyro_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
const float temperature_avg = temperature_sum / valid_samples;
// use average temperature reading
const float TEMP_degC = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
return true;
sensor_imu_fifo.temperature = TEMP_degC;
} else {
perf_count(_bad_transfer_perf);
return false;
}
if (timestamp_interval_sum > 0) {
const float dt_avg = (timestamp_interval_sum / timestamp_interval_sum_count);
// check dt is within +=1% of expected value
if ((dt_avg < (FIFO_SAMPLE_DT * 0.99f)) || (dt_avg > (FIFO_SAMPLE_DT * 1.01f))) {
PX4_ERR("DT error %.6f", (double)dt_avg);
perf_count(_fifo_timestamp_error_perf);
} else {
sensor_imu_fifo.dt = dt_avg;
}
}
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
}
void ICM42688P::FIFOReset()
{
perf_count(_fifo_reset_perf);
// SIGNAL_PATH_RESET: FIFO flush
RegisterSetBits(Register::BANK_0::SIGNAL_PATH_RESET, SIGNAL_PATH_RESET_BIT::FIFO_FLUSH);
// reset while FIFO is disabled
_drdy_timestamp_sample.store(0);
_timestamp_prev = 0;
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_ICM42688P_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_ICM42688P;
class ICM42688P : public device::SPI, public I2CSPIDriver<ICM42688P>
@@ -71,22 +72,10 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f}; // 8000 Hz accel & gyro ODR configured
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float ACCEL_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float RATE{1e6f / FIFO_SAMPLE_DT};
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (4 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
struct register_bank0_config_t {
Register::BANK_0 reg;
@@ -135,20 +124,18 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
void ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
bool ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples);
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")};
perf_counter_t _fifo_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO overflow")};
perf_counter_t _fifo_reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO reset")};
perf_counter_t _fifo_timestamp_error_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO timestamp error")};
perf_counter_t _drdy_missed_perf{nullptr};
hrt_abstime _reset_timestamp{0};
@@ -156,6 +143,8 @@ private:
hrt_abstime _temperature_update_timestamp{0};
int _failure_count{0};
uint16_t _timestamp_prev{0};
enum REG_BANK_SEL_BIT _last_register_bank {REG_BANK_SEL_BIT::USER_BANK_0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
@@ -170,25 +159,26 @@ private:
} _state{STATE::RESET};
uint16_t _fifo_empty_interval_us{1250}; // default 1250 us / 800 Hz transfer interval
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / GYRO_RATE))};
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / RATE))};
uint8_t _checked_register_bank0{0};
static constexpr uint8_t size_register_bank0_cfg{13};
static constexpr uint8_t size_register_bank0_cfg{14};
register_bank0_config_t _register_bank0_cfg[size_register_bank0_cfg] {
// Register | Set bits, Clear bits
{ Register::BANK_0::INT_CONFIG, INT_CONFIG_BIT::INT1_MODE | INT_CONFIG_BIT::INT1_DRIVE_CIRCUIT, INT_CONFIG_BIT::INT1_POLARITY },
{ Register::BANK_0::FIFO_CONFIG, FIFO_CONFIG_BIT::FIFO_MODE_STOP_ON_FULL, 0 },
{ Register::BANK_0::PWR_MGMT0, PWR_MGMT0_BIT::GYRO_MODE_LOW_NOISE | PWR_MGMT0_BIT::ACCEL_MODE_LOW_NOISE, 0 },
{ Register::BANK_0::GYRO_CONFIG0, GYRO_CONFIG0_BIT::GYRO_FS_SEL_2000_DPS | GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_SET, GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_CLEAR },
{ Register::BANK_0::ACCEL_CONFIG0, ACCEL_CONFIG0_BIT::ACCEL_FS_SEL_16G | ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_SET, ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_CLEAR },
{ Register::BANK_0::GYRO_CONFIG1, 0, GYRO_CONFIG1_BIT::GYRO_UI_FILT_ORD },
{ Register::BANK_0::GYRO_CONFIG0, GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_SET, GYRO_CONFIG0_BIT::GYRO_FS_SEL_2000_DPS_CLEAR | GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_CLEAR },
{ Register::BANK_0::ACCEL_CONFIG0, ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_SET, ACCEL_CONFIG0_BIT::ACCEL_FS_SEL_16G_CLEAR | ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_CLEAR },
{ Register::BANK_0::GYRO_CONFIG1, 0, GYRO_CONFIG1_BIT::GYRO_UI_FILT_ORD_1ST_CLEAR },
{ Register::BANK_0::GYRO_ACCEL_CONFIG0, 0, GYRO_ACCEL_CONFIG0_BIT::ACCEL_UI_FILT_BW | GYRO_ACCEL_CONFIG0_BIT::GYRO_UI_FILT_BW },
{ Register::BANK_0::ACCEL_CONFIG1, 0, ACCEL_CONFIG1_BIT::ACCEL_UI_FILT_ORD },
{ Register::BANK_0::FIFO_CONFIG1, FIFO_CONFIG1_BIT::FIFO_WM_GT_TH | FIFO_CONFIG1_BIT::FIFO_HIRES_EN | FIFO_CONFIG1_BIT::FIFO_TEMP_EN | FIFO_CONFIG1_BIT::FIFO_GYRO_EN | FIFO_CONFIG1_BIT::FIFO_ACCEL_EN, 0 },
{ Register::BANK_0::ACCEL_CONFIG1, 0, ACCEL_CONFIG1_BIT::ACCEL_UI_FILT_ORD_1ST_CLEAR },
{ Register::BANK_0::FIFO_CONFIG1, FIFO_CONFIG1_BIT::FIFO_RESUME_PARTIAL_RD | FIFO_CONFIG1_BIT::FIFO_HIRES_EN | FIFO_CONFIG1_BIT::FIFO_TEMP_EN | FIFO_CONFIG1_BIT::FIFO_GYRO_EN | FIFO_CONFIG1_BIT::FIFO_ACCEL_EN, 0 },
{ Register::BANK_0::FIFO_CONFIG2, 0, 0 }, // FIFO_WM[7:0] set at runtime
{ Register::BANK_0::FIFO_CONFIG3, 0, 0 }, // FIFO_WM[11:8] set at runtime
{ Register::BANK_0::INT_CONFIG0, INT_CONFIG0_BIT::CLEAR_ON_FIFO_READ, 0 },
{ Register::BANK_0::INT_SOURCE0, INT_SOURCE0_BIT::FIFO_THS_INT1_EN, 0 },
{ Register::BANK_0::INT_CONFIG0, INT_CONFIG0_BIT::FIFO_THS_INT_CLEAR, 0 },
{ Register::BANK_0::INT_CONFIG1, INT_CONFIG1_BIT::INT_TPULSE_DURATION | INT_CONFIG1_BIT::INT_TDEASSERT_DISABLE, INT_CONFIG1_BIT::INT_ASYNC_RESET },
{ Register::BANK_0::INT_SOURCE0, INT_SOURCE0_BIT::FIFO_THS_INT1_EN, INT_SOURCE0_BIT::RESET_DONE_INT1_EN },
};
uint8_t _checked_register_bank1{0};
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -82,8 +82,7 @@ enum class BANK_0 : uint8_t {
FIFO_DATA = 0x30,
SIGNAL_PATH_RESET = 0x4B,
INTF_CONFIG0 = 0x4C,
INTF_CONFIG1 = 0x4D,
PWR_MGMT0 = 0x4E,
GYRO_CONFIG0 = 0x4F,
ACCEL_CONFIG0 = 0x50,
@@ -96,7 +95,7 @@ enum class BANK_0 : uint8_t {
FIFO_CONFIG3 = 0x61,
INT_CONFIG0 = 0x63,
INT_CONFIG1 = 0x64,
INT_SOURCE0 = 0x65,
SELF_TEST_CONFIG = 0x70,
@@ -121,7 +120,7 @@ enum class BANK_2 : uint8_t {
// DEVICE_CONFIG
enum DEVICE_CONFIG_BIT : uint8_t {
SOFT_RESET_CONFIG = Bit0, //
SOFT_RESET_CONFIG = Bit0,
};
// INT_CONFIG
@@ -140,15 +139,14 @@ enum FIFO_CONFIG_BIT : uint8_t {
// INT_STATUS
enum INT_STATUS_BIT : uint8_t {
RESET_DONE_INT = Bit4,
DATA_RDY_INT = Bit3,
FIFO_THS_INT = Bit2,
FIFO_FULL_INT = Bit1,
};
// SIGNAL_PATH_RESET
enum SIGNAL_PATH_RESET_BIT : uint8_t {
ABORT_AND_RESET = Bit3,
FIFO_FLUSH = Bit1,
FIFO_FLUSH = Bit1,
};
// PWR_MGMT0
@@ -160,55 +158,42 @@ enum PWR_MGMT0_BIT : uint8_t {
// GYRO_CONFIG0
enum GYRO_CONFIG0_BIT : uint8_t {
// 7:5 GYRO_FS_SEL
GYRO_FS_SEL_2000_DPS = 0, // 0b000 = ±2000dps (default)
GYRO_FS_SEL_1000_DPS = Bit5,
GYRO_FS_SEL_500_DPS = Bit6,
GYRO_FS_SEL_250_DPS = Bit6 | Bit5,
GYRO_FS_SEL_125_DPS = Bit7,
// 0b000: ±2000dps (default)
GYRO_FS_SEL_2000_DPS_CLEAR = Bit7 | Bit6 | Bit5,
// 3:0 GYRO_ODR
// 0001: 32kHz
GYRO_ODR_32KHZ_SET = Bit0,
GYRO_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0010: 16kHz
GYRO_ODR_16KHZ_SET = Bit1,
GYRO_ODR_16KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0011: 8kHz
GYRO_ODR_8KHZ_SET = Bit1 | Bit0,
GYRO_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0110: 1kHz (default)
GYRO_ODR_1KHZ_SET = Bit2 | Bit1,
GYRO_ODR_1KHZ_CLEAR = Bit3 | Bit0,
// 0b0001: 32kHz (maximum)
GYRO_ODR_32KHZ_SET = Bit0,
GYRO_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0b0011: 8kHz
GYRO_ODR_8KHZ_SET = Bit1 | Bit0,
GYRO_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0b0110: 1kHz (default)
GYRO_ODR_1KHZ_SET = Bit2 | Bit1,
GYRO_ODR_1KHZ_CLEAR = Bit3 | Bit0,
};
// ACCEL_CONFIG0
enum ACCEL_CONFIG0_BIT : uint8_t {
// 7:5 ACCEL_FS_SEL
ACCEL_FS_SEL_16G = 0, // 000: ±16g (default)
ACCEL_FS_SEL_8G = Bit5,
ACCEL_FS_SEL_4G = Bit6,
ACCEL_FS_SEL_2G = Bit6 | Bit5,
// 0b000: ±16g (default)
ACCEL_FS_SEL_16G_CLEAR = Bit7 | Bit6 | Bit5,
// 3:0 ACCEL_ODR
// 0001: 32kHz
ACCEL_ODR_32KHZ_SET = Bit0,
ACCEL_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0010: 16kHz
ACCEL_ODR_16KHZ_SET = Bit1,
ACCEL_ODR_16KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0011: 8kHz
ACCEL_ODR_8KHZ_SET = Bit1 | Bit0,
ACCEL_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0110: 1kHz (default)
ACCEL_ODR_1KHZ_SET = Bit2 | Bit1,
ACCEL_ODR_1KHZ_CLEAR = Bit3 | Bit0,
// 0b0001: 32kHz (maximum)
ACCEL_ODR_32KHZ_SET = Bit0,
ACCEL_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0b0011: 8kHz
ACCEL_ODR_8KHZ_SET = Bit1 | Bit0,
ACCEL_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0b0110: 1kHz (default)
ACCEL_ODR_1KHZ_SET = Bit2 | Bit1,
ACCEL_ODR_1KHZ_CLEAR = Bit3 | Bit0,
};
// GYRO_CONFIG1
enum GYRO_CONFIG1_BIT : uint8_t {
GYRO_UI_FILT_ORD = Bit3 | Bit2, // 00: 1st Order
GYRO_UI_FILT_ORD_1ST_CLEAR = Bit3 | Bit2, // 00: 1st Order UI filter
};
// GYRO_ACCEL_CONFIG0
@@ -222,14 +207,15 @@ enum GYRO_ACCEL_CONFIG0_BIT : uint8_t {
// ACCEL_CONFIG1
enum ACCEL_CONFIG1_BIT : uint8_t {
ACCEL_UI_FILT_ORD = Bit4 | Bit3, // 00: 1st Order
ACCEL_UI_FILT_ORD_1ST_CLEAR = Bit4 | Bit3, // 00: 1st Order UI filter
};
// FIFO_CONFIG1
enum FIFO_CONFIG1_BIT : uint8_t {
FIFO_RESUME_PARTIAL_RD = Bit6,
FIFO_WM_GT_TH = Bit5,
FIFO_HIRES_EN = Bit4,
FIFO_TEMP_EN = Bit2,
FIFO_GYRO_EN = Bit1,
FIFO_ACCEL_EN = Bit0,
@@ -238,18 +224,21 @@ enum FIFO_CONFIG1_BIT : uint8_t {
// INT_CONFIG0
enum INT_CONFIG0_BIT : uint8_t {
// 3:2 FIFO_THS_INT_CLEAR
CLEAR_ON_FIFO_READ = Bit3,
FIFO_THS_INT_CLEAR = Bit3, // 10: Clear on FIFO data 1Byte Read
};
// INT_CONFIG1
enum INT_CONFIG1_BIT : uint8_t {
INT_TPULSE_DURATION = Bit6, // 1: Interrupt pulse duration is 8 µs. Required if ODR ≥ 4kHz, optional for ODR < 4kHz.
INT_TDEASSERT_DISABLE = Bit5, // 1: Disables de-assert duration. Required if ODR ≥ 4kHz, optional for ODR < 4kHz.
INT_ASYNC_RESET = Bit4, // User should change setting to 0 from default setting of 1, for proper INT1 and INT2 pin operation
};
// INT_SOURCE0
enum INT_SOURCE0_BIT : uint8_t {
UI_FSYNC_INT1_EN = Bit6,
PLL_RDY_INT1_EN = Bit5,
RESET_DONE_INT1_EN = Bit4,
UI_DRDY_INT1_EN = Bit3,
RESET_DONE_INT1_EN = Bit4, // 1: Reset done interrupt routed to INT1 (enabled by default)
FIFO_THS_INT1_EN = Bit2, // FIFO threshold interrupt routed to INT1
FIFO_FULL_INT1_EN = Bit1,
UI_AGC_RDY_INT1_EN = Bit0,
};
// REG_BANK_SEL
@@ -277,6 +266,7 @@ enum ACCEL_CONFIG_STATIC2_BIT : uint8_t {
ACCEL_AAF_DIS = Bit0,
};
namespace FIFO
{
static constexpr size_t SIZE = 2048;
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020, 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -72,13 +72,11 @@ extern "C" int icm42688p_main(int argc, char *argv[])
if (!strcmp(verb, "start")) {
return ThisDriver::module_start(cli, iterator);
}
if (!strcmp(verb, "stop")) {
} else if (!strcmp(verb, "stop")) {
return ThisDriver::module_stop(iterator);
}
if (!strcmp(verb, "status")) {
} else if (!strcmp(verb, "status")) {
return ThisDriver::module_status(iterator);
}
@@ -42,6 +42,4 @@ px4_add_module(
InvenSense_IIM42652_registers.hpp
DEPENDS
px4_work_queue
drivers_accelerometer
drivers_gyroscope
)
+328 -313
View File
@@ -33,6 +33,8 @@
#include "IIM42652.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ IIM42652::IIM42652(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
IIM42652::~IIM42652()
@@ -61,6 +65,7 @@ IIM42652::~IIM42652()
perf_free(_fifo_empty_perf);
perf_free(_fifo_overflow_perf);
perf_free(_fifo_reset_perf);
perf_free(_fifo_timestamp_error_perf);
perf_free(_drdy_missed_perf);
}
@@ -102,12 +107,14 @@ void IIM42652::print_status()
perf_print_counter(_fifo_empty_perf);
perf_print_counter(_fifo_overflow_perf);
perf_print_counter(_fifo_reset_perf);
perf_print_counter(_fifo_timestamp_error_perf);
perf_print_counter(_drdy_missed_perf);
}
int IIM42652::probe()
{
for (int i = 0; i < 3; i++) {
// 3 retries
for (int retry = 0; retry < 3; retry++) {
uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I);
if (whoami == WHOAMI) {
@@ -162,8 +169,8 @@ void IIM42652::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -171,21 +178,9 @@ void IIM42652::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
FIFOReset();
// if configure succeeded then reset the FIFO
_state = STATE::FIFO_RESET;
ScheduleDelayed(1_ms);
} else {
// CONFIGURE not complete
@@ -202,6 +197,24 @@ void IIM42652::RunImpl()
break;
case STATE::FIFO_RESET:
_state = STATE::FIFO_READ;
FIFOReset();
if (DataReadyInterruptConfigure()) {
_data_ready_interrupt_enabled = true;
// backup schedule as a watchdog timeout
ScheduleDelayed(100_ms);
} else {
_data_ready_interrupt_enabled = false;
ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us);
}
break;
case STATE::FIFO_READ: {
hrt_abstime timestamp_sample = now;
uint8_t samples = 0;
@@ -210,7 +223,7 @@ void IIM42652::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
samples = _fifo_gyro_samples;
@@ -222,6 +235,8 @@ void IIM42652::RunImpl()
ScheduleDelayed(_fifo_empty_interval_us * 2);
}
bool success = false;
if (samples == 0) {
// check current FIFO count
const uint16_t fifo_count = FIFOReadCount();
@@ -234,27 +249,42 @@ void IIM42652::RunImpl()
perf_count(_fifo_empty_perf);
} else {
// FIFO count (size in bytes)
samples = (fifo_count / sizeof(FIFO::DATA));
// FIFO count (size in bytes) should be a multiple of the FIFO::DATA structure
samples = fifo_count / sizeof(FIFO::DATA);
// tolerate minor jitter, leave sample to next iteration if behind by only 1
if (samples == _fifo_gyro_samples + 1) {
timestamp_sample -= static_cast<int>(FIFO_SAMPLE_DT);
samples--;
if (samples > _fifo_gyro_samples) {
// grab desired number of samples, but reschedule next cycle sooner
const int extra_samples = samples - _fifo_gyro_samples;
samples = _fifo_gyro_samples;
if (_fifo_gyro_samples > extra_samples) {
// reschedule to run when a total of _fifo_gyro_samples should be available in the FIFO
const uint32_t reschedule_delay_us = (_fifo_gyro_samples - extra_samples) * static_cast<int>(FIFO_SAMPLE_DT);
ScheduleOnInterval(_fifo_empty_interval_us, reschedule_delay_us);
} else {
// otherwise reschedule to run immediately
ScheduleOnInterval(_fifo_empty_interval_us);
}
} else if (samples < _fifo_gyro_samples) {
// reschedule next cycle to catch the desired number of samples
ScheduleOnInterval(_fifo_empty_interval_us, (_fifo_gyro_samples - samples) * static_cast<int>(FIFO_SAMPLE_DT));
}
if (samples > FIFO_MAX_SAMPLES) {
// not technically an overflow, but more samples than we expected or can publish
FIFOReset();
perf_count(_fifo_overflow_perf);
samples = 0;
if (samples == _fifo_gyro_samples) {
if (FIFORead(now, samples)) {
success = true;
if (_failure_count > 0) {
_failure_count--;
}
}
}
}
}
bool success = false;
if (samples >= 1) {
if (samples == _fifo_gyro_samples) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -269,13 +299,14 @@ void IIM42652::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
}
// check configuration registers periodically or immediately following any failure
if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) {
// check configuration registers periodically or immediately following any failure
if (RegisterCheck(_register_bank0_cfg[_checked_register_bank0])
&& RegisterCheck(_register_bank1_cfg[_checked_register_bank1])
&& RegisterCheck(_register_bank2_cfg[_checked_register_bank2])
@@ -303,10 +334,10 @@ void IIM42652::ConfigureSampleRate(int sample_rate)
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / RATE), (float)FIFO_MAX_SAMPLES));
// recompute FIFO empty interval (us) with actual gyro sample limit
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / GYRO_RATE);
_fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / RATE);
ConfigureFIFOWatermark(_fifo_gyro_samples);
}
@@ -356,6 +387,7 @@ bool IIM42652::Configure()
RegisterSetAndClearBits(reg_cfg.reg, reg_cfg.set_bits, reg_cfg.clear_bits);
}
// now check that all are configured
bool success = true;
@@ -377,11 +409,6 @@ bool IIM42652::Configure()
}
}
// 20-bits data format used
// the only FSR settings that are operational are ±2000dps for gyroscope and ±16g for accelerometer
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
_px4_gyro.set_range(math::radians(2000.f));
return success;
}
@@ -404,7 +431,7 @@ bool IIM42652::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool IIM42652::DataReadyInterruptDisable()
@@ -413,7 +440,7 @@ bool IIM42652::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
template <typename T>
@@ -468,114 +495,20 @@ void IIM42652::RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbits
uint16_t IIM42652::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ;
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
}
bool IIM42652::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 4, FIFO::SIZE);
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
if (buffer.INT_STATUS & INT_STATUS_BIT::FIFO_FULL_INT) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL);
if (fifo_count_bytes >= FIFO::SIZE) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
if (fifo_count_samples == 0) {
perf_count(_fifo_empty_perf);
return false;
}
// check FIFO header in every sample
uint8_t valid_samples = 0;
for (int i = 0; i < math::min(samples, fifo_count_samples); i++) {
bool valid = true;
// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8b_0110_10xx
const uint8_t FIFO_HEADER = buffer.f[i].FIFO_Header;
if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG) {
// FIFO sample empty if HEADER_MSG set
valid = false;
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL)) {
// accel bit not set
valid = false;
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO)) {
// gyro bit not set
valid = false;
} else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_20)) {
// Packet does not contain a new and valid extended 20-bit data
valid = false;
} else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL) {
// accel ODR changed
valid = false;
} else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO) {
// gyro ODR changed
valid = false;
}
if (valid) {
valid_samples++;
} else {
perf_count(_bad_transfer_perf);
break;
}
}
if (valid_samples > 0) {
if (ProcessTemperature(buffer.f, valid_samples)) {
ProcessGyro(timestamp_sample, buffer.f, valid_samples);
ProcessAccel(timestamp_sample, buffer.f, valid_samples);
return true;
}
}
return false;
}
void IIM42652::FIFOReset()
{
perf_count(_fifo_reset_perf);
// SIGNAL_PATH_RESET: FIFO flush
RegisterSetBits(Register::BANK_0::SIGNAL_PATH_RESET, SIGNAL_PATH_RESET_BIT::FIFO_FLUSH);
// reset while FIFO is disabled
_drdy_timestamp_sample.store(0);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
static constexpr int32_t reassemble_20bit(const uint32_t a, const uint32_t b, const uint32_t c)
@@ -595,206 +528,288 @@ static constexpr int32_t reassemble_20bit(const uint32_t a, const uint32_t b, co
return static_cast<int32_t>(x);
}
void IIM42652::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
bool IIM42652::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT;
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// 18-bits of accelerometer data
bool scale_20bit = false;
// cmd + INT_STATUS + FIFO_COUNTH + FIFO_COUNTL + samples (FIFO::DATA)
const size_t transfer_size = 4 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
// first pass
for (int i = 0; i < samples; i++) {
// 20 bit hires mode
// Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte)
// Accel data is 18 bit ()
int32_t accel_x = reassemble_20bit(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0,
fifo[i].Ext_Accel_X_Gyro_X & 0xF0 >> 4);
int32_t accel_y = reassemble_20bit(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0,
fifo[i].Ext_Accel_Y_Gyro_Y & 0xF0 >> 4);
int32_t accel_z = reassemble_20bit(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0,
fifo[i].Ext_Accel_Z_Gyro_Z & 0xF0 >> 4);
SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0);
// sample invalid if -524288
if (accel_x != -524288 && accel_y != -524288 && accel_z != -524288) {
// check if any values are going to exceed int16 limits
static constexpr int16_t max_accel = INT16_MAX;
static constexpr int16_t min_accel = INT16_MIN;
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
perf_count(_bad_transfer_perf);
return false;
}
if (accel_x >= max_accel || accel_x <= min_accel) {
scale_20bit = true;
if (buffer.INT_STATUS & INT_STATUS_BIT::FIFO_FULL_INT) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint16_t fifo_count_bytes = (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
if (fifo_count_bytes >= FIFO::SIZE) {
perf_count(_fifo_overflow_perf);
FIFOReset();
return false;
}
const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA);
if (fifo_count_samples == 0) {
perf_count(_fifo_empty_perf);
return false;
}
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = 0;
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 8192.f; // highres accel data 8192 LSB/g
sensor_imu_fifo.gyro_scale = math::radians(1.f / 131.f); // highres gyro data 131 LSB/dps
// check FIFO header in every sample
uint8_t valid_samples = 0;
float temperature_sum = 0;
float timestamp_interval_sum = 0;
int timestamp_interval_sum_count = 0;
bool accel_scale_16bit = false; // 18-bits of accelerometer data
bool gyro_scale_16bit = false; // 20-bits of gyroscope data
for (int i = 0; i < math::min(samples, fifo_count_samples); i++) {
const FIFO::DATA &fifo = buffer.f[i];
// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8b_0110_10xx
const uint8_t FIFO_HEADER = fifo.FIFO_Header;
const bool HEADER_MSG = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG; // FIFO is empty
const bool HEADER_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL;
const bool HEADER_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO;
const bool HEADER_20 = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_20; // valid sample of extended 20-bit data
// 3:2 HEADER_TIMESTAMP_FSYNC
const bool HEADER_ODR_ACCEL = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL; // ODR for accel is different
const bool HEADER_ODR_GYRO = FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO; // ODR for gyro is different
if (!HEADER_MSG && HEADER_ACCEL && HEADER_GYRO && HEADER_20 && !HEADER_ODR_ACCEL && !HEADER_ODR_GYRO) {
if (!accel_scale_16bit) {
// Accel: 20 bit hires mode, Accel data is 18 bit
// Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte)
int32_t accel_x = reassemble_20bit(fifo.ACCEL_DATA_X1, fifo.ACCEL_DATA_X0, fifo.Ext_Accel_X_Gyro_X & 0xF0 >> 4);
int32_t accel_y = reassemble_20bit(fifo.ACCEL_DATA_Y1, fifo.ACCEL_DATA_Y0, fifo.Ext_Accel_Y_Gyro_Y & 0xF0 >> 4);
int32_t accel_z = reassemble_20bit(fifo.ACCEL_DATA_Z1, fifo.ACCEL_DATA_Z0, fifo.Ext_Accel_Z_Gyro_Z & 0xF0 >> 4);
// sample invalid if -524288
if (accel_x == -524288 || accel_y == -524288 || accel_z == -524288) {
break;
}
// shift by 2 (2 least significant bits are always 0)
accel_x = accel_x / 4;
accel_y = accel_y / 4;
accel_z = accel_z / 4;
// check if any values are going to exceed int16 limits
if ((accel_x >= INT16_MAX || accel_x <= INT16_MIN)
|| (accel_y >= INT16_MAX || accel_y <= INT16_MIN)
|| (accel_z >= INT16_MAX || accel_z <= INT16_MIN)) {
accel_scale_16bit = true;
// 20 bit data scaled to 16 bit
sensor_imu_fifo.accel_scale = CONSTANTS_ONE_G / 2048.f;
// rescale any existing data
for (int j = 0; j < valid_samples + 1; j++) {
sensor_imu_fifo.accel_x[valid_samples] = combine(buffer.f[j].ACCEL_DATA_X1, buffer.f[j].ACCEL_DATA_X0);
sensor_imu_fifo.accel_y[valid_samples] = combine(buffer.f[j].ACCEL_DATA_Y1, buffer.f[j].ACCEL_DATA_Y0);
sensor_imu_fifo.accel_z[valid_samples] = combine(buffer.f[j].ACCEL_DATA_Z1, buffer.f[j].ACCEL_DATA_Z0);
}
} else {
sensor_imu_fifo.accel_x[valid_samples] = accel_x;
sensor_imu_fifo.accel_y[valid_samples] = accel_y;
sensor_imu_fifo.accel_z[valid_samples] = accel_z;
}
} else {
sensor_imu_fifo.accel_x[valid_samples] = combine(fifo.ACCEL_DATA_X1, fifo.ACCEL_DATA_X0);
sensor_imu_fifo.accel_y[valid_samples] = combine(fifo.ACCEL_DATA_Y1, fifo.ACCEL_DATA_Y0);
sensor_imu_fifo.accel_z[valid_samples] = combine(fifo.ACCEL_DATA_Z1, fifo.ACCEL_DATA_Z0);
}
if (accel_y >= max_accel || accel_y <= min_accel) {
scale_20bit = true;
if (!gyro_scale_16bit) {
// Gyro: 20 bit hires mode
// Gyro [19:12] + Gyro [11:4] + Gyro [3:0] (bottom 4 bits of 20 bit extension byte)
int32_t gyro_x = reassemble_20bit(fifo.GYRO_DATA_X1, fifo.GYRO_DATA_X0, fifo.Ext_Accel_X_Gyro_X & 0x0F);
int32_t gyro_y = reassemble_20bit(fifo.GYRO_DATA_Y1, fifo.GYRO_DATA_Y0, fifo.Ext_Accel_Y_Gyro_Y & 0x0F);
int32_t gyro_z = reassemble_20bit(fifo.GYRO_DATA_Z1, fifo.GYRO_DATA_Z0, fifo.Ext_Accel_Z_Gyro_Z & 0x0F);
// shift by 1 (least significant bit is always 0)
gyro_x = gyro_x / 2;
gyro_y = gyro_y / 2;
gyro_z = gyro_z / 2;
// check if any gyro values are going to exceed int16 limits
if ((gyro_x >= INT16_MAX || gyro_x <= INT16_MIN)
|| (gyro_y >= INT16_MAX || gyro_y <= INT16_MIN)
|| (gyro_z >= INT16_MAX || gyro_z <= INT16_MIN)) {
gyro_scale_16bit = true;
// 20 bit data scaled to 16 bit
sensor_imu_fifo.gyro_scale = math::radians(2000.f / 32768.f);
// rescale any existing data
for (int j = 0; j < valid_samples + 1; j++) {
sensor_imu_fifo.gyro_x[j] = combine(buffer.f[j].GYRO_DATA_X1, buffer.f[j].GYRO_DATA_X0);
sensor_imu_fifo.gyro_y[j] = combine(buffer.f[j].GYRO_DATA_Y1, buffer.f[j].GYRO_DATA_Y0);
sensor_imu_fifo.gyro_z[j] = combine(buffer.f[j].GYRO_DATA_Z1, buffer.f[j].GYRO_DATA_Z0);
}
} else {
sensor_imu_fifo.gyro_x[valid_samples] = gyro_x;
sensor_imu_fifo.gyro_y[valid_samples] = gyro_y;
sensor_imu_fifo.gyro_z[valid_samples] = gyro_z;
}
} else {
sensor_imu_fifo.gyro_x[i] = combine(fifo.GYRO_DATA_X1, fifo.GYRO_DATA_X0);
sensor_imu_fifo.gyro_y[i] = combine(fifo.GYRO_DATA_Y1, fifo.GYRO_DATA_Y0);
sensor_imu_fifo.gyro_z[i] = combine(fifo.GYRO_DATA_Z1, fifo.GYRO_DATA_Z0);
}
if (accel_z >= max_accel || accel_z <= min_accel) {
scale_20bit = true;
// temperature
const int16_t TEMP_DATA = combine(fifo.TEMP_DATA1, fifo.TEMP_DATA0);
// sample invalid if -32768
if (TEMP_DATA != -32768) {
temperature_sum += TEMP_DATA;
} else {
break;
}
// shift by 2 (2 least significant bits are always 0)
accel.x[accel.samples] = accel_x / 4;
accel.y[accel.samples] = accel_y / 4;
accel.z[accel.samples] = accel_z / 4;
accel.samples++;
}
}
if (!scale_20bit) {
// if highres enabled accel data is always 8192 LSB/g
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
// HEADER_TIMESTAMP_FSYNC - 0b10: Packet contains ODR Timestamp
if (FIFO_HEADER & Bit3) {
const uint16_t timestamp = (fifo.TimeStamp_h << 8) + fifo.TimeStamp_l;
} else {
// 20 bit data scaled to 16 bit (2^4)
for (int i = 0; i < samples; i++) {
// 20 bit hires mode
// Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte)
// Accel data is 18 bit ()
int16_t accel_x = combine(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0);
int16_t accel_y = combine(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0);
int16_t accel_z = combine(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0);
if (_timestamp_prev != 0) {
// If TMST_RES = 0 (corresponding to timestamp resolution of 1µs), timestamp interval reported in FIFO requires scaling by a factor of 32/30.
// Document Number: DS-000347 Revision: 1.5 Page 59 of 110
static constexpr float FIFO_DT_SCALE = 32.f / 30.f;
accel.x[i] = accel_x;
accel.y[i] = accel_y;
accel.z[i] = accel_z;
}
float dt = 0;
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
}
if (timestamp > _timestamp_prev) {
dt = static_cast<float>(timestamp - _timestamp_prev) * FIFO_DT_SCALE;
// correct frame for publication
for (int i = 0; i < accel.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[i] = accel.x[i];
accel.y[i] = (accel.y[i] == INT16_MIN) ? INT16_MAX : -accel.y[i];
accel.z[i] = (accel.z[i] == INT16_MIN) ? INT16_MAX : -accel.z[i];
}
} else if (timestamp < _timestamp_prev) {
// uint16_t rollover
uint32_t timestamp_new = UINT16_MAX + timestamp;
dt = static_cast<float>(timestamp_new - _timestamp_prev) * FIFO_DT_SCALE;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
timestamp_interval_sum += dt;
timestamp_interval_sum_count++;
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
}
// check dt is within +=2% of expected value
if ((dt < (FIFO_SAMPLE_DT * 0.98f)) || (dt > (FIFO_SAMPLE_DT * 1.02f))) {
perf_count(_fifo_timestamp_error_perf);
}
}
void IIM42652::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = 0;
gyro.dt = FIFO_SAMPLE_DT;
_timestamp_prev = timestamp;
}
// 20-bits of gyroscope data
bool scale_20bit = false;
// first pass
for (int i = 0; i < samples; i++) {
// 20 bit hires mode
// Gyro [19:12] + Gyro [11:4] + Gyro [3:0] (bottom 4 bits of 20 bit extension byte)
int32_t gyro_x = reassemble_20bit(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0, fifo[i].Ext_Accel_X_Gyro_X & 0x0F);
int32_t gyro_y = reassemble_20bit(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0, fifo[i].Ext_Accel_Y_Gyro_Y & 0x0F);
int32_t gyro_z = reassemble_20bit(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0, fifo[i].Ext_Accel_Z_Gyro_Z & 0x0F);
// check if any values are going to exceed int16 limits
static constexpr int16_t max_gyro = INT16_MAX;
static constexpr int16_t min_gyro = INT16_MIN;
if (gyro_x >= max_gyro || gyro_x <= min_gyro) {
scale_20bit = true;
}
if (gyro_y >= max_gyro || gyro_y <= min_gyro) {
scale_20bit = true;
}
if (gyro_z >= max_gyro || gyro_z <= min_gyro) {
scale_20bit = true;
}
gyro.x[gyro.samples] = gyro_x / 2;
gyro.y[gyro.samples] = gyro_y / 2;
gyro.z[gyro.samples] = gyro_z / 2;
gyro.samples++;
}
if (!scale_20bit) {
// if highres enabled gyro data is always 131 LSB/dps
_px4_gyro.set_scale(math::radians(1.f / 131.f));
} else {
// 20 bit data scaled to 16 bit (2^4)
for (int i = 0; i < samples; i++) {
gyro.x[i] = combine(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0);
gyro.y[i] = combine(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0);
gyro.z[i] = combine(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0);
}
_px4_gyro.set_scale(math::radians(2000.f / 32768.f));
}
// correct frame for publication
for (int i = 0; i < gyro.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro.x[i];
gyro.y[i] = (gyro.y[i] == INT16_MIN) ? INT16_MAX : -gyro.y[i];
gyro.z[i] = (gyro.z[i] == INT16_MIN) ? INT16_MAX : -gyro.z[i];
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (gyro.samples > 0) {
_px4_gyro.updateFIFO(gyro);
}
}
bool IIM42652::ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples)
{
int16_t temperature[FIFO_MAX_SAMPLES];
float temperature_sum{0};
int valid_samples = 0;
for (int i = 0; i < samples; i++) {
const int16_t t = combine(fifo[i].TEMP_DATA1, fifo[i].TEMP_DATA0);
// sample invalid if -32768
if (t != -32768) {
temperature_sum += t;
temperature[valid_samples] = t;
valid_samples++;
}
}
if (valid_samples > 0) {
const float temperature_avg = temperature_sum / valid_samples;
for (int i = 0; i < valid_samples; i++) {
// temperature changing wildly is an indication of a transfer error
if (fabsf(temperature[i] - temperature_avg) > 1000) {
perf_count(_bad_transfer_perf);
return false;
}
sensor_imu_fifo.samples = valid_samples;
for (int i = 0; i < sensor_imu_fifo.samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
// sensor_imu_fifo.accel_x[i]
sensor_imu_fifo.accel_y[i] = math::negate(sensor_imu_fifo.accel_y[i]);
sensor_imu_fifo.accel_z[i] = math::negate(sensor_imu_fifo.accel_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
// sensor_imu_fifo.gyro_x[i]
sensor_imu_fifo.gyro_y[i] = math::negate(sensor_imu_fifo.gyro_y[i]);
sensor_imu_fifo.gyro_z[i] = math::negate(sensor_imu_fifo.gyro_z[i]);
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
const float temperature_avg = temperature_sum / valid_samples;
// use average temperature reading
const float TEMP_degC = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
return true;
sensor_imu_fifo.temperature = TEMP_degC;
} else {
perf_count(_bad_transfer_perf);
return false;
}
if (timestamp_interval_sum > 0) {
const float dt_avg = (timestamp_interval_sum / timestamp_interval_sum_count);
// check dt is within +=1% of expected value
if ((dt_avg < (FIFO_SAMPLE_DT * 0.99f)) || (dt_avg > (FIFO_SAMPLE_DT * 1.01f))) {
PX4_ERR("DT error %.6f", (double)dt_avg);
perf_count(_fifo_timestamp_error_perf);
} else {
sensor_imu_fifo.dt = dt_avg;
}
}
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
}
void IIM42652::FIFOReset()
{
perf_count(_fifo_reset_perf);
// SIGNAL_PATH_RESET: FIFO flush
RegisterSetBits(Register::BANK_0::SIGNAL_PATH_RESET, SIGNAL_PATH_RESET_BIT::FIFO_FLUSH);
// reset while FIFO is disabled
_drdy_timestamp_sample.store(0);
_timestamp_prev = 0;
}
@@ -43,14 +43,15 @@
#include "InvenSense_IIM42652_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_IIM42652;
class IIM42652 : public device::SPI, public I2CSPIDriver<IIM42652>
@@ -71,22 +72,10 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f}; // 8000 Hz accel & gyro ODR configured
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float ACCEL_RATE{1e6f / FIFO_SAMPLE_DT};
static constexpr float RATE{1e6f / FIFO_SAMPLE_DT};
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::BANK_0::INT_STATUS) | DIR_READ};
uint8_t INT_STATUS{0};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (4 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
struct register_bank0_config_t {
Register::BANK_0 reg;
@@ -135,20 +124,18 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
void ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
bool ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples);
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")};
perf_counter_t _fifo_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO overflow")};
perf_counter_t _fifo_reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO reset")};
perf_counter_t _fifo_timestamp_error_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO timestamp error")};
perf_counter_t _drdy_missed_perf{nullptr};
hrt_abstime _reset_timestamp{0};
@@ -156,6 +143,9 @@ private:
hrt_abstime _temperature_update_timestamp{0};
int _failure_count{0};
uint16_t _timestamp_prev{0};
enum REG_BANK_SEL_BIT _last_register_bank {REG_BANK_SEL_BIT::USER_BANK_0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
@@ -165,29 +155,31 @@ private:
RESET,
WAIT_FOR_RESET,
CONFIGURE,
FIFO_RESET,
FIFO_READ,
} _state{STATE::RESET};
uint16_t _fifo_empty_interval_us{1250}; // default 1250 us / 800 Hz transfer interval
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / GYRO_RATE))};
int32_t _fifo_gyro_samples{static_cast<int32_t>(_fifo_empty_interval_us / (1000000 / RATE))};
uint8_t _checked_register_bank0{0};
static constexpr uint8_t size_register_bank0_cfg{13};
static constexpr uint8_t size_register_bank0_cfg{14};
register_bank0_config_t _register_bank0_cfg[size_register_bank0_cfg] {
// Register | Set bits, Clear bits
{ Register::BANK_0::INT_CONFIG, INT_CONFIG_BIT::INT1_MODE | INT_CONFIG_BIT::INT1_DRIVE_CIRCUIT, INT_CONFIG_BIT::INT1_POLARITY },
{ Register::BANK_0::FIFO_CONFIG, FIFO_CONFIG_BIT::FIFO_MODE_STOP_ON_FULL, 0 },
{ Register::BANK_0::PWR_MGMT0, PWR_MGMT0_BIT::GYRO_MODE_LOW_NOISE | PWR_MGMT0_BIT::ACCEL_MODE_LOW_NOISE, 0 },
{ Register::BANK_0::GYRO_CONFIG0, GYRO_CONFIG0_BIT::GYRO_FS_SEL_2000_DPS | GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_SET, GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_CLEAR },
{ Register::BANK_0::ACCEL_CONFIG0, ACCEL_CONFIG0_BIT::ACCEL_FS_SEL_16G | ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_SET, ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_CLEAR },
{ Register::BANK_0::GYRO_CONFIG1, 0, GYRO_CONFIG1_BIT::GYRO_UI_FILT_ORD },
{ Register::BANK_0::GYRO_CONFIG0, GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_SET, GYRO_CONFIG0_BIT::GYRO_FS_SEL_2000_DPS_CLEAR | GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_CLEAR },
{ Register::BANK_0::ACCEL_CONFIG0, ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_SET, ACCEL_CONFIG0_BIT::ACCEL_FS_SEL_16G_CLEAR | ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_CLEAR },
{ Register::BANK_0::GYRO_CONFIG1, 0, GYRO_CONFIG1_BIT::GYRO_UI_FILT_ORD_1ST_CLEAR },
{ Register::BANK_0::GYRO_ACCEL_CONFIG0, 0, GYRO_ACCEL_CONFIG0_BIT::ACCEL_UI_FILT_BW | GYRO_ACCEL_CONFIG0_BIT::GYRO_UI_FILT_BW },
{ Register::BANK_0::ACCEL_CONFIG1, 0, ACCEL_CONFIG1_BIT::ACCEL_UI_FILT_ORD },
{ Register::BANK_0::FIFO_CONFIG1, FIFO_CONFIG1_BIT::FIFO_WM_GT_TH | FIFO_CONFIG1_BIT::FIFO_HIRES_EN | FIFO_CONFIG1_BIT::FIFO_TEMP_EN | FIFO_CONFIG1_BIT::FIFO_GYRO_EN | FIFO_CONFIG1_BIT::FIFO_ACCEL_EN, 0 },
{ Register::BANK_0::ACCEL_CONFIG1, 0, ACCEL_CONFIG1_BIT::ACCEL_UI_FILT_ORD_1ST_CLEAR },
{ Register::BANK_0::FIFO_CONFIG1, FIFO_CONFIG1_BIT::FIFO_RESUME_PARTIAL_RD | FIFO_CONFIG1_BIT::FIFO_HIRES_EN | FIFO_CONFIG1_BIT::FIFO_TEMP_EN | FIFO_CONFIG1_BIT::FIFO_GYRO_EN | FIFO_CONFIG1_BIT::FIFO_ACCEL_EN, 0 },
{ Register::BANK_0::FIFO_CONFIG2, 0, 0 }, // FIFO_WM[7:0] set at runtime
{ Register::BANK_0::FIFO_CONFIG3, 0, 0 }, // FIFO_WM[11:8] set at runtime
{ Register::BANK_0::INT_CONFIG0, INT_CONFIG0_BIT::CLEAR_ON_FIFO_READ, 0 },
{ Register::BANK_0::INT_SOURCE0, INT_SOURCE0_BIT::FIFO_THS_INT1_EN, 0 },
{ Register::BANK_0::INT_CONFIG0, INT_CONFIG0_BIT::FIFO_THS_INT_CLEAR, 0 },
{ Register::BANK_0::INT_CONFIG1, INT_CONFIG1_BIT::INT_TPULSE_DURATION | INT_CONFIG1_BIT::INT_TDEASSERT_DISABLE, INT_CONFIG1_BIT::INT_ASYNC_RESET },
{ Register::BANK_0::INT_SOURCE0, INT_SOURCE0_BIT::FIFO_THS_INT1_EN, INT_SOURCE0_BIT::RESET_DONE_INT1_EN },
};
uint8_t _checked_register_bank1{0};
@@ -82,8 +82,7 @@ enum class BANK_0 : uint8_t {
FIFO_DATA = 0x30,
SIGNAL_PATH_RESET = 0x4B,
INTF_CONFIG0 = 0x4C,
INTF_CONFIG1 = 0x4D,
PWR_MGMT0 = 0x4E,
GYRO_CONFIG0 = 0x4F,
ACCEL_CONFIG0 = 0x50,
@@ -96,7 +95,7 @@ enum class BANK_0 : uint8_t {
FIFO_CONFIG3 = 0x61,
INT_CONFIG0 = 0x63,
INT_CONFIG1 = 0x64,
INT_SOURCE0 = 0x65,
SELF_TEST_CONFIG = 0x70,
@@ -121,7 +120,7 @@ enum class BANK_2 : uint8_t {
// DEVICE_CONFIG
enum DEVICE_CONFIG_BIT : uint8_t {
SOFT_RESET_CONFIG = Bit0, //
SOFT_RESET_CONFIG = Bit0,
};
// INT_CONFIG
@@ -140,15 +139,14 @@ enum FIFO_CONFIG_BIT : uint8_t {
// INT_STATUS
enum INT_STATUS_BIT : uint8_t {
RESET_DONE_INT = Bit4,
DATA_RDY_INT = Bit3,
FIFO_THS_INT = Bit2,
FIFO_FULL_INT = Bit1,
};
// SIGNAL_PATH_RESET
enum SIGNAL_PATH_RESET_BIT : uint8_t {
ABORT_AND_RESET = Bit3,
FIFO_FLUSH = Bit1,
FIFO_FLUSH = Bit1,
};
// PWR_MGMT0
@@ -160,55 +158,42 @@ enum PWR_MGMT0_BIT : uint8_t {
// GYRO_CONFIG0
enum GYRO_CONFIG0_BIT : uint8_t {
// 7:5 GYRO_FS_SEL
GYRO_FS_SEL_2000_DPS = 0, // 0b000 = ±2000dps (default)
GYRO_FS_SEL_1000_DPS = Bit5,
GYRO_FS_SEL_500_DPS = Bit6,
GYRO_FS_SEL_250_DPS = Bit6 | Bit5,
GYRO_FS_SEL_125_DPS = Bit7,
// 0b000: ±2000dps (default)
GYRO_FS_SEL_2000_DPS_CLEAR = Bit7 | Bit6 | Bit5,
// 3:0 GYRO_ODR
// 0001: 32kHz
GYRO_ODR_32KHZ_SET = Bit0,
GYRO_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0010: 16kHz
GYRO_ODR_16KHZ_SET = Bit1,
GYRO_ODR_16KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0011: 8kHz
GYRO_ODR_8KHZ_SET = Bit1 | Bit0,
GYRO_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0110: 1kHz (default)
GYRO_ODR_1KHZ_SET = Bit2 | Bit1,
GYRO_ODR_1KHZ_CLEAR = Bit3 | Bit0,
// 0b0001: 32kHz (maximum)
GYRO_ODR_32KHZ_SET = Bit0,
GYRO_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0b0011: 8kHz
GYRO_ODR_8KHZ_SET = Bit1 | Bit0,
GYRO_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0b0110: 1kHz (default)
GYRO_ODR_1KHZ_SET = Bit2 | Bit1,
GYRO_ODR_1KHZ_CLEAR = Bit3 | Bit0,
};
// ACCEL_CONFIG0
enum ACCEL_CONFIG0_BIT : uint8_t {
// 7:5 ACCEL_FS_SEL
ACCEL_FS_SEL_16G = 0, // 000: ±16g (default)
ACCEL_FS_SEL_8G = Bit5,
ACCEL_FS_SEL_4G = Bit6,
ACCEL_FS_SEL_2G = Bit6 | Bit5,
// 0b000: ±16g (default)
ACCEL_FS_SEL_16G_CLEAR = Bit7 | Bit6 | Bit5,
// 3:0 ACCEL_ODR
// 0001: 32kHz
ACCEL_ODR_32KHZ_SET = Bit0,
ACCEL_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0010: 16kHz
ACCEL_ODR_16KHZ_SET = Bit1,
ACCEL_ODR_16KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0011: 8kHz
ACCEL_ODR_8KHZ_SET = Bit1 | Bit0,
ACCEL_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0110: 1kHz (default)
ACCEL_ODR_1KHZ_SET = Bit2 | Bit1,
ACCEL_ODR_1KHZ_CLEAR = Bit3 | Bit0,
// 0b0001: 32kHz (maximum)
ACCEL_ODR_32KHZ_SET = Bit0,
ACCEL_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0,
// 0b0011: 8kHz
ACCEL_ODR_8KHZ_SET = Bit1 | Bit0,
ACCEL_ODR_8KHZ_CLEAR = Bit3 | Bit2,
// 0b0110: 1kHz (default)
ACCEL_ODR_1KHZ_SET = Bit2 | Bit1,
ACCEL_ODR_1KHZ_CLEAR = Bit3 | Bit0,
};
// GYRO_CONFIG1
enum GYRO_CONFIG1_BIT : uint8_t {
GYRO_UI_FILT_ORD = Bit3 | Bit2, // 00: 1st Order
GYRO_UI_FILT_ORD_1ST_CLEAR = Bit3 | Bit2, // 00: 1st Order UI filter
};
// GYRO_ACCEL_CONFIG0
@@ -222,14 +207,15 @@ enum GYRO_ACCEL_CONFIG0_BIT : uint8_t {
// ACCEL_CONFIG1
enum ACCEL_CONFIG1_BIT : uint8_t {
ACCEL_UI_FILT_ORD = Bit4 | Bit3, // 00: 1st Order
ACCEL_UI_FILT_ORD_1ST_CLEAR = Bit4 | Bit3, // 00: 1st Order UI filter
};
// FIFO_CONFIG1
enum FIFO_CONFIG1_BIT : uint8_t {
FIFO_RESUME_PARTIAL_RD = Bit6,
FIFO_WM_GT_TH = Bit5,
FIFO_HIRES_EN = Bit4,
FIFO_TEMP_EN = Bit2,
FIFO_GYRO_EN = Bit1,
FIFO_ACCEL_EN = Bit0,
@@ -238,18 +224,21 @@ enum FIFO_CONFIG1_BIT : uint8_t {
// INT_CONFIG0
enum INT_CONFIG0_BIT : uint8_t {
// 3:2 FIFO_THS_INT_CLEAR
CLEAR_ON_FIFO_READ = Bit3,
FIFO_THS_INT_CLEAR = Bit3, // 10: Clear on FIFO data 1Byte Read
};
// INT_CONFIG1
enum INT_CONFIG1_BIT : uint8_t {
INT_TPULSE_DURATION = Bit6, // 1: Interrupt pulse duration is 8 µs. Required if ODR ≥ 4kHz, optional for ODR < 4kHz.
INT_TDEASSERT_DISABLE = Bit5, // 1: Disables de-assert duration. Required if ODR ≥ 4kHz, optional for ODR < 4kHz.
INT_ASYNC_RESET = Bit4, // User should change setting to 0 from default setting of 1, for proper INT1 and INT2 pin operation
};
// INT_SOURCE0
enum INT_SOURCE0_BIT : uint8_t {
UI_FSYNC_INT1_EN = Bit6,
PLL_RDY_INT1_EN = Bit5,
RESET_DONE_INT1_EN = Bit4,
UI_DRDY_INT1_EN = Bit3,
RESET_DONE_INT1_EN = Bit4, // 1: Reset done interrupt routed to INT1 (enabled by default)
FIFO_THS_INT1_EN = Bit2, // FIFO threshold interrupt routed to INT1
FIFO_FULL_INT1_EN = Bit1,
UI_AGC_RDY_INT1_EN = Bit0,
};
// REG_BANK_SEL
@@ -277,6 +266,7 @@ enum ACCEL_CONFIG_STATIC2_BIT : uint8_t {
ACCEL_AAF_DIS = Bit0,
};
namespace FIFO
{
static constexpr size_t SIZE = 2048;
@@ -72,13 +72,11 @@ extern "C" int iim42652_main(int argc, char *argv[])
if (!strcmp(verb, "start")) {
return ThisDriver::module_start(cli, iterator);
}
if (!strcmp(verb, "stop")) {
} else if (!strcmp(verb, "stop")) {
return ThisDriver::module_stop(iterator);
}
if (!strcmp(verb, "status")) {
} else if (!strcmp(verb, "status")) {
return ThisDriver::module_status(iterator);
}
@@ -41,7 +41,5 @@ px4_add_module(
mpu6000_main.cpp
InvenSense_MPU6000_registers.hpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
@@ -62,6 +62,8 @@ static constexpr uint8_t WHOAMI = 0x68;
static constexpr float TEMPERATURE_SENSITIVITY = 340.f; // LSB/C
static constexpr float TEMPERATURE_OFFSET = 36.53f; // C
static constexpr float TEMPERATURE_SENSOR_MIN = -40.f; // °C
static constexpr float TEMPERATURE_SENSOR_MAX = 85.f; // °C
enum class Register : uint8_t {
CONFIG = 0x1A,
+99 -175
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "MPU6000.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ MPU6000::MPU6000(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
MPU6000::~MPU6000()
@@ -107,14 +111,19 @@ void MPU6000::print_status()
int MPU6000::probe()
{
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
if (whoami != WHOAMI) {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
return PX4_ERROR;
if (whoami == WHOAMI) {
return PX4_OK;
} else {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
}
}
return PX4_OK;
return PX4_ERROR;
}
void MPU6000::RunImpl()
@@ -156,8 +165,8 @@ void MPU6000::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -165,6 +174,8 @@ void MPU6000::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
_temperature = ReadTemperature();
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
@@ -203,7 +214,7 @@ void MPU6000::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
} else {
@@ -278,7 +289,7 @@ void MPU6000::RunImpl()
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature = ReadTemperature();
_temperature_update_timestamp = now;
}
}
@@ -288,65 +299,10 @@ void MPU6000::RunImpl()
}
}
void MPU6000::ConfigureAccel()
{
const uint8_t AFS_SEL = RegisterRead(Register::ACCEL_CONFIG) & (Bit4 | Bit3); // [4:3] AFS_SEL[1:0]
switch (AFS_SEL) {
case AFS_SEL_2G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 16384.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
break;
case AFS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case AFS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case AFS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
}
}
void MPU6000::ConfigureGyro()
{
const uint8_t GYRO_FS_SEL = RegisterRead(Register::GYRO_CONFIG) & (Bit4 | Bit3); // [4:3] FS_SEL[1:0]
float range_dps = 0.f;
switch (GYRO_FS_SEL) {
case FS_SEL_250_DPS:
range_dps = 250.f;
break;
case FS_SEL_500_DPS:
range_dps = 500.f;
break;
case FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void MPU6000::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT * 4; // limit to 2 kHz (500 us interval)
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
@@ -371,9 +327,6 @@ bool MPU6000::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -400,7 +353,7 @@ bool MPU6000::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool MPU6000::DataReadyInterruptDisable()
@@ -409,7 +362,7 @@ bool MPU6000::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
bool MPU6000::RegisterCheck(const register_config_t &reg_cfg)
@@ -462,23 +415,33 @@ void MPU6000::RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t cle
uint16_t MPU6000::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ;
set_frequency(SPI_SPEED_SENSOR);
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool MPU6000::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 1, FIFO::SIZE);
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + samples (FIFO::DATA)
const size_t transfer_size = 1 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
set_frequency(SPI_SPEED_SENSOR);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
@@ -486,9 +449,43 @@ bool MPU6000::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
const uint8_t valid_samples = samples;
ProcessGyro(timestamp_sample, buffer.f, samples);
return ProcessAccel(timestamp_sample, buffer.f, samples);
if (valid_samples > 0) {
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = valid_samples;
sensor_imu_fifo.accel_scale = ACCEL_SCALE;
sensor_imu_fifo.gyro_scale = GYRO_SCALE;
for (int i = 0; i < valid_samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
sensor_imu_fifo.accel_x[i] = combine(buffer.f[i].ACCEL_XOUT_H, buffer.f[i].ACCEL_XOUT_L);
sensor_imu_fifo.accel_y[i] = math::negate(combine(buffer.f[i].ACCEL_YOUT_H, buffer.f[i].ACCEL_YOUT_L));
sensor_imu_fifo.accel_z[i] = math::negate(combine(buffer.f[i].ACCEL_ZOUT_H, buffer.f[i].ACCEL_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
sensor_imu_fifo.gyro_x[i] = combine(buffer.f[i].GYRO_XOUT_H, buffer.f[i].GYRO_XOUT_L);
sensor_imu_fifo.gyro_y[i] = math::negate(combine(buffer.f[i].GYRO_YOUT_H, buffer.f[i].GYRO_YOUT_L));
sensor_imu_fifo.gyro_z[i] = math::negate(combine(buffer.f[i].GYRO_ZOUT_H, buffer.f[i].GYRO_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
sensor_imu_fifo.temperature = (hrt_elapsed_time(&_temperature_update_timestamp) < 5_s) ? _temperature : NAN;
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf)
+ perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
}
void MPU6000::FIFOReset()
@@ -514,105 +511,32 @@ void MPU6000::FIFOReset()
}
}
static bool fifo_accel_equal(const FIFO::DATA &f0, const FIFO::DATA &f1)
float MPU6000::ReadTemperature()
{
return (memcmp(&f0.ACCEL_XOUT_H, &f1.ACCEL_XOUT_H, 6) == 0);
}
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ};
uint8_t TEMP_OUT_H{0};
uint8_t TEMP_OUT_L{0};
} buffer{};
bool MPU6000::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
bool bad_data = false;
// FIFO contains 8 duplicated accel samples per gyro sample
for (int i = 0; i < samples; i++) {
_fifo_accel_samples_count++;
// only process new FIFO samples (1 every 8 samples expected)
const bool new_sample = !fifo_accel_equal(fifo[i], _fifo_sample_last_new_accel);
// process every 8th sample
if (_fifo_accel_samples_count == SAMPLES_PER_TRANSFER) {
int16_t accel_x = combine(fifo[i].ACCEL_XOUT_H, fifo[i].ACCEL_XOUT_L);
int16_t accel_y = combine(fifo[i].ACCEL_YOUT_H, fifo[i].ACCEL_YOUT_L);
int16_t accel_z = combine(fifo[i].ACCEL_ZOUT_H, fifo[i].ACCEL_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
} else if (new_sample && (_fifo_accel_samples_count > 1)) {
// a new unique sample after fewer than 8 samples is an error
bad_data = true;
perf_count(_bad_transfer_perf);
}
// reset previous unique sample and counter
if (new_sample || (_fifo_accel_samples_count == SAMPLES_PER_TRANSFER)) {
_fifo_accel_samples_count = 0;
_fifo_sample_last_new_accel = fifo[i];
}
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
return !bad_data;
}
void MPU6000::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_XOUT_H, fifo[i].GYRO_XOUT_L);
const int16_t gyro_y = combine(fifo[i].GYRO_YOUT_H, fifo[i].GYRO_YOUT_L);
const int16_t gyro_z = combine(fifo[i].GYRO_ZOUT_H, fifo[i].GYRO_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void MPU6000::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ;
set_frequency(SPI_SPEED_SENSOR);
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
return NAN;;
}
const int16_t TEMP_OUT = combine(temperature_buf[1], temperature_buf[2]);
const int16_t TEMP_OUT = combine(buffer.TEMP_OUT_H, buffer.TEMP_OUT_L);
const float TEMP_degC = (TEMP_OUT / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
if (PX4_ISFINITE(TEMP_degC)
&& (TEMP_degC >= TEMPERATURE_SENSOR_MIN)
&& (TEMP_degC <= TEMPERATURE_SENSOR_MAX)) {
return TEMP_degC;
}
return NAN;
}
+13 -23
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_MPU6000_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_MPU6000;
class MPU6000 : public device::SPI, public I2CSPIDriver<MPU6000>
@@ -71,20 +72,13 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f};
static constexpr int32_t SAMPLES_PER_TRANSFER{8}; // ensure at least 1 new accel sample per transfer
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; // 8000 Hz gyro
static constexpr float ACCEL_RATE{GYRO_RATE / SAMPLES_PER_TRANSFER}; // 1000 Hz accel
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0])), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (1 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr float ACCEL_SCALE{CONSTANTS_ONE_G / 2048.f}; // AFS_SEL_16G:
static constexpr float GYRO_SCALE{math::radians(2000.f / 32768.f)}; // FS_SEL_2000_DPS:
struct register_config_t {
Register reg;
@@ -97,8 +91,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
static int DataReadyInterruptCallback(int irq, void *context, void *arg);
@@ -116,14 +108,15 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
float ReadTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};
@@ -137,9 +130,6 @@ private:
hrt_abstime _temperature_update_timestamp{0};
int _failure_count{0};
FIFO::DATA _fifo_sample_last_new_accel{};
uint32_t _fifo_accel_samples_count{0};
px4::atomic<hrt_abstime> _drdy_timestamp_sample{0};
int32_t _drdy_count{0};
bool _data_ready_interrupt_enabled{false};
@@ -40,7 +40,5 @@ px4_add_module(
MPU6500.hpp
mpu6500_main.cpp
DEPENDS
drivers_accelerometer
drivers_gyroscope
px4_work_queue
)
@@ -42,6 +42,8 @@
#include <cstdint>
namespace InvenSense_MPU6500
{
// TODO: move to a central header
static constexpr uint8_t Bit0 = (1 << 0);
static constexpr uint8_t Bit1 = (1 << 1);
@@ -52,8 +54,6 @@ static constexpr uint8_t Bit5 = (1 << 5);
static constexpr uint8_t Bit6 = (1 << 6);
static constexpr uint8_t Bit7 = (1 << 7);
namespace InvenSense_MPU6500
{
static constexpr uint32_t SPI_SPEED = 1 * 1000 * 1000;
static constexpr uint32_t SPI_SPEED_SENSOR = 10 * 1000 * 1000; // 20MHz for reading sensor and interrupt registers
static constexpr uint8_t DIR_READ = 0x80;
@@ -62,6 +62,8 @@ static constexpr uint8_t WHOAMI = 0x70;
static constexpr float TEMPERATURE_SENSITIVITY = 333.87f; // LSB/C
static constexpr float TEMPERATURE_OFFSET = 21.f; // C
static constexpr float TEMPERATURE_SENSOR_MIN = -40.f; // °C
static constexpr float TEMPERATURE_SENSOR_MAX = 85.f; // °C
enum class Register : uint8_t {
+103 -178
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,6 +33,8 @@
#include "MPU6500.hpp"
#include <lib/parameters/param.h>
using namespace time_literals;
static constexpr int16_t combine(uint8_t msb, uint8_t lsb)
@@ -44,14 +46,16 @@ MPU6500::MPU6500(const I2CSPIDriverConfig &config) :
SPI(config),
I2CSPIDriver(config),
_drdy_gpio(config.drdy_gpio),
_px4_accel(get_device_id(), config.rotation),
_px4_gyro(get_device_id(), config.rotation)
_rotation(config.rotation)
{
if (config.drdy_gpio != 0) {
if (_drdy_gpio != 0) {
_drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed");
}
ConfigureSampleRate(_px4_gyro.get_max_rate_hz());
int32_t imu_gyro_rate_max = 400;
param_get(param_find("IMU_GYRO_RATEMAX"), &imu_gyro_rate_max);
ConfigureSampleRate(imu_gyro_rate_max);
}
MPU6500::~MPU6500()
@@ -131,14 +135,19 @@ bool MPU6500::StoreCheckedRegisterValue(Register reg)
int MPU6500::probe()
{
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
// 3 retries
for (int retry = 0; retry < 3; retry++) {
const uint8_t whoami = RegisterRead(Register::WHO_AM_I);
if (whoami != WHOAMI) {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
return PX4_ERROR;
if (whoami == WHOAMI) {
return PX4_OK;
} else {
DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami);
}
}
return PX4_OK;
return PX4_ERROR;
}
void MPU6500::RunImpl()
@@ -188,8 +197,8 @@ void MPU6500::RunImpl()
ScheduleDelayed(100_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
PX4_DEBUG("Reset not complete, check again in 100 ms");
ScheduleDelayed(100_ms);
}
}
@@ -197,6 +206,8 @@ void MPU6500::RunImpl()
case STATE::CONFIGURE:
if (Configure()) {
_temperature = ReadTemperature();
// if configure succeeded then start reading from FIFO
_state = STATE::FIFO_READ;
@@ -235,7 +246,7 @@ void MPU6500::RunImpl()
// scheduled from interrupt if _drdy_timestamp_sample was set as expected
const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0);
if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) {
if (now < drdy_timestamp_sample + _fifo_empty_interval_us) {
timestamp_sample = drdy_timestamp_sample;
} else {
@@ -272,7 +283,7 @@ void MPU6500::RunImpl()
FIFOReset();
perf_count(_fifo_overflow_perf);
} else if (samples >= SAMPLES_PER_TRANSFER) {
} else if (samples >= 1) {
if (FIFORead(timestamp_sample, samples)) {
success = true;
@@ -288,6 +299,7 @@ void MPU6500::RunImpl()
// full reset if things are failing consistently
if (_failure_count > 10) {
PX4_DEBUG("Full reset because things are failing consistently");
Reset();
return;
}
@@ -302,13 +314,14 @@ void MPU6500::RunImpl()
} else {
// register check failed, force reset
perf_count(_bad_register_perf);
PX4_DEBUG("Force reset because register 0x%02hhX check failed ", (uint8_t)_register_cfg[_checked_register].reg);
Reset();
}
} else {
// periodically update temperature (~1 Hz)
if (hrt_elapsed_time(&_temperature_update_timestamp) >= 1_s) {
UpdateTemperature();
_temperature = ReadTemperature();
_temperature_update_timestamp = now;
}
}
@@ -318,65 +331,10 @@ void MPU6500::RunImpl()
}
}
void MPU6500::ConfigureAccel()
{
const uint8_t ACCEL_FS_SEL = RegisterRead(Register::ACCEL_CONFIG) & (Bit4 | Bit3); // [4:3] ACCEL_FS_SEL[1:0]
switch (ACCEL_FS_SEL) {
case ACCEL_FS_SEL_2G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 16384.f);
_px4_accel.set_range(2.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_4G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f);
_px4_accel.set_range(4.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_8G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 4096.f);
_px4_accel.set_range(8.f * CONSTANTS_ONE_G);
break;
case ACCEL_FS_SEL_16G:
_px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f);
_px4_accel.set_range(16.f * CONSTANTS_ONE_G);
break;
}
}
void MPU6500::ConfigureGyro()
{
const uint8_t GYRO_FS_SEL = RegisterRead(Register::GYRO_CONFIG) & (Bit4 | Bit3); // [4:3] GYRO_FS_SEL[1:0]
float range_dps = 0.f;
switch (GYRO_FS_SEL) {
case GYRO_FS_SEL_250_DPS:
range_dps = 250.f;
break;
case GYRO_FS_SEL_500_DPS:
range_dps = 500.f;
break;
case GYRO_FS_SEL_1000_DPS:
range_dps = 1000.f;
break;
case GYRO_FS_SEL_2000_DPS:
range_dps = 2000.f;
break;
}
_px4_gyro.set_scale(math::radians(range_dps / 32768.f));
_px4_gyro.set_range(math::radians(range_dps));
}
void MPU6500::ConfigureSampleRate(int sample_rate)
{
// round down to nearest FIFO sample dt * SAMPLES_PER_TRANSFER
const float min_interval = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
// round down to nearest FIFO sample dt
const float min_interval = FIFO_SAMPLE_DT;
_fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval);
_fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES));
@@ -401,9 +359,6 @@ bool MPU6500::Configure()
}
}
ConfigureAccel();
ConfigureGyro();
return success;
}
@@ -430,7 +385,7 @@ bool MPU6500::DataReadyInterruptConfigure()
}
// Setup data ready on falling edge
return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, true, false, &DataReadyInterruptCallback, this) == 0);
}
bool MPU6500::DataReadyInterruptDisable()
@@ -439,7 +394,7 @@ bool MPU6500::DataReadyInterruptDisable()
return false;
}
return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0;
return (px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0);
}
bool MPU6500::RegisterCheck(const register_config_t &reg_cfg)
@@ -490,23 +445,33 @@ void MPU6500::RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t cle
uint16_t MPU6500::FIFOReadCount()
{
// read FIFO count
uint8_t fifo_count_buf[3] {};
fifo_count_buf[0] = static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ;
set_frequency(SPI_SPEED_SENSOR);
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_COUNTH) | DIR_READ};
uint8_t FIFO_COUNTH{0};
uint8_t FIFO_COUNTL{0};
} buffer{};
if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) {
// read FIFO count
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return 0;
}
return combine(fifo_count_buf[1], fifo_count_buf[2]);
return (buffer.FIFO_COUNTH << 8) + buffer.FIFO_COUNTL;
}
bool MPU6500::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
{
FIFOTransferBuffer buffer{};
const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 1, FIFO::SIZE);
// FIFO transfer buffer
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
} buffer{};
// cmd + samples (FIFO::DATA)
const size_t transfer_size = 1 + math::min(samples * sizeof(FIFO::DATA), FIFO::SIZE);
set_frequency(SPI_SPEED_SENSOR);
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) {
@@ -514,9 +479,43 @@ bool MPU6500::FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples)
return false;
}
const uint8_t valid_samples = samples;
ProcessGyro(timestamp_sample, buffer.f, samples);
return ProcessAccel(timestamp_sample, buffer.f, samples);
if (valid_samples > 0) {
sensor_imu_fifo_s sensor_imu_fifo{};
sensor_imu_fifo.timestamp_sample = timestamp_sample;
sensor_imu_fifo.device_id = get_device_id();
sensor_imu_fifo.dt = FIFO_SAMPLE_DT;
sensor_imu_fifo.samples = valid_samples;
sensor_imu_fifo.accel_scale = ACCEL_SCALE;
sensor_imu_fifo.gyro_scale = GYRO_SCALE;
for (int i = 0; i < valid_samples; i++) {
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
sensor_imu_fifo.accel_x[i] = combine(buffer.f[i].ACCEL_XOUT_H, buffer.f[i].ACCEL_XOUT_L);
sensor_imu_fifo.accel_y[i] = math::negate(combine(buffer.f[i].ACCEL_YOUT_H, buffer.f[i].ACCEL_YOUT_L));
sensor_imu_fifo.accel_z[i] = math::negate(combine(buffer.f[i].ACCEL_ZOUT_H, buffer.f[i].ACCEL_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.accel_x[i], sensor_imu_fifo.accel_y[i], sensor_imu_fifo.accel_z[i]);
sensor_imu_fifo.gyro_x[i] = combine(buffer.f[i].GYRO_XOUT_H, buffer.f[i].GYRO_XOUT_L);
sensor_imu_fifo.gyro_y[i] = math::negate(combine(buffer.f[i].GYRO_YOUT_H, buffer.f[i].GYRO_YOUT_L));
sensor_imu_fifo.gyro_z[i] = math::negate(combine(buffer.f[i].GYRO_ZOUT_H, buffer.f[i].GYRO_ZOUT_L));
rotate_3i(_rotation, sensor_imu_fifo.gyro_x[i], sensor_imu_fifo.gyro_y[i], sensor_imu_fifo.gyro_z[i]);
}
sensor_imu_fifo.temperature = (hrt_elapsed_time(&_temperature_update_timestamp) < 5_s) ? _temperature : NAN;
sensor_imu_fifo.error_count = perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf)
+ perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf);
sensor_imu_fifo.timestamp = hrt_absolute_time();
_sensor_imu_fifo_pub.publish(sensor_imu_fifo);
return true;
}
return false;
}
void MPU6500::FIFOReset()
@@ -542,106 +541,32 @@ void MPU6500::FIFOReset()
}
}
static bool fifo_accel_equal(const FIFO::DATA &f0, const FIFO::DATA &f1)
float MPU6500::ReadTemperature()
{
return (memcmp(&f0.ACCEL_XOUT_H, &f1.ACCEL_XOUT_H, 6) == 0);
}
// transfer buffer
struct TransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ};
uint8_t TEMP_OUT_H{0};
uint8_t TEMP_OUT_L{0};
} buffer{};
bool MPU6500::ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_accel_fifo_s accel{};
accel.timestamp_sample = timestamp_sample;
accel.samples = 0;
accel.dt = FIFO_SAMPLE_DT * SAMPLES_PER_TRANSFER;
bool bad_data = false;
// accel data is doubled in FIFO, but might be shifted
int accel_first_sample = 1;
if (samples >= 4) {
if (fifo_accel_equal(fifo[0], fifo[1]) && fifo_accel_equal(fifo[2], fifo[3])) {
// [A0, A1, A2, A3]
// A0==A1, A2==A3
accel_first_sample = 1;
} else if (fifo_accel_equal(fifo[1], fifo[2])) {
// [A0, A1, A2, A3]
// A0, A1==A2, A3
accel_first_sample = 0;
} else {
// no matching accel samples is an error
bad_data = true;
perf_count(_bad_transfer_perf);
}
}
for (int i = accel_first_sample; i < samples; i = i + SAMPLES_PER_TRANSFER) {
int16_t accel_x = combine(fifo[i].ACCEL_XOUT_H, fifo[i].ACCEL_XOUT_L);
int16_t accel_y = combine(fifo[i].ACCEL_YOUT_H, fifo[i].ACCEL_YOUT_L);
int16_t accel_z = combine(fifo[i].ACCEL_ZOUT_H, fifo[i].ACCEL_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
accel.x[accel.samples] = accel_x;
accel.y[accel.samples] = (accel_y == INT16_MIN) ? INT16_MAX : -accel_y;
accel.z[accel.samples] = (accel_z == INT16_MIN) ? INT16_MAX : -accel_z;
accel.samples++;
}
_px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
if (accel.samples > 0) {
_px4_accel.updateFIFO(accel);
}
return !bad_data;
}
void MPU6500::ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples)
{
sensor_gyro_fifo_s gyro{};
gyro.timestamp_sample = timestamp_sample;
gyro.samples = samples;
gyro.dt = FIFO_SAMPLE_DT;
for (int i = 0; i < samples; i++) {
const int16_t gyro_x = combine(fifo[i].GYRO_XOUT_H, fifo[i].GYRO_XOUT_L);
const int16_t gyro_y = combine(fifo[i].GYRO_YOUT_H, fifo[i].GYRO_YOUT_L);
const int16_t gyro_z = combine(fifo[i].GYRO_ZOUT_H, fifo[i].GYRO_ZOUT_L);
// sensor's frame is +x forward, +y left, +z up
// flip y & z to publish right handed with z down (x forward, y right, z down)
gyro.x[i] = gyro_x;
gyro.y[i] = (gyro_y == INT16_MIN) ? INT16_MAX : -gyro_y;
gyro.z[i] = (gyro_z == INT16_MIN) ? INT16_MAX : -gyro_z;
}
_px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) +
perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf));
_px4_gyro.updateFIFO(gyro);
}
void MPU6500::UpdateTemperature()
{
// read current temperature
uint8_t temperature_buf[3] {};
temperature_buf[0] = static_cast<uint8_t>(Register::TEMP_OUT_H) | DIR_READ;
set_frequency(SPI_SPEED_SENSOR);
if (transfer(temperature_buf, temperature_buf, sizeof(temperature_buf)) != PX4_OK) {
if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, sizeof(buffer)) != PX4_OK) {
perf_count(_bad_transfer_perf);
return;
return NAN;
}
const int16_t TEMP_OUT = combine(temperature_buf[1], temperature_buf[2]);
const int16_t TEMP_OUT = combine(buffer.TEMP_OUT_H, buffer.TEMP_OUT_L);
const float TEMP_degC = (TEMP_OUT / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET;
if (PX4_ISFINITE(TEMP_degC)) {
_px4_accel.set_temperature(TEMP_degC);
_px4_gyro.set_temperature(TEMP_degC);
if (PX4_ISFINITE(TEMP_degC)
&& (TEMP_degC >= TEMPERATURE_SENSOR_MIN)
&& (TEMP_degC <= TEMPERATURE_SENSOR_MAX)) {
return TEMP_degC;
}
return NAN;
}
+13 -20
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2020-2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2020-2022 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,14 +43,15 @@
#include "InvenSense_MPU6500_registers.hpp"
#include <drivers/drv_hrt.h>
#include <lib/drivers/accelerometer/PX4Accelerometer.hpp>
#include <lib/drivers/device/spi.h>
#include <lib/drivers/gyroscope/PX4Gyroscope.hpp>
#include <lib/geo/geo.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/i2c_spi_buses.h>
#include <uORB/PublicationMulti.hpp>
#include <uORB/topics/sensor_imu_fifo.h>
using namespace InvenSense_MPU6500;
class MPU6500 : public device::SPI, public I2CSPIDriver<MPU6500>
@@ -71,20 +72,13 @@ private:
// Sensor Configuration
static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f};
static constexpr int32_t SAMPLES_PER_TRANSFER{2}; // ensure at least 1 new accel sample per transfer
static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; // 8000 Hz gyro
static constexpr float ACCEL_RATE{GYRO_RATE / SAMPLES_PER_TRANSFER}; // 4000 Hz accel
// maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))};
static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), (size_t)sensor_imu_fifo_s::FIFO_SIZE)};
// Transfer data
struct FIFOTransferBuffer {
uint8_t cmd{static_cast<uint8_t>(Register::FIFO_R_W) | DIR_READ};
FIFO::DATA f[FIFO_MAX_SAMPLES] {};
};
// ensure no struct padding
static_assert(sizeof(FIFOTransferBuffer) == (1 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA)));
static constexpr float ACCEL_SCALE{CONSTANTS_ONE_G / 2048.f}; // ACCEL_FS_SEL_16G
static constexpr float GYRO_SCALE{math::radians(2000.f / 32768.f)}; // GYRO_FS_SEL_2000_DPS
struct register_config_t {
Register reg;
@@ -97,8 +91,6 @@ private:
bool Reset();
bool Configure();
void ConfigureAccel();
void ConfigureGyro();
void ConfigureSampleRate(int sample_rate);
static int DataReadyInterruptCallback(int irq, void *context, void *arg);
@@ -117,14 +109,15 @@ private:
bool FIFORead(const hrt_abstime &timestamp_sample, uint8_t samples);
void FIFOReset();
bool ProcessAccel(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void ProcessGyro(const hrt_abstime &timestamp_sample, const FIFO::DATA fifo[], const uint8_t samples);
void UpdateTemperature();
float ReadTemperature();
const spi_drdy_gpio_t _drdy_gpio;
PX4Accelerometer _px4_accel;
PX4Gyroscope _px4_gyro;
uORB::PublicationMulti<sensor_imu_fifo_s> _sensor_imu_fifo_pub{ORB_ID(sensor_imu_fifo)};
const enum Rotation _rotation;
float _temperature{NAN};
perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")};
perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")};

Some files were not shown because too many files have changed in this diff Show More