mirror of
https://github.com/alexandrebobkov/ESP-Nodes.git
synced 2025-08-08 05:07:07 +00:00
306 lines
8.2 KiB
C
306 lines
8.2 KiB
C
/*
|
|
* Copyright (c) 2019 Ruslan V. Uss <unclerus@gmail.com>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the names of itscontributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* @file ina219.c
|
|
*
|
|
* ESP-IDF driver for INA219/INA220 Zerø-Drift, Bidirectional
|
|
* Current/Power Monitor
|
|
*
|
|
* Copyright (c) 2019 Ruslan V. Uss <unclerus@gmail.com>
|
|
*
|
|
* BSD Licensed as described in the file LICENSE
|
|
*/
|
|
#include <esp_log.h>
|
|
#include <math.h>
|
|
#include <esp_idf_lib_helpers.h>
|
|
#include "ina219.h"
|
|
|
|
#define I2C_FREQ_HZ 1000000 // Max 1 MHz for esp-idf, but supports up to 2.56 MHz
|
|
|
|
static const char *TAG = "ina219";
|
|
|
|
#define REG_CONFIG 0
|
|
#define REG_SHUNT_U 1
|
|
#define REG_BUS_U 2
|
|
#define REG_POWER 3
|
|
#define REG_CURRENT 4
|
|
#define REG_CALIBRATION 5
|
|
|
|
#define BIT_RST 15
|
|
#define BIT_BRNG 13
|
|
#define BIT_PG0 11
|
|
#define BIT_BADC0 7
|
|
#define BIT_SADC0 3
|
|
#define BIT_MODE 0
|
|
|
|
#define MASK_PG (3 << BIT_PG0)
|
|
#define MASK_BADC (0xf << BIT_BADC0)
|
|
#define MASK_SADC (0xf << BIT_SADC0)
|
|
#define MASK_MODE (7 << BIT_MODE)
|
|
#define MASK_BRNG (1 << BIT_BRNG)
|
|
|
|
#define DEF_CONFIG 0x399f
|
|
|
|
#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0)
|
|
#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0)
|
|
|
|
static const float u_shunt_max[] = {
|
|
[INA219_GAIN_1] = 0.04,
|
|
[INA219_GAIN_0_5] = 0.08,
|
|
[INA219_GAIN_0_25] = 0.16,
|
|
[INA219_GAIN_0_125] = 0.32,
|
|
};
|
|
|
|
static esp_err_t read_reg_16(ina219_t *dev, uint8_t reg, uint16_t *val)
|
|
{
|
|
CHECK_ARG(val);
|
|
|
|
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
|
|
I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_read_reg(&dev->i2c_dev, reg, val, 2));
|
|
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
|
|
|
|
*val = (*val >> 8) | (*val << 8);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t write_reg_16(ina219_t *dev, uint8_t reg, uint16_t val)
|
|
{
|
|
uint16_t v = (val >> 8) | (val << 8);
|
|
|
|
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
|
|
I2C_DEV_CHECK(&dev->i2c_dev, i2c_dev_write_reg(&dev->i2c_dev, reg, &v, 2));
|
|
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t read_conf_bits(ina219_t *dev, uint16_t mask, uint8_t bit, uint16_t *res)
|
|
{
|
|
uint16_t raw;
|
|
CHECK(read_reg_16(dev, REG_CONFIG, &raw));
|
|
|
|
*res = (raw & mask) >> bit;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
esp_err_t ina219_init_desc(ina219_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio)
|
|
{
|
|
CHECK_ARG(dev);
|
|
|
|
if (addr < INA219_ADDR_GND_GND || addr > INA219_ADDR_SCL_SCL)
|
|
{
|
|
ESP_LOGE(TAG, "Invalid I2C address");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
dev->i2c_dev.port = port;
|
|
dev->i2c_dev.addr = addr;
|
|
dev->i2c_dev.cfg.sda_io_num = sda_gpio;
|
|
dev->i2c_dev.cfg.scl_io_num = scl_gpio;
|
|
#if HELPER_TARGET_IS_ESP32
|
|
dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ;
|
|
#endif
|
|
|
|
return i2c_dev_create_mutex(&dev->i2c_dev);
|
|
}
|
|
|
|
esp_err_t ina219_free_desc(ina219_t *dev)
|
|
{
|
|
CHECK_ARG(dev);
|
|
|
|
return i2c_dev_delete_mutex(&dev->i2c_dev);
|
|
}
|
|
|
|
esp_err_t ina219_init(ina219_t *dev)
|
|
{
|
|
CHECK_ARG(dev);
|
|
|
|
CHECK(read_reg_16(dev, REG_CONFIG, &dev->config));
|
|
|
|
ESP_LOGD(TAG, "Initialize, config: 0x%04x", dev->config);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t ina219_reset(ina219_t *dev)
|
|
{
|
|
CHECK_ARG(dev);
|
|
CHECK(write_reg_16(dev, REG_CONFIG, 1 << BIT_RST));
|
|
|
|
dev->config = DEF_CONFIG;
|
|
|
|
ESP_LOGD(TAG, "Device reset");
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t ina219_configure(ina219_t *dev, ina219_bus_voltage_range_t u_range,
|
|
ina219_gain_t gain, ina219_resolution_t u_res,
|
|
ina219_resolution_t i_res, ina219_mode_t mode)
|
|
{
|
|
CHECK_ARG(dev);
|
|
CHECK_ARG(u_range <= INA219_BUS_RANGE_32V);
|
|
CHECK_ARG(gain <= INA219_GAIN_0_125);
|
|
CHECK_ARG(u_res <= INA219_RES_12BIT_128S);
|
|
CHECK_ARG(i_res <= INA219_RES_12BIT_128S);
|
|
CHECK_ARG(mode <= INA219_MODE_CONT_SHUNT_BUS);
|
|
|
|
dev->config = (u_range << BIT_BRNG) |
|
|
(gain << BIT_PG0) |
|
|
(u_res << BIT_BADC0) |
|
|
(i_res << BIT_SADC0) |
|
|
(mode << BIT_MODE);
|
|
|
|
ESP_LOGD(TAG, "Config: 0x%04x", dev->config);
|
|
|
|
return write_reg_16(dev, REG_CONFIG, dev->config);
|
|
}
|
|
|
|
esp_err_t ina219_get_bus_voltage_range(ina219_t *dev, ina219_bus_voltage_range_t *range)
|
|
{
|
|
CHECK_ARG(dev && range);
|
|
*range = 0;
|
|
return read_conf_bits(dev, MASK_BRNG, BIT_BRNG, (uint16_t *)range);
|
|
}
|
|
|
|
esp_err_t ina219_get_gain(ina219_t *dev, ina219_gain_t *gain)
|
|
{
|
|
CHECK_ARG(dev && gain);
|
|
*gain = 0;
|
|
return read_conf_bits(dev, MASK_PG, BIT_PG0, (uint16_t *)gain);
|
|
}
|
|
|
|
esp_err_t ina219_get_bus_voltage_resolution(ina219_t *dev, ina219_resolution_t *res)
|
|
{
|
|
CHECK_ARG(dev && res);
|
|
*res = 0;
|
|
return read_conf_bits(dev, MASK_BADC, BIT_BADC0, (uint16_t *)res);
|
|
}
|
|
|
|
esp_err_t ina219_get_shunt_voltage_resolution(ina219_t *dev, ina219_resolution_t *res)
|
|
{
|
|
CHECK_ARG(dev && res);
|
|
*res = 0;
|
|
return read_conf_bits(dev, MASK_SADC, BIT_SADC0, (uint16_t *)res);
|
|
}
|
|
|
|
esp_err_t ina219_get_mode(ina219_t *dev, ina219_mode_t *mode)
|
|
{
|
|
CHECK_ARG(dev && mode);
|
|
*mode = 0;
|
|
return read_conf_bits(dev, MASK_MODE, BIT_MODE, (uint16_t *)mode);
|
|
}
|
|
|
|
esp_err_t ina219_calibrate(ina219_t *dev, float r_shunt)
|
|
{
|
|
CHECK_ARG(dev);
|
|
|
|
ina219_gain_t gain;
|
|
CHECK(ina219_get_gain(dev, &gain));
|
|
|
|
dev->i_lsb = (uint16_t)(u_shunt_max[gain] / r_shunt / 32767 * 100000000);
|
|
dev->i_lsb /= 100000000;
|
|
dev->i_lsb /= 0.0001;
|
|
dev->i_lsb = ceil(dev->i_lsb);
|
|
dev->i_lsb *= 0.0001;
|
|
|
|
dev->p_lsb = dev->i_lsb * 20;
|
|
|
|
uint16_t cal = (uint16_t)((0.04096) / (dev->i_lsb * r_shunt));
|
|
|
|
ESP_LOGD(TAG, "Calibration: %.04f Ohm, 0x%04x", r_shunt, cal);
|
|
|
|
return write_reg_16(dev, REG_CALIBRATION, cal);
|
|
}
|
|
|
|
esp_err_t ina219_trigger(ina219_t *dev)
|
|
{
|
|
CHECK_ARG(dev);
|
|
|
|
uint16_t mode = (dev->config & MASK_MODE) >> BIT_MODE;
|
|
if (mode < INA219_MODE_TRIG_SHUNT || mode > INA219_MODE_TRIG_SHUNT_BUS)
|
|
{
|
|
ESP_LOGE(TAG, "Could not trigger conversion in this mode: %d", mode);
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
return write_reg_16(dev, REG_CONFIG, dev->config);
|
|
}
|
|
|
|
esp_err_t ina219_get_bus_voltage(ina219_t *dev, float *voltage)
|
|
{
|
|
CHECK_ARG(dev && voltage);
|
|
|
|
uint16_t raw;
|
|
CHECK(read_reg_16(dev, REG_BUS_U, &raw));
|
|
|
|
*voltage = (raw >> 3) * 0.004;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t ina219_get_shunt_voltage(ina219_t *dev, float *voltage)
|
|
{
|
|
CHECK_ARG(dev && voltage);
|
|
|
|
int16_t raw;
|
|
CHECK(read_reg_16(dev, REG_SHUNT_U, (uint16_t *)&raw));
|
|
|
|
*voltage = raw / 100000.0;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t ina219_get_current(ina219_t *dev, float *current)
|
|
{
|
|
CHECK_ARG(dev && current);
|
|
|
|
int16_t raw;
|
|
CHECK(read_reg_16(dev, REG_CURRENT, (uint16_t *)&raw));
|
|
|
|
*current = raw * dev->i_lsb;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t ina219_get_power(ina219_t *dev, float *power)
|
|
{
|
|
CHECK_ARG(dev && power);
|
|
|
|
int16_t raw;
|
|
CHECK(read_reg_16(dev, REG_POWER, (uint16_t *)&raw));
|
|
|
|
*power = raw * dev->p_lsb;
|
|
|
|
return ESP_OK;
|
|
}
|