0.2.0 INA3221

This commit is contained in:
Rob Tillaart 2024-04-17 09:31:34 +02:00
parent 560ad24b5e
commit 27d550815d
10 changed files with 366 additions and 88 deletions

View File

@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.0] - 2024-04-15
- Fix #2, reimplement several functions
- makes 0.1.0 obsolete
- still needs more testing
- update readme.md
- add **INA3221_tests.ino** example
- sort of unit test.
## [0.1.0] - 2024-02-05
- initial version.

View File

@ -1,6 +1,6 @@
// FILE: INA3221.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// VERSION: 0.2.0
// DATE: 2024-02-05
// PURPOSE: Arduino library for the I2C INA3221 3 channel voltage and current sensor.
// URL: https://github.com/RobTillaart/INA3221_RT
@ -128,56 +128,58 @@ float INA3221::getShuntR(uint8_t channel)
//
// SHUNT ALERT WARNINGS & CRITICAL
//
int INA3221::setCriticalAlert(uint8_t channel, uint16_t microVolt)
int INA3221::setCriticalAlert(uint8_t channel, uint32_t microVolt)
{
if (channel > 2) return -1;
if (microVolt > 16383) return -2;
// Check for the full scale voltage = 163.8 mV == 163800 uV
if (microVolt > 163800) return -2;
uint16_t value = (microVolt / 40) << 3; // LSB 40uV shift 3
return _writeRegister(INA3221_CRITICAL_ALERT(channel), value);
}
uint16_t INA3221::getCriticalAlert(uint8_t channel)
uint32_t INA3221::getCriticalAlert(uint8_t channel)
{
if (channel > 2) return -1;
uint16_t value = _readRegister(INA3221_CRITICAL_ALERT(channel));
return (value >> 3) * 40;
uint32_t value = _readRegister(INA3221_CRITICAL_ALERT(channel));
return (value >> 3) * 40; // LSB 40uV
}
int INA3221::setWarningAlert(uint8_t channel, uint16_t microVolt)
int INA3221::setWarningAlert(uint8_t channel, uint32_t microVolt)
{
if (channel > 2) return -1;
if (microVolt > 16383) return -2;
// Check for the full scale voltage = 163.8 mV == 163800 uV
if (microVolt > 163800) return -2;
uint16_t value = (microVolt / 40) << 3; // LSB 40uV shift 3
return _writeRegister(INA3221_WARNING_ALERT(channel), value);
}
uint16_t INA3221::getWarningAlert(uint8_t channel)
uint32_t INA3221::getWarningAlert(uint8_t channel)
{
if (channel > 2) return -1;
uint16_t value = _readRegister(INA3221_WARNING_ALERT(channel));
return (value >> 3) * 40;
uint32_t value = _readRegister(INA3221_WARNING_ALERT(channel));
return (value >> 3) * 40; // LSB 40uV
}
// mA wrappers
int INA3221::setCriticalCurrect(uint8_t channel, uint16_t milliAmpere)
int INA3221::setCriticalCurrect(uint8_t channel, float milliAmpere)
{
return setCriticalAlert(channel, milliAmpere * _shunt[channel]);
return setCriticalAlert(channel, 1000.0 * milliAmpere * _shunt[channel]);
}
uint16_t INA3221::getCriticalCurrent(uint8_t channel)
float INA3221::getCriticalCurrent(uint8_t channel)
{
return getCriticalAlert(channel) / _shunt[channel];
return getCriticalAlert(channel) * 0.001 / _shunt[channel];
}
int INA3221::setWarningCurrent(uint8_t channel, uint16_t milliAmpere)
int INA3221::setWarningCurrent(uint8_t channel, float milliAmpere)
{
return setWarningAlert(channel, milliAmpere * _shunt[channel]);
return setWarningAlert(channel, 1000.0 * milliAmpere * _shunt[channel]);
}
uint16_t INA3221::getWarningCurrent(uint8_t channel)
float INA3221::getWarningCurrent(uint8_t channel)
{
return getWarningAlert(channel) / _shunt[channel];
return getWarningAlert(channel) * 0.001 / _shunt[channel];
}
@ -185,22 +187,26 @@ uint16_t INA3221::getWarningCurrent(uint8_t channel)
//
// SHUNT VOLTAGE SUM
//
int16_t INA3221::getShuntVoltageSum()
// LSB 40 uV;
//
int32_t INA3221::getShuntVoltageSum()
{
int16_t value = _readRegister(INA3221_SHUNT_VOLTAGE_SUM);
return (value >> 1) * 40;
return (value >> 1) * 40L;
}
int INA3221::setShuntVoltageSumLimit(int16_t microVolt)
int INA3221::setShuntVoltageSumLimit(int32_t microVolt)
{
uint16_t value = (microVolt / 40) << 1; // LSB 40 uV;
// 15 bit signed.
if (abs(microVolt) > (16383L * 40L)) return -2;
int16_t value = (microVolt / 40) << 1;
return _writeRegister(INA3221_SHUNT_VOLTAGE_LIMIT, value);
}
int16_t INA3221::getShuntVoltageSumLimit()
int32_t INA3221::getShuntVoltageSumLimit()
{
int16_t value = _readRegister(INA3221_SHUNT_VOLTAGE_LIMIT);
return (value >> 1) * 40;
int32_t value = _readRegister(INA3221_SHUNT_VOLTAGE_LIMIT);
return (value >> 1) * 40L;
}
@ -329,9 +335,12 @@ uint16_t INA3221::getMaskEnable()
//
// POWER LIMIT
//
// LSB 8mV shift 3
//
int INA3221::setPowerUpperLimit(int16_t milliVolt)
{
if (milliVolt > 16376) return -10;
// int16_t is always within the range (after masking) 32760
// if (milliVolt > 4095 * 8) return -10; // LSB 8mV shift 3
int16_t value = milliVolt & 0xFFF8; // mask reserved bits
return _writeRegister(INA3221_POWER_VALID_UPPER, value);
}
@ -339,20 +348,23 @@ int INA3221::setPowerUpperLimit(int16_t milliVolt)
int16_t INA3221::getPowerUpperLimit()
{
int16_t value = _readRegister(INA3221_POWER_VALID_UPPER);
// (value >> 3) * 8mV; shift 3 compensates 8 mV
return value;
}
int INA3221::setPowerLowerLimit(int16_t milliVolt)
{
if (milliVolt > 16376) return -10;
int16_t value = (milliVolt << 1) & 0xFFF8;
// int16_t is always within the range (after masking) 32760
// if (milliVolt > 4095 * 8) return -10; // LSB 8mV shift 3
int16_t value = milliVolt & 0xFFF8;
return _writeRegister(INA3221_POWER_VALID_LOWER, value);
}
int16_t INA3221::getPowerLowerLimit()
{
int16_t value = _readRegister(INA3221_POWER_VALID_LOWER);
return value >> 1;
// (value >> 3) * 8mV; shift 3 compensates 8 mV
return value;
}

