You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by vi...@apache.org on 2020/08/06 18:23:30 UTC

[mynewt-core] branch master updated: Add INA219 current sensor driver

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

vipulrahane 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 49667cf  Add INA219 current sensor driver
     new ca50207  Merge pull request #2342 from jipanienko/rama7
49667cf is described below

commit 49667cf1e1181a41548d1395f25e1fc69aebfa97
Author: J. Ipanienko <j....@mexicomail.com>
AuthorDate: Mon Aug 3 13:12:50 2020 -0500

    Add INA219 current sensor driver
---
 hw/drivers/sensors/ina219/include/ina219/ina219.h | 246 +++++++++++
 hw/drivers/sensors/ina219/pkg.yml                 |  40 ++
 hw/drivers/sensors/ina219/src/ina219.c            | 496 ++++++++++++++++++++++
 hw/drivers/sensors/ina219/src/ina219_shell.c      | 252 +++++++++++
 hw/drivers/sensors/ina219/syscfg.yml              |  61 +++
 5 files changed, 1095 insertions(+)

diff --git a/hw/drivers/sensors/ina219/include/ina219/ina219.h b/hw/drivers/sensors/ina219/include/ina219/ina219.h
new file mode 100644
index 0000000..94e131e
--- /dev/null
+++ b/hw/drivers/sensors/ina219/include/ina219/ina219.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2020 Jesus Ipanienko
+ *
+ * Licensed 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 __INA219_H__
+#define __INA219_H__
+
+#include <stdint.h>
+#include <os/mynewt.h>
+#include <stats/stats.h>
+#include <sensor/sensor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define INA219_CONFIGURATION_REG_ADDR       0
+#define INA219_SHUNT_VOLTAGE_REG_ADDR       1
+#define INA219_BUS_VOLTAGE_REG_ADDR         2
+#define INA219_POWER_REG_ADDR               3
+#define INA219_CURRENT_REG_ADDR             4
+#define INA219_CALIBRATION_REG_ADDR         5
+
+#define INA219_CONF_REG_RST_Msk             0x8000
+#define INA219_CONF_REG_RST_Pos             15
+#define INA219_CONF_REG_BRNG_Msk            0x2000
+#define INA219_CONF_REG_BRNG_Pos            13
+#define INA219_CONF_REG_PG_Msk              0x1800
+#define INA219_CONF_REG_PG_Pos              11
+#define INA219_CONF_REG_BADC_Msk            0x0780
+#define INA219_CONF_REG_BADC_Pos            7
+#define INA219_CONF_REG_SADC_Msk            0x0078
+#define INA219_CONF_REG_SADC_Pos            3
+#define INA219_CONF_REG_MODE_Msk            0x0007
+#define INA219_CONF_REG_MODE_Pos            0
+
+/* 10 uV */
+#define INA219_SHUNT_VOLTAGE_LSB            10
+/* 4 mV at 16V full scale */
+#define INA219_BUS_VOLTAGE_16V_LSB          4
+/* 8 mV at 32V full scale */
+#define INA219_BUS_VOLTAGE_32V_LSB          8
+
+/* INA219 ADC mode/sample */
+enum ina219_adc_mode {
+    INA219_ADC_9_BITS       = 0,
+    INA219_ADC_10_BITS      = 1,
+    INA219_ADC_11_BITS      = 2,
+    INA219_ADC_12_BITS      = 3,
+    INA219_ADC_2_SAMPLES    = 9,
+    INA219_ADC_4_SAMPLES    = 10,
+    INA219_ADC_8_SAMPLES    = 11,
+    INA219_ADC_16_SAMPLES   = 12,
+    INA219_ADC_32_SAMPLES   = 13,
+    INA219_ADC_64_SAMPLES   = 14,
+    INA219_ADC_128_SAMPLES  = 15,
+};
+
+/* INA219 operating modes */
+enum ina219_oper_mode {
+    INA219_OPER_POWER_DOWN                  = 0,
+    INA219_OPER_SHUNT_VOLTAGE_TRIGGERED     = 1,
+    INA219_OPER_BUS_VOLTAGE_TRIGGERED       = 2,
+    INA219_OPER_SHUNT_AND_BUS_TRIGGERED     = 3,
+    INA219_OPER_ADC_OFF                     = 4,
+    INA219_OPER_CONTINUOUS_MODE             = 4,
+    INA219_OPER_SHUNT_VOLTAGE_CONTINUOUS    = 5,
+    INA219_OPER_BUS_VOLTAGE_CONTINUOUS      = 6,
+    INA219_OPER_SHUNT_AND_BUS_CONTINUOUS    = 7,
+};
+
+enum ina219_vbus_full_scale {
+    INA219_VBUS_FULL_SCALE_16V              = 0,
+    INA219_VBUS_FULL_SCALE_32V              = 1,
+};
+
+struct ina219_hw_cfg {
+    struct sensor_itf itf;
+    /** Shunt resistance in mOhm. */
+    uint32_t shunt_resistance;
+};
+
+struct ina219_cfg {
+    /** VBus adc mode */
+    enum ina219_adc_mode vbus_mode;
+    /** Shunt adc mode */
+    enum ina219_adc_mode vshunt_mode;
+    /** Operation mode mode */
+    enum ina219_oper_mode oper_mode;
+    /** Full scale for VBUS */
+    enum ina219_vbus_full_scale vbus_fs;
+
+    uint32_t sensors_mask;
+};
+
+/* Define the stats section and records */
+STATS_SECT_START(ina219_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 ina219_dev {
+    struct os_dev dev;
+    struct sensor sensor;
+    /* Hardware wiring config, (shunt, i2c) */
+    struct ina219_hw_cfg hw_cfg;
+
+    uint16_t config_reg;
+    STATS_SECT_DECL(ina219_stat_section) stats;
+
+    uint32_t conversion_time;
+};
+
+/**
+ * Initialize the ina219. this function is normally called by sysinit.
+ *
+ * @param dev  Pointer to the ina219_dev device descriptor
+ * @param arg  Pointer to struct ina219_hw_cfg.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_init(struct os_dev *dev, void *arg);
+
+/**
+ * Reset sensor.
+ *
+ * @param ina219    Pointer to the ina219 device.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_reset(struct ina219_dev *ina219);
+
+/**
+ * Sets sensor device configuration.
+ *
+ * @param  ina219      Pointer to the ina219 struct
+ * @param  ina219_cfg  Pointer to the ina219 configuration
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_config(struct ina219_dev *ina219, const struct ina219_cfg *cfg);
+
+/**
+ * Reads shunt voltage.
+ *
+ * @param ina219    Pointer to ina219 device.
+ * @param voltage   Pointer to shunt voltage in uV.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_read_shunt_voltage(struct ina219_dev *ina219, int32_t *voltage);
+
+/**
+ * Reads bus voltage.
+ *
+ * @param ina219    Pointer to ina219 device.
+ * @param voltage   Pointer to output voltage in mV.
+ * @param conversion_ready Pointer to output value set to true if this is new value.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_read_bus_voltage(struct ina219_dev *ina219, uint16_t *voltage, bool *conversion_ready);
+
+/**
+ * Reads shunt voltage and returns current.
+ *
+ * @param ina219    Pointer to ina219 device.
+ * @param voltage   Pointer to output current in uA.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_read_current(struct ina219_dev *ina219, int32_t *current);
+
+/**
+ * Read current values of current and voltage.
+ *
+ * @param ina219    Pointer to ina219 device.
+ * @param current   Pointer to output current in uA (can be NULL).
+ * @param vbus      Pointer to bus voltage in mV (can be NULL).
+ * @param vshunt    Pointer to shunt voltage in uV (can be NULL).
+ *
+ * @return 0 on success, non-zero on failure. SYS_EAGAIN conversion not ready, read again.
+ */
+int ina219_read_values(struct ina219_dev *ina219, int32_t *current, uint16_t *vbus, int32_t *vshunt);
+
+/**
+ * Reads current, shunt and bus voltage.
+ *
+ * @param ina219    Pointer to ina219 device.
+ * @param current   Pointer to output current in uA (can be NULL).
+ * @param vbus      Pointer to bus voltage in mV (can be NULL).
+ * @param vshunt    Pointer to shunt voltage in uV (can be NULL).
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_one_shot_read(struct ina219_dev *ina219, int32_t *current, uint16_t *vbus, int32_t *vshunt);
+
+/**
+ * Start continuous read mode.
+ *
+ * @param ina219    Pointer to ina219 device.
+ * @param mode      shunt and/or vbus mode.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_start_continuous_mode(struct ina219_dev *ina219, enum ina219_oper_mode mode);
+
+/**
+ * Stop continuous read mode.
+ *
+ * @param ina219    Pointer to ina219 device.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_stop_continuous_mode(struct ina219_dev *ina219);
+
+#if MYNEWT_VAL(INA219_CLI)
+/**
+ * Initialize the INA219 shell extensions.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int ina219_shell_init(void);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INA219_H__ */
+
diff --git a/hw/drivers/sensors/ina219/pkg.yml b/hw/drivers/sensors/ina219/pkg.yml
new file mode 100644
index 0000000..17c62c1
--- /dev/null
+++ b/hw/drivers/sensors/ina219/pkg.yml
@@ -0,0 +1,40 @@
+#
+# Copyright 2020 Jesus Ipanienko
+#
+# Licensed 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/ina219
+pkg.description: Driver for the INA219 Current/Power monitor
+pkg.author: "Jesus Ipanienko"
+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.deps.INA219_CLI:
+    - "@apache-mynewt-core/util/parse"
+
+pkg.init.INA219_CLI:
+    ina219_shell_init: 'MYNEWT_VAL(INA219_CLI_SYSINIT_STAGE)'
diff --git a/hw/drivers/sensors/ina219/src/ina219.c b/hw/drivers/sensors/ina219/src/ina219.c
new file mode 100644
index 0000000..5b9ad53
--- /dev/null
+++ b/hw/drivers/sensors/ina219/src/ina219.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright 2020 Jesus Ipanienko
+ *
+ * Licensed 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 <modlog/modlog.h>
+#include <stats/stats.h>
+
+#include <ina219/ina219.h>
+
+/* Define stat names for querying */
+STATS_NAME_START(ina219_stat_section)
+    STATS_NAME(ina219_stat_section, read_count)
+    STATS_NAME(ina219_stat_section, write_count)
+    STATS_NAME(ina219_stat_section, read_errors)
+    STATS_NAME(ina219_stat_section, write_errors)
+STATS_NAME_END(ina219_stat_section)
+
+/* Exports for the sensor API */
+static int ina219_sensor_read(struct sensor *sensor, sensor_type_t typ,
+                              sensor_data_func_t data_func, void *data_func_arg, uint32_t timeout);
+
+static int ina219_sensor_get_config(struct sensor *sensor, sensor_type_t typ,
+                                    struct sensor_cfg *cfg);
+
+static const struct sensor_driver g_ina219_sensor_driver = {
+    .sd_read = ina219_sensor_read,
+    .sd_get_config = ina219_sensor_get_config,
+};
+
+/* Conversion times depending on ADC settings */
+static const uint32_t conversion_times[16] = {
+    93, 163, 304, 586, 93, 163, 304, 586, 586, 1060, 2130, 4260, 8510, 17020, 34050, 68100
+};
+
+/* Calculate total conversion time in us. */
+static uint32_t
+ina219_conversion_time(uint32_t config_reg)
+{
+    uint32_t time;
+
+    if (config_reg & INA219_OPER_SHUNT_VOLTAGE_TRIGGERED) {
+        time = conversion_times[(config_reg & INA219_CONF_REG_SADC_Msk) >> INA219_CONF_REG_SADC_Pos];
+    } else {
+        time = 0;
+    }
+    if (config_reg & INA219_OPER_BUS_VOLTAGE_TRIGGERED) {
+        time += conversion_times[(config_reg & INA219_CONF_REG_BADC_Msk) >> INA219_CONF_REG_BADC_Pos];
+    }
+    return time;
+}
+
+int
+ina219_write_reg(struct ina219_dev *ina219, uint8_t reg, uint16_t reg_val)
+{
+    int rc;
+    uint8_t payload[3] = {reg, reg_val >> 8, (uint8_t)reg_val };
+
+    struct hal_i2c_master_data data_struct = {
+        .address = ina219->sensor.s_itf.si_addr,
+        .len = 3,
+        .buffer = payload,
+    };
+
+    rc = sensor_itf_lock(&ina219->sensor.s_itf, MYNEWT_VAL(INA219_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+    STATS_INC(ina219->stats, write_count);
+    rc = i2cn_master_write(ina219->sensor.s_itf.si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
+                           MYNEWT_VAL(INA219_I2C_RETRIES));
+    if (rc) {
+        STATS_INC(ina219->stats, write_errors);
+        INA219_LOG_ERROR("INA219 write I2C failed\n");
+    }
+
+    sensor_itf_unlock(&ina219->sensor.s_itf);
+
+    return rc;
+}
+
+int
+ina219_read_reg(struct ina219_dev *ina219, uint8_t reg, uint16_t *reg_val)
+{
+    int rc;
+    uint8_t payload[2] = { reg };
+
+    struct hal_i2c_master_data data_struct = {
+        .address = ina219->sensor.s_itf.si_addr,
+        .len = 1,
+        .buffer = payload
+    };
+
+    rc = sensor_itf_lock(&ina219->sensor.s_itf, MYNEWT_VAL(INA219_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+
+    STATS_INC(ina219->stats, read_count);
+    rc = i2cn_master_write(ina219->sensor.s_itf.si_num, &data_struct, OS_TICKS_PER_SEC / 10,
+                           1, MYNEWT_VAL(INA219_I2C_RETRIES));
+    if (rc) {
+        STATS_INC(ina219->stats, read_errors);
+        INA219_LOG_ERROR("INA219 write I2C failed\n");
+        goto exit;
+    }
+
+    data_struct.len = 2;
+    rc = i2cn_master_read(ina219->sensor.s_itf.si_num, &data_struct, OS_TICKS_PER_SEC / 10,
+                          1, MYNEWT_VAL(INA219_I2C_RETRIES));
+    if (rc) {
+        STATS_INC(ina219->stats, read_errors);
+        INA219_LOG_ERROR("INA219 read I2C failed\n");
+    }
+
+    *reg_val = (payload[0] << 8) | payload[1];
+
+exit:
+    sensor_itf_unlock(&ina219->sensor.s_itf);
+
+    return rc;
+}
+
+int
+ina219_read_power_reg(struct ina219_dev *ina219, uint16_t *power_reg)
+{
+    return ina219_read_reg(ina219, INA219_POWER_REG_ADDR, power_reg);
+}
+
+int
+ina219_read_configuration_reg(struct ina219_dev *ina219, uint16_t *config_reg)
+{
+    return ina219_read_reg(ina219, INA219_CONFIGURATION_REG_ADDR, config_reg);
+}
+
+int
+ina219_read_bus_voltage_reg(struct ina219_dev *ina219, uint16_t *vbus_reg)
+{
+    return ina219_read_reg(ina219, INA219_BUS_VOLTAGE_REG_ADDR, vbus_reg);
+}
+
+int
+ina219_read_shunt_voltage_reg(struct ina219_dev *ina219, uint16_t *vshunt_reg)
+{
+    return ina219_read_reg(ina219, INA219_SHUNT_VOLTAGE_REG_ADDR, vshunt_reg);
+}
+
+int
+ina219_reset(struct ina219_dev *ina219)
+{
+    int rc;
+
+    rc = ina219_write_reg(ina219, INA219_CONFIGURATION_REG_ADDR, INA219_CONF_REG_RST_Msk);
+    if (rc == SYS_EOK) {
+        ina219_read_configuration_reg(ina219, &ina219->config_reg);
+    }
+    return rc;
+}
+
+int
+ina219_config(struct ina219_dev *ina219, const struct ina219_cfg *cfg)
+{
+    int rc;
+
+    /* Start in power down mode. */
+    ina219->config_reg =
+        (INA219_CONF_REG_SADC_Msk & ((uint16_t) cfg->vshunt_mode << INA219_CONF_REG_SADC_Pos)) |
+        (INA219_CONF_REG_BADC_Msk & ((uint16_t) cfg->vbus_mode << INA219_CONF_REG_BADC_Pos)) |
+        (INA219_CONF_REG_PG_Msk & ((uint16_t) cfg->vbus_fs << INA219_CONF_REG_PG_Pos));
+    rc = ina219_write_reg(ina219, INA219_CONFIGURATION_REG_ADDR, ina219->config_reg);
+
+    if (rc == 0) {
+        /* Value 2 is used to enable new value notification, and is not used for anything else. */
+        ina219_write_reg(ina219, INA219_CALIBRATION_REG_ADDR, 2);
+    }
+
+    return rc;
+}
+
+int
+ina219_read_bus_voltage(struct ina219_dev *ina219, uint16_t *voltage, bool *conversion_ready)
+{
+    uint16_t v;
+    int rc;
+
+    rc = ina219_read_bus_voltage_reg(ina219, &v);
+    if (rc == SYS_EOK) {
+        if (ina219->config_reg & INA219_CONF_REG_PG_Msk) {
+            *voltage = (v >> 3) * INA219_BUS_VOLTAGE_32V_LSB;
+        } else {
+            *voltage = (v >> 3) * INA219_BUS_VOLTAGE_16V_LSB;
+        }
+        if (conversion_ready) {
+            *conversion_ready = (v & 2) != 0;
+        }
+    }
+    return rc;
+}
+
+int
+ina219_read_shunt_voltage(struct ina219_dev *ina219, int32_t *voltage)
+{
+    int16_t v;
+    int rc;
+
+    rc = ina219_read_shunt_voltage_reg(ina219, (uint16_t *)&v);
+    if (rc == SYS_EOK) {
+        *voltage = v * (int32_t)INA219_SHUNT_VOLTAGE_LSB;
+    }
+    return rc;
+}
+
+int
+ina219_read_values(struct ina219_dev *ina219, int32_t *current, uint16_t *vbus, int32_t *vshunt)
+{
+    int32_t vshunt_uv;
+    uint16_t locla_vbus;
+    uint16_t power_reg;
+    bool conversion_ready;
+    int rc = SYS_EOK;
+
+    if (vbus == NULL) {
+        vbus = &locla_vbus;
+    }
+    rc = ina219_read_bus_voltage(ina219, vbus, &conversion_ready);
+    if (rc != SYS_EOK) {
+        goto exit;
+    }
+    /* Conversion not ready yet. Don't read shunt voltage. */
+    if (!conversion_ready) {
+        rc = SYS_EAGAIN;
+        goto exit;
+    }
+    if (current != NULL || vshunt != NULL) {
+        rc = ina219_read_shunt_voltage(ina219, &vshunt_uv);
+        if (rc != SYS_EOK) {
+            goto exit;
+        }
+        if (vshunt != NULL) {
+            *vshunt = vshunt_uv;
+        }
+        if (current != NULL) {
+            *current = 1000 * vshunt_uv / (int32_t)ina219->hw_cfg.shunt_resistance;
+        }
+    }
+    (void)ina219_read_power_reg(ina219, &power_reg);
+    (void)power_reg;
+
+exit:
+    return rc;
+}
+
+static int
+ina219_wait_and_read(struct ina219_dev *ina219, int32_t *current, uint16_t *vbus, int32_t *vshunt)
+{
+    int rc = SYS_EAGAIN;
+    int i;
+
+    os_cputime_delay_usecs(ina219->conversion_time);
+
+    for (i = 0; rc == SYS_EAGAIN && i < 10 /* Sanity */; i++) {
+        rc = ina219_read_values(ina219, current, vbus, vshunt);
+        if (rc == SYS_EAGAIN) {
+            /* Wait some more. */
+            os_cputime_delay_usecs(100);
+        }
+    }
+
+    return rc;
+}
+
+int
+ina219_one_shot_read(struct ina219_dev *ina219, int32_t *current, uint16_t *vbus, int32_t *vshunt)
+{
+    int rc;
+
+    ina219->config_reg &= ~INA219_CONF_REG_MODE_Msk;
+    if (current != NULL || vshunt != NULL) {
+        ina219->config_reg |= (uint32_t)INA219_OPER_SHUNT_VOLTAGE_TRIGGERED;
+    }
+    if (vbus != NULL) {
+        ina219->config_reg |= (uint32_t)INA219_OPER_BUS_VOLTAGE_TRIGGERED;
+    }
+    ina219->conversion_time = ina219_conversion_time(ina219->config_reg);
+
+    /* Start one shot conversion. */
+    rc = ina219_write_reg(ina219, INA219_CONFIGURATION_REG_ADDR, ina219->config_reg);
+    if (rc != SYS_EOK) {
+        goto exit;
+    }
+
+    rc = ina219_wait_and_read(ina219, current, vbus, vshunt);
+exit:
+    return rc;
+}
+
+int
+ina219_power_down(struct ina219_dev *ina219)
+{
+    int rc = SYS_EOK;
+    uint32_t config_reg;
+
+    config_reg = ina219->config_reg & ~INA219_CONF_REG_MODE_Msk;
+
+    if (config_reg != ina219->config_reg) {
+        ina219->config_reg = config_reg;
+        rc = ina219_write_reg(ina219, INA219_CONFIGURATION_REG_ADDR, ina219->config_reg);
+    }
+
+    return rc;
+}
+
+int
+ina219_start_continuous_mode(struct ina219_dev *ina219, enum ina219_oper_mode mode)
+{
+    int rc;
+
+    assert((uint8_t)mode & (uint8_t)INA219_OPER_CONTINUOUS_MODE);
+    ina219->config_reg &= ~INA219_CONF_REG_MODE_Msk;
+    ina219->config_reg |= mode;
+
+    rc = ina219_write_reg(ina219, INA219_CONFIGURATION_REG_ADDR, ina219->config_reg);
+
+    ina219->conversion_time = ina219_conversion_time(ina219->config_reg);
+
+    return rc;
+}
+
+int
+ina219_stop_continuous_mode(struct ina219_dev *ina219)
+{
+    return ina219_power_down(ina219);
+}
+
+static int
+ina219_open(struct os_dev *dev, uint32_t wait, void *arg)
+{
+    /* Default values after power on. */
+    const struct ina219_cfg default_cfg = {
+        .vbus_fs = MYNEWT_VAL(INA219_DEFAULT_VBUS_FULL_SACLE),
+        .vbus_mode = MYNEWT_VAL(INA219_DEFAULT_VBUS_ADC_MODE),
+        .vshunt_mode = MYNEWT_VAL(INA219_DEFAULT_VSHUNT_ADC_MODE)
+    };
+    int rc;
+    struct ina219_dev *ina219 = (struct ina219_dev *)dev;
+
+    (void)wait;
+
+    /* Reset sensor. */
+    rc = ina219_reset(ina219);
+
+    if (rc) {
+        goto exit;
+    }
+
+    if (arg == NULL) {
+        rc = ina219_config(ina219, &default_cfg);
+    } else {
+        rc = ina219_config(ina219, (const struct ina219_cfg *)arg);
+    }
+exit:
+    return rc;
+}
+
+static int
+ina219_close(struct os_dev *dev)
+{
+    struct ina219_dev *ina219 = (struct ina219_dev *)dev;
+
+    return ina219_power_down(ina219);
+}
+
+int
+ina219_init(struct os_dev *dev, void *arg)
+{
+    struct ina219_dev *ina219;
+    struct sensor *sensor;
+    int rc;
+
+    if (!arg || !dev) {
+        rc = SYS_ENODEV;
+        goto exit;
+    }
+
+    ina219 = (struct ina219_dev *)dev;
+    ina219->hw_cfg = *(struct ina219_hw_cfg *)arg;
+
+    sensor = &ina219->sensor;
+
+    /* Initialise the stats entry. */
+    rc = stats_init(
+        STATS_HDR(ina219->stats),
+        STATS_SIZE_INIT_PARMS(ina219->stats, STATS_SIZE_32),
+        STATS_NAME_INIT_PARMS(ina219_stat_section));
+    SYSINIT_PANIC_ASSERT(rc == SYS_EOK);
+    /* Register the entry with the stats registry */
+    rc = stats_register(ina219->dev.od_name, STATS_HDR(ina219->stats));
+    SYSINIT_PANIC_ASSERT(rc == SYS_EOK);
+
+    rc = sensor_init(sensor, dev);
+    if (rc != SYS_EOK) {
+        goto exit;
+    }
+
+    (void)sensor_set_interface(sensor, &ina219->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_ina219_sensor_driver);
+
+    OS_DEV_SETHANDLERS(dev, ina219_open, ina219_close);
+exit:
+    return rc;
+}
+
+static int
+ina219_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 ina219_dev *ina219;
+    int32_t current;
+    uint16_t vbus;
+    int32_t *pcurrent;
+    uint16_t *pvbus;
+    union {
+        struct sensor_voltage_data svd;
+        struct sensor_current_data scd;
+    } 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;
+        INA219_LOG_ERROR("ina219_sensor_read unsupported sensor type\n");
+        goto exit;
+    }
+
+    pcurrent = (type & SENSOR_TYPE_CURRENT) ? &current : NULL;
+    pvbus = (type & SENSOR_TYPE_VOLTAGE) ? &vbus : NULL;
+
+    ina219 = (struct ina219_dev *)SENSOR_GET_DEVICE(sensor);
+
+    rc = ina219_one_shot_read(ina219, pcurrent, pvbus, NULL);
+    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 mV convert to V. */
+            databuf.svd.svd_voltage = vbus / 1000.0f;
+            databuf.svd.svd_voltage_is_valid = 1;
+            data_func(sensor, data_func_arg, &databuf.scd, SENSOR_TYPE_VOLTAGE);
+        }
+    }
+exit:
+    return rc;
+}
+
+static int
+ina219_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;
+}
diff --git a/hw/drivers/sensors/ina219/src/ina219_shell.c b/hw/drivers/sensors/ina219/src/ina219_shell.c
new file mode 100644
index 0000000..89732b0
--- /dev/null
+++ b/hw/drivers/sensors/ina219/src/ina219_shell.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2020 Jesus Ipanienko
+ *
+ * Licensed 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 "ina219/ina219.h"
+#include "parse/parse.h"
+
+#if MYNEWT_VAL(INA219_CLI)
+
+static enum ina219_adc_mode vmod = MYNEWT_VAL(INA219_DEFAULT_VBUS_ADC_MODE);
+static enum ina219_adc_mode smod = MYNEWT_VAL(INA219_DEFAULT_VSHUNT_ADC_MODE);
+static enum ina219_vbus_full_scale fs = MYNEWT_VAL(INA219_DEFAULT_VBUS_FULL_SACLE);
+static uint8_t soft_avg = 1;
+
+static int ina219_shell_cmd(int argc, char **argv);
+
+static struct shell_cmd ina219_shell_cmd_struct = {
+    .sc_cmd = "ina219",
+    .sc_cmd_func = ina219_shell_cmd
+};
+
+static int
+ina219_shell_err_too_many_args(char *cmd_name)
+{
+    console_printf("Error: too many arguments for command \"%s\"\n",
+                   cmd_name);
+    return EINVAL;
+}
+
+static int
+ina219_shell_err_unknown_arg(char *cmd_name)
+{
+    console_printf("Error: unknown argument \"%s\"\n",
+                   cmd_name);
+    return EINVAL;
+}
+
+static int
+ina219_shell_err_invalid_arg(char *cmd_name)
+{
+    console_printf("Error: invalid argument \"%s\"\n",
+                   cmd_name);
+    return EINVAL;
+}
+
+static int
+ina219_shell_help(void)
+{
+    console_printf("%s cmd [flags...]\n", ina219_shell_cmd_struct.sc_cmd);
+    console_printf("cmd:\n");
+    console_printf("\tr [n_samples]\n");
+    console_printf("\tfs [0..1]\n");
+    console_printf("\tsoftavg [1..100]\n");
+    console_printf("\tsmod [0..15]\n");
+    console_printf("\tvmod [0..15]\n");
+
+    return 0;
+}
+
+static int
+ina219_shell_cmd_fs(int argc, char **argv)
+{
+    int rc = -1;
+    long long val;
+
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 0, 1, &rc);
+    }
+    if (rc == 0) {
+        fs = (uint8_t)val;
+    }
+    console_printf("fs %d V\n", fs ? 32 : 16);
+
+    return 0;
+}
+
+static int
+ina219_shell_cmd_soft_avg(int argc, char **argv)
+{
+    int rc = -1;
+    long long val;
+
+    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
+ina219_shell_cmd_smod(int argc, char **argv)
+{
+    int rc = -1;
+    long long val;
+
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 0, 7, &rc);
+    }
+    if (rc == 0) {
+        smod = (uint8_t)val;
+    }
+    console_printf("smod = %u\n", smod);
+
+    return 0;
+}
+
+static int
+ina219_shell_cmd_vmod(int argc, char **argv)
+{
+    int rc = -1;
+    long long val;
+
+    if (argc == 3) {
+        val = parse_ll_bounds(argv[2], 0, 15, &rc);
+    }
+    if (rc == 0) {
+        vmod = (uint8_t)val;
+    }
+    console_printf("vmod = %u\n", vmod);
+
+    return 0;
+}
+
+static int
+ina219_shell_cmd_read(int argc, char **argv)
+{
+    uint16_t samples = 1;
+    uint16_t val;
+    int32_t current;
+    int32_t current_acc = 0;
+    uint16_t vbus;
+    uint32_t vbus_acc = 0;
+    int i = 0;
+    int rc = 0;
+    struct ina219_dev *ina219;
+    struct ina219_cfg ina219_cfg = {
+        .vshunt_mode = smod,
+        .vbus_mode = vmod,
+        .vbus_fs = fs,
+    };
+
+    if (argc > 3) {
+        return ina219_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 ina219_shell_err_invalid_arg(argv[2]);
+        }
+        samples = val;
+    }
+
+    ina219 = (struct ina219_dev *)os_dev_open(MYNEWT_VAL(INA219_SHELL_DEV_NAME), 100, &ina219_cfg);
+    if (ina219 == NULL) {
+        console_printf("Can't open %s device\n", MYNEWT_VAL(INA219_SHELL_DEV_NAME));
+        return 0;
+    }
+
+    ina219_start_continuous_mode(ina219, INA219_OPER_SHUNT_AND_BUS_CONTINUOUS);
+
+    while (rc == 0 && samples) {
+
+        rc = ina219_read_values(ina219, &current, &vbus, NULL);
+        if (rc == SYS_EAGAIN) {
+            /* Conversion not ready yet, old interrupt fired, wait again. */
+            rc = 0;
+            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]\n", (int)current, (unsigned int)vbus);
+            current_acc = 0;
+            vbus_acc = 0;
+        }
+    }
+    (void)ina219_stop_continuous_mode(ina219);
+
+    os_dev_close((struct os_dev *)ina219);
+
+    return 0;
+}
+
+static int
+ina219_shell_cmd(int argc, char **argv)
+{
+    if (argc == 1) {
+        return ina219_shell_help();
+    }
+
+    /* Read command (get a new data sample) */
+    if (argc > 1 && strcmp(argv[1], "r") == 0) {
+        return ina219_shell_cmd_read(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "fs") == 0) {
+        return ina219_shell_cmd_fs(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "softavg") == 0) {
+        return ina219_shell_cmd_soft_avg(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "smod") == 0) {
+        return ina219_shell_cmd_smod(argc, argv);
+    } else if (argc > 1 && strcmp(argv[1], "vmod") == 0) {
+        return ina219_shell_cmd_vmod(argc, argv);
+    }
+
+    return ina219_shell_err_unknown_arg(argv[1]);
+}
+
+int
+ina219_shell_init(void)
+{
+    int rc;
+
+    rc = shell_cmd_register(&ina219_shell_cmd_struct);
+    SYSINIT_PANIC_ASSERT(rc == 0);
+
+    return rc;
+}
+
+#endif
diff --git a/hw/drivers/sensors/ina219/syscfg.yml b/hw/drivers/sensors/ina219/syscfg.yml
new file mode 100644
index 0000000..0f5bd9d
--- /dev/null
+++ b/hw/drivers/sensors/ina219/syscfg.yml
@@ -0,0 +1,61 @@
+#
+# Copyright 2020 Jesus Ipanienko
+#
+# Licensed 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:
+    INA219_CLI:
+        description: 'Enable shell support for the INA219'
+        value: 0
+    INA219_ITF_LOCK_TMO:
+        description: 'INA219 interface lock timeout in milliseconds'
+        value: 1000
+    INA219_SHELL_DEV_NAME:
+        description: 'INA219 shell device name'
+        value: '"ina219_0"'
+    INA219_I2C_RETRIES:
+        description: >
+            Number of retries to use for failed I2C communication.  A retry is
+            used when the INA219 sends an unexpected NACK.
+        value: 2
+
+    INA219_DEFAULT_VBUS_FULL_SACLE:
+        description: >
+            Full scale for VBUS.
+        value: INA219_VBUS_FULL_SCALE_32V
+    INA219_DEFAULT_VBUS_ADC_MODE:
+        description: >
+            Default ADC mode for VBUS (value from ina219_adc_mode enum).
+        value: INA219_ADC_12_BITS
+    INA219_DEFAULT_VSHUNT_ADC_MODE:
+        description: >
+            Default ADC mode for shunt (value from ina219_adc_mode enum).
+        value: INA219_ADC_12_BITS
+
+
+    ### Log settings.
+
+    INA219_LOG_MODULE:
+        description: 'Numeric module ID to use for INA219 log messages.'
+        value: 86
+    INA219_LOG_LVL:
+        description: 'Minimum level for the INA219 log.'
+        value: 1
+    INA219_CLI_SYSINIT_STAGE:
+        description: 'Sysinit stage for the INA219 shell'
+        value: 500
+syscfg.logs:
+    INA219_LOG:
+        module: MYNEWT_VAL(INA219_LOG_MODULE)
+        level: MYNEWT_VAL(INA219_LOG_LVL)