From 71772cc3b3d2ca8a84368b26faed4d61b9891a8a Mon Sep 17 00:00:00 2001 From: Schorcht Date: Tue, 9 Oct 2018 16:55:15 +0200 Subject: [PATCH 1/3] drivers: add driver for ITG320X 3-axis gyroscope --- drivers/Makefile.dep | 9 + drivers/Makefile.include | 4 + drivers/include/itg320x.h | 316 ++++++++++++++++++++ drivers/itg320x/Makefile | 3 + drivers/itg320x/include/itg320x_params.h | 106 +++++++ drivers/itg320x/include/itg320x_regs.h | 74 +++++ drivers/itg320x/itg320x.c | 357 +++++++++++++++++++++++ drivers/itg320x/itg320x_saul.c | 59 ++++ makefiles/pseudomodules.inc.mk | 3 + 9 files changed, 931 insertions(+) create mode 100644 drivers/include/itg320x.h create mode 100644 drivers/itg320x/Makefile create mode 100644 drivers/itg320x/include/itg320x_params.h create mode 100644 drivers/itg320x/include/itg320x_regs.h create mode 100644 drivers/itg320x/itg320x.c create mode 100644 drivers/itg320x/itg320x_saul.c diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 873bfc36c1..57a96b8f83 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -273,6 +273,15 @@ ifneq (,$(filter isl29125,$(USEMODULE))) FEATURES_REQUIRED += periph_i2c endif +ifneq (,$(filter itg320x_int,$(USEMODULE))) + USEMODULE += itg320x + FEATURES_REQUIRED += periph_gpio_irq +endif + +ifneq (,$(filter itg320x,$(USEMODULE))) + FEATURES_REQUIRED += periph_i2c +endif + ifneq (,$(filter jc42,$(USEMODULE))) FEATURES_REQUIRED += periph_i2c endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index d51ff35bd2..5c795ed47a 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -150,6 +150,10 @@ ifneq (,$(filter isl29125,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/isl29125/include endif +ifneq (,$(filter itg320x,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/itg320x/include +endif + ifneq (,$(filter jc42,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/jc42/include endif diff --git a/drivers/include/itg320x.h b/drivers/include/itg320x.h new file mode 100644 index 0000000000..70d7007a4e --- /dev/null +++ b/drivers/include/itg320x.h @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_itg320x ITG320X 3-axis gyroscope + * @ingroup drivers_sensors + * @ingroup drivers_saul + * @brief Device driver for InvenSense ITG320X 3-axis gyroscope + * + * The driver can be used with InvenSense ITG3200, ITG3205, and MPU3050. + * The latter one can be used only with reduced feature set. + * + * The driver implements polling mode as well as interrupt mode. Thus, + * the application may use two different approaches to retrieve new data, + * either + * + * - periodically fetching the data at a rate lower than the sensor's + * output data rate (ODR), or + * - fetching the data when the data-ready interrupt is triggered. + * + * To use the latter approach, module `itg320x_int` has to be enabled and the + * GPIO to which the sensor's **INT** output pin is connected has to be + * configured. + * + * This driver provides @ref drivers_saul capabilities. + * + * @{ + * + * @file + * @brief Device driver for InvenSense ITG320X 3-axis gyroscope + * + * @author Gunar Schorcht + */ + +#ifndef ITG320X_H +#define ITG320X_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "periph/gpio.h" +#include "periph/i2c.h" + +/** + * @name I2C addresses + * @{ + */ +#define ITG320X_I2C_ADDRESS_1 (0x68) /**< AD0 pin low */ +#define ITG320X_I2C_ADDRESS_2 (0x69) /**< AD0 pin high */ +/** @} */ + +/** ITG320X chip id defined in Who Am I */ +#define ITG320X_ID (0x68) + +/** + * @brief Named return values + */ +typedef enum { + ITG320X_OK = 0, /**< success */ + ITG320X_ERROR_I2C = -1, /**< I2C communication error */ + ITG320X_ERROR_WRONG_ID = -2, /**< wrong id read */ + ITG320X_ERROR_NO_DATA = -3, /**< no data are available */ + ITG320X_ERROR_RAW_DATA = -4, /**< reading raw data failed */ +} itg320x_error_codes_t; + +/** + * @brief Low pass filter bandwidth + * + * @note Low pass filter bandwidth determines the internal sample rate (ISR). + * The internal sample rate (ISR) together with sample rate divider + * (ISR_DIV) determines the output data rate ODR = ISR / (ISR_DIV + 1) + * where internal sample rate (ISR) is 8 kHz for #ITG320X_LPF_BW_256, + * or 1 kHz otherwise. + */ +typedef enum { + ITG320X_LPF_BW_256 = 0, /**< 256 Hz, ISR = 8 kHz */ + ITG320X_LPF_BW_188, /**< 188 Hz, ISR = 1 kHz */ + ITG320X_LPF_BW_98, /**< 98 Hz, ISR = 1 kHz */ + ITG320X_LPF_BW_42, /**< 42 Hz, ISR = 1 kHz */ + ITG320X_LPF_BW_20, /**< 20 Hz, ISR = 1 kHz */ + ITG320X_LPF_BW_10, /**< 10 Hz, ISR = 1 kHz */ + ITG320X_LPF_BW_5, /**< 5 Hz, ISR = 1 kHz (default) */ +} itg320x_lpf_bw_t; + +/** + * @brief Logic level for INT output pin (ITG320X_REG_INT_CFG<7>) + */ +typedef enum { + ITG320X_INT_HIGH = 0x00, /**< INT output is active high (default) */ + ITG320X_INT_LOW = 0x80, /**< INT output is active low */ +} itg320x_int_level_t; + +/** + * @brief Drive type for INT output pin (ITG320X_REG_INT_CFG<6>) + */ +typedef enum { + ITG320X_INT_PUSH_PULL = 0x00, /**< INT output is of type push/pull (default) */ + ITG320X_INT_OPEN_DRAIN = 0x40 /**< INT output is of type open drain */ +} itg320x_int_drive_t; + +/** + * @brief Clock source selection (ITG320X_REG_PWR_MGM<2:0>) + */ +typedef enum { + ITG320X_CLK_INTERNAL = 0, /**< Internal oscillator */ + ITG320X_CLK_PLL_X_GYRO, /**< PLL with X Gyro reference (default) */ + ITG320X_CLK_PLL_Y_GYRO, /**< PLL with X Gyro reference */ + ITG320X_CLK_PLL_Z_GYRO, /**< PLL with X Gyro reference */ + ITG320X_CLK_PLL_32K, /**< PLL with external 32.768 kHz reference */ + ITG320X_CLK_PLL_19M, /**< PLL with external 19.2 MHz reference */ +} itg320x_clk_sel_t; + +/** + * @brief Angular rate values in tenths of a degree per second + */ +typedef struct { + int16_t x; /**< angular rate x-axis (roll) */ + int16_t y; /**< angular rate y-axis (pitch) */ + int16_t z; /**< angular rate y-axis (yaw) */ +} itg320x_data_t; + +/** + * @brief Raw data set as two complements + * + * According to the sensor sensitivity of 1/14.375 degrees per second, the + * raw data values have to be divided by 14.375 to obtain the measurements + * in degrees per second. + */ +typedef struct { + int16_t x; /**< angular rate x-axis as 16 bit two's complements (roll) */ + int16_t y; /**< angular rate y-axis as 16 bit two's complements (pitch) */ + int16_t z; /**< angular rate z-axis as 16 bit two's complements (yaw) */ +} itg320x_raw_data_t; + +/** + * @brief ITG320X device initialization parameters + */ +typedef struct { + i2c_t dev; /**< I2C device (default I2C_DEV(0)) */ + uint8_t addr; /**< I2C slave address (default #ITG320X_I2C_ADDRESS_1) */ + + uint8_t isr_div; /**< Internal sample rate divider ISR_DIV (default 99) + ODR = ISR / (ISR_DIV + 1) where internal sample rate + is 1 kHz or 8 kHz dependent on the low pass filter + bandwidth #lpf_bw */ + + itg320x_lpf_bw_t lpf_bw; /**< Low pass filter bandwidth + (default #ITG320X_LPF_BW_5, ISR 1 kHz) */ + itg320x_clk_sel_t clk_sel; /**< Clock source selection + (default ITG320X_CLK_PLL_X_GYRO) */ + +#if MODULE_ITG320X_INT || DOXYGEN + gpio_t int_pin; /**< DRDY interrupt pin: #GPIO_UNDEF if + not used */ + itg320x_int_level_t int_level; /**< Logic level for INT output pin + (default #ITG320X_INT_LOW) */ + itg320x_int_drive_t int_drive; /**< Drive type for INT output pin + (default #ITG320X_INT_PUSH_PULL */ +#endif /* MODULE_ITG320X_INT || DOXYGEN */ +} itg320x_params_t; + +/** + * @brief ITG320X sensor device data structure type + */ +typedef struct { + itg320x_params_t params; /**< device initialization parameters */ +} itg320x_t; + +#if MODULE_ITG320X_INT || DOXYGEN +/** + * @brief ITG320X data ready interrupt (DRDY) callback function type + * + * Function prototype for the function which is called on DRDY interrupt if + * the interrupt is activated by #itg320x_init_int + * + * @note The @p cb function is called in interrupt context. The application + * should do nothing time consuming and not directly access sensor data. + */ +typedef void (*itg320x_drdy_int_cb_t)(void *); + +#endif /* MODULE_ITG320X_INT || DOXYGEN */ + +/** + * @brief Initialize the ITG320X sensor device + * + * This function resets the sensor and initializes the sensor according to + * given initialization parameters. All registers are reset to default values. + * + * @param[in] dev device descriptor of ITG320X sensor to be initialized + * @param[in] params ITG320X initialization parameters + * + * @retval ITG320X_OK on success + * @retval ITG320X_ERROR_* a negative error code on error, + * see #itg320x_error_codes_t + */ +int itg320x_init(itg320x_t *dev, const itg320x_params_t *params); + +#if MODULE_ITG320X_INT || DOXYGEN +/** + * @brief Initialize and activate the DRDY interrupt of ITG320X sensor device + * + * This function activates the DRDY interrupt and initializes the pin defined + * as the interrupt pin in the initialization parameters of the device. The + * @p cb parameter specifies the function, along with an optional argument + * @p arg, which is called when a DRDY interrupt is triggered. + * + * @warning The given callback function @p cb is executed in interrupt context. + * Make sure not to call any driver API function in that context. + * @note This function is only available when module `itg320x_int` is enabled. + * + * @param[in] dev device descriptor of ITG320X sensor + * @param[in] cb function called when DRDY interrupt is triggered + * @param[in] arg argument for the callback function + */ +int itg320x_init_int(const itg320x_t *dev, itg320x_drdy_int_cb_t cb, void *arg); + +#endif /* MODULE_ITG320X_INT || DOXYGEN */ + +/** + * @brief Data-ready status function + * + * The function checks the status register and returns + * + * @param[in] dev device descriptor of ITG320X sensor + * + * @retval ITG320X_OK new data available + * @retval ITG320X_ERROR_NO_DATA no new data available + * @retval ITG320X_ERROR_* negative error code, + * see #itg320x_error_codes_t + */ +int itg320x_data_ready(const itg320x_t *dev); + +/** + * @brief Read one sample of angular rates in tenths of a degree per second + * + * Raw magnetometer data are read from the sensor and normalized with + * respect to full scale +-2000 dps. Angular rate values are given in + * tenths of a degrees per second: + * + * @param[in] dev device descriptor of ITG320X sensor + * @param[out] data result vector in tenths of a degrees per second + * + * @retval ITG320X_OK on success + * @retval ITG320X_ERROR_* a negative error code on error, + * see #itg320x_error_codes_t + */ +int itg320x_read(const itg320x_t *dev, itg320x_data_t *data); + +/** + * @brief Read one sample of raw sensor data as 16 bit two's complements + * + * @param[in] dev device descriptor of ITG320X sensor + * @param[out] raw raw data vector + * + * @retval ITG320X_OK on success + * @retval ITG320X_ERROR_* a negative error code on error, + * see #itg320x_error_codes_t + */ +int itg320x_read_raw(const itg320x_t *dev, itg320x_raw_data_t *raw); + +/** + * @brief Read temperature in tenths of a degree Celsius + * + * @param[in] dev device descriptor of ITG320X sensor + * @param[out] temp temperature tenths of a degree Celsius + * + * @retval ITG320X_OK on success + * @retval ITG320X_ERROR_* a negative error code on error, + * see #itg320x_error_codes_t + */ +int itg320x_read_temp(const itg320x_t *dev, int16_t* temp); + +/** + * @brief Power down the sensor + * + * Changes the sensor operation mode to sleep mode in which almost all + * including the gyros are switched off. + * + * @param[in] dev Device descriptor of ITG320X device to read from + * + * @retval ITG320X_OK on success + * @retval ITG320X_ERROR_* a negative error code on error, + * see #itg320x_error_codes_t + */ +int itg320x_power_down(itg320x_t *dev); + +/** + * @brief Power up the sensor + * + * Swichtes the sensor back into active operation mode. It takes + * up to 20 ms since the gyros have to be switched on again. + * + * @param[in] dev Device descriptor of ITG320X device to read from + * + * @retval ITG320X_OK on success + * @retval ITG320X_ERROR_* a negative error code on error, + * see #itg320x_error_codes_t + */ +int itg320x_power_up(itg320x_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* ITG320X_H */ +/** @} */ diff --git a/drivers/itg320x/Makefile b/drivers/itg320x/Makefile new file mode 100644 index 0000000000..e0615b9bd7 --- /dev/null +++ b/drivers/itg320x/Makefile @@ -0,0 +1,3 @@ +MODULE = itg320x + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/itg320x/include/itg320x_params.h b/drivers/itg320x/include/itg320x_params.h new file mode 100644 index 0000000000..62ed2a57f5 --- /dev/null +++ b/drivers/itg320x/include/itg320x_params.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_itg320x + * @brief Default configuration for InvenSense ITG320X 3-axis gyroscope + * @author Gunar Schorcht + * @file + * @{ + */ + +#ifndef ITG320X_PARAMS_H +#define ITG320X_PARAMS_H + +#include "board.h" +#include "itg320x.h" +#include "saul_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters + * @{ + */ +#ifndef ITG320X_PARAM_DEV +#define ITG320X_PARAM_DEV I2C_DEV(0) +#endif +#ifndef ITG320X_PARAM_ADDR +#define ITG320X_PARAM_ADDR (ITG320X_I2C_ADDRESS_1) +#endif +#ifndef ITG320X_PARAM_LPF_BW +#define ITG320X_PARAM_LPF_BW (ITG320X_LPF_BW_5) +#endif +#ifndef ITG320X_PARAM_ISR_DIV +#define ITG320X_PARAM_ISR_DIV (99) +#endif +#ifndef ITG320X_PARAM_CLK_SEL +#define ITG320X_PARAM_CLK_SEL (ITG320X_CLK_PLL_X_GYRO) +#endif +#ifndef ITG320X_PARAM_INT_PIN +#define ITG320X_PARAM_INT_PIN (GPIO_UNDEF) +#endif +#ifndef ITG320X_PARAM_INT_LEVEL +#define ITG320X_PARAM_INT_LEVEL (ITG320X_INT_LOW) +#endif +#ifndef ITG320X_PARAM_INT_DRIVE +#define ITG320X_PARAM_INT_DRIVE (ITG320X_INT_PUSH_PULL) +#endif + +#ifndef ITG320X_PARAMS +#ifdef MODULE_ITG320X_INT +#define ITG320X_PARAMS { \ + .dev = ITG320X_PARAM_DEV, \ + .addr = ITG320X_PARAM_ADDR, \ + .lpf_bw = ITG320X_PARAM_LPF_BW, \ + .isr_div = ITG320X_PARAM_ISR_DIV, \ + .clk_sel = ITG320X_PARAM_CLK_SEL, \ + .int_pin = ITG320X_PARAM_INT_PIN, \ + .int_level = ITG320X_PARAM_INT_LEVEL, \ + .int_drive = ITG320X_PARAM_INT_DRIVE, \ + } +#else /* MODULE_ITG320X_INT */ +#define ITG320X_PARAMS { \ + .dev = ITG320X_PARAM_DEV, \ + .addr = ITG320X_PARAM_ADDR, \ + .lpf_bw = ITG320X_PARAM_LPF_BW, \ + .isr_div = ITG320X_PARAM_ISR_DIV, \ + .clk_sel = ITG320X_PARAM_CLK_SEL, \ + } +#endif /* MODULE_ITG320X_INT */ +#endif /* ITG320X_PARAMS */ + +#ifndef ITG320X_SAUL_INFO +#define ITG320X_SAUL_INFO { .name = "itg320x" } +#endif +/**@}*/ + +/** + * @brief Allocate some memory to store the actual configuration + */ +static const itg320x_params_t itg320x_params[] = +{ + ITG320X_PARAMS +}; + +/** + * @brief Additional meta information to keep in the SAUL registry + */ +static const saul_reg_info_t itg320x_saul_info[] = +{ + ITG320X_SAUL_INFO +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ITG320X_PARAMS_H */ +/** @} */ diff --git a/drivers/itg320x/include/itg320x_regs.h b/drivers/itg320x/include/itg320x_regs.h new file mode 100644 index 0000000000..67142d8981 --- /dev/null +++ b/drivers/itg320x/include/itg320x_regs.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_itg320x + * @brief Register definitions for InvenSense ITG320X 3-axis gyroscope + * @author Gunar Schorcht + * @file + * @{ + */ + +#ifndef ITG320X_REGS_H +#define ITG320X_REGS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @name Register addresses + * @{ + */ +#define ITG320X_REG_WHO_AM_I (0x00) +#define ITG320X_REG_SMPLRT_DIV (0x15) +#define ITG320X_REG_DLPFS (0x16) +#define ITG320X_REG_INT_CFG (0x17) +#define ITG320X_REG_INT_STATUS (0x1a) +#define ITG320X_REG_TEMP_OUT_H (0x1b) +#define ITG320X_REG_TEMP_OUT_L (0x1c) +#define ITG320X_REG_GYRO_XOUT_H (0x1d) +#define ITG320X_REG_GYRO_XOUT_L (0x1e) +#define ITG320X_REG_GYRO_YOUT_H (0x1f) +#define ITG320X_REG_GYRO_YOUT_L (0x20) +#define ITG320X_REG_GYRO_ZOUT_H (0x21) +#define ITG320X_REG_GYRO_ZOUT_L (0x22) +#define ITG320X_REG_PWR_MGM (0x3e) +/** @} */ + +/** + * @name Register structure definitions + * @{ + */ +#define ITG320X_REG_DLPFS_FS_SEL (0x18) /**< ITG320X_REG_DLPFS<4:3> */ +#define ITG320X_REG_DLPFS_FS_SEL_VAL (0x18) /**< ITG320X_REG_DLPFS<4:3> = 3 */ +#define ITG320X_REG_DLPFS_DLPF_CFG (0x07) /**< ITG320X_REG_DLPFS<2:0> */ + +#define ITG320X_REG_INT_CFG_ACTL (0x80) /**< ITG320X_REG_INT_CFG<7> */ +#define ITG320X_REG_INT_CFG_OPEN (0x40) /**< ITG320X_REG_INT_CFG<6> */ +#define ITG320X_REG_INT_CFG_LATCH_INT (0x20) /**< ITG320X_REG_INT_CFG<5> */ +#define ITG320X_REG_INT_CFG_ANY_RDY_CLR (0x10) /**< ITG320X_REG_INT_CFG<4> */ +#define ITG320X_REG_INT_CFG_ITG_RDY_EN (0x04) /**< ITG320X_REG_INT_CFG<2> */ +#define ITG320X_REG_INT_CFG_RAW_RDY_EN (0x01) /**< ITG320X_REG_INT_CFG<0> */ + +#define ITG320X_REG_INT_STATUS_ITG_RDY (0x04) /**< ITG320X_REG_INT_STATUS<2> */ +#define ITG320X_REG_INT_STATUS_RAW_RDY (0x01) /**< ITG320X_REG_INT_STATUS<0> */ + +#define ITG320X_REG_PWR_MGM_H_RESET (0x80) /**< ITG320X_REG_PWR_MGM<7> */ +#define ITG320X_REG_PWR_MGM_SLEEP (0x40) /**< ITG320X_REG_PWR_MGM<6> */ +#define ITG320X_REG_PWR_MGM_STBY_XG (0x20) /**< ITG320X_REG_PWR_MGM<5> */ +#define ITG320X_REG_PWR_MGM_STBY_YG (0x10) /**< ITG320X_REG_PWR_MGM<4> */ +#define ITG320X_REG_PWR_MGM_STBY_ZG (0x08) /**< ITG320X_REG_PWR_MGM<3> */ +#define ITG320X_REG_PWR_MGM_CLK_SEL (0x07) /**< ITG320X_REG_PWR_MGM<2:0> */ +/** @} */ +#ifdef __cplusplus +} +#endif + +#endif /* ITG320X_REGS_H */ diff --git a/drivers/itg320x/itg320x.c b/drivers/itg320x/itg320x.c new file mode 100644 index 0000000000..d562c08b3b --- /dev/null +++ b/drivers/itg320x/itg320x.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_itg320x + * @brief Device driver for the InvenSense ITG320X 3-axis gyroscope + * @author Gunar Schorcht + * @file + * @{ + */ + +#include +#include +#include + +#include "itg320x_regs.h" +#include "itg320x.h" + +#include "log.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG + +#define DEBUG_DEV(f, d, ...) \ + DEBUG("[itg320x] %s i2c dev=%d addr=%02x: " f "\n", \ + __func__, d->params.dev, d->params.addr, ## __VA_ARGS__); + +#else /* ENABLE_DEBUG */ + +#define DEBUG_DEV(f, d, ...) + +#endif /* ENABLE_DEBUG */ + +#define ERROR_DEV(f, d, ...) \ + do { \ + LOG_ERROR("[itg320x] %s i2c dev=%d addr=%02x: " f "\n", \ + __func__, d->params.dev, d->params.addr, ## __VA_ARGS__); \ + } while (0) + +#define EXEC_RET(f) \ + do { \ + int _r; \ + if ((_r = f) != ITG320X_OK) { \ + DEBUG("[itg320x] %s: error code %d\n", __func__, _r); \ + return _r; \ + } \ + } while (0) + +/** Forward declaration of functions for internal use */ + +static int _is_available(const itg320x_t *dev); +static int _reset(itg320x_t *dev); + +static int _reg_read(const itg320x_t *dev, uint8_t reg, uint8_t *data, uint16_t len); +static int _reg_write(const itg320x_t *dev, uint8_t reg, uint8_t data); +static int _update_reg(const itg320x_t *dev, uint8_t reg, uint8_t mask, uint8_t val); + +int itg320x_init(itg320x_t *dev, const itg320x_params_t *params) +{ + assert(dev != NULL); + assert(params != NULL); + + DEBUG_DEV("params=%p", dev, params); + + /* init sensor data structure */ + dev->params = *params; + + /* check availability of the sensor */ + EXEC_RET(_is_available(dev)); + + /* reset the sensor */ + EXEC_RET(_reset(dev)); + + /* set internal sample rate divider (ISR) from parameters */ + EXEC_RET(_reg_write(dev, ITG320X_REG_SMPLRT_DIV, params->isr_div)); + + /* set full scale always to +-2000 and LPF bandwidth from parameters */ + EXEC_RET(_reg_write(dev, ITG320X_REG_DLPFS, + params->lpf_bw | ITG320X_REG_DLPFS_FS_SEL_VAL)); + + /* set clock source selection from parameters */ + EXEC_RET(_reg_write(dev, ITG320X_REG_PWR_MGM, params->clk_sel)); + + return ITG320X_OK; +} + +#ifdef MODULE_ITG320X_INT + +int itg320x_init_int(const itg320x_t *dev, itg320x_drdy_int_cb_t cb, void *arg) +{ + assert(dev != NULL); + assert(dev->params.int_pin != GPIO_UNDEF); + + DEBUG_DEV("cb=%p, arg=%p", dev, cb, arg); + + if (dev->params.int_level == ITG320X_INT_HIGH) { + /* for high active interrupt signal (default) */ + gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_RISING, cb, 0); + } + else { + /* for low active interrupt signal (default) */ + gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_FALLING, cb, 0); + } + + /* + * Set interrupt configuration as following + * - Logic level and drive type used from parameters + * - Latching interrupt is enabled + * - Latch clear method is reading the status register + * - RAW data ready interrupt is enabled + */ + EXEC_RET(_reg_write(dev, ITG320X_REG_INT_CFG, + dev->params.int_level | dev->params.int_drive | + ITG320X_REG_INT_CFG_LATCH_INT | + ITG320X_REG_INT_STATUS_RAW_RDY | + ITG320X_REG_INT_CFG_RAW_RDY_EN)); + + return ITG320X_OK; +} + +#endif /* MODULE_ITG320X_INT */ + +int itg320x_data_ready(const itg320x_t *dev) +{ + assert(dev != NULL); + + DEBUG_DEV("", dev); + + uint8_t reg; + EXEC_RET(_reg_read(dev, ITG320X_REG_INT_STATUS, ®, 1)); + + return (reg & ITG320X_REG_INT_STATUS_RAW_RDY) ? ITG320X_OK + : ITG320X_ERROR_NO_DATA; +} + +int itg320x_read(const itg320x_t *dev, itg320x_data_t *data) +{ + assert(dev != NULL); + assert(data != NULL); + + DEBUG_DEV("data=%p", dev, data); + + itg320x_raw_data_t raw; + + EXEC_RET(itg320x_read_raw(dev, &raw)); + + /* + * The sensitivity of the sensor is 1/14.375 degree/seconds per LSB + * with a tolerance of the scale factor of +-6 %. Scale raw values to + * tenths of a degree/seconds according to sensors sensitivity + * which corresponds to 8/115 degrees/seconds per LSB. + */ + data->x = (uint32_t)(raw.x * 80 / 115); + data->y = (uint32_t)(raw.y * 80 / 115); + data->z = (uint32_t)(raw.z * 80 / 115); + + return ITG320X_OK; +} + +int itg320x_read_raw(const itg320x_t *dev, itg320x_raw_data_t *raw) +{ + assert(dev != NULL); + assert(raw != NULL); + + DEBUG_DEV("raw=%p", dev, raw); + + uint8_t data[6]; + + /* read raw data sample */ + EXEC_RET(_reg_read(dev, ITG320X_REG_GYRO_XOUT_H, data, 6)); + + /* data MSB @ lower address */ + raw->x = (data[0] << 8) | data[1]; + raw->y = (data[2] << 8) | data[3]; + raw->z = (data[4] << 8) | data[5]; + +#ifdef MODULE_ITG320X_INT + /* read status register to clear the interrupt */ + EXEC_RET(_reg_read(dev, ITG320X_REG_INT_STATUS, data, 1)); +#endif + + return ITG320X_OK; +} + +int itg320x_read_temp(const itg320x_t *dev, int16_t *temp) +{ + assert(dev != NULL); + assert(temp != NULL); + + DEBUG_DEV("temp=%p", dev, temp); + + uint8_t data[2]; + + /* read raw temperature */ + EXEC_RET(_reg_read(dev, ITG320X_REG_TEMP_OUT_H, data, 2)); + + /* data MSB @ lower address */ + *temp = (data[0] << 8) | data[1]; + /* convert raw temperature data to tenths of a degree Celsius */ + *temp = (*temp + 13200) / 28 + 350; + + return ITG320X_OK; +} + +int itg320x_power_down(itg320x_t *dev) +{ + assert(dev != NULL); + + DEBUG_DEV("", dev); + + return _update_reg(dev, ITG320X_REG_PWR_MGM, ITG320X_REG_PWR_MGM_SLEEP, 1); +} + +int itg320x_power_up(itg320x_t *dev) +{ + assert(dev != NULL); + + DEBUG_DEV("", dev); + + EXEC_RET(_update_reg(dev, ITG320X_REG_PWR_MGM, ITG320X_REG_PWR_MGM_SLEEP, 0)); + + /* wait 20 ms after power-up */ + xtimer_usleep(20 * US_PER_MS); + + return ITG320X_OK; +} + +/** Functions for internal use only */ + +static int _reset(itg320x_t *dev) +{ + assert(dev != NULL); + + DEBUG_DEV("", dev); + + /* set the reset flag, it automatically reset by the device */ + EXEC_RET(_update_reg(dev, ITG320X_REG_PWR_MGM, ITG320X_REG_PWR_MGM_H_RESET, 1)); + + /* wait 20 ms after reset */ + xtimer_usleep(20 * US_PER_MS); + + return ITG320X_OK; +} + +/** + * @brief Check the chip ID to test whether sensor is available + */ +static int _is_available(const itg320x_t *dev) +{ + assert(dev != NULL); + + DEBUG_DEV("", dev); + + uint8_t reg; + + /* read the chip id from ITG320X_REG_ID_X */ + EXEC_RET(_reg_read(dev, ITG320X_REG_WHO_AM_I, ®, 1)); + + if (reg != ITG320X_ID) { + DEBUG_DEV("sensor is not available, wrong id %02x, should be %02x", + dev, reg, ITG320X_ID); + return ITG320X_ERROR_WRONG_ID; + } + + return ITG320X_OK; +} + +static int _update_reg(const itg320x_t *dev, uint8_t reg, uint8_t mask, uint8_t val) +{ + assert(dev != NULL); + + DEBUG_DEV("reg=%02x mask=%02x val=%02x", dev, reg, mask, val); + + uint8_t reg_val; + uint8_t shift = 0; + + while (!((mask >> shift) & 0x01)) { + shift++; + } + + /* read current register value */ + EXEC_RET(_reg_read(dev, reg, ®_val, 1)); + + /* set masked bits to the given value */ + reg_val = (reg_val & ~mask) | ((val << shift) & mask); + + /* write back new register value */ + EXEC_RET(_reg_write(dev, reg, reg_val)); + + return ITG320X_OK; +} + +static int _reg_read(const itg320x_t *dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + assert(dev != NULL); + assert(data != NULL); + assert(len != 0); + + DEBUG_DEV("read %d bytes from reg 0x%02x", dev, len, reg); + + if (i2c_acquire(dev->params.dev) != 0) { + DEBUG_DEV("could not acquire the I2C bus", dev); + return ITG320X_ERROR_I2C; + } + + int res = i2c_read_regs(dev->params.dev, dev->params.addr, reg, data, len, 0); + i2c_release(dev->params.dev); + + if (res != 0) { + DEBUG_DEV("could not read %d bytes from sensor registers " + "starting at addr %02x, reason %d (%s)", + dev, len, reg, res, strerror(res * -1)); + return ITG320X_ERROR_I2C; + } + +#if ENABLE_DEBUG + printf("[itg320x] %s i2c dev=%d addr=%02x: read %d bytes from reg 0x%02x: ", + __func__, dev->params.dev, dev->params.addr, len, reg); + for (int i = 0; i < len; i++) { + printf("%02x ", data[i]); + } + printf("\n"); +#endif + + return ITG320X_OK; +} + +static int _reg_write(const itg320x_t *dev, uint8_t reg, uint8_t data) +{ + assert(dev != NULL); + + DEBUG_DEV("write 1 byte to reg 0x%02x: 0x%02x", dev, reg, data); + + if (i2c_acquire(dev->params.dev) != 0) { + DEBUG_DEV("could not acquire the I2C bus", dev); + return ITG320X_ERROR_I2C; + } + + int res = i2c_write_regs(dev->params.dev, dev->params.addr, reg, &data, 1, 0); + i2c_release(dev->params.dev); + + if (res != 0) { + DEBUG_DEV("could not write to sensor register 0x%02x, reason %d (%s)", + dev, reg, res, strerror(res * -1)); + return ITG320X_ERROR_I2C; + } + + return ITG320X_OK; +} diff --git a/drivers/itg320x/itg320x_saul.c b/drivers/itg320x/itg320x_saul.c new file mode 100644 index 0000000000..635139796d --- /dev/null +++ b/drivers/itg320x/itg320x_saul.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_itg320x + * @brief ITG320X adaption to the RIOT actuator/sensor interface + * @author Gunar Schorcht + * @file + */ + +#include + +#include "saul.h" +#include "itg320x.h" + +static int read_gyro(const void *dev, phydat_t *res) +{ + itg320x_data_t data; + int ret = itg320x_read((const itg320x_t *)dev, &data); + if (ret < 0) { + return -ECANCELED; + } + /* convert milli-dps to deci-dps */ + res->val[0] = data.x; + res->val[1] = data.y; + res->val[2] = data.z; + + res->unit = UNIT_DPS; + res->scale = -1; + return 3; +} + +static int read_temp(const void *dev, phydat_t *res) +{ + int ret = itg320x_read_temp((const itg320x_t *)dev, &res->val[0]); + if (ret < 0) { + return -ECANCELED; + } + res->unit = UNIT_TEMP_C; + res->scale = -1; + return 1; +} + +const saul_driver_t itg320x_saul_gyro_driver = { + .read = read_gyro, + .write = saul_notsup, + .type = SAUL_SENSE_GYRO, +}; + +const saul_driver_t itg320x_saul_temp_driver = { + .read = read_temp, + .write = saul_notsup, + .type = SAUL_SENSE_TEMP, +}; diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index e7f747a3ef..c8c8d6caa7 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -122,6 +122,9 @@ PSEUDOMODULES += cc1100 PSEUDOMODULES += cc1100e PSEUDOMODULES += cc1101 +# interrupt variant of the ITG320X driver as pseudo module +PSEUDOMODULES += itg320x_int + # include variants of MPU9X50 drivers as pseudo modules PSEUDOMODULES += mpu9150 PSEUDOMODULES += mpu9250 From 79884687b2c7df7ed296d1dc1b76d59b655aa3e9 Mon Sep 17 00:00:00 2001 From: Schorcht Date: Tue, 9 Oct 2018 16:55:34 +0200 Subject: [PATCH 2/3] sys/auto_init: add driver for ITG320X 3-axis gyroscope --- sys/auto_init/auto_init.c | 4 ++ sys/auto_init/saul/auto_init_itg320x.c | 79 ++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 sys/auto_init/saul/auto_init_itg320x.c diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index b72b856875..5b96279d90 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -451,6 +451,10 @@ void auto_init(void) extern void auto_init_isl29020(void); auto_init_isl29020(); #endif +#ifdef MODULE_ITG320X + extern void auto_init_itg320x(void); + auto_init_itg320x(); +#endif #ifdef MODULE_JC42 extern void auto_init_jc42(void); auto_init_jc42(); diff --git a/sys/auto_init/saul/auto_init_itg320x.c b/sys/auto_init/saul/auto_init_itg320x.c new file mode 100644 index 0000000000..e7cdb920ed --- /dev/null +++ b/sys/auto_init/saul/auto_init_itg320x.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_itg320x + * @ingroup sys_auto_init_saul + * @brief Auto initialization of InvenSense ITG320X 3-axis gyroscope + * @author Gunar Schorcht + * @file + * @{ + */ + +#ifdef MODULE_ITG320X + +#include "assert.h" +#include "log.h" +#include "saul_reg.h" +#include "itg320x.h" +#include "itg320x_params.h" + +/** + * @brief Define the number of configured sensors + */ +#define ITG320X_NUM (ARRAY_SIZE(itg320x_params)) + +/** + * @brief Allocate memory for the device descriptors + */ +static itg320x_t itg320x_devs[ITG320X_NUM]; + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[ITG320X_NUM * 2]; + +/** + * @brief Define the number of saul info + */ +#define ITG320X_INFO_NUM (ARRAY_SIZE(itg320x_saul_info)) + +/** + * @name Reference the driver structs + * @{ + */ +extern saul_driver_t itg320x_saul_gyro_driver; +extern saul_driver_t itg320x_saul_temp_driver; +/** @} */ + +void auto_init_itg320x(void) +{ + assert(ITG320X_NUM == ITG320X_INFO_NUM); + + for (unsigned int i = 0; i < ITG320X_NUM; i++) { + LOG_DEBUG("[auto_init_saul] initializing itg320x #%u\n", i); + + if (itg320x_init(&itg320x_devs[i], &itg320x_params[i]) < 0) { + LOG_ERROR("[auto_init_saul] error initializing itg320x #%u\n", i); + continue; + } + + saul_entries[(i * 2)].dev = &(itg320x_devs[i]); + saul_entries[(i * 2)].name = itg320x_saul_info[i].name; + saul_entries[(i * 2)].driver = &itg320x_saul_gyro_driver; + saul_entries[(i * 2) + 1].dev = &(itg320x_devs[i]); + saul_entries[(i * 2) + 1].name = itg320x_saul_info[i].name; + saul_entries[(i * 2) + 1].driver = &itg320x_saul_temp_driver; + saul_reg_add(&(saul_entries[(i * 2)])); + saul_reg_add(&(saul_entries[(i * 2) + 1])); + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_ITG320X */ From 00ec1f9e9e223065cf98617db58f76066bdd82ee Mon Sep 17 00:00:00 2001 From: Schorcht Date: Tue, 9 Oct 2018 16:56:12 +0200 Subject: [PATCH 3/3] tests: add driver for ITG320X 3-axis gyroscope --- tests/driver_itg320x/Makefile | 6 ++ tests/driver_itg320x/README.md | 29 +++++++++ tests/driver_itg320x/main.c | 112 +++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 tests/driver_itg320x/Makefile create mode 100644 tests/driver_itg320x/README.md create mode 100644 tests/driver_itg320x/main.c diff --git a/tests/driver_itg320x/Makefile b/tests/driver_itg320x/Makefile new file mode 100644 index 0000000000..3e972cddd7 --- /dev/null +++ b/tests/driver_itg320x/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.tests_common + +USEMODULE += itg320x +USEMODULE += xtimer + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_itg320x/README.md b/tests/driver_itg320x/README.md new file mode 100644 index 0000000000..19008d233b --- /dev/null +++ b/tests/driver_itg320x/README.md @@ -0,0 +1,29 @@ +# About + +This is a manual test application for the InvenSense ITG320X 3-axis gyroscope driver. + +# Usage + +The test application demonstrates the use of the ITG320X. It uses the +default configuration parameters: + +- Low pass filter bandwidth (LPF_BW) of 5 Hz (`ITG320X_LPF_BW_5`) +- Internal sample rate (ISR) of 1 kHz which results from LPF_BW of 5 Hz +- Internal sample rate divider (ISR_DIV) of 99 (`ITG320X_PARAM_ISR_DIV`) +- Output data rate (ODR) of 10 Hz resulting from ODR = ISR / (ISR_DIV + 1) + +The application may use two different approaches to retrieve new data, +either + +- periodically fetching the data at a rate lower than the sensor's + output data rate (ODR), or +- fetching the data when the data-ready interrupt is triggered. + +To use the latter approach, module `itg320x_int` has to be enabled and the +GPIO to which the sensor's **INT** output pin is connected has to be +defined by #ITG320X_PARAM_INT_PIN, for example: + +``` +USEMODULE=itg320x_int CFLAGS="-DITG320X_PARAM_INT_PIN=\(GPIO_PIN\(0,3\)\)" \ +make flash -C tests/driver_itg320x BOARD=... +``` diff --git a/tests/driver_itg320x/main.c b/tests/driver_itg320x/main.c new file mode 100644 index 0000000000..2770045fa6 --- /dev/null +++ b/tests/driver_itg320x/main.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @brief Test application for InvenSense ITG320X 3-axis gyroscope + * @author Gunar Schorcht + * @file + * + * The test application demonstrates the use of the ITG320X. It uses the + * default configuration parameters: + * + * - Low pass filter bandwidth (LPF_BW) of 5 Hz (#ITG320X_LPF_BW_5) + * - Internal sample rate (ISR) of 1 kHz which results from LPF_BW of 5 Hz + * - Internal sample rate divider (ISR_DIV) of 99 (#ITG320X_PARAM_ISR_DIV) + * - Output data rate (ODR) of 10 Hz resulting from + * + * ODR = ISR / (ISR_DIV + 1) + * + * The application may use two different approaches to retrieve new data, + * either + * + * - periodically fetching the data at a rate lower than the sensor's + * output data rate (ODR), or + * - fetching the data when the data-ready interrupt is triggered. + * + * To use the latter approach, module `itg320x_int` has to be enabled and the + * GPIO to which the sensor's **INT** output pin is connected has to be + * defined by #ITG320X_PARAM_INT_PIN, for example: + * + * ``` + * USEMODULE=itg320x_int CFLAGS="-DITG320X_PARAM_INT_PIN=\(GPIO_PIN\(0,3\)\)" \ + * make flash -C tests/driver_itg320x BOARD=... + * ``` +*/ + +#include + +#include "thread.h" +#include "xtimer.h" + +#include "itg320x.h" +#include "itg320x_params.h" + +#define ITG320X_SLEEP (50 * US_PER_MS) + +static kernel_pid_t p_main; + +#ifdef MODULE_ITG320X_INT +static void itg320x_isr_data_ready (void *arg) +{ + (void)arg; + /* send a message to trigger main thread to handle the interrupt */ + msg_t msg; + msg_send(&msg, p_main); +} +#endif + + +int main(void) +{ + itg320x_t dev; + + p_main = sched_active_pid; + + puts("ITG320X gyroscope driver test application\n"); + puts("Initializing ITG320X sensor"); + + /* initialize the sensor with default configuration parameters */ + if (itg320x_init(&dev, &itg320x_params[0]) == ITG320X_OK) { + puts("[OK]\n"); + } + else { + puts("[Failed]"); + return 1; + } + +#ifdef MODULE_ITG320X_INT + /* init interrupt */ + itg320x_init_int(&dev, itg320x_isr_data_ready, 0); +#endif + + while (1) { +#ifdef MODULE_ITG320X_INT + /* wait for data ready interrupt */ + msg_t msg; + msg_receive(&msg); +#else + while (1) { + /* wait longer than period of ITG320X DOR */ + xtimer_usleep(ITG320X_SLEEP); + if (itg320x_data_ready(&dev) == ITG320X_OK) { + break; + } + } +#endif + /* read and print data in any case */ + itg320x_data_t data; + if (itg320x_read(&dev, &data) == ITG320X_OK) { + printf("gyro [dps/10] x: " + "%+5" PRIi16 " y: %+5" PRIi16 " z: %+5" PRIi16 "\n", + data.x, data.y, data.z); + } + } + + return 0; +}