This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 297fc3129 sensor/drivers: Add driver for INA228 current monitor
297fc3129 is described below

commit 297fc3129f13f8eac31ee7eaaa2036158825021a
Author: Jerzy Kasenberg <[email protected]>
AuthorDate: Wed Oct 29 21:50:03 2025 +0100

    sensor/drivers: Add driver for INA228 current monitor
    
    Driver for INA228 current monitor.
    
    Signed-off-by: Jerzy Kasenberg <[email protected]>
---
 hw/drivers/sensors/ina228/include/ina228/ina228.h | 431 ++++++++++++
 hw/drivers/sensors/ina228/pkg.yml                 |  48 ++
 hw/drivers/sensors/ina228/src/ina228.c            | 809 ++++++++++++++++++++++
 hw/drivers/sensors/ina228/src/ina228_shell.c      | 245 +++++++
 hw/drivers/sensors/ina228/syscfg.yml              |  67 ++
 5 files changed, 1600 insertions(+)

diff --git a/hw/drivers/sensors/ina228/include/ina228/ina228.h 
b/hw/drivers/sensors/ina228/include/ina228/ina228.h
new file mode 100644
index 000000000..538951d02
--- /dev/null
+++ b/hw/drivers/sensors/ina228/include/ina228/ina228.h
@@ -0,0 +1,431 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef __INA228_H__
+#define __INA228_H__
+
+#include <stdint.h>
+#include <os/mynewt.h>
+#include <stats/stats.h>
+#include <sensor/sensor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define INA228_CONFIG_REG_ADDR            0x00
+#define INA228_ADC_CONFIG_REG_ADDR        0x01
+#define INA228_SHUNT_CAL_REG_ADDR         0x02
+#define INA228_SHUNT_TEMPCO_REG_ADDR      0x03
+#define INA228_VSHUNT_REG_ADDR            0x04
+#define INA228_VBUS_REG_ADDR              0x05
+#define INA228_DIETEMP_REG_ADDR           0x06
+#define INA228_CURRENT_REG_ADDR           0x07
+#define INA228_POWER_REG_ADDR             0x08
+#define INA228_ENERGY_REG_ADDR            0x09
+#define INA228_CHARGE_REG_ADDR            0x0A
+#define INA228_DIAG_ALRT_REG_ADDR         0x0B
+#define INA228_SOVL_REG_ADDR              0x0C
+#define INA228_SUVL_REG_ADDR              0x0D
+#define INA228_BOVL_REG_ADDR              0x0E
+#define INA228_BUVL_REG_ADDR              0x0F
+#define INA228_TEMP_LOMIT_REG_ADDR        0x10
+#define INA228_PWR_LOMIT_REG_ADDR         0x11
+#define INA228_MANUFACTURER_ID_REG_ADDR   0x3E
+#define INA228_DEVICE_ID_REG_ADDR         0x3F
+
+#define INA228_CONFIG_ADCRANGE_Pos        4u
+#define INA228_CONFIG_ADCRANGE_Msk        (1u << INA228_CONFIG_ADCRANGE_Pos)
+#define INA228_CONFIG_TEMPCOMP_Pos        5u
+#define INA228_CONFIG_TEMPCOMP_Msk        (1u << INA228_CONFIG_TEMPCOMP_Pos)
+#define INA228_CONFIG_CONVDL_Pos          6u
+#define INA228_CONFIG_CONVDL_Msk          (0xFF << INA228_CONFIG_CONVDL_Pos)
+#define INA228_CONFIG_RSTACC_Pos          14u
+#define INA228_CONFIG_RSTACC_Msk          (1u << INA228_CONFIG_RSTACC_Pos)
+#define INA228_CONFIG_RST_Pos             15u
+#define INA228_CONFIG_RST_Msk             (1u << INA228_CONFIG_RST_Pos)
+
+#define INA228_ADC_CONFIG_MODE_Pos        12u
+#define INA228_ADC_CONFIG_MODE_Msk        (0xF << INA228_ADC_CONFIG_MODE_Pos)
+#define INA228_ADC_CONFIG_VBUSCT_Pos      9u
+#define INA228_ADC_CONFIG_VBUSCT_Msk      (0x7 << INA228_ADC_CONFIG_VBUSCT_Pos)
+#define INA228_ADC_CONFIG_VSHCT_Pos       6u
+#define INA228_ADC_CONFIG_VSHCT_Msk       (0x7 << INA228_ADC_CONFIG_VSHCT_Pos)
+#define INA228_ADC_CONFIG_VTCT_Pos        3u
+#define INA228_ADC_CONFIG_VTCT_Msk        (0x7 << INA228_ADC_CONFIG_VTCT_Pos)
+#define INA228_ADC_CONFIG_AVG_Pos         0u
+#define INA228_ADC_CONFIG_AVG_Msk         (0x7 << INA228_ADC_CONFIG_AVG_Pos)
+
+#define INA228_ADC_CONFIG_MODE_SHUTDOWN   0
+#define INA228_ADC_CONFIG_MODE_VBUS       (1 << INA228_ADC_CONFIG_MODE_Pos)
+#define INA228_ADC_CONFIG_MODE_VSHUNT     (2 << INA228_ADC_CONFIG_MODE_Pos)
+#define INA228_ADC_CONFIG_MODE_TEMP       (4 << INA228_ADC_CONFIG_MODE_Pos)
+#define INA228_ADC_CONFIG_MODE_CONTINUOUS (8 << INA228_ADC_CONFIG_MODE_Pos)
+
+#define INA228_SHUNT_CAL_SHUNT_CAL_Pos    0u
+#define INA228_SHUNT_CAL_SHUNT_CAL_Msk    (0x7FFF << 
INA228_SHUNT_CAL_SHUNT_CAL_Pos)
+
+#define INA228_SHUNT_TEMPCO_TEMPCO_Pos    0u
+#define INA228_SHUNT_TEMPCO_TEMPCO_Msk    (0x3FFF << 
INA228_SHUNT_TEMPCO_TEMPCO_Pos)
+
+#define INA228_VSHUNT_VSHUNT_Pos          4u
+#define INA228_VSHUNT_VSHUNT_Msk          (0x0FFFFF << 
INA228_VSHUNT_VSHUNT_Pos)
+
+#define INA228_VBUS_VBUS_Pos              4u
+#define INA228_VBUS_VBUS_Msk              (0x0FFFFF << INA228_VBUS_VBUS_Pos)
+
+#define INA228_DIETEMP_DIETEMP_Pos        0u
+#define INA228_DIETEMP_DIETEMP_Msk        (0xFFFF << 
INA228_DIETEMP_DIETEMP_Pos)
+
+#define INA228_CURRENT_CURRENT_Pos        4u
+#define INA228_CURRENT_CURRENT_Msk        (0x0FFFFF << 
INA228_CURRENT_CURRENT_Pos)
+
+#define INA228_POWER_POWER_Pos            0u
+#define INA228_POWER_POWER_Msk            (0x0FFFFFF << INA228_POWER_POWER_Pos)
+
+#define INA228_ENERGY_ENERGY_Pos          0u
+#define INA228_ENERGY_ENERGY_Msk          (0x00FFFFFFFFFFULL << 
INA228_ENERGY_ENERGY_Pos)
+
+#define INA228_CHARGE_CHARGE_Pos          0u
+#define INA228_CHARGE_CHARGE_Msk          (0x00FFFFFFFFFFULL << 
INA228_CHARGE_CHARGE_Pos)
+
+#define INA228_MANUFACTURER_ID            0x5449
+
+#define INA228_DIAG_ALRT_MEMSTAT          0x0001
+#define INA228_DIAG_ALRT_CNVRF            0x0002
+#define INA228_DIAG_ALRT_POL              0x0004
+#define INA228_DIAG_ALRT_BUSUL            0x0008
+#define INA228_DIAG_ALRT_BUSOL            0x0010
+#define INA228_DIAG_ALRT_SHNTUL           0x0020
+#define INA228_DIAG_ALRT_SHNTOL           0x0040
+#define INA228_DIAG_ALRT_TMPOL            0x0080
+#define INA228_DIAG_ALRT_MATHOF           0x0200
+#define INA228_DIAG_ALRT_CHARGEOF         0x0400
+#define INA228_DIAG_ALRT_ENERGYOF         0x0800
+#define INA228_DIAG_ALRT_APOL             0x1000
+#define INA228_DIAG_ALRT_SLOWALERT        0x2000
+#define INA228_DIAG_ALRT_CNRV             0x4000
+#define INA228_DIAG_ALRT_ALATCH           0x8000
+
+/* 312.5 nV */
+#define INA228_SHUNT_VOLTAGE_0_LSB        312500
+/* 78.125 nV */
+#define INA228_SHUNT_VOLTAGE_1_LSB        78125
+/* 195.3125 uV */
+#define INA228_BUS_VOLTAGE_LSB            195312
+/* 7.8125 mC */
+#define INA228_TEMPERATURE_LSB            7812
+
+/* INA228 operating modes */
+enum ina228_avg_mode {
+    INA228_AVG_1,
+    INA228_AVG_4,
+    INA228_AVG_16,
+    INA228_AVG_64,
+    INA228_AVG_128,
+    INA228_AVG_256,
+    INA228_AVG_512,
+    INA228_AVG_1024,
+};
+
+/* Bus/shunt voltage/temperature conversion time */
+enum ina228_ct {
+    INA228_CT_50_US,
+    INA228_CT_84_US,
+    INA228_CT_150_US,
+    INA228_CT_280_US,
+    INA228_CT_540_US,
+    INA228_CT_1052_US,
+    INA228_CT_2074_US,
+    INA228_CT_4120_US,
+};
+
+/* INA228 operating modes */
+enum ina228_oper_mode {
+    INA228_MODE_SHUTDOWN = 0,
+    INA228_MODE_BUS_VOLTAGE = 1,
+    INA228_MODE_SHUNT_VOLTAGE = 2,
+    INA228_MODE_TEMPERATURE = 4,
+    INA228_MODE_CONTINUOUS = 8,
+};
+
+struct ina228_hw_cfg {
+    struct sensor_itf itf;
+    /** Shunt resistance in mOhm. */
+    uint32_t shunt_resistance;
+    /** Interrupt pin number, -1 if not used. */
+    int16_t interrupt_pin;
+    /** Interrupt pin requires pull-up.
+     * Set to false if external pull up resistor is present.
+     */
+    bool interrupt_pullup;
+};
+
+struct ina228_cfg {
+    /** VBus conversion time */
+    enum ina228_ct vbusct;
+    /** Shunt conversion time */
+    enum ina228_ct vshct;
+    /** Temperature conversion time */
+    enum ina228_ct vtct;
+    /** Averaging mode */
+    enum ina228_avg_mode avg_mode;
+};
+
+/* Define the stats section and records */
+STATS_SECT_START(ina228_stat_section)
+    STATS_SECT_ENTRY(read_count)
+    STATS_SECT_ENTRY(write_count)
+    STATS_SECT_ENTRY(read_errors)
+    STATS_SECT_ENTRY(write_errors)
+STATS_SECT_END
+
+struct ina228_dev {
+#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
+    struct bus_i2c_node i2c_node;
+#else
+    struct os_dev dev;
+#endif
+    struct sensor sensor;
+    /* Hardware wiring config, (pin, shunt, i2c) */
+    struct ina228_hw_cfg hw_cfg;
+
+    uint16_t config_reg;
+    uint16_t adc_config_reg;
+    uint16_t shunt_cal_reg;
+    uint16_t shunt_tempco_reg;
+    uint16_t diag_alrt_reg;
+    uint16_t sovl_reg;
+    uint16_t suvl_reg;
+    uint16_t bovl_reg;
+    uint16_t buvl_reg;
+    uint16_t temp_limit_reg;
+    uint16_t pwr_limit_reg;
+    uint16_t mask_enable_reg;
+    STATS_SECT_DECL(ina228_stat_section) stats;
+
+    uint32_t conversion_time;
+    struct os_sem conversion_done;
+};
+
+/**
+ * Write INA228 sensor register over sensor interface
+ *
+ * @param The sensor interface
+ * @param register address
+ * @param variable length payload
+ * @param length of the payload to write
+ * @return 0 on success, non-zero error on failure.
+ */
+int ina228_write_reg(struct ina228_dev *dev, uint8_t reg, uint16_t reg_val);
+
+/**
+ * Read 16 bit register from INA228
+ *
+ * @param The sensor interface
+ * @param register address
+ * @param variable length payload
+ * @param length of the payload to read
+ * @return 0 on success, non-zero error on failure.
+ */
+int ina228_read_reg(struct ina228_dev *dev, uint8_t reg, uint16_t *reg_val);
+
+/**
+ * Read 24 bit register from INA228
+ *
+ * @param The sensor interface
+ * @param register address
+ * @param variable length payload
+ * @param length of the payload to read
+ * @return 0 on success, non-zero error on failure.
+ */
+int ina228_read_reg24(struct ina228_dev *dev, uint8_t reg, uint32_t *reg_val);
+
+/**
+ * Read 32 bit register from INA228
+ *
+ * @param The sensor interface
+ * @param register address
+ * @param variable length payload
+ * @param length of the payload to read
+ * @return 0 on success, non-zero error on failure.
+ */
+int ina228_read_reg32(struct ina228_dev *dev, uint8_t reg, uint32_t *reg_val);
+
+/**
+ * Read multiple length data from INA228 sensor over sensor interface
+ *
+ * @param The Sensor interface
+ * @param variable length payload
+ * @param length of the payload to read
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int ina228_read(struct ina228_dev *dev, uint8_t *payload, uint32_t len);
+
+/**
+ * Initialize the ina228. this function is normally called by sysinit.
+ *
+ * @param ina228  Pointer to the ina228_dev device descriptor.
+ * @param arg     Pointer to struct ina228_hw_cfg.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_init(struct os_dev *ina228, void *arg);
+
+/**
+ * Reset sensor.
+ *
+ * @param ina228    Pointer to the ina228 device.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_reset(struct ina228_dev *ina228);
+
+/**
+ * Sets operating mode configuration.
+ *
+ * @param  ina228      Pointer to the ina228 device.
+ * @param  ina228_cfg  Pointer to the ina228 configuration.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_config(struct ina228_dev *ina228, const struct ina228_cfg *cfg);
+
+/**
+ * Reads shunt voltage.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param voltage   Pointer to shunt voltage in nV.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_read_shunt_voltage(struct ina228_dev *ina228, int32_t *voltage);
+
+/**
+ * Reads bus voltage.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param voltage   Pointer to output voltage in uV.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_read_bus_voltage(struct ina228_dev *ina228, uint32_t *voltage);
+
+/**
+ * Reads shunt voltage and returns current.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param current   Pointer to output current in uA.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_read_current(struct ina228_dev *ina228, int32_t *current);
+
+/**
+ * Reads manufactured and die id.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param mfg       Pointer to manufacturer id.
+ * @param die       Pointer to die id.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_read_id(struct ina228_dev *ina228, uint16_t *mfg, uint16_t *die);
+
+/**
+ * Start reads current, shunt and bus voltage.
+ *
+ * Function does not wait for actual conversion.
+ * Code should call ina228_wait_and_read() to get results.
+ * This can be useful when conversion time is big enough and user
+ * code can do other tasks while waiting for conversion to finish.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param current   Pointer to output current in uA (can be NULL).
+ * @param vbus      Pointer to bus voltage in uV (can be NULL).
+ * @param vshunt    Pointer to shunt voltage in nV (can be NULL).
+ * @param temp      Pointer to temperature mC (can be NULL).
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_one_shot_read_start(struct ina228_dev *ina228, int32_t *current,
+                               uint32_t *vbus, int32_t *vshunt, int32_t *temp);
+
+/**
+ * Reads current, shunt and bus voltage.
+ * Function reads value in blocking mode.
+ * Total time depends on conversion time and averaging mode.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param current   Pointer to output current in uA (can be NULL).
+ * @param vbus      Pointer to bus voltage in uV (can be NULL).
+ * @param vshunt    Pointer to shunt voltage in nV (can be NULL).
+ * @param temp      Pointer to temperature mC (can be NULL).
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_one_shot_read(struct ina228_dev *ina228, int32_t *current,
+                         uint32_t *vbus, int32_t *vshunt, int32_t *temp);
+
+/**
+ * Waits for interrupt to fire and read values.
+ * Intended to be used with continuous read mode.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param current   Pointer to output current in uA (can be NULL).
+ * @param vbus      Pointer to bus voltage in uV (can be NULL).
+ * @param vshunt    Pointer to shunt voltage in nV (can be NULL).
+ * @param temp      Pointer to temperature in mC (can be NULL).
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_wait_and_read(struct ina228_dev *ina228, int32_t *current,
+                         uint32_t *vbus, int32_t *vshunt, int32_t *temp);
+
+/**
+ * Start continuous read mode.
+ *
+ * @param ina228    Pointer to ina228 device.
+ * @param mode      shunt and/or vbus mode.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_start_continuous_mode(struct ina228_dev *ina228, enum 
ina228_oper_mode mode);
+
+/**
+ * Stop continuous read mode.
+ *
+ * @param ina228    Pointer to ina228 device.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina228_stop_continuous_mode(struct ina228_dev *ina228);
+
+#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
+int ina228_create_sensor_dev(struct ina228_dev *ina228, const char *name,
+                             const struct bus_i2c_node_cfg *i2c_cfg,
+                             const struct ina228_hw_cfg *hw_cfg);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INA228_H__ */
diff --git a/hw/drivers/sensors/ina228/pkg.yml 
b/hw/drivers/sensors/ina228/pkg.yml
new file mode 100644
index 000000000..64e4e8500
--- /dev/null
+++ b/hw/drivers/sensors/ina228/pkg.yml
@@ -0,0 +1,48 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/drivers/sensors/ina228
+pkg.description: Driver for the INA228 current sensor
+pkg.author:
+pkg.keywords:
+    - current
+    - power
+    - i2c
+    - sensor
+
+pkg.deps:
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/hw/hal"
+    - "@apache-mynewt-core/hw/sensor"
+    - "@apache-mynewt-core/sys/log/modlog"
+    - "@apache-mynewt-core/hw/util/i2cn"
+
+pkg.req_apis:
+    - stats
+
+pkg.source_files:
+    - "src/ina228.c"
+
+pkg.source_files.INA228_CLI:
+    - "src/ina228_shell.c"
+
+pkg.whole_archive: true
+
+pkg.deps.INA228_CLI:
+    - "@apache-mynewt-core/util/parse"
diff --git a/hw/drivers/sensors/ina228/src/ina228.c 
b/hw/drivers/sensors/ina228/src/ina228.c
new file mode 100644
index 000000000..7a50721a7
--- /dev/null
+++ b/hw/drivers/sensors/ina228/src/ina228.c
@@ -0,0 +1,809 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <os/mynewt.h>
+#include <hal/hal_i2c.h>
+#include <i2cn/i2cn.h>
+#include <sensor/sensor.h>
+#include <sensor/voltage.h>
+#include <sensor/current.h>
+#include <sensor/temperature.h>
+
+#include <stats/stats.h>
+#include <ina228/ina228.h>
+#include <hal/hal_gpio.h>
+
+#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
+#define OS_DEV(dev_)   (&dev_->i2c_node.bnode.odev)
+#define I2C_ADDR(dev_) (dev_->i2c_node.addr)
+#else
+#define OS_DEV(dev_)   (&dev_->dev)
+#define I2C_ADDR(dev_) (dev_->hw_cfg.itf.si_addr)
+#endif
+
+/* Define stat names for querying */
+STATS_NAME_START(ina228_stat_section)
+    STATS_NAME(ina228_stat_section, read_count)
+    STATS_NAME(ina228_stat_section, write_count)
+    STATS_NAME(ina228_stat_section, read_errors)
+    STATS_NAME(ina228_stat_section, write_errors)
+STATS_NAME_END(ina228_stat_section)
+
+/* Exports for the sensor API */
+static int ina228_sensor_read(struct sensor *sensor, sensor_type_t typ,
+                              sensor_data_func_t data_func,
+                              void *data_func_arg, uint32_t timeout);
+
+static int ina228_sensor_get_config(struct sensor *sensor, sensor_type_t typ,
+                                    struct sensor_cfg *cfg);
+
+static const struct sensor_driver g_ina228_sensor_driver = {
+    .sd_read = ina228_sensor_read,
+    .sd_get_config = ina228_sensor_get_config,
+};
+
+static uint32_t
+bit_field(uint32_t val, uint32_t mask, uint32_t pos)
+{
+    return (val & mask) >> pos;
+}
+
+static uint16_t
+ina228_averaging(uint16_t adc_config)
+{
+    static const uint16_t averaging[8] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
+    int ix = bit_field(adc_config, INA228_ADC_CONFIG_AVG_Msk, 
INA228_ADC_CONFIG_AVG_Pos);
+
+    return averaging[ix];
+}
+
+static uint16_t
+ina228_conv_time(uint16_t adc_config, uint32_t mask, uint32_t pos)
+{
+    /* Conversion times depending on CT filed [us] */
+    static const uint16_t conversion_times[8] = { 50,  84,   150,  280,
+                                                  540, 1052, 2074, 4120 };
+    int ix = bit_field(adc_config, mask, pos);
+
+    return conversion_times[ix];
+}
+
+uint16_t
+ina228_vshunt_conv_time(uint16_t adc_config)
+{
+    return ina228_conv_time(adc_config, INA228_ADC_CONFIG_VSHCT_Msk,
+                            INA228_ADC_CONFIG_VSHCT_Pos);
+}
+
+uint16_t
+ina228_vbus_conv_time(uint16_t adc_config)
+{
+    return ina228_conv_time(adc_config, INA228_ADC_CONFIG_VBUSCT_Msk,
+                            INA228_ADC_CONFIG_VBUSCT_Pos);
+}
+
+uint16_t
+ina228_temp_conv_time(uint16_t adc_config)
+{
+    return ina228_conv_time(adc_config, INA228_ADC_CONFIG_VTCT_Msk,
+                            INA228_ADC_CONFIG_VTCT_Pos);
+}
+
+/* Calculate total conversion time in us. */
+static uint32_t
+ina228_conversion_time(uint32_t adc_config)
+{
+    uint32_t time;
+
+    if (adc_config & INA228_ADC_CONFIG_MODE_VSHUNT) {
+        time = ina228_vshunt_conv_time(adc_config);
+    } else {
+        time = 0;
+    }
+    if (adc_config & INA228_ADC_CONFIG_MODE_VBUS) {
+        time += ina228_vbus_conv_time(adc_config);
+    }
+    if (adc_config & INA228_ADC_CONFIG_MODE_TEMP) {
+        time += ina228_temp_conv_time(adc_config);
+    }
+    /* Multiple bus and shunt conversion time by averaging count */
+    time *= ina228_averaging(adc_config);
+    return time;
+}
+
+#if MYNEWT_VAL(INA228_INT_SUPPORT)
+
+static void
+ina228_irq_handler(void *arg)
+{
+    struct ina228_dev *ina228 = (struct ina228_dev *)arg;
+
+    int sr;
+    OS_ENTER_CRITICAL(sr);
+    if (os_sem_get_count(&ina228->conversion_done) == 0) {
+        os_sem_release(&ina228->conversion_done);
+    }
+    OS_EXIT_CRITICAL(sr);
+}
+
+static int
+ina228_init_interrupt(struct ina228_dev *ina228)
+{
+    int pin;
+    bool pullup;
+    int rc = OS_OK;
+
+    pin = ina228->hw_cfg.interrupt_pin;
+    pullup = ina228->hw_cfg.interrupt_pullup;
+
+    if (pin < 0) {
+        goto exit;
+    }
+
+    rc = hal_gpio_irq_init(pin, ina228_irq_handler, ina228, 
HAL_GPIO_TRIG_FALLING,
+                           pullup ? HAL_GPIO_PULL_UP : HAL_GPIO_PULL_NONE);
+    assert(rc == 0);
+exit:
+    return rc;
+}
+
+static void
+disable_interrupt(struct ina228_dev *ina228)
+{
+    if (ina228->hw_cfg.interrupt_pin >= 0) {
+        hal_gpio_irq_disable(ina228->hw_cfg.interrupt_pin);
+    }
+}
+
+static void
+enable_interrupt(struct ina228_dev *ina228)
+{
+    if (ina228->hw_cfg.interrupt_pin >= 0) {
+        hal_gpio_irq_enable(ina228->hw_cfg.interrupt_pin);
+        if ((ina228->diag_alrt_reg & INA228_DIAG_ALRT_CNRV) == 0) {
+            ina228->diag_alrt_reg |= INA228_DIAG_ALRT_CNRV;
+            ina228_write_reg(ina228, INA228_DIAG_ALRT_REG_ADDR, 
ina228->diag_alrt_reg);
+        }
+    }
+}
+
+#else
+#define enable_interrupt(ina228)      (void)ina228
+#define disable_interrupt(ina228)     (void)ina228
+#define ina228_init_interrupt(ina228) 0
+#endif
+
+#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
+int
+ina228_write_reg(struct ina228_dev *ina228, uint8_t reg, uint16_t reg_val)
+{
+    int rc;
+    uint8_t payload[3] = { reg, reg_val >> 8, (uint8_t)reg_val };
+
+    STATS_INC(ina228->stats, write_count);
+    rc = bus_node_simple_write(OS_DEV(ina228), payload, sizeof(payload));
+    if (rc) {
+        STATS_INC(ina228->stats, write_errors);
+        INA228_LOG_ERROR("INA228 write I2C failed\n");
+    }
+
+    return rc;
+}
+
+int
+ina228_read_reg_buf(struct ina228_dev *ina228, uint8_t reg, uint8_t *buf, 
size_t buf_size)
+{
+    int rc;
+
+    STATS_INC(ina228->stats, read_count);
+    rc = bus_node_simple_write_read_transact(OS_DEV(ina228), &reg, 1, buf, 
buf_size);
+    if (rc) {
+        STATS_INC(ina228->stats, read_errors);
+        INA228_LOG_ERROR("INA228 read I2C failed\n");
+    }
+
+    return rc;
+}
+
+#else
+int
+ina228_write_reg_buf(struct ina228_dev *ina228, uint8_t reg,
+                     const uint8_t *buf, size_t buf_size)
+{
+    int rc;
+    uint8_t payload[buf_size + 1] = { reg, reg_val >> 8, (uint8_t)reg_val };
+
+    struct hal_i2c_master_data data_struct = {
+        .address = ina228->sensor.s_itf.si_addr,
+        .len = 3,
+        .buffer = payload,
+    };
+
+    rc = sensor_itf_lock(&ina228->sensor.s_itf, 
MYNEWT_VAL(INA228_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+    STATS_INC(ina228->stats, write_count);
+    rc = i2cn_master_write(ina228->sensor.s_itf.si_num, &data_struct,
+                           OS_TICKS_PER_SEC / 10, 1, 
MYNEWT_VAL(INA228_I2C_RETRIES));
+    if (rc) {
+        STATS_INC(ina228->stats, write_errors);
+        INA228_LOG_ERROR("INA228 write I2C failed\n");
+    }
+
+    sensor_itf_unlock(&ina228->sensor.s_itf);
+
+    return rc;
+}
+
+int
+ina228_read_reg_buf(struct ina228_dev *ina228, uint8_t reg, uint8_t *buf, 
size_t buf_size)
+{
+    int rc;
+
+    struct hal_i2c_master_data data_struct = {
+        .address = ina228->sensor.s_itf.si_addr,
+        .len = 1,
+        .buffer = &reg
+    };
+
+    rc = sensor_itf_lock(&ina228->sensor.s_itf, 
MYNEWT_VAL(INA228_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+
+    STATS_INC(ina228->stats, read_count);
+    rc = i2cn_master_write(ina228->sensor.s_itf.si_num, &data_struct,
+                           OS_TICKS_PER_SEC / 10, 1, 
MYNEWT_VAL(INA228_I2C_RETRIES));
+    if (rc) {
+        STATS_INC(ina228->stats, read_errors);
+        INA228_LOG_ERROR("INA228 write I2C failed\n");
+        goto exit;
+    }
+
+    data_struct.buffer = buf;
+    data_struct.len = buf_size;
+    rc = i2cn_master_read(ina228->sensor.s_itf.si_num, &data_struct,
+                          OS_TICKS_PER_SEC / 10, 1, 
MYNEWT_VAL(INA228_I2C_RETRIES));
+    if (rc) {
+        STATS_INC(ina228->stats, read_errors);
+        INA228_LOG_ERROR("INA228 read I2C failed\n");
+    }
+
+exit:
+    sensor_itf_unlock(&ina228->sensor.s_itf);
+
+    return rc;
+}
+
+#endif
+
+int
+ina228_read_reg(struct ina228_dev *ina228, uint8_t reg, uint16_t *reg_val)
+{
+    int rc;
+    uint8_t buf[2];
+
+    rc = ina228_read_reg_buf(ina228, reg, buf, sizeof(buf));
+
+    if (rc == 0) {
+        *reg_val = (buf[0] << 8) | buf[1];
+    }
+
+    return rc;
+}
+
+int
+ina228_read_reg24(struct ina228_dev *ina228, uint8_t reg, uint32_t *reg_val)
+{
+    int rc;
+    uint8_t buf[3];
+
+    rc = ina228_read_reg_buf(ina228, reg, buf, sizeof(buf));
+
+    if (rc == 0) {
+        *reg_val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+    }
+
+    return rc;
+}
+
+int
+ina228_reset(struct ina228_dev *ina228)
+{
+    int rc;
+
+    rc = ina228_write_reg(ina228, INA228_CONFIG_REG_ADDR, 
INA228_CONFIG_RST_Msk);
+    if (rc == SYS_EOK) {
+        ina228_read_reg(ina228, INA228_CONFIG_REG_ADDR, &ina228->config_reg);
+        ina228_read_reg(ina228, INA228_ADC_CONFIG_REG_ADDR, 
&ina228->adc_config_reg);
+        ina228_read_reg(ina228, INA228_SHUNT_CAL_REG_ADDR, 
&ina228->shunt_cal_reg);
+        ina228_read_reg(ina228, INA228_SHUNT_TEMPCO_REG_ADDR, 
&ina228->shunt_tempco_reg);
+        ina228_read_reg(ina228, INA228_DIAG_ALRT_REG_ADDR, 
&ina228->diag_alrt_reg);
+    }
+    return rc;
+}
+
+int
+ina228_config(struct ina228_dev *ina228, const struct ina228_cfg *cfg)
+{
+    int rc;
+
+    /* Start in power down mode. */
+    ina228->adc_config_reg =
+        (INA228_ADC_CONFIG_VSHCT_Msk &
+         ((uint16_t)cfg->vshct << INA228_ADC_CONFIG_VSHCT_Pos)) |
+        (INA228_ADC_CONFIG_VBUSCT_Msk &
+         ((uint16_t)cfg->vbusct << INA228_ADC_CONFIG_VBUSCT_Pos)) |
+        (INA228_ADC_CONFIG_VTCT_Msk &
+         ((uint16_t)cfg->vtct << INA228_ADC_CONFIG_VTCT_Pos)) |
+        (INA228_ADC_CONFIG_MODE_Msk &
+         ((uint16_t)cfg->avg_mode << INA228_ADC_CONFIG_MODE_Pos));
+
+    rc = ina228_write_reg(ina228, INA228_ADC_CONFIG_REG_ADDR, 
ina228->adc_config_reg);
+
+    return rc;
+}
+
+int
+ina228_read_bus_voltage(struct ina228_dev *ina228, uint32_t *voltage)
+{
+    uint32_t v;
+    int rc;
+
+    rc = ina228_read_reg24(ina228, INA228_VBUS_REG_ADDR, &v);
+    if (rc == SYS_EOK) {
+        /* Extract bits 23-4 */
+        v >>= 4;
+        if (v > 16384) {
+            *voltage = v * (INA228_BUS_VOLTAGE_LSB / 1000);
+        } else {
+            *voltage = (v * INA228_BUS_VOLTAGE_LSB) / 1000;
+        }
+    }
+    return rc;
+}
+
+int
+ina228_read_temperature(struct ina228_dev *ina228, int32_t *temp)
+{
+    int16_t t;
+    int rc;
+
+    rc = ina228_read_reg(ina228, INA228_DIETEMP_REG_ADDR, (uint16_t *)&t);
+    if (rc == SYS_EOK) {
+        if (abs(t) > 16384) {
+            *temp = t * (INA228_TEMPERATURE_LSB / 1000);
+        } else {
+            *temp = (t * INA228_TEMPERATURE_LSB) / 1000;
+        }
+    }
+    return rc;
+}
+
+int
+ina228_read_shunt_voltage(struct ina228_dev *ina228, int32_t *voltage)
+{
+    int32_t v;
+    int rc;
+
+    rc = ina228_read_reg24(ina228, INA228_VSHUNT_REG_ADDR, (uint32_t *)&v);
+    if (rc == SYS_EOK) {
+        /* Extract bits 23-4 with sign extension */
+        v = (v << 8) >> 12;
+        if (abs(v) > 4096) {
+            if (ina228->config_reg & INA228_CONFIG_ADCRANGE_Msk) {
+                v = v * ((int32_t)INA228_SHUNT_VOLTAGE_1_LSB / 1000);
+            } else {
+                v = v * ((int32_t)INA228_SHUNT_VOLTAGE_0_LSB / 1000);
+            }
+        } else {
+            if (ina228->config_reg & INA228_CONFIG_ADCRANGE_Msk) {
+                v = (v * (int32_t)INA228_SHUNT_VOLTAGE_1_LSB) / 1000;
+            } else {
+                v = v * ((int32_t)INA228_SHUNT_VOLTAGE_0_LSB) / 1000;
+            }
+        }
+        *voltage = v;
+    }
+    return rc;
+}
+
+int
+ina228_read_diag_alert(struct ina228_dev *ina228, uint16_t *diag_alert)
+{
+    return ina228_read_reg(ina228, INA228_DIAG_ALRT_REG_ADDR, diag_alert);
+}
+
+int
+ina228_conversion_ready(struct ina228_dev *ina228)
+{
+    int rc;
+    uint16_t diag_alert;
+
+    /* Clear interrupt by reading mask/enable register */
+    rc = ina228_read_diag_alert(ina228, &diag_alert);
+    if (rc == SYS_EOK) {
+        rc = diag_alert & INA228_DIAG_ALRT_CNVRF ? SYS_EOK : SYS_EBUSY;
+    }
+    return rc;
+}
+
+int
+ina228_read_values(struct ina228_dev *ina228, int32_t *current, uint32_t *vbus,
+                   int32_t *vshunt, int32_t *temp)
+{
+    int32_t vshunt_nv;
+    int rc = SYS_EOK;
+
+    if (current != NULL || vshunt != NULL) {
+        rc = ina228_read_shunt_voltage(ina228, &vshunt_nv);
+        if (rc != SYS_EOK) {
+            goto exit;
+        }
+        if (vshunt != NULL) {
+            *vshunt = vshunt_nv;
+        }
+        if (current != NULL) {
+            *current = vshunt_nv / (int32_t)ina228->hw_cfg.shunt_resistance;
+        }
+    }
+    if (vbus != NULL) {
+        rc = ina228_read_bus_voltage(ina228, vbus);
+        if (rc != SYS_EOK) {
+            goto exit;
+        }
+    }
+    if (temp != NULL) {
+        rc = ina228_read_temperature(ina228, temp);
+        if (rc != SYS_EOK) {
+            goto exit;
+        }
+    }
+
+exit:
+    return rc;
+}
+
+int
+ina228_wait_and_read(struct ina228_dev *ina228, int32_t *current,
+                     uint32_t *vbus, int32_t *vshunt, int32_t *temp)
+{
+    int rc;
+
+    if (ina228 == NULL) {
+        return SYS_EINVAL;
+    }
+    if (MYNEWT_VAL(INA228_INT_SUPPORT) && ina228->hw_cfg.interrupt_pin >= 0) {
+        os_sem_pend(&ina228->conversion_done,
+                    2 * (1 + os_time_ms_to_ticks32(ina228->conversion_time / 
1000)));
+    } else {
+        os_cputime_delay_usecs(ina228->conversion_time);
+    }
+
+    do {
+        rc = ina228_conversion_ready(ina228);
+    } while (rc == SYS_EBUSY);
+
+    if (rc == SYS_EOK) {
+        rc = ina228_read_values(ina228, current, vbus, vshunt, temp);
+    }
+    return rc;
+}
+
+int
+ina228_one_shot_read_start(struct ina228_dev *ina228, int32_t *current,
+                           uint32_t *vbus, int32_t *vshunt, int32_t *temp)
+{
+    int rc;
+
+    ina228->adc_config_reg &= ~INA228_ADC_CONFIG_MODE_Msk;
+
+    if (current != NULL || vshunt != NULL) {
+        ina228->adc_config_reg |= INA228_ADC_CONFIG_MODE_VSHUNT;
+    }
+    if (vbus != NULL) {
+        ina228->adc_config_reg |= INA228_ADC_CONFIG_MODE_VBUS;
+    }
+    if (temp != NULL) {
+        ina228->adc_config_reg |= INA228_ADC_CONFIG_MODE_TEMP;
+    }
+    ina228->conversion_time = ina228_conversion_time(ina228->adc_config_reg);
+
+    os_sem_pend(&ina228->conversion_done, 0);
+
+    /* Start one shot conversion. */
+    rc = ina228_write_reg(ina228, INA228_ADC_CONFIG_REG_ADDR, 
ina228->adc_config_reg);
+
+    return rc;
+}
+
+int
+ina228_one_shot_read(struct ina228_dev *ina228, int32_t *current,
+                     uint32_t *vbus, int32_t *vshunt, int32_t *temp)
+{
+    int rc;
+
+    rc = ina228_one_shot_read_start(ina228, current, vbus, vshunt, temp);
+
+    if (rc == SYS_EOK) {
+        rc = ina228_wait_and_read(ina228, current, vbus, vshunt, temp);
+    }
+
+    return rc;
+}
+
+int
+ina228_power_down(struct ina228_dev *ina228)
+{
+    int rc = SYS_EOK;
+    uint32_t adc_config_reg;
+
+    adc_config_reg = ina228->config_reg & ~INA228_ADC_CONFIG_MODE_Msk;
+
+    if (adc_config_reg != ina228->config_reg) {
+        ina228->adc_config_reg = adc_config_reg;
+        rc = ina228_write_reg(ina228, INA228_ADC_CONFIG_REG_ADDR,
+                              ina228->adc_config_reg);
+        (void)ina228_conversion_ready(ina228);
+    }
+
+    return rc;
+}
+
+int
+ina228_start_continuous_mode(struct ina228_dev *ina228, enum ina228_oper_mode 
mode)
+{
+    int rc;
+
+    assert((uint8_t)mode & (uint8_t)INA228_MODE_CONTINUOUS);
+    ina228->adc_config_reg &= ~INA228_ADC_CONFIG_MODE_Msk;
+    ina228->adc_config_reg |= (mode << INA228_ADC_CONFIG_MODE_Pos);
+
+    /* Read status register */
+    (void)ina228_conversion_ready(ina228);
+    os_sem_pend(&ina228->conversion_done, 0);
+
+    rc = ina228_write_reg(ina228, INA228_ADC_CONFIG_REG_ADDR, 
ina228->adc_config_reg);
+
+    ina228->conversion_time = ina228_conversion_time(ina228->adc_config_reg);
+
+    return rc;
+}
+
+int
+ina228_stop_continuous_mode(struct ina228_dev *ina228)
+{
+    return ina228_power_down(ina228);
+}
+
+int
+ina228_read_id(struct ina228_dev *ina228, uint16_t *mfg, uint16_t *die)
+{
+    int rc;
+
+    rc = ina228_read_reg(ina228, INA228_MANUFACTURER_ID_REG_ADDR, mfg);
+    if (rc == SYS_EOK) {
+        rc = ina228_read_reg(ina228, INA228_DEVICE_ID_REG_ADDR, die);
+    }
+    return rc;
+}
+
+static int
+ina228_open(struct os_dev *dev, uint32_t wait, void *arg)
+{
+    /* Default values after power on. */
+    const struct ina228_cfg default_cfg = {
+        .avg_mode = MYNEWT_VAL_INA228_DEFAULT_AVERAGING,
+        .vbusct = MYNEWT_VAL_INA228_DEFAULT_VBUS_CONVERSION_TIME,
+        .vshct = MYNEWT_VAL_INA228_DEFAULT_VSHUNT_CONVERSION_TIME,
+        .vtct = MYNEWT_VAL_INA228_DEFAULT_TEMPERATURE_CONVERSION_TIME,
+    };
+    int rc;
+    uint16_t mfg;
+    uint16_t die;
+    struct ina228_dev *ina228 = (struct ina228_dev *)dev;
+
+    (void)wait;
+
+    /* Reset sensor. */
+    rc = ina228_reset(ina228);
+
+    if (rc) {
+        goto exit;
+    }
+
+    /* Verify sensor ID. */
+    rc = ina228_read_id(ina228, &mfg, &die);
+    if (rc != SYS_EOK) {
+        goto exit;
+    }
+    if (mfg != INA228_MANUFACTURER_ID) {
+        INA228_LOG_ERROR("INA228 read ID failed, no INA228 at 0x%X, found 0x%X 
0x%X\n",
+                         I2C_ADDR(ina228), mfg, die);
+        rc = SYS_ENODEV;
+        goto exit;
+    }
+
+    if (arg == NULL) {
+        rc = ina228_config(ina228, &default_cfg);
+    } else {
+        rc = ina228_config(ina228, (const struct ina228_cfg *)arg);
+    }
+    if (rc == SYS_EOK) {
+        enable_interrupt(ina228);
+    }
+exit:
+    return rc;
+}
+
+static int
+ina228_close(struct os_dev *dev)
+{
+    struct ina228_dev *ina228 = (struct ina228_dev *)dev;
+
+    disable_interrupt(ina228);
+    return ina228_power_down(ina228);
+}
+
+int
+ina228_init(struct os_dev *dev, void *arg)
+{
+    struct ina228_dev *ina228;
+    struct sensor *sensor;
+    int rc;
+
+    if (!arg || !dev) {
+        rc = SYS_ENODEV;
+        goto exit;
+    }
+
+    ina228 = (struct ina228_dev *)dev;
+    ina228->hw_cfg = *(struct ina228_hw_cfg *)arg;
+    os_sem_init(&ina228->conversion_done, 0);
+
+    sensor = &ina228->sensor;
+
+    /* Initialise the stats entry. */
+    rc = stats_init(STATS_HDR(ina228->stats),
+                    STATS_SIZE_INIT_PARMS(ina228->stats, STATS_SIZE_32),
+                    STATS_NAME_INIT_PARMS(ina228_stat_section));
+    SYSINIT_PANIC_ASSERT(rc == SYS_EOK);
+    /* Register the entry with the stats registry */
+    rc = stats_register(OS_DEV(ina228)->od_name, STATS_HDR(ina228->stats));
+    SYSINIT_PANIC_ASSERT(rc == SYS_EOK);
+
+    rc = ina228_init_interrupt(ina228);
+    if (rc != SYS_EOK) {
+        goto exit;
+    }
+
+    rc = sensor_init(sensor, dev);
+    if (rc != SYS_EOK) {
+        goto exit;
+    }
+
+    (void)sensor_set_interface(sensor, &ina228->hw_cfg.itf);
+    (void)sensor_set_type_mask(sensor, SENSOR_TYPE_VOLTAGE | 
SENSOR_TYPE_CURRENT);
+    (void)sensor_set_driver(sensor, SENSOR_TYPE_VOLTAGE | SENSOR_TYPE_CURRENT,
+                            (struct sensor_driver *)&g_ina228_sensor_driver);
+
+    OS_DEV_SETHANDLERS(dev, ina228_open, ina228_close);
+exit:
+    return rc;
+}
+
+static int
+ina228_sensor_read(struct sensor *sensor, sensor_type_t type,
+                   sensor_data_func_t data_func, void *data_func_arg, uint32_t 
timeout)
+{
+    int rc;
+    struct ina228_dev *ina228;
+    int32_t current;
+    uint32_t vbus;
+    int32_t temp;
+    int32_t *pcurrent;
+    uint32_t *pvbus;
+    int32_t *ptemp;
+    union {
+        struct sensor_voltage_data svd;
+        struct sensor_current_data scd;
+        struct sensor_temp_data std;
+    } databuf;
+    (void)timeout;
+
+    /* If the read isn't looking for bus voltage or current, don't do 
anything. */
+    if (0 == (type & (SENSOR_TYPE_VOLTAGE | SENSOR_TYPE_CURRENT))) {
+        rc = SYS_EINVAL;
+        INA228_LOG_ERROR("ina228_sensor_read unsupported sensor type\n");
+        goto exit;
+    }
+
+    pcurrent = (type & SENSOR_TYPE_CURRENT) ? &current : NULL;
+    pvbus = (type & SENSOR_TYPE_VOLTAGE) ? &vbus : NULL;
+    ptemp = (type & SENSOR_TYPE_TEMPERATURE) ? &temp : NULL;
+
+    ina228 = (struct ina228_dev *)SENSOR_GET_DEVICE(sensor);
+
+    rc = ina228_one_shot_read(ina228, pcurrent, pvbus, NULL, ptemp);
+    if (rc == 0) {
+        if (pcurrent) {
+            databuf.scd.scd_current = current / 1000000.0f;
+            databuf.scd.scd_current_is_valid = 1;
+            data_func(sensor, data_func_arg, &databuf.scd, 
SENSOR_TYPE_CURRENT);
+        }
+        if (pvbus) {
+            /* vbus in uV convert to V. */
+            databuf.svd.svd_voltage = vbus / 1000000.0f;
+            databuf.svd.svd_voltage_is_valid = 1;
+            data_func(sensor, data_func_arg, &databuf.scd, 
SENSOR_TYPE_VOLTAGE);
+        }
+        if (ptemp) {
+            /* temperature in mC convert to C. */
+            databuf.std.std_temp = temp / 1000000.0f;
+            databuf.std.std_temp_is_valid = 1;
+            data_func(sensor, data_func_arg, &databuf.std, 
SENSOR_TYPE_TEMPERATURE);
+        }
+    }
+exit:
+    return rc;
+}
+
+static int
+ina228_sensor_get_config(struct sensor *sensor, sensor_type_t typ,
+                         struct sensor_cfg *cfg)
+{
+    int rc;
+
+    (void)sensor;
+    if ((cfg == NULL) || (0 != (typ & (SENSOR_TYPE_VOLTAGE | 
SENSOR_TYPE_CURRENT)))) {
+        rc = SYS_EINVAL;
+    } else {
+        cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT;
+        rc = SYS_EOK;
+    }
+    return rc;
+}
+
+#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
+
+static void
+init_node_cb(struct bus_node *bnode, void *arg)
+{
+    ina228_init(&bnode->odev, arg);
+}
+
+int
+ina228_create_sensor_dev(struct ina228_dev *ina228, const char *name,
+                         const struct bus_i2c_node_cfg *i2c_cfg,
+                         const struct ina228_hw_cfg *hw_cfg)
+{
+    int rc;
+    static struct sensor_itf itf;
+
+    rc = sensor_create_i2c_device(&ina228->i2c_node, name, i2c_cfg,
+                                  init_node_cb, (void *)hw_cfg, &itf);
+
+    return rc;
+}
+#endif
diff --git a/hw/drivers/sensors/ina228/src/ina228_shell.c 
b/hw/drivers/sensors/ina228/src/ina228_shell.c
new file mode 100644
index 000000000..578cecd07
--- /dev/null
+++ b/hw/drivers/sensors/ina228/src/ina228_shell.c
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "os/mynewt.h"
+#include "console/console.h"
+#include "shell/shell.h"
+#include "sensor/accel.h"
+#include "ina228/ina228.h"
+#include "parse/parse.h"
+
+#if MYNEWT_VAL(INA228_CLI)
+
+static enum ina228_ct vct;
+static enum ina228_ct sct;
+static enum ina228_avg_mode avg;
+static uint8_t soft_avg = 1;
+
+static int
+ina228_shell_err_too_many_args(char *cmd_name)
+{
+    console_printf("Error: too many arguments for command \"%s\"\n", cmd_name);
+    return EINVAL;
+}
+
+static int
+ina228_shell_err_unknown_arg(char *cmd_name)
+{
+    console_printf("Error: unknown argument \"%s\"\n", cmd_name);
+    return EINVAL;
+}
+
+static int
+ina228_shell_err_invalid_arg(char *cmd_name)
+{
+    console_printf("Error: invalid argument \"%s\"\n", cmd_name);
+    return EINVAL;
+}
+
+static int
+ina228_shell_help(void)
+{
+    console_printf("ina228 cmd [flags...]\n");
+    console_printf("cmd:\n");
+    console_printf("\tr [n_samples]\n");
+    console_printf("\tavg n\n");
+    console_printf("\tsoftavg n\n");
+    console_printf("\tsct n\n");
+    console_printf("\tvct n\n");
+
+    return 0;
+}
+
+const static uint16_t avg_mode[8] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
+const static uint16_t ct_mode[8] = { 50, 150, 280, 540, 1052, 2074, 4120 };
+
+static int
+ina228_shell_cmd_avg(int argc, char **argv)
+{
+    int rc = -1;
+    long long val = 0;
+
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 0, 7, &rc);
+    }
+    if (rc == 0) {
+        avg = (uint8_t)val;
+    }
+    console_printf("avg of %u\n", avg_mode[avg]);
+
+    return 0;
+}
+
+static int
+ina228_shell_cmd_soft_avg(int argc, char **argv)
+{
+    int rc = -1;
+    long long val = 0;
+
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 1, 100, &rc);
+    }
+    if (rc == 0) {
+        soft_avg = (uint8_t)val;
+    }
+    console_printf("softavg of %u\n", soft_avg);
+
+    return 0;
+}
+
+static int
+ina228_shell_cmd_sct(int argc, char **argv)
+{
+    int rc = -1;
+    long long val = 0;
+
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 0, 7, &rc);
+    }
+    if (rc == 0) {
+        sct = (uint8_t)val;
+    }
+    console_printf("sct = %u us\n", ct_mode[sct]);
+
+    return 0;
+}
+
+static int
+ina228_shell_cmd_vct(int argc, char **argv)
+{
+    int rc = -1;
+    long long val = 0;
+
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 0, 7, &rc);
+    }
+    if (rc == 0) {
+        vct = (uint8_t)val;
+    }
+    console_printf("vct = %u us\n", ct_mode[vct]);
+
+    return 0;
+}
+
+static int
+ina228_shell_cmd_read(int argc, char **argv)
+{
+    uint16_t samples = 1;
+    uint16_t val;
+    int32_t current;
+    int32_t current_acc = 0;
+    uint32_t vbus;
+    uint32_t vbus_acc = 0;
+    int32_t temp;
+    int i = 0;
+    int rc = 0;
+    struct ina228_dev *ina228;
+    struct ina228_cfg ina228_cfg = {
+        .vshct = sct,
+        .vbusct = vct,
+        .avg_mode = avg,
+    };
+
+    if (argc > 3) {
+        return ina228_shell_err_too_many_args(argv[1]);
+    }
+
+    /* Check if more than one sample requested */
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 1, UINT16_MAX, &rc);
+        if (rc) {
+            return ina228_shell_err_invalid_arg(argv[2]);
+        }
+        samples = val;
+    }
+
+    ina228 = (struct ina228_dev 
*)os_dev_open(MYNEWT_VAL(INA228_SHELL_DEV_NAME),
+                                              100, &ina228_cfg);
+    if (ina228 == NULL) {
+        console_printf("Can't open %s device\n", 
MYNEWT_VAL(INA228_SHELL_DEV_NAME));
+        return 0;
+    }
+
+    ina228_start_continuous_mode(ina228, INA228_MODE_CONTINUOUS |
+                                 INA228_MODE_SHUNT_VOLTAGE |
+                                 INA228_MODE_BUS_VOLTAGE |
+                                 INA228_MODE_TEMPERATURE);
+
+    while (rc == 0 && samples) {
+
+        rc = ina228_wait_and_read(ina228, &current, &vbus, NULL, &temp);
+        if (rc == SYS_EBUSY) {
+            /* Conversion not ready yet, old interrupt fired, wait again. */
+            continue;
+        }
+
+        if (rc) {
+            console_printf("Read failed: %d\n", rc);
+            break;
+        }
+
+        current_acc += current;
+        vbus_acc += vbus;
+        if (++i == soft_avg) {
+            i = 0;
+            --samples;
+            current = current_acc / soft_avg;
+            vbus = vbus_acc / soft_avg;
+            console_printf("current: %5d [uA], vbus = %5u [mV], t = %d.%d C\n",
+                           (int)current, (unsigned int)vbus / 1000,
+                           (int)temp / 1000, abs(temp) / 100 % 10);
+            current_acc = 0;
+            vbus_acc = 0;
+        }
+    }
+    (void)ina228_stop_continuous_mode(ina228);
+
+    os_dev_close((struct os_dev *)ina228);
+
+    return 0;
+}
+
+static int
+ina228_shell_cmd(int argc, char **argv)
+{
+    if (argc == 1) {
+        return ina228_shell_help();
+    }
+
+    /* Read command (get a new data sample) */
+    if (argc > 1 && strcmp(argv[1], "r") == 0) {
+        return ina228_shell_cmd_read(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "avg") == 0) {
+        return ina228_shell_cmd_avg(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "softavg") == 0) {
+        return ina228_shell_cmd_soft_avg(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "sct") == 0) {
+        return ina228_shell_cmd_sct(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "vct") == 0) {
+        return ina228_shell_cmd_vct(argc, argv);
+    }
+
+    return ina228_shell_err_unknown_arg(argv[1]);
+}
+
+MAKE_SHELL_CMD(ina228, ina228_shell_cmd, NULL)
+
+#endif
diff --git a/hw/drivers/sensors/ina228/syscfg.yml 
b/hw/drivers/sensors/ina228/syscfg.yml
new file mode 100644
index 000000000..514acf728
--- /dev/null
+++ b/hw/drivers/sensors/ina228/syscfg.yml
@@ -0,0 +1,67 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    INA228_CLI:
+        description: 'Enable shell support for the INA228'
+        value: 0
+    INA228_ITF_LOCK_TMO:
+        description: 'INA228 interface lock timeout in milliseconds'
+        value: 1000
+    INA228_SHELL_DEV_NAME:
+        description: 'INA228 shell device name'
+        value: '"ina228_0"'
+    INA228_I2C_RETRIES:
+        description: >
+            Number of retries to use for failed I2C communication.  A retry is
+            used when the INA228 sends an unexpected NACK.
+        value: 2
+
+    INA228_INT_SUPPORT:
+        description: >
+            INA228 interrupt code enabled.
+        value: 1
+    INA228_DEFAULT_AVERAGING:
+        description: >
+            Default averaging (value from INA228_avg_mode enum).
+        value: INA228_AVG_1
+    INA228_DEFAULT_VBUS_CONVERSION_TIME:
+        description: >
+            Default vbus conversion time (value from INA228_ct enum).
+        value: INA228_CT_1052_US
+    INA228_DEFAULT_VSHUNT_CONVERSION_TIME:
+        description: >
+            Default vshunt conversion time (value from INA228_ct enum).
+        value: INA228_CT_1052_US
+    INA228_DEFAULT_TEMPERATURE_CONVERSION_TIME:
+        description: >
+            Default vshunt conversion time (value from INA228_ct enum).
+        value: INA228_CT_1052_US
+    ### Log settings.
+
+    INA228_LOG_MODULE:
+        description: 'Numeric module ID to use for INA228 log messages.'
+        value: 90
+    INA228_LOG_LVL:
+        description: 'Minimum level for the INA228 log.'
+        value: 1
+syscfg.logs:
+    INA228_LOG:
+        module: MYNEWT_VAL(INA228_LOG_MODULE)
+        level: MYNEWT_VAL(INA228_LOG_LVL)

Reply via email to