mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-16 22:52:43 +01:00
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
This commit is contained in:
parent
f424421f79
commit
a6476bd813
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Eistec AB
|
* 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
|
* 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
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
@ -16,30 +17,43 @@
|
|||||||
* Interface
|
* Interface
|
||||||
*
|
*
|
||||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||||
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||||
*
|
*
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "ina2xx.h"
|
#include "ina2xx.h"
|
||||||
#include "ina2xx-regs.h"
|
#include "ina2xx_defines.h"
|
||||||
#include "periph/i2c.h"
|
#include "periph/i2c.h"
|
||||||
#include "byteorder.h"
|
#include "byteorder.h"
|
||||||
|
|
||||||
#define ENABLE_DEBUG (0)
|
#define ENABLE_DEBUG (0)
|
||||||
#include "debug.h"
|
#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. */
|
/** @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)
|
static int ina2xx_read_reg(const ina2xx_t *dev, uint8_t reg, uint16_t *out)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
uint8_t c[2];
|
uint8_t c[2];
|
||||||
uint16_t u16;
|
uint16_t u16;
|
||||||
} tmp = { .u16 = 0 };
|
} tmp;
|
||||||
int status = 0;
|
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) {
|
if (status < 0) {
|
||||||
return status;
|
return status;
|
||||||
@ -55,12 +69,13 @@ static int ina2xx_write_reg(const ina2xx_t *dev, uint8_t reg, uint16_t in)
|
|||||||
union {
|
union {
|
||||||
uint8_t c[2];
|
uint8_t c[2];
|
||||||
uint16_t u16;
|
uint16_t u16;
|
||||||
} tmp = { .u16 = 0 };
|
} tmp;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
tmp.u16 = htons(in);
|
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) {
|
if (status < 0) {
|
||||||
return status;
|
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 */
|
uint16_t config;
|
||||||
dev->i2c = i2c;
|
int status;
|
||||||
dev->addr = address;
|
if (!dev || !params) {
|
||||||
return 0;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ina2xx_set_calibration(const ina2xx_t *dev, uint16_t calibration)
|
dev->params = *params;
|
||||||
{
|
/* Reset device */
|
||||||
return ina2xx_write_reg(dev, INA2XX_REG_CALIBRATION, calibration);
|
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)
|
/* Check if default config is preset after reset */
|
||||||
{
|
status = ina2xx_read_reg(dev, INA2XX_REG_CONFIGURATION, &config);
|
||||||
return ina2xx_write_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)
|
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);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ina2xx_read_current(const ina2xx_t *dev, int16_t *current)
|
/* The voltage given by bits 15-3 in steps of 4 mV
|
||||||
{
|
* ==> Take bits 15-3, shift 3 bits right, multiply by 4
|
||||||
return ina2xx_read_reg(dev, INA2XX_REG_CURRENT, (uint16_t *)current);
|
* ==> 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ina2xx_read_power(const ina2xx_t *dev, int16_t *power)
|
return (tmp & INA2XX_VBUS_CNVR) ? 1 : 0;
|
||||||
{
|
}
|
||||||
return ina2xx_read_reg(dev, INA2XX_REG_POWER, (uint16_t *)power);
|
|
||||||
|
int ina2xx_read_current(const ina2xx_t *dev, int32_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, uint32_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;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Eistec AB
|
* 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
|
* 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
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
@ -11,14 +12,15 @@
|
|||||||
* @{
|
* @{
|
||||||
*
|
*
|
||||||
* @file
|
* @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
|
* Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface
|
||||||
*
|
*
|
||||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||||
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef INA2XX_REGS_H
|
#ifndef INA2XX_DEFINES_H
|
||||||
#define INA2XX_REGS_H
|
#define INA2XX_DEFINES_H
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -40,10 +42,25 @@ typedef enum ina2xx_reg {
|
|||||||
INA2XX_REG_CALIBRATION = 0x05, /**< Calibration register (read/write) */
|
INA2XX_REG_CALIBRATION = 0x05, /**< Calibration register (read/write) */
|
||||||
} ina2xx_reg_t;
|
} 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* INA2XX_REGS_H */
|
#endif /* INA2XX_DEFINES_H */
|
||||||
/** @} */
|
/** @} */
|
109
drivers/ina2xx/include/ina2xx_params.h
Normal file
109
drivers/ina2xx/include/ina2xx_params.h
Normal file
@ -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 <marian.buschsieweke@ovgu.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
/** @} */
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Eistec AB
|
* 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
|
* 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
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
@ -9,20 +10,38 @@
|
|||||||
/**
|
/**
|
||||||
* @defgroup drivers_ina2xx INA2XX current/power monitor
|
* @defgroup drivers_ina2xx INA2XX current/power monitor
|
||||||
* @ingroup drivers_sensors
|
* @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
|
* Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface
|
||||||
*
|
*
|
||||||
* # Supported Hardware
|
* # Supported Hardware
|
||||||
* This driver currently supports the INA219 and the INA220 - and possibly
|
* This driver currently supports the INA219 and the INA2XX - and possibly
|
||||||
* other devices of the INA2xx family.
|
* 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
|
* @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
|
* Bi-Directional CURRENT/POWER MONITOR with Two-Wire Interface
|
||||||
*
|
*
|
||||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||||
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef INA2XX_H
|
#ifndef INA2XX_H
|
||||||
@ -36,14 +55,6 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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
|
* @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_CONTINUOUS_SHUNT_BUS = 0x0007, /**< Shunt and Bus, Continuous, default */
|
||||||
} ina2xx_mode_t;
|
} 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
|
* @brief Shunt ADC settings
|
||||||
*
|
*
|
||||||
@ -140,51 +133,83 @@ typedef enum ina2xx_badc {
|
|||||||
INA2XX_BADC_AVG_128_SAMPLES = 0x0780,
|
INA2XX_BADC_AVG_128_SAMPLES = 0x0780,
|
||||||
} ina2xx_badc_t;
|
} 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
|
* @brief Initialize a current sensor
|
||||||
*
|
*
|
||||||
* @param[out] dev device descriptor of sensor to initialize
|
* @param[out] dev device descriptor of sensor to initialize
|
||||||
* @param[in] i2c I2C bus the sensor is connected to
|
* @param[in] params Configuration parameters to use
|
||||||
* @param[in] address I2C slave address of the sensor
|
|
||||||
*
|
*
|
||||||
* @return 0 on success
|
* @return `0` on success, `<0` on error.
|
||||||
* @return <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
|
* @brief Read shunt voltage in E-05 V
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*
|
*
|
||||||
* @param[in] dev device descriptor of sensor
|
* @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 success
|
||||||
* @return <0 on error
|
* @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);
|
int ina2xx_read_shunt(const ina2xx_t *dev, int16_t *voltage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read bus voltage register
|
* @brief Read bus voltage in mV
|
||||||
*
|
|
||||||
* 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[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, `>= 0` on success
|
||||||
* @return <0 on error
|
* @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[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 success
|
||||||
* @return <0 on error
|
* @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[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 success
|
||||||
* @return <0 on error
|
* @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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
include ../Makefile.tests_common
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
USEMODULE += ina220
|
USEMODULE += fmt_table
|
||||||
USEMODULE += xtimer
|
USEMODULE += ina219
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
include $(RIOTBASE)/Makefile.include
|
include $(RIOTBASE)/Makefile.include
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
# About
|
# 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.
|
monitor driver.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
This test application will initialize the sensor with the following parameters:
|
This test application will initialize the sensor with the default parameters
|
||||||
- ADC resolution: 12 bit
|
in @ref ina2xx_params.h
|
||||||
- Sampling time: 532 us
|
|
||||||
- Calibration register: 4096
|
|
||||||
|
|
||||||
After initialization, the sensor reads the measurement values every 100ms
|
After initialization, the application will print a table of the devices
|
||||||
and prints them to the STDOUT.
|
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.)
|
||||||
|
@ -14,88 +14,90 @@
|
|||||||
* @brief Test application for the INA2XX sensor driver
|
* @brief Test application for the INA2XX sensor driver
|
||||||
*
|
*
|
||||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||||
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||||
*
|
*
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TEST_INA2XX_I2C
|
#include <errno.h>
|
||||||
#error "TEST_INA2XX_I2C not defined"
|
#include <stdlib.h>
|
||||||
#endif
|
|
||||||
#ifndef TEST_INA2XX_ADDR
|
|
||||||
#error "TEST_INA2XX_ADDR not defined"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include "fmt.h"
|
||||||
|
#include "fmt_table.h"
|
||||||
#include "xtimer.h"
|
|
||||||
#include "ina2xx.h"
|
#include "ina2xx.h"
|
||||||
|
#include "ina2xx_params.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)
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
ina2xx_t dev;
|
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... ",
|
print_str("Initializing INA2XX sensor at I2C_");
|
||||||
TEST_INA2XX_I2C, TEST_INA2XX_ADDR);
|
print_s32_dec(ina2xx_params[0].i2c);
|
||||||
if (ina2xx_init(&dev, TEST_INA2XX_I2C, TEST_INA2XX_ADDR) == 0) {
|
print_str(", address 0x");
|
||||||
puts("[OK]\n");
|
print_u32_hex(ina2xx_params[0].addr);
|
||||||
|
print_str("\n");
|
||||||
|
if (ina2xx_init(&dev, &ina2xx_params[0]) == 0) {
|
||||||
|
print_str("[OK]\n");
|
||||||
} else {
|
} else {
|
||||||
puts("[Failed]");
|
print_str("[Failed]\n");
|
||||||
return 1;
|
return EXIT_FAILURE;
|
||||||
}
|
|
||||||
puts("Set configuration register");
|
|
||||||
if (ina2xx_set_config(&dev, CONFIG) == 0) {
|
|
||||||
puts("[OK]\n");
|
|
||||||
} else {
|
|
||||||
puts("[Failed]");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
puts("Set calibration register");
|
const char *line = "+------------+--------------+----------+--------+\n";
|
||||||
if (ina2xx_set_calibration(&dev, CALIBRATION) == 0) {
|
print_str(line);
|
||||||
puts("[OK]\n");
|
print_str("| U_Bus [mV] | U_Shunt [µV] | I [µA] | P [µW] |\n");
|
||||||
} else {
|
print_str(line);
|
||||||
puts("[Failed]");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
/* Read shunt resistor voltage, in millivolts */
|
uint16_t u_bus;
|
||||||
ina2xx_read_shunt(&dev, &val);
|
int16_t u_shunt;
|
||||||
printf("shunt: %6d", val);
|
int32_t i_shunt;
|
||||||
|
uint32_t p;
|
||||||
|
|
||||||
/* Read VBUS voltage, in millivolts */
|
/* Read bus voltage until flag indicates new value is present */
|
||||||
ina2xx_read_bus(&dev, &val);
|
switch (ina2xx_read_bus(&dev, &u_bus)){
|
||||||
/* The bus voltage is found in the topmost 13 bits of the bus voltage
|
case 0:
|
||||||
* register */
|
/* No measurement available yet */
|
||||||
val = (val >> INA2XX_BUS_VOLTAGE_SHIFT);
|
continue;
|
||||||
printf("\tbus: %6d", val);
|
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
|
if (ina2xx_read_shunt(&dev, &u_shunt) < 0) {
|
||||||
* calibration register */
|
print_str("Error while reading shunt voltage\n");
|
||||||
ina2xx_read_current(&dev, &val);
|
return EXIT_FAILURE;
|
||||||
printf("\tcurrent: %6d", val);
|
}
|
||||||
|
|
||||||
/* Read power register, the scale depends on the value of the
|
if (ina2xx_read_current(&dev, &i_shunt) < 0) {
|
||||||
* calibration register */
|
print_str("Error while reading current\n");
|
||||||
ina2xx_read_power(&dev, &val);
|
return EXIT_FAILURE;
|
||||||
printf("\tpower: %6d\n", val);
|
}
|
||||||
|
|
||||||
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;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user