1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers/ina3221: INA3221 driver implementation

This commit is contained in:
fabian18 2019-11-05 12:29:42 +01:00
parent 92b46cb335
commit 195379a9b8
11 changed files with 2278 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,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

@ -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