drivers: sensor: ina23x: specify current LSB in uA and fix units

Specify the units of the current LSB in microamperes, so that we can
measure low maximum currents. Right now it was specified in
milliamperes, but ignored and always hardcoded to 1mA in the driver.
This makes the driver pretty much useless when the maximum current to be
measured is in a range of e.g. 20-50mA.

This patch also removes some unnecessary ifdeffery: since we write the
calibration register, we can always provice measurements with the right
units. It is also wrong to provide sensor readings that do not match
with the units specified by the channel. After this change voltage is
always reported in V, current in A and power in W.

Note that power measurement had the current LSB hardcoded in the
calculation (assuming 1mA/LSB), this has been fixed as well.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2022-10-24 17:39:05 +02:00 committed by Carles Cufí
commit d5734bc003
7 changed files with 76 additions and 94 deletions

View file

@ -143,8 +143,7 @@
INA230_CONV_TIME_1100,
INA230_CONV_TIME_1100,
INA230_AVG_MODE_1)>;
/* Set current LSB to 1mA */
current-lsb = <1>;
current-lsb-microamps = <1000>;
rshunt-milliohms = <15>;
};
};

View file

@ -15,21 +15,14 @@
LOG_MODULE_REGISTER(INA230, CONFIG_SENSOR_LOG_LEVEL);
/**
* @brief Internal fixed value of INA230 that is used to ensure
* scaling is properly maintained.
*
*/
#define INA230_INTERNAL_FIXED_SCALING_VALUE 5120
/** @brief Calibration scaling value (value scaled by 100000) */
#define INA230_CAL_SCALING 512U
/** @brief The LSB value for the bus voltage register, in microvolts/LSB. */
#define INA230_BUS_VOLTAGE_UV_LSB 1250U
/**
* @brief The LSB value for the power register.
*
*/
#define INA230_POWER_VALUE_LSB 25
/** @brief The scaling for the power register. */
#define INA230_POWER_SCALING 25
static int ina230_channel_get(const struct device *dev,
enum sensor_channel chan,
@ -37,7 +30,8 @@ static int ina230_channel_get(const struct device *dev,
{
struct ina230_data *data = dev->data;
const struct ina230_config *const config = dev->config;
uint32_t bus_uv;
uint32_t bus_uv, current_ua, power_uw;
int32_t sign;
switch (chan) {
case SENSOR_CHAN_VOLTAGE:
@ -49,37 +43,31 @@ static int ina230_channel_get(const struct device *dev,
break;
case SENSOR_CHAN_CURRENT:
if (config->current_lsb == INA23X_CURRENT_LSB_1MA) {
/**
* If current is negative, convert it to a
* magnitude and return the negative of that
* magnitude.
*/
if (data->current & INA23X_CURRENT_SIGN_BIT) {
uint16_t current_mag = (~data->current + 1);
current_ua = ~data->current + 1U;
sign = -1;
} else {
current_ua = data->current;
sign = 1;
}
/* see datasheet "Programming" section for reference */
current_ua = current_ua * config->current_lsb;
/* convert to fractional amperes */
val->val1 = sign * (int32_t)(current_ua / 1000000U);
val->val2 = sign * (int32_t)(current_ua % 1000000U);
val->val1 = -(current_mag / 1000U);
val->val2 = -(current_mag % 1000) * 1000;
} else {
val->val1 = data->current / 1000U;
val->val2 = (data->current % 1000) * 1000;
}
} else {
val->val1 = data->current;
val->val2 = 0;
}
break;
case SENSOR_CHAN_POWER:
if (config->current_lsb == INA23X_CURRENT_LSB_1MA) {
uint32_t power_mw = data->power * INA230_POWER_VALUE_LSB;
power_uw = data->power * INA230_POWER_SCALING
* config->current_lsb;
/* convert to fractional watts */
val->val1 = (int32_t)(power_uw / 1000000U);
val->val2 = (int32_t)(power_uw % 1000000U);
val->val1 = power_mw / 1000U;
val->val2 = (power_mw % 1000) * 1000;
} else {
val->val1 = data->power;
val->val2 = 0;
}
break;
default:
@ -202,7 +190,9 @@ static int ina230_calibrate(const struct device *dev)
uint16_t val;
int ret;
val = (INA230_INTERNAL_FIXED_SCALING_VALUE / (config->current_lsb * config->rshunt));
/* See datasheet "Programming" section */
val = (INA230_CAL_SCALING * 10000U) /
(config->current_lsb * config->rshunt);
ret = ina23x_reg_write(&config->bus, INA230_REG_CALIB, val);
if (ret < 0) {
@ -285,7 +275,7 @@ static const struct sensor_driver_api ina230_driver_api = {
static const struct ina230_config drv_config_##inst = { \
.bus = I2C_DT_SPEC_INST_GET(inst), \
.config = DT_INST_PROP(inst, config), \
.current_lsb = DT_INST_PROP(inst, current_lsb), \
.current_lsb = DT_INST_PROP(inst, current_lsb_microamps),\
.rshunt = DT_INST_PROP(inst, rshunt_milliohms), \
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \
(INA230_CFG_IRQ(inst)), ()) \

View file

@ -48,7 +48,7 @@ struct ina230_data {
struct ina230_config {
struct i2c_dt_spec bus;
uint16_t config;
uint16_t current_lsb;
uint32_t current_lsb;
uint16_t rshunt;
#ifdef CONFIG_INA230_TRIGGER
bool trig_enabled;

View file

@ -16,21 +16,14 @@
LOG_MODULE_REGISTER(INA237, CONFIG_SENSOR_LOG_LEVEL);
/**
* @brief Internal fixed value of INA237 that is used to ensure
* scaling is properly maintained.
*
*/
#define INA237_INTERNAL_FIXED_SCALING_VALUE 8192
/** @brief Calibration scaling value (scaled by 10^-5) */
#define INA237_CAL_SCALING 8192U
/** @brief The LSB value for the bus voltage register, in microvolts/LSB. */
#define INA237_BUS_VOLTAGE_UV_LSB 3125
/**
* @brief The LSB value for the power register.
*
*/
#define INA237_POWER_VALUE_LSB 2
/** @brief Power scaling (scaled by 10) */
#define INA237_POWER_SCALING 2
static int ina237_channel_get(const struct device *dev,
enum sensor_channel chan,
@ -38,7 +31,8 @@ static int ina237_channel_get(const struct device *dev,
{
struct ina237_data *data = dev->data;
const struct ina237_config *config = dev->config;
uint32_t bus_uv;
uint32_t bus_uv, current_ua, power_uw;
int32_t sign;
switch (chan) {
case SENSOR_CHAN_VOLTAGE:
@ -49,40 +43,30 @@ static int ina237_channel_get(const struct device *dev,
break;
case SENSOR_CHAN_CURRENT:
if (config->current_lsb == INA23X_CURRENT_LSB_1MA) {
/**
* If current is negative, convert it to a
* magnitude and return the negative of that
* magnitude.
*/
if (data->current & INA23X_CURRENT_SIGN_BIT) {
uint16_t current_mag = (~data->current + 1);
val->val1 = -(current_mag / 10000U);
val->val2 = -(current_mag % 10000) * 100;
current_ua = ~data->current + 1U;
sign = -1;
} else {
val->val1 = data->current / 10000U;
val->val2 = (data->current % 10000) * 100;
}
} else {
val->val1 = data->current;
val->val2 = 0;
current_ua = data->current;
sign = 1;
}
/* see datasheet "Current and Power calculations" section */
current_ua = current_ua * config->current_lsb;
/* convert to fractional amperes */
val->val1 = sign * (int32_t)(current_ua / 1000000U);
val->val2 = sign * (int32_t)(current_ua % 1000000U);
break;
case SENSOR_CHAN_POWER:
if (config->current_lsb == INA23X_CURRENT_LSB_1MA) {
uint32_t power_mw = ((data->power *
config->current_lsb *
INA237_POWER_VALUE_LSB) / 10);
/* see datasheet "Current and Power calculations" section */
power_uw = (data->power * INA237_POWER_SCALING *
config->current_lsb) / 10000U;
val->val1 = power_mw / 10000U;
val->val2 = (power_mw % 10000) * 100;
} else {
val->val1 = data->power;
val->val2 = 0;
}
/* convert to fractional watts */
val->val1 = (int32_t)(power_uw / 1000000U);
val->val2 = (int32_t)(power_uw % 1000000U);
break;
default:
@ -259,7 +243,9 @@ static int ina237_calibrate(const struct device *dev)
uint16_t val;
int ret;
val = ((INA237_INTERNAL_FIXED_SCALING_VALUE * config->current_lsb * config->rshunt) / 100);
/* see datasheet "Current and Power calculations" section */
val = (INA237_CAL_SCALING * config->current_lsb * config->rshunt) /
10000000U;
ret = ina23x_reg_write(&config->bus, INA237_REG_CALIB, val);
if (ret < 0) {
@ -393,7 +379,7 @@ static const struct sensor_driver_api ina237_driver_api = {
.bus = I2C_DT_SPEC_INST_GET(inst), \
.config = DT_INST_PROP(inst, config), \
.adc_config = DT_INST_PROP(inst, adc_config), \
.current_lsb = DT_INST_PROP(inst, current_lsb), \
.current_lsb = DT_INST_PROP(inst, current_lsb_microamps), \
.rshunt = DT_INST_PROP(inst, rshunt_milliohms), \
.alert_config = DT_INST_PROP_OR(inst, alert_config, 0x01), \
.gpio_alert = GPIO_DT_SPEC_INST_GET_OR(inst, irq_gpios, {0}), \

View file

@ -48,7 +48,7 @@ struct ina237_config {
struct i2c_dt_spec bus;
uint16_t config;
uint16_t adc_config;
uint16_t current_lsb;
uint32_t current_lsb;
uint16_t rshunt;
const struct gpio_dt_spec gpio_alert;
uint16_t alert_config;

View file

@ -17,15 +17,22 @@ properties:
delay for initial ADC conversion, shunt full scale range
for INA237.
current-lsb:
current-lsb-microamps:
type: int
required: true
description: |
Value of Current LSB in milliamps. When set to 1mA,
Current is read in A, Bus Voltage in V, Shunt
Voltage in V, and Power in mW. Any other value
results in Current, Voltage, and Power registers
being read in counts.
This value should be selected so that measurement resolution is
maximized, that is:
current-lsb(A) = maximum expected current(A) / 2^15
(sensor has 15 bits). For example, if maximum expected current is 15A:
current-lsb(A) = 15A / 2^15 ~= 457uA
Rounded values may be used for convenience, e.g. 500uA/LSB or 1mA/LSB
while keeping a good measurement resolution. The units are in uA/LSB
so that low maximum currents can be measured with enough resolution.
rshunt-milliohms:
type: int

View file

@ -591,7 +591,7 @@ test_i2c_ina230: ina230@4f {
compatible = "ti,ina230";
reg = <0x4f>;
config = <0>;
current-lsb = <1>;
current-lsb-microamps = <1000>;
rshunt-milliohms = <0>;
mask = <0>;
alert-limit = <0>;
@ -608,7 +608,7 @@ test_i2c_ina231: ina231@51 {
compatible = "ti,ina230";
reg = <0x51>;
config = <0>;
current-lsb = <1>;
current-lsb-microamps = <1000>;
rshunt-milliohms = <0>;
mask = <0>;
alert-limit = <0>;
@ -619,7 +619,7 @@ test_i2c_ina237: ina237@52 {
compatible = "ti,ina237";
reg = <0x52>;
config = <0>;
current-lsb = <1>;
current-lsb-microamps = <1000>;
adc-config = <0>;
rshunt-milliohms = <0>;
alert-config = <0>;