From 52c848a556fa7b82273dac7ff915893f43dd01f8 Mon Sep 17 00:00:00 2001 From: Daniel Agar <daniel@agar.ca> Date: Mon, 14 Jan 2019 16:22:42 -0500 Subject: [PATCH] mpu6000 split into separate main, header, implementation --- src/drivers/imu/mpu6000/CMakeLists.txt | 7 +- .../imu/mpu6000/{mpu6000.cpp => MPU6000.cpp} | 692 +----------------- .../imu/mpu6000/{mpu6000.h => MPU6000.hpp} | 296 +++++++- .../{mpu6000_i2c.cpp => MPU6000_I2C.cpp} | 2 +- .../{mpu6000_spi.cpp => MPU6000_SPI.cpp} | 2 +- src/drivers/imu/mpu6000/mpu6000_main.cpp | 428 +++++++++++ 6 files changed, 729 insertions(+), 698 deletions(-) rename src/drivers/imu/mpu6000/{mpu6000.cpp => MPU6000.cpp} (61%) rename src/drivers/imu/mpu6000/{mpu6000.h => MPU6000.hpp} (53%) rename src/drivers/imu/mpu6000/{mpu6000_i2c.cpp => MPU6000_I2C.cpp} (99%) rename src/drivers/imu/mpu6000/{mpu6000_spi.cpp => MPU6000_SPI.cpp} (99%) create mode 100644 src/drivers/imu/mpu6000/mpu6000_main.cpp diff --git a/src/drivers/imu/mpu6000/CMakeLists.txt b/src/drivers/imu/mpu6000/CMakeLists.txt index b904a0d51d..ec8c861537 100644 --- a/src/drivers/imu/mpu6000/CMakeLists.txt +++ b/src/drivers/imu/mpu6000/CMakeLists.txt @@ -35,9 +35,10 @@ px4_add_module( MAIN mpu6000 STACK_MAIN 1500 SRCS - mpu6000.cpp - mpu6000_i2c.cpp - mpu6000_spi.cpp + MPU6000.cpp + MPU6000_I2C.cpp + mpu6000_main.cpp + MPU6000_SPI.cpp DEPENDS drivers_accelerometer drivers_gyroscope diff --git a/src/drivers/imu/mpu6000/mpu6000.cpp b/src/drivers/imu/mpu6000/MPU6000.cpp similarity index 61% rename from src/drivers/imu/mpu6000/mpu6000.cpp rename to src/drivers/imu/mpu6000/MPU6000.cpp index 6d89fc0690..1cfd72b3c5 100644 --- a/src/drivers/imu/mpu6000/mpu6000.cpp +++ b/src/drivers/imu/mpu6000/MPU6000.cpp @@ -31,303 +31,7 @@ * ****************************************************************************/ -/** - * @file mpu6000.cpp - * - * Driver for the Invensense MPU6000, MPU6050, ICM20608, and ICM20602 connected via - * SPI or I2C. - * - * When the device is on the SPI bus the hrt is used to provide thread of - * execution to the driver. - * - * When the device is on the I2C bus a work queue is used provide thread of - * execution to the driver. - * - * The I2C code is only included in the build if USE_I2C is defined by the - * existance of any of PX4_I2C_MPU6050_ADDR, PX4_I2C_MPU6000_ADDR - * PX4_I2C_ICM_20608_G_ADDR in the board_config.h file. - * - * The command line option -T 6000|20608|20602 (default 6000) selects between - * MPU60x0, ICM20608G, or ICM20602G; - * - * @author Andrew Tridgell - * @author Pat Hickey - * @author David Sidrane - */ - -#include <drivers/drv_hrt.h> -#include <lib/cdev/CDev.hpp> -#include <lib/drivers/accelerometer/PX4Accelerometer.hpp> -#include <lib/drivers/device/i2c.h> -#include <lib/drivers/device/spi.h> -#include <lib/drivers/gyroscope/PX4Gyroscope.hpp> -#include <lib/ecl/geo/geo.h> -#include <lib/perf/perf_counter.h> -#include <px4_config.h> -#include <px4_getopt.h> -#include <px4_time.h> -#include <px4_workqueue.h> -#include <systemlib/conversions.h> -#include <systemlib/px4_macros.h> - -#include "mpu6000.h" - -/* - we set the timer interrupt to run a bit faster than the desired - sample rate and then throw away duplicates by comparing - accelerometer values. This time reduction is enough to cope with - worst case timing jitter due to other timers - - I2C bus is running at 100 kHz Transaction time is 2.163ms - I2C bus is running at 400 kHz (304 kHz actual) Transaction time - is 583 us - - */ -#define MPU6000_TIMER_REDUCTION 200 - -enum MPU6000_BUS { - MPU6000_BUS_ALL = 0, - MPU6000_BUS_I2C_INTERNAL, - MPU6000_BUS_I2C_EXTERNAL, - MPU6000_BUS_SPI_INTERNAL1, - MPU6000_BUS_SPI_INTERNAL2, - MPU6000_BUS_SPI_EXTERNAL1, - MPU6000_BUS_SPI_EXTERNAL2 -}; - -class MPU6000 : public cdev::CDev -{ -public: - MPU6000(device::Device *interface, const char *path, enum Rotation rotation, int device_type); - - virtual ~MPU6000(); - - virtual int init(); - - /** - * Diagnostics - print some basic information about the driver. - */ - void print_info(); - - void print_registers(); - - /** - * Test behaviour against factory offsets - * - * @return 0 on success, 1 on failure - */ - int factory_self_test(); - - // deliberately cause a sensor error - void test_error(); - - /** - * Start automatic measurement. - */ - void start(); - - /** - * Reset chip. - * - * Resets the chip and measurements ranges, but not scale and offset. - */ - int reset(); - -protected: - device::Device *_interface; - - virtual int probe(); - -private: - int _device_type; - uint8_t _product{0}; /** product code */ - -#if defined(USE_I2C) - /* - * SPI bus based device use hrt - * I2C bus needs to use work queue - */ - work_s _work{}; -#endif - bool _use_hrt; - - struct hrt_call _call {}; - unsigned _call_interval{1000}; - - PX4Accelerometer _px4_accel; - PX4Gyroscope _px4_gyro; - - unsigned _sample_rate{1000}; - - perf_counter_t _sample_perf; - perf_counter_t _measure_interval; - perf_counter_t _bad_transfers; - perf_counter_t _bad_registers; - perf_counter_t _reset_retries; - perf_counter_t _duplicates; - - uint8_t _register_wait{0}; - uint64_t _reset_wait{0}; - - // this is used to support runtime checking of key - // configuration registers to detect SPI bus errors and sensor - // reset - static constexpr int MPU6000_CHECKED_PRODUCT_ID_INDEX = 0; - static constexpr int MPU6000_NUM_CHECKED_REGISTERS = 10; - - static constexpr uint8_t _checked_registers[MPU6000_NUM_CHECKED_REGISTERS] { - MPUREG_PRODUCT_ID, - MPUREG_PWR_MGMT_1, - MPUREG_USER_CTRL, - MPUREG_SMPLRT_DIV, - MPUREG_CONFIG, - MPUREG_GYRO_CONFIG, - MPUREG_ACCEL_CONFIG, - MPUREG_INT_ENABLE, - MPUREG_INT_PIN_CFG, - MPUREG_ICM_UNDOC1 - }; - - uint8_t _checked_values[MPU6000_NUM_CHECKED_REGISTERS]; - uint8_t _checked_next{0}; - - // use this to avoid processing measurements when in factory - // self test - volatile bool _in_factory_test{false}; - - // keep last accel reading for duplicate detection - uint16_t _last_accel[3] {}; - bool _got_duplicate{false}; - - /** - * Stop automatic measurement. - */ - void stop(); - - /** - * is_icm_device - */ - bool is_icm_device() { return !is_mpu_device(); } - /** - * is_mpu_device - */ - bool is_mpu_device() { return _device_type == MPU_DEVICE_TYPE_MPU6000; } - - -#if defined(USE_I2C) - /** - * When the I2C interfase is on - * Perform a poll cycle; collect from the previous measurement - * and start a new one. - * - * This is the heart of the measurement state machine. This function - * alternately starts a measurement, or collects the data from the - * previous measurement. - * - * When the interval between measurements is greater than the minimum - * measurement interval, a gap is inserted between collection - * and measurement to provide the most recent measurement possible - * at the next interval. - */ - void cycle(); - - /** - * Static trampoline from the workq context; because we don't have a - * generic workq wrapper yet. - * - * @param arg Instance pointer for the driver that is polling. - */ - static void cycle_trampoline(void *arg); - - void use_i2c(bool on_true) { _use_hrt = !on_true; } - -#endif - - bool is_i2c(void) { return !_use_hrt; } - - - /** - * Static trampoline from the hrt_call context; because we don't have a - * generic hrt wrapper yet. - * - * Called by the HRT in interrupt context at the specified rate if - * automatic polling is enabled. - * - * @param arg Instance pointer for the driver that is polling. - */ - static void measure_trampoline(void *arg); - - /** - * Fetch measurements from the sensor and update the report buffers. - */ - int measure(); - - /** - * Read a register from the MPU6000 - * - * @param The register to read. - * @return The value that was read. - */ - uint8_t read_reg(unsigned reg, uint32_t speed = MPU6000_LOW_BUS_SPEED); - uint16_t read_reg16(unsigned reg); - - - /** - * Write a register in the MPU6000 - * - * @param reg The register to write. - * @param value The new value to write. - */ - int write_reg(unsigned reg, uint8_t value); - - /** - * Modify a register in the MPU6000 - * - * Bits are cleared before bits are set. - * - * @param reg The register to modify. - * @param clearbits Bits in the register to clear. - * @param setbits Bits in the register to set. - */ - void modify_reg(unsigned reg, uint8_t clearbits, uint8_t setbits); - - /** - * Write a register in the MPU6000, updating _checked_values - * - * @param reg The register to write. - * @param value The new value to write. - */ - void write_checked_reg(unsigned reg, uint8_t value); - - /** - * Set the MPU6000 measurement range. - * - * @param max_g The maximum G value the range must support. - * @return OK if the value can be supported, -ERANGE otherwise. - */ - int set_accel_range(unsigned max_g); - - /* - set low pass filter frequency - */ - void _set_dlpf_filter(uint16_t frequency_hz); - void _set_icm_acc_dlpf_filter(uint16_t frequency_hz); - - /* - set sample rate (approximate) - 1kHz to 5Hz - */ - void _set_sample_rate(unsigned desired_sample_rate_hz); - - /* - check that key registers still have the right value - */ - void check_registers(void); - - /* do not allow to copy this class due to pointer data members */ - MPU6000(const MPU6000 &); - MPU6000 operator=(const MPU6000 &); - -}; +#include "MPU6000.hpp" /* list of registers that will be checked in check_registers(). Note @@ -335,9 +39,6 @@ private: */ constexpr uint8_t MPU6000::_checked_registers[MPU6000_NUM_CHECKED_REGISTERS]; -/** driver 'main' command */ -extern "C" { __EXPORT int mpu6000_main(int argc, char *argv[]); } - MPU6000::MPU6000(device::Device *interface, const char *path, enum Rotation rotation, int device_type) : CDev(path), _interface(interface), @@ -1286,394 +987,3 @@ MPU6000::print_registers() printf("\n"); } - -/** - * Local functions in support of the shell command. - */ -namespace mpu6000 -{ - -/* - list of supported bus configurations - */ - -struct mpu6000_bus_option { - enum MPU6000_BUS busid; - MPU_DEVICE_TYPE device_type; - const char *devpath; - MPU6000_constructor interface_constructor; - uint8_t busnum; - bool external; - MPU6000 *dev; -} bus_options[] = { -#if defined (USE_I2C) -# if defined(PX4_I2C_BUS_ONBOARD) - { MPU6000_BUS_I2C_INTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH, &MPU6000_I2C_interface, PX4_I2C_BUS_ONBOARD, false, NULL }, -# endif -# if defined(PX4_I2C_BUS_EXPANSION) - { MPU6000_BUS_I2C_EXTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT, &MPU6000_I2C_interface, PX4_I2C_BUS_EXPANSION, true, NULL }, -# endif -# if defined(PX4_I2C_BUS_EXPANSION1) - { MPU6000_BUS_I2C_EXTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT1, &MPU6000_I2C_interface, PX4_I2C_BUS_EXPANSION1, true, NULL }, -# endif -# if defined(PX4_I2C_BUS_EXPANSION2) - { MPU6000_BUS_I2C_EXTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT2, &MPU6000_I2C_interface, PX4_I2C_BUS_EXPANSION2, true, NULL }, -# endif -#endif -#ifdef PX4_SPIDEV_MPU - { MPU6000_BUS_SPI_INTERNAL1, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, -#endif -#if defined(PX4_SPI_BUS_EXT) - { MPU6000_BUS_SPI_EXTERNAL1, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT, &MPU6000_SPI_interface, PX4_SPI_BUS_EXT, true, NULL }, -#endif -#ifdef PX4_SPIDEV_ICM_20602 - { MPU6000_BUS_SPI_INTERNAL1, MPU_DEVICE_TYPE_ICM20602, ICM20602_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, -#endif -#ifdef PX4_SPIDEV_ICM_20608 - { MPU6000_BUS_SPI_INTERNAL1, MPU_DEVICE_TYPE_ICM20608, ICM20608_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, -#endif -#ifdef PX4_SPIDEV_ICM_20689 - { MPU6000_BUS_SPI_INTERNAL2, MPU_DEVICE_TYPE_ICM20689, ICM20689_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, -#endif -#if defined(PX4_SPI_BUS_EXTERNAL) - { MPU6000_BUS_SPI_EXTERNAL1, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT, &MPU6000_SPI_interface, PX4_SPI_BUS_EXTERNAL, true, NULL }, - { MPU6000_BUS_SPI_EXTERNAL2, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT1, &MPU6000_SPI_interface, PX4_SPI_BUS_EXTERNAL, true, NULL }, -#endif -}; - -#define NUM_BUS_OPTIONS (sizeof(bus_options)/sizeof(bus_options[0])) - - -void start(enum MPU6000_BUS busid, enum Rotation rotation, int device_type); -bool start_bus(struct mpu6000_bus_option &bus, enum Rotation rotation, int device_type); -void stop(enum MPU6000_BUS busid); -static struct mpu6000_bus_option &find_bus(enum MPU6000_BUS busid); -void reset(enum MPU6000_BUS busid); -void info(enum MPU6000_BUS busid); -void regdump(enum MPU6000_BUS busid); -void testerror(enum MPU6000_BUS busid); -void factorytest(enum MPU6000_BUS busid); -void usage(); - -/** - * find a bus structure for a busid - */ -struct mpu6000_bus_option &find_bus(enum MPU6000_BUS busid) -{ - for (uint8_t i = 0; i < NUM_BUS_OPTIONS; i++) { - if ((busid == MPU6000_BUS_ALL || - busid == bus_options[i].busid) && bus_options[i].dev != NULL) { - return bus_options[i]; - } - } - - errx(1, "bus %u not started", (unsigned)busid); -} - -/** - * start driver for a specific bus option - */ -bool -start_bus(struct mpu6000_bus_option &bus, enum Rotation rotation, int device_type) -{ - if (bus.dev != nullptr) { - warnx("%s SPI not available", bus.external ? "External" : "Internal"); - return false; - } - - device::Device *interface = bus.interface_constructor(bus.busnum, device_type, bus.external); - - if (interface == nullptr) { - warnx("failed creating interface for bus #%u (SPI%u)", (unsigned)bus.busid, (unsigned)bus.busnum); - return false; - } - - if (interface->init() != OK) { - delete interface; - warnx("no device on bus #%u (SPI%u)", (unsigned)bus.busid, (unsigned)bus.busnum); - return false; - } - - bus.dev = new MPU6000(interface, bus.devpath, rotation, device_type); - - if (bus.dev == nullptr) { - delete interface; - return false; - } - - if (OK != bus.dev->init()) { - goto fail; - } - - /* set the poll rate to default, starts automatic data collection */ - - bus.dev->start(); - - return true; - -fail: - - if (bus.dev != nullptr) { - delete bus.dev; - bus.dev = nullptr; - } - - return false; -} - -/** - * Start the driver. - * - * This function only returns if the driver is up and running - * or failed to detect the sensor. - */ -void -start(enum MPU6000_BUS busid, enum Rotation rotation, int device_type) -{ - bool started = false; - - for (unsigned i = 0; i < NUM_BUS_OPTIONS; i++) { - if (busid == MPU6000_BUS_ALL && bus_options[i].dev != NULL) { - // this device is already started - continue; - } - - if (busid != MPU6000_BUS_ALL && bus_options[i].busid != busid) { - // not the one that is asked for - continue; - } - - if (bus_options[i].device_type != device_type) { - // not the one that is asked for - continue; - } - - started |= start_bus(bus_options[i], rotation, device_type); - } - - exit(started ? 0 : 1); -} - -void -stop(enum MPU6000_BUS busid) -{ - struct mpu6000_bus_option &bus = find_bus(busid); - - if (bus.dev != nullptr) { - delete bus.dev; - bus.dev = nullptr; - - } else { - /* warn, but not an error */ - warnx("already stopped."); - } - - exit(0); -} - -/** - * Reset the driver. - */ -void -reset(enum MPU6000_BUS busid) -{ - struct mpu6000_bus_option &bus = find_bus(busid); - - if (bus.dev == nullptr) { - errx(1, "driver not running"); - } - - bus.dev->reset(); - - exit(0); -} - -/** - * Print a little info about the driver. - */ -void -info(enum MPU6000_BUS busid) -{ - struct mpu6000_bus_option &bus = find_bus(busid); - - if (bus.dev == nullptr) { - errx(1, "driver not running"); - } - - bus.dev->print_info(); - - exit(0); -} - -/** - * Dump the register information - */ -void -regdump(enum MPU6000_BUS busid) -{ - struct mpu6000_bus_option &bus = find_bus(busid); - - if (bus.dev == nullptr) { - errx(1, "driver not running"); - } - - printf("regdump @ %p\n", bus.dev); - bus.dev->print_registers(); - - exit(0); -} - -/** - * deliberately produce an error to test recovery - */ -void -testerror(enum MPU6000_BUS busid) -{ - struct mpu6000_bus_option &bus = find_bus(busid); - - - if (bus.dev == nullptr) { - errx(1, "driver not running"); - } - - bus.dev->test_error(); - - exit(0); -} - -/** - * Dump the register information - */ -void -factorytest(enum MPU6000_BUS busid) -{ - struct mpu6000_bus_option &bus = find_bus(busid); - - - if (bus.dev == nullptr) { - errx(1, "driver not running"); - } - - bus.dev->factory_self_test(); - - exit(0); -} - -void -usage() -{ - warnx("missing command: try 'start', 'info', 'stop',\n'reset', 'regdump', 'factorytest', 'testerror'"); - warnx("options:"); - warnx(" -X external I2C bus"); - warnx(" -I internal I2C bus"); - warnx(" -S external SPI bus"); - warnx(" -s internal SPI bus"); - warnx(" -Z external1 SPI bus"); - warnx(" -z internal2 SPI bus"); - warnx(" -T 6000|20608|20602 (default 6000)"); - warnx(" -R rotation"); -} - -} // namespace - -int -mpu6000_main(int argc, char *argv[]) -{ - int myoptind = 1; - int ch; - const char *myoptarg = nullptr; - - enum MPU6000_BUS busid = MPU6000_BUS_ALL; - int device_type = MPU_DEVICE_TYPE_MPU6000; - enum Rotation rotation = ROTATION_NONE; - - while ((ch = px4_getopt(argc, argv, "T:XISsZzR:a:", &myoptind, &myoptarg)) != EOF) { - switch (ch) { - case 'X': - busid = MPU6000_BUS_I2C_EXTERNAL; - break; - - case 'I': - busid = MPU6000_BUS_I2C_INTERNAL; - break; - - case 'S': - busid = MPU6000_BUS_SPI_EXTERNAL1; - break; - - case 's': - busid = MPU6000_BUS_SPI_INTERNAL1; - break; - - case 'Z': - busid = MPU6000_BUS_SPI_EXTERNAL2; - break; - - case 'z': - busid = MPU6000_BUS_SPI_INTERNAL2; - break; - - case 'T': - device_type = atoi(myoptarg); - break; - - case 'R': - rotation = (enum Rotation)atoi(myoptarg); - break; - - default: - mpu6000::usage(); - return 0; - } - } - - if (myoptind >= argc) { - mpu6000::usage(); - return -1; - } - - const char *verb = argv[myoptind]; - - /* - * Start/load the driver. - */ - if (!strcmp(verb, "start")) { - mpu6000::start(busid, rotation, device_type); - } - - if (!strcmp(verb, "stop")) { - mpu6000::stop(busid); - } - - /* - * Reset the driver. - */ - if (!strcmp(verb, "reset")) { - mpu6000::reset(busid); - } - - /* - * Print driver information. - */ - if (!strcmp(verb, "info") || !strcmp(verb, "status")) { - mpu6000::info(busid); - } - - /* - * Print register information. - */ - if (!strcmp(verb, "regdump")) { - mpu6000::regdump(busid); - } - - if (!strcmp(verb, "factorytest")) { - mpu6000::factorytest(busid); - } - - if (!strcmp(verb, "testerror")) { - mpu6000::testerror(busid); - } - - mpu6000::usage(); - return -1; -} diff --git a/src/drivers/imu/mpu6000/mpu6000.h b/src/drivers/imu/mpu6000/MPU6000.hpp similarity index 53% rename from src/drivers/imu/mpu6000/mpu6000.h rename to src/drivers/imu/mpu6000/MPU6000.hpp index 7fc3a03bbf..5e7d37f929 100644 --- a/src/drivers/imu/mpu6000/mpu6000.h +++ b/src/drivers/imu/mpu6000/MPU6000.hpp @@ -32,9 +32,54 @@ ****************************************************************************/ /** - * @file .h + * @file mpu6000.cpp * - * Shared defines for the mpu6000 driver. + * Driver for the Invensense MPU6000, MPU6050, ICM20608, and ICM20602 connected via + * SPI or I2C. + * + * When the device is on the SPI bus the hrt is used to provide thread of + * execution to the driver. + * + * When the device is on the I2C bus a work queue is used provide thread of + * execution to the driver. + * + * The I2C code is only included in the build if USE_I2C is defined by the + * existance of any of PX4_I2C_MPU6050_ADDR, PX4_I2C_MPU6000_ADDR + * PX4_I2C_ICM_20608_G_ADDR in the board_config.h file. + * + * The command line option -T 6000|20608|20602 (default 6000) selects between + * MPU60x0, ICM20608G, or ICM20602G; + * + * @author Andrew Tridgell + * @author Pat Hickey + * @author David Sidrane + */ + +#include <drivers/drv_hrt.h> +#include <lib/cdev/CDev.hpp> +#include <lib/drivers/accelerometer/PX4Accelerometer.hpp> +#include <lib/drivers/device/i2c.h> +#include <lib/drivers/device/spi.h> +#include <lib/drivers/gyroscope/PX4Gyroscope.hpp> +#include <lib/ecl/geo/geo.h> +#include <lib/perf/perf_counter.h> +#include <px4_config.h> +#include <px4_getopt.h> +#include <px4_time.h> +#include <px4_workqueue.h> +#include <systemlib/conversions.h> +#include <systemlib/px4_macros.h> + +/* + we set the timer interrupt to run a bit faster than the desired + sample rate and then throw away duplicates by comparing + accelerometer values. This time reduction is enough to cope with + worst case timing jitter due to other timers + + I2C bus is running at 100 kHz Transaction time is 2.163ms + I2C bus is running at 400 kHz (304 kHz actual) Transaction time + is 583 us + */ #pragma once @@ -243,3 +288,250 @@ extern device::Device *MPU6000_I2C_interface(int bus, int device_type, bool exte extern int MPU6000_probe(device::Device *dev, int device_type); typedef device::Device *(*MPU6000_constructor)(int, int, bool); + + +#define MPU6000_TIMER_REDUCTION 200 + +enum MPU6000_BUS { + MPU6000_BUS_ALL = 0, + MPU6000_BUS_I2C_INTERNAL, + MPU6000_BUS_I2C_EXTERNAL, + MPU6000_BUS_SPI_INTERNAL1, + MPU6000_BUS_SPI_INTERNAL2, + MPU6000_BUS_SPI_EXTERNAL1, + MPU6000_BUS_SPI_EXTERNAL2 +}; + +class MPU6000 : public cdev::CDev +{ +public: + MPU6000(device::Device *interface, const char *path, enum Rotation rotation, int device_type); + + virtual ~MPU6000(); + + virtual int init(); + + /** + * Diagnostics - print some basic information about the driver. + */ + void print_info(); + + void print_registers(); + + /** + * Test behaviour against factory offsets + * + * @return 0 on success, 1 on failure + */ + int factory_self_test(); + + // deliberately cause a sensor error + void test_error(); + + /** + * Start automatic measurement. + */ + void start(); + + /** + * Reset chip. + * + * Resets the chip and measurements ranges, but not scale and offset. + */ + int reset(); + +protected: + device::Device *_interface; + + virtual int probe(); + +private: + int _device_type; + uint8_t _product{0}; /** product code */ + +#if defined(USE_I2C) + /* + * SPI bus based device use hrt + * I2C bus needs to use work queue + */ + work_s _work{}; +#endif + bool _use_hrt; + + struct hrt_call _call {}; + unsigned _call_interval{1000}; + + PX4Accelerometer _px4_accel; + PX4Gyroscope _px4_gyro; + + unsigned _sample_rate{1000}; + + perf_counter_t _sample_perf; + perf_counter_t _measure_interval; + perf_counter_t _bad_transfers; + perf_counter_t _bad_registers; + perf_counter_t _reset_retries; + perf_counter_t _duplicates; + + uint8_t _register_wait{0}; + uint64_t _reset_wait{0}; + + // this is used to support runtime checking of key + // configuration registers to detect SPI bus errors and sensor + // reset + static constexpr int MPU6000_CHECKED_PRODUCT_ID_INDEX = 0; + static constexpr int MPU6000_NUM_CHECKED_REGISTERS = 10; + + static constexpr uint8_t _checked_registers[MPU6000_NUM_CHECKED_REGISTERS] { + MPUREG_PRODUCT_ID, + MPUREG_PWR_MGMT_1, + MPUREG_USER_CTRL, + MPUREG_SMPLRT_DIV, + MPUREG_CONFIG, + MPUREG_GYRO_CONFIG, + MPUREG_ACCEL_CONFIG, + MPUREG_INT_ENABLE, + MPUREG_INT_PIN_CFG, + MPUREG_ICM_UNDOC1 + }; + + uint8_t _checked_values[MPU6000_NUM_CHECKED_REGISTERS]; + uint8_t _checked_next{0}; + + // use this to avoid processing measurements when in factory + // self test + volatile bool _in_factory_test{false}; + + // keep last accel reading for duplicate detection + uint16_t _last_accel[3] {}; + bool _got_duplicate{false}; + + /** + * Stop automatic measurement. + */ + void stop(); + + /** + * is_icm_device + */ + bool is_icm_device() { return !is_mpu_device(); } + /** + * is_mpu_device + */ + bool is_mpu_device() { return _device_type == MPU_DEVICE_TYPE_MPU6000; } + + +#if defined(USE_I2C) + /** + * When the I2C interfase is on + * Perform a poll cycle; collect from the previous measurement + * and start a new one. + * + * This is the heart of the measurement state machine. This function + * alternately starts a measurement, or collects the data from the + * previous measurement. + * + * When the interval between measurements is greater than the minimum + * measurement interval, a gap is inserted between collection + * and measurement to provide the most recent measurement possible + * at the next interval. + */ + void cycle(); + + /** + * Static trampoline from the workq context; because we don't have a + * generic workq wrapper yet. + * + * @param arg Instance pointer for the driver that is polling. + */ + static void cycle_trampoline(void *arg); + + void use_i2c(bool on_true) { _use_hrt = !on_true; } + +#endif + + bool is_i2c(void) { return !_use_hrt; } + + + /** + * Static trampoline from the hrt_call context; because we don't have a + * generic hrt wrapper yet. + * + * Called by the HRT in interrupt context at the specified rate if + * automatic polling is enabled. + * + * @param arg Instance pointer for the driver that is polling. + */ + static void measure_trampoline(void *arg); + + /** + * Fetch measurements from the sensor and update the report buffers. + */ + int measure(); + + /** + * Read a register from the MPU6000 + * + * @param The register to read. + * @return The value that was read. + */ + uint8_t read_reg(unsigned reg, uint32_t speed = MPU6000_LOW_BUS_SPEED); + uint16_t read_reg16(unsigned reg); + + + /** + * Write a register in the MPU6000 + * + * @param reg The register to write. + * @param value The new value to write. + */ + int write_reg(unsigned reg, uint8_t value); + + /** + * Modify a register in the MPU6000 + * + * Bits are cleared before bits are set. + * + * @param reg The register to modify. + * @param clearbits Bits in the register to clear. + * @param setbits Bits in the register to set. + */ + void modify_reg(unsigned reg, uint8_t clearbits, uint8_t setbits); + + /** + * Write a register in the MPU6000, updating _checked_values + * + * @param reg The register to write. + * @param value The new value to write. + */ + void write_checked_reg(unsigned reg, uint8_t value); + + /** + * Set the MPU6000 measurement range. + * + * @param max_g The maximum G value the range must support. + * @return OK if the value can be supported, -ERANGE otherwise. + */ + int set_accel_range(unsigned max_g); + + /* + set low pass filter frequency + */ + void _set_dlpf_filter(uint16_t frequency_hz); + void _set_icm_acc_dlpf_filter(uint16_t frequency_hz); + + /* + set sample rate (approximate) - 1kHz to 5Hz + */ + void _set_sample_rate(unsigned desired_sample_rate_hz); + + /* + check that key registers still have the right value + */ + void check_registers(void); + + /* do not allow to copy this class due to pointer data members */ + MPU6000(const MPU6000 &); + MPU6000 operator=(const MPU6000 &); + +}; diff --git a/src/drivers/imu/mpu6000/mpu6000_i2c.cpp b/src/drivers/imu/mpu6000/MPU6000_I2C.cpp similarity index 99% rename from src/drivers/imu/mpu6000/mpu6000_i2c.cpp rename to src/drivers/imu/mpu6000/MPU6000_I2C.cpp index 18537e1e96..a7a29fee06 100644 --- a/src/drivers/imu/mpu6000/mpu6000_i2c.cpp +++ b/src/drivers/imu/mpu6000/MPU6000_I2C.cpp @@ -42,7 +42,7 @@ #include <drivers/drv_accel.h> #include <drivers/drv_device.h> -#include "mpu6000.h" +#include "MPU6000.hpp" #ifdef USE_I2C diff --git a/src/drivers/imu/mpu6000/mpu6000_spi.cpp b/src/drivers/imu/mpu6000/MPU6000_SPI.cpp similarity index 99% rename from src/drivers/imu/mpu6000/mpu6000_spi.cpp rename to src/drivers/imu/mpu6000/MPU6000_SPI.cpp index 6872955163..da6fdb2ce3 100644 --- a/src/drivers/imu/mpu6000/mpu6000_spi.cpp +++ b/src/drivers/imu/mpu6000/MPU6000_SPI.cpp @@ -46,7 +46,7 @@ #include <drivers/drv_accel.h> #include <drivers/drv_device.h> -#include "mpu6000.h" +#include "MPU6000.hpp" #include <string.h> diff --git a/src/drivers/imu/mpu6000/mpu6000_main.cpp b/src/drivers/imu/mpu6000/mpu6000_main.cpp new file mode 100644 index 0000000000..43106953e1 --- /dev/null +++ b/src/drivers/imu/mpu6000/mpu6000_main.cpp @@ -0,0 +1,428 @@ +/**************************************************************************** + * + * Copyright (c) 2012-2015 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include "MPU6000.hpp" + +/** + * Local functions in support of the shell command. + */ +namespace mpu6000 +{ + +/* + list of supported bus configurations + */ + +struct mpu6000_bus_option { + enum MPU6000_BUS busid; + MPU_DEVICE_TYPE device_type; + const char *devpath; + MPU6000_constructor interface_constructor; + uint8_t busnum; + bool external; + MPU6000 *dev; +} bus_options[] = { +#if defined (USE_I2C) +# if defined(PX4_I2C_BUS_ONBOARD) + { MPU6000_BUS_I2C_INTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH, &MPU6000_I2C_interface, PX4_I2C_BUS_ONBOARD, false, NULL }, +# endif +# if defined(PX4_I2C_BUS_EXPANSION) + { MPU6000_BUS_I2C_EXTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT, &MPU6000_I2C_interface, PX4_I2C_BUS_EXPANSION, true, NULL }, +# endif +# if defined(PX4_I2C_BUS_EXPANSION1) + { MPU6000_BUS_I2C_EXTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT1, &MPU6000_I2C_interface, PX4_I2C_BUS_EXPANSION1, true, NULL }, +# endif +# if defined(PX4_I2C_BUS_EXPANSION2) + { MPU6000_BUS_I2C_EXTERNAL, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT2, &MPU6000_I2C_interface, PX4_I2C_BUS_EXPANSION2, true, NULL }, +# endif +#endif +#ifdef PX4_SPIDEV_MPU + { MPU6000_BUS_SPI_INTERNAL1, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, +#endif +#if defined(PX4_SPI_BUS_EXT) + { MPU6000_BUS_SPI_EXTERNAL1, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT, &MPU6000_SPI_interface, PX4_SPI_BUS_EXT, true, NULL }, +#endif +#ifdef PX4_SPIDEV_ICM_20602 + { MPU6000_BUS_SPI_INTERNAL1, MPU_DEVICE_TYPE_ICM20602, ICM20602_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, +#endif +#ifdef PX4_SPIDEV_ICM_20608 + { MPU6000_BUS_SPI_INTERNAL1, MPU_DEVICE_TYPE_ICM20608, ICM20608_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, +#endif +#ifdef PX4_SPIDEV_ICM_20689 + { MPU6000_BUS_SPI_INTERNAL2, MPU_DEVICE_TYPE_ICM20689, ICM20689_DEVICE_PATH, &MPU6000_SPI_interface, PX4_SPI_BUS_SENSORS, false, NULL }, +#endif +#if defined(PX4_SPI_BUS_EXTERNAL) + { MPU6000_BUS_SPI_EXTERNAL1, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT, &MPU6000_SPI_interface, PX4_SPI_BUS_EXTERNAL, true, NULL }, + { MPU6000_BUS_SPI_EXTERNAL2, MPU_DEVICE_TYPE_MPU6000, MPU_DEVICE_PATH_EXT1, &MPU6000_SPI_interface, PX4_SPI_BUS_EXTERNAL, true, NULL }, +#endif +}; + +#define NUM_BUS_OPTIONS (sizeof(bus_options)/sizeof(bus_options[0])) + + +void start(enum MPU6000_BUS busid, enum Rotation rotation, int device_type); +bool start_bus(struct mpu6000_bus_option &bus, enum Rotation rotation, int device_type); +void stop(enum MPU6000_BUS busid); +static struct mpu6000_bus_option &find_bus(enum MPU6000_BUS busid); +void reset(enum MPU6000_BUS busid); +void info(enum MPU6000_BUS busid); +void regdump(enum MPU6000_BUS busid); +void testerror(enum MPU6000_BUS busid); +void factorytest(enum MPU6000_BUS busid); +void usage(); + +/** + * find a bus structure for a busid + */ +struct mpu6000_bus_option &find_bus(enum MPU6000_BUS busid) +{ + for (uint8_t i = 0; i < NUM_BUS_OPTIONS; i++) { + if ((busid == MPU6000_BUS_ALL || + busid == bus_options[i].busid) && bus_options[i].dev != NULL) { + return bus_options[i]; + } + } + + errx(1, "bus %u not started", (unsigned)busid); +} + +/** + * start driver for a specific bus option + */ +bool +start_bus(struct mpu6000_bus_option &bus, enum Rotation rotation, int device_type) +{ + if (bus.dev != nullptr) { + warnx("%s SPI not available", bus.external ? "External" : "Internal"); + return false; + } + + device::Device *interface = bus.interface_constructor(bus.busnum, device_type, bus.external); + + if (interface == nullptr) { + warnx("failed creating interface for bus #%u (SPI%u)", (unsigned)bus.busid, (unsigned)bus.busnum); + return false; + } + + if (interface->init() != OK) { + delete interface; + warnx("no device on bus #%u (SPI%u)", (unsigned)bus.busid, (unsigned)bus.busnum); + return false; + } + + bus.dev = new MPU6000(interface, bus.devpath, rotation, device_type); + + if (bus.dev == nullptr) { + delete interface; + return false; + } + + if (OK != bus.dev->init()) { + goto fail; + } + + /* set the poll rate to default, starts automatic data collection */ + + bus.dev->start(); + + return true; + +fail: + + if (bus.dev != nullptr) { + delete bus.dev; + bus.dev = nullptr; + } + + return false; +} + +/** + * Start the driver. + * + * This function only returns if the driver is up and running + * or failed to detect the sensor. + */ +void +start(enum MPU6000_BUS busid, enum Rotation rotation, int device_type) +{ + bool started = false; + + for (unsigned i = 0; i < NUM_BUS_OPTIONS; i++) { + if (busid == MPU6000_BUS_ALL && bus_options[i].dev != NULL) { + // this device is already started + continue; + } + + if (busid != MPU6000_BUS_ALL && bus_options[i].busid != busid) { + // not the one that is asked for + continue; + } + + if (bus_options[i].device_type != device_type) { + // not the one that is asked for + continue; + } + + started |= start_bus(bus_options[i], rotation, device_type); + } + + exit(started ? 0 : 1); +} + +void +stop(enum MPU6000_BUS busid) +{ + struct mpu6000_bus_option &bus = find_bus(busid); + + if (bus.dev != nullptr) { + delete bus.dev; + bus.dev = nullptr; + + } else { + /* warn, but not an error */ + warnx("already stopped."); + } + + exit(0); +} + +/** + * Reset the driver. + */ +void +reset(enum MPU6000_BUS busid) +{ + struct mpu6000_bus_option &bus = find_bus(busid); + + if (bus.dev == nullptr) { + errx(1, "driver not running"); + } + + bus.dev->reset(); + + exit(0); +} + +/** + * Print a little info about the driver. + */ +void +info(enum MPU6000_BUS busid) +{ + struct mpu6000_bus_option &bus = find_bus(busid); + + if (bus.dev == nullptr) { + errx(1, "driver not running"); + } + + bus.dev->print_info(); + + exit(0); +} + +/** + * Dump the register information + */ +void +regdump(enum MPU6000_BUS busid) +{ + struct mpu6000_bus_option &bus = find_bus(busid); + + if (bus.dev == nullptr) { + errx(1, "driver not running"); + } + + printf("regdump @ %p\n", bus.dev); + bus.dev->print_registers(); + + exit(0); +} + +/** + * deliberately produce an error to test recovery + */ +void +testerror(enum MPU6000_BUS busid) +{ + struct mpu6000_bus_option &bus = find_bus(busid); + + + if (bus.dev == nullptr) { + errx(1, "driver not running"); + } + + bus.dev->test_error(); + + exit(0); +} + +/** + * Dump the register information + */ +void +factorytest(enum MPU6000_BUS busid) +{ + struct mpu6000_bus_option &bus = find_bus(busid); + + + if (bus.dev == nullptr) { + errx(1, "driver not running"); + } + + bus.dev->factory_self_test(); + + exit(0); +} + +void +usage() +{ + warnx("missing command: try 'start', 'info', 'stop',\n'reset', 'regdump', 'factorytest', 'testerror'"); + warnx("options:"); + warnx(" -X external I2C bus"); + warnx(" -I internal I2C bus"); + warnx(" -S external SPI bus"); + warnx(" -s internal SPI bus"); + warnx(" -Z external1 SPI bus"); + warnx(" -z internal2 SPI bus"); + warnx(" -T 6000|20608|20602 (default 6000)"); + warnx(" -R rotation"); +} + +} // namespace + +/** driver 'main' command */ +extern "C" { __EXPORT int mpu6000_main(int argc, char *argv[]); } + +int +mpu6000_main(int argc, char *argv[]) +{ + int myoptind = 1; + int ch; + const char *myoptarg = nullptr; + + enum MPU6000_BUS busid = MPU6000_BUS_ALL; + int device_type = MPU_DEVICE_TYPE_MPU6000; + enum Rotation rotation = ROTATION_NONE; + + while ((ch = px4_getopt(argc, argv, "T:XISsZzR:a:", &myoptind, &myoptarg)) != EOF) { + switch (ch) { + case 'X': + busid = MPU6000_BUS_I2C_EXTERNAL; + break; + + case 'I': + busid = MPU6000_BUS_I2C_INTERNAL; + break; + + case 'S': + busid = MPU6000_BUS_SPI_EXTERNAL1; + break; + + case 's': + busid = MPU6000_BUS_SPI_INTERNAL1; + break; + + case 'Z': + busid = MPU6000_BUS_SPI_EXTERNAL2; + break; + + case 'z': + busid = MPU6000_BUS_SPI_INTERNAL2; + break; + + case 'T': + device_type = atoi(myoptarg); + break; + + case 'R': + rotation = (enum Rotation)atoi(myoptarg); + break; + + default: + mpu6000::usage(); + return 0; + } + } + + if (myoptind >= argc) { + mpu6000::usage(); + return -1; + } + + const char *verb = argv[myoptind]; + + /* + * Start/load the driver. + */ + if (!strcmp(verb, "start")) { + mpu6000::start(busid, rotation, device_type); + } + + if (!strcmp(verb, "stop")) { + mpu6000::stop(busid); + } + + /* + * Reset the driver. + */ + if (!strcmp(verb, "reset")) { + mpu6000::reset(busid); + } + + /* + * Print driver information. + */ + if (!strcmp(verb, "info") || !strcmp(verb, "status")) { + mpu6000::info(busid); + } + + /* + * Print register information. + */ + if (!strcmp(verb, "regdump")) { + mpu6000::regdump(busid); + } + + if (!strcmp(verb, "factorytest")) { + mpu6000::factorytest(busid); + } + + if (!strcmp(verb, "testerror")) { + mpu6000::testerror(busid); + } + + mpu6000::usage(); + return -1; +} -- GitLab