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), ®, 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 = ®
+ };
+
+ 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) ? ¤t : 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, ¤t, &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)