Merge pull request #12055 from fabian18/driver-ina3221

Driver for INA3221 current and power and bus voltage monitor
This commit is contained in:
Marian Buschsieweke 2019-11-05 14:45:03 +01:00 committed by GitHub
commit 4d279ae76e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2997 additions and 1 deletions

View File

@ -214,6 +214,16 @@ ifneq (,$(filter ina220,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
endif
ifneq (,$(filter ina3221,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_i2c
endif
ifneq (,$(filter ina3221_alerts,$(USEMODULE)))
USEMODULE += ina3221
USEMODULE += periph_gpio_irq
endif
ifneq (,$(filter io1_xplained,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_adc

View File

@ -118,6 +118,10 @@ ifneq (,$(filter ina220,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ina220/include
endif
ifneq (,$(filter ina3221,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ina3221/include
endif
ifneq (,$(filter io1_xplained,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/io1_xplained/include
endif

7
drivers/ina3221/Makefile Normal file
View File

@ -0,0 +1,7 @@
# enable submodules
SUBMODULES := 1
# Always build base module and SAUL integration (rely on linker to garbage collect SAUL)
SRC := ina3221.c ina3221_saul.c
include $(RIOTBASE)/Makefile.base

57
drivers/ina3221/alerts.c Normal file
View File

@ -0,0 +1,57 @@
/*
* 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_ina3221
* @{
*
* @file
* @brief Functions to enable/disable GPIO alerts
* for INA3221
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/
#include <errno.h>
#include "periph/gpio.h"
#include "ina3221.h"
int _ina3221_enable_alert(ina3221_t *dev, ina3221_alert_t alert,
ina3221_alert_cb_t cb, void *arg)
{
if (alert >= INA3221_NUM_ALERTS) {
return -ERANGE;
}
if (dev->params.upins.apins.alert_pins[alert] == GPIO_UNDEF) {
return -ENOTSUP;
}
dev->alert_callbacks[alert] = cb;
dev->alert_callback_arguments[alert] = arg;
int check = gpio_init_int(
dev->params.upins.apins.alert_pins[alert],
(dev->params.gpio_config & (1 << alert)) ? GPIO_IN_PU : GPIO_IN,
GPIO_FALLING,
cb,
arg
);
return check ? check : INA3221_OK;
}
int _ina3221_disable_alert(ina3221_t *dev, ina3221_alert_t alert)
{
if (alert >= INA3221_NUM_ALERTS) {
return -ERANGE;
}
if (dev->params.upins.apins.alert_pins[alert] == GPIO_UNDEF) {
return -ENOTSUP;
}
gpio_irq_disable(dev->params.upins.apins.alert_pins[alert]);
return INA3221_OK;
}

686
drivers/ina3221/ina3221.c Normal file
View File

@ -0,0 +1,686 @@
/*
* 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_ina3221
* @{
*
* @file
* @brief Device driver implementation for Texas Instruments INA3221
* three-channel, high-side current and bus voltage
* monitor
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/
#include <errno.h>
#include <string.h>
#include "byteorder.h"
#include "ina3221_internal.h"
#include "ina3221_params.h"
#include "ina3221_regs.h"
#include "ina3221.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
typedef struct {
uint8_t chi_reg_shunt;
uint8_t chi_reg_bus;
uint8_t chi_reg_crit_alert_limit;
uint8_t chi_reg_warn_alert_limit;
} ina3221_channel_info_t;
static ina3221_channel_info_t _chi[INA3221_NUM_CH] = {
{
.chi_reg_shunt = INA3221_REG_CH1_SHUNT_VOLTAGE,
.chi_reg_bus = INA3221_REG_CH1_BUS_VOLTAGE,
.chi_reg_crit_alert_limit = INA3221_REG_CH1_CRIT_ALERT_LIMIT,
.chi_reg_warn_alert_limit = INA3221_REG_CH1_WARN_ALERT_LIMIT
},
{
.chi_reg_shunt = INA3221_REG_CH2_SHUNT_VOLTAGE,
.chi_reg_bus = INA3221_REG_CH2_BUS_VOLTAGE,
.chi_reg_crit_alert_limit = INA3221_REG_CH2_CRIT_ALERT_LIMIT,
.chi_reg_warn_alert_limit = INA3221_REG_CH2_WARN_ALERT_LIMIT
},
{
.chi_reg_shunt = INA3221_REG_CH3_SHUNT_VOLTAGE,
.chi_reg_bus = INA3221_REG_CH3_BUS_VOLTAGE,
.chi_reg_crit_alert_limit = INA3221_REG_CH3_CRIT_ALERT_LIMIT,
.chi_reg_warn_alert_limit = INA3221_REG_CH3_WARN_ALERT_LIMIT
}
};
/**
* @brief Read register value
*
* @param[in] dev Device handle
* @param[in] reg Register address
* @param[out] out Output register value
*
* @post @p out is in host byte order
*
* @return 0, on success
* @return -INA3221_I2C_ERROR, if i2c bus acquirement failed
* @return @see i2c_read_regs
*/
static int _read_reg(const ina3221_t *dev, uint8_t reg, uint16_t *out)
{
if (i2c_acquire(dev->params.i2c)) {
return -INA3221_I2C_ERROR;
}
int status = i2c_read_regs(dev->params.i2c, dev->params.addr, reg, out,
INA3221_REG_LEN, 0);
i2c_release(dev->params.i2c);
if (status < 0) {
return status;
}
*out = ntohs(*out);
return 0;
}
/**
* @brief Write register value
*
* @param[in] dev Device handle
* @param[in] reg Register address
* @param[out] in Input register value
*
* @pre @p in must be in host byte order
*
* @return 0, on success
* @return -INA3221_I2C_ERROR, if i2c bus acquirement failed
* @return @see i2c_write_regs
*/
static int _write_reg(const ina3221_t *dev, uint8_t reg, uint16_t in)
{
in = htons(in);
if (i2c_acquire(dev->params.i2c)) {
return -INA3221_I2C_ERROR;
}
int status = i2c_write_regs(dev->params.i2c, dev->params.addr, reg, &in,
INA3221_REG_LEN, 0);
i2c_release(dev->params.i2c);
if (status < 0) {
return status;
}
return 0;
}
int ina3221_reset(ina3221_t *dev)
{
uint16_t config;
int status = _write_reg(dev, INA3221_REG_CONFIGURATION, INA3221_RESET);
if (status < 0) {
return status;
}
/* Check if default config is present after reset */
status = _read_reg(dev, INA3221_REG_CONFIGURATION, &config);
if (status < 0) {
return status;
}
if (config != INA3221_DEFCONFIG) {
return -INA3221_RESET_FAILED;
}
dev->params.config = INA3221_DEFCONFIG;
return INA3221_OK;
}
int ina3221_init(ina3221_t *dev, const ina3221_params_t *params)
{
int status;
if (!dev || !params) {
return -EINVAL;
}
dev->params = *params;
uint16_t id;
status = _read_reg(dev, INA3221_REG_MANUFACTURER_ID, &id);
if (status < 0) {
return status;
}
if (id != INA3221_MANUFACTURER_ID) {
return -INA3221_BAD_MANUF_ID;
}
status = _read_reg(dev, INA3221_REG_DIE_ID, &id);
if (status < 0) {
return status;
}
if (id != INA3221_DIE_ID) {
return -INA3221_BAD_DIE_ID;
}
if (ina3221_reset(dev) != INA3221_OK) {
return -INA3221_RESET_FAILED;
}
if (_ina3221_set_config(dev, params->config) != INA3221_OK) {
return -INA3221_CONFIG_FAILED;
}
uint16_t cfg;
if (_ina3221_get_config(dev, &cfg) != INA3221_OK || cfg != params->config) {
return -INA3221_CONFIG_FAILED;
}
#if defined(MODULE_INA3221_ALERTS) || defined(DOXYGEN)
memset(dev->alert_callbacks, 0, sizeof(dev->alert_callbacks));
memset(dev->alert_callback_arguments, 0,
sizeof(dev->alert_callback_arguments));
#endif
return INA3221_OK;
}
int _ina3221_set_config(ina3221_t *dev, uint16_t cfg)
{
cfg &= ~INA3221_RESET; /* prevent accidental reset */
int status = _write_reg(dev, INA3221_REG_CONFIGURATION, cfg);
if (status < 0) {
return status;
}
dev->params.config = cfg;
return INA3221_OK;
}
int _ina3221_get_config(const ina3221_t *dev, uint16_t *cfg)
{
*cfg = dev->params.config;
*cfg &= ~INA3221_RESET; /* clear reset flag */
return INA3221_OK;
}
int _ina3221_set_enable_channel(ina3221_t *dev, ina3221_enable_ch_t ech)
{
uint16_t cfg;
int status = _read_reg(dev, INA3221_REG_CONFIGURATION, &cfg);
if (status < 0) {
return status;
}
cfg &= ~INA3221_ENABLE_CH_MASK;
cfg |= (ech & INA3221_ENABLE_CH_MASK);
status = _write_reg(dev, INA3221_REG_CONFIGURATION, cfg);
if (status < 0) {
return status;
}
dev->params.config = cfg;
return INA3221_OK;
}
int _ina3221_get_enable_channel(const ina3221_t *dev, ina3221_enable_ch_t *ech)
{
*ech = dev->params.config & INA3221_ENABLE_CH_MASK;
return ((*ech & INA3221_ENABLE_CH1) ? 1 : 0) +
((*ech & INA3221_ENABLE_CH2) ? 1 : 0) +
((*ech & INA3221_ENABLE_CH3) ? 1 : 0);
}
int ina3221_set_num_samples(ina3221_t *dev, ina3221_num_samples_t ns)
{
uint16_t cfg;
int status = _read_reg(dev, INA3221_REG_CONFIGURATION, &cfg);
if (status < 0) {
return status;
}
cfg &= ~INA3221_NUM_SAMPLES_MASK;
cfg |= (ns & INA3221_NUM_SAMPLES_MASK);
status = _write_reg(dev, INA3221_REG_CONFIGURATION, cfg);
if (status < 0) {
return status;
}
dev->params.config = cfg;
return INA3221_OK;
}
int ina3221_get_num_samples(const ina3221_t *dev, ina3221_num_samples_t *ns)
{
*ns = dev->params.config & INA3221_NUM_SAMPLES_MASK;
return INA3221_OK;
}
int ina3221_set_conv_time_bus_adc(ina3221_t *dev,
ina3221_conv_time_bus_adc_t ctb)
{
uint16_t cfg;
int status = _read_reg(dev, INA3221_REG_CONFIGURATION, &cfg);
if (status < 0) {
return status;
}
cfg &= ~INA3221_CONV_TIME_BADC_MASK;
cfg |= (ctb & INA3221_CONV_TIME_BADC_MASK);
status = _write_reg(dev, INA3221_REG_CONFIGURATION, cfg);
if (status < 0) {
return status;
}
dev->params.config = cfg;
return INA3221_OK;
}
int ina3221_get_conv_time_bus_adc(const ina3221_t *dev,
ina3221_conv_time_bus_adc_t *ctb)
{
*ctb = dev->params.config & INA3221_CONV_TIME_BADC_MASK;
return INA3221_OK;
}
int ina3221_set_conv_time_shunt_adc(ina3221_t *dev,
ina3221_conv_time_shunt_adc_t ctb)
{
uint16_t cfg;
int status = _read_reg(dev, INA3221_REG_CONFIGURATION, &cfg);
if (status < 0) {
return status;
}
cfg &= ~INA3221_CONV_TIME_SADC_MASK;
cfg |= (ctb & INA3221_CONV_TIME_SADC_MASK);
status = _write_reg(dev, INA3221_REG_CONFIGURATION, cfg);
if (status < 0) {
return status;
}
dev->params.config = cfg;
return INA3221_OK;
}
int ina3221_get_conv_time_shunt_adc(const ina3221_t *dev,
ina3221_conv_time_shunt_adc_t *ctb)
{
*ctb = dev->params.config & INA3221_CONV_TIME_SADC_MASK;
return INA3221_OK;
}
int ina3221_set_mode(ina3221_t *dev, ina3221_mode_t mode)
{
uint16_t cfg;
int status = _read_reg(dev, INA3221_REG_CONFIGURATION, &cfg);
if (status < 0) {
return status;
}
cfg &= ~INA3221_MODE_MASK;
cfg |= (mode & INA3221_MODE_MASK);
status = _write_reg(dev, INA3221_REG_CONFIGURATION, cfg);
if (status < 0) {
return status;
}
dev->params.config = cfg;
return INA3221_OK;
}
int ina3221_get_mode(const ina3221_t *dev, ina3221_mode_t *mode)
{
*mode = dev->params.config & INA3221_MODE_MASK;
return INA3221_OK;
}
int _ina3221_set_enable_sum_channel(const ina3221_t *dev,
ina3221_enable_sum_ch_t esch)
{
uint16_t mask_en;
int status = _read_reg(dev, INA3221_REG_MASK_ENABLE, &mask_en);
if (status < 0) {
return status;
}
mask_en &= ~INA3221_ENABLE_SUM_CH_MASK;
mask_en |= (esch & INA3221_ENABLE_SUM_CH_MASK);
status = _write_reg(dev, INA3221_REG_MASK_ENABLE, mask_en);
if (status < 0) {
return status;
}
return INA3221_OK;
}
int _ina3221_get_enable_sum_channel(const ina3221_t *dev,
ina3221_enable_sum_ch_t *esch)
{
uint16_t mask_en;
int status = _read_reg(dev, INA3221_REG_MASK_ENABLE, &mask_en);
if (status < 0) {
return status;
}
*esch = mask_en & (INA3221_ENABLE_SUM_CH_MASK);
return ((*esch & INA3221_ENABLE_SUM_CH1) ? 1 : 0) +
((*esch & INA3221_ENABLE_SUM_CH2) ? 1 : 0) +
((*esch & INA3221_ENABLE_SUM_CH3) ? 1 : 0);
}
int ina3221_set_latch(const ina3221_t *dev, ina3221_enable_latch_t latch)
{
uint16_t mask_en;
int status = _read_reg(dev, INA3221_REG_MASK_ENABLE, &mask_en);
if (status < 0) {
return status;
}
mask_en &= ~INA3221_ENABLE_LATCH_MASK;
mask_en |= (latch & INA3221_ENABLE_LATCH_MASK);
status = _write_reg(dev, INA3221_REG_MASK_ENABLE, mask_en);
if (status < 0) {
return status;
}
return INA3221_OK;
}
int ina3221_get_latch(const ina3221_t *dev, ina3221_enable_latch_t *latch)
{
uint16_t mask_en;
int status = _read_reg(dev, INA3221_REG_MASK_ENABLE, &mask_en);
if (status < 0) {
return status;
}
*latch = mask_en & INA3221_ENABLE_LATCH_MASK;
return INA3221_OK;
}
int ina3221_set_crit_alert_limit(const ina3221_t *dev, ina3221_channel_t ch,
int32_t in_uv)
{
if (in_uv < INA3221_MIN_SHUNT_UV || in_uv > INA3221_MAX_SHUNT_UV) {
return -ERANGE;
}
int16_t reg_val = shunt_voltage_uv_to_reg_val(in_uv);
int status = INA3221_OK;
int i, j;
for (i = 0, j = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
status = _write_reg(dev, _chi[i].chi_reg_crit_alert_limit, reg_val);
if (status < 0) {
break;
}
j++;
}
}
return j ? j : status;
}
int ina3221_get_crit_alert_limit(const ina3221_t *dev, ina3221_channel_t ch,
int32_t *out_uv)
{
uint16_t reg_val;
int status = INA3221_OK;
int i, j;
for (i = 0, j = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
status = _read_reg(dev, _chi[i].chi_reg_crit_alert_limit, &reg_val);
if (status < 0) {
break;
}
out_uv[j++] = reg_val_to_shunt_voltage_uv(reg_val);
}
}
return j ? j : status;
}
int ina3221_set_warn_alert_limit(const ina3221_t *dev, ina3221_channel_t ch,
int32_t in_uv)
{
if (in_uv < INA3221_MIN_SHUNT_UV || in_uv > INA3221_MAX_SHUNT_UV) {
return -ERANGE;
}
int16_t reg_val = shunt_voltage_uv_to_reg_val(in_uv);
int status = INA3221_OK;
int i, j;
for (i = 0, j = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
status = _write_reg(dev, _chi[i].chi_reg_warn_alert_limit, reg_val);
if (status < 0) {
break;
}
j++;
}
}
return j ? j : status;
}
int ina3221_get_warn_alert_limit(const ina3221_t *dev, ina3221_channel_t ch,
int32_t *out_uv)
{
uint16_t reg_val;
int status = INA3221_OK;
int i, j;
for (i = 0, j = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
status = _read_reg(dev, _chi[i].chi_reg_warn_alert_limit, &reg_val);
if (status < 0) {
break;
}
out_uv[j++] = reg_val_to_shunt_voltage_uv(reg_val);
}
}
return j ? j : status;
}
int ina3221_set_shunt_voltage_sum_alert_limit(const ina3221_t *dev,
int32_t in_uv)
{
if (in_uv < INA3221_MIN_SHUNT_SUM_UV || in_uv > INA3221_MAX_SHUNT_SUM_UV) {
return -ERANGE;
}
int16_t reg_val = sum_shunt_voltage_uv_to_reg_val(in_uv);
int status = _write_reg(dev, INA3221_REG_SHUNT_VOLTAGE_SUM_LIMIT, reg_val);
if (status < 0) {
return status;
}
return INA3221_OK;
}
int ina3221_get_shunt_voltage_sum_alert_limit(const ina3221_t *dev,
int32_t *out_uv)
{
uint16_t reg_val;
int status = _read_reg(dev, INA3221_REG_SHUNT_VOLTAGE_SUM_LIMIT, &reg_val);
if (status < 0) {
return status;
}
*out_uv = sum_reg_val_to_shunt_voltage_uv(reg_val);
return INA3221_OK;
}
int ina3221_set_power_valid_upper_limit(const ina3221_t *dev, int32_t in_mv)
{
if (in_mv < INA3221_MIN_BUS_MV || in_mv > INA3221_MAX_BUS_MV) {
return -ERANGE;
}
int16_t reg_val = bus_voltage_mv_to_reg_val(in_mv);
int status = _write_reg(dev, INA3221_REG_PV_UPPER_LIMIT, reg_val);
if (status < 0) {
return status;
}
return INA3221_OK;
}
int ina3221_get_power_valid_upper_limit(const ina3221_t *dev, int32_t *out_mv)
{
uint16_t reg_val;
int status = _read_reg(dev, INA3221_REG_PV_UPPER_LIMIT, &reg_val);
if (status < 0) {
return status;
}
*out_mv = reg_val_to_bus_voltage_mv(reg_val);
return INA3221_OK;
}
int ina3221_set_power_valid_lower_limit(const ina3221_t *dev, int32_t in_mv)
{
if (in_mv < INA3221_MIN_BUS_MV || in_mv > INA3221_MAX_BUS_MV) {
return -ERANGE;
}
int16_t reg_val = bus_voltage_mv_to_reg_val(in_mv);
int status = _write_reg(dev, INA3221_REG_PV_LOWER_LIMIT, reg_val);
if (status < 0) {
return status;
}
return INA3221_OK;
}
int ina3221_get_power_valid_lower_limit(const ina3221_t *dev, int32_t *out_mv)
{
uint16_t reg_val;
int status = _read_reg(dev, INA3221_REG_PV_LOWER_LIMIT, &reg_val);
if (status < 0) {
return status;
}
*out_mv = reg_val_to_bus_voltage_mv(reg_val);
return INA3221_OK;
}
int ina3221_read_flags(const ina3221_t *dev, uint16_t *flags)
{
uint16_t reg_val;
int status = _read_reg(dev, INA3221_REG_MASK_ENABLE, &reg_val);
if (status < 0) {
return status;
}
*flags = reg_val & INA3221_FLAGS_MASK;
return INA3221_OK;
}
int ina3221_read_shunt_sum_uv(const ina3221_t *dev, int32_t *out_uv,
uint16_t *flags)
{
uint16_t reg_val;
int status = _read_reg(dev, INA3221_REG_SHUNT_VOLTAGE_SUM, &reg_val);
if (status < 0) {
return status;
}
*out_uv = sum_reg_val_to_shunt_voltage_uv(reg_val);
if (flags) {
status = _read_reg(dev, INA3221_REG_MASK_ENABLE, flags);
if (status < 0) {
*flags = 0;
DEBUG("ina3221_read_shunt_sum_uv: Reading flags failed\n");
}
else {
*flags &= INA3221_FLAGS_MASK;
}
}
return INA3221_OK;
}
int ina3221_read_shunt_uv(const ina3221_t *dev, ina3221_channel_t ch,
int32_t *out_uv, uint16_t *flags)
{
uint16_t reg_val;
int status = INA3221_OK;
int i, j;
for (i = 0, j = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
status = _read_reg(dev, _chi[i].chi_reg_shunt, &reg_val);
if (status < 0) {
break;
}
out_uv[j++] = reg_val_to_shunt_voltage_uv(reg_val);
}
}
if (j && flags) {
status = _read_reg(dev, INA3221_REG_MASK_ENABLE, flags);
if (status < 0) {
*flags = 0;
DEBUG("ina3221_read_shunt_uv: Reading flags failed\n");
}
else {
*flags &= INA3221_FLAGS_MASK;
}
}
return j ? j : status;
}
int ina3221_read_bus_mv(const ina3221_t *dev, ina3221_channel_t ch,
int16_t *out_mv, uint16_t *flags)
{
uint16_t reg_val;
int status = INA3221_OK;
int i, j = 0;
for (i = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
status = _read_reg(dev, _chi[i].chi_reg_bus, &reg_val);
if (status < 0) {
break;
}
out_mv[j++] = reg_val_to_bus_voltage_mv(reg_val);
}
}
if (j && flags) {
status = _read_reg(dev, INA3221_REG_MASK_ENABLE, flags);
if (status < 0) {
*flags = 0;
DEBUG("ina3221_read_bus_mv: Reading flags failed\n");
}
else {
*flags &= INA3221_FLAGS_MASK;
}
}
return j ? j : status;
}
int ina3221_calculate_current_ua(const ina3221_t *dev, ina3221_channel_t ch,
int32_t *in_uv, int32_t *out_ua)
{
int i, j = 0;
for (i = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
out_ua[j] = in_uv[j] * 1000 / dev->params.rshunt_mohm[i];
j++;
}
}
return j;
}
int ina3221_calculate_power_uw(int16_t *in_mv, int32_t *in_ua, uint8_t num,
int32_t *out_uw)
{
if (num > INA3221_NUM_CH) {
return -ERANGE;
}
int i;
for (i = 0; i < num; i++) {
/* max 26V bus voltage */
/* (2^31)-1 resolution; 2.147483647 Watt in Nanowatt resolutiona */
/* 2.147483647 / 26000 = 82595.525 */
if (in_ua[i] < (82596 - 500)) {
out_uw[i] = (in_ua[i] * in_mv[i] + 500) / 1000;
}
else {
out_uw[i] = (in_ua[i] + 500) / 1000 * in_mv[i];
}
}
return i;
}
void ina3221_ch_align(ina3221_channel_t ch, const void *in_res, void *out_res,
size_t res_val_size)
{
uint8_t *in = (uint8_t *)in_res;
uint8_t tmp_out[INA3221_NUM_CH][res_val_size];
int j = 0;
for (int i = 0; i < INA3221_NUM_CH; i++) {
if (ch & (1 << i)) {
memcpy(&tmp_out[i][0], in + j * res_val_size, res_val_size);
j++;
}
else {
memset(&tmp_out[i][0], 0, res_val_size);
}
}
memcpy(out_res, tmp_out, sizeof(tmp_out));
}

View File

@ -0,0 +1,178 @@
/*
* 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_ina3221
* @{
*
* @file
* @brief SAUL adaption for INA3221 device
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/
#include <errno.h>
#include "phydat.h"
#include "saul.h"
#include "ina3221.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define SAUL_INA3221_NO_VALUE (0)
static int read_bus_voltage(const void *dev, phydat_t *res)
{
ina3221_t *_dev = (ina3221_t *)dev;
ina3221_enable_ch_t ench = 0;
int16_t voltage[INA3221_NUM_CH] = { 0 };
int num_ch = _ina3221_get_enable_channel(_dev, &ench);
if (!num_ch) {
return -ECANCELED;
}
ina3221_channel_t ch = (((ench & INA3221_ENABLE_CH1) ? INA3221_CH1 : 0) |
((ench & INA3221_ENABLE_CH2) ? INA3221_CH2 : 0) |
((ench & INA3221_ENABLE_CH3) ? INA3221_CH3 : 0));
ina3221_read_bus_mv(_dev, ch, voltage, NULL);
res->scale = -3; /* mV to V*/
res->unit = UNIT_V;
for (int i = 0, j = 0; i < INA3221_NUM_CH; i++) {
res->val[i] = (ch & (1 << i))
? voltage[j++] : SAUL_INA3221_NO_VALUE;
}
return INA3221_NUM_CH;
}
static int read_current(const void *dev, phydat_t *res)
{
ina3221_t *_dev = (ina3221_t *)dev;
ina3221_enable_ch_t ench = 0;
int32_t shunt_uv[INA3221_NUM_CH] = { 0 };
int32_t current[INA3221_NUM_CH] = { 0 };
int num_ch = _ina3221_get_enable_channel(_dev, &ench);
if (!num_ch) {
return -ECANCELED;
}
ina3221_channel_t ch = (((ench & INA3221_ENABLE_CH1) ? INA3221_CH1 : 0) |
((ench & INA3221_ENABLE_CH2) ? INA3221_CH2 : 0) |
((ench & INA3221_ENABLE_CH3) ? INA3221_CH3 : 0));
ina3221_read_shunt_uv(_dev, ch, shunt_uv, NULL);
ina3221_calculate_current_ua(_dev, ch, shunt_uv, current);
res->scale = -6; /* uA to A*/
res->unit = UNIT_A;
for (int i = 0, j = 0; i < INA3221_NUM_CH; i++) {
res->val[i] = (ch & (1 << i))
? (int16_t)current[j++] : SAUL_INA3221_NO_VALUE;
}
ina3221_ch_align(ch, current, current, sizeof(int32_t));
phydat_fit(res, current, 3);
return INA3221_NUM_CH;
}
static int read_power(const void *dev, phydat_t *res)
{
ina3221_t *_dev = (ina3221_t *)dev;
ina3221_enable_ch_t ench = 0;
int32_t shunt_uv[INA3221_NUM_CH] = { 0 };
int32_t current_ua[INA3221_NUM_CH] = { 0 };
int16_t bus_mv[INA3221_NUM_CH] = { 0 };
int32_t power[INA3221_NUM_CH] = { 0 };
int num_ch = _ina3221_get_enable_channel(_dev, &ench);
if (!num_ch) {
return -ECANCELED;
}
ina3221_channel_t ch = (((ench & INA3221_ENABLE_CH1) ? INA3221_CH1 : 0) |
((ench & INA3221_ENABLE_CH2) ? INA3221_CH2 : 0) |
((ench & INA3221_ENABLE_CH3) ? INA3221_CH3 : 0));
int num_ch_shunt_uv = ina3221_read_shunt_uv(_dev, ch, shunt_uv, NULL);
ina3221_calculate_current_ua(_dev, ch, shunt_uv, current_ua);
ina3221_read_bus_mv(_dev, ch, bus_mv, NULL);
ina3221_calculate_power_uw(bus_mv, current_ua, num_ch_shunt_uv, power);
res->scale = -6; /* uW to W*/
res->unit = UNIT_W;
for (int i = 0, j = 0; i < INA3221_NUM_CH; i++) {
res->val[i] = (ch & (1 << i))
? (int16_t)power[j++] : SAUL_INA3221_NO_VALUE;
}
ina3221_ch_align(ch, power, power, sizeof(int32_t));
phydat_fit(res, power, 3);
return INA3221_NUM_CH;
}
static int read_shunt_voltage_sum(const void *dev, phydat_t *res)
{
ina3221_t *_dev = (ina3221_t *)dev;
ina3221_enable_ch_t ench = 0;
int32_t shunt_voltage_sum = SAUL_INA3221_NO_VALUE;
int num_ch = _ina3221_get_enable_channel(_dev, &ench);
if (!num_ch) {
return -ECANCELED;
}
ina3221_read_shunt_sum_uv(_dev, &shunt_voltage_sum, NULL);
res->scale = -6; /* uV to V*/
res->unit = UNIT_V;
res->val[0] = shunt_voltage_sum;
phydat_fit(res, &shunt_voltage_sum, 1);
return 1;
}
static int configure_channel(const void *dev, phydat_t *data)
{
ina3221_t *_dev = (ina3221_t *)dev;
ina3221_enable_ch_t ench =
((data->val[0] & INA3221_CH1) ? INA3221_ENABLE_CH1 : 0) |
((data->val[0] & INA3221_CH2) ? INA3221_ENABLE_CH2 : 0) |
((data->val[0] & INA3221_CH3) ? INA3221_ENABLE_CH3 : 0);
if (_ina3221_set_enable_channel(_dev, ench) != INA3221_OK) {
return -ECANCELED;
}
return INA3221_NUM_CH;
}
static int configure_channel_sum(const void *dev, phydat_t *data)
{
ina3221_t *_dev = (ina3221_t *)dev;
ina3221_enable_sum_ch_t esch =
((data->val[0] & INA3221_CH1) ? INA3221_ENABLE_SUM_CH1 : 0) |
((data->val[0] & INA3221_CH2) ? INA3221_ENABLE_SUM_CH2 : 0) |
((data->val[0] & INA3221_CH3) ? INA3221_ENABLE_SUM_CH3 : 0);
if (_ina3221_set_enable_sum_channel(_dev, esch) != INA3221_OK) {
return -ECANCELED;
}
return INA3221_NUM_CH;
}
const saul_driver_t ina3221_saul_drivers[] = {
{
.read = read_bus_voltage,
.write = configure_channel,
.type = SAUL_SENSE_VOLTAGE
},
{
.read = read_current,
.write = configure_channel,
.type = SAUL_SENSE_CURRENT
},
{
.read = read_power,
.write = configure_channel,
.type = SAUL_SENSE_POWER
},
{
.read = read_shunt_voltage_sum,
.write = configure_channel_sum,
.type = SAUL_SENSE_VOLTAGE
}
};

View File

@ -0,0 +1,60 @@
/*
* 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_ina3221
* @{
*
* @file
* @brief Internal definitions for Texas Instruments INA3221
* three-channel, high-side current and bus voltage
* monitor
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*/
#ifndef INA3221_DEFINES_H
#define INA3221_DEFINES_H
#ifdef __cplusplus
extern "C" {
#endif
#define INA3221_REG_ADDR_LEN (1) /**< Register address length */
#define INA3221_REG_LEN (2) /**< Register length */
#define INA3221_SHUNT_VOLTAGE_PRECISION_UV (40) /**< Shunt voltage measurement precision */
#define INA3221_BUS_VOLTAGE_PRECISION_MV (8) /**< Bus voltage measurement precision */
#define INA3221_MANUFACTURER_ID (0x5449) /**< INA3221 manufacturer ID */
#define INA3221_DIE_ID (0x3220) /**< INA3221 DIE ID */
/**
* @name INA3221 Limits
* @{
*/
#define INA3221_MAX_SHUNT_REG_VAL ((int16_t)(0x7FF8)) /**< 0111 1111 1111 1000 */
#define INA3221_MIN_SHUNT_REG_VAL ((int16_t)(0x8000)) /**< 1000 0000 0000 0000 */
#define INA3221_MAX_BUS_REG_VAL ((int16_t)(0x7FF8)) /**< 0111 1111 1111 1000 */
#define INA3221_MIN_BUS_REG_VAL ((int16_t)(0x8000)) /**< 1000 0000 0000 0000 */
#define INA3221_MAX_SHUNT_SUM_REG_VAL ((int16_t)(0x7FFE)) /**< 0111 1111 1111 1110 */
#define INA3221_MIN_SHUNT_SUM_REG_VAL ((int16_t)(0x8000)) /**< 1000 0000 0000 0000 */
#define INA3221_MAX_SHUNT_UV (163800) /**< Max. measurable shunt voltage value */
#define INA3221_MIN_SHUNT_UV (-163840) /**< Min. measurable shunt voltage value */
#define INA3221_MAX_BUS_MV (26000) /**< Max. measurable bus voltage value */
#define INA3221_MIN_BUS_MV (0) /**< Min. measurable bus voltage value */
#define INA3221_MAX_SHUNT_SUM_UV (655320) /**< Max. measurable shunt sum voltage value */
#define INA3221_MIN_SHUNT_SUM_UV (-655360) /**< Min. measurable shunt sum voltage value */
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* INA3221_DEFINES_H */
/** @} */

View File

@ -0,0 +1,133 @@
/*
* 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_ina3221
* @{
*
* @file
* @brief Functions to convert register values of INA3221
* to actual voltage values and vice versa
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*/
#ifndef INA3221_INTERNAL_H
#define INA3221_INTERNAL_H
#include <assert.h>
#include "ina3221_defines.h"
#include "ina3221.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Convert register value to shunt voltage in uV
*
* @param[in] reg_val Register value
*
* @pre reg_val must be in host byte order
*
* @return Shunt voltage in uV
*/
static inline int32_t reg_val_to_shunt_voltage_uv(int16_t reg_val)
{
assert(reg_val <= INA3221_MAX_SHUNT_REG_VAL);
return reg_val / 8 * INA3221_SHUNT_VOLTAGE_PRECISION_UV;
}
/**
* @brief Convert shunt voltage in uV to register value
*
* @param[in] s_uv Shunt voltage in uV
*
* @post Output register value is in host byte order
*
* @return Register value
*/
static inline int16_t shunt_voltage_uv_to_reg_val(int32_t s_uv)
{
assert(s_uv <= INA3221_MAX_SHUNT_UV);
return s_uv / INA3221_SHUNT_VOLTAGE_PRECISION_UV * 8;
}
/**
* @brief Convert register value to bus voltage in mV
*
* @param[in] reg_val Register value
*
* @pre reg_val must be in host byte order
*
* @return Shunt voltage in mV
*/
static inline int16_t reg_val_to_bus_voltage_mv(int16_t reg_val)
{
assert(reg_val <= INA3221_MAX_BUS_REG_VAL);
return reg_val / 8 * INA3221_BUS_VOLTAGE_PRECISION_MV;
}
/**
* @brief Convert bus voltage in mV to register value
*
* @param[in] b_mv Bus voltage in mV
*
* @post Output register value is in host byte order
*
* @return Register value
*/
static inline int16_t bus_voltage_mv_to_reg_val(int16_t b_mv)
{
assert(b_mv >= INA3221_MIN_BUS_MV);
assert(b_mv <= INA3221_MAX_BUS_MV);
return b_mv / INA3221_BUS_VOLTAGE_PRECISION_MV * 8;
}
/**
* @brief Convert register value to shunt voltage sum value in mV
*
* @param[in] sum_reg_val Register value
*
* @pre sum_reg_val must be in host byte order
*
* @return Shunt voltage sum value in mV
*/
static inline int32_t sum_reg_val_to_shunt_voltage_uv(int16_t sum_reg_val)
{
assert(sum_reg_val <= INA3221_MAX_SHUNT_SUM_REG_VAL);
return sum_reg_val / 2 * INA3221_SHUNT_VOLTAGE_PRECISION_UV;
}
/**
* @brief Convert shunt voltage sum value in uV to register value
*
* @param[in] sum_suv shunt voltage sum value in uV
*
* @post Output register value is in host byte order
*
* @return Register value
*/
static inline int16_t sum_shunt_voltage_uv_to_reg_val(int32_t sum_suv)
{
assert(sum_suv <= INA3221_MAX_SHUNT_SUM_UV);
return sum_suv / INA3221_SHUNT_VOLTAGE_PRECISION_UV * 2;
}
#ifdef __cplusplus
}
#endif
#endif /* INA3221_INTERNAL_H */
/** @} */

View File

@ -0,0 +1,144 @@
/*
* 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_ina3221
* @{
*
* @file
* @brief Driver configuration parameters for Texas Instruments INA3221
* three-channel, high-side current and bus voltage
* monitor
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*/
#ifndef INA3221_PARAMS_H
#define INA3221_PARAMS_H
#include "board.h"
#include "ina3221.h"
#include "saul_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef INA3221_PARAM_I2C
#define INA3221_PARAM_I2C (I2C_DEV(0)) /**< I2C bus index */
#endif
#ifndef INA3221_PARAM_ADDR
#define INA3221_PARAM_ADDR (INA3221_ADDR_00) /**< I2C device address */
#endif
#ifndef INA3221_PARAM_PIN_WRN
#define INA3221_PARAM_PIN_WRN (GPIO_UNDEF) /**< Warning alert pin */
#endif
#ifndef INA3221_PARAM_PIN_CRT
#define INA3221_PARAM_PIN_CRT (GPIO_UNDEF) /**< Critical alert pin */
#endif
#ifndef INA3221_PARAM_PIN_PV
#define INA3221_PARAM_PIN_PV (GPIO_UNDEF) /**< Power valid alert pin */
#endif
#ifndef INA3221_PARAM_PIN_TC
#define INA3221_PARAM_PIN_TC (GPIO_UNDEF) /**< Timing control alert pin */
#endif
#ifndef INA3221_PARAM_INT_PU_PIN_WRN
#define INA3221_PARAM_INT_PU_PIN_WRN (0) /**< Enable or disable internal pull up resistor for pin WRN */
#endif
#ifndef INA3221_PARAM_INT_PU_PIN_CRT
#define INA3221_PARAM_INT_PU_PIN_CRT (0) /**< Enable or disable internal pull up resistor for pin CRT */
#endif
#ifndef INA3221_PARAM_INT_PU_PIN_PV
#define INA3221_PARAM_INT_PU_PIN_PV (0) /**< Enable or disable internal pull up resistor for pin PV */
#endif
#ifndef INA3221_PARAM_INT_PU_PIN_TC
#define INA3221_PARAM_INT_PU_PIN_TC (0) /**< Enable or disable internal pull up resistor for pin TC */
#endif
#ifndef INA3221_PARAM_CONFIG
#define INA3221_PARAM_CONFIG ( \
INA3221_ENABLE_CH1 | \
INA3221_ENABLE_CH2 | \
INA3221_ENABLE_CH3 | \
INA3221_NUM_SAMPLES_4 | \
INA3221_CONV_TIME_BADC_4156US | \
INA3221_CONV_TIME_SADC_4156US | \
INA3221_MODE_CONTINUOUS_SHUNT_BUS \
) /**< Configuration register value */
#endif
#ifndef INA3221_PARAM_RSHUNT_MOHM_CH1
#define INA3221_PARAM_RSHUNT_MOHM_CH1 (100) /**< Channel 1 shunt resistance */
#endif
#ifndef INA3221_PARAM_RSHUNT_MOHM_CH2
#define INA3221_PARAM_RSHUNT_MOHM_CH2 (100) /**< Channel 2 shunt resistance */
#endif
#ifndef INA3221_PARAM_RSHUNT_MOHM_CH3
#define INA3221_PARAM_RSHUNT_MOHM_CH3 (100) /**< Channel 3 shunt resistance */
#endif
#ifndef INA3221_PARAMS
#define INA3221_PARAMS { \
.i2c = INA3221_PARAM_I2C, \
.addr = INA3221_PARAM_ADDR, \
.upins.pins = { \
.pin_warn = INA3221_PARAM_PIN_WRN, \
.pin_crit = INA3221_PARAM_PIN_CRT, \
.pin_tc = INA3221_PARAM_PIN_TC, \
.pin_pv = INA3221_PARAM_PIN_PV \
}, \
.gpio_config = (INA3221_PARAM_INT_PU_PIN_WRN << INA3221_ALERT_WRN) | \
(INA3221_PARAM_INT_PU_PIN_CRT << INA3221_ALERT_CRT) | \
(INA3221_PARAM_INT_PU_PIN_TC << INA3221_ALERT_TC) | \
(INA3221_PARAM_INT_PU_PIN_PV << INA3221_ALERT_PV), \
.config = INA3221_PARAM_CONFIG, \
.rshunt_mohm = { \
INA3221_PARAM_RSHUNT_MOHM_CH1, \
INA3221_PARAM_RSHUNT_MOHM_CH2, \
INA3221_PARAM_RSHUNT_MOHM_CH3 \
} \
} /**< Default device initialization parameters */
#endif
#ifndef INA3221_SAUL_INFO
#define INA3221_SAUL_INFO { .name = "INA3221 bus voltage" }, \
{ .name = "INA3221 current" }, \
{ .name = "INA3221 power" }, \
{ .name = "INA3221 shunt voltage sum" } /**< SAUL driver information */
#endif
/**
* @brief INA3221 array of device configurations
*/
static const ina3221_params_t ina3221_params[] = {
INA3221_PARAMS
};
/**
* @brief INA3221 array of SAUL driver information
*/
static const saul_reg_info_t ina3221_saul_info[] = {
INA3221_SAUL_INFO
};
#ifdef __cplusplus
}
#endif
#endif /* INA3221_PARAMS_H */
/** @} */

View File

@ -0,0 +1,58 @@
/*
* 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_ina3221
* @{
*
* @file
* @brief Register definitions for Texas Instruments INA3221
* three-channel, high-side current and bus voltage
* monitor
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*/
#ifndef INA3221_REGS_H
#define INA3221_REGS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief INA3221 internal register addresses
*/
typedef enum ina3221_reg {
INA3221_REG_CONFIGURATION = 0x00, /**< Configuration register (R/W) */
INA3221_REG_CH1_SHUNT_VOLTAGE = 0x01, /**< Channel 1 shunt voltage register (R) */
INA3221_REG_CH1_BUS_VOLTAGE = 0x02, /**< Channel 1 bus voltage register (R) */
INA3221_REG_CH2_SHUNT_VOLTAGE = 0x03, /**< Channel 2 shunt voltage register (R) */
INA3221_REG_CH2_BUS_VOLTAGE = 0x04, /**< Channel 2 bus voltage register (R) */
INA3221_REG_CH3_SHUNT_VOLTAGE = 0x05, /**< Channel 3 shunt voltage register (R) */
INA3221_REG_CH3_BUS_VOLTAGE = 0x06, /**< Channel 3 bus voltage register (R) */
INA3221_REG_CH1_CRIT_ALERT_LIMIT = 0x07, /**< Channel 1 voltage thresholt for critical alert (R/W) */
INA3221_REG_CH1_WARN_ALERT_LIMIT = 0x08, /**< Channel 1 voltage thresholt for warning alert (R/W) */
INA3221_REG_CH2_CRIT_ALERT_LIMIT = 0x09, /**< Channel 2 voltage thresholt for critical alert (R/W) */
INA3221_REG_CH2_WARN_ALERT_LIMIT = 0x0A, /**< Channel 2 voltage thresholt for warning alert (R/W) */
INA3221_REG_CH3_CRIT_ALERT_LIMIT = 0x0B, /**< Channel 3 voltage thresholt for critical alert (R/W) */
INA3221_REG_CH3_WARN_ALERT_LIMIT = 0x0C, /**< Channel 3 voltage thresholt for warning alert (R/W) */
INA3221_REG_SHUNT_VOLTAGE_SUM = 0x0D, /**< Shunt voltage sum register over all channels (R) */
INA3221_REG_SHUNT_VOLTAGE_SUM_LIMIT = 0x0E, /**< Shunt voltage sum threshold for critical alert (R/W) */
INA3221_REG_MASK_ENABLE = 0x0F, /**< Channel enable and status register (R/W) */
INA3221_REG_PV_UPPER_LIMIT = 0x10, /**< Pover valid upper limit register (R/W) */
INA3221_REG_PV_LOWER_LIMIT = 0x11, /**< Power valid lower limit register (R/W) */
INA3221_REG_MANUFACTURER_ID = 0xFE, /**< Manufacturer ID (=0x5449) (R) */
INA3221_REG_DIE_ID = 0xFF /**< Die ID (=0x3220) (R) */
} ina3221_reg_t;
#ifdef __cplusplus
}
#endif
#endif /* INA3221_REGS_H */
/** @} */

