Merge pull request #12055 from fabian18/driver-ina3221
Driver for INA3221 current and power and bus voltage monitor
This commit is contained in:
commit
4d279ae76e
@ -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
|
||||
|
||||
@ -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
7
drivers/ina3221/Makefile
Normal 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
57
drivers/ina3221/alerts.c
Normal 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
686
drivers/ina3221/ina3221.c
Normal 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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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));
|
||||
}
|
||||
178
drivers/ina3221/ina3221_saul.c
Normal file
178
drivers/ina3221/ina3221_saul.c
Normal 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
|
||||
}
|
||||
};
|
||||
60
drivers/ina3221/include/ina3221_defines.h
Normal file
60
drivers/ina3221/include/ina3221_defines.h
Normal 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 */
|
||||
/** @} */
|
||||
133
drivers/ina3221/include/ina3221_internal.h
Normal file
133
drivers/ina3221/include/ina3221_internal.h
Normal 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 */
|
||||
/** @} */
|
||||
144
drivers/ina3221/include/ina3221_params.h
Normal file
144
drivers/ina3221/include/ina3221_params.h
Normal 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 */
|
||||
/** @} */
|
||||
58
drivers/ina3221/include/ina3221_regs.h
Normal file
58
drivers/ina3221/include/ina3221_regs.h
Normal 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
1117
drivers/include/ina3221.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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... */
|
||||
};
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
65
sys/auto_init/saul/auto_init_ina3221.c
Normal file
65
sys/auto_init/saul/auto_init_ina3221.c
Normal 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 */
|
||||
@ -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 */
|
||||
|
||||
@ -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";
|
||||
|
||||
21
tests/driver_ina3221/Makefile
Normal file
21
tests/driver_ina3221/Makefile
Normal 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
|
||||
29
tests/driver_ina3221/README.md
Normal file
29
tests/driver_ina3221/README.md
Normal 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
418
tests/driver_ina3221/main.c
Normal 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user