/**************************************************************************/ /*! @file ina219.c @author K. Townsend (microBuilder.eu) @brief Driver for the TI INA219 current/power monitor @section DESCRIPTION The INA219 is an I2C-based current/power monitor that monitors the voltage drop across a shunt resistor, as well as the supply voltage. @section EXAMPLE @code ina219Init(); int16_t current = 0; int16_t power = 0; int16_t current_mA = 0; int16_t power_mW = 0; int16_t busvoltage = 0; int16_t shuntvoltage = 0; int16_t loadVoltage = 0; while(1) { shuntvoltage = ina219GetShuntVoltage(); busvoltage = ina219GetBusVoltage(); power = ina219GetPower(); current = ina219GetCurrent(); power_mW = ina219GetPower_mW(); current_mA = ina219GetCurrent_mA(); loadVoltage = busvoltage + (shuntvoltage / 100); printf("%-15s %6d = %d.%dmV (%duV) \r\n", "Shunt Voltage:", shuntvoltage, shuntvoltage / 100, shuntvoltage % 100, shuntvoltage * 10); printf("%-15s %6d = %d.%dV \r\n", "Bus Voltage:", busvoltage, busvoltage / 1000, busvoltage % 1000); printf("%-15s %6d = %d.%dV \r\n", "Load Voltage:", loadVoltage, loadVoltage / 1000, loadVoltage % 1000); printf("%-15s %6d = %dmW \r\n", "Power:", power, power_mW); printf("%-15s %6d = %dmA \r\n", "Current:", current, current_mA); printf("\r\n"); systickDelay(5000); } @endcode @section LICENSE Software License Agreement (BSD License) Copyright (c) 2012 Kevin Townsend All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /**************************************************************************/ #include "ina219.h" #include "I2C.h" #include unsigned char I2CMasterBuffer[I2C_BUFSIZE]; unsigned char I2CSlaveBuffer[I2C_BUFSIZE]; // The following multipliers are used to convert raw current and power // values to mA and mW, taking into account the current config settings uint32_t ina219_currentDivider_mA = 0; uint32_t ina219_powerDivider_mW = 0; /**************************************************************************/ /*! @brief Sends a single command byte over I2C */ /**************************************************************************/ static int ina219WriteRegister (uint8_t reg, uint16_t value) { // Clear write buffers uint32_t i; for ( i = 0; i < I2C_BUFSIZE; i++ ) { I2CMasterBuffer[i] = 0x00; } //I2CWriteLength = 4; // I2CReadLength = 0; I2CMasterBuffer[0] = INA219_ADDRESS; // I2C device address I2CMasterBuffer[1] = reg; // Register I2CMasterBuffer[2] = value >> 8; // Upper 8-bits I2CMasterBuffer[3] = value & 0xFF; // Lower 8-bits return I2CWrite(I2CMasterBuffer,4); //i2cEngine(); } /**************************************************************************/ /*! @brief Reads a 16 bit values over I2C */ /**************************************************************************/ static void ina219Read16(uint8_t reg, uint16_t *value) { // Clear write buffers int RET = RET_OK; uint32_t i; for ( i = 0; i < I2C_BUFSIZE; i++ ) { I2CMasterBuffer[i] = 0x00; } // I2CWriteLength = 2; //I2CReadLength = 2; I2CMasterBuffer[0] = INA219_ADDRESS; // I2C device address I2CMasterBuffer[1] = reg; // Command register // Append address w/read bit I2CMasterBuffer[2] = INA219_ADDRESS | INA219_READ; // i2cEngine(); I2CWrite(I2CMasterBuffer,2); //write pointer to the good register RET = I2CRead(I2CMasterBuffer[2],&I2CSlaveBuffer[0],2); //read the data if(RET != RET_OK) { * value = 0; return; } // Shift values to create properly formed integer *value = ((I2CSlaveBuffer[0] << 8) | I2CSlaveBuffer[1]); } void ina219SetCalibration_13V_10A(void) { // By default we use a pretty huge range for the input voltage, // which probably isn't the most appropriate choice for system // that don't use a lot of power. But all of the calculations // are shown below if you want to change the settings. You will // also need to change any relevant register settings, such as // setting the VBUS_MAX to 16V instead of 32V, etc. // VBUS_MAX = 13V (Assumes 32V, can also be set to 16V) // VSHUNT_MAX = 0.32 (Assumes Gain 8, 320mV, can also be 0.16, 0.08, 0.04) // RSHUNT = 0.1 (Resistor value in ohms) // 1. Determine max possible current // MaxPossible_I = VSHUNT_MAX / RSHUNT // MaxPossible_I = 3.2A // 2. Determine max expected current // MaxExpected_I = 3.2A // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit) // MinimumLSB = MaxExpected_I/32767 // MinimumLSB = 0.00009765 (98µA per bit) // MaximumLSB = MaxExpected_I/4096 // MaximumLSB = 0,000488 (488µA per bit) // 4. Choose an LSB between the min and max values // (Preferrably a roundish number close to MinLSB) // CurrentLSB = 0.0001 (100µA per bit) // 5. Compute the calibration register // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 4096 (0x1000) // 6. Calculate the power LSB // PowerLSB = 20 * CurrentLSB // PowerLSB = 0.002 (2mW per bit) // 7. Compute the maximum current and shunt voltage values before overflow // // Max_Current = Current_LSB * 32767 // Max_Current = 3.2767A before overflow // // If Max_Current > Max_Possible_I then // Max_Current_Before_Overflow = MaxPossible_I // Else // Max_Current_Before_Overflow = Max_Current // End If // // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT // Max_ShuntVoltage = 0.32V // // If Max_ShuntVoltage >= VSHUNT_MAX // Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX // Else // Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage // End If // 8. Computer the Maximum Power // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX // MaximumPower = 3.2 * 32V // MaximumPower = 102.4W // Set multipliers to convert raw current/power values ina219_currentDivider_mA = 10; // Current LSB = 100uA per bit (1000/100 = 10) ina219_powerDivider_mW = 2; // Power LSB = 1mW per bit (2/1) // Set Calibration register to 'Cal' calculated above ina219WriteRegister(INA219_REG_CALIBRATION, 0x1000); // Set Config register to take into account the settings above uint16_t config = INA219_CONFIG_BVOLTAGERANGE_32V | INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | // INA219_CONFIG_MODE_SVOLT_CONTINUOUS; INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; ina219WriteRegister(INA219_REG_CONFIG, config); } /**************************************************************************/ /*! @brief Configures to INA219 to be able to measure up to 32V and 2A of current. Each unit of current corresponds to 100uA, and each unit of power corresponds to 2mW. Counter overflow occurs at 3.2A. @note These calculations assume a 0.1 ohm resistor is present */ /**************************************************************************/ void ina219SetCalibration_32V_2A(void) { // By default we use a pretty huge range for the input voltage, // which probably isn't the most appropriate choice for system // that don't use a lot of power. But all of the calculations // are shown below if you want to change the settings. You will // also need to change any relevant register settings, such as // setting the VBUS_MAX to 16V instead of 32V, etc. // VBUS_MAX = 32V (Assumes 32V, can also be set to 16V) // VSHUNT_MAX = 0.32 (Assumes Gain 8, 320mV, can also be 0.16, 0.08, 0.04) // RSHUNT = 0.1 (Resistor value in ohms) // 1. Determine max possible current // MaxPossible_I = VSHUNT_MAX / RSHUNT // MaxPossible_I = 3.2A // 2. Determine max expected current // MaxExpected_I = 2.0A // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit) // MinimumLSB = MaxExpected_I/32767 // MinimumLSB = 0.000061 (61µA per bit) // MaximumLSB = MaxExpected_I/4096 // MaximumLSB = 0,000488 (488µA per bit) // 4. Choose an LSB between the min and max values // (Preferrably a roundish number close to MinLSB) // CurrentLSB = 0.0001 (100µA per bit) // 5. Compute the calibration register // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 4096 (0x1000) // 6. Calculate the power LSB // PowerLSB = 20 * CurrentLSB // PowerLSB = 0.002 (2mW per bit) // 7. Compute the maximum current and shunt voltage values before overflow // // Max_Current = Current_LSB * 32767 // Max_Current = 3.2767A before overflow // // If Max_Current > Max_Possible_I then // Max_Current_Before_Overflow = MaxPossible_I // Else // Max_Current_Before_Overflow = Max_Current // End If // // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT // Max_ShuntVoltage = 0.32V // // If Max_ShuntVoltage >= VSHUNT_MAX // Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX // Else // Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage // End If // 8. Computer the Maximum Power // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX // MaximumPower = 3.2 * 32V // MaximumPower = 102.4W // Set multipliers to convert raw current/power values ina219_currentDivider_mA = 10; // Current LSB = 100uA per bit (1000/100 = 10) ina219_powerDivider_mW = 2; // Power LSB = 1mW per bit (2/1) // Set Calibration register to 'Cal' calculated above ina219WriteRegister(INA219_REG_CALIBRATION, 0x1000); // Set Config register to take into account the settings above uint16_t config = INA219_CONFIG_BVOLTAGERANGE_32V | INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; ina219WriteRegister(INA219_REG_CONFIG, config); } /**************************************************************************/ /*! @brief Configures to INA219 to be able to measure up to 32V and 1A of current. Each unit of current corresponds to 40uA, and each unit of power corresponds to 800µW. Counter overflow occurs at 1.3A. @note These calculations assume a 0.1 ohm resistor is present */ /**************************************************************************/ void ina219SetCalibration_32V_1A(void) { // By default we use a pretty huge range for the input voltage, // which probably isn't the most appropriate choice for system // that don't use a lot of power. But all of the calculations // are shown below if you want to change the settings. You will // also need to change any relevant register settings, such as // setting the VBUS_MAX to 16V instead of 32V, etc. // VBUS_MAX = 32V (Assumes 32V, can also be set to 16V) // VSHUNT_MAX = 0.32 (Assumes Gain 8, 320mV, can also be 0.16, 0.08, 0.04) // RSHUNT = 0.1 (Resistor value in ohms) // 1. Determine max possible current // MaxPossible_I = VSHUNT_MAX / RSHUNT // MaxPossible_I = 3.2A // 2. Determine max expected current // MaxExpected_I = 1.0A // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit) // MinimumLSB = MaxExpected_I/32767 // MinimumLSB = 0.0000305 (30.5µA per bit) // MaximumLSB = MaxExpected_I/4096 // MaximumLSB = 0.000244 (244µA per bit) // 4. Choose an LSB between the min and max values // (Preferrably a roundish number close to MinLSB) // CurrentLSB = 0.0000400 (40µA per bit) // 5. Compute the calibration register // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 10240 (0x2800) // 6. Calculate the power LSB // PowerLSB = 20 * CurrentLSB // PowerLSB = 0.0008 (800µW per bit) // 7. Compute the maximum current and shunt voltage values before overflow // // Max_Current = Current_LSB * 32767 // Max_Current = 1.31068A before overflow // // If Max_Current > Max_Possible_I then // Max_Current_Before_Overflow = MaxPossible_I // Else // Max_Current_Before_Overflow = Max_Current // End If // // ... In this case, we're good though since Max_Current is less than MaxPossible_I // // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT // Max_ShuntVoltage = 0.131068V // // If Max_ShuntVoltage >= VSHUNT_MAX // Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX // Else // Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage // End If // 8. Computer the Maximum Power // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX // MaximumPower = 1.31068 * 32V // MaximumPower = 41.94176W // Set multipliers to convert raw current/power values ina219_currentDivider_mA = 25; // Current LSB = 40uA per bit (1000/40 = 25) ina219_powerDivider_mW = 1; // Power LSB = 800µW per bit // Set Calibration register to 'Cal' calculated above ina219WriteRegister(INA219_REG_CALIBRATION, 0x2800); // Set Config register to take into account the settings above uint16_t config = INA219_CONFIG_BVOLTAGERANGE_32V | INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; ina219WriteRegister(INA219_REG_CONFIG, config); } /**************************************************************************/ /*! @brief Configures to INA219 to be able to measure up to 16V and 500mA of current. Each unit of current corresponds to 25uA, and each unit of power corresponds to 500µW. Counter overflow occurs at 800mA. @note These calculations assume a 0.1 ohm resistor is present */ /**************************************************************************/ void ina219SetCalibration_16V_500mA(void) { // VBUS_MAX = 16V // VSHUNT_MAX = 0.08 (Assumes Gain 2, 80mV, can also be 0.32, 0.16, 0.04) // RSHUNT = 0.1 (Resistor value in ohms) // 1. Determine max possible current // MaxPossible_I = VSHUNT_MAX / RSHUNT // MaxPossible_I = 0.8A // 2. Determine max expected current // MaxExpected_I = 0.5A // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit) // MinimumLSB = MaxExpected_I/32767 // MinimumLSB = 0.0000153 (15.3µA per bit) // MaximumLSB = MaxExpected_I/4096 // MaximumLSB = 0.0001221 (122µA per bit) // 4. Choose an LSB between the min and max values // (Preferrably a roundish number close to MinLSB) // CurrentLSB = 0.0000250 (25µA per bit) // 5. Compute the calibration register // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 16384 (0x4000) // 6. Calculate the power LSB // PowerLSB = 20 * CurrentLSB // PowerLSB = 0.0005 (500µW per bit) // 7. Compute the maximum current and shunt voltage values before overflow // // Max_Current = Current_LSB * 32767 // Max_Current = 0.819175 (819 mA before overflow) // // If Max_Current > Max_Possible_I then // Max_Current_Before_Overflow = MaxPossible_I // Else // Max_Current_Before_Overflow = Max_Current // End If // // Max_Current_Before_Overflow = 0.8A // // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT // Max_ShuntVoltage = 0.8 * 0.1 // Max_ShuntVoltage = 0.08V // // If Max_ShuntVoltage >= VSHUNT_MAX // Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX // Else // Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage // End If // 8. Computer the Maximum Power // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX // MaximumPower = 0.8 * 16V // MaximumPower = 12.8W // Set multipliers to convert raw current/power values ina219_currentDivider_mA = 40; // Current LSB = 25uA per bit (1000/25 = 40) ina219_powerDivider_mW = 1; // Power LSB = 500µW per bit // Set Calibration register to 'Cal' calculated above ina219WriteRegister(INA219_REG_CALIBRATION, 0x4000); // Set Config register to take into account the settings above uint16_t config = INA219_CONFIG_BVOLTAGERANGE_16V | INA219_CONFIG_GAIN_2_80MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; ina219WriteRegister(INA219_REG_CONFIG, config); } /**************************************************************************/ /*! @brief Configures to INA219 to be able to measure up to 16V and 200mA of current. Each unit of current corresponds to 10uA, and each unit of power corresponds to 200µW. Counter overflow occurs at 327mA. @note These calculations assume a 0.1 ohm resistor is present */ /**************************************************************************/ void ina219SetCalibration_16V_200mA(void) { // VBUS_MAX = 16V // VSHUNT_MAX = 0.04 (Assumes Gain 1, 40mV, can also be 0.32, 0.16, 0.08) // RSHUNT = 0.1 (Resistor value in ohms) // 1. Determine max possible current // MaxPossible_I = VSHUNT_MAX / RSHUNT // MaxPossible_I = 0.4A // 2. Determine max expected current // MaxExpected_I = 0.2A // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit) // MinimumLSB = MaxExpected_I/32767 // MinimumLSB = 0.000006104 (6.104µA per bit) // MaximumLSB = MaxExpected_I/4096 // MaximumLSB = 0,000048828 (48.82µA per bit) // 4. Choose an LSB between the min and max values // CurrentLSB = 0.000010 (10µA per bit) // 5. Compute the calibration register // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 40960 (0xA000) // 6. Calculate the power LSB // PowerLSB = 20 * CurrentLSB // PowerLSB = 0.0002 (200µW per bit) // 7. Compute the maximum current and shunt voltage values before overflow // // Max_Current = Current_LSB * 32767 // Max_Current = 0.32767 (328 mA before overflow) // // If Max_Current > Max_Possible_I then // Max_Current_Before_Overflow = MaxPossible_I // Else // Max_Current_Before_Overflow = Max_Current // End If // // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT // Max_ShuntVoltage = 0.032767V // // If Max_ShuntVoltage >= VSHUNT_MAX // Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX // Else // Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage // End If // 8. Computer the Maximum Power // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX // MaximumPower = 0.32767 * 16V // MaximumPower = 5.24W // Set multipliers to convert raw current/power values ina219_currentDivider_mA = 100; // Current LSB = 10uA per bit (1000/10 = 100) ina219_powerDivider_mW = 1; // Power LSB = 200µW per bit // Set Calibration register to 'Cal' calculated above ina219WriteRegister(INA219_REG_CALIBRATION, 0xA000); // Set Config register to take into account the settings above uint16_t config = INA219_CONFIG_BVOLTAGERANGE_16V | INA219_CONFIG_GAIN_1_40MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; ina219WriteRegister(INA219_REG_CONFIG, config); } /**************************************************************************/ /*! @brief Initialises the I2C block */ /**************************************************************************/ int ina219Init(void) { int RET = RET_OK; uint16_t ReadBack; // Reset INA219 (set to default values) RET = ina219WriteRegister(INA219_REG_CONFIG, INA219_CONFIG_RESET); ina219Read16(INA219_REG_CONFIG,&ReadBack); if(ReadBack != INA219_CONFIG_RESET_READBACK) { return RET_ERROR; } // Setup chip for 32V and 2A by default // ina219SetCalibration_32V_2A(); ina219SetCalibration_13V_10A(); return RET; } /**************************************************************************/ /*! @brief Gets the shunt voltage (16-bit signed integer, so +-32767) */ /**************************************************************************/ int16_t ina219GetShuntVoltage(void) { uint16_t value; ina219Read16(INA219_REG_SHUNTVOLTAGE, &value); return value; } /**************************************************************************/ /*! @brief Gets the shunt voltage (16-bit signed integer, so +-32767) */ /**************************************************************************/ int16_t ina219GetBusVoltage(void) { uint16_t value; ina219Read16(INA219_REG_BUSVOLTAGE, &value); // Shift to the right 3 to drop CNVR and OVF and then multiply by LSB return (value >> 3) * 4; } /**************************************************************************/ /*! @brief Gets the raw power value (16-bit signed integer, so +-32767) */ /**************************************************************************/ int16_t ina219GetPower(void) { uint16_t value; ina219Read16(INA219_REG_POWER, &value); return value; } /**************************************************************************/ /*! @brief Gets the power value in mW, taking into account the config settings and power LSB */ /**************************************************************************/ int16_t ina219GetPower_mW(void) { uint16_t value; ina219Read16(INA219_REG_POWER, &value); return value / ina219_powerDivider_mW; } /**************************************************************************/ /*! @brief Gets the raw current value (16-bit signed integer, so +-32767) */ /**************************************************************************/ int16_t ina219GetCurrent(void) { uint16_t value; ina219Read16(INA219_REG_CURRENT, &value); return value; } /**************************************************************************/ /*! @brief Gets the current value in mA, taking into account the config settings and current LSB */ /**************************************************************************/ int16_t ina219GetCurrent_mA(void) { uint16_t value; ina219Read16(INA219_REG_CURRENT, &value); return value / ina219_currentDivider_mA; }