drivers: sensor: INA219: added support
This adds support for the TI INA219 Zero-Drift, Bidirectional Current/Power Monitor with I2C Interface Signed-off-by: Leonard Pollak <leonardp@tr-host.de>
This commit is contained in:
parent
35b1a6f337
commit
d6eb80ac27
7 changed files with 560 additions and 0 deletions
|
@ -37,6 +37,7 @@ add_subdirectory_ifdef(CONFIG_IIS2DLPC iis2dlpc)
|
|||
add_subdirectory_ifdef(CONFIG_IIS2ICLX iis2iclx)
|
||||
add_subdirectory_ifdef(CONFIG_IIS2MDC iis2mdc)
|
||||
add_subdirectory_ifdef(CONFIG_IIS3DHHC iis3dhhc)
|
||||
add_subdirectory_ifdef(CONFIG_INA219 ina219)
|
||||
add_subdirectory_ifdef(CONFIG_INA23X ina23x)
|
||||
add_subdirectory_ifdef(CONFIG_ISL29035 isl29035)
|
||||
add_subdirectory_ifdef(CONFIG_ISM330DHCX ism330dhcx)
|
||||
|
|
|
@ -116,6 +116,8 @@ source "drivers/sensor/ina23x/Kconfig"
|
|||
|
||||
source "drivers/sensor/isl29035/Kconfig"
|
||||
|
||||
source "drivers/sensor/ina219/Kconfig"
|
||||
|
||||
source "drivers/sensor/ism330dhcx/Kconfig"
|
||||
|
||||
source "drivers/sensor/lis2dh/Kconfig"
|
||||
|
|
5
drivers/sensor/ina219/CMakeLists.txt
Normal file
5
drivers/sensor/ina219/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(ina219.c)
|
10
drivers/sensor/ina219/Kconfig
Normal file
10
drivers/sensor/ina219/Kconfig
Normal file
|
@ -0,0 +1,10 @@
|
|||
# INA219 Bidirectional Current/Power Monitor
|
||||
|
||||
# Copyright (c) 2021 Leonard Pollak
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config INA219
|
||||
bool "INA219 Current/Power Monitor"
|
||||
depends on I2C
|
||||
help
|
||||
Enable driver for INA219 Bidirectional Current/Power Monitor.
|
311
drivers/sensor/ina219/ina219.c
Normal file
311
drivers/sensor/ina219/ina219.c
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Leonard Pollak
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ti_ina219
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <kernel.h>
|
||||
#include <drivers/sensor.h>
|
||||
#include <logging/log.h>
|
||||
#include <sys/byteorder.h>
|
||||
|
||||
#include "ina219.h"
|
||||
|
||||
LOG_MODULE_REGISTER(INA219, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int ina219_reg_read(const struct device *dev,
|
||||
uint8_t reg_addr,
|
||||
uint16_t *reg_data)
|
||||
{
|
||||
const struct ina219_config *cfg = dev->config;
|
||||
uint8_t rx_buf[2];
|
||||
int rc;
|
||||
|
||||
rc = i2c_write_read_dt(&cfg->bus,
|
||||
®_addr, sizeof(reg_addr),
|
||||
rx_buf, sizeof(rx_buf));
|
||||
|
||||
*reg_data = sys_get_be16(rx_buf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ina219_reg_write(const struct device *dev,
|
||||
uint8_t addr,
|
||||
uint16_t reg_data)
|
||||
{
|
||||
const struct ina219_config *cfg = dev->config;
|
||||
uint8_t tx_buf[3];
|
||||
|
||||
tx_buf[0] = addr;
|
||||
sys_put_be16(reg_data, &tx_buf[1]);
|
||||
|
||||
return i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
|
||||
}
|
||||
|
||||
static int ina219_reg_field_update(const struct device *dev,
|
||||
uint8_t addr,
|
||||
uint16_t mask,
|
||||
uint16_t field)
|
||||
{
|
||||
uint16_t reg_data;
|
||||
int rc;
|
||||
|
||||
rc = ina219_reg_read(dev, addr, ®_data);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
reg_data = (reg_data & ~mask) | field;
|
||||
|
||||
return ina219_reg_write(dev, addr, reg_data);
|
||||
}
|
||||
|
||||
static int ina219_set_msr_delay(const struct device *dev)
|
||||
{
|
||||
const struct ina219_config *cfg = dev->config;
|
||||
struct ina219_data *data = dev->data;
|
||||
|
||||
data->msr_delay = ina219_conv_delay(cfg->badc) +
|
||||
ina219_conv_delay(cfg->sadc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ina219_set_config(const struct device *dev)
|
||||
{
|
||||
const struct ina219_config *cfg = dev->config;
|
||||
uint16_t reg_data;
|
||||
|
||||
reg_data = (cfg->brng & INA219_BRNG_MASK) << INA219_BRNG_SHIFT |
|
||||
(cfg->pg & INA219_PG_MASK) << INA219_PG_SHIFT |
|
||||
(cfg->badc & INA219_ADC_MASK) << INA219_BADC_SHIFT |
|
||||
(cfg->sadc & INA219_ADC_MASK) << INA219_SADC_SHIFT |
|
||||
(cfg->mode & INA219_MODE_NORMAL);
|
||||
|
||||
return ina219_reg_write(dev, INA219_REG_CONF, reg_data);
|
||||
}
|
||||
|
||||
static int ina219_set_calib(const struct device *dev)
|
||||
{
|
||||
const struct ina219_config *cfg = dev->config;
|
||||
uint16_t cal;
|
||||
|
||||
cal = INA219_SCALING_FACTOR / ((cfg->r_shunt) * (cfg->current_lsb));
|
||||
|
||||
return ina219_reg_write(dev, INA219_REG_CALIB, cal);
|
||||
}
|
||||
|
||||
static int ina219_sample_fetch(const struct device *dev,
|
||||
enum sensor_channel chan)
|
||||
{
|
||||
struct ina219_data *data = dev->data;
|
||||
uint16_t status;
|
||||
uint16_t tmp;
|
||||
int rc;
|
||||
|
||||
if (chan != SENSOR_CHAN_ALL &&
|
||||
chan != SENSOR_CHAN_VOLTAGE &&
|
||||
chan != SENSOR_CHAN_POWER &&
|
||||
chan != SENSOR_CHAN_CURRENT) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Trigger measurement and wait for completion */
|
||||
rc = ina219_reg_field_update(dev,
|
||||
INA219_REG_CONF,
|
||||
INA219_MODE_MASK,
|
||||
INA219_MODE_NORMAL);
|
||||
if (rc) {
|
||||
LOG_ERR("Failed to start measurement.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
k_sleep(K_USEC(data->msr_delay));
|
||||
|
||||
rc = ina219_reg_read(dev, INA219_REG_V_BUS, &status);
|
||||
if (rc) {
|
||||
LOG_ERR("Failed to read device status.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
while (!(INA219_CNVR_RDY(status))) {
|
||||
rc = ina219_reg_read(dev, INA219_REG_V_BUS, &status);
|
||||
if (rc) {
|
||||
LOG_ERR("Failed to read device status.");
|
||||
return rc;
|
||||
}
|
||||
k_sleep(K_USEC(INA219_WAIT_MSR_RETRY));
|
||||
}
|
||||
|
||||
/* Check for overflow */
|
||||
if (INA219_OVF_STATUS(status)) {
|
||||
LOG_WRN("Power and/or Current calculations are out of range.");
|
||||
}
|
||||
|
||||
if (chan == SENSOR_CHAN_ALL ||
|
||||
chan == SENSOR_CHAN_VOLTAGE) {
|
||||
|
||||
rc = ina219_reg_read(dev, INA219_REG_V_BUS, &tmp);
|
||||
if (rc) {
|
||||
LOG_ERR("Error reading bus voltage.");
|
||||
return rc;
|
||||
}
|
||||
data->v_bus = INA219_VBUS_GET(tmp);
|
||||
}
|
||||
|
||||
if (chan == SENSOR_CHAN_ALL ||
|
||||
chan == SENSOR_CHAN_POWER) {
|
||||
|
||||
rc = ina219_reg_read(dev, INA219_REG_POWER, &tmp);
|
||||
if (rc) {
|
||||
LOG_ERR("Error reading power register.");
|
||||
return rc;
|
||||
}
|
||||
data->power = tmp;
|
||||
}
|
||||
|
||||
if (chan == SENSOR_CHAN_ALL ||
|
||||
chan == SENSOR_CHAN_CURRENT) {
|
||||
|
||||
rc = ina219_reg_read(dev, INA219_REG_CURRENT, &tmp);
|
||||
if (rc) {
|
||||
LOG_ERR("Error reading current register.");
|
||||
return rc;
|
||||
}
|
||||
data->current = tmp;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ina219_channel_get(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
const struct ina219_config *cfg = dev->config;
|
||||
struct ina219_data *data = dev->data;
|
||||
double tmp;
|
||||
int8_t sign = 1;
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_VOLTAGE:
|
||||
tmp = data->v_bus * INA219_V_BUS_MUL;
|
||||
sensor_value_from_double(val, tmp);
|
||||
break;
|
||||
case SENSOR_CHAN_POWER:
|
||||
tmp = data->power * cfg->current_lsb * INA219_POWER_MUL * INA219_SI_MUL;
|
||||
sensor_value_from_double(val, tmp);
|
||||
break;
|
||||
case SENSOR_CHAN_CURRENT:
|
||||
if (INA219_SIGN_BIT(data->current)) {
|
||||
data->current = ~data->current + 1;
|
||||
sign = -1;
|
||||
}
|
||||
tmp = sign * data->current * cfg->current_lsb * INA219_SI_MUL;
|
||||
sensor_value_from_double(val, tmp);
|
||||
break;
|
||||
default:
|
||||
LOG_DBG("Channel not supported by device!");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
static int ina219_pm_ctrl(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
return ina219_init(dev);
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
reg_val = INA219_MODE_SLEEP;
|
||||
break;
|
||||
case PM_DEVICE_ACTION_TURN_OFF:
|
||||
reg_val = INA219_MODE_OFF;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return ina219_reg_field_update(dev,
|
||||
INA219_REG_CONF,
|
||||
INA219_MODE_MASK,
|
||||
reg_val);
|
||||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
static int ina219_init(const struct device *dev)
|
||||
{
|
||||
const struct ina219_config *cfg = dev->config;
|
||||
int rc;
|
||||
|
||||
if (!device_is_ready(cfg->bus.bus)) {
|
||||
LOG_ERR("Device not ready.");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = ina219_reg_write(dev, INA219_REG_CONF, INA219_RST);
|
||||
if (rc) {
|
||||
LOG_ERR("Could not reset device.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ina219_set_config(dev);
|
||||
if (rc) {
|
||||
LOG_ERR("Could not set configuration data.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ina219_set_calib(dev);
|
||||
if (rc) {
|
||||
LOG_DBG("Could not set calibration data.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set measurement delay */
|
||||
rc = ina219_set_msr_delay(dev);
|
||||
if (rc) {
|
||||
LOG_ERR("Could not get measurement delay.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
k_sleep(K_USEC(INA219_WAIT_STARTUP));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api ina219_api = {
|
||||
.sample_fetch = ina219_sample_fetch,
|
||||
.channel_get = ina219_channel_get,
|
||||
};
|
||||
|
||||
|
||||
#define INA219_INIT(n) \
|
||||
static struct ina219_data ina219_data_##n; \
|
||||
\
|
||||
static const struct ina219_config ina219_config_##n = { \
|
||||
.bus = I2C_DT_SPEC_INST_GET(n), \
|
||||
.current_lsb = DT_INST_PROP(n, lsb_microamp), \
|
||||
.r_shunt = DT_INST_PROP(n, shunt_milliohm), \
|
||||
.brng = DT_INST_PROP(n, brng), \
|
||||
.pg = DT_INST_PROP(n, pg), \
|
||||
.badc = DT_INST_PROP(n, badc), \
|
||||
.sadc = DT_INST_PROP(n, sadc), \
|
||||
.mode = INA219_MODE_NORMAL \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, \
|
||||
ina219_init, \
|
||||
ina219_pm_ctrl,\
|
||||
&ina219_data_##n, \
|
||||
&ina219_config_##n, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&ina219_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(INA219_INIT)
|
100
drivers/sensor/ina219/ina219.h
Normal file
100
drivers/sensor/ina219/ina219.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Leonard Pollak
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_INA219_INA219_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_INA219_INA219_H_
|
||||
|
||||
/* Device register addresses */
|
||||
#define INA219_REG_CONF 0x00
|
||||
#define INA219_REG_V_SHUNT 0x01
|
||||
#define INA219_REG_V_BUS 0x02
|
||||
#define INA219_REG_POWER 0x03
|
||||
#define INA219_REG_CURRENT 0x04
|
||||
#define INA219_REG_CALIB 0x05
|
||||
|
||||
/* Config register shifts and masks */
|
||||
#define INA219_RST BIT(15)
|
||||
#define INA219_BRNG_MASK 0x1
|
||||
#define INA219_BRNG_SHIFT 13
|
||||
#define INA219_PG_MASK 0x3
|
||||
#define INA219_PG_SHIFT 11
|
||||
#define INA219_ADC_MASK 0xF
|
||||
#define INA219_BADC_SHIFT 7
|
||||
#define INA219_SADC_SHIFT 3
|
||||
#define INA219_MODE_MASK 0x7
|
||||
|
||||
/* Bus voltage register */
|
||||
#define INA219_VBUS_GET(x) ((x) >> 3) & 0x3FFF
|
||||
#define INA219_CNVR_RDY(x) ((x) >> 1) & 0x1
|
||||
#define INA219_OVF_STATUS(x) x & 0x1
|
||||
|
||||
/* Mode fields */
|
||||
#define INA219_MODE_NORMAL 0x3 /* shunt and bus, triggered */
|
||||
#define INA219_MODE_SLEEP 0x4 /* ADC off */
|
||||
#define INA219_MODE_OFF 0x0 /* Power off */
|
||||
|
||||
/* Others */
|
||||
#define INA219_SIGN_BIT(x) ((x) >> 15) & 0x1
|
||||
#define INA219_V_BUS_MUL 0.004f
|
||||
#define INA219_SI_MUL 0.00001f
|
||||
#define INA219_POWER_MUL 20
|
||||
#define INA219_WAIT_STARTUP 40
|
||||
#define INA219_WAIT_MSR_RETRY 100
|
||||
#define INA219_SCALING_FACTOR 4096000
|
||||
|
||||
struct ina219_config {
|
||||
struct i2c_dt_spec bus;
|
||||
uint16_t current_lsb;
|
||||
uint16_t r_shunt;
|
||||
uint8_t brng;
|
||||
uint8_t pg;
|
||||
uint8_t badc;
|
||||
uint8_t sadc;
|
||||
uint8_t mode;
|
||||
};
|
||||
|
||||
struct ina219_data {
|
||||
uint16_t config;
|
||||
uint16_t v_bus;
|
||||
uint16_t power;
|
||||
uint16_t current;
|
||||
uint16_t calib;
|
||||
uint32_t msr_delay;
|
||||
};
|
||||
|
||||
static int ina219_init(const struct device *dev);
|
||||
|
||||
static inline int ina219_conv_delay(uint8_t delay_idx)
|
||||
{
|
||||
switch (delay_idx) {
|
||||
case 0:
|
||||
return 84;
|
||||
case 1:
|
||||
return 148;
|
||||
case 2:
|
||||
return 276;
|
||||
case 3:
|
||||
return 532;
|
||||
case 9:
|
||||
return 1060;
|
||||
case 10:
|
||||
return 2013;
|
||||
case 11:
|
||||
return 4260;
|
||||
case 12:
|
||||
return 8510;
|
||||
case 13:
|
||||
return 17020;
|
||||
case 14:
|
||||
return 34050;
|
||||
case 15:
|
||||
return 68100;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_INA219_INA219_H_ */
|
131
dts/bindings/sensor/ti,ina219.yaml
Normal file
131
dts/bindings/sensor/ti,ina219.yaml
Normal file
|
@ -0,0 +1,131 @@
|
|||
# Copyright (c) 2021 Leonard Pollak
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Texas Instruments Bidirectional Current/Power Sensor
|
||||
|
||||
compatible: "ti,ina219"
|
||||
|
||||
include: i2c-device.yaml
|
||||
|
||||
properties:
|
||||
lsb-microamp:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Current LSB in microAmpere
|
||||
Current LSB = max expected current [A] / 2^15
|
||||
example: 100 -> ~3A
|
||||
shunt-milliohm:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Value of the shunt resistor in milliOhm
|
||||
brng:
|
||||
type: int
|
||||
required: false
|
||||
default: 1
|
||||
description: |
|
||||
Bus Voltage Range
|
||||
0 = 16 V FSR
|
||||
1 = 32 V FSR
|
||||
|
||||
The default of 32V is the power-on reset value of the device.
|
||||
|
||||
Should the expected bus voltage be below 16V set this to 0.
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
pg:
|
||||
type: int
|
||||
required: false
|
||||
default: 3
|
||||
description: |
|
||||
Programmable Gain
|
||||
0 = 1 -> ±40 mV
|
||||
1 = /2 -> ±80 mV
|
||||
2 = /4 -> ±160 mV
|
||||
3 = /8 -> ±320 mV
|
||||
|
||||
The default of ±320 mV is the power-on reset value of the device.
|
||||
|
||||
In case the expected voltage drop across the shunt resistor is lower
|
||||
one can adjust this to get more accurate readings.
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
badc:
|
||||
type: int
|
||||
required: false
|
||||
default: 3
|
||||
description: |
|
||||
Bus ADC configuration
|
||||
0 = 9 bit -> 84 µs
|
||||
1 = 10 bit -> 148 µs
|
||||
2 = 11 bit -> 276 µs
|
||||
3 = 12 bit -> 532 µs
|
||||
9 = 12 bit - 2 sample averaging -> 1.06 ms
|
||||
10 = 12 bit - 4 sample averaging -> 2.13 ms
|
||||
11 = 12 bit - 8 sample averaging -> 4.26 ms
|
||||
12 = 12 bit - 16 sample averaging -> 8.51 ms
|
||||
13 = 12 bit - 32 sample averaging -> 17.02 ms
|
||||
14 = 12 bit - 64 sample averaging -> 34.05 ms
|
||||
15 = 12 bit - 128 sample averaging -> 68.10 ms
|
||||
|
||||
The default of 12 bit is the power-on reset value of the device.
|
||||
|
||||
Lowering the resolution of the ADC gives less accurate readings but
|
||||
cuts down on conversion times.
|
||||
|
||||
Averaging over multiple samples gives more stable readings but adds
|
||||
to overall conversion time.
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 9
|
||||
- 10
|
||||
- 11
|
||||
- 12
|
||||
- 13
|
||||
- 14
|
||||
- 15
|
||||
sadc:
|
||||
type: int
|
||||
required: false
|
||||
default: 3
|
||||
description: |
|
||||
Shunt ADC configuration
|
||||
0 = 9 bit -> 84 µs
|
||||
1 = 10 bit -> 148 µs
|
||||
2 = 11 bit -> 276 µs
|
||||
3 = 12 bit -> 532 µs
|
||||
9 = 12 bit - 2 sample averaging -> 1.06 ms
|
||||
10 = 12 bit - 4 sample averaging -> 2.13 ms
|
||||
11 = 12 bit - 8 sample averaging -> 4.26 ms
|
||||
12 = 12 bit - 16 sample averaging -> 8.51 ms
|
||||
13 = 12 bit - 32 sample averaging -> 17.02 ms
|
||||
14 = 12 bit - 64 sample averaging -> 34.05 ms
|
||||
15 = 12 bit - 128 sample averaging -> 68.10 ms
|
||||
|
||||
The default of 12 bit is the power-on reset value of the device.
|
||||
|
||||
Lowering the resolution of the ADC gives less accurate readings but
|
||||
cuts down on conversion times.
|
||||
|
||||
Averaging over multiple samples gives more stable readings but adds
|
||||
to overall conversion time.
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 9
|
||||
- 10
|
||||
- 11
|
||||
- 12
|
||||
- 13
|
||||
- 14
|
||||
- 15
|
Loading…
Add table
Add a link
Reference in a new issue