View File

@ -1,7 +1,7 @@
#pragma once
// FILE: INA3221.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// VERSION: 0.2.0
// DATE: 2024-02-05
// PURPOSE: Arduino library for the I2C INA3221 3 channel voltage and current sensor.
// URL: https://github.com/RobTillaart/INA3221_RT
@ -13,7 +13,7 @@
#include "Wire.h"
#define INA3221_LIB_VERSION "0.1.0"
#define INA3221_LIB_VERSION "0.2.0"
class INA3221
@ -49,21 +49,26 @@ public:
float getShuntR(uint8_t channel);
// SHUNT ALERT WARNINGS & CRITICAL
int setCriticalAlert(uint8_t channel, uint16_t microVolt);
uint16_t getCriticalAlert(uint8_t channel); // returns microVolt
int setWarningAlert(uint8_t channel, uint16_t microVolt);
uint16_t getWarningAlert(uint8_t channel); // returns microVolt
// NOTE: full scale voltage == 163.8 mV == 163800 uV
// NOTE: LSB == 40 uV so microVolt should be >= 40uV
int setCriticalAlert(uint8_t channel, uint32_t microVolt);
uint32_t getCriticalAlert(uint8_t channel); // returns microVolt
int setWarningAlert(uint8_t channel, uint32_t microVolt);
uint32_t getWarningAlert(uint8_t channel); // returns microVolt
// Wrappers using milliAmpere (Shunt must be set correctly!).
int setCriticalCurrect(uint8_t channel, uint16_t milliAmpere);
uint16_t getCriticalCurrent(uint8_t channel);
int setWarningCurrent(uint8_t channel, uint16_t milliAmpere);
uint16_t getWarningCurrent(uint8_t channel);
// NOTE: LSB = 40 uV so milliAmpere should be >= 0.4 mA (assume R = 0.1)
int setCriticalCurrect(uint8_t channel, float milliAmpere);
float getCriticalCurrent(uint8_t channel);
int setWarningCurrent(uint8_t channel, float milliAmpere);
float getWarningCurrent(uint8_t channel);
// SHUNT VOLTAGE SUM
int16_t getShuntVoltageSum(); // returns microVolt
int setShuntVoltageSumLimit(int16_t microVolt);
int16_t getShuntVoltageSumLimit(); // returns microVolt
// NOTE: LSB = 40 uV (15 bits)
int32_t getShuntVoltageSum(); // returns microVolt
// microVolt = max 655.320 == 16383L * 40L
int setShuntVoltageSumLimit(int32_t microVolt);
int32_t getShuntVoltageSumLimit(); // returns microVolt
// CONFIGURATION
// all fields at once. (short/fast/atomic code)
@ -95,9 +100,10 @@ public:
// all fields at once. (short/fast/atomic code)
int setMaskEnable(uint16_t mask);
uint16_t getMaskEnable();
// TODO convenience wrappers 9 x getters 9 x setters
// convenience wrappers for MASK/ENABLE?
// POWER LIMIT (guards BUS voltage)
// max = 4095 * 8 mV = 32760 mV
int setPowerUpperLimit(int16_t milliVolt);
int16_t getPowerUpperLimit();
int setPowerLowerLimit(int16_t milliVolt);

