Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize to handle different I2C ports, fix some subtle bugs #3

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions examples/BasicCurrentRead/BasicCurrentRead.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -30,4 +31,4 @@ void loop() {
}
Serial.println();
delay(1000);
}
}
57 changes: 37 additions & 20 deletions src/INA220.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,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
Expand All @@ -17,13 +17,22 @@ 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
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.
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
Expand All @@ -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();
I2C_Ptr->begin();
uint8_t availableDevices = resetAll(); // Check if communication is working, then write calibration and configuration registers
return availableDevices;
}
Expand All @@ -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);
I2C_Ptr->setClock(i2cSpeed);
}

void INA220::setMode(const uint8_t mode, const uint8_t deviceNumber) {
Expand Down Expand Up @@ -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();
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
Expand Down Expand Up @@ -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
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
Wire.requestFrom(deviceAddress, (uint8_t)2); // Request 2 consecutive bytes
int16_t returnData = Wire.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 |= Wire.read(); // Read the lsb
returnData |= I2C_Ptr->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
Expand All @@ -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
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
}
}
20 changes: 11 additions & 9 deletions src/INA220.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************************************************/

#include "Wire.h"
#include "Arduino.h"

#ifndef INA220__Class_h
Expand Down Expand Up @@ -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

/*****************************************************************************************************************
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -122,5 +123,6 @@
uint32_t power_LSB;
uint16_t calibrationRegister;
uint16_t configRegister;
TwoWire* I2C_Ptr;
};
#endif
#endif