0.4.0 INA226

This commit is contained in:
rob tillaart 2022-08-28 07:32:00 +02:00
parent 7d22ccd45d
commit 459bb84ac2
9 changed files with 197 additions and 75 deletions

View File

@ -1,6 +1,6 @@
// FILE: INA226.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.3.0
// VERSION: 0.4.0
// DATE: 2021-05-18
// PURPOSE: Arduino library for INA226 power sensor
// URL: https://github.com/RobTillaart/INA226
@ -10,6 +10,7 @@
#include "INA226.h"
// REGISTERS
#define INA226_CONFIGURATION 0x00
#define INA226_SHUNT_VOLTAGE 0x01
#define INA226_BUS_VOLTAGE 0x02
@ -32,13 +33,13 @@
////////////////////////////////////////////////////////
//
// Constructor
// Constructor
//
INA226::INA226(const uint8_t address, TwoWire *wire)
{
_address = address;
_wire = wire;
// not calibrated values by default.
// not calibrated values by default.
_current_LSB = 0;
_maxCurrent = 0;
_shunt = 0;
@ -79,26 +80,26 @@ uint8_t INA226::getAddress()
////////////////////////////////////////////////////////
//
// Core functions
// Core functions
//
float INA226::getShuntVoltage()
{
int16_t val = _readRegister(INA226_SHUNT_VOLTAGE);
return val * 2.5e-6; // fixed 2.50 uV
return val * 2.5e-6; // fixed 2.50 uV
}
float INA226::getBusVoltage()
{
uint16_t val = _readRegister(INA226_BUS_VOLTAGE);
return val * 1.25e-3; // fixed 1.25 mV
return val * 1.25e-3; // fixed 1.25 mV
}
float INA226::getPower()
{
uint16_t val = _readRegister(INA226_POWER);
return val * 25 * _current_LSB;
return val * 25 * _current_LSB; // fixed 25 Watt
}
@ -111,14 +112,14 @@ float INA226::getCurrent()
////////////////////////////////////////////////////////
//
// Configuration
// Configuration
//
void INA226::reset()
{
uint16_t mask = _readRegister(INA226_CONFIGURATION);
mask |= INA226_CONF_RESET_MASK;
_writeRegister(INA226_CONFIGURATION, mask);
// reset calibration
// reset calibration
_current_LSB = 0;
_maxCurrent = 0;
_shunt = 0;
@ -187,16 +188,18 @@ uint8_t INA226::getShuntVoltageConversionTime()
////////////////////////////////////////////////////////
//
// Calibration
// Calibration
//
bool INA226::setMaxCurrentShunt(float maxCurrent, float shunt, bool normalize)
int INA226::setMaxCurrentShunt(float maxCurrent, float shunt, bool normalize)
{
// #define printdebug true
uint32_t calib = 0;
uint32_t factor = 1;
// #define printdebug true
if ((maxCurrent > 20) || (maxCurrent < 0.001)) return false;
if (shunt < 0.001) return false;
// fix #16 - datasheet 6.5 Electrical Characteristics
// rounded value to 80 mV
float shuntVoltage = abs(maxCurrent * shunt);
if (shuntVoltage > 0.080) return INA226_ERR_SHUNTVOLTAGE_HIGH;
if (maxCurrent < 0.001) return INA226_ERR_MAXCURRENT_LOW;
if (shunt < 0.001) return INA226_ERR_SHUNT_LOW;
_current_LSB = maxCurrent * 3.0517578125e-5; // maxCurrent / 32768;
@ -209,8 +212,11 @@ bool INA226::setMaxCurrentShunt(float maxCurrent, float shunt, bool normalize)
Serial.println(" uA / bit");
#endif
// normalize the LSB to a round number
// LSB will increase
uint32_t calib = 0;
uint32_t factor = 1;
// normalize the LSB to a round number
// LSB will increase
if (normalize)
{
calib = round(0.00512 / (_current_LSB * shunt));
@ -222,7 +228,7 @@ bool INA226::setMaxCurrentShunt(float maxCurrent, float shunt, bool normalize)
Serial.println(" uA / bit");
#endif
// auto scale current_LSB
// auto scale current_LSB
factor = 1;
while (_current_LSB < 1)
{
@ -232,7 +238,7 @@ bool INA226::setMaxCurrentShunt(float maxCurrent, float shunt, bool normalize)
_current_LSB = 1.0 / factor;
}
// auto scale calibration
// auto scale calibration
calib = round(0.00512 / (_current_LSB * shunt));
while (calib > 65535)
{
@ -241,7 +247,7 @@ bool INA226::setMaxCurrentShunt(float maxCurrent, float shunt, bool normalize)
}
_writeRegister(INA226_CALIBRATION, calib);
_maxCurrent = _current_LSB * 32768.0;
_maxCurrent = _current_LSB * 32768;
_shunt = shunt;
#ifdef printdebug
@ -258,15 +264,18 @@ bool INA226::setMaxCurrentShunt(float maxCurrent, float shunt, bool normalize)
Serial.print("Shunt:\t");
Serial.print(_shunt, 8);
Serial.println(" ohm");
Serial.print("ShuntV:\t");
Serial.print(shuntVoltage, 4);
Serial.println(" Volt");
#endif
return true;
return INA226_ERR_NONE;
}
////////////////////////////////////////////////////////
//
// operating mode
// operating mode
//
bool INA226::setMode(uint8_t mode)
{
@ -332,7 +341,7 @@ uint16_t INA226::getDieID()
////////////////////////////////////////////////////////
//
// PRIVATE
// PRIVATE
//
uint16_t INA226::_readRegister(uint8_t reg)
{

View File

@ -1,7 +1,7 @@
#pragma once
// FILE: INA226.h
// AUTHOR: Rob Tillaart
// VERSION: 0.3.0
// VERSION: 0.4.0
// DATE: 2021-05-18
// PURPOSE: Arduino library for INA226 power sensor
// URL: https://github.com/RobTillaart/INA226
@ -14,10 +14,10 @@
#include "Wire.h"
#define INA226_LIB_VERSION (F("0.3.0"))
#define INA226_LIB_VERSION (F("0.4.0"))
// set by setAlertRegister
// set by setAlertRegister
#define INA226_SHUNT_OVER_VOLTAGE 0x8000
#define INA226_SHUNT_UNDER_VOLTAGE 0x4000
#define INA226_BUS_OVER_VOLTAGE 0x2000
@ -25,18 +25,25 @@
#define INA226_POWER_OVER_LIMIT 0x0800
#define INA226_CONVERSION_READY 0x0400
// returned by getAlertFlag
// returned by getAlertFlag
#define INA226_ALERT_FUNCTION_FLAG 0x0010
#define INA226_CONVERSION_READY_FLAG 0x0008
#define INA226_MATH_OVERFLOW_FLAG 0x0004
#define INA226_ALERT_POLARITY_FLAG 0x0002
#define INA226_ALERT_LATCH_ENABLE_FLAG 0x0001
// returned by setMaxCurrentShunt
#define INA226_ERR_NONE 0x0000
#define INA226_ERR_SHUNTVOLTAGE_HIGH 0x8000
#define INA226_ERR_MAXCURRENT_LOW 0x8001
#define INA226_ERR_SHUNT_LOW 0x8002
class INA226
{
public:
// address between 0x40 and 0x4F
// address between 0x40 and 0x4F
explicit INA226(const uint8_t address, TwoWire *wire = &Wire);
#if defined (ESP8266) || defined(ESP32)
@ -47,14 +54,14 @@ public:
uint8_t getAddress();
// Core functions
// Core functions
float getBusVoltage();
float getShuntVoltage();
float getCurrent();
float getPower();
// scale helpers
// Scale helpers
float getBusVoltage_mV() { return getBusVoltage() * 1e3; };
float getShuntVoltage_mV() { return getShuntVoltage() * 1e3; };
float getCurrent_mA() { return getCurrent() * 1e3; };
@ -64,7 +71,7 @@ public:
float getPower_uW() { return getPower() * 1e6; };
// Configuration
// Configuration
void reset();
bool setAverage(uint8_t avg = 0);
uint8_t getAverage();
@ -74,16 +81,17 @@ public:
uint8_t getShuntVoltageConversionTime();
// Calibration
// mandatory to set these!
// maxCurrent = 0.001 .. 20
// shunt >= 0.001
bool setMaxCurrentShunt(float macCurrent = 20.0,
// Calibration
// mandatory to set these!
// shunt * maxCurrent < 81 mV
// maxCurrent >= 0.001
// shunt >= 0.001
int setMaxCurrentShunt(float macCurrent = 20.0,
float shunt = 0.002,
bool normalize = true);
bool isCalibrated() { return _current_LSB != 0.0; };
// these return zero if not calibrated!
// These functions return zero if not calibrated!
float getCurrentLSB() { return _current_LSB; };
float getCurrentLSB_mA() { return _current_LSB * 1e3; };
float getCurrentLSB_uA() { return _current_LSB * 1e6; };
@ -91,7 +99,7 @@ public:
float getMaxCurrent() { return _maxCurrent; };
// Operating mode
// Operating mode
bool setMode(uint8_t mode = 7);
uint8_t getMode();
bool shutDown() { return setMode(0); };
@ -103,24 +111,24 @@ public:
bool setModeShuntBusContinuous() { return setMode(7); }; // default.
// Alert
// - separate functions per flag?
// - what is a reasonable limit?
// - which units to define a limit per mask ?
// same as voltage registers ?
// - how to test
// Alert
// - separate functions per flag?
// - what is a reasonable limit?
// - which units to define a limit per mask ?
// same as voltage registers ?
// - how to test
void setAlertRegister(uint16_t mask);
uint16_t getAlertFlag();
void setAlertLimit(uint16_t limit);
uint16_t getAlertLimit();
// Meta information
// Meta information
uint16_t getManufacturerID(); // should return 0x5449
uint16_t getDieID(); // should return 0x2260
// DEBUG
// DEBUG
uint16_t getRegister(uint8_t reg) { return _readRegister(reg); };

View File

@ -155,8 +155,10 @@ Note the value returned is not a unit of time.
| 6 | 4.2 ms |
| 7 | 8.3 ms |
Note: times are typical, check datasheet for operational range.
(max is ~10% higher)
Note that total conversion time can take up to 1024 \* 8.3 ms ~ 10 seconds.
Note: total conversion time can take up to 1024 \* 8.3 ms ~ 10 seconds.
### Calibration
@ -167,10 +169,8 @@ Calibration is mandatory to get **getCurrent()** and **getPower()** to work.
- **bool setMaxCurrentShunt(float ampere = 20.0, float ohm = 0.002, bool normalize = true)**
set the calibration register based upon the shunt and the max ampere.
From this the LSB is derived.
Note the function will round up the LSB to nearest round value by default.
This may cause loss of precision. The function may force normalization if underflow detected.
The user **must** check the return value == true, otherwise the calibration register is **not** set.
From this the LSB is derived.
The function may force normalization if underflow is detected.
- **bool isCalibrated()** returns true if CurrentLSB has been calculated by **setMaxCurrentShunt()**.
- **float getCurrentLSB()** returns the LSB in Ampere == precision of the calibration.
- **float getCurrentLSB_mA()** returns the LSB in milliampere.
@ -178,7 +178,26 @@ The user **must** check the return value == true, otherwise the calibration regi
- **float getShunt()** returns the value set for the shunt.
- **float getMaxCurrent()** returns the value for the maxCurrent which can be corrected.
To print these values one might use https://github.com/RobTillaart/printHelpers
To print these values in scientific notation use https://github.com/RobTillaart/printHelpers
#### About normalization
**setMaxCurrentShunt()** will round the LSB to nearest round value (typical 0.001) by default (normalize == true).
- The user **must** check the return value == 0x000, otherwise the calibration register is **not** set.
- Normalization typically gives smaller steps => improve precision
- Normalization can cause that the maxCurrent passed cannot be reached any more.
Solution is not to normalize if this max range is needed.
#### Error codes setMaxCurrentShunt
| descriptive name error | value | meaning |
|:-----------------------------|-------:|:--------|
| INA226_ERR_NONE | 0x0000 | OK
| INA226_ERR_SHUNTVOLTAGE_HIGH | 0x8000 | maxCurrent \* shunt > 80 mV
| INA226_ERR_MAXCURRENT_LOW | 0x8001 | maxCurrent < 0.001
| INA226_ERR_SHUNT_LOW | 0x8002 | shunt < 0.001
### Operating mode
@ -250,25 +269,42 @@ See examples..
## Future
- test different loads (low edge)
- expand unit tests possible?
- test examples
- investigate alert functions / interface
- disconnected load,
#### Should
- test different loads (low edge).
- test examples.
- investigate alert functions / interface.
- disconnected load.
- can it be recognized? => current drop?
- **lastError()** do we need this
- if **BVCT SVCT** is set to 6 or 7
- does the long timing affects RTOS? ==> yield()
- cache configuration ? ==> 2 bytes
- what is gained? updates are faster.
- 15 times used,
- can the calibration math be optimized
#### Could
- can the calibration math be optimized?
- integer only?
- less iterations?
- local var for current_lsb?
- ??
- make defines of "magic" numbers
- const floats (most used only once)
#### Won't
- **lastError()** do we need this?
- no
- if **BVCT SVCT** is set to 6 or 7
- does the long timing affects RTOS? ==> yield()
- wait for issue
- expand unit tests possible?
- need virtual device => too much work
- cache configuration ? ==> 2 bytes.
- what is gained? updates are faster. footprint code?
- how often operational?
- 15 times used..
## Resources
- [TI - INA226 Details](https://www.ti.com/product/INA226#params)
- [TI - INA226 datasheet](https://www.ti.com/document-viewer/INA226/datasheet)

View File

@ -0,0 +1,45 @@
//
// FILE: INA226_compare_normalize.ino
// AUTHOR: Rob Tillaart
// PURPOSE: show difference in normalize flag
// URL: https://github.com/RobTillaart/INA226
#include "INA226.h"
#include "Wire.h"
INA226 INA(0x40);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
if (!INA.begin() )
{
Serial.println("could not connect. Fix and Reboot");
}
INA.setMaxCurrentShunt(79, 0.001, true);
Serial.print("\nNormalize = true\n");
Serial.print("MaxCurrent:\t");
Serial.println(INA.getMaxCurrent(), 3);
Serial.print("currentLSB:\t");
Serial.println(INA.getCurrentLSB(), 5);
INA.setMaxCurrentShunt(79, 0.001, false);
Serial.print("\nNormalize = false\n");
Serial.print("MaxCurrent:\t");
Serial.println(INA.getMaxCurrent(), 3);
Serial.print("currentLSB:\t");
Serial.println(INA.getCurrentLSB(), 5);
}
void loop()
{
}
// -- END OF FILE --

View File

@ -74,3 +74,8 @@ INA226_MATH_OVERFLOW_FLAG LITERAL1
INA226_ALERT_POLARITY_FLAG LITERAL1
INA226_ALERT_LATCH_ENABLE_FLAG LITERAL1
INA226_ERR_NONE LITERAL1
INA226_ERR_SHUNTVOLTAGE_HIGH LITERAL1
INA226_ERR_MAXCURRENT_LOW LITERAL1
INA226_ERR_SHUNT_LOW LITERAL1

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/INA226.git"
},
"version": "0.3.0",
"version": "0.4.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=INA226
version=0.3.0
version=0.4.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for INA226 power sensor

View File

@ -3,6 +3,14 @@
## Release notes
## 0.4.0 2022-08-XX
- fix #16 - change error to warning for max current
setMaxCurrentShunt now returns an int indicating OK == 0
otherwise error. See INA226.h for error codes.
- updated readme.md
- updated unit tests (error codes)
## 0.3.0 2022-07-01
- fix #14 RESET FLAG
- add getAddress()

View File

@ -73,6 +73,11 @@ unittest(test_constants)
assertEqual(0x0004, INA226_MATH_OVERFLOW_FLAG);
assertEqual(0x0002, INA226_ALERT_POLARITY_FLAG);
assertEqual(0x0001, INA226_ALERT_LATCH_ENABLE_FLAG);
assertEqual(0x0000, INA226_ERR_NONE);
assertEqual(0x8000, INA226_ERR_SHUNTVOLTAGE_HIGH);
assertEqual(0x8001, INA226_ERR_MAXCURRENT_LOW);
assertEqual(0x8002, INA226_ERR_SHUNT_LOW);
}
@ -114,14 +119,20 @@ unittest(test_calibration)
INA226 INA(0x40);
// assertTrue(INA.begin());
// only errors can be tested
assertFalse(INA.setMaxCurrentShunt(30));
assertFalse(INA.setMaxCurrentShunt(0.0009));
assertFalse(INA.setMaxCurrentShunt(0));
assertFalse(INA.setMaxCurrentShunt(-1));
assertEqual(INA226_ERR_NONE, INA.setMaxCurrentShunt(30, 0.002));
assertEqual(INA226_ERR_NONE, INA.setMaxCurrentShunt(1, 0.05));
assertEqual(INA226_ERR_NONE, INA.setMaxCurrentShunt(1, 0.080));
assertFalse(INA.setMaxCurrentShunt(10, 0));
assertFalse(INA.setMaxCurrentShunt(10, 0.0009));
assertEqual(INA226_ERR_SHUNTVOLTAGE_HIGH, INA.setMaxCurrentShunt(80.1, 0.001));
assertEqual(INA226_ERR_SHUNTVOLTAGE_HIGH, INA.setMaxCurrentShunt(40.1, 0.002));
assertEqual(INA226_ERR_SHUNTVOLTAGE_HIGH, INA.setMaxCurrentShunt(20.1, 0.004));
assertEqual(INA226_ERR_SHUNTVOLTAGE_HIGH, INA.setMaxCurrentShunt(1.1, 0.080));
assertEqual(INA226_ERR_MAXCURRENT_LOW, INA.setMaxCurrentShunt(0.0009));
assertEqual(INA226_ERR_MAXCURRENT_LOW, INA.setMaxCurrentShunt(0));
assertEqual(INA226_ERR_MAXCURRENT_LOW, INA.setMaxCurrentShunt(-1));
assertEqual(INA226_ERR_SHUNT_LOW, INA.setMaxCurrentShunt(10, 0));
assertEqual(INA226_ERR_SHUNT_LOW, INA.setMaxCurrentShunt(10, 0.0009));
}