In this article, we are going to learn INA219 interfacing with STM32 microcontroller. For STM32, CubeMX IDE will be used. Using a suitable Shunt resistor we can configure the sensor for the different current ranges. Here in this article, we will learn in detail. So let’s started.
Disclaimer: Electricity is always dangerous. Proper skill is required to work with electricity. Do work at your own risk. The author will not be responsible for any misuse or harmful act or any mistake you make. The contents of this website are unique and copyright protected. Kindly don’t do any nonsensical act copying and claiming it as yours. Most of the articles published here are kept open-source to help you. Take the knowledge for free and use it, but if you are interested you can buy the ready resources offered here. If you need any help or guidance feel free to comment below, the author will try to help you. Also, there can be affiliation links in the article. Which will not affect you anyway, but allows the author with some commission. So please don’t take it otherwise. Thanks.
INA219:
The INA219 is a current shunt and power monitor with an I2C- or SMBus-compatible interface. The device monitors both shunt voltage drop and bus supply voltage, with programmable conversion times and filtering
When you need to monitor the voltage, current & power, or Energy of a circuit output or any line or point. then INA219 is a good choice for your embedded system. Using a suitable Shunt resistor we can configure the sensor for the different current ranges. Here in this article, we will learn in detail.
First thing first, INA219 works with the I2C protocol only. So whenever you are planning to interface this IC, you must have this feature in your microcontroller. Most of the microcontrollers nowadays have this feature. And there are lots of examples for Arduino. But there were very few for STM32. So in this project, I’ll explain for STM32 only.
Configuration of INA219:
Before we use it, we have to understand the right configuration of the IC. According to the datasheet, we have to configure the IC like the circuit posted above. Now, there are other parameters, we need to keep in mind.
Shunt voltage: 40mV, 80mV, 160mV & 320mV: This is not what you need to fix, but this is a range of the scale of the measurement. That means you can calculate your shunt resistor for your full A range (say 8A) and the maximum voltage at your rated current should be within the ranges noted here.
Voltage range: 16V or 32V: There are two ranges of the sensing voltage. And you need to select the right one for your purpose. This means the IC will configure the internal registers for either 16V or 32V. So, you have to select the range carefully.
Configuring CubeMx:
You can use different STM ICs but here, I’m using STM32F407VET6 for my purpose. First, initialize the basic settings like RCC and SYS, and then from Connectivity, select the I2C option.
Then set up the clock and save the project. Now open the main.c from Core>>Scr.
Including INA219 library:
Now, you have to include the .h files in the user include section. Create these two files and save them in the project folder (Core>>Inc (.h) & >>Scr(.c)).
Here in this code, I have multiple INA219 ICs onboard. So, I configured it in a way so that I can read/write to anyone I need to.
ina219.h file:
#ifndef _LIB_ADAFRUIT_INA219_ #define _LIB_ADAFRUIT_INA219_ /**************************************************************************/ /*! @brief default I2C address */ /**************************************************************************/ #define INA219_ADDRESS (0x40) // 1000000 (A0+A1=GND) /**************************************************************************/ /*! @brief read */ /**************************************************************************/ #define INA219_READ (0x01) /*========================================================================= CONFIG REGISTER (R/W) -----------------------------------------------------------------------*/ /**************************************************************************/ /*! @brief config register address */ /**************************************************************************/ #define INA219_REG_CONFIG (0x00) /*---------------------------------------------------------------------*/ /**************************************************************************/ /*! @brief reset bit */ /**************************************************************************/ #define INA219_CONFIG_RESET (0x8000) // Reset Bit /**************************************************************************/ /*! @brief mask for bus voltage range */ /**************************************************************************/ #define INA219_CONFIG_BVOLTAGERANGE_MASK (0x2000) // Bus Voltage Range Mask /**************************************************************************/ /*! @brief bus voltage range values */ /**************************************************************************/ enum { INA219_CONFIG_BVOLTAGERANGE_16V = (0x0000), // 0-16V Range INA219_CONFIG_BVOLTAGERANGE_32V = (0x2000), // 0-32V Range }; /**************************************************************************/ /*! @brief mask for gain bits */ /**************************************************************************/ #define INA219_CONFIG_GAIN_MASK (0x1800) // Gain Mask /**************************************************************************/ /*! @brief values for gain bits */ /**************************************************************************/ enum { INA219_CONFIG_GAIN_1_40MV = (0x0000), // Gain 1, 40mV Range INA219_CONFIG_GAIN_2_80MV = (0x0800), // Gain 2, 80mV Range INA219_CONFIG_GAIN_4_160MV = (0x1000), // Gain 4, 160mV Range INA219_CONFIG_GAIN_8_320MV = (0x1800), // Gain 8, 320mV Range }; /**************************************************************************/ /*! @brief mask for bus ADC resolution bits */ /**************************************************************************/ #define INA219_CONFIG_BADCRES_MASK (0x0780) // Bus ADC Resolution Mask /**************************************************************************/ /*! @brief values for bus ADC resolution */ /**************************************************************************/ enum { INA219_CONFIG_BADCRES_9BIT = (0x0000), // 9-bit bus res = 0..511 INA219_CONFIG_BADCRES_10BIT = (0x0080), // 10-bit bus res = 0..1023 INA219_CONFIG_BADCRES_11BIT = (0x0100), // 11-bit bus res = 0..2047 INA219_CONFIG_BADCRES_12BIT = (0x0180), // 12-bit bus res = 0..4097 }; /**************************************************************************/ /*! @brief mask for shunt ADC resolution bits */ /**************************************************************************/ #define INA219_CONFIG_SADCRES_MASK (0x0078) // Shunt ADC Resolution and Averaging Mask /**************************************************************************/ /*! @brief values for shunt ADC resolution */ /**************************************************************************/ enum { INA219_CONFIG_SADCRES_9BIT_1S_84US = (0x0000), // 1 x 9-bit shunt sample INA219_CONFIG_SADCRES_10BIT_1S_148US = (0x0008), // 1 x 10-bit shunt sample INA219_CONFIG_SADCRES_11BIT_1S_276US = (0x0010), // 1 x 11-bit shunt sample INA219_CONFIG_SADCRES_12BIT_1S_532US = (0x0018), // 1 x 12-bit shunt sample INA219_CONFIG_SADCRES_12BIT_2S_1060US = (0x0048),// 2 x 12-bit shunt samples averaged together INA219_CONFIG_SADCRES_12BIT_4S_2130US = (0x0050), // 4 x 12-bit shunt samples averaged together INA219_CONFIG_SADCRES_12BIT_8S_4260US = (0x0058), // 8 x 12-bit shunt samples averaged together INA219_CONFIG_SADCRES_12BIT_16S_8510US = (0x0060), // 16 x 12-bit shunt samples averaged together INA219_CONFIG_SADCRES_12BIT_32S_17MS = (0x0068), // 32 x 12-bit shunt samples averaged together INA219_CONFIG_SADCRES_12BIT_64S_34MS = (0x0070), // 64 x 12-bit shunt samples averaged together INA219_CONFIG_SADCRES_12BIT_128S_69MS = (0x0078), // 128 x 12-bit shunt samples averaged together }; /**************************************************************************/ /*! @brief mask for operating mode bits */ /**************************************************************************/ #define INA219_CONFIG_MODE_MASK (0x0007) // Operating Mode Mask /**************************************************************************/ /*! @brief values for operating mode */ /**************************************************************************/ enum { INA219_CONFIG_MODE_POWERDOWN = (0x0000), INA219_CONFIG_MODE_SVOLT_TRIGGERED = (0x0001), INA219_CONFIG_MODE_BVOLT_TRIGGERED = (0x0002), INA219_CONFIG_MODE_SANDBVOLT_TRIGGERED = (0x0003), INA219_CONFIG_MODE_ADCOFF = (0x0004), INA219_CONFIG_MODE_SVOLT_CONTINUOUS = (0x0005), INA219_CONFIG_MODE_BVOLT_CONTINUOUS = (0x0006), INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS = (0x0007), }; /*=========================================================================*/ /**************************************************************************/ /*! @brief shunt voltage register */ /**************************************************************************/ #define INA219_REG_SHUNTVOLTAGE (0x01) /*=========================================================================*/ /**************************************************************************/ /*! @brief bus voltage register */ /**************************************************************************/ #define INA219_REG_BUSVOLTAGE (0x02) /*=========================================================================*/ /**************************************************************************/ /*! @brief power register */ /**************************************************************************/ #define INA219_REG_POWER (0x03) /*=========================================================================*/ /**************************************************************************/ /*! @brief current register */ /**************************************************************************/ #define INA219_REG_CURRENT (0x04) /*=========================================================================*/ /**************************************************************************/ /*! @brief calibration register */ /**************************************************************************/ #define INA219_REG_CALIBRATION (0x05) /*=========================================================================*/ /**************************************************************************/ /*! @brief Class that stores state and functions for interacting with INA219 current/power monitor IC */ /**************************************************************************/ void setCalibration_32V_2A(void); void setCalibration_32V_1A(void); void setCalibration_16V_400mA(void); float getBusVoltage_V(void); float getShuntVoltage_mV(void); float getCurrent_mA(void); float getPower_mW(void); void wireWriteRegister(uint8_t reg, uint16_t value); void wireReadRegister(uint8_t reg, uint16_t *value); int16_t getBusVoltage_raw(void); int16_t getShuntVoltage_raw(void); int16_t getCurrent_raw(void); int16_t getPower_raw(void); int contMeasureInit(uint8_t reg); float convertMeasure(int rawValue); int contMeasureUpdate(void); int getNSamples(void); #endif
ina219.c file:
#include <stdint.h> #include <string.h> #include "ina219.h" #include "stm32F4xx_hal.h" extern I2C_HandleTypeDef hi2c2; // 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; uint32_t ina219_powerMultiplier_mW; uint32_t ina219_calValue; #define BUFFERLEN 4096 int16_t contBuffer[BUFFERLEN]; unsigned int bufferPos; /* Holds currently type of continuous measurement (voltage, power, current). * Used to avoid floating point numbers prolification */ int measureType; /**************************************************************************/ /*! @brief Sends a single command byte over I2C */ /**************************************************************************/ uint8_t adrs_219 = 0x41; // you can call this from main function when necessary especially when using multiple INA219 on board void wireWriteRegister (uint8_t reg, uint16_t value) { uint8_t i2c_temp[2]; i2c_temp[0] = value>>8; i2c_temp[1] = value; HAL_I2C_Mem_Write(&hi2c2, adrs_219<<1, (uint16_t)reg, 1, i2c_temp, 2, 0xffffffff); HAL_Delay(1); } /**************************************************************************/ /*! @brief Reads a 16 bit values over I2C */ /**************************************************************************/ void wireReadRegister(uint8_t reg, uint16_t *value) { uint8_t i2c_temp[2]; HAL_I2C_Mem_Read(&hi2c2, adrs_219<<1, (uint16_t)reg, 1,i2c_temp, 2, 0xffffffff); HAL_Delay(1); *value = ((uint16_t)i2c_temp[0]<<8 )|(uint16_t)i2c_temp[1]; } /**************************************************************************/ /*! @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 setCalibration_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 (61uA per bit) // MaximumLSB = MaxExpected_I/4096 // MaximumLSB = 0,000488 (488uA per bit) // 4. Choose an LSB between the min and max values // (Preferrably a roundish number close to MinLSB) // CurrentLSB = 0.0001 (100uA per bit) // 5. Compute the calibration register // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 4096 (0x1000) // ina219_calValue = 4096; ina219_calValue = 5120; // Rsh=0.04R, // 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. Compute 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 = 6; // Current LSB = 100uA per bit (1000/100 = 10) ina219_powerMultiplier_mW = 3; // Power LSB = 1mW per bit (2/1) // Set Calibration register to 'Cal' calculated above wireWriteRegister(INA219_REG_CALIBRATION, ina219_calValue); // 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; wireWriteRegister(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 setCalibration_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) ina219_calValue = 10240; // 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. Compute 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_powerMultiplier_mW = 1; // Power LSB = 800mW per bit // Set Calibration register to 'Cal' calculated above wireWriteRegister(INA219_REG_CALIBRATION, ina219_calValue); // 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; wireWriteRegister(INA219_REG_CONFIG, config); } /**************************************************************************/ /*! @brief set device to alibration which uses the highest precision for current measurement (0.1mA), at the expense of only supporting 16V at 400mA max. */ /**************************************************************************/ void setCalibration_16V_400mA(void) { // Calibration which uses the highest precision for // current measurement (0.1mA), at the expense of // only supporting 16V at 400mA max. // VBUS_MAX = 16V // VSHUNT_MAX = 0.04 (Assumes Gain 1, 40mV) // 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.4A // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit) // MinimumLSB = MaxExpected_I/32767 // MinimumLSB = 0.0000122 (12uA per bit) // MaximumLSB = MaxExpected_I/4096 // MaximumLSB = 0.0000977 (98uA per bit) // 4. Choose an LSB between the min and max values // (Preferrably a roundish number close to MinLSB) // CurrentLSB = 0.00005 (50uA per bit) // 5. Compute the calibration register // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 8192 (0x2000) ina219_calValue = 8192; // 6. Calculate the power LSB // PowerLSB = 20 * CurrentLSB // PowerLSB = 0.001 (1mW per bit) // 7. Compute the maximum current and shunt voltage values before overflow // // Max_Current = Current_LSB * 32767 // Max_Current = 1.63835A 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 = MaxPossible_I // Max_Current_Before_Overflow = 0.4 // // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT // Max_ShuntVoltage = 0.04V // // If Max_ShuntVoltage >= VSHUNT_MAX // Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX // Else // Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage // End If // // Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX // Max_ShuntVoltage_Before_Overflow = 0.04V // 8. Compute the Maximum Power // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX // MaximumPower = 0.4 * 16V // MaximumPower = 6.4W // Set multipliers to convert raw current/power values ina219_currentDivider_mA = 20; // Current LSB = 50uA per bit (1000/50 = 20) ina219_powerMultiplier_mW = 1; // Power LSB = 1mW per bit // Set Calibration register to 'Cal' calculated above wireWriteRegister(INA219_REG_CALIBRATION, ina219_calValue); // 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; wireWriteRegister(INA219_REG_CONFIG, config); } /**************************************************************************/ /*! @brief Gets the raw bus voltage (16-bit signed integer, so +-32767) @return the raw bus voltage reading */ /**************************************************************************/ int16_t getBusVoltage_raw() { uint16_t value; wireReadRegister(INA219_REG_BUSVOLTAGE, &value); // Shift to the right 3 to drop CNVR and OVF and multiply by LSB return (int16_t) ((value >> 3) * 4); } /**************************************************************************/ /*! @brief Gets the raw shunt voltage (16-bit signed integer, so +-32767) @return the raw shunt voltage reading */ /**************************************************************************/ int16_t getShuntVoltage_raw() { uint16_t value; wireReadRegister(INA219_REG_SHUNTVOLTAGE, &value); return (int16_t) value; } /**************************************************************************/ /*! @brief Gets the raw current value (16-bit signed integer, so +-32767) @return the raw current reading */ /**************************************************************************/ int16_t getCurrent_raw() { uint16_t value; // Sometimes a sharp load will reset the INA219, which will // reset the cal register, meaning CURRENT and POWER will // not be available ... avoid this by always setting a cal // value even if it's an unfortunate extra step wireWriteRegister(INA219_REG_CALIBRATION, ina219_calValue); // Now we can safely read the CURRENT register! wireReadRegister(INA219_REG_CURRENT, &value); return (int16_t) value; } /**************************************************************************/ /*! @brief Gets the raw power value (16-bit signed integer, so +-32767) @return raw power reading */ /**************************************************************************/ int16_t getPower_raw() { uint16_t value; // Sometimes a sharp load will reset the INA219, which will // reset the cal register, meaning CURRENT and POWER will // not be available ... avoid this by always setting a cal // value even if it's an unfortunate extra step wireWriteRegister(INA219_REG_CALIBRATION, ina219_calValue); // Now we can safely read the POWER register! wireReadRegister(INA219_REG_POWER, &value); return (int16_t) value; } /**************************************************************************/ /*! @brief Gets the shunt voltage in mV (so +-327mV) @return the shunt voltage converted to millivolts */ /**************************************************************************/ float getShuntVoltage_mV() { int16_t value; value = getShuntVoltage_raw(); return value * 0.01; } /**************************************************************************/ /*! @brief Gets the shunt voltage in volts @return the bus voltage converted to volts */ /**************************************************************************/ float getBusVoltage_V() { int16_t value = getBusVoltage_raw(); return value * 0.001; } /**************************************************************************/ /*! @brief Gets the current value in mA, taking into account the config settings and current LSB @return the current reading convereted to milliamps */ /**************************************************************************/ float getCurrent_mA() { float valueDec = getCurrent_raw(); valueDec /= ina219_currentDivider_mA; return valueDec; } /**************************************************************************/ /*! @brief Gets the power value in mW, taking into account the config settings and current LSB @return power reading converted to milliwatts */ /**************************************************************************/ float getPower_mW() { float valueDec = getPower_raw(); valueDec *= ina219_powerMultiplier_mW; return valueDec; } /* Initialize continuous measurement. * Sets register pointer to desired measurement type (current, bus voltage, shunt voltage, power). * @param reg Device register */ int contMeasureInit(uint8_t reg) { HAL_StatusTypeDef status; measureType = reg; /* Set register pointer to desired register */ status = HAL_I2C_Master_Transmit(&hi2c2, adrs_219 << 1, ®, 1, 0xffffffff); if (status != HAL_OK) while (1) ; bufferPos = 0; memset(contBuffer, 0, sizeof(contBuffer)); return 0; } /* Updates measurement buffers. To be called periodically by application. * @retval Current buffer position */ int contMeasureUpdate(void) { HAL_StatusTypeDef status; uint8_t measure[2]; status = HAL_I2C_Master_Receive(&hi2c2, adrs_219 << 1, (uint8_t*) &measure, 2, 0xffffffff); if (status != HAL_OK) while (1) ; /* Change endinanness */ if (bufferPos < BUFFERLEN) contBuffer[bufferPos++] = ina219_powerMultiplier_mW * (((uint16_t) measure[0] << 8) | (uint16_t) measure[1]); return bufferPos; } /** Converts a given integer raw measure in actual floating point one * @param rawValue Raw value as obtained from continuous measurement buffer * @retval Actual floating point value of given raw measurement */ float convertMeasure(int rawValue) { float val = rawValue; switch (measureType) { case INA219_REG_SHUNTVOLTAGE: val *= 0.01f; break; case INA219_REG_BUSVOLTAGE: val *= 0.001f; break; case INA219_REG_POWER: val *= ina219_powerMultiplier_mW; break; case INA219_REG_CURRENT: val /= ina219_currentDivider_mA; break; default: val = -1; } return val; } /** Returns the number of samples that have been acquired during continous measurement * @retval Numner of samples acquired */ int getNSamples(void) { return bufferPos; }
Now in the main section, put this (with your other codes if needed).
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "ina219.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c2; DMA_HandleTypeDef hdma_i2c2_rx; /* USER CODE BEGIN PV */ extern int adrs_219; /* USER CODE END PV */ /* USER CODE BEGIN 0 */ uint32_t ina_curr = 0; uint32_t ina_vol = 0; uint32_t ina_pwr = 0; uint8_t cnt = 0; uint8_t channel=0; /* USER CODE END 0 */
Now, in the main(void) section pass these to configure the configuration of your INA for the first time.
/* USER CODE BEGIN 2 */ adrs_219 = 0x40; setCalibration_32V_2A(); adrs_219 = 0x41; setCalibration_32V_2A(); adrs_219 = 0x44; setCalibration_32V_2A(); /* USER CODE END 2 */
Here in my hardware, there were 3 INA219 ICs so I configured them one by one. If you have only one, you can configure only one. But the address is important here. I think you know that the INA219 address is 7-bit size.
Now in the while(1) loop, you can read from any one of your IC.
adrs_219 = 0x40; ina_curr = getCurrent_mA(); ina_vol = getBusVoltage_V(); ina_pwr = getPower_mW();
or
adrs_219 = 0x41; ina_curr = getCurrent_mA(); ina_vol = getBusVoltage_V(); ina_pwr = getPower_mW();
And it’s all done. Now, let’s debug the code if it is error-free. My one was error free. so do yours should be.
Debugging:
I had a big hardware here with this sensor IC, which I debugged with connecting loads and supplies. For this, I use STLink programmer v2. You can use one you have or collecting one.
Conclusion:
I hope now you can use this code/library for your own project with STM32. The explanation is given in the comments section of each line where necessary. Hope this will help you. Thanks.
You can also read my other articles here:
- Logging Data to Excel from Arduino
- STM32 as USB Device
- Detecting USB Port or devices in Pi
- Make an MPPT Solar charge Controller with Synchronous Buck Converter
- Pure sine wave inverter using PIC16F76
- BIG Font display with 16×2 LCD
- Arduino based egg incubator
- How to make an AC dimmer with PIC12F675 and TRIAC
For Professional Designs or Help:
Also let me know what you want to get as the next article, comment below!
2 Comments
Zach · 11/01/2023 at 11:54 pm
Great tutorial, but unfortunately I didn’t get much clarification on the multiple INA219’s across I2C.
MKDas · 12/01/2023 at 11:27 am
simple, different address.