1117
drivers/include/ina3221.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -105,6 +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_CLASS_ANY = 0xff /**< any device - wildcard */
/* extend this list as needed... */
};

View File

@ -62,6 +62,7 @@ const char *saul_class_to_str(const uint8_t class_id)
case SAUL_SENSE_CAPACITANCE: return "SENSE_CAPACITANCE";
case SAUL_SENSE_VOLTAGE: return "SENSE_VOLTAGE";
case SAUL_SENSE_PH: return "SENSE_PH";
case SAUL_SENSE_POWER: return "SENSE_POWER";
case SAUL_CLASS_ANY: return "CLASS_ANY";
default: return "CLASS_UNKNOWN";
}

View File

@ -40,8 +40,9 @@ PSEUDOMODULES += gnrc_sixlowpan_router
PSEUDOMODULES += gnrc_sixlowpan_router_default
PSEUDOMODULES += gnrc_sock_check_reuse
PSEUDOMODULES += gnrc_txtsnd
PSEUDOMODULES += i2c_scan
PSEUDOMODULES += heap_cmd
PSEUDOMODULES += i2c_scan
PSEUDOMODULES += ina3221_alerts
PSEUDOMODULES += l2filter_blacklist
PSEUDOMODULES += l2filter_whitelist
PSEUDOMODULES += lis2dh12_i2c

