Open lorenzo6201 opened 1 year ago
I have run into the same issue. My 50A 75mV shunt did not read any current higher than 27A. I finally realised the issue was the 40.97mV range set on the INA229 and not a problem with my inverter.
Looking at your code it is hard to work out what changes you have made, but you do not seem to have changed the ADCRANGE flag. This is set in the function Configure()
From line ~489
//registers.R_CONFIG = _BV(4); // ADCRANGE = 40.96mV scale
registers.R_CONFIG &= 0xffef ; //Set ADCRANGE to 163.84 mV scale
I have it working now for my 50A 75mV shunt. The attached patch should work for any type of shunt. For 50mV shunts it sets the ADCRANGE to 40.96mV and for higher Voltage shunts it sets the range to 163.84mV.
The only change in the calculations is R_SHUNT_CAL is 4 times larger when the 40.96mV range is selected.file:///home/derek/Desktop/INA229.diff
diff --git a/ESPController/include/CurrentMonitorINA229.h b/ESPController/include/CurrentMonitorINA229.h
index 599f2ef..0557c01 100644
--- a/ESPController/include/CurrentMonitorINA229.h
+++ b/ESPController/include/CurrentMonitorINA229.h
@@ -293,7 +293,7 @@ private:
float power = 0;
float temperature = 0;
- const float full_scale_adc = 40.96;
+ float full_scale_adc = 40.96;
// const float CoulombsToAmpHours = 1.0 / 3600.0;
const float CoulombsToMilliAmpHours = 1.0 / 3.6;
diff --git a/ESPController/src/CurrentMonitorINA229.cpp b/ESPController/src/CurrentMonitorINA229.cpp
index 32c1f9e..0ef51d5 100644
--- a/ESPController/src/CurrentMonitorINA229.cpp
+++ b/ESPController/src/CurrentMonitorINA229.cpp
@@ -36,32 +36,39 @@ void CurrentMonitorINA229::CalculateLSB()
// in above - click COG icon top left.
// https://e2e.ti.com/support/amplifiers-group/amplifiers/f/amplifiers-forum/1034569/ina228-accumulated-energy-and-charge-is-wrong
- // 150A/50mV shunt = full_scale_current= 150.00A / 50.00 * 40.96 = 122.88 AMPS
+ // 150A/50mV shunt = full_scale_current= 150.00A / 50.00 * 40.96 = 122.88 AMPS ADCRANGE=1
// RSHUNT = (50 / 1000) / 150 = 0.00033333333
// CURRENT_LSB = 150/ 524288 = 0.000286102294921875
// R_SHUNT_CAL = 52428800000*0.000286102294921875*0.00033333333 = 4999.999 = 5000
- // 300A/75mV shunt = full_scale_current= 300.00A / 75.00 * 40.96 = 163.84 AMPS
- // RSHUNT = (75 / 1000) / 163.84 = 0.000457763671875
- // CURRENT_LSB = 163.84/ 524288 = 2.648162841796875e-4
- // R_SHUNT_CAL = 52428800000*0.00057220458984375*0.00025 = 7499.9922688 = 7500
+ // 300A/75mV shunt = full_scale_current= 300.00A ADCRANGE=0
+ // RSHUNT = (75 / 1000) / 300 = 0.00025
+ // CURRENT_LSB = 300/ 524288 = 5.72204589844e-4 = 0.000572204589844
+ // R_SHUNT_CAL = 13107.2e6 * 5.72204589844e-4 * 0.00025 = 1875
+
+ // 50A/75mV shunt = full_scale_current=50.00A ADCRANGE=0
+ // RSHUNT = (75 / 1000) / 50 = 0.0015
+ // CURRENT_LSB = 50/ 524288 = 9.53674316406e-05 = 0.0000953674316406
+ // R_SHUNT_CAL = 13107.2e6 * 9.53674316406e-05 * 0.0015 = 1875
// Calculate CURRENT_LSB and R_SHUNT_CAL values
// registers.full_scale_current = ((float)registers.shunt_max_current / (float)registers.shunt_millivolt) * full_scale_adc;
registers.RSHUNT = ((float)registers.shunt_millivolt / 1000.0) / (float)registers.shunt_max_current;
registers.CURRENT_LSB = registers.shunt_max_current / (float)0x80000;
- registers.R_SHUNT_CAL = 4L * (13107200000L * registers.CURRENT_LSB * registers.RSHUNT);
+ registers.R_SHUNT_CAL = (13107200000L * registers.CURRENT_LSB * registers.RSHUNT);
+ if(registers.shunt_millivolt <= 50) registers.R_SHUNT_CAL * 4L;
ESP_LOGI(TAG, "RSHUNT=%.8f, CURRENT_LSB=%.8f, R_SHUNT_CAL=%u", registers.RSHUNT, registers.CURRENT_LSB, registers.R_SHUNT_CAL);
- // Will the full scale current be over the 40.96mV range the ADC can handle?
+ // Will the full scale current be over the range the ADC can handle?
if (((float)registers.shunt_max_current * registers.RSHUNT * 1000.0) > full_scale_adc)
{
float true_max_current = ((float)registers.shunt_max_current / (float)registers.shunt_millivolt) * full_scale_adc;
ESP_LOGW(TAG, "WARNING: True Max Current Measurable %.2f Amps", true_max_current);
registers.CURRENT_LSB = true_max_current / (float)0x80000;
- registers.R_SHUNT_CAL = 4L * (13107200000L * registers.CURRENT_LSB * registers.RSHUNT);
+ registers.R_SHUNT_CAL = (13107200000L * registers.CURRENT_LSB * registers.RSHUNT);
+ if(registers.shunt_millivolt <= 50) registers.R_SHUNT_CAL * 4L;
ESP_LOGI(TAG, "RECALC: RSHUNT=%.8f, CURRENT_LSB=%.8f, R_SHUNT_CAL=%u", registers.RSHUNT, registers.CURRENT_LSB, registers.R_SHUNT_CAL);
}
@@ -456,6 +463,8 @@ bool CurrentMonitorINA229::Configure(uint16_t shuntmv,
registers.tail_current_amps = tailcurrent / 100.0;
registers.charge_efficiency_factor = chargeefficiency / 100.0;
+ full_scale_adc = (registers.shunt_millivolt > 50) ? 163.84 : 40.96; //Select ADC Range
+
CalculateLSB();
if (shuntcal != 0 && registers.R_SHUNT_CAL != shuntcal)
@@ -486,7 +495,7 @@ bool CurrentMonitorINA229::Configure(uint16_t shuntmv,
// ESP_LOGI(TAG, "undercurrentlimit=%f, R_SUVL=%u", ((float)undercurrentlimit / 100.0F), registers.R_SUVL);
- registers.R_CONFIG = _BV(4); // ADCRANGE = 40.96mV scale
+ registers.R_CONFIG =(registers.shunt_millivolt > 50) ? registers.R_CONFIG & 0xffef: _BV(4); // ADCRANGE = 163.84mV or 40.96mV scale
if (TemperatureCompEnabled)
{
diff --git a/ESPController/web_src/default.htm b/ESPController/web_src/default.htm
index de58b13..b31e2aa 100644
--- a/ESPController/web_src/default.htm
+++ b/ESPController/web_src/default.htm
@@ -1395,7 +1395,7 @@
<h2>Basic Settings</h2>
<p id="b3">Ensure the current shunt parameters match the data sheet for your particular shunt resistor.</p>
<p id="b4">
- The DIYBMS current monitor uses a 40.96mV maximum scale, so shunt voltages over this will be scaled down
+ For 50mA internal shunts DIYBMS current monitor uses a 40.96mV maximum scale, so shunt voltages over this will be scaled down
proportionally.
</p>
<p id="b11">
can you post the entire file in a zip. I tried this code , but I think i did something wrong , because it does not work. I`m not that good with programing.
Sure. I have applied it to my fork of Stuart's repository at https://github.com/delboy711/diyBMSv4ESP32 You can download a Zip file of the entire code there and compile it in the same way as you do Stuart's. Are you OK with compiling in PlatformIO?
The affected files are ESPController/include/CurrentMonitorINA229.h ESPController/src/CurrentMonitorINA229.cpp ESPController/web_src/default.htm (just trivial changes to the text. )
i`m okay with compiling. Thank you
Like I mentioned in the forum I would like to use 75mV shunts with ina229 addon board. I have tried to alter CurrentMonitorINA229.cpp and CurrentMonitorINA229.h It works but not precise. The value is higher or lower than real. I think I missed something or maybe is due to scaling to 50mV shunts. i looked several times over the code , used the TI ina229 calculator but I have no real idea where to put the values. I will copy paste the code here
CurrentMonitorINA229.cpp `/*
( ( )( \/ )( ( \/ )/ ) ( \/ )/. | )() ))( \ / ) < ) ( _ \ \ /( _) (/() () (__/(/\/_)(/ \/ (_)
DIYBMS V4.0 INA229 CURRENT/ENERGY MONITOR CHIP (SPI INTERFACE)
(c)2023 Stuart Pittaway
COMPILE THIS CODE USING PLATFORM.IO LICENSE Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales (CC BY-NC-SA 2.0 UK) https://creativecommons.org/licenses/by-nc-sa/2.0/uk/
COMMERCIAL USE AND RESALE PROHIBITED */
define USE_ESP_IDF_LOG 1
static constexpr const char *const TAG = "curmon";
include "CurrentMonitorINA229.h"
void CurrentMonitorINA229::CalculateLSB() { // Take a look at these for information on how it works! // https://dev.ti.com/gallery/view/4910879/INA228_229_237_238_239EVM_GUI/ver/2.0.0/ // in above - click COG icon top left. // https://e2e.ti.com/support/amplifiers-group/amplifiers/f/amplifiers-forum/1034569/ina228-accumulated-energy-and-charge-is-wrong
}
// Sets SOC by setting "fake" in/out amphour counts // value=8212 = 82.12% void CurrentMonitorINA229::SetSOC(uint16_t value) { // Assume battery is fully charged milliamphour_in = 1000 (uint32_t)registers.batterycapacity_amphour; // And we have consumed this much... milliamphour_out = (1.0 - ((float)value / 10000.0)) milliamphour_in;
}
uint8_t CurrentMonitorINA229::readRegisterValue(INA_REGISTER r) { // These are not really registers, but shape the SPI frame to indicate read/write // page 19 of documentation, Table 7-2. First 8-MSB Bits of SPI Frame. // Read register return (r << 2) | B00000001; } uint8_t CurrentMonitorINA229::writeRegisterValue(INA_REGISTER r) { // These are not really registers, but shape the SPI frame to indicate read/write // page 19 of documentation, Table 7-2. First 8-MSB Bits of SPI Frame. // Write register return (r << 2) | B00000000; }
uint16_t CurrentMonitorINA229::read16bits(INA_REGISTER r) { SPI_Ptr->beginTransaction(_spisettings); digitalWrite(chipselectpin, LOW); // The transfers are always a step behind, so the transfer reads the previous value/command SPI_Ptr->write(readRegisterValue(r)); uint16_t value = SPI_Ptr->transfer16(0); digitalWrite(chipselectpin, HIGH); SPI_Ptr->endTransaction();
}
// Write a register in INA229 // During an SPI write frame, while new data is shifted into the INA229-Q1 // register, the old data from the same register is shifted out on the MISO line. uint16_t CurrentMonitorINA229::write16bits(INA_REGISTER r, uint16_t value) { SPI_Ptr->beginTransaction(_spisettings); digitalWrite(chipselectpin, LOW); // The transfers are always a step behind, so the transfer reads the previous value/command SPI_Ptr->write(writeRegisterValue(r)); uint16_t retvalue = SPI_Ptr->transfer16(value); digitalWrite(chipselectpin, HIGH); SPI_Ptr->endTransaction();
}
// Reads 3 bytes from SPI bus, and returns them in a uint32 (lower 24 bits) uint32_t CurrentMonitorINA229::spi_readUint24(INA_REGISTER r) { SPI_Ptr->beginTransaction(_spisettings); digitalWrite(chipselectpin, LOW); SPI_Ptr->write(readRegisterValue(r)); uint8_t a = SPI_Ptr->transfer(0); uint8_t b = SPI_Ptr->transfer(0); uint8_t c = SPI_Ptr->transfer(0); digitalWrite(chipselectpin, HIGH); SPI_Ptr->endTransaction();
}
int64_t CurrentMonitorINA229::spi_readInt40(INA_REGISTER r) { SPI_Ptr->beginTransaction(_spisettings); digitalWrite(chipselectpin, LOW); SPI_Ptr->write(readRegisterValue(r)); uint8_t a = SPI_Ptr->transfer(0); uint8_t b = SPI_Ptr->transfer(0); uint8_t c = SPI_Ptr->transfer(0); uint8_t d = SPI_Ptr->transfer(0); uint8_t e = SPI_Ptr->transfer(0); digitalWrite(chipselectpin, HIGH); SPI_Ptr->endTransaction();
}
uint64_t CurrentMonitorINA229::spi_readUint40(INA_REGISTER r) { SPI_Ptr->beginTransaction(_spisettings); digitalWrite(chipselectpin, LOW); SPI_Ptr->write(readRegisterValue(r)); uint8_t a = SPI_Ptr->transfer(0); uint8_t b = SPI_Ptr->transfer(0); uint8_t c = SPI_Ptr->transfer(0); uint8_t d = SPI_Ptr->transfer(0); uint8_t e = SPI_Ptr->transfer(0); digitalWrite(chipselectpin, HIGH); SPI_Ptr->endTransaction();
}
// Read a 24 bit (3 byte) unsigned integer into a uint32 (including right shift 4 bits) uint32_t CurrentMonitorINA229::readUInt24(INA_REGISTER r) { return spi_readUint24(r) >> 4; }
// Energy in JOULES float CurrentMonitorINA229::Energy() { uint64_t energy = spi_readUint40(INA_REGISTER::ENERGY); return 16.0 3.2 registers.CURRENT_LSB * energy; }
// Charge in Coulombs. // The raw value is 40 bit, but we frequently reset this back to zero so it prevents // overflow and keeps inside the int32 limits // NEGATIVE value means battery is being charged int32_t CurrentMonitorINA229::ChargeInCoulombsAsInt() { // Calculated charge output. Output value is in Coulombs.Two's complement value. 40bit number stored in int64 return registers.CURRENT_LSB * (float)spi_readInt40(INA_REGISTER::CHARGE); }
// Bus voltage output. Two's complement value, however always positive. Value in bits 23 to 4 float CurrentMonitorINA229::BusVoltage() { uint32_t busVoltage = readUInt24(INA_REGISTER::VBUS) & 0x000FFFFF; // The accuracy is 20bits and 195.3125uV is the LSB uint32_t busVoltage_mV = (uint64_t)busVoltage * (uint64_t)0x1DCD65 / (uint64_t)0x989680; // conversion to get mV
}
// Read a 20 bit (3 byte) TWOS COMPLIMENT integer from an INA register // Used to read VSHUNT value int32_t CurrentMonitorINA229::readInt20(INA_REGISTER r) { // Returns uint32_t value = readUInt24(r);
}
// Shunt voltage in MILLIVOLTS mV float CurrentMonitorINA229::ShuntVoltage() { // 78.125 nV/LSB when ADCRANGE = 1 // Differential voltage measured across the shunt output. Two's complement value. // 20 bit value max = 1048575 int32_t vshunt = readInt20(INA_REGISTER::VSHUNT); return (float)(((int64_t)vshunt) * 312500UL) / 1000000000.0; }
void CurrentMonitorINA229::TakeReadings() { voltage = BusVoltage(); current = Current(); power = Power(); temperature = DieTemperature(); SOC = CalculateSOC();
}
uint16_t CurrentMonitorINA229::CalculateSOC() { float milliamphour_in_scaled = ((float)milliamphour_in / 100.0) registers.charge_efficiency_factor; float milliamphour_batterycapacity = 1000.0 (uint32_t)registers.batterycapacity_amphour; float difference = milliamphour_in_scaled - milliamphour_out;
}
void CurrentMonitorINA229::CalculateAmpHourCounts() { // We amp-hour count using units of 18 coulombs = 5mAh, to avoid rounding issues
}
// Guess the SoC % based on battery voltage - not accurate, just a guess! void CurrentMonitorINA229::GuessSOC() { // Default SOC% at 60% uint16_t soc = 6000;
}
bool CurrentMonitorINA229::Configure(uint16_t shuntmv, uint16_t shuntmaxcur, uint16_t batterycapacity, uint16_t fullchargevolt, uint16_t tailcurrent, uint16_t chargeefficiency, uint16_t shuntcal, int16_t temperaturelimit, int16_t overvoltagelimit, int16_t undervoltagelimit, int32_t overcurrentlimit, int32_t undercurrentlimit, int32_t overpowerlimit, uint16_t shunttempcoefficient, bool TemperatureCompEnabled) { registers.shunt_millivolt = shuntmv; registers.shunt_max_current = shuntmaxcur; registers.fully_charged_voltage = fullchargevolt / 100.0; registers.batterycapacity_amphour = batterycapacity; registers.tail_current_amps = tailcurrent / 100.0; registers.charge_efficiency_factor = chargeefficiency / 100.0;
}
// Initialise INA229 chip and class object // fullchargevolt, tailcurrent and chargeefficiency are fixed point values (x100, example 1234=12.34) bool CurrentMonitorINA229::Initialise(SPIClass *SPI, uint8_t cs_pin) { SPI_Ptr = SPI; chipselectpin = cs_pin; assert(SPI != NULL);
}
void CurrentMonitorINA229::SetINA229Registers() { write16bits(INA_REGISTER::SHUNT_CAL, registers.R_SHUNT_CAL); write16bits(INA_REGISTER::SHUNT_TEMPCO, registers.R_SHUNT_TEMPCO); write16bits(INA_REGISTER::SOVL, registers.R_SOVL); write16bits(INA_REGISTER::SUVL, registers.R_SUVL); write16bits(INA_REGISTER::BOVL, registers.R_BOVL); write16bits(INA_REGISTER::BUVL, registers.R_BUVL); write16bits(INA_REGISTER::TEMP_LIMIT, registers.R_TEMP_LIMIT); write16bits(INA_REGISTER::PWR_LIMIT, registers.R_PWR_LIMIT); } ` CurrentMonitorINA229.h
`/*
( ( )( \/ )( ( \/ )/ ) ( \/ )/. | )() ))( \ / ) < ) ( _ \ \ /( _) (/() () (__/(/\/_)(/ \/ (_)
DIYBMS V4.0 INA229 CURRENT/ENERGY MONITOR CHIP (SPI INTERFACE)
(c)2023 Stuart Pittaway
COMPILE THIS CODE USING PLATFORM.IO LICENSE Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales (CC BY-NC-SA 2.0 UK) https://creativecommons.org/licenses/by-nc-sa/2.0/uk/
COMMERCIAL USE AND RESALE PROHIBITED */
/ DATASHEET INA229-Q1 AEC-Q100, 85-V, 20-Bit, Ultra-Precise Power/Energy/Charge Monitor With SPI Interface https://www.ti.com/lit/ds/symlink/ina229-q1.pdf?ts=1599057970767 /
define CONFIG_DISABLE_HAL_LOCKS 1
pragma once
include
include
ifndef CURRENTMONITORINA229H
define CURRENTMONITORINA229H
class CurrentMonitorINA229 { // This structure is held in EEPROM, it has the same register/values // as the INA229 chip and is used to set the INA229 chip to the correct parameters on power up // On initial power up (or EEPROM clear) these parameters are read from the INA229 chip // to provide defaults. Some values are overridden in code (like ADC_CONFIG and CONFIG) // to configure to our prescribed needs. struct eeprom_regs { uint16_t R_CONFIG; uint16_t R_ADC_CONFIG; uint16_t R_SHUNT_CAL; // Shunt Calibration uint16_t R_SHUNT_TEMPCO; // Shunt Temperature Coefficient uint16_t R_DIAG_ALRT; uint16_t R_SOVL; uint16_t R_SUVL; uint16_t R_BOVL; uint16_t R_BUVL; uint16_t R_TEMP_LIMIT; uint16_t R_PWR_LIMIT;
public: CurrentMonitorINA229() { INA229Installed = false; SPI_Ptr = NULL; chipselectpin = 0;
private: uint16_t SOC = 0; float voltage = 0; float current = 0; float power = 0; float temperature = 0;
};
endif
`