From 5ea9eaf386fa6cdbac13626d2e5b13ea3ebcf7fd Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Tue, 3 Feb 2015 07:10:56 +0100 Subject: [PATCH] drivers/ina220: Add driver for INA220 current and power sensor. --- drivers/Makefile.include | 3 + drivers/ina220/Makefile | 3 + drivers/ina220/ina220.c | 109 +++++++++++++ drivers/ina220/include/ina220-regs.h | 50 ++++++ drivers/include/ina220.h | 227 +++++++++++++++++++++++++++ tests/driver_ina220/Makefile | 22 +++ tests/driver_ina220/README.md | 11 ++ tests/driver_ina220/main.c | 106 +++++++++++++ 8 files changed, 531 insertions(+) create mode 100644 drivers/ina220/Makefile create mode 100644 drivers/ina220/ina220.c create mode 100644 drivers/ina220/include/ina220-regs.h create mode 100644 drivers/include/ina220.h create mode 100644 tests/driver_ina220/Makefile create mode 100644 tests/driver_ina220/README.md create mode 100644 tests/driver_ina220/main.c diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 07901e3e2b..1075ca8593 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -40,3 +40,6 @@ endif ifneq (,$(filter mpu9150,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/mpu9150/include endif +ifneq (,$(filter ina220,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ina220/include +endif diff --git a/drivers/ina220/Makefile b/drivers/ina220/Makefile new file mode 100644 index 0000000000..f0f0efca36 --- /dev/null +++ b/drivers/ina220/Makefile @@ -0,0 +1,3 @@ +MODULE = ina220 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/ina220/ina220.c b/drivers/ina220/ina220.c new file mode 100644 index 0000000000..76b50d7a66 --- /dev/null +++ b/drivers/ina220/ina220.c @@ -0,0 +1,109 @@ +/* + * 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 driver_ina220 + * @{ + * + * @file + * @brief Device driver implementation for Texas Instruments INA220 High + * or Low Side, Bi-Directional CURRENT/POWER MONITOR with Two-Wire + * Interface + * + * @author Joakim Gebart + * + * @} + */ + +#include + +#include "ina220.h" +#include "ina220-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(ina220_t *dev, uint8_t reg, uint16_t *out) +{ + union { + char c[2]; + uint16_t u16; + } tmp = { .u16 = 0 }; + int status = 0; + + status = i2c_read_regs(dev->i2c, dev->addr, reg, &tmp.c[0], 2); + + if (status != 2) { + return -1; + } + + *out = NTOHS(tmp.u16); + return 0; +} + +/** @brief Write one 16 bit register to a INA220 device and swap byte order, if necessary. */ +static int ina220_write_reg(ina220_t *dev, uint8_t reg, uint16_t in) +{ + union { + char c[2]; + uint16_t u16; + } tmp = { .u16 = 0 }; + int status = 0; + + tmp.u16 = HTONS(in); + + status = i2c_write_regs(dev->i2c, dev->addr, reg, &tmp.c[0], 2); + + if (status != 2) { + return -1; + } + + return 0; +} + + +int ina220_init(ina220_t *dev, i2c_t i2c, uint8_t address) +{ + /* write device descriptor */ + dev->i2c = i2c; + dev->addr = address; + return 0; +} + +int ina220_set_calibration(ina220_t *dev, uint16_t calibration) +{ + return ina220_write_reg(dev, INA220_REG_CALIBRATION, calibration); +} + +int ina220_set_config(ina220_t *dev, uint16_t config) +{ + return ina220_write_reg(dev, INA220_REG_CONFIGURATION, config); +} + +int ina220_read_shunt(ina220_t *dev, int16_t *voltage) +{ + return ina220_read_reg(dev, INA220_REG_SHUNT_VOLTAGE, (uint16_t *)voltage); +} + +int ina220_read_bus(ina220_t *dev, int16_t *voltage) +{ + return ina220_read_reg(dev, INA220_REG_BUS_VOLTAGE, (uint16_t *)voltage); +} + +int ina220_read_current(ina220_t *dev, int16_t *current) +{ + return ina220_read_reg(dev, INA220_REG_CURRENT, (uint16_t *)current); +} + +int ina220_read_power(ina220_t *dev, int16_t *power) +{ + return ina220_read_reg(dev, INA220_REG_POWER, (uint16_t *)power); +} diff --git a/drivers/ina220/include/ina220-regs.h b/drivers/ina220/include/ina220-regs.h new file mode 100644 index 0000000000..e0b19e7f8c --- /dev/null +++ b/drivers/ina220/include/ina220-regs.h @@ -0,0 +1,50 @@ +/* + * 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 driver_ina220 + * @{ + * + * @file + * @brief Register definitions for Texas Instruments INA220 High or Low + * Side, Bi-Directional CURRENT/POWER MONITOR with Two-Wire + * Interface + * + * @author Joakim Gebart + */ + +#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 /* __L3G4200D_REGS_H */ +/** @} */ diff --git a/drivers/include/ina220.h b/drivers/include/ina220.h new file mode 100644 index 0000000000..cc61792645 --- /dev/null +++ b/drivers/include/ina220.h @@ -0,0 +1,227 @@ +/* + * 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. + */ + +/** + * @defgroup driver_ina220 INA220 current/power monitor + * @ingroup drivers + * @brief Device driver for Texas Instruments INA220 High or Low Side, + * Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface + * @{ + * + * @file + * @brief Device driver interface for Texas Instruments INA220 High or Low + * Side, Bi-Directional CURRENT/POWER MONITOR with Two-Wire + * Interface + * + * @author Joakim Gebart + */ + +#ifndef INA220_H +#define INA220_H + +#include + +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Device descriptor for INA220 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; + +/** @brief INA220 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; + +/** @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; + +/** @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 = 0x0200, /**< 32 V bus voltage full scale range, default. */ +} ina220_brng_t; + +/** + * @brief Shunt ADC settings + * + * @see Table 5 in INA220 data sheet + */ +typedef enum ina220_sadc { + /** 9 bit resolution, 84 us conversion time */ + INA220_SADC_9BIT = 0x0000, + /** 10 bit resolution, 148 us conversion time */ + INA220_SADC_10BIT = 0x0008, + /** 11 bit resolution, 276 us conversion time */ + INA220_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, + /** 2 sample average, 1.06 ms conversion time */ + INA220_SADC_AVG_2_SAMPLES = 0x0048, + /** 4 sample average, 2.13 ms conversion time */ + INA220_SADC_AVG_4_SAMPLES = 0x0050, + /** 8 sample average, 4.26 ms conversion time */ + INA220_SADC_AVG_8_SAMPLES = 0x0058, + /** 16 sample average, 8.51 ms conversion time */ + INA220_SADC_AVG_16_SAMPLES = 0x0060, + /** 32 sample average, 17.02 ms conversion time */ + INA220_SADC_AVG_32_SAMPLES = 0x0068, + /** 64 sample average, 34.05 ms conversion time */ + INA220_SADC_AVG_64_SAMPLES = 0x0070, + /** 128 sample average, 68.10 ms conversion time */ + INA220_SADC_AVG_128_SAMPLES = 0x0078, +} ina220_sadc_t; + +/** + * @brief Bus ADC settings + * + * @see Table 5 in INA220 data sheet + */ +typedef enum ina220_badc { + /** 9 bit resolution, 84 us conversion time */ + INA220_BADC_9BIT = 0x0000, + /** 10 bit resolution, 148 us conversion time */ + INA220_BADC_10BIT = 0x0080, + /** 11 bit resolution, 276 us conversion time */ + INA220_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, + /** 2 sample average, 1.06 ms conversion time */ + INA220_BADC_AVG_2_SAMPLES = 0x0480, + /** 4 sample average, 2.13 ms conversion time */ + INA220_BADC_AVG_4_SAMPLES = 0x0500, + /** 8 sample average, 4.26 ms conversion time */ + INA220_BADC_AVG_8_SAMPLES = 0x0580, + /** 16 sample average, 8.51 ms conversion time */ + INA220_BADC_AVG_16_SAMPLES = 0x0600, + /** 32 sample average, 17.02 ms conversion time */ + INA220_BADC_AVG_32_SAMPLES = 0x0680, + /** 64 sample average, 34.05 ms conversion time */ + INA220_BADC_AVG_64_SAMPLES = 0x0700, + /** 128 sample average, 68.10 ms conversion time */ + INA220_BADC_AVG_128_SAMPLES = 0x0780, +} ina220_badc_t; + +/** INA220 reset command bit (in configuration register) */ +#define INA220_RESET_BIT (0x8000) + +/** Location of the bus voltage in the INA220 bus voltage register */ +#define INA220_BUS_VOLTAGE_SHIFT (3) + +/** + * @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 + * + * @return 0 on success + * @return <0 on error + */ +int ina220_init(ina220_t *dev, i2c_t i2c, uint8_t address); + +/** + * @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 ina220_set_calibration(ina220_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 ina220_set_config(ina220_t *dev, uint16_t config); + +/** + * @brief Read shunt voltage + * + * @param[in] dev device descriptor of sensor + * @param[out] voltage measured voltage across shunt resistor + * + * @return 0 on success + * @return <0 on error + */ +int ina220_read_shunt(ina220_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. + * + * @param[in] dev device descriptor of sensor + * @param[out] voltage measured bus voltage + * + * @return 0 on success + * @return <0 on error + */ +int ina220_read_bus(ina220_t *dev, int16_t *voltage); + +/** + * @brief Read shunt current + * + * @param[in] dev device descriptor of sensor + * @param[out] current measured current through shunt resistor + * + * @return 0 on success + * @return <0 on error + */ +int ina220_read_current(ina220_t *dev, int16_t *current); + +/** + * @brief Read power consumption + * + * @param[in] dev device descriptor of sensor + * @param[out] power measured power consumption + * + * @return 0 on success + * @return <0 on error + */ +int ina220_read_power(ina220_t *dev, int16_t *power); + +#ifdef __cplusplus +} +#endif + +#endif /* INA220_H */ +/** @} */ diff --git a/tests/driver_ina220/Makefile b/tests/driver_ina220/Makefile new file mode 100644 index 0000000000..5dd0e63841 --- /dev/null +++ b/tests/driver_ina220/Makefile @@ -0,0 +1,22 @@ +APPLICATION = driver_ina220 +include ../Makefile.tests_common + +FEATURES_REQUIRED = periph_i2c + +USEMODULE += ina220 +USEMODULE += vtimer + +ifneq (,$(TEST_INA220_I2C)) + CFLAGS += -DTEST_INA220_I2C=$(TEST_INA220_I2C) +else + # set arbitrary default + CFLAGS += -DTEST_INA220_I2C=I2C_0 +endif +ifneq (,$(TEST_INA220_ADDR)) + CFLAGS += -DTEST_INA220_ADDR=$(TEST_INA220_ADDR) +else + # set arbitrary default + CFLAGS += -DTEST_INA220_ADDR=0x40 +endif + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_ina220/README.md b/tests/driver_ina220/README.md new file mode 100644 index 0000000000..e922443dcd --- /dev/null +++ b/tests/driver_ina220/README.md @@ -0,0 +1,11 @@ +# About +This is a manual test application for the 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 + +After initialization, the sensor reads the measurement values every 100ms +and prints them to the STDOUT. diff --git a/tests/driver_ina220/main.c b/tests/driver_ina220/main.c new file mode 100644 index 0000000000..b6c2885f12 --- /dev/null +++ b/tests/driver_ina220/main.c @@ -0,0 +1,106 @@ +/* + * 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 tests + * @{ + * + * @file + * @brief Test application for the INA220 sensor driver + * + * @author Joakim Gebart + * + * @} + */ + +#ifndef TEST_INA220_I2C +#error "TEST_INA220_I2C not defined" +#endif +#ifndef TEST_INA220_ADDR +#error "TEST_INA220_ADDR not defined" +#endif + +#include + +#include "vtimer.h" +#include "ina220.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 (INA220_MODE_CONTINUOUS_SHUNT_BUS | INA220_RANGE_320MV | \ + INA220_BRNG_32V_FSR | INA220_SADC_12BIT | INA220_BADC_12BIT) +#define CALIBRATION (4096) +#define SLEEP (100 * 1000U) + +int main(void) +{ + ina220_t dev; + int16_t val; + + puts("INA220 sensor driver test application\n"); + printf("Initializing I2C_%i... ", TEST_INA220_I2C); + if (i2c_init_master(TEST_INA220_I2C, I2C_SPEED_FAST) < 0) { + return -1; + } + + 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) { + puts("[OK]\n"); + } else { + puts("[Failed]"); + return 1; + } + puts("Set configuration register"); + if (ina220_set_config(&dev, CONFIG) == 0) { + puts("[OK]\n"); + } else { + puts("[Failed]"); + return 1; + } + + puts("Set calibration register"); + if (ina220_set_calibration(&dev, CALIBRATION) == 0) { + puts("[OK]\n"); + } else { + puts("[Failed]"); + return 1; + } + + while (1) { + /* Read shunt resistor voltage, in millivolts */ + ina220_read_shunt(&dev, &val); + printf("shunt: %6d", val); + + /* Read VBUS voltage, in millivolts */ + ina220_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); + printf("\tbus: %6d", val); + + /* Read current register, the scale depends on the value of the + * calibration register */ + ina220_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); + printf("\tpower: %6d\n", val); + + vtimer_usleep(SLEEP); + } + + return 0; +}