Browse Source

ina219: shunt resistance and expected current

address #2516, implement through ratio setting
pull/2525/head
Maxim Prokhorov 2 years ago
parent
commit
15f6446479
3 changed files with 154 additions and 74 deletions
  1. +8
    -0
      code/espurna/config/sensors.h
  2. +2
    -0
      code/espurna/sensor.cpp
  3. +144
    -74
      code/espurna/sensors/INA219Sensor.h

+ 8
- 0
code/espurna/config/sensors.h View File

@ -1402,6 +1402,10 @@
#define INA219_SHUNT_MODE BIT_MODE_12
#endif
#ifndef INA219_SHUNT_RESISTANCE
#define INA219_SHUNT_RESISTANCE 0.1 // Ohms
#endif
#ifndef INA219_BUS_MODE
#define INA219_BUS_MODE BIT_MODE_12
#endif
@ -1414,6 +1418,10 @@
#define INA219_GAIN PG_320
#endif
#ifndef INA219_MAX_EXPECTED_CURRENT
#define INA219_MAX_EXPECTED_CURRENT 2.0 // A
#endif
// -----------------------------------------------------------------------------
// ADC
// -----------------------------------------------------------------------------


+ 2
- 0
code/espurna/sensor.cpp View File

@ -2904,6 +2904,8 @@ void _sensorLoad() {
sensor->setBusMode(INA219Sensor::INA219_BUS_MODE);
sensor->setBusRange(INA219Sensor::INA219_BUS_RANGE);
sensor->setGain(INA219Sensor::INA219_GAIN);
sensor->setShuntResistance(INA219_SHUNT_RESISTANCE);
sensor->setMaxExpectedCurrent(INA219_MAX_EXPECTED_CURRENT);
_sensors.push_back(sensor);
}
#endif


+ 144
- 74
code/espurna/sensors/INA219Sensor.h View File

