INA226 Strom- und Leistungssensor

Über den Beitrag

Nachdem ich in meinem letzten Beitrag über den INA219 berichtet habe, möchte ich in diesem Beitrag den INA226 und meine zugehörige Bibliothek INA226_WE vorstellen.

In erster Näherung ist der INA226 ein INA219 mit Alarmfunktion, wodurch man ihn besonders gut zur Überwachung von Strömen verwenden kann. Darüber hinaus ist der INA226 sowohl auf der High-Side wie auf der Low-Side einsetzbar. Auf weitere Unterschied zum INA219 komme ich im Laufe des Beitrages zurück.

Zunächst gehe ich auf das Messprinzip und die technischen Daten ein. Danach stelle ich die Bibliothek mit ihren zahlreichen Beispielsketchen vor. Der letzte Teil schließlich ist für diejenigen, die tiefer einsteigen wollen. Er beschäftigt sich mit inneren Details des INA226 und der Bibliothek.

Das Messprinzip

Ein INA226 Modul
Ein INA226 Modul, Vor- und Rückseite

Im Prinzip funktioniert der INA226 genauso wie der INA219. Ihr leitet den zu messenden Strom über die Anschlüsse IN+ und IN- durch einen Shunt (Stromesswiderstand). Ein A/D-Wandler misst den Spannungsabfall über dem Shunt und daraus berechnet der INA226 den Strom.

Wenn ihr den blanken INA226 verwendet (der achtbeinige IC auf dem Modul), dann seid ihr frei in der Wahl der Größe des Shunts. Die Module haben Shunts von 0,1 Ohm. Jedenfalls gilt das für alle Modelle, die mit untergekommen sind.

Zusätzlich misst der INA226 die Busspannung, also den Spannungsabfall über dem Verbraucher. Das passiert zwischen den Anschlüssen VBUS und GND. Der INA219 hingegen misst die Busspannung zwischen IN- und GND. Deswegen müsst ihr den INA219 vor den Verbraucher setzen (High-Side). Beim INA226 seid ihr flexibler, ihr könnt ihn sowohl auf der High-Side wie auch auf der Low-Side einsetzen.

Aus dem Strom und dem Spannungsabfall über dem Verbraucher berechnet der INA226 die Leistung. Die ermittelten Messwerte legt er in seinen Datenregistern ab, von wo ihr sie per I2C abholen könnt.

INA226 in High-Side Konfiguration
INA226 in High-Side Konfiguration

Typische Schaltung

Typische INA226 Schaltung (wird für die Beispiele verwendet)
Typische INA226 Schaltung (wird für die Beispiele verwendet)

Die oben abgebildete (High-Side) Schaltung habe ich für alle Beispielsketche verwendet. Wichtig ist, dass der INA226 und der Verbraucher ein gemeinsames GND haben, sonst funktioniert die Messung der Busspannung nicht. Falls ihr IN+ und IN- untereinander vertauscht, erhaltet ihr negative Werte für die Shuntspannung und den Strom.

Einige technische Daten des INA226 Moduls

  • Busspannung: 0 – 36 Volt
  • maximaler Busstrom: 800 Milliampere
  • Versorgungsspannung: 3 – 5.5 Volt
  • Stromverbrauch (selbst ermittelt):
    • kontinuierlicher Modus: 0,35 mA
    • Power-Down Modus: 2,3 µA 
  • Messmodi: kontinuierlich („continuous“) oder on-Demand („triggered“);
  • Mittelwertbildung aus 1, 4, 64, 128, 256, 512 oder 1024 Einzelmessungen
  • A/D-Wandlungszeit in acht Stufen einstellbar: 0,14 bis 8,2 ms
  • Datenregister:
    • Shunt Spannung (shunt voltage register)
    • Bus Spannung (bus voltage register)
    • Strom (current register)
    • Leistung (power register)
  • Kommunikation über I2C, 4 Adressen einstellbar (Modulrückseite):
    • 0x40: A0, A1 offen
    • 0x41: A0 geschlossen, A1 offen
    • 0x44: A0 offen, A1 geschlossen
    • 0x45: A0, A1 geschlossen
  • programmierbarer Alarm Pin für Limitüberschreitungen und verfügbare Messwerte

Weitere technische Daten findet ihr im Datenblatt des Herstellers.

