From f424421f794b20aab8dc4f6af271db2a53658bc6 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 1 Apr 2019 16:50:28 +0200 Subject: [PATCH 1/4] drivers: Renamed ina220 --> ina2xx The INA219 has the exact same interface as the INA220 (including values and semantics of the configuration register). Thus, this driver can be used for both. The ina220 has been renamed to ina2xx to reflect this and pseudo modules for the ina220 and ina219 have been added. --- drivers/Makefile.dep | 6 +- drivers/Makefile.include | 4 +- drivers/ina220/include/ina220-regs.h | 50 ------ drivers/{ina220 => ina2xx}/Makefile | 2 +- drivers/{ina220/ina220.c => ina2xx/ina2xx.c} | 42 ++--- drivers/ina2xx/include/ina2xx-regs.h | 49 ++++++ drivers/include/{ina220.h => ina2xx.h} | 152 +++++++++--------- makefiles/pseudomodules.inc.mk | 4 + .../{driver_ina220 => driver_ina2xx}/Makefile | 8 +- .../README.md | 3 +- tests/{driver_ina220 => driver_ina2xx}/main.c | 40 ++--- 11 files changed, 186 insertions(+), 174 deletions(-) delete mode 100644 drivers/ina220/include/ina220-regs.h rename drivers/{ina220 => ina2xx}/Makefile (68%) rename drivers/{ina220/ina220.c => ina2xx/ina2xx.c} (55%) create mode 100644 drivers/ina2xx/include/ina2xx-regs.h rename drivers/include/{ina220.h => ina2xx.h} (55%) rename tests/{driver_ina220 => driver_ina2xx}/Makefile (55%) rename tests/{driver_ina220 => driver_ina2xx}/README.md (75%) rename tests/{driver_ina220 => driver_ina2xx}/main.c (65%) diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 8a424566d7..e3b212373e 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -219,7 +219,11 @@ ifneq (,$(filter hts221,$(USEMODULE))) FEATURES_REQUIRED += periph_i2c endif -ifneq (,$(filter ina220,$(USEMODULE))) +ifneq (,$(filter ina2%,$(USEMODULE))) + USEMODULE += ina2xx +endif + +ifneq (,$(filter ina2xx,$(USEMODULE))) FEATURES_REQUIRED += periph_i2c endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index e6b5037cf3..974dba51f1 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -114,8 +114,8 @@ ifneq (,$(filter hts221,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/hts221/include endif -ifneq (,$(filter ina220,$(USEMODULE))) - USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ina220/include +ifneq (,$(filter ina2xx,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ina2xx/include endif ifneq (,$(filter ina3221,$(USEMODULE))) diff --git a/drivers/ina220/include/ina220-regs.h b/drivers/ina220/include/ina220-regs.h deleted file mode 100644 index b9cbab740c..0000000000 --- a/drivers/ina220/include/ina220-regs.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2015 Eistec AB - * - * 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_ina220 - * @{ - * - * @file - * @brief Register definitions for Texas Instruments INA220 High or Low - * Side, Bi-Directional CURRENT/POWER MONITOR with Two-Wire - * Interface - * - * @author Joakim Nohlgård - */ - -#ifndef INA220_REGS_H -#define INA220_REGS_H - - -#ifdef __cplusplus - extern "C" { -#endif - - -/** - * @brief INA220 register addresses - * - * All registers in the INA220 are 16 bit wide and transmitted MSB first. - */ -typedef enum ina220_reg { - INA220_REG_CONFIGURATION = 0x00, /**< Configuration register (read/write) */ - INA220_REG_SHUNT_VOLTAGE = 0x01, /**< Shunt voltage register (read only) */ - INA220_REG_BUS_VOLTAGE = 0x02, /**< Bus voltage register (read only) */ - INA220_REG_POWER = 0x03, /**< Power register (read only) */ - INA220_REG_CURRENT = 0x04, /**< Current register (read only) */ - INA220_REG_CALIBRATION = 0x05, /**< Calibration register (read/write) */ -} ina220_reg_t; - - -#ifdef __cplusplus -} -#endif - -#endif /* INA220_REGS_H */ -/** @} */ diff --git a/drivers/ina220/Makefile b/drivers/ina2xx/Makefile similarity index 68% rename from drivers/ina220/Makefile rename to drivers/ina2xx/Makefile index f0f0efca36..661721f916 100644 --- a/drivers/ina220/Makefile +++ b/drivers/ina2xx/Makefile @@ -1,3 +1,3 @@ -MODULE = ina220 +MODULE = ina2xx include $(RIOTBASE)/Makefile.base diff --git a/drivers/ina220/ina220.c b/drivers/ina2xx/ina2xx.c similarity index 55% rename from drivers/ina220/ina220.c rename to drivers/ina2xx/ina2xx.c index bb9134895e..4ac1830fbc 100644 --- a/drivers/ina220/ina220.c +++ b/drivers/ina2xx/ina2xx.c @@ -7,11 +7,11 @@ */ /** - * @ingroup drivers_ina220 + * @ingroup drivers_ina2xx * @{ * * @file - * @brief Device driver implementation for Texas Instruments INA220 High + * @brief Device driver implementation for Texas Instruments INA2XX High * or Low Side, Bi-Directional CURRENT/POWER MONITOR with Two-Wire * Interface * @@ -22,16 +22,16 @@ #include -#include "ina220.h" -#include "ina220-regs.h" +#include "ina2xx.h" +#include "ina2xx-regs.h" #include "periph/i2c.h" #include "byteorder.h" #define ENABLE_DEBUG (0) #include "debug.h" -/** @brief Read one 16 bit register from a INA220 device and swap byte order, if necessary. */ -static int ina220_read_reg(const ina220_t *dev, uint8_t reg, uint16_t *out) +/** @brief Read one 16 bit register from a INA2XX device and swap byte order, if necessary. */ +static int ina2xx_read_reg(const ina2xx_t *dev, uint8_t reg, uint16_t *out) { union { uint8_t c[2]; @@ -49,8 +49,8 @@ static int ina220_read_reg(const ina220_t *dev, uint8_t reg, uint16_t *out) return 0; } -/** @brief Write one 16 bit register to a INA220 device and swap byte order, if necessary. */ -static int ina220_write_reg(const ina220_t *dev, uint8_t reg, uint16_t in) +/** @brief Write one 16 bit register to a INA2XX device and swap byte order, if necessary. */ +static int ina2xx_write_reg(const ina2xx_t *dev, uint8_t reg, uint16_t in) { union { uint8_t c[2]; @@ -70,7 +70,7 @@ static int ina220_write_reg(const ina220_t *dev, uint8_t reg, uint16_t in) } -int ina220_init(ina220_t *dev, i2c_t i2c, uint8_t address) +int ina2xx_init(ina2xx_t *dev, i2c_t i2c, uint8_t address) { /* write device descriptor */ dev->i2c = i2c; @@ -78,32 +78,32 @@ int ina220_init(ina220_t *dev, i2c_t i2c, uint8_t address) return 0; } -int ina220_set_calibration(const ina220_t *dev, uint16_t calibration) +int ina2xx_set_calibration(const ina2xx_t *dev, uint16_t calibration) { - return ina220_write_reg(dev, INA220_REG_CALIBRATION, calibration); + return ina2xx_write_reg(dev, INA2XX_REG_CALIBRATION, calibration); } -int ina220_set_config(const ina220_t *dev, uint16_t config) +int ina2xx_set_config(const ina2xx_t *dev, uint16_t config) { - return ina220_write_reg(dev, INA220_REG_CONFIGURATION, config); + return ina2xx_write_reg(dev, INA2XX_REG_CONFIGURATION, config); } -int ina220_read_shunt(const ina220_t *dev, int16_t *voltage) +int ina2xx_read_shunt(const ina2xx_t *dev, int16_t *voltage) { - return ina220_read_reg(dev, INA220_REG_SHUNT_VOLTAGE, (uint16_t *)voltage); + return ina2xx_read_reg(dev, INA2XX_REG_SHUNT_VOLTAGE, (uint16_t *)voltage); } -int ina220_read_bus(const ina220_t *dev, int16_t *voltage) +int ina2xx_read_bus(const ina2xx_t *dev, int16_t *voltage) { - return ina220_read_reg(dev, INA220_REG_BUS_VOLTAGE, (uint16_t *)voltage); + return ina2xx_read_reg(dev, INA2XX_REG_BUS_VOLTAGE, (uint16_t *)voltage); } -int ina220_read_current(const ina220_t *dev, int16_t *current) +int ina2xx_read_current(const ina2xx_t *dev, int16_t *current) { - return ina220_read_reg(dev, INA220_REG_CURRENT, (uint16_t *)current); + return ina2xx_read_reg(dev, INA2XX_REG_CURRENT, (uint16_t *)current); } -int ina220_read_power(const ina220_t *dev, int16_t *power) +int ina2xx_read_power(const ina2xx_t *dev, int16_t *power) { - return ina220_read_reg(dev, INA220_REG_POWER, (uint16_t *)power); + return ina2xx_read_reg(dev, INA2XX_REG_POWER, (uint16_t *)power); } diff --git a/drivers/ina2xx/include/ina2xx-regs.h b/drivers/ina2xx/include/ina2xx-regs.h new file mode 100644 index 0000000000..d064d9d61c --- /dev/null +++ b/drivers/ina2xx/include/ina2xx-regs.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Eistec AB + * + * 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_ina2xx + * @{ + * + * @file + * @brief Register definitions for Texas Instruments INA219/INA220 + * Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface + * + * @author Joakim Nohlgård + */ + +#ifndef INA2XX_REGS_H +#define INA2XX_REGS_H + + +#ifdef __cplusplus + extern "C" { +#endif + + +/** + * @brief INA2XX register addresses + * + * All registers in the INA2XX are 16 bit wide and transmitted MSB first. + */ +typedef enum ina2xx_reg { + INA2XX_REG_CONFIGURATION = 0x00, /**< Configuration register (read/write) */ + INA2XX_REG_SHUNT_VOLTAGE = 0x01, /**< Shunt voltage register (read only) */ + INA2XX_REG_BUS_VOLTAGE = 0x02, /**< Bus voltage register (read only) */ + INA2XX_REG_POWER = 0x03, /**< Power register (read only) */ + INA2XX_REG_CURRENT = 0x04, /**< Current register (read only) */ + INA2XX_REG_CALIBRATION = 0x05, /**< Calibration register (read/write) */ +} ina2xx_reg_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* INA2XX_REGS_H */ +/** @} */ diff --git a/drivers/include/ina220.h b/drivers/include/ina2xx.h similarity index 55% rename from drivers/include/ina220.h rename to drivers/include/ina2xx.h index 803e84d883..8b774ddd1a 100644 --- a/drivers/include/ina220.h +++ b/drivers/include/ina2xx.h @@ -7,22 +7,26 @@ */ /** - * @defgroup drivers_ina220 INA220 current/power monitor + * @defgroup drivers_ina2xx INA2XX current/power monitor * @ingroup drivers_sensors - * @brief Device driver for Texas Instruments INA220 High or Low Side, + * @brief Device driver for Texas Instruments INA219/INA220 * Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface + * + * # Supported Hardware + * This driver currently supports the INA219 and the INA220 - and possibly + * other devices of the INA2xx family. + * * @{ * * @file - * @brief Device driver interface for Texas Instruments INA220 High or Low - * Side, Bi-Directional CURRENT/POWER MONITOR with Two-Wire - * Interface + * @brief Device driver interface for Texas Instruments INA219/INA220 + * Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface * * @author Joakim Nohlgård */ -#ifndef INA220_H -#define INA220_H +#ifndef INA2XX_H +#define INA2XX_H #include @@ -33,114 +37,114 @@ extern "C" { #endif /** - * @brief Device descriptor for INA220 sensors + * @brief Device descriptor for INA2XX sensors */ typedef struct { i2c_t i2c; /**< I2C device the sensor is connected to */ uint8_t addr; /**< the slave address of the sensor on the I2C bus */ -} ina220_t; +} ina2xx_t; /** - * @brief INA220 possible mode settings + * @brief INA2XX possible mode settings */ -typedef enum ina220_mode { - INA220_MODE_POWERDOWN = 0x0000, /**< Power down */ - INA220_MODE_TRIGGER_SHUNT_ONLY = 0x0001, /**< Shunt Voltage, Triggered */ - INA220_MODE_TRIGGER_BUS_ONLY = 0x0002, /**< Bus Voltage, Triggered */ - INA220_MODE_TRIGGER_SHUNT_BUS = 0x0003, /**< Shunt and Bus, Triggered */ - INA220_MODE_ADC_DISABLE = 0x0004, /**< ADC Off (disabled) */ - INA220_MODE_CONTINUOUS_SHUNT_ONLY = 0x0005, /**< Shunt Voltage, Continuous */ - INA220_MODE_CONTINUOUS_BUS_ONLY = 0x0006, /**< Bus Voltage, Continuous */ - INA220_MODE_CONTINUOUS_SHUNT_BUS = 0x0007, /**< Shunt and Bus, Continuous, default */ -} ina220_mode_t; +typedef enum ina2xx_mode { + INA2XX_MODE_POWERDOWN = 0x0000, /**< Power down */ + INA2XX_MODE_TRIGGER_SHUNT_ONLY = 0x0001, /**< Shunt Voltage, Triggered */ + INA2XX_MODE_TRIGGER_BUS_ONLY = 0x0002, /**< Bus Voltage, Triggered */ + INA2XX_MODE_TRIGGER_SHUNT_BUS = 0x0003, /**< Shunt and Bus, Triggered */ + INA2XX_MODE_ADC_DISABLE = 0x0004, /**< ADC Off (disabled) */ + INA2XX_MODE_CONTINUOUS_SHUNT_ONLY = 0x0005, /**< Shunt Voltage, Continuous */ + INA2XX_MODE_CONTINUOUS_BUS_ONLY = 0x0006, /**< Bus Voltage, Continuous */ + INA2XX_MODE_CONTINUOUS_SHUNT_BUS = 0x0007, /**< Shunt and Bus, Continuous, default */ +} ina2xx_mode_t; /** * @brief Shunt voltage measurement range (PGA settings) */ -typedef enum ina220_range { - INA220_RANGE_40MV = 0x0000, /**< +/- 40 mV range */ - INA220_RANGE_80MV = 0x0800, /**< +/- 80 mV range */ - INA220_RANGE_160MV = 0x1000, /**< +/- 160 mV range */ - INA220_RANGE_320MV = 0x1800, /**< +/- 320 mV range, default */ -} ina220_range_t; +typedef enum ina2xx_range { + INA2XX_RANGE_40MV = 0x0000, /**< +/- 40 mV range */ + INA2XX_RANGE_80MV = 0x0800, /**< +/- 80 mV range */ + INA2XX_RANGE_160MV = 0x1000, /**< +/- 160 mV range */ + INA2XX_RANGE_320MV = 0x1800, /**< +/- 320 mV range, default */ +} ina2xx_range_t; /** * @brief Bus voltage measurement range */ -typedef enum ina220_brng { - INA220_BRNG_16V_FSR = 0x0000, /**< 16 V bus voltage full scale range */ - INA220_BRNG_32V_FSR = 0x2000, /**< 32 V bus voltage full scale range, default. */ -} ina220_brng_t; +typedef enum ina2xx_brng { + INA2XX_BRNG_16V_FSR = 0x0000, /**< 16 V bus voltage full scale range */ + INA2XX_BRNG_32V_FSR = 0x2000, /**< 32 V bus voltage full scale range, default. */ +} ina2xx_brng_t; /** * @brief Shunt ADC settings * - * @see Table 4 in INA220 data sheet + * @see Table 4 in INA2XX data sheet */ -typedef enum ina220_sadc { +typedef enum ina2xx_sadc { /** 9 bit resolution, 84 us conversion time */ - INA220_SADC_9BIT = 0x0000, + INA2XX_SADC_9BIT = 0x0000, /** 10 bit resolution, 148 us conversion time */ - INA220_SADC_10BIT = 0x0008, + INA2XX_SADC_10BIT = 0x0008, /** 11 bit resolution, 276 us conversion time */ - INA220_SADC_11BIT = 0x0010, + INA2XX_SADC_11BIT = 0x0010, /** 12 bit resolution, 532 us conversion time, default */ - INA220_SADC_12BIT = 0x0018, - /** 12 bit resolution, 532 us conversion time, same as INA220_SADC_12BIT */ - INA220_SADC_AVG_1_SAMPLE = 0x0040, + INA2XX_SADC_12BIT = 0x0018, + /** 12 bit resolution, 532 us conversion time, same as INA2XX_SADC_12BIT */ + INA2XX_SADC_AVG_1_SAMPLE = 0x0040, /** 2 sample average, 1.06 ms conversion time */ - INA220_SADC_AVG_2_SAMPLES = 0x0048, + INA2XX_SADC_AVG_2_SAMPLES = 0x0048, /** 4 sample average, 2.13 ms conversion time */ - INA220_SADC_AVG_4_SAMPLES = 0x0050, + INA2XX_SADC_AVG_4_SAMPLES = 0x0050, /** 8 sample average, 4.26 ms conversion time */ - INA220_SADC_AVG_8_SAMPLES = 0x0058, + INA2XX_SADC_AVG_8_SAMPLES = 0x0058, /** 16 sample average, 8.51 ms conversion time */ - INA220_SADC_AVG_16_SAMPLES = 0x0060, + INA2XX_SADC_AVG_16_SAMPLES = 0x0060, /** 32 sample average, 17.02 ms conversion time */ - INA220_SADC_AVG_32_SAMPLES = 0x0068, + INA2XX_SADC_AVG_32_SAMPLES = 0x0068, /** 64 sample average, 34.05 ms conversion time */ - INA220_SADC_AVG_64_SAMPLES = 0x0070, + INA2XX_SADC_AVG_64_SAMPLES = 0x0070, /** 128 sample average, 68.10 ms conversion time */ - INA220_SADC_AVG_128_SAMPLES = 0x0078, -} ina220_sadc_t; + INA2XX_SADC_AVG_128_SAMPLES = 0x0078, +} ina2xx_sadc_t; /** * @brief Bus ADC settings * - * @see Table 4 in INA220 data sheet + * @see Table 4 in INA2XX data sheet */ -typedef enum ina220_badc { +typedef enum ina2xx_badc { /** 9 bit resolution, 84 us conversion time */ - INA220_BADC_9BIT = 0x0000, + INA2XX_BADC_9BIT = 0x0000, /** 10 bit resolution, 148 us conversion time */ - INA220_BADC_10BIT = 0x0080, + INA2XX_BADC_10BIT = 0x0080, /** 11 bit resolution, 276 us conversion time */ - INA220_BADC_11BIT = 0x0100, + INA2XX_BADC_11BIT = 0x0100, /** 12 bit resolution, 532 us conversion time, default */ - INA220_BADC_12BIT = 0x0180, - /** 12 bit resolution, 532 us conversion time, same as INA220_BADC_12BIT */ - INA220_BADC_AVG_1_SAMPLE = 0x0400, + INA2XX_BADC_12BIT = 0x0180, + /** 12 bit resolution, 532 us conversion time, same as INA2XX_BADC_12BIT */ + INA2XX_BADC_AVG_1_SAMPLE = 0x0400, /** 2 sample average, 1.06 ms conversion time */ - INA220_BADC_AVG_2_SAMPLES = 0x0480, + INA2XX_BADC_AVG_2_SAMPLES = 0x0480, /** 4 sample average, 2.13 ms conversion time */ - INA220_BADC_AVG_4_SAMPLES = 0x0500, + INA2XX_BADC_AVG_4_SAMPLES = 0x0500, /** 8 sample average, 4.26 ms conversion time */ - INA220_BADC_AVG_8_SAMPLES = 0x0580, + INA2XX_BADC_AVG_8_SAMPLES = 0x0580, /** 16 sample average, 8.51 ms conversion time */ - INA220_BADC_AVG_16_SAMPLES = 0x0600, + INA2XX_BADC_AVG_16_SAMPLES = 0x0600, /** 32 sample average, 17.02 ms conversion time */ - INA220_BADC_AVG_32_SAMPLES = 0x0680, + INA2XX_BADC_AVG_32_SAMPLES = 0x0680, /** 64 sample average, 34.05 ms conversion time */ - INA220_BADC_AVG_64_SAMPLES = 0x0700, + INA2XX_BADC_AVG_64_SAMPLES = 0x0700, /** 128 sample average, 68.10 ms conversion time */ - INA220_BADC_AVG_128_SAMPLES = 0x0780, -} ina220_badc_t; + INA2XX_BADC_AVG_128_SAMPLES = 0x0780, +} ina2xx_badc_t; -/** INA220 reset command bit (in configuration register) */ -#define INA220_RESET_BIT (0x8000) +/** INA2XX reset command bit (in configuration register) */ +#define INA2XX_RESET_BIT (0x8000) -/** Location of the bus voltage in the INA220 bus voltage register */ -#define INA220_BUS_VOLTAGE_SHIFT (3) +/** Location of the bus voltage in the INA2XX bus voltage register */ +#define INA2XX_BUS_VOLTAGE_SHIFT (3) /** * @brief Initialize a current sensor @@ -152,7 +156,7 @@ typedef enum ina220_badc { * @return 0 on success * @return <0 on error */ -int ina220_init(ina220_t *dev, i2c_t i2c, uint8_t address); +int ina2xx_init(ina2xx_t *dev, i2c_t i2c, uint8_t address); /** * @brief Write to calibration register @@ -163,7 +167,7 @@ int ina220_init(ina220_t *dev, i2c_t i2c, uint8_t address); * @return 0 on success * @return <0 on error */ -int ina220_set_calibration(const ina220_t *dev, uint16_t calibration); +int ina2xx_set_calibration(const ina2xx_t *dev, uint16_t calibration); /** * @brief Write to configuration register @@ -174,7 +178,7 @@ int ina220_set_calibration(const ina220_t *dev, uint16_t calibration); * @return 0 on success * @return <0 on error */ -int ina220_set_config(const ina220_t *dev, uint16_t config); +int ina2xx_set_config(const ina2xx_t *dev, uint16_t config); /** * @brief Read shunt voltage @@ -185,7 +189,7 @@ int ina220_set_config(const ina220_t *dev, uint16_t config); * @return 0 on success * @return <0 on error */ -int ina220_read_shunt(const ina220_t *dev, int16_t *voltage); +int ina2xx_read_shunt(const ina2xx_t *dev, int16_t *voltage); /** * @brief Read bus voltage register @@ -201,7 +205,7 @@ int ina220_read_shunt(const ina220_t *dev, int16_t *voltage); * @return 0 on success * @return <0 on error */ -int ina220_read_bus(const ina220_t *dev, int16_t *voltage); +int ina2xx_read_bus(const ina2xx_t *dev, int16_t *voltage); /** * @brief Read shunt current @@ -212,7 +216,7 @@ int ina220_read_bus(const ina220_t *dev, int16_t *voltage); * @return 0 on success * @return <0 on error */ -int ina220_read_current(const ina220_t *dev, int16_t *current); +int ina2xx_read_current(const ina2xx_t *dev, int16_t *current); /** * @brief Read power consumption @@ -223,11 +227,11 @@ int ina220_read_current(const ina220_t *dev, int16_t *current); * @return 0 on success * @return <0 on error */ -int ina220_read_power(const ina220_t *dev, int16_t *power); +int ina2xx_read_power(const ina2xx_t *dev, int16_t *power); #ifdef __cplusplus } #endif -#endif /* INA220_H */ +#endif /* INA2XX_H */ /** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index f85d219b6a..e85a8f33b5 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -124,6 +124,10 @@ PSEUDOMODULES += cc1101 PSEUDOMODULES += mpu9150 PSEUDOMODULES += mpu9250 +# include variants of INA2xx drivers as pseudo modules +PSEUDOMODULES += ina219 +PSEUDOMODULES += ina220 + # include variants of mrf24j40 drivers as pseudo modules PSEUDOMODULES += mrf24j40m% diff --git a/tests/driver_ina220/Makefile b/tests/driver_ina2xx/Makefile similarity index 55% rename from tests/driver_ina220/Makefile rename to tests/driver_ina2xx/Makefile index 276de9c03a..0321a1a38a 100644 --- a/tests/driver_ina220/Makefile +++ b/tests/driver_ina2xx/Makefile @@ -4,11 +4,11 @@ USEMODULE += ina220 USEMODULE += xtimer # set default device parameters in case they are undefined -TEST_INA220_I2C ?= I2C_DEV\(0\) -TEST_INA220_ADDR ?= 0x40 +TEST_INA2XX_I2C ?= I2C_DEV\(0\) +TEST_INA2XX_ADDR ?= 0x40 # export parameters -CFLAGS += -DTEST_INA220_I2C=$(TEST_INA220_I2C) -CFLAGS += -DTEST_INA220_ADDR=$(TEST_INA220_ADDR) +CFLAGS += -DTEST_INA2XX_I2C=$(TEST_INA2XX_I2C) +CFLAGS += -DTEST_INA2XX_ADDR=$(TEST_INA2XX_ADDR) include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_ina220/README.md b/tests/driver_ina2xx/README.md similarity index 75% rename from tests/driver_ina220/README.md rename to tests/driver_ina2xx/README.md index e922443dcd..c352947b97 100644 --- a/tests/driver_ina220/README.md +++ b/tests/driver_ina2xx/README.md @@ -1,5 +1,6 @@ # About -This is a manual test application for the INA220 current and power monitor driver. +This is a manual test application for the INA210/INA220 current and power +monitor driver. # Usage This test application will initialize the sensor with the following parameters: diff --git a/tests/driver_ina220/main.c b/tests/driver_ina2xx/main.c similarity index 65% rename from tests/driver_ina220/main.c rename to tests/driver_ina2xx/main.c index 74252d4aba..0d048a6fe7 100644 --- a/tests/driver_ina220/main.c +++ b/tests/driver_ina2xx/main.c @@ -11,24 +11,24 @@ * @{ * * @file - * @brief Test application for the INA220 sensor driver + * @brief Test application for the INA2XX sensor driver * * @author Joakim Nohlgård * * @} */ -#ifndef TEST_INA220_I2C -#error "TEST_INA220_I2C not defined" +#ifndef TEST_INA2XX_I2C +#error "TEST_INA2XX_I2C not defined" #endif -#ifndef TEST_INA220_ADDR -#error "TEST_INA220_ADDR not defined" +#ifndef TEST_INA2XX_ADDR +#error "TEST_INA2XX_ADDR not defined" #endif #include #include "xtimer.h" -#include "ina220.h" +#include "ina2xx.h" /* Use the following configuration: * @@ -37,28 +37,28 @@ * - 32 V maximum bus voltage * - 12 bit ADC resolution, no hardware averaging */ -#define CONFIG (INA220_MODE_CONTINUOUS_SHUNT_BUS | INA220_RANGE_320MV | \ - INA220_BRNG_32V_FSR | INA220_SADC_12BIT | INA220_BADC_12BIT) +#define CONFIG (INA2XX_MODE_CONTINUOUS_SHUNT_BUS | INA2XX_RANGE_320MV | \ + INA2XX_BRNG_32V_FSR | INA2XX_SADC_12BIT | INA2XX_BADC_12BIT) #define CALIBRATION (4096) #define SLEEP_USEC (100 * 1000U) int main(void) { - ina220_t dev; + ina2xx_t dev; int16_t val; - puts("INA220 sensor driver test application\n"); + puts("INA2XX sensor driver test application\n"); - printf("Initializing INA220 sensor at I2C_%i, address 0x%02x... ", - TEST_INA220_I2C, TEST_INA220_ADDR); - if (ina220_init(&dev, TEST_INA220_I2C, TEST_INA220_ADDR) == 0) { + printf("Initializing INA2XX sensor at I2C_%i, address 0x%02x... ", + TEST_INA2XX_I2C, TEST_INA2XX_ADDR); + if (ina2xx_init(&dev, TEST_INA2XX_I2C, TEST_INA2XX_ADDR) == 0) { puts("[OK]\n"); } else { puts("[Failed]"); return 1; } puts("Set configuration register"); - if (ina220_set_config(&dev, CONFIG) == 0) { + if (ina2xx_set_config(&dev, CONFIG) == 0) { puts("[OK]\n"); } else { puts("[Failed]"); @@ -66,7 +66,7 @@ int main(void) } puts("Set calibration register"); - if (ina220_set_calibration(&dev, CALIBRATION) == 0) { + if (ina2xx_set_calibration(&dev, CALIBRATION) == 0) { puts("[OK]\n"); } else { puts("[Failed]"); @@ -75,24 +75,24 @@ int main(void) while (1) { /* Read shunt resistor voltage, in millivolts */ - ina220_read_shunt(&dev, &val); + ina2xx_read_shunt(&dev, &val); printf("shunt: %6d", val); /* Read VBUS voltage, in millivolts */ - ina220_read_bus(&dev, &val); + ina2xx_read_bus(&dev, &val); /* The bus voltage is found in the topmost 13 bits of the bus voltage * register */ - val = (val >> INA220_BUS_VOLTAGE_SHIFT); + val = (val >> INA2XX_BUS_VOLTAGE_SHIFT); printf("\tbus: %6d", val); /* Read current register, the scale depends on the value of the * calibration register */ - ina220_read_current(&dev, &val); + ina2xx_read_current(&dev, &val); printf("\tcurrent: %6d", val); /* Read power register, the scale depends on the value of the * calibration register */ - ina220_read_power(&dev, &val); + ina2xx_read_power(&dev, &val); printf("\tpower: %6d\n", val); xtimer_usleep(SLEEP_USEC); From a6476bd813e59c28601efff95480370ae1dc3032 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Thu, 4 Apr 2019 11:05:26 +0200 Subject: [PATCH 2/4] drivers/ina2xx: Match RIOT's driver design goals - Use standard RIOT style `ina2xx_params_t` on initialization as explained in [1] instead of a custom API - Provided a default configuration via `ina2xx_params_t` as required by [1] that works fine for the INA219 breakout board and with an optimal resolution that still covers the whole range of USB high-power devices (500 mA @ 5V) with a comfortable safe margin. - Changed initialization procedure to include a device reset and connectivity test, as required by [1] - The calibration value is now calculated by the driver - This simplifies using the driver a lot - The user can still choose a trade-off between range and resolution that matches the application requirements, but now among predefined values - This allows the driver to easily convert the raw data into meaningful physical data, as the resolution of the raw data is known - All measurements are provided as meaningful physical data as required by [1] [1]: https://github.com/RIOT-OS/RIOT/wiki/Guide:-Writing-a-device-driver-in-RIOT --- drivers/ina2xx/ina2xx.c | 147 +++++++++++--- .../{ina2xx-regs.h => ina2xx_defines.h} | 25 ++- drivers/ina2xx/include/ina2xx_params.h | 109 +++++++++++ drivers/include/ina2xx.h | 181 ++++++++++-------- tests/driver_ina2xx/Makefile | 12 +- tests/driver_ina2xx/README.md | 17 +- tests/driver_ina2xx/main.c | 124 ++++++------ 7 files changed, 429 insertions(+), 186 deletions(-) rename drivers/ina2xx/include/{ina2xx-regs.h => ina2xx_defines.h} (60%) create mode 100644 drivers/ina2xx/include/ina2xx_params.h diff --git a/drivers/ina2xx/ina2xx.c b/drivers/ina2xx/ina2xx.c index 4ac1830fbc..5b05c9d228 100644 --- a/drivers/ina2xx/ina2xx.c +++ b/drivers/ina2xx/ina2xx.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Eistec AB + * 2019 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -16,30 +17,43 @@ * Interface * * @author Joakim Nohlgård + * @author Marian Buschsieweke * * @} */ +#include #include #include "ina2xx.h" -#include "ina2xx-regs.h" +#include "ina2xx_defines.h" #include "periph/i2c.h" #include "byteorder.h" -#define ENABLE_DEBUG (0) +#define ENABLE_DEBUG (0) #include "debug.h" +/* + * The value in the current register is obtained by: + * + * I = (V_shunt * C) / 4096 + * + * Where V_shunt is the value in the shunt voltage register and C is the + * value programmed (upon driver initialization) into the calibration register + */ +#define CURRENT_QUOTIENT (4096LU) + /** @brief Read one 16 bit register from a INA2XX device and swap byte order, if necessary. */ static int ina2xx_read_reg(const ina2xx_t *dev, uint8_t reg, uint16_t *out) { union { uint8_t c[2]; uint16_t u16; - } tmp = { .u16 = 0 }; + } tmp; int status = 0; - status = i2c_read_regs(dev->i2c, dev->addr, reg, &tmp.c[0], 2, 0); + status = i2c_read_regs(dev->params.i2c, dev->params.addr, reg, &tmp.c[0], + 2, 0); if (status < 0) { return status; @@ -55,12 +69,13 @@ static int ina2xx_write_reg(const ina2xx_t *dev, uint8_t reg, uint16_t in) union { uint8_t c[2]; uint16_t u16; - } tmp = { .u16 = 0 }; + } tmp; int status = 0; tmp.u16 = htons(in); - status = i2c_write_regs(dev->i2c, dev->addr, reg, &tmp.c[0], 2, 0); + status = i2c_write_regs(dev->params.i2c, dev->params.addr, reg, &tmp.c[0], + 2, 0); if (status < 0) { return status; @@ -70,22 +85,51 @@ static int ina2xx_write_reg(const ina2xx_t *dev, uint8_t reg, uint16_t in) } -int ina2xx_init(ina2xx_t *dev, i2c_t i2c, uint8_t address) +int ina2xx_init(ina2xx_t *dev, const ina2xx_params_t *params) { - /* write device descriptor */ - dev->i2c = i2c; - dev->addr = address; - return 0; -} + uint16_t config; + int status; + if (!dev || !params) { + return -EINVAL; + } -int ina2xx_set_calibration(const ina2xx_t *dev, uint16_t calibration) -{ - return ina2xx_write_reg(dev, INA2XX_REG_CALIBRATION, calibration); -} + dev->params = *params; + /* Reset device */ + status = ina2xx_write_reg(dev, INA2XX_REG_CONFIGURATION, INA2XX_RESET); + if (status < 0) { + return status; + } -int ina2xx_set_config(const ina2xx_t *dev, uint16_t config) -{ - return ina2xx_write_reg(dev, INA2XX_REG_CONFIGURATION, config); + /* Check if default config is preset after reset */ + status = ina2xx_read_reg(dev, INA2XX_REG_CONFIGURATION, &config); + if (status < 0) { + return status; + } + + if (config != INA2XX_DEFCONFIG) { + DEBUG("[ina2xx]: Reset did't restore default config. Wrong device?\n"); + return -ENODEV; + } + + status = ina2xx_write_reg(dev, INA2XX_REG_CONFIGURATION, params->config); + if (status < 0) { + return status; + } + + /* Set calibration register based on the shunt resistor. + * ==> Current will be in mA + * Multiply by 100 + * ==> Current will be in mA + */ + uint32_t calib = (100 * CURRENT_QUOTIENT) / params->rshunt_mohm; + /* Divide by 2^i_range to reduce the resolution by factor 2^i_range */ + calib >>= params->i_range; + + if (calib > UINT16_MAX) { + return -ERANGE; + } + + return ina2xx_write_reg(dev, INA2XX_REG_CALIBRATION, (uint16_t)calib); } int ina2xx_read_shunt(const ina2xx_t *dev, int16_t *voltage) @@ -93,17 +137,70 @@ int ina2xx_read_shunt(const ina2xx_t *dev, int16_t *voltage) return ina2xx_read_reg(dev, INA2XX_REG_SHUNT_VOLTAGE, (uint16_t *)voltage); } -int ina2xx_read_bus(const ina2xx_t *dev, int16_t *voltage) +int ina2xx_read_bus(const ina2xx_t *dev, uint16_t *voltage) { - return ina2xx_read_reg(dev, INA2XX_REG_BUS_VOLTAGE, (uint16_t *)voltage); + uint16_t tmp; + int status = ina2xx_read_reg(dev, INA2XX_REG_BUS_VOLTAGE, &tmp); + if (status < 0) { + return status; + } + + /* The voltage given by bits 15-3 in steps of 4 mV + * ==> Take bits 15-3, shift 3 bits right, multiply by 4 + * ==> Same as: Take bits 15-3, shift 3 bits right, shift 2 bits left + * ==> Same as: Take bits 15-3, shift 1 bit right + */ + *voltage = (tmp & ~0x7) >> 1; + + if (tmp & INA2XX_VBUS_OVF) { + return -EDOM; + } + + return (tmp & INA2XX_VBUS_CNVR) ? 1 : 0; } -int ina2xx_read_current(const ina2xx_t *dev, int16_t *current) +int ina2xx_read_current(const ina2xx_t *dev, int32_t *current) { - return ina2xx_read_reg(dev, INA2XX_REG_CURRENT, (uint16_t *)current); + int16_t tmp; + int status = ina2xx_read_reg(dev, INA2XX_REG_CURRENT, (uint16_t *)&tmp); + if (status < 0) { + return status; + } + + /* + * The calibration is chosen according to the selected value in + * dev->params.i_range, so that tmp * 2^i_range gives us the + * current in E-05 A. We can thus simple use a left shift to convert + * the current into E-05 A. + */ + *current = ((int32_t)tmp) << dev->params.i_range; + return 0; } -int ina2xx_read_power(const ina2xx_t *dev, int16_t *power) +int ina2xx_read_power(const ina2xx_t *dev, uint32_t *power) { - return ina2xx_read_reg(dev, INA2XX_REG_POWER, (uint16_t *)power); + int status; + uint16_t tmp; + status = ina2xx_read_reg(dev, INA2XX_REG_POWER, &tmp); + if (status < 0) { + return status; + } + + /* + * The resolution of the raw power value depends only on the resolution + * of the raw current value, as the bus voltage has a resolution of 4 mV + * regardless of configuration and calibration values. The product of + * bus voltage and raw current value is divided by 5000, this results in + * the following resolutions: + * + * Res current | res power + * 0.01 mA | 0.2 mW + * 0.02 mA | 0.4 mW + * 0.04 mA | 0.8 mW + * ... + * + * ==> multiply by 2^(1 + i_range) to get power in E-04W + */ + *power = ((uint32_t)tmp) << (1 + dev->params.i_range); + return 0; } diff --git a/drivers/ina2xx/include/ina2xx-regs.h b/drivers/ina2xx/include/ina2xx_defines.h similarity index 60% rename from drivers/ina2xx/include/ina2xx-regs.h rename to drivers/ina2xx/include/ina2xx_defines.h index d064d9d61c..88289f567e 100644 --- a/drivers/ina2xx/include/ina2xx-regs.h +++ b/drivers/ina2xx/include/ina2xx_defines.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Eistec AB + * 2019 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -11,14 +12,15 @@ * @{ * * @file - * @brief Register definitions for Texas Instruments INA219/INA220 + * @brief Various definitions for Texas Instruments INA219/INA220 * Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface * * @author Joakim Nohlgård + * @author Marian Buschsieweke */ -#ifndef INA2XX_REGS_H -#define INA2XX_REGS_H +#ifndef INA2XX_DEFINES_H +#define INA2XX_DEFINES_H #ifdef __cplusplus @@ -40,10 +42,25 @@ typedef enum ina2xx_reg { INA2XX_REG_CALIBRATION = 0x05, /**< Calibration register (read/write) */ } ina2xx_reg_t; +/** + * @name Flags in the INA2XX Bus Voltage Register + * @{ + */ +#define INA2XX_VBUS_CNVR (0x2) /**< Unread value in power register ready */ +#define INA2XX_VBUS_OVF (0x1) /**< Math overflow during conversion */ +/** @} */ + +/** + * @name Special configuration register values + * @{ + */ +#define INA2XX_RESET (0x8000)/**< Write to config reg to reset device */ +#define INA2XX_DEFCONFIG (0x399f)/**< Default config after reset */ +/** @} */ #ifdef __cplusplus } #endif -#endif /* INA2XX_REGS_H */ +#endif /* INA2XX_DEFINES_H */ /** @} */ diff --git a/drivers/ina2xx/include/ina2xx_params.h b/drivers/ina2xx/include/ina2xx_params.h new file mode 100644 index 0000000000..d26ea8991d --- /dev/null +++ b/drivers/ina2xx/include/ina2xx_params.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * 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_ina2xx + * + * @{ + * @file + * @brief Default configuration for INA2xx power/current monitors + * + * @author Marian Buschsieweke + */ + +#ifndef INA2XX_PARAMS_H +#define INA2XX_PARAMS_H + +#include "board.h" +#include "ina2xx.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters for the INA2XX + * @{ + */ +#ifndef INA2XX_PARAM_I2C +/** + * @brief Default to first I2C device + */ +#define INA2XX_PARAM_I2C (I2C_DEV(0)) +#endif +#ifndef INA2XX_PARAM_ADDR +/** + * @brief Default to address 0x40, which is active if A0 and A1 are connected + * to GND + * + * On the popular INA219 breakout board this is the default address if solder + * jumpers remain open. + */ +#define INA2XX_PARAM_ADDR (0x40) +#endif +#ifndef INA2XX_PARAM_CONFIG +/** + * @brief Default to an optimal configuration for current/power measurements + * of USB high-power devices using the popular INA219 break out board + * with 128 samples averaged + * + * | Setting | Value | + * |:------------------- |:--------------------------------------------- | + * | Mode | Continuous shunt and bus voltage measurements | + * | Shunt ADC Setting | 128 Samples, 68.10 ms per conversion | + * | Bus ADC Setting | 128 Samples, 68.10 ms per conversion | + * | Shunt Voltage Range | ±80mV | + * | Bus Voltage Range | 16V | + */ +#define INA2XX_PARAM_CONFIG (INA2XX_MODE_CONTINUOUS_SHUNT_BUS | \ + INA2XX_SADC_AVG_128_SAMPLES | \ + INA2XX_BADC_AVG_128_SAMPLES | \ + INA2XX_SHUNT_RANGE_80MV | \ + INA2XX_BUS_RANGE_16V) +#endif +#ifndef INA2XX_PARAM_RSHUNT_MOHM +/** + * @brief Default to 100 mΩ as shunt resistor + * + * This is the value used in the popular INA219 breakout board. + */ +#define INA2XX_PARAM_RSHUNT_MOHM (100) +#endif +#ifndef INA2XX_PARAM_I_RANGE +/** + * @brief Default to a current range of ±655.36mA + * + * This is the highest resolution suitable to measure USB high-power devices + * (up to 500 mA). + */ +#define INA2XX_PARAM_I_RANGE (INA2XX_CURRENT_RANGE_655_MA) +#endif +#ifndef INA2XX_PARAMS +#define INA2XX_PARAMS { .i2c = INA2XX_PARAM_I2C, \ + .addr = INA2XX_PARAM_ADDR, \ + .config = INA2XX_PARAM_CONFIG, \ + .rshunt_mohm = INA2XX_PARAM_RSHUNT_MOHM, \ + .i_range = INA2XX_PARAM_I_RANGE } +#endif +/**@}*/ + + +/** + * @brief Configure INA2XX devices + */ +static const ina2xx_params_t ina2xx_params[] = +{ + INA2XX_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* INA2XX_PARAMS_H */ +/** @} */ diff --git a/drivers/include/ina2xx.h b/drivers/include/ina2xx.h index 8b774ddd1a..8a1b7a55c6 100644 --- a/drivers/include/ina2xx.h +++ b/drivers/include/ina2xx.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Eistec AB + * 2019 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -9,20 +10,38 @@ /** * @defgroup drivers_ina2xx INA2XX current/power monitor * @ingroup drivers_sensors - * @brief Device driver for Texas Instruments INA219/INA220 + * @brief Device driver for Texas Instruments INA219/INA2XX * Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface * * # Supported Hardware - * This driver currently supports the INA219 and the INA220 - and possibly - * other devices of the INA2xx family. + * This driver currently supports the INA219 and the INA2XX - and possibly + * other devices of the INA2xx family. The popular INA219 breakout boards are + * supported out of the box. + * + * # Choosing the Right Shunt Resistor + * The shunt resistor should generate a voltage drop of at least 40 mV and of + * at most 320 mV at maximum current (add some safety margin to the maximum + * current). E.g. when the expected maximum current is 500 mA and we add a + * safety margin of 33 mA, a 75 mΩ shunt resistor would cause a drop of 40 mV + * at about 533 mA, and a 600 mΩ shunt resistor would cause a drop of 320 mV. + * So every shunt resistor between 75 mΩ and 600 mΩ would result in a decent + * resolution, but shunt resistors that almost max out one of the selectable + * shunt voltage ranges (40 mV, 80 mV, 160 mV and 320 mV) would be ideal. + * Often the voltage drop should be as low as possible, therefore the lowest + * reasonable shunt resistor has to be chosen in that case. + * + * The popular INA219 breakout boards have a shunt resistor of 100 mΩ, which + * allows to measure a current of up to 400 mA at PGA = /1, and of up two 3.2 A + * at PGA = /8. * * @{ * * @file - * @brief Device driver interface for Texas Instruments INA219/INA220 + * @brief Device driver interface for Texas Instruments INA219/INA2XX * Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface * * @author Joakim Nohlgård + * @author Marian Buschsieweke */ #ifndef INA2XX_H @@ -36,14 +55,6 @@ extern "C" { #endif -/** - * @brief Device descriptor for INA2XX sensors - */ -typedef struct { - i2c_t i2c; /**< I2C device the sensor is connected to */ - uint8_t addr; /**< the slave address of the sensor on the I2C bus */ -} ina2xx_t; - /** * @brief INA2XX possible mode settings */ @@ -58,24 +69,6 @@ typedef enum ina2xx_mode { INA2XX_MODE_CONTINUOUS_SHUNT_BUS = 0x0007, /**< Shunt and Bus, Continuous, default */ } ina2xx_mode_t; -/** - * @brief Shunt voltage measurement range (PGA settings) - */ -typedef enum ina2xx_range { - INA2XX_RANGE_40MV = 0x0000, /**< +/- 40 mV range */ - INA2XX_RANGE_80MV = 0x0800, /**< +/- 80 mV range */ - INA2XX_RANGE_160MV = 0x1000, /**< +/- 160 mV range */ - INA2XX_RANGE_320MV = 0x1800, /**< +/- 320 mV range, default */ -} ina2xx_range_t; - -/** - * @brief Bus voltage measurement range - */ -typedef enum ina2xx_brng { - INA2XX_BRNG_16V_FSR = 0x0000, /**< 16 V bus voltage full scale range */ - INA2XX_BRNG_32V_FSR = 0x2000, /**< 32 V bus voltage full scale range, default. */ -} ina2xx_brng_t; - /** * @brief Shunt ADC settings * @@ -140,51 +133,83 @@ typedef enum ina2xx_badc { INA2XX_BADC_AVG_128_SAMPLES = 0x0780, } ina2xx_badc_t; -/** INA2XX reset command bit (in configuration register) */ -#define INA2XX_RESET_BIT (0x8000) +/** + * @brief Shunt voltage measurement range (PGA settings) + */ +typedef enum ina2xx_pga { + INA2XX_SHUNT_RANGE_40MV = 0x0000, /**< +/- 40 mV range */ + INA2XX_SHUNT_RANGE_80MV = 0x0800, /**< +/- 80 mV range */ + INA2XX_SHUNT_RANGE_160MV = 0x1000, /**< +/- 160 mV range */ + INA2XX_SHUNT_RANGE_320MV = 0x1800, /**< +/- 320 mV range, default */ +} ina2xx_pga_t; -/** Location of the bus voltage in the INA2XX bus voltage register */ -#define INA2XX_BUS_VOLTAGE_SHIFT (3) +/** + * @brief Bus voltage measurement range + */ +typedef enum ina2xx_brng { + INA2XX_BUS_RANGE_16V = 0x0000, /**< 16 V bus voltage full scale range */ + INA2XX_BUS_RANGE_32V = 0x2000, /**< 32 V bus voltage full scale range, default. */ +} ina2xx_brng_t; + +/** + * @brief Current measurement range + * @warning Choosing a low range and a small shunt resistor can cause + * @ref ina2xx_init to fail. But for high resolution measurements of + * low currents a "big" shunt resistor (e.g. 100 mΩ) should be chosen + * anyway. + * + * Choosing the lowest sufficient range for your use case will result in the + * optimal resolution + */ +typedef enum ina2xx_i_range { + INA2XX_CURRENT_RANGE_327_MA, /**< ±327.68 mA, 0.01mA resolution */ + INA2XX_CURRENT_RANGE_655_MA, /**< ±655.36 mA, 0.02mA resolution */ + INA2XX_CURRENT_RANGE_1310_MA, /**< ±1310.72 mA, 0.04mA resolution */ + INA2XX_CURRENT_RANGE_2621_MA, /**< ±2621.44 mA, 0.08mA resolution */ + INA2XX_CURRENT_RANGE_5242_MA, /**< ±5242.88 mA, 0.16mA resolution */ + INA2XX_CURRENT_RANGE_10485_MA, /**< ±10485.76 mA, 0.32mA resolution */ +} ina2xx_i_range_t; + +/** + * @brief Configuration parameters of the INA2xx driver + */ +typedef struct { + i2c_t i2c; /**< I2C device the sensor is connected to */ + uint8_t addr; /**< I2C address of the sensr */ + uint16_t config; /**< Configuration to apply */ + uint16_t rshunt_mohm; /**< Size of the shunt resistor in mΩ */ + ina2xx_i_range_t i_range; /**< Range of the measured current */ +} ina2xx_params_t; + +/** + * @brief Device descriptor for INA2XX sensors + */ +typedef struct { + ina2xx_params_t params; /**< Configuration parameters of the driver */ +} ina2xx_t; /** * @brief Initialize a current sensor * * @param[out] dev device descriptor of sensor to initialize - * @param[in] i2c I2C bus the sensor is connected to - * @param[in] address I2C slave address of the sensor + * @param[in] params Configuration parameters to use * - * @return 0 on success - * @return <0 on error + * @return `0` on success, `<0` on error. + * @retval 0 Success + * @retval -ENODEV Device seems not to be an INA2XX device + * @retval -EINVAL @p dev or @p params is NULL + * @retval -ERANGE @p params contained invalid setting: Increase + * current range + * @retval <0 Failure, error of @ref i2c_read_regs or + * @ref i2c_write_regs passed through */ -int ina2xx_init(ina2xx_t *dev, i2c_t i2c, uint8_t address); +int ina2xx_init(ina2xx_t *dev, const ina2xx_params_t *params); /** - * @brief Write to calibration register - * - * @param[in] dev device descriptor of sensor to configure - * @param[in] calibration calibration register settings, see data sheet - * - * @return 0 on success - * @return <0 on error - */ -int ina2xx_set_calibration(const ina2xx_t *dev, uint16_t calibration); - -/** - * @brief Write to configuration register - * - * @param[in] dev device descriptor of sensor to configure - * @param[in] config configuration register settings, see data sheet - * - * @return 0 on success - * @return <0 on error - */ -int ina2xx_set_config(const ina2xx_t *dev, uint16_t config); - -/** - * @brief Read shunt voltage + * @brief Read shunt voltage in E-05 V * * @param[in] dev device descriptor of sensor - * @param[out] voltage measured voltage across shunt resistor + * @param[out] voltage measured voltage across shunt resistor in E-05 V * * @return 0 on success * @return <0 on error @@ -192,42 +217,40 @@ int ina2xx_set_config(const ina2xx_t *dev, uint16_t config); int ina2xx_read_shunt(const ina2xx_t *dev, int16_t *voltage); /** - * @brief Read bus voltage register - * - * The bus voltage can be found in the most significant bits of the bus voltage - * register, the lower three bits are flags/reserved. - * - * See the device data sheet for details. + * @brief Read bus voltage in mV * * @param[in] dev device descriptor of sensor - * @param[out] voltage measured bus voltage + * @param[out] voltage The measured bus voltage in mV * - * @return 0 on success - * @return <0 on error + * @return `<0` on error, `>= 0` on success + * @retval 0 Success, no new power value available + * @retval 1 Success, new value for @ref ina2xx_read_power ready + * @retval -EDOM Overflow during power/current calculations. + * @retval <0 Error code of @ref i2c_read_regs passed through */ -int ina2xx_read_bus(const ina2xx_t *dev, int16_t *voltage); +int ina2xx_read_bus(const ina2xx_t *dev, uint16_t *voltage); /** - * @brief Read shunt current + * @brief Read shunt current in E-05 A * * @param[in] dev device descriptor of sensor - * @param[out] current measured current through shunt resistor + * @param[out] current measured current through shunt resistor in E-05 A * * @return 0 on success * @return <0 on error */ -int ina2xx_read_current(const ina2xx_t *dev, int16_t *current); +int ina2xx_read_current(const ina2xx_t *dev, int32_t *current); /** - * @brief Read power consumption + * @brief Read power consumption in E-04 W * * @param[in] dev device descriptor of sensor - * @param[out] power measured power consumption + * @param[out] power measured power consumption in E-04 W * * @return 0 on success * @return <0 on error */ -int ina2xx_read_power(const ina2xx_t *dev, int16_t *power); +int ina2xx_read_power(const ina2xx_t *dev, uint32_t *power); #ifdef __cplusplus } diff --git a/tests/driver_ina2xx/Makefile b/tests/driver_ina2xx/Makefile index 0321a1a38a..88dd18f1ca 100644 --- a/tests/driver_ina2xx/Makefile +++ b/tests/driver_ina2xx/Makefile @@ -1,14 +1,6 @@ include ../Makefile.tests_common -USEMODULE += ina220 -USEMODULE += xtimer - -# set default device parameters in case they are undefined -TEST_INA2XX_I2C ?= I2C_DEV\(0\) -TEST_INA2XX_ADDR ?= 0x40 - -# export parameters -CFLAGS += -DTEST_INA2XX_I2C=$(TEST_INA2XX_I2C) -CFLAGS += -DTEST_INA2XX_ADDR=$(TEST_INA2XX_ADDR) +USEMODULE += fmt_table +USEMODULE += ina219 include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_ina2xx/README.md b/tests/driver_ina2xx/README.md index c352947b97..13c9a3eb7b 100644 --- a/tests/driver_ina2xx/README.md +++ b/tests/driver_ina2xx/README.md @@ -1,12 +1,15 @@ # About -This is a manual test application for the INA210/INA220 current and power +This is a manual test application for the INA219/INA220 current and power monitor driver. # Usage -This test application will initialize the sensor with the following parameters: - - ADC resolution: 12 bit - - Sampling time: 532 us - - Calibration register: 4096 +This test application will initialize the sensor with the default parameters +in @ref ina2xx_params.h -After initialization, the sensor reads the measurement values every 100ms -and prints them to the STDOUT. +After initialization, the application will print a table of the devices +measurement data. If the I2C interface is fast enough, one row corresponds to +one measurement of the device. (The sensor indicates when a new measurement is +available via flags, this flags are read until the measurement is done. The +duration of one measurement depends on the sensor settings and is between +84µs and 68.1ms. Please note that blocking stdio might very well be the +bottleneck causing the test to miss measurements.) diff --git a/tests/driver_ina2xx/main.c b/tests/driver_ina2xx/main.c index 0d048a6fe7..97ef83a5e0 100644 --- a/tests/driver_ina2xx/main.c +++ b/tests/driver_ina2xx/main.c @@ -14,88 +14,90 @@ * @brief Test application for the INA2XX sensor driver * * @author Joakim Nohlgård + * @author Marian Buschsieweke * * @} */ -#ifndef TEST_INA2XX_I2C -#error "TEST_INA2XX_I2C not defined" -#endif -#ifndef TEST_INA2XX_ADDR -#error "TEST_INA2XX_ADDR not defined" -#endif +#include +#include -#include - -#include "xtimer.h" +#include "fmt.h" +#include "fmt_table.h" #include "ina2xx.h" - -/* Use the following configuration: - * - * - Continuous measurements, both shunt and bus voltage - * - +/- 320 mV Vshunt range - * - 32 V maximum bus voltage - * - 12 bit ADC resolution, no hardware averaging - */ -#define CONFIG (INA2XX_MODE_CONTINUOUS_SHUNT_BUS | INA2XX_RANGE_320MV | \ - INA2XX_BRNG_32V_FSR | INA2XX_SADC_12BIT | INA2XX_BADC_12BIT) -#define CALIBRATION (4096) -#define SLEEP_USEC (100 * 1000U) +#include "ina2xx_params.h" int main(void) { ina2xx_t dev; - int16_t val; - puts("INA2XX sensor driver test application\n"); + print_str("INA2XX sensor driver test application\n\n"); - printf("Initializing INA2XX sensor at I2C_%i, address 0x%02x... ", - TEST_INA2XX_I2C, TEST_INA2XX_ADDR); - if (ina2xx_init(&dev, TEST_INA2XX_I2C, TEST_INA2XX_ADDR) == 0) { - puts("[OK]\n"); + print_str("Initializing INA2XX sensor at I2C_"); + print_s32_dec(ina2xx_params[0].i2c); + print_str(", address 0x"); + print_u32_hex(ina2xx_params[0].addr); + print_str("\n"); + if (ina2xx_init(&dev, &ina2xx_params[0]) == 0) { + print_str("[OK]\n"); } else { - puts("[Failed]"); - return 1; - } - puts("Set configuration register"); - if (ina2xx_set_config(&dev, CONFIG) == 0) { - puts("[OK]\n"); - } else { - puts("[Failed]"); - return 1; + print_str("[Failed]\n"); + return EXIT_FAILURE; } - puts("Set calibration register"); - if (ina2xx_set_calibration(&dev, CALIBRATION) == 0) { - puts("[OK]\n"); - } else { - puts("[Failed]"); - return 1; - } + const char *line = "+------------+--------------+----------+--------+\n"; + print_str(line); + print_str("| U_Bus [mV] | U_Shunt [µV] | I [µA] | P [µW] |\n"); + print_str(line); while (1) { - /* Read shunt resistor voltage, in millivolts */ - ina2xx_read_shunt(&dev, &val); - printf("shunt: %6d", val); + uint16_t u_bus; + int16_t u_shunt; + int32_t i_shunt; + uint32_t p; - /* Read VBUS voltage, in millivolts */ - ina2xx_read_bus(&dev, &val); - /* The bus voltage is found in the topmost 13 bits of the bus voltage - * register */ - val = (val >> INA2XX_BUS_VOLTAGE_SHIFT); - printf("\tbus: %6d", val); + /* Read bus voltage until flag indicates new value is present */ + switch (ina2xx_read_bus(&dev, &u_bus)){ + case 0: + /* No measurement available yet */ + continue; + case 1: + /* New measurement available, continue */ + break; + case -EDOM: + print_str("[WARNING]: INA2xx detected math overflow ==> data " + "will be incorrect\n"); + break; + default: + /* Error */ + print_str("Error while reading bus voltage\n"); + return EXIT_FAILURE; + } - /* Read current register, the scale depends on the value of the - * calibration register */ - ina2xx_read_current(&dev, &val); - printf("\tcurrent: %6d", val); + if (ina2xx_read_shunt(&dev, &u_shunt) < 0) { + print_str("Error while reading shunt voltage\n"); + return EXIT_FAILURE; + } - /* Read power register, the scale depends on the value of the - * calibration register */ - ina2xx_read_power(&dev, &val); - printf("\tpower: %6d\n", val); + if (ina2xx_read_current(&dev, &i_shunt) < 0) { + print_str("Error while reading current\n"); + return EXIT_FAILURE; + } - xtimer_usleep(SLEEP_USEC); + if (ina2xx_read_power(&dev, &p) < 0) { + print_str("Error while reading power\n"); + return EXIT_FAILURE; + } + + print_str("| "); + print_col_u32_dec(u_bus, 10); + print_str(" | "); + print_col_s32_dec(10 * (int32_t)u_shunt, 12); + print_str(" | "); + print_col_s32_dec(10 * i_shunt, 8); + print_str(" | "); + print_col_u32_dec(100 * p, 6); + print_str(" |\n"); } return 0; From 3ebbec84c79b173098fab206b190cf38d67c18b7 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Thu, 4 Apr 2019 12:54:57 +0200 Subject: [PATCH 3/4] drivers/saul: Fixed documentation There is no need to distinguish between sensors for electrical power or other types of power. --- drivers/include/saul.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/include/saul.h b/drivers/include/saul.h index a5e6b55d3a..c8afbcd3fa 100644 --- a/drivers/include/saul.h +++ b/drivers/include/saul.h @@ -105,7 +105,7 @@ enum { SAUL_SENSE_CAPACITANCE = 0x97, /**< sensor: capacitance */ SAUL_SENSE_VOLTAGE = 0x98, /**< sensor: voltage */ SAUL_SENSE_PH = 0x99, /**< sensor: pH */ - SAUL_SENSE_POWER = 0x9A, /**< sensor: (electrical) power */ + SAUL_SENSE_POWER = 0x9a, /**< sensor: power */ SAUL_CLASS_ANY = 0xff /**< any device - wildcard */ /* extend this list as needed... */ }; From 0a0c1697d7e1d5f6aee907d7382a0f09bb583b57 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 5 Apr 2019 13:49:37 +0200 Subject: [PATCH 4/4] drivers/ina2xx: Added SAUL integration --- drivers/ina2xx/ina2xx_saul.c | 86 ++++++++++++++++++++++++++ drivers/ina2xx/include/ina2xx_params.h | 19 ++++++ sys/auto_init/auto_init.c | 6 +- sys/auto_init/saul/auto_init_ina2xx.c | 82 ++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 drivers/ina2xx/ina2xx_saul.c create mode 100644 sys/auto_init/saul/auto_init_ina2xx.c diff --git a/drivers/ina2xx/ina2xx_saul.c b/drivers/ina2xx/ina2xx_saul.c new file mode 100644 index 0000000000..b92c57e414 --- /dev/null +++ b/drivers/ina2xx/ina2xx_saul.c @@ -0,0 +1,86 @@ +/* + * Copyright 2019 Otto-von-Guericke-Universität Magdeburg + * + * 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_ina2xx + * @{ + * + * @file + * @brief SAUL adaption of the INA2XX driver + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include +#include + +#include "phydat.h" +#include "saul.h" +#include "ina2xx.h" + +static int read_current(const void *_dev, phydat_t *res) +{ + ina2xx_t *dev = (ina2xx_t *)_dev; + int32_t current; + + if (ina2xx_read_current(dev, ¤t) == 0) { + res->scale = -5; + res->unit = UNIT_A; + phydat_fit(res, ¤t, 1); + return 1; + } + + return -ECANCELED; +} + +static int read_power(const void *_dev, phydat_t *res) +{ + ina2xx_t *dev = (ina2xx_t *)_dev; + uint32_t power; + if (ina2xx_read_power(dev, &power) == 0) { + res->unit = UNIT_W; + res->scale = -4; + phydat_fit(res, (int32_t *)&power, 1); + return 1; + } + + return -ECANCELED; +} + +static int read_voltage(const void *_dev, phydat_t *res) +{ + ina2xx_t *dev = (ina2xx_t *)_dev; + if (ina2xx_read_bus(dev, (uint16_t *)&res->val[0]) >= 0) { + res->unit = UNIT_V; + res->scale = -3; + return 1; + } + + return -ECANCELED; +} + +const saul_driver_t ina2xx_saul_current_driver = { + .read = read_current, + .write = saul_notsup, + .type = SAUL_SENSE_CURRENT +}; + +const saul_driver_t ina2xx_saul_power_driver = { + .read = read_power, + .write = saul_notsup, + .type = SAUL_SENSE_POWER +}; + +const saul_driver_t ina2xx_saul_voltage_driver = { + .read = read_voltage, + .write = saul_notsup, + .type = SAUL_SENSE_VOLTAGE +}; diff --git a/drivers/ina2xx/include/ina2xx_params.h b/drivers/ina2xx/include/ina2xx_params.h index d26ea8991d..0311c7fbdf 100644 --- a/drivers/ina2xx/include/ina2xx_params.h +++ b/drivers/ina2xx/include/ina2xx_params.h @@ -21,6 +21,7 @@ #include "board.h" #include "ina2xx.h" +#include "saul_reg.h" #ifdef __cplusplus extern "C" { @@ -92,6 +93,16 @@ extern "C" { #endif /**@}*/ +/** + * @name Set default SAUL info for the INA2XX + * @{ + */ +#ifndef INA2XX_SAULINFO +#define INA2XX_SAULINFO { .name = "INA2XX current" }, \ + { .name = "INA2XX power" }, \ + { .name = "INA2XX voltage" } +#endif +/**@}*/ /** * @brief Configure INA2XX devices @@ -101,6 +112,14 @@ static const ina2xx_params_t ina2xx_params[] = INA2XX_PARAMS }; +/** + * @brief Allocate and configure entries to the SAUL registry + */ +static const saul_reg_info_t ina2xx_saul_info[] = +{ + INA2XX_SAULINFO +}; + #ifdef __cplusplus } #endif diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 0a4ed3febc..0e09305184 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -422,10 +422,14 @@ void auto_init(void) extern void auto_init_hts221(void); auto_init_hts221(); #endif +#ifdef MODULE_INA2XX + extern void auto_init_ina2xx(void); + auto_init_ina2xx(); +#endif #ifdef MODULE_INA3221 extern void auto_init_ina3221(void); auto_init_ina3221(); - #endif +#endif #ifdef MODULE_IO1_XPLAINED extern void auto_init_io1_xplained(void); auto_init_io1_xplained(); diff --git a/sys/auto_init/saul/auto_init_ina2xx.c b/sys/auto_init/saul/auto_init_ina2xx.c new file mode 100644 index 0000000000..ad72810766 --- /dev/null +++ b/sys/auto_init/saul/auto_init_ina2xx.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * 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 sys_auto_init_saul + * @{ + * + * @file + * @brief Auto initialization for INA2XX power/current monitors + * + * @author Marian Buschsieweke + * + * @} + */ + +#ifdef MODULE_INA2XX + +#include "assert.h" +#include "log.h" +#include "saul_reg.h" +#include "ina2xx_params.h" +#include "ina2xx.h" + +/** + * @brief Define the number of configured sensors + */ +#define INA2XX_NUM ARRAY_SIZE(ina2xx_params) + +/** + * @brief Allocate memory for the device descriptors + */ +static ina2xx_t ina2xx_devs[INA2XX_NUM]; + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[INA2XX_NUM * 3]; + +/** + * @brief Define the number of saul info + */ +#define INA2XX_INFO_NUM ARRAY_SIZE(ina2xx_saul_info) + +/** + * @name Import SAUL endpoints + * @{ + */ +extern const saul_driver_t ina2xx_saul_current_driver; +extern const saul_driver_t ina2xx_saul_power_driver; +extern const saul_driver_t ina2xx_saul_voltage_driver; +/** @} */ + +void auto_init_ina2xx(void) +{ + assert(INA2XX_INFO_NUM == 3 * INA2XX_NUM); + + for (unsigned int i = 0; i < INA2XX_NUM; i++) { + LOG_DEBUG("[auto_init_saul] initializing ina2xx #%u\n", i); + + if (ina2xx_init(&ina2xx_devs[i], &ina2xx_params[i])) { + LOG_ERROR("[auto_init_saul] error initializing ina2xx #%u\n", i); + continue; + } + + for (unsigned int j = 0; j < 3; j++) { + saul_entries[i * 3 + j].dev = &(ina2xx_devs[i]); + saul_entries[i * 3 + j].name = ina2xx_saul_info[3 * i + j].name; + saul_entries[i * 3 + j].driver = &ina2xx_saul_power_driver; + saul_reg_add(&(saul_entries[i * 3 + j])); + } + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_INA2XX */