/***************************************************************************************************************//*!
* @file INA.cpp
*
* @section INAcpp_intro_section Description
*
* Arduino Library for accessing the INA2xx Family of power measurement devices\n\n
* See main library header file "INA.h" for details and license information
*******************************************************************************************************************/
#include <INA.h> ///< Include the header definition
#include <Wire.h> ///< I2C Library definition
#if defined(__AVR__) || defined(ESP32) || defined(ESP8266)
#include <EEPROM.h> ///< Include the EEPROM library for AVR-Boards
#endif
inaDet::inaDet(){} ///< Empty constructor for INA Detail structure
/***************************************************************************************************************//*!
* @brief INA Detail Class Constructor (Overloaded)
* @details Construct the class using the saved EEPROM data structure
* @param[in] inaEE Saved EEPROM Values
*******************************************************************************************************************/
inaDet::inaDet(inaEEPROM inaEE)
{
type = inaEE.type;
operatingMode = inaEE.operatingMode;
address = inaEE.address;
maxBusAmps = inaEE.maxBusAmps;
microOhmR = inaEE.microOhmR;
switch (type)
{
case INA219:
busVoltageRegister = INA_BUS_VOLTAGE_REGISTER;
shuntVoltageRegister = INA219_SHUNT_VOLTAGE_REGISTER;
currentRegister = INA219_CURRENT_REGISTER;
busVoltage_LSB = INA219_BUS_VOLTAGE_LSB;
shuntVoltage_LSB = INA219_SHUNT_VOLTAGE_LSB;
current_LSB = (uint64_t)maxBusAmps * 1000000000 / 32767; // Get the best possible LSB in nA
power_LSB = (uint32_t)20*current_LSB; // Fixed multiplier per device
break;
case INA226:
case INA230:
case INA231:
busVoltageRegister = INA_BUS_VOLTAGE_REGISTER;
shuntVoltageRegister = INA226_SHUNT_VOLTAGE_REGISTER;
currentRegister = INA226_CURRENT_REGISTER;
busVoltage_LSB = INA226_BUS_VOLTAGE_LSB;
shuntVoltage_LSB = INA226_SHUNT_VOLTAGE_LSB;
current_LSB = (uint64_t)maxBusAmps * 1000000000 / 32767;
power_LSB = (uint32_t)25*current_LSB;
break;
case INA260:
busVoltageRegister = INA_BUS_VOLTAGE_REGISTER;
shuntVoltageRegister = INA260_SHUNT_VOLTAGE_REGISTER; // Register not present
currentRegister = INA260_CURRENT_REGISTER;
busVoltage_LSB = INA260_BUS_VOLTAGE_LSB;
current_LSB = 1250000; // Fixed LSB of 1.25mv
power_LSB = 10000000; // Fixed multiplier per device
break;
case INA3221_0:
case INA3221_1:
case INA3221_2:
busVoltageRegister = INA_BUS_VOLTAGE_REGISTER;
shuntVoltageRegister = INA3221_SHUNT_VOLTAGE_REGISTER;
currentRegister = 0; // INA3221 has no current Register
busVoltage_LSB = INA3221_BUS_VOLTAGE_LSB;
shuntVoltage_LSB = INA3221_SHUNT_VOLTAGE_LSB;
current_LSB = 0; // INA3221 has no current register
power_LSB = 0; // INA3221 has no power register
if (type==INA3221_1)
{
busVoltageRegister += 2; // Register for 2nd bus voltage
shuntVoltageRegister += 2; // Register for 2nd shunt voltage
}
else
{
if (type==INA3221_2)
{
busVoltageRegister += 4; // Register for 3rd bus voltage
shuntVoltageRegister += 4; // Register for 3rd shunt voltage
} // of if-then INA322_2
} // of if-then-else INA3221_1
break;
} // of switch type
} // of constructor
INA_Class::INA_Class() {} ///< Unused Class constructor
INA_Class::~INA_Class() {} ///< Unused Class destructor
/***************************************************************************************************************//*!
* @brief Read one word (2 bytes) from the specified I2C address
* @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been added to let the INAxxx
* devices have sufficient time to get the return data ready.
* @param[in] addr I2C address to read from
* @param[in] deviceAddress Address on the I2C device to read from
* @return integer value read from the I2C device
*******************************************************************************************************************/
int16_t INA_Class::readWord(const uint8_t addr, const uint8_t deviceAddress)
{
Wire.beginTransmission(deviceAddress); // Address the I2C device
Wire.write(addr); // Send register address to read
Wire.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
returnData = returnData<<8; // shift the data over 8 bits
returnData |= Wire.read(); // Read the lsb
return returnData;
} // of method readWord()
/***************************************************************************************************************//*!
* @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
* devices have sufficient time to process the data
* @param[in] addr I2C address to write to
* @param[in] data 2 Bytes to write to the device
* @param[in] deviceAddress Address on the I2C device to write to
*******************************************************************************************************************/
void INA_Class::writeWord(const uint8_t addr, const uint16_t data, const uint8_t deviceAddress)
{
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
delayMicroseconds(I2C_DELAY); // delay required for sync
} // of method writeWord()
/***************************************************************************************************************//*!
* @brief Read INA device information from EEPROM
* @details Retrieve the stored information for a device from EEPROM. Since this method is private and access is
* controlled, no range error checking is performed
* @param[in] deviceNumber Index to device array
*******************************************************************************************************************/
void INA_Class::readInafromEEPROM(const uint8_t deviceNumber)
{
if (deviceNumber==_currentINA || deviceNumber>_DeviceCount) return; // Do nothing if correct device addressed
#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || (__STM32F1__)
#ifdef __STM32F1__ // STM32F1 has no built-in EEPROM
uint16_t e = deviceNumber * sizeof(inaEE); // it uses flash memory to emulate
uint16_t *ptr = (uint16_t*)&inaEE; // "EEPROM" calls are uint16_t type
for (uint8_t n = sizeof(inaEE); n; --n) // Implement EEPROM.get template
{
EEPROM.read(e++, ptr++); // for ina (inaDet type)
} // of for-next each byte
#else
EEPROM.get(deviceNumber * sizeof(inaEE), inaEE); // Read EEPROM values to st