Es ist sehr schade, dass das INA226 Modul keinen kleineren Shunt besitzt, denn mit 800 mA ist die Stromobergrenze nicht sehr üppig. Das ist wie Fahren mit angezogener Handbremse. Warum dieses Stromlimit nur ein Viertel des INA219 Limits bei gleichem Shunt beträgt, erläutere ich später.

Gebrauch der INA226 Bibliothek

Ihr könnt die Bibliothek INA226_WE hier von Github herunterladen oder auch direkt über die Arduino IDE Bibliotheksverwaltung installieren.

Ich habe insgesamt sechs Beispielsketche erstellt, anhand derer ich die Funktionen der Bibliothek vorstelle. Am intensivsten gehe ich auf das Beispiel für den kontinuierlichen Modus ein. Viele der Funktionen werden in allen Sketchen verwendet und müssen deshalb nur einmal erläutert werden. 

Beispiel 1: Kontinuierlicher (Continuous) Modus

Nachdem ihr die Bibliothek installiert und euren INA226 verdrahtet habt, ladet ihr den Sketch „Continous.ino“ hoch.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

INA226_WE ina226(I2C_ADDRESS);
// INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16          16
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  //ina226.setAverage(AVERAGE_16); // choose mode and uncomment for change of default

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  //ina226.setConversionTime(CONV_TIME_1100); //choose conversion time and uncomment for change of default
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - Continuous measurements (default)
  */
  //ina226.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Continuous");
  
  ina226.waitUntilConversionCompleted(); //if you comment this line the first data might be zero
}

void loop() {
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 

  ina226.readAndClearFlags();
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
  
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
  
  delay(3000);
}

 

Parametereinstellung am Beispiel des kontinuierlichen Modus

Die Funktion init() aktiviert den INA219 mit den Standardwerten. Um diese Grundeinstellungen zu ändern, könnt ihr im Setup an vier verschiedenen Schrauben drehen:

  1. Anzahl Einzelmessungen für die Shunt- und Busspannungskonversion mit setAverage() einstellen
    • 1, 4, 16, 64, 128, 256, 512 oder 1024 Einzelmessungen werden gemittelt
  2. Einstellung der A/D-Wandlungszeit für die Shunt- und Busspannung mit setConvTime()
    • 8 Stufen zwischen 140 µs und 8,244 ms einstellbar
    • zu beachten: ein Set aus Shunt- und Busspannung braucht die doppelte Zeit
  3. Messmodus über setMeasureMode() einstellen
    • CONTINUOUS – kontinuierliche Messung 
    • TRIGGERED – „auf Anfrage“: erkläre ich im nächsten Beispiel.
    • POWER_DOWN – schaltet den INA219 ab. Verwendet dazu aber besser die komfortablere powerDown() Funktion, welche weiter unten erläutert wird.
    • Der INA226 erlaubt es eigentlich auch, Shunt- oder Busspannungen zu ermitteln – das habe ich aber nicht implementiert. Bei mir gibt es die Messungen nur im Doppelpack.
  4. Strombereich über setCurrentRange() einstellen
    • Ihr könnt 400 oder 800 mA als maximalen Strom einstellen. Je kleiner der Strombereich, desto höher ist die Auflösung für den Strom und die Leistung.

Mit setCorrectionFactor() könnt ihr einen Korrekturfaktor einführen, falls die mit dem INA226 ermittelten Stromwerte von solchen abweichen sollten, die ihr zum Beispiel mit kalibrierten Messgeräten ermittelt habt. Der Faktor ist dabei der Quotient aus dem exakten und dem INA226 Wert. 

Weitere Funktionen des Beispiels

Die Datenregister des INA226 könnt ihr jederzeit abfragen. Sie enthalten den jeweils zuletzt gemessenen Wert. Vor der ersten abgeschlossenen Messung sind alle Werte Null. Mit waitUntilConversionCompleted() könnt ihr warten bis die aktuelle Messung abgeschlossen ist.

Mit readAndClearFlags() werden die Überlauf- und Alarmflags ausgelesen. In diesem Beispielsketch brauchen wir diesen Aufruf nur, um den Status der Variable overflow zu aktualisieren, die – wenn sie „true“ ist – den Überlauf eines Registers signalisiert.

Die Funktionen zum Auslesen der Datenregister, wie zum Beispiel getShuntVoltage_mV(), sollten selbsterklärend sein.