View File

@ -16,7 +16,7 @@ Arduino library for the I2C INA3221 3 channel voltage and current sensor.
## Description
**Experimental** library for the I2C INA3221.
**Experimental** library for the I2C INA3221. ==> **USE WITH CARE**
The INA3221 is a 3 channel measuring device to measure voltage.
Derived from that voltage and a given shunt, the library calculates current (amperes)
@ -33,7 +33,7 @@ They can however return the same value if no new data is available yet.
Some important maxima, see datasheet for all details.
| description | max | unit | notes |
|:--------------|-------:|-------:|:------|
|:--------------|-------:|-------:|:--------|
| channels | 3 | |
| bus voltage | 26 | Volt | unclear for how long.
| shunt voltage | 163 | mVolt | at 0.1 Ohm
@ -41,15 +41,25 @@ Some important maxima, see datasheet for all details.
| current | 1.63 | Ampere | I = U/R
#### 0.2.0 breaking changes.
Several functions have been reimplemented after issue #2.
This makes pre 0.2.0 versions obsolete.
#### Test
==> **USE WITH CARE**
Only tested partially with an Arduino UNO.
Not all functionality is tested and investigated. ==> **USE WITH CARE**
Not all functionality is tested and investigated with hardware.
Another point to be tested and verified is negative values in registers.
Read datasheet for details.
As always feedback is welcome, please open an issue on Github.
As always feedback is welcome, please open an issue on GitHub.
#### Special characters
@ -89,7 +99,8 @@ and high-speed mode (1 kHz to 2.94 MHz).
All data bytes are transmitted most significant byte first._
(timing in us, Arduino UN), indicative by example sketch)
(timing in us, Arduino UNO),
indicative by example INA3221_performance.ino
Four most important calls.
| Clock | bus V | shunt V | mA | mW |
@ -125,6 +136,8 @@ Note: one needs to set **Wire.begin()** before calling **begin()**.
Note the power and the current are not meaningful without calibrating the sensor.
Also the value is not meaningful if there is no shunt connected.
The parameter **channel** should always be 0..2
- **float getBusVoltage(uint8_t channel)** idem. in volts. Max 26 Volt.
- **float getShuntVoltage(uint8_t channel)** idem, in volts.
- **float getCurrent(uint8_t channel)** is the current through the shunt in Ampere.
@ -149,30 +162,43 @@ Wrapper functions for the micro scale.
#### Shunt Resistor
- **int setShuntR(uint8_t channel, float ohm)** sets value in Ohm
- **float getShuntR(uint8_t channel)** returns value in Ohm
The shunt resistor is typical in the order of 0.100 Ohm.
- **int setShuntR(uint8_t channel, float ohm)** sets value in Ohm.
- **float getShuntR(uint8_t channel)** returns value in Ohm.
#### Shunt Alerts, warning and critical
(not tested)
Read datasheet!
- **int setCriticalAlert(uint8_t channel, uint16_t microVolt)**
The user is responsible to be sure that the critical value >= warning value
if he decides to use both.
If only one of the two is used, critical might be less than warning.
The parameter **channel** should always be 0..2
The parameter **microVolt** should not exceed 163800 µV, will return error -2.
NOTE: LSB = 40 uV so microVolt should be >= 40uV
- **int setCriticalAlert(uint8_t channel, uint32_t microVolt)**
sets the critical alert level in microvolts.
- **uint16_t getCriticalAlert(uint8_t channel)** returns microVolt
- **int setWarningAlert(uint8_t channel, uint16_t microVolt)**
- **uint32_t getCriticalAlert(uint8_t channel)** returns microVolt
- **int setWarningAlert(uint8_t channel, uint32_t microVolt)**
sets the warning alert level in microvolts.
- **uint16_t getWarningAlert(uint8_t channel)** returns microVolt
- **uint32_t getWarningAlert(uint8_t channel)** returns microVolt
Wrappers using milliAmpere (assuming Shunt is set correctly!).
These are often more intuitive from user perspective.
NOTE: LSB = 40 uV so milliAmpere should be >= 0.4 mA (assume Shunt = 0.1 Ohm)
- **int setCriticalCurrect(uint8_t channel, uint16_t milliAmpere)**
- **int setCriticalCurrect(uint8_t channel, float milliAmpere)**
sets the critical alert level in milliAmpere.
- **uint16_t getCriticalCurrent(uint8_t channel)** returns milliAmpere
- **int setWarningCurrent(uint8_t channel, uint16_t milliAmpere)**
- **float getCriticalCurrent(uint8_t channel)** returns milliAmpere
- **int setWarningCurrent(uint8_t channel, float milliAmpere)**
sets the warning alert level in milliAmpere.
- **uint16_t getWarningCurrent(uint8_t channel)** returns milliAmpere
- **float getWarningCurrent(uint8_t channel)** returns milliAmpere
#### Shunt voltage sum
@ -250,7 +276,7 @@ Note: In combination with average the total conversion time can take up to
#### Operating mode
(not tested)
See datasheet
See datasheet!
Mode = 4 is not used, is also a **shutdown()** unknown if there is a difference with mode == 0.
The underlying bit pattern (not used).
@ -278,7 +304,7 @@ Descriptive mode functions (convenience wrappers).
#### Mask / enable register
(not tested)
See datasheet
See datasheet!
Setting all bits at once with a mask is faster, atomic and uses less code.
@ -293,9 +319,9 @@ TODO: convenience wrappers
#### Power Limit
(not tested)
See datasheet
See datasheet!
TO guard the BUS voltage
To guard the BUS voltage, max value 32760
- **int setPowerUpperLimit(int16_t milliVolt)**
- **int16_t getPowerUpperLimit()**
@ -305,8 +331,10 @@ TO guard the BUS voltage
#### Meta information
- **uint16_t getManufacturerID()** should return 0x5449
- **uint16_t getDieID()** should return 0x2260
(tested)
- **uint16_t getManufacturerID()** should return 0x5449, mine returns 0x5449.
- **uint16_t getDieID()** should return 0x2260, mine returns 0x3220.
#### Debugging
@ -323,19 +351,21 @@ TO guard the BUS voltage
- update documentation.
- return values
- test all functionality
- negative values = two complements - does it work?
#### Should
- TODO's in code.
- convenience wrappers MASK/ENABLE register.
- keep in sync with INA219/226 where possible.
#### Could
- convenience wrappers MASK/ENABLE register.
- 9 x getters 9 x setters (quite a lot)
- error handling / documentation
- clean up magic numbers in the code (e.g. 40 uV and 8 mV)
- comments?
#### Won't

