|
|
|
@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* |
|
|
|
|
* Copyright (c) 2018 PX4 Development Team. All rights reserved. |
|
|
|
|
* Copyright (c) 2018-2019 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 |
|
|
|
@ -32,7 +32,6 @@
@@ -32,7 +32,6 @@
|
|
|
|
|
****************************************************************************/ |
|
|
|
|
|
|
|
|
|
#include "ADIS16497.hpp" |
|
|
|
|
#include "ADIS16497_gyro.hpp" |
|
|
|
|
|
|
|
|
|
#include <px4_config.h> |
|
|
|
|
#include <ecl/geo/geo.h> |
|
|
|
@ -41,7 +40,7 @@
@@ -41,7 +40,7 @@
|
|
|
|
|
#define DIR_WRITE 0x80 |
|
|
|
|
|
|
|
|
|
// ADIS16497 registers
|
|
|
|
|
static constexpr uint8_t PAGE_ID = 0x0; // Page identifier
|
|
|
|
|
static constexpr uint8_t PAGE_ID = 0x0; // Page identifier
|
|
|
|
|
|
|
|
|
|
// Page 0x00
|
|
|
|
|
static constexpr uint8_t SYS_E_FLAG = 0x08; // Output, system error flags
|
|
|
|
@ -53,145 +52,70 @@ static constexpr uint8_t PROD_ID = 0x7E; // Output, product identification
@@ -53,145 +52,70 @@ static constexpr uint8_t PROD_ID = 0x7E; // Output, product identification
|
|
|
|
|
static constexpr uint8_t GLOB_CMD = 0x02; // Control, global commands
|
|
|
|
|
static constexpr uint8_t FNCTIO_CTRL = 0x06; // Control, I/O pins, functional definitions
|
|
|
|
|
static constexpr uint8_t GPIO_CTRL = 0x08; // Control, I/O pins, general-purpose
|
|
|
|
|
static constexpr uint8_t CONFIG = 0x0A; // Control, clock and miscellaneous corrections
|
|
|
|
|
static constexpr uint8_t DEC_RATE = 0x0C; // Control, output sample rate decimation
|
|
|
|
|
static constexpr uint8_t CONFIG = 0x0A; // Control, clock and miscellaneous corrections
|
|
|
|
|
static constexpr uint8_t DEC_RATE = 0x0C; // Control, output sample rate decimation
|
|
|
|
|
static constexpr uint8_t NULL_CNFG = 0x0E; // Control, automatic bias correction configuration
|
|
|
|
|
static constexpr uint8_t SYNC_SCALE = 0x10; // Control, automatic bias correction configuration
|
|
|
|
|
static constexpr uint8_t RANG_MDL = 0x12; // Measurement range (model-specific) Identifier TODO use this
|
|
|
|
|
static constexpr uint8_t FILTR_BNK_0 = 0x16; // Filter selection
|
|
|
|
|
static constexpr uint8_t FILTR_BNK_1 = 0x18; // Filter selection
|
|
|
|
|
|
|
|
|
|
static constexpr uint16_t PROD_ID_ADIS16497 = 0x4071; // ADIS16497 Identification, device number
|
|
|
|
|
|
|
|
|
|
// Stall time between SPI transfers
|
|
|
|
|
static constexpr uint8_t T_STALL = 2; |
|
|
|
|
|
|
|
|
|
static constexpr uint32_t ADIS16497_DEFAULT_RATE = 1000; |
|
|
|
|
|
|
|
|
|
using namespace time_literals; |
|
|
|
|
|
|
|
|
|
ADIS16497::ADIS16497(int bus, const char *path_accel, const char *path_gyro, uint32_t device, enum Rotation rotation) : |
|
|
|
|
SPI("ADIS16497", path_accel, bus, device, SPIDEV_MODE3, 5000000), |
|
|
|
|
ADIS16497::ADIS16497(int bus, uint32_t device, enum Rotation rotation) : |
|
|
|
|
SPI("ADIS16497", nullptr, bus, device, SPIDEV_MODE3, 5000000), |
|
|
|
|
ScheduledWorkItem(px4::device_bus_to_wq(get_device_id())), |
|
|
|
|
_gyro(new ADIS16497_gyro(this, path_gyro)), |
|
|
|
|
_sample_perf(perf_alloc(PC_ELAPSED, "ADIS16497_read")), |
|
|
|
|
_sample_interval_perf(perf_alloc(PC_INTERVAL, "ADIS16497_read_int")), |
|
|
|
|
_bad_transfers(perf_alloc(PC_COUNT, "ADIS16497_bad_transfers")), |
|
|
|
|
_rotation(rotation) |
|
|
|
|
_px4_accel(get_device_id(), ORB_PRIO_MAX, rotation), |
|
|
|
|
_px4_gyro(get_device_id(), ORB_PRIO_MAX, rotation), |
|
|
|
|
_sample_interval_perf(perf_alloc(PC_INTERVAL, "adis16497: read interval")), |
|
|
|
|
_sample_perf(perf_alloc(PC_ELAPSED, "adis16497: read")), |
|
|
|
|
_bad_transfers(perf_alloc(PC_COUNT, "adis16497: bad transfers")) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
#ifdef GPIO_SPI1_RESET_ADIS16497 |
|
|
|
|
// Configure hardware reset line
|
|
|
|
|
px4_arch_configgpio(GPIO_SPI1_RESET_ADIS16497); |
|
|
|
|
#endif /* GPIO_SPI1_RESET_ADIS16497 */ |
|
|
|
|
|
|
|
|
|
_device_id.devid_s.devtype = DRV_ACC_DEVTYPE_ADIS16497; |
|
|
|
|
#endif // GPIO_SPI1_RESET_ADIS16497
|
|
|
|
|
|
|
|
|
|
_gyro->_device_id.devid = _device_id.devid; |
|
|
|
|
_gyro->_device_id.devid_s.devtype = DRV_GYR_DEVTYPE_ADIS16497; |
|
|
|
|
_px4_accel.set_device_type(DRV_ACC_DEVTYPE_ADIS16497); |
|
|
|
|
_px4_accel.set_sample_rate(ADIS16497_DEFAULT_RATE); |
|
|
|
|
_px4_accel.set_scale(1.25f * CONSTANTS_ONE_G / 1000.0f); // accel 1.25 mg/LSB
|
|
|
|
|
|
|
|
|
|
// Software low pass filter for controllers
|
|
|
|
|
const param_t accel_cut_ph = param_find("IMU_ACCEL_CUTOFF"); |
|
|
|
|
float accel_cut = ADIS16497_ACCEL_DEFAULT_DRIVER_FILTER_FREQ; |
|
|
|
|
|
|
|
|
|
if (accel_cut_ph != PARAM_INVALID && param_get(accel_cut_ph, &accel_cut) == PX4_OK) { |
|
|
|
|
_accel_filter.set_cutoff_frequency(_sample_rate, accel_cut); |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
PX4_ERR("IMU_ACCEL_CUTOFF param invalid"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const param_t gyro_cut_ph = param_find("IMU_GYRO_CUTOFF"); |
|
|
|
|
float gyro_cut = ADIS16497_GYRO_DEFAULT_DRIVER_FILTER_FREQ; |
|
|
|
|
|
|
|
|
|
if (gyro_cut_ph != PARAM_INVALID && param_get(gyro_cut_ph, &gyro_cut) == PX4_OK) { |
|
|
|
|
_gyro_filter.set_cutoff_frequency(_sample_rate, gyro_cut); |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
PX4_ERR("IMU_GYRO_CUTOFF param invalid"); |
|
|
|
|
} |
|
|
|
|
_px4_gyro.set_device_type(DRV_GYR_DEVTYPE_ADIS16497); |
|
|
|
|
_px4_gyro.set_sample_rate(ADIS16497_DEFAULT_RATE); |
|
|
|
|
_px4_gyro.set_scale(math::radians(0.025f)); // gyro 0.025 °/sec/LSB
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ADIS16497::~ADIS16497() |
|
|
|
|
{ |
|
|
|
|
/* make sure we are truly inactive */ |
|
|
|
|
// make sure we are truly inactive
|
|
|
|
|
stop(); |
|
|
|
|
|
|
|
|
|
/* delete the gyro subdriver */ |
|
|
|
|
delete _gyro; |
|
|
|
|
|
|
|
|
|
if (_accel_class_instance != -1) { |
|
|
|
|
unregister_class_devname(ACCEL_BASE_DEVICE_PATH, _accel_class_instance); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* delete the perf counter */ |
|
|
|
|
perf_free(_sample_perf); |
|
|
|
|
// delete the perf counters
|
|
|
|
|
perf_free(_sample_interval_perf); |
|
|
|
|
perf_free(_sample_perf); |
|
|
|
|
perf_free(_bad_transfers); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
ADIS16497::init() |
|
|
|
|
{ |
|
|
|
|
if (hrt_absolute_time() < 250_ms) { |
|
|
|
|
// Power-on startup time (if needed)
|
|
|
|
|
up_mdelay(250); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* do SPI init (and probe) first */ |
|
|
|
|
// do SPI init (and probe) first
|
|
|
|
|
if (SPI::init() != OK) { |
|
|
|
|
/* if probe/setup failed, bail now */ |
|
|
|
|
DEVICE_DEBUG("SPI setup failed"); |
|
|
|
|
// if probe/setup failed, bail now
|
|
|
|
|
PX4_DEBUG("SPI setup failed"); |
|
|
|
|
return PX4_ERROR; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Initialize offsets and scales */ |
|
|
|
|
_gyro_scale.x_offset = 0.0f; |
|
|
|
|
_gyro_scale.x_scale = 1.0f; |
|
|
|
|
_gyro_scale.y_offset = 0.0f; |
|
|
|
|
_gyro_scale.y_scale = 1.0f; |
|
|
|
|
_gyro_scale.z_offset = 0.0f; |
|
|
|
|
_gyro_scale.z_scale = 1.0f; |
|
|
|
|
|
|
|
|
|
_accel_scale.x_offset = 0.0f; |
|
|
|
|
_accel_scale.x_scale = 1.0f; |
|
|
|
|
_accel_scale.y_offset = 0.0f; |
|
|
|
|
_accel_scale.y_scale = 1.0f; |
|
|
|
|
_accel_scale.z_offset = 0.0f; |
|
|
|
|
_accel_scale.z_scale = 1.0f; |
|
|
|
|
|
|
|
|
|
/* do CDev init for the gyro device node, keep it optional */ |
|
|
|
|
int ret = _gyro->init(); |
|
|
|
|
|
|
|
|
|
/* if probe/setup failed, bail now */ |
|
|
|
|
if (ret != OK) { |
|
|
|
|
DEVICE_DEBUG("gyro init failed"); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_accel_class_instance = register_class_devname(ACCEL_BASE_DEVICE_PATH); |
|
|
|
|
|
|
|
|
|
/* fetch an initial set of measurements for advertisement */ |
|
|
|
|
measure(); |
|
|
|
|
|
|
|
|
|
/* advertise sensor topic, measure manually to initialize valid report */ |
|
|
|
|
sensor_accel_s arp = {}; |
|
|
|
|
|
|
|
|
|
/* measurement will have generated a report, publish */ |
|
|
|
|
_accel_topic = orb_advertise_multi(ORB_ID(sensor_accel), &arp, &_accel_orb_class_instance, ORB_PRIO_MAX); |
|
|
|
|
|
|
|
|
|
if (_accel_topic == nullptr) { |
|
|
|
|
PX4_ERR("ADVERT FAIL"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sensor_gyro_s grp = {}; |
|
|
|
|
_gyro->_gyro_topic = orb_advertise_multi(ORB_ID(sensor_gyro), &grp, &_gyro->_gyro_orb_class_instance, ORB_PRIO_MAX); |
|
|
|
|
|
|
|
|
|
if (_gyro->_gyro_topic == nullptr) { |
|
|
|
|
PX4_ERR("ADVERT FAIL"); |
|
|
|
|
} |
|
|
|
|
start(); |
|
|
|
|
|
|
|
|
|
if (ret == PX4_OK) { |
|
|
|
|
start(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
return PX4_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int ADIS16497::reset() |
|
|
|
@ -201,22 +125,19 @@ int ADIS16497::reset()
@@ -201,22 +125,19 @@ int ADIS16497::reset()
|
|
|
|
|
px4_arch_gpiowrite(GPIO_SPI1_RESET_ADIS16497, 0); |
|
|
|
|
|
|
|
|
|
// The RST line must be in a low state for at least 10 μs to ensure a proper reset initiation and recovery.
|
|
|
|
|
up_udelay(10); |
|
|
|
|
usleep(10_us); |
|
|
|
|
|
|
|
|
|
px4_arch_gpiowrite(GPIO_SPI1_RESET_ADIS16497, 1); |
|
|
|
|
|
|
|
|
|
// Reset recovery time
|
|
|
|
|
up_mdelay(250); |
|
|
|
|
#else |
|
|
|
|
// Software reset (global command bit 7)
|
|
|
|
|
uint8_t value[2] = {}; |
|
|
|
|
uint8_t value[2] {}; |
|
|
|
|
value[0] = (1 << 7); |
|
|
|
|
write_reg16(PAGE_ID, 0x03); |
|
|
|
|
write_reg16(GLOB_CMD, (uint16_t)value[0]); |
|
|
|
|
#endif // GPIO_SPI1_RESET_ADIS16497
|
|
|
|
|
|
|
|
|
|
// Reset recovery time
|
|
|
|
|
up_mdelay(210); |
|
|
|
|
#endif /* GPIO_SPI1_RESET_ADIS16497 */ |
|
|
|
|
usleep(210_ms); |
|
|
|
|
|
|
|
|
|
// Switch to configuration page
|
|
|
|
|
write_reg16(PAGE_ID, 0x03); |
|
|
|
@ -226,7 +147,7 @@ int ADIS16497::reset()
@@ -226,7 +147,7 @@ int ADIS16497::reset()
|
|
|
|
|
|
|
|
|
|
write_reg16(FNCTIO_CTRL, FNCTIO_CTRL_DEFAULT); |
|
|
|
|
|
|
|
|
|
up_udelay(340); |
|
|
|
|
usleep(340_us); |
|
|
|
|
|
|
|
|
|
const uint16_t fnctio_ctrl = read_reg16(FNCTIO_CTRL); |
|
|
|
|
|
|
|
|
@ -240,7 +161,7 @@ int ADIS16497::reset()
@@ -240,7 +161,7 @@ int ADIS16497::reset()
|
|
|
|
|
|
|
|
|
|
write_reg16(CONFIG, CONFIG_DEFAULT); |
|
|
|
|
|
|
|
|
|
up_udelay(45); |
|
|
|
|
usleep(45_us); |
|
|
|
|
|
|
|
|
|
const uint16_t config = read_reg16(CONFIG); |
|
|
|
|
|
|
|
|
@ -254,7 +175,7 @@ int ADIS16497::reset()
@@ -254,7 +175,7 @@ int ADIS16497::reset()
|
|
|
|
|
|
|
|
|
|
write_reg16(DEC_RATE, DEC_RATE_DEFAULT); |
|
|
|
|
|
|
|
|
|
up_udelay(340); |
|
|
|
|
usleep(340_us); |
|
|
|
|
|
|
|
|
|
const uint16_t dec_rate = read_reg16(DEC_RATE); |
|
|
|
|
|
|
|
|
@ -268,7 +189,7 @@ int ADIS16497::reset()
@@ -268,7 +189,7 @@ int ADIS16497::reset()
|
|
|
|
|
|
|
|
|
|
write_reg16(NULL_CNFG, NULL_CNFG_DEFAULT); |
|
|
|
|
|
|
|
|
|
up_udelay(71); |
|
|
|
|
usleep(71_us); |
|
|
|
|
|
|
|
|
|
const uint16_t null_cnfg = read_reg16(NULL_CNFG); |
|
|
|
|
|
|
|
|
@ -284,7 +205,7 @@ int ADIS16497::reset()
@@ -284,7 +205,7 @@ int ADIS16497::reset()
|
|
|
|
|
write_reg16(FILTR_BNK_0, FILTR_BNK_0_SETUP); |
|
|
|
|
write_reg16(FILTR_BNK_1, FILTR_BNK_1_SETUP); |
|
|
|
|
|
|
|
|
|
up_udelay(65); |
|
|
|
|
usleep(65_us); |
|
|
|
|
|
|
|
|
|
const uint16_t filtr_bnk_0 = read_reg16(FILTR_BNK_0); |
|
|
|
|
|
|
|
|
@ -308,7 +229,7 @@ int ADIS16497::reset()
@@ -308,7 +229,7 @@ int ADIS16497::reset()
|
|
|
|
|
write_reg16(GLOB_CMD, (uint16_t)value[0]); |
|
|
|
|
|
|
|
|
|
// save Recovery Time
|
|
|
|
|
up_mdelay(1125); |
|
|
|
|
usleep(1125_ms); |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
return OK; |
|
|
|
@ -317,35 +238,30 @@ int ADIS16497::reset()
@@ -317,35 +238,30 @@ int ADIS16497::reset()
|
|
|
|
|
int |
|
|
|
|
ADIS16497::probe() |
|
|
|
|
{ |
|
|
|
|
DEVICE_DEBUG("probe"); |
|
|
|
|
reset(); |
|
|
|
|
|
|
|
|
|
// read product id (5 attempts)
|
|
|
|
|
for (int i = 0; i < 5; i++) { |
|
|
|
|
|
|
|
|
|
if (reset() != PX4_OK) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Switch to output page
|
|
|
|
|
write_reg16(PAGE_ID, 0x00); |
|
|
|
|
|
|
|
|
|
// Check if the device is an ADIS16497
|
|
|
|
|
static constexpr uint16_t PROD_ID_ADIS16497 = 0x4071; |
|
|
|
|
|
|
|
|
|
uint16_t product_id = read_reg16(PROD_ID); |
|
|
|
|
|
|
|
|
|
if (product_id == PROD_ID_ADIS16497) { |
|
|
|
|
PX4_DEBUG("PRODUCT: %X", product_id); |
|
|
|
|
|
|
|
|
|
if (self_test_sensor()) { |
|
|
|
|
// TODO check model here and set measurement ranges
|
|
|
|
|
if (self_test()) { |
|
|
|
|
return PX4_OK; |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
PX4_ERR("probe attempt %d: self test failed, resetting", i); |
|
|
|
|
reset(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
PX4_ERR("probe attempt %d: read product id failed, resetting", i); |
|
|
|
|
reset(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -353,17 +269,17 @@ ADIS16497::probe()
@@ -353,17 +269,17 @@ ADIS16497::probe()
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool |
|
|
|
|
ADIS16497::self_test_sensor() |
|
|
|
|
ADIS16497::self_test() |
|
|
|
|
{ |
|
|
|
|
// Switch to configuration page
|
|
|
|
|
write_reg16(PAGE_ID, 0x03); |
|
|
|
|
|
|
|
|
|
// Self test (globa l command bit 1)
|
|
|
|
|
uint8_t value[2] = {}; |
|
|
|
|
uint8_t value[2] {}; |
|
|
|
|
value[0] = (1 << 1); |
|
|
|
|
write_reg16(GLOB_CMD, (uint16_t)value[0]); |
|
|
|
|
|
|
|
|
|
up_mdelay(20); // Self test time
|
|
|
|
|
usleep(20_ms); // Self test time
|
|
|
|
|
|
|
|
|
|
// Switch to output page
|
|
|
|
|
write_reg16(PAGE_ID, 0x0); |
|
|
|
@ -387,50 +303,10 @@ ADIS16497::self_test_sensor()
@@ -387,50 +303,10 @@ ADIS16497::self_test_sensor()
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
ADIS16497::ioctl(struct file *filp, int cmd, unsigned long arg) |
|
|
|
|
{ |
|
|
|
|
switch (cmd) { |
|
|
|
|
case SENSORIOCRESET: |
|
|
|
|
return reset(); |
|
|
|
|
|
|
|
|
|
case ACCELIOCSSCALE: { |
|
|
|
|
/* copy scale, but only if off by a few percent */ |
|
|
|
|
struct accel_calibration_s *s = (struct accel_calibration_s *) arg; |
|
|
|
|
memcpy(&_accel_scale, s, sizeof(_accel_scale)); |
|
|
|
|
return OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
/* give it to the superclass */ |
|
|
|
|
return SPI::ioctl(filp, cmd, arg); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
ADIS16497::gyro_ioctl(struct file *filp, int cmd, unsigned long arg) |
|
|
|
|
{ |
|
|
|
|
switch (cmd) { |
|
|
|
|
|
|
|
|
|
/* these are shared with the accel side */ |
|
|
|
|
case SENSORIOCRESET: |
|
|
|
|
return ioctl(filp, cmd, arg); |
|
|
|
|
|
|
|
|
|
case GYROIOCSSCALE: |
|
|
|
|
/* copy scale in */ |
|
|
|
|
memcpy(&_gyro_scale, (struct gyro_calibration_s *) arg, sizeof(_gyro_scale)); |
|
|
|
|
return OK; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
/* give it to the superclass */ |
|
|
|
|
return SPI::ioctl(filp, cmd, arg); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint16_t |
|
|
|
|
ADIS16497::read_reg16(uint8_t reg) |
|
|
|
|
{ |
|
|
|
|
uint16_t cmd[1]; |
|
|
|
|
uint16_t cmd[1] {}; |
|
|
|
|
|
|
|
|
|
cmd[0] = ((reg | DIR_READ) << 8) & 0xff00; |
|
|
|
|
transferhword(cmd, nullptr, 1); |
|
|
|
@ -444,7 +320,7 @@ ADIS16497::read_reg16(uint8_t reg)
@@ -444,7 +320,7 @@ ADIS16497::read_reg16(uint8_t reg)
|
|
|
|
|
void |
|
|
|
|
ADIS16497::write_reg(uint8_t reg, uint8_t val) |
|
|
|
|
{ |
|
|
|
|
uint8_t cmd[2]; |
|
|
|
|
uint8_t cmd[2] {}; |
|
|
|
|
cmd[0] = reg | 0x8; |
|
|
|
|
cmd[1] = val; |
|
|
|
|
transfer(cmd, cmd, sizeof(cmd)); |
|
|
|
@ -453,7 +329,7 @@ ADIS16497::write_reg(uint8_t reg, uint8_t val)
@@ -453,7 +329,7 @@ ADIS16497::write_reg(uint8_t reg, uint8_t val)
|
|
|
|
|
void |
|
|
|
|
ADIS16497::write_reg16(uint8_t reg, uint16_t value) |
|
|
|
|
{ |
|
|
|
|
uint16_t cmd[2]; |
|
|
|
|
uint16_t cmd[2] {}; |
|
|
|
|
|
|
|
|
|
cmd[0] = ((reg | DIR_WRITE) << 8) | (0x00ff & value); |
|
|
|
|
cmd[1] = (((reg + 0x1) | DIR_WRITE) << 8) | ((0xff00 & value) >> 8); |
|
|
|
@ -474,8 +350,8 @@ ADIS16497::start()
@@ -474,8 +350,8 @@ ADIS16497::start()
|
|
|
|
|
// Make sure we are stopped first
|
|
|
|
|
stop(); |
|
|
|
|
|
|
|
|
|
// Start polling at the specified rate
|
|
|
|
|
ScheduleOnInterval(_call_interval, 1000); |
|
|
|
|
// start polling at the specified rate
|
|
|
|
|
ScheduleOnInterval((1_s / ADIS16497_DEFAULT_RATE), 10000); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -495,7 +371,7 @@ ADIS16497::data_ready_interrupt(int irq, void *context, void *arg)
@@ -495,7 +371,7 @@ ADIS16497::data_ready_interrupt(int irq, void *context, void *arg)
|
|
|
|
|
{ |
|
|
|
|
ADIS16497 *dev = reinterpret_cast<ADIS16497 *>(arg); |
|
|
|
|
|
|
|
|
|
/* make another measurement */ |
|
|
|
|
// make another measurement
|
|
|
|
|
dev->ScheduleNow(); |
|
|
|
|
|
|
|
|
|
return PX4_OK; |
|
|
|
@ -504,24 +380,24 @@ ADIS16497::data_ready_interrupt(int irq, void *context, void *arg)
@@ -504,24 +380,24 @@ ADIS16497::data_ready_interrupt(int irq, void *context, void *arg)
|
|
|
|
|
void |
|
|
|
|
ADIS16497::Run() |
|
|
|
|
{ |
|
|
|
|
/* make another measurement */ |
|
|
|
|
// make another measurement
|
|
|
|
|
measure(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
ADIS16497::measure() |
|
|
|
|
{ |
|
|
|
|
perf_begin(_sample_perf); |
|
|
|
|
perf_count(_sample_interval_perf); |
|
|
|
|
perf_begin(_sample_perf); |
|
|
|
|
|
|
|
|
|
// Fetch the full set of measurements from the ADIS16497 in one pass (burst read).
|
|
|
|
|
ADISReport adis_report {}; |
|
|
|
|
ADISReport adis_report{}; |
|
|
|
|
adis_report.cmd = ((BURST_CMD | DIR_READ) << 8) & 0xff00; |
|
|
|
|
|
|
|
|
|
// ADIS16497 burst report should be 320 bits
|
|
|
|
|
static_assert(sizeof(adis_report) == (320 / 8), "ADIS16497 report not 320 bits"); |
|
|
|
|
|
|
|
|
|
const hrt_abstime t = hrt_absolute_time(); |
|
|
|
|
const hrt_abstime timestamp_sample = hrt_absolute_time(); |
|
|
|
|
|
|
|
|
|
if (OK != transferhword((uint16_t *)&adis_report, ((uint16_t *)&adis_report), sizeof(adis_report) / sizeof(uint16_t))) { |
|
|
|
|
perf_count(_bad_transfers); |
|
|
|
@ -534,19 +410,21 @@ ADIS16497::measure()
@@ -534,19 +410,21 @@ ADIS16497::measure()
|
|
|
|
|
static constexpr uint16_t BURST_ID_DEFAULT = 0xA5A5; |
|
|
|
|
|
|
|
|
|
if (adis_report.BURST_ID != BURST_ID_DEFAULT) { |
|
|
|
|
PX4_ERR("BURST_ID: %#X", adis_report.BURST_ID); |
|
|
|
|
PX4_DEBUG("BURST_ID: %#X", adis_report.BURST_ID); |
|
|
|
|
|
|
|
|
|
perf_count(_bad_transfers); |
|
|
|
|
perf_end(_sample_perf); |
|
|
|
|
|
|
|
|
|
return -EIO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check all Status/Error Flag Indicators
|
|
|
|
|
if (adis_report.SYS_E_FLAG != 0) { |
|
|
|
|
PX4_ERR("SYS_E_FLAG: %#X", adis_report.SYS_E_FLAG); |
|
|
|
|
PX4_DEBUG("SYS_E_FLAG: %#X", adis_report.SYS_E_FLAG); |
|
|
|
|
|
|
|
|
|
perf_count(_bad_transfers); |
|
|
|
|
perf_end(_sample_perf); |
|
|
|
|
|
|
|
|
|
return -EIO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -554,123 +432,49 @@ ADIS16497::measure()
@@ -554,123 +432,49 @@ ADIS16497::measure()
|
|
|
|
|
uint32_t checksum_calc = crc32((uint16_t *)&adis_report.SYS_E_FLAG, 15); |
|
|
|
|
|
|
|
|
|
if (checksum != checksum_calc) { |
|
|
|
|
PX4_ERR("CHECKSUM: %#X vs. calculated: %#X", checksum, checksum_calc); |
|
|
|
|
PX4_DEBUG("CHECKSUM: %#X vs. calculated: %#X", checksum, checksum_calc); |
|
|
|
|
|
|
|
|
|
perf_count(_bad_transfers); |
|
|
|
|
perf_end(_sample_perf); |
|
|
|
|
|
|
|
|
|
return -EIO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO check data counter here to see if we're missing samples/getting repeated samples
|
|
|
|
|
|
|
|
|
|
publish_accel(t, adis_report); |
|
|
|
|
publish_gyro(t, adis_report); |
|
|
|
|
|
|
|
|
|
perf_end(_sample_perf); |
|
|
|
|
return OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
ADIS16497::publish_accel(const hrt_abstime &t, const ADISReport &report) |
|
|
|
|
{ |
|
|
|
|
float xraw_f = (int32_t(report.X_ACCEL_OUT) << 16 | report.X_ACCEL_LOW) / 65536.0f * _accel_range_scale; |
|
|
|
|
float yraw_f = (int32_t(report.Y_ACCEL_OUT) << 16 | report.Y_ACCEL_LOW) / 65536.0f * _accel_range_scale; |
|
|
|
|
float zraw_f = (int32_t(report.Z_ACCEL_OUT) << 16 | report.Z_ACCEL_LOW) / 65536.0f * _accel_range_scale; |
|
|
|
|
|
|
|
|
|
// Apply user specified rotation
|
|
|
|
|
rotate_3f(_rotation, xraw_f, yraw_f, zraw_f); |
|
|
|
|
|
|
|
|
|
const float x_in_new = (xraw_f - _accel_scale.x_offset) * _accel_scale.x_scale; |
|
|
|
|
const float y_in_new = (yraw_f - _accel_scale.y_offset) * _accel_scale.y_scale; |
|
|
|
|
const float z_in_new = (zraw_f - _accel_scale.z_offset) * _accel_scale.z_scale; |
|
|
|
|
|
|
|
|
|
matrix::Vector3f aval(x_in_new, y_in_new, z_in_new); |
|
|
|
|
|
|
|
|
|
const matrix::Vector3f val_filt = _accel_filter.apply(aval); |
|
|
|
|
|
|
|
|
|
sensor_accel_s arb{}; |
|
|
|
|
matrix::Vector3f aval_integrated; |
|
|
|
|
|
|
|
|
|
if (_accel_int.put(t, aval, aval_integrated, arb.integral_dt)) { |
|
|
|
|
arb.timestamp = t; |
|
|
|
|
const uint64_t error_count = perf_event_count(_bad_transfers); |
|
|
|
|
_px4_accel.set_error_count(error_count); |
|
|
|
|
_px4_gyro.set_error_count(error_count); |
|
|
|
|
|
|
|
|
|
arb.device_id = _device_id.devid; |
|
|
|
|
arb.error_count = perf_event_count(_bad_transfers); |
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
// Raw sensor readings
|
|
|
|
|
arb.x_raw = report.X_ACCEL_OUT; |
|
|
|
|
arb.y_raw = report.Y_ACCEL_OUT; |
|
|
|
|
arb.z_raw = report.Z_ACCEL_OUT; |
|
|
|
|
arb.scaling = _accel_range_scale; |
|
|
|
|
|
|
|
|
|
// Filtered values for controls
|
|
|
|
|
arb.x = val_filt(0); |
|
|
|
|
arb.y = val_filt(1); |
|
|
|
|
arb.z = val_filt(2); |
|
|
|
|
|
|
|
|
|
// Intgrated values for estimation
|
|
|
|
|
arb.x_integral = aval_integrated(0); |
|
|
|
|
arb.y_integral = aval_integrated(1); |
|
|
|
|
arb.z_integral = aval_integrated(2); |
|
|
|
|
|
|
|
|
|
arb.temperature = (int16_t(report.TEMP_OUT) * 0.0125f) + 25.0f; // 1 LSB = 0.0125°C, 0x0000 at 25°C
|
|
|
|
|
|
|
|
|
|
orb_publish(ORB_ID(sensor_accel), _accel_topic, &arb); |
|
|
|
|
// 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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
ADIS16497::publish_gyro(const hrt_abstime &t, const ADISReport &report) |
|
|
|
|
{ |
|
|
|
|
float xraw_f = (int32_t(report.X_GYRO_OUT) << 16 | report.X_GYRO_LOW) / 65536.0f; |
|
|
|
|
float yraw_f = (int32_t(report.Y_GYRO_OUT) << 16 | report.Y_GYRO_LOW) / 65536.0f; |
|
|
|
|
float zraw_f = (int32_t(report.Z_GYRO_OUT) << 16 | report.Z_GYRO_LOW) / 65536.0f; |
|
|
|
|
|
|
|
|
|
// Apply user specified rotation
|
|
|
|
|
rotate_3f(_rotation, xraw_f, yraw_f, zraw_f); |
|
|
|
|
|
|
|
|
|
const float x_gyro_in_new = (math::radians(xraw_f * _gyro_range_scale) - _gyro_scale.x_offset) * _gyro_scale.x_scale; |
|
|
|
|
const float y_gyro_in_new = (math::radians(yraw_f * _gyro_range_scale) - _gyro_scale.y_offset) * _gyro_scale.y_scale; |
|
|
|
|
const float z_gyro_in_new = (math::radians(zraw_f * _gyro_range_scale) - _gyro_scale.z_offset) * _gyro_scale.z_scale; |
|
|
|
|
|
|
|
|
|
matrix::Vector3f gval(x_gyro_in_new, y_gyro_in_new, z_gyro_in_new); |
|
|
|
|
|
|
|
|
|
const matrix::Vector3f gval_filt = _gyro_filter.apply(gval); |
|
|
|
|
|
|
|
|
|
sensor_gyro_s grb{}; |
|
|
|
|
matrix::Vector3f gval_integrated; |
|
|
|
|
|
|
|
|
|
if (_gyro_int.put(t, gval, gval_integrated, grb.integral_dt)) { |
|
|
|
|
grb.timestamp = t; |
|
|
|
|
|
|
|
|
|
grb.device_id = _gyro->_device_id.devid; |
|
|
|
|
grb.error_count = perf_event_count(_bad_transfers); |
|
|
|
|
|
|
|
|
|
// Raw sensor readings
|
|
|
|
|
grb.x_raw = report.X_GYRO_OUT; |
|
|
|
|
grb.y_raw = report.Y_GYRO_OUT; |
|
|
|
|
grb.z_raw = report.Z_GYRO_OUT; |
|
|
|
|
grb.scaling = math::radians(_gyro_range_scale); |
|
|
|
|
|
|
|
|
|
// Filtered values for controls
|
|
|
|
|
grb.x = gval_filt(0); |
|
|
|
|
grb.y = gval_filt(1); |
|
|
|
|
grb.z = gval_filt(2); |
|
|
|
|
|
|
|
|
|
// Unfiltered integrated values for estimation
|
|
|
|
|
grb.x_integral = gval_integrated(0); |
|
|
|
|
grb.y_integral = gval_integrated(1); |
|
|
|
|
grb.z_integral = gval_integrated(2); |
|
|
|
|
{ |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grb.temperature = (int16_t(report.TEMP_OUT) * 0.0125f) + 25.0f; // 1 LSB = 0.0125°C, 0x0000 at 25°C
|
|
|
|
|
perf_end(_sample_perf); |
|
|
|
|
|
|
|
|
|
orb_publish(ORB_ID(sensor_gyro), _gyro->_gyro_topic, &grb); |
|
|
|
|
} |
|
|
|
|
return OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
ADIS16497::print_info() |
|
|
|
|
{ |
|
|
|
|
perf_print_counter(_sample_perf); |
|
|
|
|
perf_print_counter(_sample_interval_perf); |
|
|
|
|
perf_print_counter(_sample_perf); |
|
|
|
|
perf_print_counter(_bad_transfers); |
|
|
|
|
|
|
|
|
|
_px4_accel.print_status(); |
|
|
|
|
_px4_gyro.print_status(); |
|
|
|
|
} |
|
|
|
|