Berechnung der Messdauer

Die Dauer einer Messung ergibt sich aus:

Messdauer = Anzahl_{Messungen} (Averages) \cdot Conversion\ Time\cdot 2

Ausgabe

Und so sieht dann die Ausgabe des Sketches auf dem seriellen Monitor aus:

Ausgabe des Continuous Sketches
Ausgabe des Continuous Sketches

Beispiel 2: On-Demand (Triggered) Modus

Den Triggered Modus stellt ihr mit setMeasureMode(TRIGGERED) ein (Zeile 47 in Triggered.ino).  Jede Messung wird manuell mit startSingleMeasurement() gestartet (Zeile 74). Ich habe die Funktion so programmiert, dass sie automatisch wartet, bis die aktuellen Messwerte verfügbar sind. Ihr braucht waitUntilConversionCompleted() im Triggered Modus also nicht aufzurufen.

Ansonsten ist der Sketch identisch mit Continous.ino.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

INA226_WE ina226(I2C_ADDRESS);
// INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16           8
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  //ina226.setAverage(AVERAGE_1); // choose mode and uncomment for change of default

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  //ina226.setConversionTime(CONV_TIME_1100); //choose conversion time and uncomment for change of default
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - Continuous measurements (default)
  */
  ina226.setMeasureMode(TRIGGERED); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Triggered");
   
  // ina226.waitUntilConversionCompleted(); //makes no sense - in triggered mode we wait anyway for completed conversion
}

void loop() {
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
  
  ina226.startSingleMeasurement();
  ina226.readAndClearFlags();
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
  
  delay(3000);
}

 

Beispiel 3: Power-Down Modus

Im Power-Down Modus bringt ihr den Strombedarf des INA226 von ca. 0.35 mA auf ca. 2,3 µA herunter (eigene Messungen).

Der Beispielsketch PowerDown.ino zeigt den Power-Down Modus in Aktion. Der Sketch startet den INA226 mit den Standardparametern. Fünf Messwert-Pakete werden im Abstand von je drei Sekunden ausgegeben. Die Funktion powerDown() sichert dann den Inhalt des Konfigurationsregisters und schaltet den INA226 ab. Die Funktion powerUp() schreibt die Kopie des Konfigurationsregisters wieder zurück. Zum einen weckt dieser Schreibvorgang den INA226 auf, zum anderen stellt er sicher, dass der INA226 in den zuvor gewählten Modus (hier: kontinuierlich) zurückkehrt.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

INA226_WE ina226(I2C_ADDRESS);
// INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

void setup() {
  Serial.begin(9600);
  Wire.begin();
  // default parameters are set - for change check the other examples
  ina226.init();
  Serial.println("INA226 Current Sensor Example Sketch - PowerDown");
  Serial.println("Continuous Sampling starts");
  Serial.println();
}

void loop() {
  for(int i=0; i<5; i++){
    continuousSampling();
    delay(3000);
  }
  
  Serial.println("Power down for 10s");
  ina226.powerDown();
  for(int i=0; i<10; i++){
    Serial.print(".");
    delay(1000);
  }
  
  Serial.println("Power up!");
  Serial.println("");
  ina226.powerUp();
}

void continuousSampling(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 

  ina226.readAndClearFlags();
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
  
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}

 

Beispiel 4: Conversion Ready Alarm

Ladet nun den Sketch „Continous_Alert_Controlled.ino“ hoch. Mit diesem Beispiel lernt ihr den Alert Pin kennen. Zunächst wird mit 1024 die größtmögliche Zahl an zu mittelnden Einzelmessungen eingestellt. Dann gehen wir auch mit der Conversion Time an die obere Grenze, nämlich 8,244 Millisekunden. Damit braucht die Kombi aus Shunt- und Busspannungsmessung circa 16,9 Sekunden. Als Messmodus wählen wir CONTINOUS. Die Funktion enableConvReadyAlert() aktiviert den Alert Pin, der in seiner Standardeinstellung active-low ist. Der Alert Pin ist mit dem Arduino Pin 2 verbunden, für den wir einen Interrupt einrichten.

