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
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)
|
Loading…
Add table
Add a link
Reference in a new issue