From fc47211554cf95b264495fe7efda84ed9abf7637 Mon Sep 17 00:00:00 2001 From: John Ford Date: Thu, 24 Mar 2022 16:08:54 -0700 Subject: [PATCH 1/4] Changed to use Wire1 I2C port. Added check for overflow on constructor Added CCHM Example program --- examples/CCHM_Test/CCHM_Test.ino | 80 ++++++++++++++++++++++++++++++++ src/INA220.cpp | 55 ++++++++++++++-------- src/INA220.h | 15 +++--- 3 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 examples/CCHM_Test/CCHM_Test.ino diff --git a/examples/CCHM_Test/CCHM_Test.ino b/examples/CCHM_Test/CCHM_Test.ino new file mode 100644 index 0000000..69bc203 --- /dev/null +++ b/examples/CCHM_Test/CCHM_Test.ino @@ -0,0 +1,80 @@ +#include +#include + +/****************** + * Begin Configure + ******************/ + +const uint8_t NUM_INA = 4; // 4 INA devices +const uint8_t MAX_CUR = 4; // 4 Amps minimum with 0.010 ohm shunt to avoid overflow in calibration register! +const uint32_t SHUNT_R = 10000; // 10 mOhm, expressed in microOhms +const uint8_t V12_1 = 0x40; +const uint8_t V12_2 = 0x41; +const uint8_t V5_1 = 0x42; +const uint8_t V5_2 = 0x43; + +uint8_t ina_addresses[NUM_INA] = {V12_1,V12_2,V5_1,V5_2}; // INA I2C addresses + +/****************** + * End Configure + ******************/ + +INA220 ina220; + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); + delay(1000); + uint8_t availableDevices = ina220.begin(MAX_CUR, SHUNT_R, INA_ADC_MODE_128AVG, INA_ADC_MODE_128AVG, INA_MODE_CONTINUOUS_BOTH, ina_addresses, NUM_INA); + Serial.print("Configured "); Serial.print(availableDevices); Serial.print(" of "); Serial.print(NUM_INA); Serial.println(" INA220 current sensors"); + + delay(1000); +} + +int blink = 0; +uint16_t registers[6]; + +#define BITSIZEOF(x) (sizeof(x)*8) + +char *int_to_bitstring_static(int x, int count) +{ + static char bitbuf[BITSIZEOF(x)+1]; + count = (count<1 || count>BITSIZEOF(x)) ? BITSIZEOF(x) : count; + for(int i = 0; i>(count-1-i))&1); + bitbuf[count]=0; + return bitbuf; +} + + +void dumpAll() +{ + for( int i = 0; i < NUM_INA ;i++) + { + ina220.dumpRegisters(registers,ina220.getDeviceAddress(i)); + for(int j = 0; j < 6; j++ ) + { + Serial.print("Device "); Serial.print(i); Serial.print( " Register ");Serial.print(j);Serial.print(" = " );Serial.print(registers[j],DEC);Serial.print(" "); + Serial.print(registers[j],HEX); Serial.print(" "); Serial.println(int_to_bitstring_static(registers[j],16)); + + } + } +} + + + +void loop() { + for (int i = 0; i < NUM_INA; i++) { + float vol = ina220.getBusMilliVolts(i) / 1000.0; + float shunt_vol = ina220.getShuntMicroVolts(i)/1000.0; + float cur = ina220.getBusMicroAmps(i) / 1000.0; + float power = ina220.getBusMicroWatts(i) / 1000.0; + Serial.print("INA at 0x"); Serial.print(ina_addresses[i], HEX); Serial.print(" measures "); Serial.print(vol); Serial.print(" V, "); + Serial.print(shunt_vol); Serial.print(" mV, "); + Serial.print(cur); Serial.print(" mA, and "); Serial.print(power); Serial.println(" mW"); + } + Serial.println(); + digitalWrite(LED_BUILTIN, (blink++ & 0x01)); + // dumpAll(); + delay(1000); +} diff --git a/src/INA220.cpp b/src/INA220.cpp index c9b7542..d8fdccd 100644 --- a/src/INA220.cpp +++ b/src/INA220.cpp @@ -1,5 +1,6 @@ #include "INA220.h" #include +#include "assert.h" INA220::INA220() { } @@ -17,13 +18,21 @@ uint8_t INA220::begin(uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode @param[in] deviceAddresses pointer to array of device addresses @param[in] numDevices number of devices enumerated in deviceAddresses array @return Number of INA220 devices found on the I2C bus */ - + float temp; this->deviceAddresses = deviceAddresses; this->numDevices = numDevices; this->configRegister = INA220_CONFIG_DEFAULT; // Initialize to the INA220 default value - this->current_LSB = (uint64_t)maxBusAmps * 1000000000 / 32767; // Get the best possible LSB in nA + this->current_LSB = (uint64_t) maxBusAmps * 1000000000 / 32767; // Get the best possible LSB in nA this->power_LSB = (uint32_t)20 * this->current_LSB; // Fixed multiplier per device - this->calibrationRegister = (uint64_t)409600000 / ((uint64_t)this->current_LSB * (uint64_t)microOhmR / (uint64_t)100000); // Compute calibration register + // Compute calibration register + // Convert numerator to nanoamps, Convert microohms to ohms, perform equation 1 in datasheet + // There be dragons here! Watch for overflow. Notice cast to uint16_t. + uint64_t calReg = (uint64_t)40960000 / ((uint64_t)this->current_LSB * (uint64_t)microOhmR / (uint64_t)1000000); + + if(calReg >= 65536) + return 0; + + this->calibrationRegister = (uint16_t)calReg; /* Determine optimal programmable gain so that there is no chance of an overflow yet with maximum accuracy */ uint8_t programmableGain; // work variable for the programmable gain @@ -45,7 +54,7 @@ uint8_t INA220::begin(uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode this->configRegister &= ~INA_CONFIG_MODE_MASK; // Zero out the device mode bits this->configRegister |= INA_CONFIG_MODE_MASK & deviceMode; // Mask off unused bits then shift in the mode settings - Wire.begin(); + Wire1.begin(); uint8_t availableDevices = resetAll(); // Check if communication is working, then write calibration and configuration registers return availableDevices; } @@ -57,7 +66,7 @@ void INA220::setI2CSpeed(const uint32_t i2cSpeed) { is done. @param[in] i2cSpeed [optional] changes the I2C speed to the rate specified in Hertz */ - Wire.setClock(i2cSpeed); + Wire1.setClock(i2cSpeed); } void INA220::setMode(const uint8_t mode, const uint8_t deviceNumber) { @@ -184,8 +193,8 @@ uint8_t INA220::reset(const uint8_t deviceNumber) { @return uint8_t number of devices that identified as INA220 (0 or 1) */ uint8_t availableDevices = 0; - Wire.beginTransmission(getDeviceAddress(deviceNumber)); - uint8_t good = Wire.endTransmission(); + Wire1.beginTransmission(getDeviceAddress(deviceNumber)); + uint8_t good = Wire1.endTransmission(); if (good == 0) { // If no I2C error writeWord(INA_CONFIGURATION_REGISTER, INA_RESET_DEVICE, getDeviceAddress(deviceNumber)); // Forces INAs to reset uint16_t tempRegister = readWord(INA_CONFIGURATION_REGISTER, getDeviceAddress(deviceNumber)); // Read the newly reset register @@ -266,17 +275,25 @@ int16_t INA220::readWord(const uint8_t addr, const uint8_t deviceAddress) { @param[in] deviceAddress Address on the I2C device to read from @return integer value read from the I2C device */ - Wire.beginTransmission(deviceAddress); // Address the I2C device - Wire.write(addr); // Send register address to read - Wire.endTransmission(); // Close transmission + Wire1.beginTransmission(deviceAddress); // Address the I2C device + Wire1.write(addr); // Send register address to read + Wire1.endTransmission(); // Close transmission delayMicroseconds(I2C_DELAY); // Delay required for sync - Wire.requestFrom(deviceAddress, (uint8_t)2); // Request 2 consecutive bytes - int16_t returnData = Wire.read(); // Read the msb + Wire1.requestFrom(deviceAddress, (uint8_t)2); // Request 2 consecutive bytes + int16_t returnData = Wire1.read(); // Read the msb returnData = returnData << 8; // Shift the data over 8 bits - returnData |= Wire.read(); // Read the lsb + returnData |= Wire1.read(); // Read the lsb return returnData; } +void INA220::dumpRegisters(uint16_t * regBuffer, const uint8_t deviceAddress) +{ + uint8_t regAddress; + for( regAddress = 0; regAddress < 6; regAddress++ ) + regBuffer[regAddress] = readWord(regAddress, deviceAddress); +} + + void INA220::writeWord(const uint8_t addr, const uint16_t data, const uint8_t deviceAddress) { /*! @brief Write 2 bytes to the specified I2C address @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been added to let the INAxxx @@ -285,10 +302,10 @@ void INA220::writeWord(const uint8_t addr, const uint16_t data, const uint8_t de @param[in] data 2 Bytes to write to the device @param[in] deviceAddress Address on the I2C device to write to */ - Wire.beginTransmission(deviceAddress); // Address the I2C device - Wire.write(addr); // Send register address to write - Wire.write((uint8_t)(data >> 8)); // Write the first (MSB) byte - Wire.write((uint8_t)data); // And then the second - Wire.endTransmission(); // Close transmission and actually send data + Wire1.beginTransmission(deviceAddress); // Address the I2C device + Wire1.write(addr); // Send register address to write + Wire1.write((uint8_t)(data >> 8)); // Write the first (MSB) byte + Wire1.write((uint8_t)data); // And then the second + Wire1.endTransmission(); // Close transmission and actually send data delayMicroseconds(I2C_DELAY); // Delay required for sync -} \ No newline at end of file +} diff --git a/src/INA220.h b/src/INA220.h index fcf6c77..a03ff13 100644 --- a/src/INA220.h +++ b/src/INA220.h @@ -49,12 +49,12 @@ INA_ADC_MODE_11BIT = 0x2, INA_ADC_MODE_12BIT = 0x3, INA_ADC_MODE_2AVG = 0x9, - INA_ADC_MODE_4AVG = 0x10, - INA_ADC_MODE_8AVG = 0x11, - INA_ADC_MODE_16AVG = 0x12, - INA_ADC_MODE_32AVG = 0x13, - INA_ADC_MODE_64AVG = 0x14, - INA_ADC_MODE_128AVG = 0x15 + INA_ADC_MODE_4AVG = 0x0A, + INA_ADC_MODE_8AVG = 0x0B, + INA_ADC_MODE_16AVG = 0x0C, + INA_ADC_MODE_32AVG = 0x0D, + INA_ADC_MODE_64AVG = 0x0E, + INA_ADC_MODE_128AVG = 0x0F }; // of enumerated type /***************************************************************************************************************** @@ -112,6 +112,7 @@ bool conversionFinished (const uint8_t deviceNumber); bool waitForConversion (const uint16_t timeout, const uint8_t deviceNumber); uint8_t waitForConversionAll (const uint16_t timeout); + void dumpRegisters(uint16_t * regBuffer, const uint8_t deviceAddress); private: void initDevice (const uint8_t deviceNumber); int16_t readWord (const uint8_t addr, const uint8_t deviceAddress); @@ -123,4 +124,4 @@ uint16_t calibrationRegister; uint16_t configRegister; }; -#endif \ No newline at end of file +#endif From 36dde32426fba79dd16aa5f540bde8fa8c816a20 Mon Sep 17 00:00:00 2001 From: John Ford Date: Tue, 29 Mar 2022 15:35:49 -0700 Subject: [PATCH 2/4] Added Wire reference passing to the begin() of the library to support dual I2C ports Tested with Teensy 4.1. Added check to see if there is overflow in the calculation for the CAL register. It was silently failing if the number calculated was more than 65536. --- .../BasicCurrentRead/BasicCurrentRead.ino | 5 +-- src/INA220.cpp | 33 ++++++++++--------- src/INA220.h | 5 +-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/examples/BasicCurrentRead/BasicCurrentRead.ino b/examples/BasicCurrentRead/BasicCurrentRead.ino index 14fe72e..a598e0b 100644 --- a/examples/BasicCurrentRead/BasicCurrentRead.ino +++ b/examples/BasicCurrentRead/BasicCurrentRead.ino @@ -15,7 +15,8 @@ INA220 ina220; void setup() { Serial.begin(115200); - uint8_t availableDevices = ina220.begin(MAX_CUR, SHUNT_R, INA_ADC_MODE_128AVG, INA_ADC_MODE_128AVG, INA_MODE_CONTINUOUS_BOTH, ina_addresses, NUM_INA); + Wire.begin(); + uint8_t availableDevices = ina220.begin(Wire, MAX_CUR, SHUNT_R, INA_ADC_MODE_128AVG, INA_ADC_MODE_128AVG, INA_MODE_CONTINUOUS_BOTH, ina_addresses, NUM_INA); Serial.print("Configured "); Serial.print(availableDevices); Serial.print(" of "); Serial.print(NUM_INA); Serial.println(" INA220 current sensors"); delay(100); } @@ -30,4 +31,4 @@ void loop() { } Serial.println(); delay(1000); -} \ No newline at end of file +} diff --git a/src/INA220.cpp b/src/INA220.cpp index d8fdccd..a2a7073 100644 --- a/src/INA220.cpp +++ b/src/INA220.cpp @@ -5,7 +5,7 @@ INA220::INA220() { } -uint8_t INA220::begin(uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode busAdcMode, const ina_Adc_Mode shuntAdcMode, const ina_Mode deviceMode, uint8_t* deviceAddresses, uint8_t numDevices) { +uint8_t INA220::begin( TwoWire &I2C, uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode busAdcMode, const ina_Adc_Mode shuntAdcMode, const ina_Mode deviceMode, uint8_t* deviceAddresses, uint8_t numDevices) { /*! @brief Initializes the contents of the class @details Sets INA Configuration registers for the devices at the specified addresses. @param[in] maxBusAmps Integer value holding the maximum expected bus amperage, this value is used to @@ -24,6 +24,7 @@ uint8_t INA220::begin(uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode this->configRegister = INA220_CONFIG_DEFAULT; // Initialize to the INA220 default value this->current_LSB = (uint64_t) maxBusAmps * 1000000000 / 32767; // Get the best possible LSB in nA this->power_LSB = (uint32_t)20 * this->current_LSB; // Fixed multiplier per device + this->I2C_Ptr = &I2C; // Compute calibration register // Convert numerator to nanoamps, Convert microohms to ohms, perform equation 1 in datasheet // There be dragons here! Watch for overflow. Notice cast to uint16_t. @@ -54,7 +55,7 @@ uint8_t INA220::begin(uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode this->configRegister &= ~INA_CONFIG_MODE_MASK; // Zero out the device mode bits this->configRegister |= INA_CONFIG_MODE_MASK & deviceMode; // Mask off unused bits then shift in the mode settings - Wire1.begin(); + I2C_Ptr->begin(); uint8_t availableDevices = resetAll(); // Check if communication is working, then write calibration and configuration registers return availableDevices; } @@ -66,7 +67,7 @@ void INA220::setI2CSpeed(const uint32_t i2cSpeed) { is done. @param[in] i2cSpeed [optional] changes the I2C speed to the rate specified in Hertz */ - Wire1.setClock(i2cSpeed); + I2C_Ptr->setClock(i2cSpeed); } void INA220::setMode(const uint8_t mode, const uint8_t deviceNumber) { @@ -193,8 +194,8 @@ uint8_t INA220::reset(const uint8_t deviceNumber) { @return uint8_t number of devices that identified as INA220 (0 or 1) */ uint8_t availableDevices = 0; - Wire1.beginTransmission(getDeviceAddress(deviceNumber)); - uint8_t good = Wire1.endTransmission(); + I2C_Ptr->beginTransmission(getDeviceAddress(deviceNumber)); + uint8_t good = I2C_Ptr->endTransmission(); if (good == 0) { // If no I2C error writeWord(INA_CONFIGURATION_REGISTER, INA_RESET_DEVICE, getDeviceAddress(deviceNumber)); // Forces INAs to reset uint16_t tempRegister = readWord(INA_CONFIGURATION_REGISTER, getDeviceAddress(deviceNumber)); // Read the newly reset register @@ -275,14 +276,14 @@ int16_t INA220::readWord(const uint8_t addr, const uint8_t deviceAddress) { @param[in] deviceAddress Address on the I2C device to read from @return integer value read from the I2C device */ - Wire1.beginTransmission(deviceAddress); // Address the I2C device - Wire1.write(addr); // Send register address to read - Wire1.endTransmission(); // Close transmission + I2C_Ptr->beginTransmission(deviceAddress); // Address the I2C device + I2C_Ptr->write(addr); // Send register address to read + I2C_Ptr->endTransmission(); // Close transmission delayMicroseconds(I2C_DELAY); // Delay required for sync - Wire1.requestFrom(deviceAddress, (uint8_t)2); // Request 2 consecutive bytes - int16_t returnData = Wire1.read(); // Read the msb + I2C_Ptr->requestFrom(deviceAddress, (uint8_t)2); // Request 2 consecutive bytes + int16_t returnData = I2C_Ptr->read(); // Read the msb returnData = returnData << 8; // Shift the data over 8 bits - returnData |= Wire1.read(); // Read the lsb + returnData |= I2C_Ptr->read(); // Read the lsb return returnData; } @@ -302,10 +303,10 @@ void INA220::writeWord(const uint8_t addr, const uint16_t data, const uint8_t de @param[in] data 2 Bytes to write to the device @param[in] deviceAddress Address on the I2C device to write to */ - Wire1.beginTransmission(deviceAddress); // Address the I2C device - Wire1.write(addr); // Send register address to write - Wire1.write((uint8_t)(data >> 8)); // Write the first (MSB) byte - Wire1.write((uint8_t)data); // And then the second - Wire1.endTransmission(); // Close transmission and actually send data + I2C_Ptr->beginTransmission(deviceAddress); // Address the I2C device + I2C_Ptr->write(addr); // Send register address to write + I2C_Ptr->write((uint8_t)(data >> 8)); // Write the first (MSB) byte + I2C_Ptr->write((uint8_t)data); // And then the second + I2C_Ptr->endTransmission(); // Close transmission and actually send data delayMicroseconds(I2C_DELAY); // Delay required for sync } diff --git a/src/INA220.h b/src/INA220.h index a03ff13..4ac28d9 100644 --- a/src/INA220.h +++ b/src/INA220.h @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *******************************************************************************************************************/ - +#include "Wire.h" #include "Arduino.h" #ifndef INA220__Class_h @@ -93,7 +93,7 @@ class INA220 { public: INA220 (); - uint8_t begin (uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode busAdcMode, const ina_Adc_Mode shuntAdcMode, const ina_Mode deviceMode, uint8_t* deviceAddresses, uint8_t numDevices); + uint8_t begin ( TwoWire &I2C, uint8_t maxBusAmps, uint32_t microOhmR, const ina_Adc_Mode busAdcMode, const ina_Adc_Mode shuntAdcMode, const ina_Mode deviceMode, uint8_t* deviceAddresses, uint8_t numDevices); void setI2CSpeed (const uint32_t i2cSpeed = INA_I2C_STANDARD_MODE); void setMode (const uint8_t mode, const uint8_t deviceNumber); void setModeAll (const uint8_t mode); @@ -123,5 +123,6 @@ uint32_t power_LSB; uint16_t calibrationRegister; uint16_t configRegister; + TwoWire* I2C_Ptr; }; #endif From c6e6fd6731e1ded1e9d81a831144d4e1c7223c30 Mon Sep 17 00:00:00 2001 From: John Ford Date: Tue, 29 Mar 2022 15:54:38 -0700 Subject: [PATCH 3/4] Removed CCHM_Test example, and assert.h from INA220.h --- examples/CCHM_Test/CCHM_Test.ino | 80 -------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 examples/CCHM_Test/CCHM_Test.ino diff --git a/examples/CCHM_Test/CCHM_Test.ino b/examples/CCHM_Test/CCHM_Test.ino deleted file mode 100644 index 69bc203..0000000 --- a/examples/CCHM_Test/CCHM_Test.ino +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include - -/****************** - * Begin Configure - ******************/ - -const uint8_t NUM_INA = 4; // 4 INA devices -const uint8_t MAX_CUR = 4; // 4 Amps minimum with 0.010 ohm shunt to avoid overflow in calibration register! -const uint32_t SHUNT_R = 10000; // 10 mOhm, expressed in microOhms -const uint8_t V12_1 = 0x40; -const uint8_t V12_2 = 0x41; -const uint8_t V5_1 = 0x42; -const uint8_t V5_2 = 0x43; - -uint8_t ina_addresses[NUM_INA] = {V12_1,V12_2,V5_1,V5_2}; // INA I2C addresses - -/****************** - * End Configure - ******************/ - -INA220 ina220; - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - Serial.begin(115200); - delay(1000); - uint8_t availableDevices = ina220.begin(MAX_CUR, SHUNT_R, INA_ADC_MODE_128AVG, INA_ADC_MODE_128AVG, INA_MODE_CONTINUOUS_BOTH, ina_addresses, NUM_INA); - Serial.print("Configured "); Serial.print(availableDevices); Serial.print(" of "); Serial.print(NUM_INA); Serial.println(" INA220 current sensors"); - - delay(1000); -} - -int blink = 0; -uint16_t registers[6]; - -#define BITSIZEOF(x) (sizeof(x)*8) - -char *int_to_bitstring_static(int x, int count) -{ - static char bitbuf[BITSIZEOF(x)+1]; - count = (count<1 || count>BITSIZEOF(x)) ? BITSIZEOF(x) : count; - for(int i = 0; i>(count-1-i))&1); - bitbuf[count]=0; - return bitbuf; -} - - -void dumpAll() -{ - for( int i = 0; i < NUM_INA ;i++) - { - ina220.dumpRegisters(registers,ina220.getDeviceAddress(i)); - for(int j = 0; j < 6; j++ ) - { - Serial.print("Device "); Serial.print(i); Serial.print( " Register ");Serial.print(j);Serial.print(" = " );Serial.print(registers[j],DEC);Serial.print(" "); - Serial.print(registers[j],HEX); Serial.print(" "); Serial.println(int_to_bitstring_static(registers[j],16)); - - } - } -} - - - -void loop() { - for (int i = 0; i < NUM_INA; i++) { - float vol = ina220.getBusMilliVolts(i) / 1000.0; - float shunt_vol = ina220.getShuntMicroVolts(i)/1000.0; - float cur = ina220.getBusMicroAmps(i) / 1000.0; - float power = ina220.getBusMicroWatts(i) / 1000.0; - Serial.print("INA at 0x"); Serial.print(ina_addresses[i], HEX); Serial.print(" measures "); Serial.print(vol); Serial.print(" V, "); - Serial.print(shunt_vol); Serial.print(" mV, "); - Serial.print(cur); Serial.print(" mA, and "); Serial.print(power); Serial.println(" mW"); - } - Serial.println(); - digitalWrite(LED_BUILTIN, (blink++ & 0x01)); - // dumpAll(); - delay(1000); -} From 7be5324940b83eb344ca0444812529446b362f42 Mon Sep 17 00:00:00 2001 From: John Ford Date: Thu, 7 Apr 2022 16:01:37 -0700 Subject: [PATCH 4/4] Removed Spurious include --- src/INA220.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/INA220.cpp b/src/INA220.cpp index a2a7073..a451d30 100644 --- a/src/INA220.cpp +++ b/src/INA220.cpp @@ -1,6 +1,5 @@ #include "INA220.h" #include -#include "assert.h" INA220::INA220() { }