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_CONV_TIME_1100, INA230_CONV_TIME_1100,
INA230_AVG_MODE_1)>; INA230_AVG_MODE_1)>;
/* Set current LSB to 1mA */ current-lsb-microamps = <1000>;
current-lsb = <1>;
rshunt-milliohms = <15>; rshunt-milliohms = <15>;
}; };
}; };

View file

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

View file

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

View file

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

View file

@ -17,15 +17,22 @@ properties:
delay for initial ADC conversion, shunt full scale range delay for initial ADC conversion, shunt full scale range
for INA237. for INA237.
current-lsb: current-lsb-microamps:
type: int type: int
required: true required: true
description: | description: |
Value of Current LSB in milliamps. When set to 1mA, This value should be selected so that measurement resolution is
Current is read in A, Bus Voltage in V, Shunt maximized, that is:
Voltage in V, and Power in mW. Any other value
results in Current, Voltage, and Power registers current-lsb(A) = maximum expected current(A) / 2^15
being read in counts.
(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: rshunt-milliohms:
type: int type: int

View file

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