From 71772cc3b3d2ca8a84368b26faed4d61b9891a8a Mon Sep 17 00:00:00 2001 From: Schorcht Date: Tue, 9 Oct 2018 16:55:15 +0200 Subject: [PATCH] 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