@ -47,6 +47,10 @@ static constexpr uint8_t INA219_CAL_REG { 0x05 }; // Calibration Register
static constexpr uint16_t INA219_RST { 0x8000 };
class INA219Sensor : public I2CSensor<BaseEmonSensor> {
protected:
using BaseEmonSensor::_current_ratio;
using BaseEmonSensor::_power_active_ratio;
private:
// CONF register bits [0...2] are operating mode
static constexpr uint16_t OperatingModeMask { 0b111 };
@ -114,8 +118,11 @@ private:
// ref. 8.5.1, calibration is calculated using special LSB value:
// - calibration = truncated(0.04096 / (current LSB * shunt R))
// (0.04096 is a fixed value, set by the device)
// - current LSB = (maximum expected current) / 32768
// - current LSB = is a value between
// (maximum expected current) / 32767
// (maximum expected current) / 4096
// we specify it in A, calculation is in mA
// determines how much A per bit can be encoded, limited to u16 size
// - power LSB = 20 * current LSB
// we specify it in W, calculation is in mW
// - shunt voltage LSB = 0.01 (mV) (device converts this)
@ -136,58 +143,56 @@ private:
// - power 0x1766 (5990); LSB 20 mW, reading means 119.8 W
// (matching with current multiplied by bus reading)
struct Parameters {
bool ready;
uint16_t gain;
uint16_t calibration;
float current_lsb;
float power_lsb;
struct Calibration {
double current_lsb;
double power_lsb;
uint16_t value;
};
static Parameters fromGain(Gain gain) {
Parameters out;
out.ready = false;
switch (gain) {
case PG_40:
out = {
.ready = true,
.gain = gain,
.calibration = 0x5000,
.current_lsb = 0.02,
.power_lsb = 0.4,
};
break;
case PG_80:
out = {
.ready = true,
.gain = gain,
.calibration = 0x2800,
.current_lsb = 0.04,
.power_lsb = 0.8,
};
break;
case PG_160:
out = {
.ready = true,
.gain = gain,
.calibration = 0x2000,
.current_lsb = 0.05,
.power_lsb = 1.0,
};
break;
case PG_320:
out = {
.ready = true,
.gain = gain,
.calibration = 0x1000,
.current_lsb = 0.1,
.power_lsb = 2.0,
};
break;
}
struct Lsb {
double min;
double max;
};
static constexpr double maxShuntVoltage(Gain gain) {
return (gain == PG_40) ? 0.04 :
(gain == PG_80) ? 0.08 :
(gain == PG_160) ? 0.016 :
(gain == PG_320) ? 0.032 :
0.0;
}
static constexpr double maxPossibleCurrent(Gain gain, double shunt_resistance) {
return (maxShuntVoltage(gain) > 0.0)
? (maxShuntVoltage(gain) / shunt_resistance)
: 0.0;
}
static constexpr Lsb currentLsbRange(double max_expected_current) {
return {
.min = (max_expected_current / 32767.0), // amperes per bit, 15bit resolution
.max = (max_expected_current / 4096.0), // same but 12bit
};
}
return out;
static constexpr double currentLsb(double value, double max_expected_current) {
return std::clamp(value,
currentLsbRange(max_expected_current).min,
currentLsbRange(max_expected_current).max);
}
static constexpr double powerLsb(double current_lsb) {
return 20.0 * current_lsb;
}
static
#if __cplusplus > 201703L
constexpr
#endif
uint16_t calibration(double current_lsb, double shunt_resistance) {
constexpr double Min = std::numeric_limits<uint16_t>::min();
constexpr double Max = std::numeric_limits<uint16_t>::max();
return std::clamp(std::trunc(0.04096 / (current_lsb * shunt_resistance)), Min, Max);
}
struct I2CPort {
@ -339,7 +344,6 @@ private:
bool _energy_ready = false;
I2CPort _port;
Parameters _params;
OperatingMode _operating_mode;
AdcMode _bus_mode;
@ -347,6 +351,12 @@ private:
BusRange _bus_range;
Gain _gain;
Calibration _calibration;
double _shunt_resistance;
double _max_expected_current;
Lsb _current_lsb_range;
double _voltage = 0.0;
double _current = 0.0;
double _power = 0.0;
@ -354,27 +364,33 @@ private:
public:
void setOperatingMode(OperatingMode mode) {
_operating_mode = mode;
_dirty = true;
}
void setBusMode(AdcMode mode) {
_bus_mode = mode;
_dirty = true;
}
void setShuntMode(AdcMode mode) {
_shunt_mode = mode;
_dirty = true;
}
void setBusRange(BusRange range) {
_bus_range = range;
_dirty = true;
}
void setGain(Gain gain) {
_params.gain = gain;
_dirty = true;
_gain = gain;
}
void setShuntResistance(double value) {
_shunt_resistance = value;
}
void setMaxExpectedCurrent(double current) {
_max_expected_current = current;
_current_lsb_range = currentLsbRange(current);
_current_ratio = currentLsb(_current_lsb_range.min, current);
_power_active_ratio = powerLsb(_current_ratio);
}
static constexpr Magnitude Magnitudes[] {
@ -401,13 +417,9 @@ public:
}
void begin() override {
if (!_dirty) {
return;
}
_params = fromGain(_gain);
if (!_params.ready) {
_error = SENSOR_ERROR_CONFIG;
auto max_possible_current = maxPossibleCurrent(_gain, _shunt_resistance);
if (max_possible_current < _max_expected_current) {
_error = SENSOR_ERROR_OVERFLOW;
return;
}
@ -431,8 +443,25 @@ public:
.bus_range = _bus_range,
});
_port.calibration(_params.calibration);
_calibration = Calibration{
.current_lsb = _current_ratio,
.power_lsb = _power_active_ratio,
.value = calibration(_current_ratio, _shunt_resistance),
};
#if SENSOR_DEBUG
DEBUG_MSG(PSTR("[INA219] Current LSB %s\n"),
String(_calibration.current_lsb, 6).c_str());
DEBUG_MSG(PSTR("[INA219] Power LSB %s\n"),
String(_calibration.power_lsb, 6).c_str());
uint8_t buf[2];
std::memcpy(&buf, _calibration.value, sizeof(buf));
DEBUG_MSG(PSTR("[INA219] Calibration 0x%s\n"),
hexEncode(std::begin(buf), std::end(buf)).c_str());
#endif
_port.calibration(_calibration.value);
espurna::time::blockingDelay(
espurna::duration::Milliseconds(100));
@ -440,7 +469,6 @@ public:
_error = SENSOR_ERROR_OK;
_ready = true;
_dirty = false;
}
String address(unsigned char index) const override {
@ -490,8 +518,8 @@ public:
_voltage = voltage.value;
_current = _port.current() * _params.current_lsb;
_power = _port.power() * _params.power_lsb;
_current = _port.current() * _calibration.current_lsb;
_power = _port.power() * _calibration.power_lsb;
if (_energy_ready) {
const auto now = TimeSource::now();
@ -504,16 +532,58 @@ public:
}
double value(unsigned char index) override {
if (index == 0) {
return _voltage;
} else if (index == 1) {
return _current;
} else if (index == 2) {
return _power;
if (index < std::size(Magnitudes)) {
switch (Magnitudes[index].type) {
case MAGNITUDE_VOLTAGE:
return _voltage;
case MAGNITUDE_CURRENT:
return _current;
case MAGNITUDE_POWER_ACTIVE:
return _power;
}
}
return 0;
}
void setRatio(unsigned char index, double value) override {
if (index < std::size(Magnitudes)) {
switch (Magnitudes[index].type) {
case MAGNITUDE_CURRENT:
_current_ratio = value;
break;
case MAGNITUDE_POWER_ACTIVE:
_power_active_ratio = value;
break;
}
}
}
double getRatio(unsigned char index) const override {
if (index < std::size(Magnitudes)) {
switch (Magnitudes[index].type) {
case MAGNITUDE_CURRENT:
return _current_ratio;
case MAGNITUDE_POWER_ACTIVE:
return _power_active_ratio;
}
}
return defaultRatio(index);
}
double defaultRatio(unsigned char index) const override {
if (index < std::size(Magnitudes)) {
switch (Magnitudes[index].type) {
case MAGNITUDE_CURRENT:
return _current_lsb_range.min;
case MAGNITUDE_POWER_ACTIVE:
return powerLsb(_current_lsb_range.min);
}
}
return BaseEmonSensor::defaultRatio(index);
}
};
#if __cplusplus < 201703L


Loading…
Cancel
Save