diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 8b15fbd4b7..4607c76281 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -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 diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 5cc2aa3b3b..3b8ea0fe1d 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -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 diff --git a/drivers/ina3221/Makefile b/drivers/ina3221/Makefile new file mode 100644 index 0000000000..85e9f95fef --- /dev/null +++ b/drivers/ina3221/Makefile @@ -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 diff --git a/drivers/ina3221/alerts.c b/drivers/ina3221/alerts.c new file mode 100644 index 0000000000..68af79396f --- /dev/null +++ b/drivers/ina3221/alerts.c @@ -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 + * + * @} + */ + +#include +#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; +} diff --git a/drivers/ina3221/ina3221.c b/drivers/ina3221/ina3221.c new file mode 100644 index 0000000000..269c542390 --- /dev/null +++ b/drivers/ina3221/ina3221.c @@ -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 + * + * @} + */ + +#include +#include +#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)); +} diff --git a/drivers/ina3221/include/ina3221_defines.h b/drivers/ina3221/include/ina3221_defines.h new file mode 100644 index 0000000000..92397cb041 --- /dev/null +++ b/drivers/ina3221/include/ina3221_defines.h @@ -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 + */ + +#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 */ +/** @} */ diff --git a/drivers/ina3221/include/ina3221_internal.h b/drivers/ina3221/include/ina3221_internal.h new file mode 100644 index 0000000000..0075c1d9a0 --- /dev/null +++ b/drivers/ina3221/include/ina3221_internal.h @@ -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 + */ + +#ifndef INA3221_INTERNAL_H +#define INA3221_INTERNAL_H + +#include +#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 */ +/** @} */ diff --git a/drivers/ina3221/include/ina3221_params.h b/drivers/ina3221/include/ina3221_params.h new file mode 100644 index 0000000000..7534ac3543 --- /dev/null +++ b/drivers/ina3221/include/ina3221_params.h @@ -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 + */ + +#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 */ +/** @} */ diff --git a/drivers/ina3221/include/ina3221_regs.h b/drivers/ina3221/include/ina3221_regs.h new file mode 100644 index 0000000000..f960152293 --- /dev/null +++ b/drivers/ina3221/include/ina3221_regs.h @@ -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 + */ + +#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 */ +/** @} */ diff --git a/drivers/include/ina3221.h b/drivers/include/ina3221.h new file mode 100644 index 0000000000..11ddb0bc30 --- /dev/null +++ b/drivers/include/ina3221.h @@ -0,0 +1,1117 @@ +/* + * 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. + */ + +/** + * @defgroup drivers_ina3221 INA3221 current/power monitor + * @ingroup drivers_sensors + * @brief Device driver for Texas Instruments INA3221 + * three-channel ,high-side current and bus voltage + * monitor + * @{ + * + * @file + * @brief Device driver interface for Texas Instruments INA3221 + * three-channel ,high-side current and bus voltage + * monitor. + * + * The alert pins WRN, CRT, TC, PV are open drain + * output pins, so they should be pulled high with + * internal or external pull-ups. They pull low, if + * the alert condition becomes true. If they become + * pulled low, the configured input pins in + * @see ina3221_params_t will read a low flank and + * the related interrupt callback will be executed, + * if configured with @see ina3221_enable_alert. + * WRN condition: + * Any channel´s measured shunt + * voltage drop was higher than the configured value in + * INA3221_REG_CHx_WARN_ALERT_LIMIT, + * CRT condition: + * Any channel´s measured shunt + * voltage drop was higher than the configured value in + * INA3221_REG_CHx_CRIT_ALERT_LIMIT, + * or the sum of all channel´s voltage drops was higher + * than the configured value in + * INA3221_REG_SHUNT_VOLTAGE_SUM_LIMIT. Note that the + * sum of voltage drops only makes sense, if all + * channels are using the same shunt resistance value! + * TC condition: + * After a 1.2-V level is detected on channel 1, if the + * INA3221 does not detecta 1.2-V value or greater on + * the bus voltage measurement following four complete + * cycles of all three channels, the timing control + * (TC)alert pin pulls low. + * PV condition: + * If any channel´s measured bus voltage was not within + * the configured values in INA3221_REG_PV_LOWER_LIMIT + * and INA3221_REG_PV_UPPER_LIMIT, the PV pin is pulled + * low. + * + * For all enabled channels (1 <= i <= INA3221_NUM_CH), + * the time to complete a full measurment cycle is: + * + * num_samples * (shunt_conv_time_ch_i + bus_voltage_conv_time_ch_i) + * + * @author Fabian Hüßler + */ + +#ifndef INA3221_H +#define INA3221_H + +#include + +#include "periph/gpio.h" +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define INA3221_NUM_CH (3) /**< Number of channels */ + +#define INA3221_NUM_ALERTS (4) /**< Number of alerts */ + +/** + * @brief INA3221 alert callback for alert pins + * CRT, WRN, TC, PV + */ +typedef void (*ina3221_alert_cb_t)(void *arg); + +/** + * @brief Return codes + */ +typedef enum ina3221_ret_val { + INA3221_OK, /**< Everything is fine */ + INA3221_I2C_ERROR, /**< I2C bus acquirenment failed */ + INA3221_BAD_MANUF_ID, /**< Read device manufacturer ID failed */ + INA3221_BAD_DIE_ID, /**< Read device DIE ID failed */ + INA3221_RESET_FAILED, /**< Device reset failed */ + INA3221_CONFIG_FAILED /**< Device configuration failed */ +} ina3221_ret_val_t; + +/** + * @brief I2C device addresses + */ +typedef enum ina3221_dev_addr { + INA3221_ADDR_00 = 0x40, /**< A0 connected to GND */ + INA3221_ADDR_01 = 0x41, /**< A0 connected to VS */ + INA3221_ADDR_10 = 0x42, /**< A0 connected to SDA */ + INA3221_ADDR_11 = 0x43, /**< A0 connected to SCL */ +} ina3221_dav_addr_t; + +/** + * @brief Channel flags + */ +typedef enum ina3221_channel { + INA3221_CH1 = (1 << 0), /**< Channel 1 */ + INA3221_CH2 = (1 << 1), /**< Channel 2 */ + INA3221_CH3 = (1 << 2) /**< Channel 3 */ +} ina3221_channel_t; + +/** + * @brief Alert indices + */ +typedef enum ina3221_alert { + INA3221_ALERT_WRN = 0, /**< Warning alert */ + INA3221_ALERT_CRT = 1, /**< Critical alert */ + INA3221_ALERT_TC = 2, /**< Timing control */ + INA3221_ALERT_PV = 3 /**< Power valid alert */ +} ina3221_alert_t; + +/** + * @brief Device operation modes + */ +typedef enum ina3221_mode { + INA3221_MODE_POWER_DOWN = 0x0000, /**< Sleep mode */ + INA3221_MODE_TRIGGER_SHUNT_ONLY = 0x0001, /**< Trigger shunt voltage measurement only */ + INA3221_MODE_TRIGGER_BUS_ONLY = 0x0002, /**< Trigger bus voltage measurement only */ + INA3221_MODE_TRIGGER_SHUNT_BUS = 0x0003, /**< Trigger shunt and bus voltage measurement */ + INA3221_MODE_POWER_DOWN_ = 0x0004, /**< Also power down (see datasheet)*/ + INA3221_MODE_CONTINUOUS_SHUNT_ONLY = 0x0005, /**< Continuous shunt voltage measurement only*/ + INA3221_MODE_CONTINUOUS_BUS_ONLY = 0x0006, /**< Continuous bus voltage measurement only*/ + INA3221_MODE_CONTINUOUS_SHUNT_BUS = 0x0007 /**< Continuous shunt and bus voltage measurement */ +} ina3221_mode_t; +#define INA3221_MODE_MASK (0x0007) /**< Device operation mode bit mask */ + +/** + * @brief ADC shunt voltage conversion times + */ +typedef enum ina3221_conv_time_shunt_adc { + INA3221_CONV_TIME_SADC_140US = 0x0000, /**< 140 us */ + INA3221_CONV_TIME_SADC_204US = 0x0008, /**< 204 us */ + INA3221_CONV_TIME_SADC_332US = 0x0010, /**< 332 us */ + INA3221_CONV_TIME_SADC_588US = 0x0018, /**< 588 us */ + INA3221_CONV_TIME_SADC_1100US = 0x0020, /**< 1100 us */ + INA3221_CONV_TIME_SADC_2116US = 0x0028, /**< 2116 us */ + INA3221_CONV_TIME_SADC_4156US = 0x0030, /**< 4156 us */ + INA3221_CONV_TIME_SADC_8244US = 0x0038 /**< 8244 us */ +} ina3221_conv_time_shunt_adc_t; +#define INA3221_CONV_TIME_SADC_MASK (0x0038) /**< ADC shunt voltage conversion times bit mask*/ + +/** + * @brief ADC bus voltage conversion times + */ +typedef enum ina3221_conv_time_bus_adc { + INA3221_CONV_TIME_BADC_140US = 0x0000, /**< 140 us */ + INA3221_CONV_TIME_BADC_204US = 0x0040, /**< 204 us */ + INA3221_CONV_TIME_BADC_332US = 0x0080, /**< 332 us */ + INA3221_CONV_TIME_BADC_588US = 0x00C0, /**< 588 us */ + INA3221_CONV_TIME_BADC_1100US = 0x0100, /**< 1100 us */ + INA3221_CONV_TIME_BADC_2116US = 0x0140, /**< 2116 us */ + INA3221_CONV_TIME_BADC_4156US = 0x0180, /**< 4156 us */ + INA3221_CONV_TIME_BADC_8244US = 0x01C0 /**< 8244 us */ +} ina3221_conv_time_bus_adc_t; +#define INA3221_CONV_TIME_BADC_MASK (0x01C0) /**< ADC bus voltage conversion times bit mask*/ + +/** + * @brief Number of samples to calculate average shunt/bus voltage value + */ +typedef enum ina3221_num_samples { + INA3221_NUM_SAMPLES_1 = 0x0000, /**< 1 */ + INA3221_NUM_SAMPLES_4 = 0x0200, /**< 4 */ + INA3221_NUM_SAMPLES_16 = 0x0400, /**< 16 */ + INA3221_NUM_SAMPLES_64 = 0x0600, /**< 64 */ + INA3221_NUM_SAMPLES_128 = 0x0800, /**< 128 */ + INA3221_NUM_SAMPLES_256 = 0x0A00, /**< 256 */ + INA3221_NUM_SAMPLES_512 = 0x0C00, /**< 512 */ + INA3221_NUM_SAMPLES_1024 = 0x0E00 /**< 1024 */ +} ina3221_num_samples_t; +#define INA3221_NUM_SAMPLES_MASK (0x0E00) /**< Number of samples to calculate average shunt/bus voltage value bit mask */ + +/** + * @brief Enable channel flags + */ +typedef enum ina3221_enable_ch { + INA3221_ENABLE_CH1 = 0x4000, /**< Enable channel 1 */ + INA3221_ENABLE_CH2 = 0x2000, /**< Enable channel 2 */ + INA3221_ENABLE_CH3 = 0x1000 /**< Enable channel 3 */ +} ina3221_enable_ch_t; +#define INA3221_ENABLE_CH_MASK (INA3221_ENABLE_CH1 \ + | INA3221_ENABLE_CH2 \ + | INA3221_ENABLE_CH3) /**< Enable channel bit mask */ + +/** + * @brief Channel state enabled/disabled + */ +typedef enum ina3221_channel_state { + INA3221_CH_DISABLE, /**< Channel disabled */ + INA3221_CH_ENABLE /**< Channel enabled */ +} ina3221_channel_state_t; + +/** + * @brief Enable shunt voltage sum calculation channel flags + */ +typedef enum ina3221_enable_sum_ch { + INA3221_ENABLE_SUM_CH1 = 0x4000, /**< Enable sum channel 1 */ + INA3221_ENABLE_SUM_CH2 = 0x2000, /**< Enable sum channel 2 */ + INA3221_ENABLE_SUM_CH3 = 0x1000 /**< Enable sum channel 3 */ +} ina3221_enable_sum_ch_t; +#define INA3221_ENABLE_SUM_CH_MASK (INA3221_ENABLE_SUM_CH1 \ + | INA3221_ENABLE_SUM_CH2 \ + | INA3221_ENABLE_SUM_CH3) /**< Enable shunt voltage sum calculation channel bit mask */ + +/** + * @brief Enable latch flags + */ +typedef enum ina3221_enable_latch { + INA3221_ENABLE_WARN_LATCH = 0x0800, /**< Enable warning alert latch */ + INA3221_ENABLE_CRIT_LATCH = 0x0400 /**< Enable critical alert latch */ +} ina3221_enable_latch_t; +#define INA3221_ENABLE_LATCH_MASK (INA3221_ENABLE_WARN_LATCH \ + | INA3221_ENABLE_CRIT_LATCH) /**< Enable latch bit mask */ +/** + * @brief INA3221 device parameters + */ +typedef struct { + i2c_t i2c; /**< I2C bus */ + uint8_t addr; /**< I2C address */ + union { + struct { + gpio_t pin_warn; /**< Warning alert pin, becomes configured as input*/ + gpio_t pin_crit; /**< Critical alert pin, becomes configured as input*/ + gpio_t pin_tc; /**< Timing control alert pin, becomes configured as input*/ + gpio_t pin_pv; /**< Power valid alert pin, becomes configured as input*/ + } pins; /**< Struct of alert pins */ + struct { + gpio_t alert_pins[INA3221_NUM_ALERTS]; /**< Array to access alert pins via an index */ + } apins; /**< Struct of array of alert pins */ + } upins; /**< Union of alert pins as array and as single members */ + /* + For each pin 1 bit states whether to use internal pull up resistors. + d = don´t care + [d][d][d][d][pu PV][pu TC][pu CRT][pu WRN] + */ + uint8_t gpio_config; /**< Configure pull up resistors for gpio pins */ + uint16_t config; /**< Configuration */ + uint16_t rshunt_mohm[INA3221_NUM_CH]; /**< Shunt resistances im mOhm for each channel */ +} ina3221_params_t; + +/* ( INA3221_ENABLE_CH_1 + | INA3221_ENABLE_CH_2 + | INA3221_ENABLE_CH_2 + | INA3221_NUM_SAMPLES_1 + | INA3221_CONV_TIME_BADC_1100US + | INA3221_CONV_TIME_SADC_1100US + | INA3221_MODE_CONTINUOUS_SHUNT_BUS ) */ +#define INA3221_DEFCONFIG (0x7127) /**< Default configuration register value */ + +#define INA3221_RESET (0x8000) /**< Reset configuration register value*/ + +#define INA3221_FLAG_CRIT_ALERT_CH1 (0x0200) /**< Critical alert channel 1 flag */ +#define INA3221_FLAG_CRIT_ALERT_CH2 (0x0100) /**< Critical alert channel 2 flag */ +#define INA3221_FLAG_CRIT_ALERT_CH3 (0x0080) /**< Critical alert channel 3 flag */ + +#define INA3221_FLAG_SHUNT_SUM_ALERT (0x0040) /**< Alert channel sum flag */ + +#define INA3221_FLAG_WARN_ALERT_CH1 (0x0020) /**< Warning alert channel 1 flag */ +#define INA3221_FLAG_WARN_ALERT_CH2 (0x0010) /**< Warning alert channel 2 flag */ +#define INA3221_FLAG_WARN_ALERT_CH3 (0x0008) /**< Warning alert channel 3 flag */ + +#define INA3221_FLAG_POWER_VALID (0x0004) /**< Power valid flag */ + +#define INA3221_FLAG_TIMING_CONTROL (0x0002) /**< Timing Control flag */ + +#define INA3221_FLAG_CONV_READY (0x0001) /**< Conversion ready flag */ + +#define INA3221_FLAGS_MASK (INA3221_FLAG_CRIT_ALERT_CH1 \ + | INA3221_FLAG_CRIT_ALERT_CH2 \ + | INA3221_FLAG_CRIT_ALERT_CH3 \ + | INA3221_FLAG_SHUNT_SUM_ALERT \ + | INA3221_FLAG_WARN_ALERT_CH1 \ + | INA3221_FLAG_WARN_ALERT_CH2 \ + | INA3221_FLAG_WARN_ALERT_CH3 \ + | INA3221_FLAG_POWER_VALID \ + | INA3221_FLAG_TIMING_CONTROL \ + | INA3221_FLAG_CONV_READY) /**< Flags bit mask*/ + +/** + * @brief INA3221 device handle struct + */ +typedef struct { + ina3221_params_t params; /**< Device parameters */ +#if defined(MODULE_INA3221_ALERTS) || defined(DOXYGEN) + union { + struct { + ina3221_alert_cb_t warn_cb; /**< Warning alert callback: executed when WRN is pulled low */ + ina3221_alert_cb_t crit_cb; /**< Critical alert callback: executed when CRT is pulled low */ + ina3221_alert_cb_t tc_cb; /**< Timing control alert callback: executed when TC is pulled low */ + ina3221_alert_cb_t pv_cb; /**< Power Valid alert callback: executed when PV is pulled low */ + }; + struct { + ina3221_alert_cb_t alert_callbacks[INA3221_NUM_ALERTS]; /**< Array to access alert callbacks via an index */ + }; + }; + union { + struct { + void *arg_warn_cb; /**< Argument for Warning alert callback */ + void *arg_crit_cb; /**< Argument for Critical alert callback */ + void *arg_tc_cb; /**< Argument for Timing control alert callback */ + void *arg_pv_cb; /**< Argument for Power Valid alert callback */ + }; + struct { + void *alert_callback_arguments[INA3221_NUM_ALERTS]; /**< Array to access alert callback arguments via an index */ + }; + }; +#endif /* MODULE_INA3221_ALERTS */ +} ina3221_t; + +/** + * @brief Reset device, i.e. apply default configuration + * + * @param[in,out] dev Device handle + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return -INA3221_RESET_FAILED, if device could not be reset + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int ina3221_reset(ina3221_t *dev); + +/** + * @brief Initialize device + * + * @param[out] dev Device handle + * @param[in] params Device parameters + * + * @return INA3221_OK, on success + * @return -EINVAL, if NULL pointer was passed + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return -INA3221_BAD_MANUF_ID, if manufacturer ID does not match + * @return -INA3221_DIE_ID, if DIE ID does not match + * @return -INA3221_RESET_FAILED, if reset failed + * @return -INA3221_CONFIG_FAILED, if configuration could not be applied + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int ina3221_init(ina3221_t *dev, const ina3221_params_t *params); + +#if defined(MODULE_INA3221_ALERTS) || defined(DOXYGEN) +/** + * @brief Enable alert callback and argument for alert @p alert + * + * @param[in,out] dev Device handle + * @param[in] alert Alert index + * @param[in] cb Alert callback + * @param[in] arg Alert callback argument + * + * @return INA3221_OK, on success + * @return -ERANGE, if @p alert was out of bounds + * @return -ENUTSUP, if alert pin was initialized with GPIO_UNDEF + */ +int _ina3221_enable_alert(ina3221_t *dev, ina3221_alert_t alert, + ina3221_alert_cb_t cb, void *arg); + +/** + * @brief Wrapper around @see _ina3221_enable_alert, to + * enble warning alert + * + * @param[in,out] dev Device handle + * @param[in] cb Alert callback + * @param[in] arg Alert callback argument + * + * @return @see _ina3221_enable_alert + */ +static inline int ina3221_enable_warning_alert(ina3221_t *dev, + ina3221_alert_cb_t cb, void *arg) +{ + return _ina3221_enable_alert(dev, INA3221_ALERT_WRN, cb, arg); +} + +/** + * @brief Wrapper around @see _ina3221_enable_alert, to + * enable critical alert + * + * @param[in,out] dev Device handle + * @param[in] cb Alert callback + * @param[in] arg Alert callback argument + * + * @return @see _ina3221_enable_alert + */ +static inline int ina3221_enable_critical_alert(ina3221_t *dev, + ina3221_alert_cb_t cb, + void *arg) +{ + return _ina3221_enable_alert(dev, INA3221_ALERT_CRT, cb, arg); +} + +/** + * @brief Wrapper around @see _ina3221_enable_alert, to + * enable timing control alert + * + * @param[in,out] dev Device handle + * @param[in] cb Alert callback + * @param[in] arg Alert callback argument + * + * @return @see _ina3221_enable_alert + */ +static inline int ina3221_enable_timing_control_alert(ina3221_t *dev, + ina3221_alert_cb_t cb, + void *arg) +{ + return _ina3221_enable_alert(dev, INA3221_ALERT_TC, cb, arg); +} + +/** + * @brief Wrapper around @see _ina3221_enable_alert, to + * enable power valid alert + * + * @param[in,out] dev Device handle + * @param[in] cb Alert callback + * @param[in] arg Alert callback argument + * + * @return @see _ina3221_enable_alert + */ +static inline int ina3221_enable_power_valid_alert(ina3221_t *dev, + ina3221_alert_cb_t cb, + void *arg) +{ + return _ina3221_enable_alert(dev, INA3221_ALERT_PV, cb, arg); +} + +/** + * @brief Disable alert callback and argument for alert @p alert + * + * @param[in,out] dev Device handle + * @param[in] alert Alert index + * + * @return INA3221_OK, on success + * @return -ERANGE, if @p alert was out of bounds + * @return -ENUTSUP, if alert pin was initialized with GPIO_UNDEF + */ +int _ina3221_disable_alert(ina3221_t *dev, ina3221_alert_t alert); + +/** + * @brief Wrapper around @see _ina3221_disable_alert, to + * disable warning alert + * + * @param[in,out] dev Device handle + * + * @return @see _ina3221_disable_alert + */ +static inline int ina3221_disable_warning_alert(ina3221_t *dev) +{ + return _ina3221_disable_alert(dev, INA3221_ALERT_WRN); +} + +/** + * @brief Wrapper around @see _ina3221_disable_alert, to + * disable critical alert + * + * @param[in,out] dev Device handle + * + * @return @see _ina3221_disable_alert + */ +static inline int ina3221_disable_critical_alert(ina3221_t *dev) +{ + return _ina3221_disable_alert(dev, INA3221_ALERT_CRT); +} + +/** + * @brief Wrapper around @see _ina3221_disable_alert, to + * disable timing control alert + * + * @param[in,out] dev Device handle + * + * @return @see _ina3221_disable_alert + */ +static inline int ina3221_disable_timing_control_alert(ina3221_t *dev) +{ + return _ina3221_disable_alert(dev, INA3221_ALERT_TC); +} + +/** + * @brief Wrapper around @see _ina3221_disable_alert, to + * disable power valid alert + * + * @param[in,out] dev Device handle + * + * @return @see _ina3221_disable_alert + */ +static inline int ina3221_disable_power_valid_alert(ina3221_t *dev) +{ + return _ina3221_disable_alert(dev, INA3221_ALERT_PV); +} + +#endif /* MODULE_INA3221_ALERTS */ + +/** + * @brief Write configuration register value to configuration register + * + * @param[in,out] dev Device handle + * @param[in] cfg Configuration register value in host byte order + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_write_regs + */ +int _ina3221_set_config(ina3221_t *dev, uint16_t cfg); + +/** + * @brief Wrapper around @see _ina3221_set_config + * + * @param[in, out] dev Device handle + * @param[in] chs Channel enable flags + * @param[in] ns Number of samples + * @param[in] ctbadc Conversion time for bus voltage ADC + * @param[in] ctsadc Conversion time for shunt voltage ADC + * @param[in] mode Device operation mode + * + * @return @see _ina3221_set_config + */ +static inline int ina3221_set_config(ina3221_t *dev, + ina3221_enable_ch_t chs, + ina3221_num_samples_t ns, + ina3221_conv_time_bus_adc_t ctbadc, + ina3221_conv_time_shunt_adc_t ctsadc, + ina3221_mode_t mode) +{ + return _ina3221_set_config(dev, chs | ns | ctbadc | ctsadc | mode); +} + +/** + * @brief Read currently saved configuration register value + * + * @param[in] dev Device handle + * @param[out] cfg Pointer to configuration register output value in host byte order + * + * @return INA3221_OK + */ +int _ina3221_get_config(const ina3221_t *dev, uint16_t *cfg); + +/** + * @brief Wrapper around @see _ina3221_get_config + * + * @param[in] dev Device handle + * @param[out] chs Pointer to enabled channels variable + * @param[out] ns Pointer to number of samples variable + * @param[out] ctbadc Pointer to conversion time bus adc variable + * @param[out] ctsadc Pointer to conversion time shunt adc variable + * @param[out] mode Pointer to measurement mode variable + * + * @return @see _ina3221_get_config + */ +static inline int ina3221_get_config(const ina3221_t *dev, + ina3221_enable_ch_t *chs, + ina3221_num_samples_t *ns, + ina3221_conv_time_bus_adc_t *ctbadc, + ina3221_conv_time_shunt_adc_t *ctsadc, + ina3221_mode_t *mode) +{ + uint16_t cfg = 0; + int ret = _ina3221_get_config(dev, &cfg); + + *chs = cfg & INA3221_ENABLE_CH_MASK; + *ns = cfg & INA3221_NUM_SAMPLES_MASK; + *ctbadc = cfg & INA3221_CONV_TIME_BADC_MASK; + *ctsadc = cfg & INA3221_CONV_TIME_SADC_MASK; + *mode = cfg & INA3221_MODE_MASK; + return ret; +} + +/** + * @brief Enable channels + * + * @param[in,out] dev Device handle + * @param[in] ech Channel enable flags + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int _ina3221_set_enable_channel(ina3221_t *dev, ina3221_enable_ch_t ech); + +/** + * @brief Wrapper around @see _ina3221_set_enable_channel + * + * @param[in, out] dev Device handle + * @param[in] ch1 Channel 1 state + * @param[in] ch2 Channel 2 state + * @param[in] ch3 Channel 3 state + * + * @return @see _ina3221_set_enable_channel + */ +static inline int ina3221_set_channel_state(ina3221_t *dev, + ina3221_channel_state_t ch1, + ina3221_channel_state_t ch2, + ina3221_channel_state_t ch3) +{ + return _ina3221_set_enable_channel(dev, + (ch1 ? INA3221_ENABLE_CH1 : 0) | + (ch2 ? INA3221_ENABLE_CH2 : 0) | + (ch3 ? INA3221_ENABLE_CH3 : 0)); +} + +/** + * @brief Read which channels are currently enabled + * + * @param[in] dev Device handle + * @param[out] ech Pointer to enabled channels output variable + * + * @return Number of enabled channels + */ +int _ina3221_get_enable_channel(const ina3221_t *dev, ina3221_enable_ch_t *ech); + +/** + * @brief Wrapper around _ina3221_get_enable_channel + * + * @param[in] dev Device handle + * @param[out] ch1 Pointer to channel 1 state variable + * @param[out] ch2 Pointer to channel 2 state variable + * @param[out] ch3 Pointer to channel 3 state variable + * + * @return @see _ina3221_get_enable_channel + */ +static inline int ina3221_get_channel_state(const ina3221_t *dev, + ina3221_channel_state_t *ch1, + ina3221_channel_state_t *ch2, + ina3221_channel_state_t *ch3) +{ + ina3221_enable_ch_t ech = 0; + int ret = _ina3221_get_enable_channel(dev, &ech); + + *ch1 = (ech & INA3221_ENABLE_CH1) ? INA3221_CH_ENABLE : INA3221_CH_DISABLE; + *ch2 = (ech & INA3221_ENABLE_CH2) ? INA3221_CH_ENABLE : INA3221_CH_DISABLE; + *ch3 = (ech & INA3221_ENABLE_CH3) ? INA3221_CH_ENABLE : INA3221_CH_DISABLE; + return ret; +} + +/** + * @brief Update number of samples and write to configuration register + * + * @param[in, out] dev Device handle + * @param[in] ns Number of samples + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int ina3221_set_num_samples(ina3221_t *dev, ina3221_num_samples_t ns); + +/** + * @brief Read currently applied number of samples + * + * @param[in] dev Device handle + * @param[out] ns Pointer to number of samples output variable + * + * @return INA3221_OK + */ +int ina3221_get_num_samples(const ina3221_t *dev, ina3221_num_samples_t *ns); + +/** + * @brief Update conversion time of bus voltage ADC and write to configuration register + * + * @param[in,out] dev Device handle + * @param[in] ctb Bus voltage conversion time + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int ina3221_set_conv_time_bus_adc(ina3221_t *dev, + ina3221_conv_time_bus_adc_t ctb); + +/** + * @brief Read currently applied bus voltage ADC conversion time + * + * @param[in] dev Device handle + * @param[out] ctb Pointer to bus voltage ADC conversion time output variable + * + * @return INA3221_OK + */ +int ina3221_get_conv_time_bus_adc(const ina3221_t *dev, + ina3221_conv_time_bus_adc_t *ctb); + +/** + * @brief Update conversion time of shunt voltage ADC and write to configuration register + * + * @param[in,out] dev Device handle + * @param[in] cts Shunt voltage conversion time value + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int ina3221_set_conv_time_shunt_adc(ina3221_t *dev, + ina3221_conv_time_shunt_adc_t cts); + +/** + * @brief Read currently applied shunt voltage ADC conversion time + * + * @param[in] dev Device handle + * @param[out] cts Pointer to shunt voltage ADC conversion time output variable + * + * @return INA3221_OK + */ +int ina3221_get_conv_time_shunt_adc(const ina3221_t *dev, + ina3221_conv_time_shunt_adc_t *cts); + +/** + * @brief Update device operation mode + * + * @param[in,out] dev Device handle + * @param[in] mode Operation mode value in host byte order + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int ina3221_set_mode(ina3221_t *dev, ina3221_mode_t mode); + +/** + * @brief Read currently applied device operation mode + * + * @param[in] dev Device handle + * @param[out] mode Pointer to device operation mode output variable + * + * @return INA3221_OK + */ +int ina3221_get_mode(const ina3221_t *dev, ina3221_mode_t *mode); + +/** + * @brief Enable channels for shunt voltage sum calculation + * + * @param[in] dev Device handle + * @param[in] esch Enable channel shunt voltage sum flags in host byte order + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int _ina3221_set_enable_sum_channel(const ina3221_t *dev, + ina3221_enable_sum_ch_t esch); + +/** + * @brief Wrapper around @see _ina3221_set_enable_sum_channel + * + * @param[in] dev Device handle + * @param[in] ch1 Channel 1 state for sum voltage drop calculation + * @param[in] ch2 Channel 2 state for sum voltage drop calculation + * @param[in] ch3 Channel 3 state for sum voltage drop calculation + * + * @return @see _ina3221_set_enable_sum_channel + */ +static inline int ina3221_set_enable_sum_channel(const ina3221_t *dev, + ina3221_channel_state_t ch1, + ina3221_channel_state_t ch2, + ina3221_channel_state_t ch3) +{ + return _ina3221_set_enable_sum_channel(dev, + (ch1 ? INA3221_ENABLE_SUM_CH1 : 0) | + (ch2 ? INA3221_ENABLE_SUM_CH2 : 0) | + (ch3 ? INA3221_ENABLE_SUM_CH3 : 0)); +} + +/** + * @brief Read enabled channels for shunt voltage sum calculation + * + * @param[in] dev Device handle + * @param[out] esch Pointer to enabled channels for shunt voltage sum calculation output variable + * + * @return Number of enabled channels for shunt voltage sum calculation, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int _ina3221_get_enable_sum_channel(const ina3221_t *dev, + ina3221_enable_sum_ch_t *esch); + +/** + * @brief Wrapper for @see _ina3221_get_enable_sum_channel + * + * @param[in] dev Device handle + * @param[out] ch1 Pointer to channel 1 state variable for sum voltage drop calculation + * @param[out] ch2 Pointer to channel 2 state variable for sum voltage drop calculation + * @param[out] ch3 Pointer to channel 3 state variable for sum voltage drop calculation + * + * @return @see _ina3221_get_enable_sum_channel + */ +static inline int ina3221_get_enable_sum_channel(const ina3221_t *dev, + ina3221_channel_state_t *ch1, + ina3221_channel_state_t *ch2, + ina3221_channel_state_t *ch3) +{ + ina3221_enable_sum_ch_t esch = 0; + int ret = _ina3221_get_enable_sum_channel(dev, &esch); + + *ch1 = + (esch & + INA3221_ENABLE_SUM_CH1) ? INA3221_CH_ENABLE : INA3221_CH_DISABLE; + *ch2 = + (esch & + INA3221_ENABLE_SUM_CH2) ? INA3221_CH_ENABLE : INA3221_CH_DISABLE; + *ch3 = + (esch & + INA3221_ENABLE_SUM_CH3) ? INA3221_CH_ENABLE : INA3221_CH_DISABLE; + return ret; +} + +/** + * @brief Enable latches for critical/warning alert pins + * + * @param[in] dev Device handle + * @param[in] latch Enable latch flags + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + * @return @see i2c_write_regs + */ +int ina3221_set_latch(const ina3221_t *dev, ina3221_enable_latch_t latch); + +/** + * @brief Read enabled latches for critical/warning alert pins + * + * @param[in] dev Device handle + * @param[in] latch Pointer to latch flags output variable + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_get_latch(const ina3221_t *dev, ina3221_enable_latch_t *latch); + +/** + * @brief Set critical shunt voltage alert limit to @p in_uv for each channel in @p ch + * + * The function exits on the first channel that could not be updated. + * + * @param[in] dev Device handle + * @param[in] ch Channel flags + * @param[in] in_uv Critical shunt voltage limit in uv + * + * @return Number of channels whose critical shunt voltage alert limit could be set, if any + * @return -ERANGE, if @p in_uv was not in [INA3221_MIN_SHUNT_UV; INA3221_MAX_SHUNT_UV] + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_write_regs + */ +int ina3221_set_crit_alert_limit(const ina3221_t *dev, ina3221_channel_t ch, + int32_t in_uv); + +/** + * @brief Read critical shunt voltage alert limit for each channel in @p ch + * + * The function exits on the first channel whose critical alert limit could not be read. + * + * @param[in] dev Device handle + * @param[in] ch Channel flags + * @param[out] out_uv Output array of shunt voltage alert limits in uV + * + * @return Number of channels whose critical shunt voltage alert limits could be read, if any + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_get_crit_alert_limit(const ina3221_t *dev, ina3221_channel_t ch, + int32_t *out_uv); + +/** + * @brief Set warning shunt voltage alert limit to @p in_uv for each channel in @p ch + * + * The function exits on the first channel that could not be updated. + * + * @param[in] dev Device handle + * @param[in] ch Channel flags + * @param[in] in_uv Warning shunt voltage limit in uV in host byte order + * + * @return Number of channels whose warning shunt voltage alert limit could be set, if any + * @return -ERANGE, if @p in_uv was not in [INA3221_MIN_SHUNT_UV; INA3221_MAX_SHUNT_UV] + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_write_regs + */ +int ina3221_set_warn_alert_limit(const ina3221_t *dev, ina3221_channel_t ch, + int32_t in_uv); + +/** + * @brief Read warning shunt voltage alert limit for each channel in @p ch + * + * The function exits on the first channel whose warning alert limit could not be read. + * + * @param[in] dev Device handle + * @param[in] ch Channel flags + * @param[out] out_uv Output array of shunt voltage alert limits in uV + * + * @return Number of channels whose warning shunt voltage alert limits could be read, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_get_warn_alert_limit(const ina3221_t *dev, ina3221_channel_t ch, + int32_t *out_uv); + +/** + * @brief Set shunt voltage sum alert limit to @p in_uv + * + * @param[in] dev Device handle + * @param[in] in_uv shunt voltage sum limit in uv + * + * @return INA3221_OK, on success + * @return -ERANGE, if @p in_uv was not in [INA3221_MIN_SHUNT_SUM_UV; INA3221_MAX_SHUNT_SUM_UV] + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_write_regs + */ +int ina3221_set_shunt_voltage_sum_alert_limit(const ina3221_t *dev, + int32_t in_uv); + +/** + * @brief Read shunt voltage sum alert limit + * + * @param[in] dev Device handle + * @param[out] out_uv Pointer to sum voltage sum alert limit output variable + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_get_shunt_voltage_sum_alert_limit(const ina3221_t *dev, + int32_t *out_uv); + +/** + * @brief Set bus voltage power valid upper limit to @p in_mv + * + * @param[in] dev Device handle + * @param[in] in_mv bus voltage power valid upper limit in mv + * + * @return INA3221_OK, on success + * @return -ERANGE, if @p in_mv was not in [INA3221_MIN_BUS_MV; INA3221_MAX_BUS_MV] + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_write_regs + */ +int ina3221_set_power_valid_upper_limit(const ina3221_t *dev, int32_t in_mv); + +/** + * @brief Read bus voltage power valid upper limit + * + * @param[in] dev Device handle + * @param[out] out_mv Pointer to bus voltage power valid upper limit output variable + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_get_power_valid_upper_limit(const ina3221_t *dev, int32_t *out_mv); + +/** + * @brief Set bus voltage power valid lower limit to @p in_mv + * + * @param[in] dev Device handle + * @param[in] in_mv bus voltage power valid lower limit in mV + * + * @return INA3221_OK, on success + * @return -ERANGE, if @p in_mv was not in [INA3221_MIN_BUS_MV; INA3221_MAX_BUS_MV] + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_write_regs + */ +int ina3221_set_power_valid_lower_limit(const ina3221_t *dev, int32_t in_mv); + +/** + * @brief Read bus voltage power valid lower limit + * + * @param[in] dev Device handle + * @param[out] out_mv Pointer to bus voltage power valid lower limit output variable + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_get_power_valid_lower_limit(const ina3221_t *dev, int32_t *out_mv); + +/** + * @brief Read status flags + * + * @param[in] dev Device handle + * @param[out] flags Pointer to flags output variable + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_read_flags(const ina3221_t *dev, uint16_t *flags); + +/** + * @brief Read sum of shunt voltages + * + * @param[in] dev Device handle + * @param[out] out_uv Pointer to shunt voltage sum output variable + * @param[out] flags Pointer to flags output variable, may be NULL + * + * @return INA3221_OK, on success + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_read_shunt_sum_uv(const ina3221_t *dev, int32_t *out_uv, + uint16_t *flags); + +/** + * @brief Read shunt voltages for each channel in @p ch + * + * The function exits on the first channel whose shunt voltage could not be read + * + * @param[in] dev Device handle + * @param[in] ch Channel flags + * @param[out] out_uv Array of output shunt voltage values in uV + * @param[out] flags Pointer to flags output variable, may be NULL + * + * @return Number of channels whose shunt voltages could be read, if any + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_read_shunt_uv(const ina3221_t *dev, ina3221_channel_t ch, + int32_t *out_uv, uint16_t *flags); + +/** + * @brief Read bus voltages for each channel in @p ch + * + * The function exits on the first channel whose bus voltage could not be read + * + * @param[in] dev Device handle + * @param[in] ch Channel flags + * @param[out] out_mv Array of output bus voltage values in mV + * @param[out] flags Pointer to flags output variable, may be NULL + * + * @return Number of channels whose bus voltages could be read, if any + * @return -INA3221_I2C_ERROR, if I2C bus acquirement failed + * @return @see i2c_read_regs + */ +int ina3221_read_bus_mv(const ina3221_t *dev, ina3221_channel_t ch, + int16_t *out_mv, uint16_t *flags); + +/** + * @brief Calculate current for each channel in @p ch + * + * @param[in] dev Device handle + * @param[in] ch Channel flags + * @param[in] in_uv Array of input shunt voltage values + * @param[out] out_ma Array of output current values + * + * @return Number of clculated current values + */ +int ina3221_calculate_current_ua(const ina3221_t *dev, ina3221_channel_t ch, + int32_t *in_uv, int32_t *out_ma); + +/** + * @brief Calculate power from bus voltage and current values + * + * @param[in] in_mv Array of input bus voltage values in mV + * @param[in] in_ua Array of input current values in uA + * @param[in] num Number of values in @p in_mv + * @param[out] out_mw Array of output power values in uW + * + * @return Number of calculated power values, on success + * @return -ERANGE, if @p num is greater than INA3221_NUM_CH + */ +int ina3221_calculate_power_uw(int16_t *in_mv, int32_t *in_ua, uint8_t num, + int32_t *out_mw); + +/** + * @brief Align @p in_res to the number of channels + * For example: @p ch = (INA3221_CH1 | INA3221_CH3) + * @p in_res = {value_ch1, value_ch3}, then + * @p out_res will be {value_ch1, 0, value_ch3} + * + * @param[in] ch Channel flags + * @param[in] in_res Output of e.g. @see ina3221_calculate_current_ua + * @param[out] out_res Channel aligned result + * @param[in] res_val_size Size of a value in @p in_res in bytes + */ +void ina3221_ch_align(ina3221_channel_t ch, const void *in_res, void *out_res, + size_t res_val_size); + +/** + * @brief Set operation mode to INA3221_MODE_TRIGGER_SHUNT_ONLY to trigger shunt voltage measurement + * + * @param dev Device handle + */ +#define INA3221_TRIGGER_SHUNT(dev) \ + ina3221_set_mode(dev, INA3221_MODE_TRIGGER_SHUNT_ONLY) + +/** + * @brief Set operation mode to INA3221_MODE_TRIGGER_BUS_ONLY to trigger bus voltage measurement + * + * @param dev Device handle + */ +#define INA3221_TRIGGER_BUS(dev) \ + ina3221_set_mode(dev, INA3221_MODE_TRIGGER_BUS_ONLY) + +/** + * @brief Set operation mode to INA3221_MODE_TRIGGER_SHUNT_BUS to trigger shunt and bus voltage measurement + * + * @param dev Device handle + */ +#define INA3221_TRIGGER_SHUNT_AND_BUS(dev) \ + ina3221_set_mode(dev, INA3221_MODE_TRIGGER_SHUNT_BUS) + +#ifdef __cplusplus +} +#endif + +#endif /* INA3221_H */ +/** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index b11af0f939..f54c5a2993 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -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