Wenn eine Messung beendet ist, dann geht der Alert Pin auf LOW und es wird ein Interrupt ausgelöst. Die Variable „event“ wird wahr und die if-Konstruktion in der Hauptschleife abgearbeitet. Zunächst wird readAndClearFlags() ausgeführt. Dadurch wird das Conversion Ready Flag wieder gelöscht und außerdem das Überlauf Flag gelesen. Die Messdaten werden ausgelesen und angezeigt. Der Interrupt an Pin 2 wurde nach dem Auslösen deaktiviert und wird nach der Ausgabe der Werte wieder angeschaltet. 

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

int interruptPin = 2;
volatile bool event = false;

INA226_WE ina226(I2C_ADDRESS);
// INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16          16
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  ina226.setAverage(AVERAGE_1024); 

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  ina226.setConversionTime(CONV_TIME_8244); // Conversion ready after conversion time x number of averages x 2
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - Continuous measurements (default)
  */
  //ina226.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Continous_Alert_Controlled");
  
  attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING);

  ina226.enableConvReadyAlert(); // an interrupt will occur on interrupt pin when conversion is ready
}

void loop() {
  if(event){
    ina226.readAndClearFlags(); // reads interrupt and overflow flags and deletes them 
    displayResults();
    attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING); 
    event = false;  
  }
  
  delay(100);
}

void displayResults(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
  
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}

void alert(){
  event = true;
  detachInterrupt(2);
}

 

Praktische Anwendung des Conversion Ready Alerts

Während der langen 17 Sekunden zwischen den Messungen hat der Arduino bzw. der Microcontroller, den ihr in eurem Projekt einsetzt, nichts zu tun. Das verbraucht in batteriebetriebenen Projekten wertvollen Strom. Also schickt euren Microcontroller doch einfach schlafen und lasst ihn durch den Interrupt wecken. Falls ihr nicht wisst, wie das geht, schaut hier in meinen Beitrag zu dem Thema.

Beispiel 5: Limit Alert

Den Limit Alarm möchte ich euch anhand des Sketches Limit_Alert.ino näherbringen. Der INA226 läuft dabei im kontinuierlichen Modus. Auf der Arduino Seite wird wieder ein Interrupt an Pin 2 eingerichtet.

Durch die Funktion enableAlertLatch() wird der Alarm Pin so eingerichtet, dass er bei Auslösung eines Alarms so lange aktiv ist, bis er manuell durch readAndClearFlags() wieder inaktiv gesetzt wird. Ohne diese Einrichtung würde der Pin bei der nächsten Messung innerhalb der Limits automatisch zurückgesetzt werden.

Mit setAlertType() bestimmt ihr, welcher der Messwerte beobachtet wird und wo das Limit liegt. Ihr könnt ein Min- oder Max-Limit für die Shuntspannung, die Busspannung oder den Strom angeben. Für die Leistung ist nur ein Max-Limit vorgesehen. Von Haus aus ist übrigens keine Alarmfunktion für den Strom vorgesehen, das habe ich über einen Umweg implementiert.

Und das war es eigentlich auch schon. Wenn das eingestellte Limit gerissen wird, wird der Alarm Pin aktiv, der Interrupt ausgelöst und die Messwerte ausgelesen. Es sind wieder die aktuellen Messwerte. Bei schneller Messfrequenz erwischt ihr deshalb nicht unbedingt genau den Messwert, der den Alarm ausgelöst hat. Wenn ihr das wolltet, könntet ihr den INA226 bei Auslösung des Alarms in den Power-Down Modus schicken. Dann könnt ihr euch Zeit lassen mit dem Auslesen.

Mit der Funktion readAndClearFlags() müsst ihr ein bisschen vorsichtig sein. Wenn ihr die Flags lest, um sie auszuwerten, löscht ihr sie. Wenn dann die Alarmbedingung noch besteht, wird der Alarm Pin gleich wieder aktiv. Passiert das, bevor der Interrupt wieder aktiviert ist, kommt alles durcheinander. Der Alarm Pin wäre schon Low und der Interrupt Pin wartete auf ein Falling. Das kann er dann sehr lange tun! Deswegen wird readAndClearFlags() nochmal aufgerufen, nachdem der Interrupt wieder aktiviert wurde. Eine Trennung von Lesen und Löschen der Flags wäre etwas einfacher zu kontrollieren, das ist aber im INA226 so nicht implementiert.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

int interruptPin = 2;
volatile bool event = false;