View File

@ -422,6 +422,10 @@ void auto_init(void)
extern void auto_init_hts221(void);
auto_init_hts221();
#endif
#ifdef MODULE_INA3221
extern void auto_init_ina3221(void);
auto_init_ina3221();
#endif
#ifdef MODULE_IO1_XPLAINED
extern void auto_init_io1_xplained(void);
auto_init_io1_xplained();

View File

@ -0,0 +1,65 @@
/*
* 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 HTS221 devices
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/
#ifdef MODULE_INA3221
#include "assert.h"
#include "log.h"
#include "saul_reg.h"
#include "ina3221_params.h"
#include "ina3221.h"
#define INA3221_NUM ARRAY_SIZE(ina3221_params)
#define INA3221_SAUL_INFO_NUM ARRAY_SIZE(ina3221_saul_info)
#define INA3221_SAUL_DRIVERS_NUM (4)
extern const saul_driver_t ina3221_saul_drivers[INA3221_SAUL_DRIVERS_NUM];
static ina3221_t ina3221_devs[INA3221_NUM];
static saul_reg_t saul_entries[INA3221_NUM * INA3221_SAUL_DRIVERS_NUM];
void auto_init_ina3221(void)
{
assert(INA3221_SAUL_INFO_NUM == INA3221_SAUL_DRIVERS_NUM * INA3221_NUM);
for (unsigned i = 0; i < INA3221_NUM; i++) {
LOG_DEBUG("[auto_init_saul] initializing ina3221 #%u\n", i);
int status = ina3221_init(&ina3221_devs[i], &ina3221_params[i]);
if (status != INA3221_OK) {
LOG_ERROR("[auto_init_saul] error(%d) initializing ina3221 #%u\n",
status, i);
continue;
}
for (unsigned j = 0; j < INA3221_SAUL_DRIVERS_NUM; j++) {
saul_entries[i * INA3221_SAUL_DRIVERS_NUM + j] = (saul_reg_t) {
.dev = &ina3221_devs[i],
.name =
ina3221_saul_info[i * INA3221_SAUL_DRIVERS_NUM + j].name,
.driver = &ina3221_saul_drivers[j]
};
saul_reg_add(&saul_entries[i * INA3221_SAUL_DRIVERS_NUM + j]);
}
}
}
#else
typedef int dont_be_pedantic;
#endif /* MODULE_INA3221 */