View File

@ -90,7 +90,7 @@ void loop()
Serial.print("\t");
Serial.print(INA.getPower_mW(ch), 3);
Serial.println();
delay(10000);
delay(1000);
}
}

View File

@ -53,6 +53,7 @@ void setup()
void performance(uint32_t speed)
{
Wire.setClock(speed);
Serial.print("\nSpeed:\t");
Serial.println(speed);
delay(100); // fllussssh IO

View File

@ -0,0 +1,35 @@
IDE 1.8.19
Arduino UNO
INA3221_performance.ino
INA3221_LIB_VERSION: 0.2.0
Found: 64
DieID: 3220
ManID: 5449
Conf: 7127
Speed: 100000
BUS V: 5.048 564
SHUNT mV: 0.560 564
mA: 5.098 612
mW: 25.735 1196
Speed: 200000
BUS V: 5.048 320
SHUNT mV: 0.520 328
mA: 5.098 372
mW: 25.735 720
Speed: 300000
BUS V: 5.048 240
SHUNT mV: 0.520 236
mA: 5.098 288
mW: 27.715 552
Speed: 400000
BUS V: 5.048 204
SHUNT mV: 0.520 204
mA: 5.098 252
mW: 25.735 472

View File

@ -0,0 +1,185 @@
//
// FILE: INA3221_tests.ino
// AUTHOR: Rob Tillaart
// PURPOSE: test functions read/write values (crude unit test).
// URL: https://github.com/RobTillaart/INA3221_RT
#include "INA3221.h"
INA3221 INA(0x40);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("INA3221_LIB_VERSION: ");
Serial.println(INA3221_LIB_VERSION);
Serial.println();
Wire.begin();
if (!INA.begin() )
{
Serial.println("could not connect. Fix and Reboot");
}
else
{
Serial.print("Found: \t");
Serial.println(INA.getAddress());
}
Serial.print("DieID: \t");
Serial.println(INA.getDieID(), HEX);
Serial.print("ManID: \t");
Serial.println(INA.getManufacturerID(), HEX);
Serial.print(" Conf: \t");
Serial.println(INA.getConfiguration(), HEX);
Serial.println("\nSHUNT0\tSHUNT1\tSHUNT2 (Ohm)");
for (int ch = 0; ch < 3; ch++)
{
Serial.print(INA.getShuntR(ch), 4);
Serial.print("\t");
}
Serial.println();
// overwrite default shunts.
INA.setShuntR(0, 0.100);
INA.setShuntR(1, 0.102);
INA.setShuntR(2, 0.099);
for (int ch = 0; ch < 3; ch++)
{
Serial.print(INA.getShuntR(ch), 4);
Serial.print("\t");
}
Serial.println();
Serial.println("\nCHAN\tCRITIC\tWARNING\t (uV)");
for (int ch = 0; ch < 3; ch++)
{
Serial.print(ch);
Serial.print("\t");
Serial.print(INA.getCriticalAlert(ch));
Serial.print("\t");
Serial.print(INA.getWarningAlert(ch));
Serial.println();
}
// overwrite default Alerts
INA.setCriticalAlert(0, 50000);
INA.setCriticalAlert(1, 100000);
INA.setCriticalAlert(2, 150000);
INA.setWarningAlert(0, 25000);
INA.setWarningAlert(1, 75000);
INA.setWarningAlert(2, 125000);
Serial.println("\nCHAN\tCRITIC\tWARNING\t (uV)");
for (int ch = 0; ch < 3; ch++)
{
Serial.print(ch);
Serial.print("\t");
Serial.print(INA.getCriticalAlert(ch));
Serial.print("\t");
Serial.print(INA.getWarningAlert(ch));
Serial.println();
}
Serial.println("\nShunt Voltage (uV)");
Serial.print(" SVSUM:\t");
Serial.println(INA.getShuntVoltageSum());
Serial.print("SVLIMIT:\t");
Serial.println(INA.getShuntVoltageSumLimit());
INA.setShuntVoltageSumLimit(32198);
Serial.print("SVLIMIT:\t");
Serial.println(INA.getShuntVoltageSumLimit());
Serial.println("\nMask/ Enable");
Serial.print("M/E:\t");
Serial.println(INA.getMaskEnable(), HEX);
Serial.println("\nPower Limit");
Serial.print("UPPER:\t");
Serial.println(INA.getPowerUpperLimit());
Serial.print("LOWER:\t");
Serial.println(INA.getPowerLowerLimit());
INA.setPowerUpperLimit(5000);
INA.setPowerLowerLimit(4000);
Serial.print("UPPER:\t");
Serial.println(INA.getPowerUpperLimit());
Serial.print("LOWER:\t");
Serial.println(INA.getPowerLowerLimit());
Serial.println("\ngetEnableChannel");
for (int ch = 0; ch < 3; ch++)
{
Serial.print(INA.getEnableChannel(ch));
Serial.print("\t");
}
Serial.println();
Serial.println("Disable Channel");
for (int ch = 0; ch < 3; ch++)
{
INA.disableChannel(ch);
Serial.print(INA.getEnableChannel(ch));
Serial.print("\t");
}
Serial.println();
Serial.println("Enable Channel");
for (int ch = 0; ch < 3; ch++)
{
INA.enableChannel(ch);
Serial.print(INA.getEnableChannel(ch));
Serial.print("\t");
}
Serial.println();
Serial.println("\nAverage");
for (int avg = 0; avg < 8; avg++)
{
INA.setAverage(avg);
Serial.print(INA.getAverage());
Serial.print("\t");
}
Serial.println();
INA.setAverage(0);
Serial.println("\nBusVoltageConversionTime");
for (int bvct = 0; bvct < 8; bvct++)
{
INA.setBusVoltageConversionTime(bvct);
Serial.print(INA.getBusVoltageConversionTime());
Serial.print("\t");
}
Serial.println();
INA.setBusVoltageConversionTime(0);
Serial.println("\nShuntVoltageConversionTime");
for (int svct = 0; svct < 8; svct++)
{
INA.setShuntVoltageConversionTime(svct);
Serial.print(INA.getShuntVoltageConversionTime());
Serial.print("\t");
}
Serial.println();
INA.setShuntVoltageConversionTime(0);
}
void loop()
{
}
// -- END OF FILE --

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/INA3221_RT.git"
},
"version": "0.1.0",
"version": "0.2.0",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=INA3221_RT
version=0.1.0
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for the I2C INA3221 3 channel voltage and current sensor.