diff --git a/README.md b/README.md index c9eb959..85bda19 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ SwitchDoc Labs SDL_Arduino_INA3221 Library SwitchDoc Labs - www.switchdoc.com -Version 1.1 January 31, 2015 +Version 2.1 July 17, 2017 The INA3221 is Triple-Channel, High-SideMeasurement, Shunt and Bus Voltage Monitor with I2C Interface @@ -10,7 +10,47 @@ It is used in the SwitchDoc Labs Solar Controller Product, SunAirPlus and in a s http://www.switchdoc.com/sunairplus-solar-power-controllerdata-collector/ +This fork enhances V1.1 with improved arithmetic precision and easier configuration, including a pretty print of the current config. -Thanks to the original Adafruit INA219 library, although very little of it ended up being used The INA3221 is a much different beast than the INA219. +V2.1 has one incompatibility with V1 -- the #define SHUNT_RESISTOR_VALUE units are changed to from ohms to milli-ohms. The Arduino IDE with default compiler warnings will flag an existing #define SHUNT_RESISTOR_VALUE in your sketch so you can adjust it (or remove it). +These methods are faster and more precise if you avoid floating point numbers: + // These functions because integer arithmetic can be faster and more precise + + int32_t getBusVoltage_mV(int channel); + + int32_t getCurrent_uA(int channel); + + int32_t getShuntVoltage_uV(int channel); // who cares? -- get the current directly + +You may set some configuration options such as the number of samples in each measurement and the conversion time for each sample. + +Increasing the number of samples averaged into a measurement can increase accuracy when values are fluctuating. Using a longer conversion time increases the ADC accuracy of each sample. Of course, more samples or longer times slows the rate at which measurements can be obtained. + +You can also speed things up by disabling unused channels, and turning off shunt or bus reporting if you don't need that information. + +The default settings will probably limit you to capture and return all three channels no faster than every 7ms. Then add the time for your sketch to do its work... + +The configuration register is documented on page 21-22, and 13-14 of the datasheet available at this link: http://pdf.datasheetarchive.com/indexerfiles/Datasheets-IS41/DSA00810318.pdf + +The begin(config) operand is a 16 bit configuration register value. It defaults to INA3221_CONFIG_SETCONFIG, which is "all channels", "16 samples" per measurement, "1.1 ms/sample" conversion time. In the header file just before INA3221_CONFIG_SETCONFIG is defined, you will find the various values which can be or'ed together. Mix and match to create your own. + +Also in the header file are two arrays with the values available for sample size and conversion time: + + // available number of sampleSize collected and averaged together for measurement + + #define INA3221_SAMPLE_NUMBERS 1, 4, 16, 64, 128, 256, 512, 1024 + + // available conversion times for shunt and bus voltage measurement + + #define INA3221_CONVERSION_TIMES 140, 204, 332, 588, 1100, 2116, 4156, 8244 + +Each 3 bit configuration register sub-field is functionally an index 0-7 into one of these arrays. + +You can change the operating configuration on the fly using setConfigSettings(config). + +If you have Serial port connected up, printConfigValues(config) will pretty print any settings passed to it. To print the active settings, +call printConfigvalues(getConfigValues()) to read and pass them to the formatter. + +Finally, if you get out the soldering iron and hack your board address, instantiating the INA3321 object with your new address is enough. If you solder in new individual shunt resistors, pass their values (in milliohms) using the begin(config,r1,r2,r3) method operands. Otherwise all three resistors will be assumed to (all) have the (same) value given the INA3321 instantiation. \ No newline at end of file diff --git a/SDL_Arduino_INA3221.cpp b/SDL_Arduino_INA3221.cpp index 126231f..b918620 100644 --- a/SDL_Arduino_INA3221.cpp +++ b/SDL_Arduino_INA3221.cpp @@ -4,13 +4,10 @@ // Version 1.1 // SwitchDoc Labs January 31, 2015 // -// - - - /* Initial code from INA219 code (Basically just a core structure left) @author K.Townsend (Adafruit Industries) + @author Jared Thomas @license BSD (see BSDlicense.txt) */ @@ -24,6 +21,41 @@ #include "SDL_Arduino_INA3221.h" +/**************************************************************************/ +/*! + @brief Instantiates a new SDL_Arduino_INA3221 class +*/ +/**************************************************************************/ +SDL_Arduino_INA3221::SDL_Arduino_INA3221(uint8_t addr, int32_t shuntResistor) { + global.i2cAddr = addr; + global.shuntResistor = shuntResistor; + // all channels start configured with the same shunt resistor value. + // Use begin(...) to change values if board has been custom altered + global.channelShuntResistors[0] = shuntResistor; + global.channelShuntResistors[1] = shuntResistor; + global.channelShuntResistors[2] = shuntResistor; + } + +/**************************************************************************/ +/*! + @brief Sets up the HW configuration +*/ +/**************************************************************************/ +void SDL_Arduino_INA3221::begin(uint16_t config, + int32_t sr0, int32_t sr1, int32_t sr2 ) +{ + // save configuration + global.configRegister = config; + // If board shunt resistors have been altered then here's the customized values + if (sr0 != 0) { global.channelShuntResistors[0] = sr0; } + if (sr1 != 0) { global.channelShuntResistors[1] = sr1; } + if (sr2 != 0) { global.channelShuntResistors[2] = sr2; } + + Wire.begin(); + // Set chip to known config values to start + setConfigSettings(config); +} + /**************************************************************************/ /*! @brief Sends a single command byte over I2C @@ -31,7 +63,7 @@ /**************************************************************************/ void SDL_Arduino_INA3221::wireWriteRegister (uint8_t reg, uint16_t value) { - Wire.beginTransmission(INA3221_i2caddr); + Wire.beginTransmission(global.i2cAddr); #if ARDUINO >= 100 Wire.write(reg); // Register Wire.write((value >> 8) & 0xFF); // Upper 8-bits @@ -52,7 +84,7 @@ void SDL_Arduino_INA3221::wireWriteRegister (uint8_t reg, uint16_t value) void SDL_Arduino_INA3221::wireReadRegister(uint8_t reg, uint16_t *value) { - Wire.beginTransmission(INA3221_i2caddr); + Wire.beginTransmission(global.i2cAddr); #if ARDUINO >= 100 Wire.write(reg); // Register #else @@ -62,7 +94,7 @@ void SDL_Arduino_INA3221::wireReadRegister(uint8_t reg, uint16_t *value) delay(1); // Max 12-bit conversion time is 586us per sample - Wire.requestFrom(INA3221_i2caddr, (uint8_t)2); + Wire.requestFrom(global.i2cAddr, (uint8_t)2); #if ARDUINO >= 100 // Shift values to create properly formed integer *value = ((Wire.read() << 8) | Wire.read()); @@ -72,110 +104,212 @@ void SDL_Arduino_INA3221::wireReadRegister(uint8_t reg, uint16_t *value) #endif } -// -void SDL_Arduino_INA3221::INA3221SetConfig(void) -{ - - - // Set Config register to take into account the settings above - uint16_t config = INA3221_CONFIG_ENABLE_CHAN1 | - INA3221_CONFIG_ENABLE_CHAN2 | - INA3221_CONFIG_ENABLE_CHAN3 | - INA3221_CONFIG_AVG1 | - INA3221_CONFIG_VBUS_CT2 | - INA3221_CONFIG_VSH_CT2 | - INA3221_CONFIG_MODE_2 | - INA3221_CONFIG_MODE_1 | - INA3221_CONFIG_MODE_0; - wireWriteRegister(INA3221_REG_CONFIG, config); +/**************************************************************************/ +/*! + @brief Gets the raw bus voltage (16-bit signed integer, milli-volts, +/- 4 milli-volts) +*/ +/**************************************************************************/ +int16_t SDL_Arduino_INA3221::getBusVoltage_raw(int channel) { + union bit16 { int16_t i16; uint16_t u16; } value; + wireReadRegister(INA3221_REG_BUSVOLTAGE_1+(channel -1) *2, &value.u16); +/* + Serial.print("\nBusVoltage_raw="); Serial.print(value); +*/ + return (value.i16); } /**************************************************************************/ /*! - @brief Instantiates a new SDL_Arduino_INA3221 class + @brief Gets the raw shunt voltage (16-bit signed integer, units of 5 micro-volts) */ /**************************************************************************/ -SDL_Arduino_INA3221::SDL_Arduino_INA3221(uint8_t addr, float shuntresistor) { - - INA3221_i2caddr = addr; - INA3221_shuntresistor = shuntresistor; - +int16_t SDL_Arduino_INA3221::getShuntVoltage_raw(int channel) { + union bit16 { int16_t i16; uint16_t u16; } value; + wireReadRegister(INA3221_REG_SHUNTVOLTAGE_1+(channel -1) *2, &value.u16); +/* + Serial.print("\nShuntVoltage_raw="); + Serial.print(value); Serial.print(" x"); Serial.print(value,HEX); +*/ + return (value.i16); } + /**************************************************************************/ /*! - @brief Setups the HW (defaults to 32V and 2A for calibration values) + @brief Gets the Bus voltage in milli-volts */ /**************************************************************************/ -void SDL_Arduino_INA3221::begin() { - Wire.begin(); - // Set chip to known config values to start - INA3221SetConfig(); - - Serial.print("shut resistor="); Serial.println(INA3221_shuntresistor); - Serial.print("address="); Serial.println(INA3221_i2caddr); - +int32_t SDL_Arduino_INA3221::getBusVoltage_mV(int channel) { + return getBusVoltage_raw(channel); } /**************************************************************************/ /*! - @brief Gets the raw bus voltage (16-bit signed integer, so +-32767) + @brief Gets the current value in uA, taking into account the + config settings and current LSB */ /**************************************************************************/ -int16_t SDL_Arduino_INA3221::getBusVoltage_raw(int channel) { - uint16_t value; - wireReadRegister(INA3221_REG_BUSVOLTAGE_1+(channel -1) *2, &value); -// Serial.print("BusVoltage_raw="); -// Serial.println(value,HEX); - - // Shift to the right 3 to drop CNVR and OVF and multiply by LSB - return (int16_t)(value ); +int32_t SDL_Arduino_INA3221::getCurrent_uA(int channel) { + int32_t value = getShuntVoltage_raw(channel); // units of 5 uV +/* + Serial.print("\ngetCurrent_uA value="); + Serial.print(value); Serial.print(" x"); Serial.print(value,HEX); + float units = value; + Serial.print(" units="); Serial.print(units,12); + float uV = value*5; + Serial.print(" micro-volts="); Serial.print(uV,12); + float mA = (uV / global.channelShuntResistors[channel]); // micro over milli begets milli + Serial.print(" milli-amps="); Serial.print(mA,12); + units = (value*5*1000) / global.channelShuntResistors[channel]; + Serial.print(" sr=x"); Serial.print(global.channelShuntResistors[channel],HEX); + Serial.print(" return="); Serial.print(units,12); +*/ + // *5 gives uV, *1000 makes mV + return ((value*5*1000) / global.channelShuntResistors[channel]); } /**************************************************************************/ /*! - @brief Gets the raw shunt voltage (16-bit signed integer, so +-32767) + @brief Gets the shunt voltage in uV ( +/- 168.3mV) */ /**************************************************************************/ -int16_t SDL_Arduino_INA3221::getShuntVoltage_raw(int channel) { - uint16_t value; - wireReadRegister(INA3221_REG_SHUNTVOLTAGE_1+(channel -1) *2, &value); - // Serial.print("ShuntVoltage_raw="); - // Serial.println(value,HEX); - return (int16_t)value; +int32_t SDL_Arduino_INA3221::getShuntVoltage_uV(int channel) { + int32_t value = getShuntVoltage_raw(channel); // units of 5 uV + return (value * 5); // 5 uV units to uV } - - /**************************************************************************/ /*! - @brief Gets the shunt voltage in mV (so +-168.3mV) + @brief Gets the Bus voltage in volts */ /**************************************************************************/ +float SDL_Arduino_INA3221::getBusVoltage_V(int channel) { + int32_t value = getBusVoltage_raw(channel); // units of 1mv + return (float(value)/1000.0); +} +/**************************************************************************/ +/*! + @brief Gets the shunt current in milli-Amps +*/ +/**************************************************************************/ +float SDL_Arduino_INA3221::getCurrent_mA(int channel) { + int32_t value = getShuntVoltage_raw(channel); // units of 5 uV + // stay in int mode & keep precision //micro over milli begets milli + value = (value*5)/global.channelShuntResistors[channel]; + return ( float(value)); +} +/**************************************************************************/ +/*! + @brief Gets the shunt voltage drop in milli volts +*/ +//**************************************************************************/ float SDL_Arduino_INA3221::getShuntVoltage_mV(int channel) { - int16_t value; - value = getShuntVoltage_raw(channel); - return value * 0.005; + int32_t value = getShuntVoltage_raw(channel); // units of 5 uV + value = value * 5; // to uV + return ( float(value) / 1000.0); // uV units to mV } /**************************************************************************/ /*! - @brief Gets the shunt voltage in volts + @brief Send the configuration bytes */ /**************************************************************************/ -float SDL_Arduino_INA3221::getBusVoltage_V(int channel) { - int16_t value = getBusVoltage_raw(channel); - return value * 0.001; +void SDL_Arduino_INA3221::setConfigSettings(uint16_t config) +{ + wireWriteRegister(INA3221_REG_CONFIG, INA3221_CONFIG_RESET); // POR + wireWriteRegister(INA3221_REG_CONFIG, config); } /**************************************************************************/ /*! - @brief Gets the current value in mA, taking into account the - config settings and current LSB + @brief Returns hardware configurations values in INA3221_ConfigValues struct */ /**************************************************************************/ -float SDL_Arduino_INA3221::getCurrent_mA(int channel) { - float valueDec = getShuntVoltage_mV(channel)/INA3221_shuntresistor; - - return valueDec; +INA3221_ConfigValues SDL_Arduino_INA3221::getConfigSettings() +{ + uint16_t config; + wireReadRegister(INA3221_REG_CONFIG, &config); + return (getConfigSettings(config)); } + +/**************************************************************************/ +/*! + @brief Returns hardware configurations values in INA3221_ConfigValues struct +*/ +/**************************************************************************/ +INA3221_ConfigValues SDL_Arduino_INA3221::getConfigSettings(uint16_t config) +{ + INA3221_ConfigValues values; + const uint16_t numAvgsBits = INA3221_CONFIG_AVG2 + INA3221_CONFIG_AVG1 + INA3221_CONFIG_AVG0; + const uint16_t sampleSize[8] = {INA3221_SAMPLE_NUMBERS}; + const uint16_t busCTbits = INA3221_CONFIG_VBUS_CT2 + INA3221_CONFIG_VBUS_CT1 + INA3221_CONFIG_VBUS_CT0; + const uint16_t busCT[8] = {INA3221_CONVERSION_TIMES}; + const uint16_t shuntCTbits = INA3221_CONFIG_VSH_CT2 + INA3221_CONFIG_VSH_CT1 + INA3221_CONFIG_VSH_CT0; + const uint16_t shuntCT[8] = {INA3221_CONVERSION_TIMES}; + const uint16_t modeBits = INA3221_CONFIG_MODE_2 + INA3221_CONFIG_MODE_1 + INA3221_CONFIG_MODE_0; + + values.configRegister = config; + values.sampleSize = sampleSize[(config & numAvgsBits) >> 9]; + values.busCT = busCT[(config & busCTbits) >> 6]; + values.shuntCT = shuntCT[(config & shuntCTbits) >> 3]; + values.opMode = (config & modeBits); + + // and while we're here, copy the saved global configuration + values.i2cAddr = global.i2cAddr; + values.shuntResistor = global.shuntResistor; + values.channelShuntResistors[0] = global.channelShuntResistors[0]; + values.channelShuntResistors[1] = global.channelShuntResistors[1]; + values.channelShuntResistors[2] = global.channelShuntResistors[2]; + + return (values); + } + +/**************************************************************************/ +/*! + @brief Prints hardware configurations values in INA3221_ConfigValues struct +*/ +/**************************************************************************/ +void SDL_Arduino_INA3221::printConfigValues(INA3221_ConfigValues values) +{ + float ohms; + Serial.print(F("\nINA3221 at I2C address x")); Serial.print(values.i2cAddr,HEX); + Serial.print(F(" default shunt value ")); Serial.print(((float)values.shuntResistor) / 1000.0); + Serial.print(F(" ohm")); + + Serial.print(F("\nConfigRegister=")); Serial.print(values.configRegister,HEX); + + if (values.configRegister & INA3221_CONFIG_RESET) + { Serial.print(F("\n Power-On Reset Request")); } + if (values.configRegister & INA3221_CONFIG_ENABLE_CHAN1) + { Serial.print(F("\n Channel 1 enabled ")); + ohms = ((float)values.channelShuntResistors[0]) / 1000.0; + Serial.print(ohms); Serial.print(F(" ohm shunt"));} + if (values.configRegister & INA3221_CONFIG_ENABLE_CHAN2) + { Serial.print(F("\n Channel 2 enabled ")); + ohms = ((float)values.channelShuntResistors[1]) / 1000.0; + Serial.print(ohms); Serial.print(F(" ohm shunt"));} + if (values.configRegister & INA3221_CONFIG_ENABLE_CHAN3) + { Serial.print(F("\n Channel 3 enabled ")); + ohms = ((float)values.channelShuntResistors[2]) / 1000.0; + Serial.print(ohms); Serial.print(F(" ohm shunt"));} + + Serial.print(F("\n ")); Serial.print(values.sampleSize); Serial.print(F(" points per sample ")); + Serial.print(F("\n Amperage sample time ")); Serial.print(values.shuntCT); + Serial.print(F(" micro-seconds")); + Serial.print(F("\n Voltage sample time ")); Serial.print(values.busCT); + Serial.print(F(" micro-seconds")); + Serial.print(F("\n Operating Mode ")); Serial.print(values.opMode); + + if (values.configRegister & INA3221_CONFIG_MODE_2) + {Serial.print(F(" Continuous")); } + else Serial.print(F(" Triggered")); + if (!(values.configRegister & (INA3221_CONFIG_MODE_1+INA3221_CONFIG_MODE_0))) + { Serial.print(F(" -- Power Down mode")); } + else + { + if (values.configRegister & INA3221_CONFIG_MODE_1) + { Serial.print(F(" Voltage")); } + if (values.configRegister & INA3221_CONFIG_MODE_0) + { Serial.print(F(" Amperage")); } + } +} diff --git a/SDL_Arduino_INA3221.h b/SDL_Arduino_INA3221.h index 731e8e6..025ae22 100644 --- a/SDL_Arduino_INA3221.h +++ b/SDL_Arduino_INA3221.h @@ -10,9 +10,10 @@ /*! Initial code from INA219 code (Basically just a core structure left) @author K. Townsend (Adafruit Industries) - @license BSD (see BSDlicense.txt) - - */ + @author J. Thomas added ConfigValues structure support. + @license BSD (see BSDlicense.txt) + +*/ /**************************************************************************/ #if ARDUINO >= 100 @@ -23,72 +24,104 @@ #include +/*========================================================================= + Shunt Resistor value in milli-ohms + -----------------------------------------------------------------------*/ +#define SHUNT_RESISTOR_VALUE 100 // default shunt resistor value of 0.1 Ohm /*========================================================================= I2C ADDRESS/BITS -----------------------------------------------------------------------*/ - #define INA3221_ADDRESS (0x40) // 1000000 (A0+A1=GND) - #define INA3221_READ (0x01) -/*=========================================================================*/ +#define INA3221_ADDRESS (0x40) // 1000000 (A0+A1=GND) + #define INA3221_REG_CONFIG (0x00) + #define INA3221_READ (0x01) + #define INA3221_REG_SHUNTVOLTAGE_1 (0x01) + #define INA3221_REG_BUSVOLTAGE_1 (0x02) /*========================================================================= CONFIG REGISTER (R/W) -----------------------------------------------------------------------*/ - #define INA3221_REG_CONFIG (0x00) - /*---------------------------------------------------------------------*/ #define INA3221_CONFIG_RESET (0x8000) // Reset Bit - + #define INA3221_CONFIG_ENABLE_CHAN1 (0x4000) // Enable Channel 1 #define INA3221_CONFIG_ENABLE_CHAN2 (0x2000) // Enable Channel 2 #define INA3221_CONFIG_ENABLE_CHAN3 (0x1000) // Enable Channel 3 - - #define INA3221_CONFIG_AVG2 (0x0800) // AVG Samples Bit 2 - See table 3 spec - #define INA3221_CONFIG_AVG1 (0x0400) // AVG Samples Bit 1 - See table 3 spec - #define INA3221_CONFIG_AVG0 (0x0200) // AVG Samples Bit 0 - See table 3 spec - - #define INA3221_CONFIG_VBUS_CT2 (0x0100) // VBUS bit 2 Conversion time - See table 4 spec - #define INA3221_CONFIG_VBUS_CT1 (0x0080) // VBUS bit 1 Conversion time - See table 4 spec - #define INA3221_CONFIG_VBUS_CT0 (0x0040) // VBUS bit 0 Conversion time - See table 4 spec - - #define INA3221_CONFIG_VSH_CT2 (0x0020) // Vshunt bit 2 Conversion time - See table 5 spec - #define INA3221_CONFIG_VSH_CT1 (0x0010) // Vshunt bit 1 Conversion time - See table 5 spec - #define INA3221_CONFIG_VSH_CT0 (0x0008) // Vshunt bit 0 Conversion time - See table 5 spec - - #define INA3221_CONFIG_MODE_2 (0x0004) // Operating Mode bit 2 - See table 6 spec - #define INA3221_CONFIG_MODE_1 (0x0002) // Operating Mode bit 1 - See table 6 spec - #define INA3221_CONFIG_MODE_0 (0x0001) // Operating Mode bit 0 - See table 6 spec - -/*=========================================================================*/ -/*========================================================================= - SHUNT VOLTAGE REGISTER (R) - -----------------------------------------------------------------------*/ - #define INA3221_REG_SHUNTVOLTAGE_1 (0x01) + // {1, 4, 16, 64, 128, 256, 512, 1024}[three setting bits] + #define INA3221_CONFIG_AVG2 (0x0800) // AVG Samples Bit 2 - See table 3 spec 128 + #define INA3221_CONFIG_AVG1 (0x0400) // AVG Samples Bit 1 - See table 3 spec 16 + #define INA3221_CONFIG_AVG0 (0x0200) // AVG Samples Bit 0 - See table 3 spec 4 + + // msec {.140 .204 332 .588 1.1 2.116 4.156 8.244} [3 setting bits] + #define INA3221_CONFIG_VBUS_CT2 (0x0100) // VBUS bit 2 Conversion time - See table 4 spec 1.1 + #define INA3221_CONFIG_VBUS_CT1 (0x0080) // VBUS bit 1 Conversion time - See table 4 spec .332 + #define INA3221_CONFIG_VBUS_CT0 (0x0040) // VBUS bit 0 Conversion time - See table 4 spec .204 + + // msec {.140 .204 332 .588 1.1 2.116 4.156 8.244} [3 setting bits] + #define INA3221_CONFIG_VSH_CT2 (0x0020) // Vshunt bit 2 Conversion time - See table 5 spec 1.1 + #define INA3221_CONFIG_VSH_CT1 (0x0010) // Vshunt bit 1 Conversion time - See table 5 spec .332 + #define INA3221_CONFIG_VSH_CT0 (0x0008) // Vshunt bit 0 Conversion time - See table 5 spec .204 + + #define INA3221_CONFIG_MODE_2 (0x0004) // Operating Mode bit 2 - See table 6 spec continuous + #define INA3221_CONFIG_MODE_1 (0x0002) // Operating Mode bit 1 - See table 6 spec bus + #define INA3221_CONFIG_MODE_0 (0x0001) // Operating Mode bit 0 - See table 6 spec shunt + + #define INA3221_CONFIG_SETCONFIG INA3221_CONFIG_ENABLE_CHAN1 | \ + INA3221_CONFIG_ENABLE_CHAN2 | \ + INA3221_CONFIG_ENABLE_CHAN3 | \ + INA3221_CONFIG_AVG1 | \ + INA3221_CONFIG_VBUS_CT2 | \ + INA3221_CONFIG_VSH_CT2 | \ + INA3221_CONFIG_MODE_2 | \ + INA3221_CONFIG_MODE_1 | \ + INA3221_CONFIG_MODE_0 + + // available number of sampleSize collected and averaged together for measurement + #define INA3221_SAMPLE_NUMBERS 1, 4, 16, 64, 128, 256, 512, 1024 + // available conversion times for shunt and bus voltage measurement + #define INA3221_CONVERSION_TIMES 140, 204, 332, 588, 1100, 2116, 4156, 8244 /*=========================================================================*/ - -/*========================================================================= - BUS VOLTAGE REGISTER (R) - -----------------------------------------------------------------------*/ - #define INA3221_REG_BUSVOLTAGE_1 (0x02) -/*=========================================================================*/ - -#define SHUNT_RESISTOR_VALUE (0.1) // default shunt resistor value of 0.1 Ohm +typedef struct INA3221_ConfigValues +{ + uint16_t configRegister; // hardware bit inside INA3221 + uint16_t sampleSize; // number of points averaged in a sample + uint16_t busCT; // Conversion Time microseconds + uint16_t shuntCT; // Conversion Time microseconds + uint16_t opMode; // 0-7 + uint8_t avail_1; // padding for boundary alignment + uint8_t i2cAddr; // IIC hardware address byte in use + int32_t shuntResistor; // milli-ohms + int32_t channelShuntResistors[3]; // milli-ohms +} +INA3221_ConfigValues; class SDL_Arduino_INA3221{ public: - SDL_Arduino_INA3221(uint8_t addr = INA3221_ADDRESS, float shuntresistor = SHUNT_RESISTOR_VALUE); - void begin(void); - float getBusVoltage_V(int channel); - float getShuntVoltage_mV(int channel); - float getCurrent_mA(int channel); - + SDL_Arduino_INA3221(uint8_t addr = INA3221_ADDRESS, + int32_t shuntResistor = SHUNT_RESISTOR_VALUE); + void begin(uint16_t config = INA3221_CONFIG_SETCONFIG, + int32_t shunt1 = 0, + int32_t shunt2 = 0, + int32_t shunt3 = 0); + float getBusVoltage_V(int channel); + float getShuntVoltage_mV(int channel); // who cares -- get the current directly? + float getCurrent_mA(int channel); + + // These functions because integer arithmetic can be faster and more precise + int32_t getBusVoltage_mV(int channel); + int32_t getCurrent_uA(int channel); + int32_t getShuntVoltage_uV(int channel); // who cares -- get the current directly? + + // These functions provided for setting/obtaining/documenting operating parameters + void setConfigSettings(uint16_t config); + INA3221_ConfigValues getConfigSettings(); + INA3221_ConfigValues getConfigSettings(uint16_t config); + void printConfigValues(INA3221_ConfigValues values); + private: - uint8_t INA3221_i2caddr; - float INA3221_shuntresistor; - + INA3221_ConfigValues global; void wireWriteRegister(uint8_t reg, uint16_t value); void wireReadRegister(uint8_t reg, uint16_t *value); - void INA3221SetConfig(void); int16_t getBusVoltage_raw(int channel); int16_t getShuntVoltage_raw(int channel); diff --git a/examples/Ammeter.ino b/examples/Ammeter.ino new file mode 100644 index 0000000..503e088 --- /dev/null +++ b/examples/Ammeter.ino @@ -0,0 +1,179 @@ +#include +//#include +//#include +#include // for library resolution +#include // for object defination in library +//#include // for object defination in library +#include +#include + +/*-----( Declare Constants )-----*/ +static const uint8_t _LED = LED_BUILTIN; // LED hooked to Pin 13 +static const uint8_t _rsPin = 4; +static const uint8_t _rwPin = 5; +static const uint8_t _enPin = 6; +static const uint8_t _contrastPin = 9; // PWM pin controling contrast +static const uint8_t _backlightPin = 10; // PWM pin controling backlight + +static const uint8_t _am_addr = 64; // hex 40 I2C address of sdl board +static const int32_t _am_shunt_value = 100; // shunt resistor value on sdl board in milli-ohms + +/*-----( Declare objects )-----*/ +// initialize the library with the numbers of the interface pins +// RS, RW, EN, D0-7, backlight, polarity +//LiquidCrystal lcd(_rsPin, _rwPin, _enPin, +// A3, A2, A1, A0, +// _backlightPin, POSITIVE); + +// initialize the library with the numbers of the interface pins +//LiquidCrystal_I2C lcd(0x23); // address +// PCF8574 port::LCD2004A pin +// P0=Rs/4, P1=RW/5, P2=En/6, P3=nc, P4=D4/11, P5=D5/12, P6=D6/13, P7=D7/14 + +// addr, en,rw,rs,d4,d5,d6,d7,bl,blpol +LiquidCrystal_I2C lcd(0x23, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address + +SDL_Arduino_INA3221 ampmeter(_am_addr, _am_shunt_value); + +/*-----( Declare Variables )-----*/ +INA3221_ConfigValues configValues; +int channels; +float time; + +/*-----( Declare Helper functions )-----*/ +void outLCDformatted(int32_t value, int width, int shift=6, bool trace=false); +void showChannelData(int channel, bool trace=false); + + +void setup() +{ + RXLED0; TXLED0; // set the LEDS off + +// ------- Fire up peripherials ------------- + Serial.begin(9600); //This pipes to the serial monitor + Wire.begin(); // I am the master + lcd.begin(20, 4); // cols, rows + lcd.setContrast( 76, _contrastPin ); // lower is darker "off" dots + lcd.clear(); + ampmeter.begin(); + + RXLED1; TXLED1; +// delay(5000); // wait so user can open serial monitor + +// I2Cscan(true, false, 5); // header, ALL, delay after active address found + TWBR = (F_CPU/(100L*1000L) - 16)/2; // I2C bus speed to 100 Mhz + RXLED0; TXLED1; + + // Determine some config info needed for the LCD heading lines. + configValues = ampmeter.getConfigSettings(); + channels = (configValues.configRegister && INA3221_CONFIG_ENABLE_CHAN1) + + (configValues.configRegister && INA3221_CONFIG_ENABLE_CHAN2) + + (configValues.configRegister && INA3221_CONFIG_ENABLE_CHAN3); + time = ((configValues.busCT + configValues.shuntCT) * channels) * 1.10; // plus 10% fudge + time = time / 1000; // micros to milli-seconds +/**/ + // Show hardware settings, sample rate, sample times + ampmeter.PrintConfigValues(configValues); + Serial.print(F("\nMinimum cycle time ")); Serial.print(time); Serial.print(F(" ms total for ")); + Serial.print(channels); Serial.println(F(" channel(s)")); +/**/ +} + +void loop() +{ +// heading/static text + lcd.setCursor(0, 0); // column, line (zero origin) + lcd.print(F("mA+V v1 sampleN=")); lcd.print(configValues.sampleSize); + + lcd.setCursor(0, 1); // column, line (zero origin) + lcd.print(F("CTa ")); lcd.print(configValues.shuntCT); + lcd.print(F(" CTv ")); lcd.print(configValues.busCT); + lcd.print(" ms"); +/**/ + showChannelData(1, true); + showChannelData(2, true); + showChannelData(3, true); +/**/ + for (int i=0; i<49; i++) + { + showChannelData(1); + showChannelData(2); + showChannelData(3); + delay(500); // wait for a half second + } +} + +void showChannelData(int channel, bool trace) +{ + int32_t CuA = ampmeter.getCurrent_uA(channel); + int32_t BmV = ampmeter.getBusVoltage_mV(channel); + int32_t SuV = ampmeter.getShuntVoltage_uV(channel); +/* + if (trace) { + float bV = ampmeter.getBusVoltage_V(channel); + float cmA = ampmeter.getCurrent_mA(channel); + float smV = ampmeter.getShuntVoltage_mV(channel); + + Serial.print(F("\nC")); + Serial.print(channel); Serial.print(' '); + Serial.print(cmA,9); Serial.print(F(" milli-amps ")); + Serial.print(bV,9); Serial.print(F(" Volts ")); + Serial.print(smV,9); Serial.print(F(" Shunt mV ")); +// The Arduino library can't recognize 32 bit values for proper printing. +// A circumvention is sprintf adding a 'l' (ell) to the pattern. + Serial.print(CuA,6); Serial.print(F(" \xb5\x41 ")); // micro symbol, cap A + Serial.print(BmV,6); Serial.print(F(" bmV ")); + Serial.print(SuV,6); Serial.print(F(" \xb5V ")); + } +/**/ + // setCursor (column, line) // (zero origin) + // channels wired upside down inside box + lcd.setCursor((channel-1)*7, 2); outLCDformatted(CuA/10,6,2, trace); + lcd.setCursor((channel-1)*7, 3); outLCDformatted(BmV,6,3, trace); +} + +void outLCDformatted(int32_t value, int width, int shift, bool trace) +{ + char buffer[16] = "\0\0"; + int dp=12; // location of decimal point in pattern buffer + int first, last, ptr; + + int size = snprintf (buffer, sizeof(buffer), "%12li.", value); + // buffer has 14 chars, buffer[dp=12] is a decimal point, + // leading zeros are suppressed + + // shift the decimal point left requested amount + for (int i = 1; i<=shift, dp>=1; i++) + { + // float any minus sign + if (buffer[dp-1] == '-') {buffer[max(dp-2,0)] = '-'; buffer[max(dp-1,0)] = '0';} + // undo suppression after (new) decimal point + if (buffer[dp-1] == ' ') {buffer[dp-1] = '0';} + buffer[dp] = buffer[dp-1]; + buffer[dp-1] = '.'; + dp = dp - 1; + } + // undo suppression before (new) decimal point + if (buffer[dp-1] == ' ') {buffer[dp-1] = '0';} + + // locate first significant digit + for (ptr=0; (buffer[ptr] <= '0') && (ptr<(dp-1)); ptr++) {} + + // locate (width) characters, avoiding a trailing decimal point + first = ptr; last = first + (width-1); + + // Back up 1 when last selection character is the decimal + if (last == dp) { first = first - 1; last = last - 1; } + + // If selection runs off buffer (wide with, small shift), + // move selection left back inside buffer + while (last>= size) { first = first-1; last = last-1; } + + // Overflow when selection doesn't include the units digit + if (last < (dp-1)) { buffer[last] = '*'; } + +// Finally -- the goal of our quest! + for (int i=first; i<=last; i++) {lcd.write(buffer[i]); } +} + +// #include diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..a4b6b9a --- /dev/null +++ b/keywords.txt @@ -0,0 +1,58 @@ +########################################### +# Syntax Coloring Map For SDL_Arduino_INA3221 V2.1 +########################################### + +########################################### +# Datatypes (KEYWORD1) +########################################### +SDL_Arduino_INA3221 KEYWORD1 +INA3221_ConfigValues KEYWORD1 +configRegister; KEYWORD1 +sampleSize; KEYWORD1 +busCT; KEYWORD1 +shuntCT; KEYWORD1 +opMode; KEYWORD1 +i2cAddr; KEYWORD1 +shuntResistor; KEYWORD1 +channelShuntResistors KEYWORD1 + +########################################### +# Methods and Functions (KEYWORD2) +########################################### +SDL_Arduino_INA3221 KEYWORD2 +begin KEYWORD2 +getBusVoltage_V KEYWORD2 +getShuntVoltage_mV KEYWORD2 +getCurrent_mA KEYWORD2 +getBusVoltage_mV KEYWORD2 +getCurrent_uA KEYWORD2 +getShuntVoltage_uV KEYWORD2 +getConfigSettings KEYWORD2 +printConfigValues KEYWORD2 + +########################################### +# Constants (LITERAL1) +########################################### +INA3221_REG_CONFIG LITERAL1 +INA3221_READ LITERAL1 +INA3221_REG_SHUNTVOLTAGE_1 LITERAL1 +INA3221_REG_BUSVOLTAGE_1 LITERAL1 +INA3221_CONFIG_RESET LITERAL1 +INA3221_CONFIG_ENABLE_CHAN1 LITERAL1 +INA3221_CONFIG_ENABLE_CHAN2 LITERAL1 +INA3221_CONFIG_ENABLE_CHAN3 LITERAL1 +INA3221_CONFIG_AVG2 LITERAL1 +INA3221_CONFIG_AVG1 LITERAL1 +INA3221_CONFIG_AVG0 LITERAL1 +INA3221_CONFIG_VBUS_CT2 LITERAL1 +INA3221_CONFIG_VBUS_CT1 LITERAL1 +INA3221_CONFIG_VBUS_CT0 LITERAL1 +INA3221_CONFIG_VSH_CT2 LITERAL1 +INA3221_CONFIG_VSH_CT1 LITERAL1 +INA3221_CONFIG_VSH_CT0 LITERAL1 +INA3221_CONFIG_MODE_2 LITERAL1 +INA3221_CONFIG_MODE_1 LITERAL1 +INA3221_CONFIG_MODE_0 LITERAL1 +INA3221_CONFIG_SETCONFIG LITERAL1 +INA3221_SAMPLE_NUMBERS LITERAL1 +INA3221_CONVERSION_TIMES LITERAL1