View File

@ -94,6 +94,7 @@ enum {
/* electricity */
UNIT_A, /**< Ampere */
UNIT_V, /**< Volts */
UNIT_W, /**< Watt */
UNIT_GS, /**< gauss */
UNIT_DBM, /**< decibel-milliwatts */
UNIT_COULOMB, /**< coulomb */

View File

@ -92,6 +92,7 @@ const char *phydat_unit_to_str(uint8_t unit)
case UNIT_GR: return "G";
case UNIT_A: return "A";
case UNIT_V: return "V";
case UNIT_W: return "W";
case UNIT_DBM: return "dBm";
case UNIT_GS: return "Gs";
case UNIT_BAR: return "Bar";

View File

@ -0,0 +1,21 @@
include ../Makefile.tests_common
DEBUG ?= 0
USEMODULE += ina3221
USEMODULE += fmt
USEMODULE += xtimer
INA3221_ADDR ?= INA3221_ADDR_00
INA3221_PIN_WRN ?= GPIO_UNDEF
INA3221_PIN_CRT ?= GPIO_UNDEF
INA3221_PIN_PV ?= GPIO_UNDEF
INA3221_PIN_TC ?= GPIO_UNDEF
CFLAGS += -DINA3221_PARAM_ADDR=$(INA3221_ADDR)
CFLAGS += -DINA3221_PARAM_PIN_WRN=$(INA3221_PIN_WRN)
CFLAGS += -DINA3221_PARAM_PIN_CRT=$(INA3221_PIN_CRT)
CFLAGS += -DINA3221_PARAM_PIN_PV=$(INA3221_PIN_PV)
CFLAGS += -DINA3221_PARAM_PIN_TC=$(INA3221_PIN_TC)
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,29 @@
# About
This is a manual test application for the INA3221 current and power
monitor driver.
# Usage
This test application will initialize the sensor with the following parameters:
- INA3221_PARAM_I2C: I2C_DEV(0)
- INA3221_PARAM_ADDR: INA3221_ADDR_00
- INA3221_PARAM_PIN_WRN: GPIO_UNDEF, or given WRN input pin
- INA3221_PARAM_PIN_CRT: GPIO_UNDEF, or given CRT input pin
- INA3221_PARAM_PIN_TC: GPIO_UNDEF, or given TC input pin
- INA3221_PARAM_PIN_PV: GPIO_UNDEF, or given PV input pin
- INA3221_PARAM_CONFIG: (
INA3221_ENABLE_CH1 | INA3221_ENABLE_CH2 | INA3221_ENABLE_CH3,
INA3221_NUM_SAMPLES_4 |
INA3221_CONV_TIME_BADC_4156US |
INA3221_CONV_TIME_SADC_4156US |
INA3221_MODE_CONTINUOUS_SHUNT_BUS
)
- INA3221_PARAM_RSHUNT_MOHM_CH1: 100
- INA3221_PARAM_RSHUNT_MOHM_CH2: 100
- INA3221_PARAM_RSHUNT_MOHM_CH3: 100
After initialization, the application will perform basic read/write
functionality tests. If the preceding static tests succeed, the program
will enter an endless loop and check if a full conversion cycle has
completed. If new values for shunt voltage and bus voltage are available,
current and power are calculated and each value for every channel is
printed to stdio in a neat table, with available status flags.

418
tests/driver_ina3221/main.c Normal file
View File

@ -0,0 +1,418 @@
/*
* 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 tests
* @{
*
* @file
* @brief Test application for the INA3221 sensor driver
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/
#include <string.h>
#include "periph/gpio.h"
#include "xtimer.h"
#include "fmt.h"
#include "ina3221_internal.h"
#include "ina3221_params.h"
#include "ina3221.h"
/* Issue critical alert if shunt voltage is above thist value */
#define CRIT_ALERT_LIM_UV (2500)
/* Issue warning alert if shunt voltage is above this value */
#define WARN_ALERT_LIM_UV (1600)
/* Issue power valid alert if bus voltage is below this value */
#define PV_LOWER_LIM_MV (4500)
/* Issue power valid alert if bus voltage is above this value */
#define PV_UPPER_LIM_MV (5500)
/* Issue critical alert if the sum of all shunt voltages is above this value */
#define SUM_SHUNT_ALERT_LIM_UV (8000)
#define COL (" ")
#define THEAD ( \
"channel | shunt [uV] | bus [mV] | current [uA] | power [uW] ")
#define HLINE ( \
"--------------+--------------+--------------+--------------+--------------")
#ifdef MODULE_INA3221_ALERTS
static void warning_alert(void *arg)
{
ina3221_t *dev = (ina3221_t *)arg;
(void)dev;
puts("WARNING");
}
static void critical_alert(void *arg)
{
ina3221_t *dev = (ina3221_t *)arg;
(void)dev;
puts("CRITICAL");
}
static void timing_control_alert(void *arg)
{
ina3221_t *dev = (ina3221_t *)arg;
(void)dev;
puts("TIMING CONTROL");
}
static void power_valid_alert(void *arg)
{
ina3221_t *dev = (ina3221_t *)arg;
(void)dev;
puts("POWER VALID");
}
#endif
int main(void)
{
xtimer_init();
int status;
ina3221_t dev;
for (int32_t i = INA3221_MIN_SHUNT_UV; i <= INA3221_MAX_SHUNT_UV;
i += INA3221_SHUNT_VOLTAGE_PRECISION_UV) {
int16_t reg = shunt_voltage_uv_to_reg_val(i);
int32_t sv = reg_val_to_shunt_voltage_uv(reg);
int16_t _reg = shunt_voltage_uv_to_reg_val(sv);
if (reg != _reg || i != sv) {
printf(
"[FAILURE] shunt V conversiom: shunt V: %" PRId32 ", REG: %" PRId16 ", shunt V: %" PRId32 ", REG: %" PRId16 "\n", i, reg, sv,
_reg);
return 1;
}
}
for (int32_t i = INA3221_MIN_BUS_MV; i <= INA3221_MAX_BUS_MV;
i += INA3221_BUS_VOLTAGE_PRECISION_MV) {
int16_t reg = bus_voltage_mv_to_reg_val(i);
int32_t bv = reg_val_to_bus_voltage_mv(reg);
int16_t _reg = bus_voltage_mv_to_reg_val(bv);
if (reg != _reg || i != bv) {
printf(
"[FAILURE] bus V conversion: bus V: %" PRId32 ", REG: %" PRId16 ", bus V: %" PRId32 ", REG: %" PRId16 "\n", i, reg, bv,
_reg);
return 1;
}
}
for (int32_t i = INA3221_MIN_SHUNT_SUM_UV; i <= INA3221_MAX_SHUNT_SUM_UV;
i += INA3221_SHUNT_VOLTAGE_PRECISION_UV) {
int16_t reg = sum_shunt_voltage_uv_to_reg_val(i);
int32_t sv = sum_reg_val_to_shunt_voltage_uv(reg);
int16_t _reg = sum_shunt_voltage_uv_to_reg_val(sv);
if (reg != _reg || i != sv) {
printf(
"[FAILURE] shunt sum V conversion: sum V: %" PRId32 ", REG: %" PRId16 ", sum V: %" PRId32 ", REG: %" PRId16 "\n", i, reg, sv,
_reg);
return 1;
}
}
status = ina3221_init(&dev, &ina3221_params[0]);
if (status != INA3221_OK) {
printf("[FAILURE] ina3221_init: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_init");
}
#ifdef MODULE_INA3221_ALERTS
status = ina3221_enable_warning_alert(&dev, warning_alert, &dev);
if (status == -ENOTSUP) {
puts("[WARNING] INA3221_ALERT_WRN not supported");
}
else if (status != INA3221_OK) {
printf("[FAILURE] ina3221_enable_alert INA3221_ALERT_WRN: %d\n",
status);
return 1;
}
status = ina3221_enable_critical_alert(&dev, critical_alert, &dev);
if (status == -ENOTSUP) {
puts("[WARNING] INA3221_ALERT_CRT not supported");
}
else if (status != INA3221_OK) {
printf("[FAILURE] ina3221_enable_alert INA3221_ALERT_CRT: %d\n",
status);
return 1;
}
status = ina3221_enable_timing_control_alert(&dev, timing_control_alert,
&dev);
if (status == -ENOTSUP) {
puts("[WARNING] INA3221_ALERT_TC not supported");
}
else if (status != INA3221_OK) {
printf("[FAILURE] ina3221_enable_alert INA3221_ALERT_TC: %d\n", status);
return 1;
}
status = ina3221_enable_power_valid_alert(&dev, power_valid_alert, &dev);
if (status == -ENOTSUP) {
puts("[WARNING] INA3221_ALERT_PV not supported");
}
else if (status != INA3221_OK) {
printf("[FAILURE] ina3221_enable_alert INA3221_ALERT_PV: %d\n", status);
return 1;
}
puts("[SUCCESS] ina3221_enable_alert");
#endif
int32_t shunt_uv[INA3221_NUM_CH] = { 0 };
int16_t bus_mv[INA3221_NUM_CH] = { 0 };
int32_t sum_shunt_uv = 0;
int32_t current_ua[INA3221_NUM_CH] = { 0 };
int32_t power_uw[INA3221_NUM_CH] = { 0 };
uint16_t flags = 0;
status = ina3221_read_shunt_uv(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
shunt_uv, &flags);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_read_shunt_uv: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_read_shunt_uv");
}
status = ina3221_calculate_current_ua(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3, shunt_uv,
current_ua);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_calculate_current_ua: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_calculate_current_ua");
}
status = ina3221_read_bus_mv(&dev, INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
bus_mv, &flags);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_read_bus_mv: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_read_bus_mv");
}
status = ina3221_calculate_power_uw(bus_mv, current_ua, status, power_uw);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_calculate_power_uw: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_calculate_power_uw");
}
status = ina3221_set_enable_sum_channel(&dev, INA3221_CH_ENABLE,
INA3221_CH_ENABLE,
INA3221_CH_ENABLE);
if (status < 0) {
printf("[FAILURE] ina3221_set_enable_sum_channel: %d\n", status);
return 1;
}
else {
printf("[SUCCESS] ina3221_set_enable_sum_channel");
}
status = ina3221_read_shunt_sum_uv(&dev, &sum_shunt_uv, &flags);
if (status != INA3221_OK) {
printf("[FAILURE] ina3221_read_shunt_sum_uv: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_read_shunt_sum_uv");
}
status = INA3221_TRIGGER_SHUNT_AND_BUS(&dev);
if (status < 0) {
printf("[FAILURE] INA3221_TRIGGER_SHUNT_AND_BUS: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] INA3221_TRIGGER_SHUNT_AND_BUS");
}
flags = 0;
while (!(flags & INA3221_FLAG_CONV_READY)) {
status = ina3221_read_flags(&dev, &flags);
if (status < 0) {
printf("[FAILURE] ina3221_read_flags: %d\n", status);
return 1;
}
}
status = ina3221_read_shunt_uv(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
shunt_uv, &flags);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_read_shunt_uv (triggered): %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_read_shunt_uv (triggered)");
}
status = ina3221_read_bus_mv(&dev, INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
bus_mv, &flags);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_read_bus_mv (triggered): %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_read_bus_mv (triggered)");
}
int32_t crit_alert_lim = CRIT_ALERT_LIM_UV;
status = ina3221_set_crit_alert_limit(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
crit_alert_lim);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_set_crit_alert_limit: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_set_crit_alert_limit");
}
int32_t warn_alert_lim = WARN_ALERT_LIM_UV;
status = ina3221_set_warn_alert_limit(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
warn_alert_lim);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_set_warn_alert_limit: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_set_warn_alert_limit");
}
int32_t pv_lower_lim = PV_LOWER_LIM_MV;
status = ina3221_set_power_valid_lower_limit(&dev, pv_lower_lim);
if (status != INA3221_OK) {
printf("[FAILURE] ina3221_set_power_valid_lower_limit: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_set_power_valid_lower_limit");
}
int32_t pv_upper_lim = PV_UPPER_LIM_MV;
status = ina3221_set_power_valid_upper_limit(&dev, pv_upper_lim);
if (status != INA3221_OK) {
printf("[FAILURE] ina3221_set_power_valid_upper_limit: %d\n", status);
return 1;
}
else {
puts("[SUCCESS] ina3221_set_power_valid_upper_limit");
}
int32_t sum_shunt_alert_lim = SUM_SHUNT_ALERT_LIM_UV;
status =
ina3221_set_shunt_voltage_sum_alert_limit(&dev, sum_shunt_alert_lim);
if (status != INA3221_OK) {
printf("[FAILURE] ina3221_set_shunt_voltage_sum_alert_limit: %d\n",
status);
return 1;
}
else {
puts("[SUCCESS] ina3221_set_shunt_voltage_sum_alert_limit");
}
status = ina3221_set_mode(&dev, INA3221_MODE_CONTINUOUS_SHUNT_BUS);
if (status != INA3221_OK) {
printf("[FAILURE] ina3221_set_mode: %d\n", status);
return 1;
}
char line_buffer[strlen(THEAD) + 1];
char col_buffer[strlen(COL) + 1];
while (1) {
status = ina3221_read_flags(&dev, &flags);
if (status != INA3221_OK) {
printf("[FAILURE] ina3221_read_flags: %d\n", status);
return 1;
}
if (!(flags & INA3221_FLAG_CONV_READY)) {
xtimer_sleep(2);
continue;
}
status = ina3221_read_shunt_uv(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
shunt_uv, NULL);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_read_shunt_uv: %d\n", status);
return 1;
}
status = ina3221_read_bus_mv(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3,
bus_mv, NULL);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_read_bus_mv: %d\n", status);
return 1;
}
status = ina3221_calculate_current_ua(&dev,
INA3221_CH1 | INA3221_CH2 | INA3221_CH3, shunt_uv,
current_ua);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_calculate_current_ua: %d\n", status);
return 1;
}
status = ina3221_calculate_power_uw(bus_mv, current_ua, INA3221_NUM_CH,
power_uw);
if (status != INA3221_NUM_CH) {
printf("[FAILURE] ina3221_calculate_power_uw: %d\n", status);
return 1;
}
puts(THEAD);
puts(HLINE);
for (int8_t i = 0; i < INA3221_NUM_CH; i++) {
char *p = line_buffer;
strcpy(col_buffer, COL);
fmt_char(col_buffer, '0' + i);
p += sprintf(p, "%s|", col_buffer);
strcpy(col_buffer, COL);
fmt_s32_dec(col_buffer, shunt_uv[i]);
p += sprintf(p, "%s|", col_buffer);
strcpy(col_buffer, COL);
fmt_s16_dec(col_buffer, bus_mv[i]);
p += sprintf(p, "%s|", col_buffer);
strcpy(col_buffer, COL);
fmt_s32_dec(col_buffer, current_ua[i]);
p += sprintf(p, "%s|", col_buffer);
strcpy(col_buffer, COL);
fmt_s32_dec(col_buffer, power_uw[i]);
p += sprintf(p, "%s", col_buffer);
printf("%s\n", line_buffer);
}
puts("");
snprintf(line_buffer, sizeof(line_buffer),
"FLAGS: CF1:%d CF2:%d CF3:%d SF:%d WF1:%d WF2:%d WF3:%d PVF:%d TCF:%d CVRF:%d",
(flags & INA3221_FLAG_CRIT_ALERT_CH1) ? 1 : 0,
(flags & INA3221_FLAG_CRIT_ALERT_CH2) ? 1 : 0,
(flags & INA3221_FLAG_CRIT_ALERT_CH3) ? 1 : 0,
(flags & INA3221_FLAG_SHUNT_SUM_ALERT) ? 1 : 0,
(flags & INA3221_FLAG_WARN_ALERT_CH1) ? 1 : 0,
(flags & INA3221_FLAG_WARN_ALERT_CH2) ? 1 : 0,
(flags & INA3221_FLAG_WARN_ALERT_CH3) ? 1 : 0,
(flags & INA3221_FLAG_POWER_VALID) ? 1 : 0,
(flags & INA3221_FLAG_TIMING_CONTROL) ? 1 : 0,
(flags & INA3221_FLAG_CONV_READY) ? 1 : 0);
printf("%s\n", line_buffer);
puts(HLINE);
}
return 0;
}