INA226_WE ina226(I2C_ADDRESS);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16          16
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  // ina226.setAverage(AVERAGE_1024); 

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  // ina226.setConversionTime(CONV_TIME_8244); 
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - Continuous measurements (default)
  */
  //ina226.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Limit_Alert");
  
  /* In the default mode the limit interrupt flag will be deleted after the next measurement within limits. 
     With enableAltertLatch(), the flag will have to be deleted with readAndClearFlags(). 
  */
    ina226.enableAlertLatch();
  
  /* Set the alert type and the limit
      * Mode *        * Description *           * limit unit *
    SHUNT_OVER     Shunt Voltage over limit          mV
    SHUNT_UNDER    Shunt Voltage under limit         mV
    CURRENT_OVER   Current over limit                mA
    CURRENT_UNDER  Current under limit               mA
    BUS_OVER       Bus Voltage over limit            V
    BUS_UNDER      Bus Voltage under limit           V
    POWER_OVER     Power over limit                  mW
  */
  ina226.setAlertType(POWER_OVER, 230.0);
 
  attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING);
}

void loop() {
  if(event){
    ina226.readAndClearFlags(); // reads interrupt and overflow flags and deletes them 
    displayResults();
    attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING); 
    event = false;
    ina226.readAndClearFlags();
  }  
  delay(1000);
}

void displayResults(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
  
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}

void alert(){
  event = true;
  detachInterrupt(2);
}

 

Beispiel 6: Limit und Conversion Alert

Ich hoffe, ihr könnt noch – das ist aber auch das letzte Beispiel. Mit dem Sketch Limit_And_Conversion_Alert.ino möchte ich zeigen, wie ihr den Limit und den Conversion Ready Alarm nebeneinander nutzen könnt. Beide Alarme werden wie in den vorhergehenden Sketchen aktiviert.

Im Alarmfall wollt ihr nun unterscheiden können, welche Bedingung den Alarm ausgelöst hat. Lest dazu im Alarmfall die Flags über readAndClearFlags(). Dadurch aktualisiert ihr die Variablen limitAlert und convAltert und könnt sie abfragen.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

int interruptPin = 2;
volatile bool event = false;

INA226_WE ina226(I2C_ADDRESS);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  // Conversion will be ready after conversion time x number of averages x 2
  ina226.setAverage(AVERAGE_512); 
  ina226.setConversionTime(CONV_TIME_8244); 
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Limit_And_Conversion_Alert");
  
  /* In the default mode the limit interrupt flag will be deleted after the next measurement within limits. 
     With enableAltertLatch(), the flag will have to be deleted with readAndClearFlags(). 
  */
  ina226.enableAlertLatch();
  
  /* Set the alert type and the limit
      * Mode *        * Description *           * limit unit *
    SHUNT_OVER     Shunt Voltage over limit          mV
    SHUNT_UNDER    Shunt Voltage under limit         mV
    CURRENT_OVER   Current over limit                mA
    CURRENT_UNDER  Current under limit               mA
    BUS_OVER       Bus Voltage over limit            V
    BUS_UNDER      Bus Voltage under limit           V
    POWER_OVER     Power over limit                  mW
  */
  ina226.setAlertType(CURRENT_UNDER, 45.0);
  ina226.enableConvReadyAlert(); // In this example we also enable the conversion ready alert interrupt
 
  attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING);
}

void loop() {
  if(event){
    ina226.readAndClearFlags();
    displayResults();
    attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING); 
    ina226.readAndClearFlags(); 
    event = false;
  }  
  delay(1000);
}

void displayResults(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
   
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  if(ina226.limitAlert){
    Serial.println("Limit Alert !!!!");
  }
  if(ina226.convAlert){
    Serial.println("Conversion Alert!!!!");
  }
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}

void alert(){
  event = true;
  detachInterrupt(2);
}

 

Ausgabe von Limit_And_Conversion_Alert.ino

Für die folgende Ausgabe habe ich das gesetzte Limit dauerhaft überschritten. Entsprechend meldet der Sketch Limitüberschreitungen im Sekundentakt. Bei den oben eingestellten Bedingungen gibt der Sketch zusätzlich ca. alle 8 Sekunden ein Conversion Ready Alert aus:

Ausgabe des Limit_And_Conversion_Alert.ino Sketches
Ausgabe des Limit_And_Conversion_Alert.ino Sketches

Alle Funktionen auf einen Blick

Hier seht ihr noch einmal alle Funktionen auf einen Blick. Das ist in Englisch, da ich die Tabelle auch für die Dokumentation auf Github verwende.

Liste der (öffentlichen) Funktionen der INA226_WE Bibliothek
Liste der (öffentlichen) Funktionen der INA226_WE Bibliothek

Details zur Bibliothek und den Registern das INA226

Wer noch immer nicht genug hat, kann jetzt noch etwas tiefer in den INA226 und die Bibliothek einsteigen.

Die Register des INA226

Der INA226 besitzt 10 Register, also deutlich mehr als der INA219 mit seinen 6 Registern. Alle Register umfassen 16 Bit.

Register des INA226
Register des INA226

Configuration Register

Configuration Register des INA226

Im Configuration Register könnt ihr grundlegende Einstellungen vornehmen:

  • RST – Reset Bit: ist es gesetzt, wird ein Reset ausgelöst.
  • AVGX – Average Bits: damit legt ihr die Anzahl der zu mittelnden Einzelmessungen fest.
  • VBUSCTX – Bus Voltage Conversion Time Bits: siehe Tabelle.
  • VSHCTX – Shunt Voltage Conversion Time Bits: siehe Tabelle.
    • Ich habe die Vereinfachung vorgenommen, dass nur eine Conversion Time wählbar ist, sie gilt dann für die Wandlung der Bus- und der Shuntspannung gleichermaßen.
  • MODEX – MODE Bits: legen den Modus fest, siehe Tabelle.
Average Bits des INA226
Average Bits
Shunt und Bus Voltage Conversion Time Bits des INA226
Shunt und Bus Voltage Conversion Time Bits
Mode Bits - nur die grün hervorgehobenen Modi habe ich implementiert
Mode Bits – nur die grün hervorgehobenen Modi habe ich implementiert

Mask/Enable Register

Ich springe zum Mask/Enable Register, weil dies neben dem Configuration Register das andere Register ist, in dem ihr Einstellungen vornehmen könnt.

Mask/Enable Register des INA226

Die Bits 10 bis 15 dienen zum Aktivieren der Alarme:

  • SOL / SUL: Shunt voltage over / under limit alert
  • BOL / BUL: Bus voltage over / under limit alert
  • POL: Power over limit alert
  • CNVR: Conversion ready alert

Von den Limit Alerts könnt ihr nur jeweils einen aktivieren. Setzt ihr mehrere Bits, dann gilt das höchste. Nur den Conversion Ready Alarm könnt ihr parallel aktivieren.

In meiner Bibliothek gibt es noch einen Current over und Current under Alarm. Intern wird dabei das SOL bzw. SUL Bit gesetzt und das Current Limit in ein Shunt Voltage Limit umgerechnet.

Weitere Einstellungen könnt ihr mit dem APOL und dem LEN Bit vornehmen:

  • APOL: Alert Pin Polarity Bit – Polarität des Alarm Pins. Standardeinstellung ist active-high (APOL = 0).
  • LEN: Latch Enable Bit. Standardeinstellung ist Latch disabled (LEN = 0). Das bedeutet, dass der Alarm Pin deaktiviert und das Alarm Flag Bit gelöscht werden, sobald eine OK – Messung erfolgt, also kein Limit gerissen wird. Ist LEN gesetzt, bleibt der Alarm Pin aktiviert und das Alarm Flag Bit gesetzt, bis das Mask/Enable Register gelesen wird (readAndClearFlags()).

Der INA226 setzt je nach Einstellung und Messergebnissen drei Flags:

  • AFF: Alarm Function Flag – ein Limit wurde überschritten.
  • CVRF: Conversion Ready Flag – Messwerte stehen bereit.
  • OVF: Overflow Flag – ein Datenregister ist übergelaufen.

Das CVRF Bit wird gelöscht, wenn das Mask/Enable Register gelesen oder Configuration Register beschrieben wird.

Ich habe die Funktion readAndClearFlags() so programmiert, dass sie den Zustand von AFF, CVRF und OVF in den Variablen limitAlert, convAlert und overflow speichert.

Shunt Voltage Register

Das Shunt Voltage Register enthält die Shunt Spannung mit einem LSB (least significant bit) von 2,5 µV. Mit anderen Worten: die Auflösung des Shunt Registers beträgt 2,5 µV pro Bit. Da das Register ein Vorzeichenbit besitzt, ist der Wertebereich der Shuntspannung begrenzt auf: 

