drivers: sensor: ina237: add high-precision mode
The current-shunt calibration requires a factor of 4x if high-precision mode is selected. Signed-off-by: Eric Holmberg <eric.holmberg@northriversystems.co.nz>
This commit is contained in:
parent
2d3a391191
commit
a70d056513
6 changed files with 112 additions and 7 deletions
|
@ -22,8 +22,8 @@ LOG_MODULE_REGISTER(INA237, CONFIG_SENSOR_LOG_LEVEL);
|
|||
/** @brief The LSB value for the bus voltage register, in microvolts/LSB. */
|
||||
#define INA237_BUS_VOLTAGE_TO_uV(x) ((x) * 3125U)
|
||||
|
||||
/** @brief Power scaling (scaled by 10) */
|
||||
#define INA237_POWER_SCALING 2
|
||||
/** @brief Power scaling (need factor of 0.2) */
|
||||
#define INA237_POWER_TO_uW(x) ((x) / 5ULL)
|
||||
|
||||
/**
|
||||
* @brief Scale die temperture from 0.125 degC/bit to micro-degrees C
|
||||
|
@ -37,6 +37,12 @@ static void micro_s32_to_sensor_value(struct sensor_value *val, int32_t value_mi
|
|||
val->val2 = value_microX % 1000000L;
|
||||
}
|
||||
|
||||
static void micro_u64_to_sensor_value(struct sensor_value *val, uint64_t value_microX)
|
||||
{
|
||||
val->val1 = value_microX / 1000000U;
|
||||
val->val2 = value_microX % 1000000U;
|
||||
}
|
||||
|
||||
static int ina237_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
|
@ -54,9 +60,9 @@ static int ina237_channel_get(const struct device *dev, enum sensor_channel chan
|
|||
break;
|
||||
|
||||
case SENSOR_CHAN_POWER:
|
||||
/* see datasheet "Current and Power calculations" section */
|
||||
micro_s32_to_sensor_value(val,
|
||||
(data->power * INA237_POWER_SCALING * config->current_lsb) / 10000U);
|
||||
/* power in uW is power_reg * current_lsb * 0.2 */
|
||||
micro_u64_to_sensor_value(val,
|
||||
INA237_POWER_TO_uW((uint64_t)data->power * config->current_lsb));
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_DIE_TEMP:
|
||||
|
@ -364,6 +370,10 @@ static const struct sensor_driver_api ina237_driver_api = {
|
|||
.channel_get = ina237_channel_get,
|
||||
};
|
||||
|
||||
/* Shunt calibration must be muliplied by 4 if high-prevision mode is selected */
|
||||
#define CAL_PRECISION_MULTIPLIER(config) \
|
||||
(((config & INA237_CFG_HIGH_PRECISION) >> 4) * 3 + 1)
|
||||
|
||||
#define INA237_DRIVER_INIT(inst) \
|
||||
static struct ina237_data ina237_data_##inst; \
|
||||
static const struct ina237_config ina237_config_##inst = { \
|
||||
|
@ -376,8 +386,9 @@ static const struct sensor_driver_api ina237_driver_api = {
|
|||
(DT_INST_ENUM_IDX(inst, temp_conversion_time_us) << 3) | \
|
||||
DT_INST_ENUM_IDX(inst, avg_count), \
|
||||
.current_lsb = DT_INST_PROP(inst, current_lsb_microamps), \
|
||||
.cal = INA237_CAL_SCALING * DT_INST_PROP(inst, current_lsb_microamps) * \
|
||||
DT_INST_PROP(inst, rshunt_micro_ohms) / 10000000000ULL, \
|
||||
.cal = CAL_PRECISION_MULTIPLIER(DT_INST_PROP(inst, config)) * \
|
||||
INA237_CAL_SCALING * DT_INST_PROP(inst, current_lsb_microamps) * \
|
||||
DT_INST_PROP(inst, rshunt_micro_ohms) / 10000000ULL, \
|
||||
.alert_config = DT_INST_PROP_OR(inst, alert_config, 0x01), \
|
||||
.alert_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, alert_gpios, {0}), \
|
||||
}; \
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
#define INA237_REG_CONFIG 0x00
|
||||
#define INA237_CFG_HIGH_PRECISION BIT(4)
|
||||
|
||||
#define INA237_REG_ADC_CONFIG 0x01
|
||||
#define INA237_REG_CALIB 0x02
|
||||
#define INA237_REG_SHUNT_VOLT 0x04
|
||||
|
|
|
@ -97,3 +97,8 @@ properties:
|
|||
Default is the power-on reset value.
|
||||
default: 1
|
||||
enum: [1, 4, 16, 64, 128, 256, 512, 1024]
|
||||
|
||||
high-precision:
|
||||
type: boolean
|
||||
description: |
|
||||
Enable high precision mode (4x the resolution).
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <zephyr/dt-bindings/dt-util.h>
|
||||
|
||||
/* Operating Mode */
|
||||
#define INA237_CFG_HIGH_PRECISION BIT(4)
|
||||
#define INA237_OPER_MODE_SHUTDOWN 0x00
|
||||
#define INA237_OPER_MODE_BUS_VOLTAGE_TRIG 0x01
|
||||
#define INA237_OPER_MODE_SHUNT_VOLTAGE_TRIG 0x02
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
&i2c0 {
|
||||
status = "okay";
|
||||
|
||||
/* standard precision mode */
|
||||
ina237_default_test: ina237@40 {
|
||||
compatible = "ti,ina237";
|
||||
reg = <0x40>;
|
||||
|
@ -14,4 +15,34 @@
|
|||
current-lsb-microamps = <123>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
/* high precision mode */
|
||||
ina237@41 {
|
||||
compatible = "ti,ina237";
|
||||
reg = <0x41>;
|
||||
high-precision;
|
||||
rshunt-micro-ohms = <400>;
|
||||
current-lsb-microamps = <123>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
/* high precision mode, maximum current lsb */
|
||||
ina237@42 {
|
||||
compatible = "ti,ina237";
|
||||
reg = <0x42>;
|
||||
high-precision;
|
||||
rshunt-micro-ohms = <0x00ff>;
|
||||
current-lsb-microamps = <0xffff>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
/* high precision mode, maximum rshunt */
|
||||
ina237@43 {
|
||||
compatible = "ti,ina237";
|
||||
reg = <0x43>;
|
||||
high-precision;
|
||||
rshunt-micro-ohms = <0xffff>;
|
||||
current-lsb-microamps = <0x00ff>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <zephyr/drivers/emul.h>
|
||||
#include <zephyr/drivers/i2c_emul.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/dt-bindings/sensor/ina237.h>
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
#include <ina237_emul.h>
|
||||
|
@ -44,6 +45,26 @@ ZTEST(ina237_0, test_default_config)
|
|||
*
|
||||
* @param fixture
|
||||
*/
|
||||
|
||||
static void test_shunt_cal(struct ina237_fixture *fixture)
|
||||
{
|
||||
/* Confirm SHUNT_CAL register which is 819.2e6 * Current_LSB * Rshunt */
|
||||
double shunt_cal = 819.2e6 * fixture->current_lsb_uA * 1e-6 * fixture->rshunt_uOhms * 1e-6;
|
||||
|
||||
if (fixture->config & INA237_CFG_HIGH_PRECISION) {
|
||||
/* High precision mode */
|
||||
shunt_cal *= 4;
|
||||
}
|
||||
|
||||
uint32_t shunt_register_actual;
|
||||
uint16_t shunt_register_expected = (uint16_t)shunt_cal;
|
||||
|
||||
zassert_ok(ina237_mock_get_register(fixture->mock->data, INA237_REG_CALIB,
|
||||
&shunt_register_actual));
|
||||
zexpect_within(shunt_register_expected, shunt_register_actual, 1,
|
||||
"Expected %d, got %d", shunt_register_expected, shunt_register_actual);
|
||||
}
|
||||
|
||||
static void test_current(struct ina237_fixture *fixture)
|
||||
{
|
||||
/* 16-bit signed value for current register */
|
||||
|
@ -109,6 +130,38 @@ static void test_bus_voltage(struct ina237_fixture *fixture)
|
|||
}
|
||||
}
|
||||
|
||||
static void test_power(struct ina237_fixture *fixture)
|
||||
{
|
||||
/* 24-bit unsigned value for power register */
|
||||
const uint32_t power_reg_vectors[] = {
|
||||
16777215,
|
||||
65535,
|
||||
32767,
|
||||
1000,
|
||||
100,
|
||||
1,
|
||||
0,
|
||||
};
|
||||
|
||||
for (int idx = 0; idx < ARRAY_SIZE(power_reg_vectors); idx++) {
|
||||
struct sensor_value sensor_val;
|
||||
uint32_t power_register = power_reg_vectors[idx];
|
||||
|
||||
/* power is 0.2 * current_lsb * register */
|
||||
double power_expected_W = 0.2 * fixture->current_lsb_uA * 1e-6 * power_register;
|
||||
|
||||
/* set current reading */
|
||||
ina237_mock_set_register(fixture->mock->data, INA237_REG_POWER, power_register);
|
||||
|
||||
/* Verify sensor value is correct */
|
||||
zassert_ok(sensor_sample_fetch(fixture->dev));
|
||||
zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_POWER, &sensor_val));
|
||||
double power_actual_W = sensor_value_to_double(&sensor_val);
|
||||
|
||||
zexpect_within(power_expected_W, power_actual_W, 1e-6,
|
||||
"Expected %.6f C, got %.6f C", power_expected_W, power_actual_W);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_temperature(struct ina237_fixture *fixture)
|
||||
{
|
||||
|
@ -161,8 +214,10 @@ static struct ina237_fixture fixtures[] = {
|
|||
|
||||
/* Create a test suite for each enabled ina237 device node */
|
||||
#define INA237_TESTS(inst) \
|
||||
ZTEST(ina237_##inst, test_shunt_cal) { test_shunt_cal(&fixtures[inst]); } \
|
||||
ZTEST(ina237_##inst, test_current) { test_current(&fixtures[inst]); } \
|
||||
ZTEST(ina237_##inst, test_bus_voltage) { test_bus_voltage(&fixtures[inst]); } \
|
||||
ZTEST(ina237_##inst, test_power) { test_power(&fixtures[inst]); } \
|
||||
ZTEST(ina237_##inst, test_temperature) { test_temperature(&fixtures[inst]); } \
|
||||
ZTEST_SUITE(ina237_##inst, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue