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) ? ¤t : 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, ¤t, &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)