Shuntspannungsbereich = \pm (2^{15}-1)\cdot 2,\!5 \text{\ µ\!V}=81,\!9175 \text{\ m\!V}

Über die Größe des Shunts könntet ihr theoretisch den maximal messbaren Strom festlegen. Allerdings habt ihr beim Modul keine Wahl und müsst mit den 0,1 Ohm vorlieb nehmen. Daraus ergibt sich ein Messbereich von +/- 819,175 mA für den Strom. Beim INA219 hingegen beträgt das Current LSB 10 µV. Damit ist der maximal messbare Strom dort viermal größer, wenn auch mit geringerer Auflösung. 

Bus Voltage Register

Das Ergebnis der Busspannungswandlung legt der INA226 im Bus Voltage Register ab. Vom Bus Voltage Register werden nur 15 Bit genutzt – das Vorzeichen ist immer positiv. Das LSB beträgt 1.25 mV, woraus sich eine maximale Busspannung von 40,96 V ergibt.

Current Register

Den aus der Shuntspannung berechneten Strom speichert der INA226 im Current Register. Hier wird es jetzt ein bisschen kompliziert, da ein Kalibrierfaktor CAL ins Spiel kommt, der vom maximal zu erwartenden Strom und der Größe des Shunts abhängt:

C\!AL = \frac{0,\!00512}{Current\_LSB \cdot R_{Shunt}}\\
Current\_LSB = \frac{Maximum\ expected\ current }{2^{15}}

Man startet bei der Berechnung des Kalibrierwertes mit dem maximal zu erwartenden Strom. Für meine Bibliothek habe ich 0,8 und 0,4 A gewählt. 0,8 A, weil dieser Wert, wie weiter oben erklärt, der maximale Strom des Moduls ist und 0,4 A, um eine Abstufung zu haben und die dadurch höhere Auflösung nutzen zu können. Die Formel für CAL ergibt einen „krummen“ Wert, den man nach oben hin aufrundet, um in der Folge handlichere Zahlen zu haben. Der Inhalt des Current Registers ergibt sich aus:

Current\ Reg = \frac{Shunt\ Voltage\ Reg \cdot C\!AL}{2048}

Am Ende müsst ihr den Inhalt des Current Registers mit dem Current_LSB multiplizieren, um den Strom zu erhalten.

Power Register

Das Power Register enthält den berechneten Wert für die Leistung gemäß der folgenden Formel:

Power\ Reg = \frac{Current\ Reg \cdot Bus\ Voltage\ Reg}{20000}

Und schließlich ist die Leistung dann der Inhalt des Power Registers, multipliziert mit dem Power_LSB. Für den Power_LSB gilt (intern festgelegt):

Power\_LSB = Current\_LSB\cdot 25

Calibration Register

Das Calibration Register enthält den oben erwähnten Kalibrierwert.

Alert Limit Register

Das Alarm Limit tragt ihr in das Alert Limit Register ein. Ich habe das so implementiert, dass ihr die Grenzwerte direkt über die Funktion setAlertType() in den jeweils vorgesehenen Einheiten übergeben könnt.

Berechnung der Kalibrierfaktoren und der LSBs

Den Inhalt der Datenregister rechnet die Bibliothek mit den LSBs und weiteren Faktoren in die gewünschten Einheiten um:

Umrechnung der Datenregisterinhalte
Umrechnung der Datenregisterinhalte

Um handliche Umrechnungsfaktoren zu erhalten, habe ich einen Current Divider und einen Power Multiplier eingeführt:

Current\ \text{[mA]}=\frac{Current\ Register}{Current\ Divider}\ \ \\
\text{mit}\ \ Current\ Divider = \frac{1}{Current\_LSB\cdot 1000}

und

Power\ \text{[mW]} = Power\ Register \cdot Power\ Multiplier
Power\ Multiplier = Power\_LSB \cdot 1000

Die folgenden Werte habe ich dann in der Bibliothek implementiert:

Werte für die implementierten Strombereiche
Werte für die implementierten Strombereiche

Danksagung

Das Bild von dem Strommesser stammt aus dem Free-Photos Fundus von Pixabay. Ebenso stammen die Alarmleuchte von Alexey Hulsov und die Anzeige von den OpenClipart-Vectors aus Pixabay.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.