From 64fb6f0673debe935277b12c1d61d23f3484129c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Ju=C5=99ena?= Date: Fri, 6 Dec 2024 08:12:42 +0100 Subject: [PATCH] drivers: sensor: ti: ina23x: Refactor INA230 trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trigger use own thread or system work queue to handle interrupts. This allows to read the device state in the trigger handler. Also trigger can distinguish between data ready and alert functionality, and does not require prepare device state before setting trigger. Signed-off-by: Tomáš Juřena --- drivers/sensor/ti/ina23x/Kconfig | 35 +++- drivers/sensor/ti/ina23x/ina230.c | 78 ++++++--- drivers/sensor/ti/ina23x/ina230.h | 11 +- drivers/sensor/ti/ina23x/ina230_trigger.c | 152 ++++++++++++++++-- include/zephyr/drivers/sensor/ina230.h | 33 ++++ .../sensor/ina230/boards/native_sim.overlay | 2 + tests/drivers/sensor/ina230/prj.conf | 2 + tests/drivers/sensor/ina230/src/ina230_test.c | 148 +++++++++++++---- 8 files changed, 384 insertions(+), 77 deletions(-) create mode 100644 include/zephyr/drivers/sensor/ina230.h diff --git a/drivers/sensor/ti/ina23x/Kconfig b/drivers/sensor/ti/ina23x/Kconfig index b83ecda24c2272..0c8f92c0bdba90 100644 --- a/drivers/sensor/ti/ina23x/Kconfig +++ b/drivers/sensor/ti/ina23x/Kconfig @@ -41,12 +41,41 @@ config INA237_VSHUNT measurement is required. config INA230_TRIGGER - bool "INA230 trigger mode" + bool + +choice + prompt "Trigger mode" + default INA230_TRIGGER_GLOBAL_THREAD + help + Specify the type of triggering to be used by the driver. + +config INA230_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on INA230 + depends on GPIO + depends on $(dt_compat_any_has_prop,$(DT_COMPAT_TI_INA230),alert-gpios) || $(dt_compat_any_has_prop,$(DT_COMPAT_TI_INA236),alert-gpios) + select INA230_TRIGGER + +config INA230_TRIGGER_GLOBAL_THREAD + bool "Use global thread" depends on INA230 depends on GPIO depends on $(dt_compat_any_has_prop,$(DT_COMPAT_TI_INA230),alert-gpios) || $(dt_compat_any_has_prop,$(DT_COMPAT_TI_INA236),alert-gpios) + select INA230_TRIGGER +endchoice + +config INA230_THREAD_PRIORITY + int "Own thread priority" + depends on INA230_TRIGGER_OWN_THREAD + default 10 + help + The priority of the thread used for handling interrupts. + +config INA230_THREAD_STACK_SIZE + int "Own thread stack size" + depends on INA230_TRIGGER_OWN_THREAD + default 1024 help - Set to enable trigger mode using gpio interrupt, where - interrupts are configured to line ALERT PIN. + The thread stack size. endif # INA23X diff --git a/drivers/sensor/ti/ina23x/ina230.c b/drivers/sensor/ti/ina23x/ina230.c index 1e496a400ff064..f0aec400e068db 100644 --- a/drivers/sensor/ti/ina23x/ina230.c +++ b/drivers/sensor/ti/ina23x/ina230.c @@ -10,6 +10,7 @@ #include #include +#include LOG_MODULE_REGISTER(INA230, CONFIG_SENSOR_LOG_LEVEL); @@ -24,13 +25,17 @@ LOG_MODULE_REGISTER(INA230, CONFIG_SENSOR_LOG_LEVEL); #define INA230_POWER_SCALING 25 #define INA236_POWER_SCALING 32 +#define INA230_VSHUNT_NV_LSV 2500 + +#define INA230_MASK_REG_MASK (INA230_ALERT_POLARITY | INA230_ALERT_LATCH_ENABLE) + static int ina230_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { struct ina230_data *data = dev->data; const struct ina230_config *const config = dev->config; uint32_t bus_uv, power_uw; - int32_t current_ua; + int32_t current_ua, shunt_nv; switch (chan) { case SENSOR_CHAN_VOLTAGE: @@ -54,9 +59,16 @@ static int ina230_channel_get(const struct device *dev, enum sensor_channel chan power_uw = data->power * config->power_scale * config->current_lsb; /* convert to fractional watts */ - val->val1 = (int32_t)(power_uw / 1000000U); - val->val2 = (int32_t)(power_uw % 1000000U); + val->val1 = power_uw / 1000000U; + val->val2 = power_uw % 1000000U; + break; + case SENSOR_CHAN_VSHUNT: + shunt_nv = data->shunt_voltage * INA230_VSHUNT_NV_LSV; + + /* convert to fractional milli-volts */ + val->val1 = shunt_nv / 1000000L; + val->val2 = shunt_nv % 1000000L; break; default: @@ -73,7 +85,7 @@ static int ina230_sample_fetch(const struct device *dev, enum sensor_channel cha int ret; if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_CURRENT && - chan != SENSOR_CHAN_POWER) { + chan != SENSOR_CHAN_POWER && chan != SENSOR_CHAN_VSHUNT) { return -ENOTSUP; } @@ -101,6 +113,14 @@ static int ina230_sample_fetch(const struct device *dev, enum sensor_channel cha } } + if ((chan == SENSOR_CHAN_ALL) || (chan == SENSOR_CHAN_VSHUNT)) { + ret = ina23x_reg_read_16(&config->bus, INA230_REG_SHUNT_VOLT, &data->shunt_voltage); + if (ret < 0) { + LOG_ERR("Failed to read shunt voltage"); + return ret; + } + } + return 0; } @@ -108,7 +128,10 @@ static int ina230_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { const struct ina230_config *config = dev->config; + struct ina230_data *d = dev->data; uint16_t data = val->val1; + uint16_t new_val; + int ret; switch (attr) { case SENSOR_ATTR_CONFIGURATION: @@ -116,7 +139,15 @@ static int ina230_attr_set(const struct device *dev, enum sensor_channel chan, case SENSOR_ATTR_CALIBRATION: return ina23x_reg_write(&config->bus, INA230_REG_CALIB, data); case SENSOR_ATTR_FEATURE_MASK: - return ina23x_reg_write(&config->bus, INA230_REG_MASK, data); + new_val = (d->mask & ~INA230_MASK_REG_MASK) | (data & INA230_MASK_REG_MASK); + + ret = ina23x_reg_write(&config->bus, INA230_REG_MASK, new_val); + if (!ret) { + d->mask = new_val; + } + + return ret; + case SENSOR_ATTR_ALERT: return ina23x_reg_write(&config->bus, INA230_REG_ALERT, data); default: @@ -185,6 +216,9 @@ static int ina230_calibrate(const struct device *dev) static int ina230_init(const struct device *dev) { const struct ina230_config *const config = dev->config; +#ifdef CONFIG_INA230_TRIGGER + struct ina230_data *data = dev->data; +#endif int ret; if (!device_is_ready(config->bus.bus)) { @@ -205,25 +239,25 @@ static int ina230_init(const struct device *dev) } #ifdef CONFIG_INA230_TRIGGER - if (config->trig_enabled) { - ret = ina230_trigger_mode_init(dev); - if (ret < 0) { - LOG_ERR("Failed to init trigger mode\n"); - return ret; - } + ret = ina230_trigger_mode_init(dev); + if (ret < 0) { + LOG_ERR("Failed to init trigger mode\n"); + return ret; + } - ret = ina23x_reg_write(&config->bus, INA230_REG_ALERT, config->alert_limit); - if (ret < 0) { - LOG_ERR("Failed to write alert register!"); - return ret; - } + ret = ina23x_reg_write(&config->bus, INA230_REG_ALERT, config->alert_limit); + if (ret < 0) { + LOG_ERR("Failed to write alert register!"); + return ret; + } - ret = ina23x_reg_write(&config->bus, INA230_REG_MASK, config->mask); - if (ret < 0) { - LOG_ERR("Failed to write mask register!"); - return ret; - } + ret = ina23x_reg_write(&config->bus, INA230_REG_MASK, config->mask); + if (ret < 0) { + LOG_ERR("Failed to write mask register!"); + return ret; } + + data->mask = config->mask; #endif /* CONFIG_INA230_TRIGGER */ return 0; @@ -241,7 +275,7 @@ static DEVICE_API(sensor, ina230_driver_api) = { #ifdef CONFIG_INA230_TRIGGER #define INA230_CFG_IRQ(inst) \ - .trig_enabled = true, .mask = DT_INST_PROP(inst, mask), \ + .mask = DT_INST_PROP(inst, mask) & INA230_MASK_REG_MASK, \ .alert_limit = DT_INST_PROP(inst, alert_limit), \ .alert_gpio = GPIO_DT_SPEC_INST_GET(inst, alert_gpios) #else diff --git a/drivers/sensor/ti/ina23x/ina230.h b/drivers/sensor/ti/ina23x/ina230.h index 1e01283f60e7bc..4fe2197ec8d98a 100644 --- a/drivers/sensor/ti/ina23x/ina230.h +++ b/drivers/sensor/ti/ina23x/ina230.h @@ -39,12 +39,20 @@ struct ina230_data { int16_t current; uint16_t bus_voltage; uint16_t power; + int16_t shunt_voltage; + uint16_t mask; #ifdef CONFIG_INA230_TRIGGER const struct device *gpio; struct gpio_callback gpio_cb; - struct k_work work; sensor_trigger_handler_t handler_alert; const struct sensor_trigger *trig_alert; + sensor_trigger_handler_t handler_cnvr; + const struct sensor_trigger *trig_cnvr; +#if CONFIG_INA230_TRIGGER_OWN_THREAD + struct k_sem sem; +#elif CONFIG_INA230_TRIGGER_GLOBAL_THREAD + struct k_work work; +#endif #endif /* CONFIG_INA230_TRIGGER */ }; @@ -56,7 +64,6 @@ struct ina230_config { uint8_t power_scale; uint32_t uv_lsb; #ifdef CONFIG_INA230_TRIGGER - bool trig_enabled; uint16_t mask; const struct gpio_dt_spec alert_gpio; uint16_t alert_limit; diff --git a/drivers/sensor/ti/ina23x/ina230_trigger.c b/drivers/sensor/ti/ina23x/ina230_trigger.c index f449b1cbc2d3dc..fe1c8ee9e32fb1 100644 --- a/drivers/sensor/ti/ina23x/ina230_trigger.c +++ b/drivers/sensor/ti/ina23x/ina230_trigger.c @@ -6,40 +6,149 @@ */ #include "ina230.h" +#include "ina23x_common.h" #include #include #include +#include +#include LOG_MODULE_DECLARE(INA230, CONFIG_SENSOR_LOG_LEVEL); -static void ina230_gpio_callback(const struct device *port, - struct gpio_callback *cb, uint32_t pin) +static void ina230_gpio_callback(const struct device *port, struct gpio_callback *cb, uint32_t pin) { struct ina230_data *ina230 = CONTAINER_OF(cb, struct ina230_data, gpio_cb); - const struct device *dev = (const struct device *)ina230->dev; ARG_UNUSED(port); ARG_UNUSED(pin); - ARG_UNUSED(cb); - if (ina230->handler_alert) { - ina230->handler_alert(dev, ina230->trig_alert); +#if defined(CONFIG_INA230_TRIGGER_OWN_THREAD) + k_sem_give(&ina230->sem); +#elif defined(CONFIG_INA230_TRIGGER_GLOBAL_THREAD) + k_work_submit(&ina230->work); +#endif +} + +static void ina230_handle_interrupts(const struct device *dev) +{ + const struct ina230_config *config = dev->config; + struct ina230_data *data = dev->data; + uint16_t reg; + + if (ina23x_reg_read_16(&config->bus, INA230_REG_MASK, ®) < 0) { + return; + } + + if (data->handler_alert && reg & reg & INA230_ALERT_FUNCTION_FLAG) { + data->handler_alert(dev, data->trig_alert); + } + + if (data->handler_cnvr && reg & INA230_CONVERSION_READY_FLAG) { + data->handler_cnvr(dev, data->trig_cnvr); + } +} + +#ifdef CONFIG_INA230_TRIGGER_GLOBAL_THREAD +static void ina230_work_handler(struct k_work *work) +{ + struct ina230_data *data = CONTAINER_OF(work, struct ina230_data, work); + + ina230_handle_interrupts(data->dev); +} +#endif + +#ifdef CONFIG_INA230_TRIGGER_OWN_THREAD +static K_KERNEL_STACK_DEFINE(ina230_thread_stack, CONFIG_INA230_THREAD_STACK_SIZE); +static struct k_thread ina230_thread; + +static void ina230_thread_main(void *p1, void *p2, void *p3) +{ + struct ina230_data *data = p1; + + while (1) { + k_sem_take(&data->sem, K_FOREVER); + ina230_handle_interrupts(data->dev); } } +#endif -int ina230_trigger_set(const struct device *dev, - const struct sensor_trigger *trig, +static int get_enable_flag_from_trigger(const struct sensor_trigger *trig, uint16_t *bit) +{ + switch (trig->chan) { + case SENSOR_CHAN_VSHUNT: + *bit = INA230_SHUNT_VOLTAGE_OVER; + break; + + case SENSOR_CHAN_VOLTAGE: + *bit = INA230_BUS_VOLTAGE_OVER; + break; + + case SENSOR_CHAN_POWER: + *bit = INA230_OVER_LIMIT_POWER; + if ((enum sensor_trigger_type_ina230)trig->type == SENSOR_TRIG_INA230_UNDER) { + return -ENOTSUP; + } + break; + + default: + return -ENOTSUP; + } + + return 0; +}; + +int ina230_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { + enum sensor_trigger_type_ina230 type = (enum sensor_trigger_type_ina230)trig->type; + const struct ina230_config *config = dev->config; struct ina230_data *ina230 = dev->data; + uint16_t new_val, mask, flag; + int ret = 0; + + if (trig->type != SENSOR_TRIG_DATA_READY && type != SENSOR_TRIG_INA230_OVER && + type != SENSOR_TRIG_INA230_UNDER) { + return -ENOTSUP; + } - ARG_UNUSED(trig); + if (trig->type == SENSOR_TRIG_DATA_READY) { + ina230->handler_cnvr = handler; + ina230->trig_cnvr = trig; + mask = INA230_CONVERSION_READY; + flag = INA230_CONVERSION_READY; + } else { + ret = get_enable_flag_from_trigger(trig, &flag); + if (ret) { + goto exit; + } + + ina230->handler_alert = handler; + ina230->trig_alert = trig; + mask = INA230_SHUNT_VOLTAGE_OVER | INA230_SHUNT_VOLTAGE_UNDER | + INA230_BUS_VOLTAGE_OVER | INA230_BUS_VOLTAGE_UNDER | INA230_OVER_LIMIT_POWER; + + if (type == SENSOR_TRIG_INA230_UNDER) { + flag >>= 1; + } + } - ina230->handler_alert = handler; - ina230->trig_alert = trig; + new_val = (ina230->mask & ~mask) | (!!handler * flag); - return 0; + if (new_val == ina230->mask) { + goto exit; + } + + ret = ina23x_reg_write(&config->bus, INA230_REG_MASK, new_val); + if (ret) { + LOG_ERR("Failed to enable CNVR flag"); + goto exit; + } + + ina230->mask = new_val; + +exit: + return ret; } int ina230_trigger_mode_init(const struct device *dev) @@ -62,9 +171,19 @@ int ina230_trigger_mode_init(const struct device *dev) return ret; } - gpio_init_callback(&ina230->gpio_cb, - ina230_gpio_callback, - BIT(config->alert_gpio.pin)); +#if defined(CONFIG_INA230_TRIGGER_OWN_THREAD) + k_sem_init(&ina230->sem, 0, K_SEM_MAX_LIMIT); + + k_tid_t tid = + k_thread_create(&ina230_thread, ina230_thread_stack, + CONFIG_INA230_THREAD_STACK_SIZE, ina230_thread_main, ina230, NULL, + NULL, K_PRIO_COOP(CONFIG_INA230_THREAD_PRIORITY), 0, K_NO_WAIT); + k_thread_name_set(tid, "ina230-trigger"); +#elif defined(CONFIG_INA230_TRIGGER_GLOBAL_THREAD) + ina230->work.handler = ina230_work_handler; +#endif + + gpio_init_callback(&ina230->gpio_cb, ina230_gpio_callback, BIT(config->alert_gpio.pin)); ret = gpio_add_callback(config->alert_gpio.port, &ina230->gpio_cb); if (ret < 0) { @@ -72,6 +191,5 @@ int ina230_trigger_mode_init(const struct device *dev) return ret; } - return gpio_pin_interrupt_configure_dt(&config->alert_gpio, - GPIO_INT_EDGE_BOTH); + return gpio_pin_interrupt_configure_dt(&config->alert_gpio, GPIO_INT_EDGE_TO_ACTIVE); } diff --git a/include/zephyr/drivers/sensor/ina230.h b/include/zephyr/drivers/sensor/ina230.h new file mode 100644 index 00000000000000..f98cc1bf5bee61 --- /dev/null +++ b/include/zephyr/drivers/sensor/ina230.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, Tomas Jurena + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Extended public API for TI INA230/INA236 current-shunt and power monitor + * + * This exposes two triggers for the INA230/INA236 which can be used for + * setting trigger channel being above or bellow defined alert limit. + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_INA230_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_INA230_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum sensor_trigger_type_ina230 { + /** Alert over limit trigger. */ + SENSOR_TRIG_INA230_OVER = SENSOR_TRIG_PRIV_START, + /** Alert bellow trigger. */ + SENSOR_TRIG_INA230_UNDER, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_INA230_H_ */ diff --git a/tests/drivers/sensor/ina230/boards/native_sim.overlay b/tests/drivers/sensor/ina230/boards/native_sim.overlay index 3b81407dee4bbe..d7c248f9430ba6 100644 --- a/tests/drivers/sensor/ina230/boards/native_sim.overlay +++ b/tests/drivers/sensor/ina230/boards/native_sim.overlay @@ -13,6 +13,7 @@ reg = <0x40>; rshunt-micro-ohms = <2000>; current-lsb-microamps = <1000>; + alert-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; status = "okay"; }; @@ -21,6 +22,7 @@ reg = <0x41>; rshunt-micro-ohms = <2000>; current-lsb-microamps = <1000>; + alert-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; status = "okay"; }; }; diff --git a/tests/drivers/sensor/ina230/prj.conf b/tests/drivers/sensor/ina230/prj.conf index 6ad5ef41b5683a..9d30aab2dfac91 100644 --- a/tests/drivers/sensor/ina230/prj.conf +++ b/tests/drivers/sensor/ina230/prj.conf @@ -4,10 +4,12 @@ CONFIG_ZTEST=y CONFIG_SENSOR=y CONFIG_EMUL=y +CONFIG_GPIO=y CONFIG_I2C_EMUL=y CONFIG_I2C_LOG_LEVEL_DBG=y CONFIG_INA230=y +CONFIG_INA230_TRIGGER_OWN_THREAD=y CONFIG_CBPRINTF_FP_SUPPORT=y diff --git a/tests/drivers/sensor/ina230/src/ina230_test.c b/tests/drivers/sensor/ina230/src/ina230_test.c index 4269efc7fa76a0..b94949467a449e 100644 --- a/tests/drivers/sensor/ina230/src/ina230_test.c +++ b/tests/drivers/sensor/ina230/src/ina230_test.c @@ -8,12 +8,18 @@ #include #include #include +#include +#include #include +#include +#include #include #include #include +DEFINE_FFF_GLOBALS; + enum ina23x_ids { INA230, INA236 @@ -26,6 +32,7 @@ struct ina230_fixture { const uint16_t rshunt_uOhms; const uint16_t config; const enum ina23x_ids dev_type; + const struct gpio_dt_spec alert_gpios; }; /** @@ -43,8 +50,7 @@ ZTEST(ina230_0, test_default_config) /* confirm default DT configuration */ uint16_t expected = 0x0127; - zexpect_equal(expected, config->config, "0x%x != config (0x%x)", - expected, config->config); + zexpect_equal(expected, config->config, "0x%x != config (0x%x)", expected, config->config); } static void test_datasheet_example(struct ina230_fixture *fixture) @@ -94,31 +100,23 @@ static void test_datasheet_example(struct ina230_fixture *fixture) static void test_shunt_cal(struct ina230_fixture *fixture) { /* Confirm SHUNT_CAL register which is 5120e-6 / Current_LSB * Rshunt */ - double shunt_cal = 5120e-6 / (fixture->current_lsb_uA * 1e-6 * - fixture->rshunt_uOhms * 1e-6); + double shunt_cal = + 5120e-6 / (fixture->current_lsb_uA * 1e-6 * fixture->rshunt_uOhms * 1e-6); uint32_t shunt_register_actual; uint16_t shunt_register_expected = (uint16_t)shunt_cal; zassert_ok(ina230_mock_get_register(fixture->mock->data, INA230_REG_CALIB, - &shunt_register_actual)); - zexpect_within(shunt_register_expected, shunt_register_actual, 1, - "Expected %d, got %d", shunt_register_expected, shunt_register_actual); + &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 ina230_fixture *fixture) { /* 16-bit signed value for current register */ const int16_t current_reg_vectors[] = { - 32767, - 1000, - 100, - 1, - 0, - -1, - -100, - -1000, - -32768, + 32767, 1000, 100, 1, 0, -1, -100, -1000, -32768, }; for (int idx = 0; idx < ARRAY_SIZE(current_reg_vectors); idx++) { @@ -134,8 +132,8 @@ static void test_current(struct ina230_fixture *fixture) zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_CURRENT, &sensor_val)); double current_actual_A = sensor_value_to_double(&sensor_val); - zexpect_within(current_expected_A, current_actual_A, fixture->current_lsb_uA*1e-6, - "Expected %.6f A, got %.6f A", current_expected_A, current_actual_A); + zexpect_within(current_expected_A, current_actual_A, fixture->current_lsb_uA * 1e-6, + "Expected %.6f A, got %.6f A", current_expected_A, current_actual_A); } } @@ -145,12 +143,8 @@ static void test_bus_voltage(struct ina230_fixture *fixture) /* 16-bit signed value for voltage register (but always positive) 1.25 mV/bit */ const int16_t voltage_reg_vectors[] = { - 32767, - 28800, /* 36V maximum voltage */ - 1000, - 100, - 1, - 0, + 32767, 28800, /* 36V maximum voltage */ + 1000, 100, 1, 0, }; double bitres = fixture->dev_type == INA236 ? 1.6e-3 : 1.25e-3; @@ -159,7 +153,7 @@ static void test_bus_voltage(struct ina230_fixture *fixture) struct sensor_value sensor_val; ina230_mock_set_register(fixture->mock->data, INA230_REG_BUS_VOLT, - voltage_reg_vectors[idx]); + voltage_reg_vectors[idx]); /* Verify sensor value is correct */ zassert_ok(sensor_sample_fetch(fixture->dev)); @@ -169,7 +163,7 @@ static void test_bus_voltage(struct ina230_fixture *fixture) double voltage_expected_V = voltage_reg_vectors[idx] * bitres; zexpect_within(voltage_expected_V, voltage_actual_V, 1e-6, - "Expected %.6f A, got %.6f A", voltage_expected_V, voltage_actual_V); + "Expected %.6f A, got %.6f A", voltage_expected_V, voltage_actual_V); } } @@ -177,13 +171,7 @@ static void test_power(struct ina230_fixture *fixture) { /* 16-bit unsigned value for power register */ const uint16_t power_reg_vectors[] = { - 65535, - 32767, - 10000, - 1000, - 100, - 1, - 0, + 65535, 32767, 10000, 1000, 100, 1, 0, }; int scale = fixture->dev_type == INA236 ? 32 : 25; @@ -209,6 +197,87 @@ static void test_power(struct ina230_fixture *fixture) } } +static void test_shunt_voltage(struct ina230_fixture *fixture) +{ + /* 16-bit signed value for vshunt register */ + const int16_t vshunt_reg_vectors[] = { + 32767, 1000, 100, 1, 0, -1, -100, -1000, -32768, + }; + + ARRAY_FOR_EACH(vshunt_reg_vectors, idx) { + struct sensor_value sensor_val; + int32_t vshunt_register = vshunt_reg_vectors[idx]; + + /* shunt voltage is vshunt_register * 2.5 uV */ + double vshunt_expected_mV = vshunt_register * 2.5 * 1e-3; + + /* set current reading */ + ina230_mock_set_register(fixture->mock->data, INA230_REG_SHUNT_VOLT, + vshunt_register); + + /* Verify sensor value is correct */ + zassert_ok(sensor_sample_fetch(fixture->dev)); + zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_VSHUNT, &sensor_val)); + double vshunt_actual_mV = sensor_value_to_double(&sensor_val); + + zexpect_within(vshunt_expected_mV, vshunt_actual_mV, 1e-6, + "Expected %.6f mV, got %.6f mV for %d", vshunt_expected_mV, + vshunt_actual_mV); + } +} + +FAKE_VOID_FUNC(test_cnvr_trigger_handler, const struct device *, const struct sensor_trigger *); +FAKE_VOID_FUNC(test_alert_trigger_handler, const struct device *, const struct sensor_trigger *); + +static void test_trigger(struct ina230_fixture *fixture) +{ + const struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + int ret; + + RESET_FAKE(test_cnvr_trigger_handler); + ret = sensor_trigger_set(fixture->dev, &trigger, test_cnvr_trigger_handler); + zassert_equal(ret, 0); + + ina230_mock_set_register(fixture->mock->data, INA230_REG_MASK, + INA230_CONVERSION_READY | INA230_CONVERSION_READY_FLAG); + + /* Toggle the GPIO */ + gpio_emul_input_set(fixture->alert_gpios.port, fixture->alert_gpios.pin, 1); + k_msleep(5); + gpio_emul_input_set(fixture->alert_gpios.port, fixture->alert_gpios.pin, 0); + k_msleep(5); + + /* Verify the handler was called */ + zassert_equal(test_cnvr_trigger_handler_fake.call_count, 1); +} + +static void test_trigger_config(struct ina230_fixture *fixture) +{ + const struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + const struct sensor_trigger alarm_trigger = { + .type = SENSOR_TRIG_INA230_OVER, + .chan = SENSOR_CHAN_VOLTAGE, + }; + struct sensor_value reg; + int ret; + + ret = sensor_trigger_set(fixture->dev, &trigger, test_cnvr_trigger_handler); + zassert_equal(ret, 0); + + ret = sensor_trigger_set(fixture->dev, &alarm_trigger, test_alert_trigger_handler); + zassert_equal(ret, 0); + + sensor_attr_get(fixture->dev, SENSOR_CHAN_ALL, SENSOR_ATTR_FEATURE_MASK, ®); + zassert_equal(reg.val1 & (INA230_CONVERSION_READY | INA230_BUS_VOLTAGE_OVER), + (INA230_CONVERSION_READY | INA230_BUS_VOLTAGE_OVER)); +} + /* Create a test fixture for each enabled ina230 device node */ #define INA230_FIXTURE_ENTRY(inst, v) \ static struct ina230_fixture fixture_23##v##_##inst = { \ @@ -218,6 +287,7 @@ static void test_power(struct ina230_fixture *fixture) .rshunt_uOhms = DT_INST_PROP(inst, rshunt_micro_ohms), \ .config = DT_INST_PROP(inst, config), \ .dev_type = INA23##v, \ + .alert_gpios = GPIO_DT_SPEC_INST_GET_OR(inst, alert_gpios, {0}), \ } /* Create a test suite for each enabled ina230 device node */ @@ -243,6 +313,18 @@ static void test_power(struct ina230_fixture *fixture) { \ test_power(&fixture_23##v##_##inst); \ } \ + ZTEST(ina23##v##_##inst, test_shunt_voltage) \ + { \ + test_shunt_voltage(&fixture_23##v##_##inst); \ + } \ + ZTEST(ina23##v##_##inst, test_trigger) \ + { \ + test_trigger(&fixture_23##v##_##inst); \ + } \ + ZTEST(ina23##v##_##inst, test_trigger_config) \ + { \ + test_trigger_config(&fixture_23##v##_##inst); \ + } \ ZTEST_SUITE(ina23##v##_##inst, NULL, NULL, NULL, NULL, NULL); #undef DT_DRV_COMPAT