ADS1220 4-Kanal, 24-Bit A/D-Wandler

Über den Beitrag

Viele Anwendungen erfordern eine präzise Messung von Spannungen. Mit den internen A/D-Wandlern der Arduino-, ESP8266- oder ESP32-Boards kommt ihr da schnell an Grenzen. Sehr gute Ergebnisse könnt ihr mit dem 16-Bit A/D-Wandler ADS1115 erzielen, über den ich hier berichtet habe. Aber selbst 16 Bit reichen manchmal nicht, z.B. bei der Ansteuerung von Wägezellen (siehe meine Beiträge über HX711 basierte Waagen oder Dehnungsmessstreifen). In diesem Beitrag möchte ich den (vergleichsweise) einfach zu handhabenden 24-Bit A/D-Wandler ADS1220 vorstellen.

Im Rahmen meiner Recherchen habe ich die Bibliothek Protocentral_ADS1220 ausprobiert. Sie ist schlank und im Prinzip funktioniert sie gut, bis auf einen der beiden Beispielsketche. Allerdings musste ich beim Testen verschiedener Einstellungen doch recht häufig in das Datenblatt des ADS1220 schauen oder die Parameter für die Funktionen aus der Headerdatei heraussuchen. Ich habe deswegen die Bibliothek ADS1220_WE geschrieben. Es ist ein Versuch, die Bedienung des ADS1220 komfortabel zu gestalten. Ihr findet meine Bibliothek hier auf GitHub oder in der Arduino Bibliotheksverwaltung. Probiert einfach beide aus und nehmt, was euch besser gefällt.

Technische Daten des ADS1220

IC vs. Modul

Für den Anschluss des ADS1220 an einen Mikrocontroller empfiehlt das Datenblatt in Abschnitt 9.1.1 die Verwendung einiger Kondensatoren und Widerstände. Wer es, so wie ich, einfach haben möchte, verwendet ein Modul, das diese zusätzlichen Bauteile schon implementiert hat. So ein Modul bekommt ihr in Online-Shops für unter 10 Euro, meist direkt aus China.

ADS1220 Modul
ADS1220 Modul

Wesentliche Daten auf einen Blick

Wesentliche technische Daten sind:

  • Analoge Spannungsversorgung: 2.3 bis 5.5 Volt an AVDD / AVSS (AGND).
  • Digitale Spannungsversorgung: 2.3 bis 5.5 Volt an DVDD / DGND.
  • Bipolare Spannungsversorgung möglich: z.B. AVSS zu DGND = -2.5 Volt, AVDD zu DGND = +2.5 Volt.
  • Stromverbrauch: 120 Mikroampere im Duty-Cycle Modus.
  • Kommunikation: über SPI.
  • Referenzspannungen:
    • Intern: 2.048 Volt.
    • Extern: VREFPx – VREFNx = 0.75 Volt bis AVDD.
  • Verstärkung: 1 – 128 (mit PGA = Programmable Gain Amplifier), 1 – 4 (ohne PGA).
  • Datenrate: 5 – 2000 SPS (Samples per Second)
  • Konversionsmodi: Kontinuierlicher und „Single-Shot“ Modus.
  • Spannungseingänge: 4 single-ended (gegen AVSS) oder 2 Differenzeingänge.
  • Messung im Multiplex-Verfahren.
  • Zwei programmierbare IDACs (Digital-zu-Analog-Konverter für Strom): 10 Mikroampere bis 1.5 Milliampere (im Datenblatt auch „excitation current source“ genannt).
  • Digitaler Filter: 50 und/oder 60 Hz.
  • Integrierter Temperatursensor.

Pinout des ADS1220 (Moduls)

Die Bezeichnungen der Ein- und Ausgänge unterscheiden sich auf dem Modul teilweise vom Datenblatt (in Klammern, falls abweichend):

  • MISO (DOUT/DRDY), MOSI (DIN), SCLK, CS: SPI – Anschlüsse
  • AVDD, AGND (AVSS): Analoge Spannungsversorgung
  • DVDD, DGND: Digitale Spannung
  • CLK: externer Taktgeber (optional)
  • DRDY: Data Ready Pin, ist LOW, wenn Daten verfügbar sind
  • REFP0, REFN0: externe Referenzspannung; „P“ = positiv, „N“ = negativ.
  • AIN0 (AIN0/REFP1), AIN1, AIN2, AIN3 (AIN3/REFN1): Eingänge für die zu messenden Spannungen.
    • AIN0/AIN3 lassen sich alternativ als Eingang für eine weitere externe Referenzspannung nutzen.

Einfache Messungen

Schaltung

Wie ihr den ADS1220 verwendet, erkläre ich anhand von Beispielsketchen. Als Erstes möchte ich euch zeigen, wie ihr unter Verwendung der Standardeinstellungen die Eingangskanäle wechselt und Daten abfragt. Für meinen Beispielsketch kam die folgende Schaltung zum Einsatz:

Beispielschaltung: ADS1220 am Arduino Uno
Beispielschaltung: ADS1220 am Arduino Uno

Vorher noch ein paar Anmerkungen

In diesem Beispiel wird die interne Spannungsreferenz von 2.048 Volt verwendet. Das bedeutet, dass ihr Spannungen nur im Bereich von -2.048 bis +2.048 Volt messen könnt.

Die Ausgangsspannung des PGA muss 200 mV oberhalb von AVSS und 200 mV unterhalb von AVDD liegen. Außerhalb dieser Grenzen arbeitet der ADS1220 nicht mehr linear. Da die Standardverstärkung 1 ist, gelten diese Limits im ersten Beispiel für auch für die Eingangsspannung. Um auf der sicheren Seite zu sein, schalten wir den PGA mit bypassPGA(true) ab.

Die gemessene Spannung wird aus dem Rohwert wie folgt berechnet:

\text{Spannung} = \frac{\text{Rohwert}\cdot \text{Referenzspannung}}{2^{23}\cdot \text{Gainfaktor}}\;\text{[V]}

Der Sketch

Hier nun der Sketch:

#include <ADS1220_WE.h>
#include <SPI.h>

#define ADS1220_CS_PIN    7 // chip select pin
#define ADS1220_DRDY_PIN  6 // data ready pin 

/* Create your ADS1220 object */
ADS1220_WE ads = ADS1220_WE(ADS1220_CS_PIN, ADS1220_DRDY_PIN);
/* Alternatively you can also pass the SPI object as reference */
// ADS1220_WE ads = ADS1220_WE(&SPI, ADS1220_CS_PIN, ADS1220_DRDY_PIN);

void setup(){
  Serial.begin(9600);
  if(!ads.init()){
    Serial.println("ADS1220 is not connected!");
    while(1);
  }
/* The voltages to be measured need to be between negative VREF + 0.2 V and positive
 * VREF -0.2 V if PGA is enabled. For this basic example I disable PGA, to be on the 
 * safe side.
 */
  ads.bypassPGA(true); 
}

/* 
 *  You set the channels to be measured with setCompareChannels(); You
 * can choose the following parameters:
 * Parameter                  Pos. Input     Neg. Input         Comment
 * ADS1220_MUX_0_1              AIN0           AIN1
 * ADS1220_MUX_0_2              AIN0           AIN2
 * ADS1220_MUX_0_3              AIN0           AIN3
 * ADS1220_MUX_1_2              AIN1           AIN2
 * ADS1220_MUX_1_3              AIN1           AIN3
 * ADS1220_MUX_2_3              AIN2           AIN2
 * ADS1220_MUX_1_0              AIN1           AIN0
 * ADS1220_MUX_3_2              AIN3           AIN2
 * ADS1220_MUX_0_AVSS           AIN0           AVSS           single-ended
 * ADS1220_MUX_1_AVSS           AIN1           AVSS           single-ended  
 * ADS1220_MUX_2_AVSS           AIN2           AVSS           single-ended
 * ADS1220_MUX_3_AVSS           AIN3           AVSS           single-ended
 * ADS1220_MUX_REFPX_REFNX_4   REFP0/REFP1   REFN0/REFN1     (REFPX-REFNX)/4; PGA bypassed
 * ADS1220_MUX_AVDD_M_AVSS_4    AVDD           AVSS           (AVDD-AVSS)/4; PGA bypassed
 * ADS1220_MUX_AVDD_P_AVSS_2    AVDD           AVSS           (AVDD+AVSS)/2
 * 
 * The last three modes use the internal reference (2.048 V) and gain = 1, independent of 
 * your settings.
 */


void loop(){
  float result = 0.0;
  long longResult = 0;
    
  ads.setCompareChannels(ADS1220_MUX_0_1);
  result = ads.getVoltage_mV();
  longResult = ads.getRawData();
  Serial.print("AIN0 vs. AIN1  [mV]: ");
  Serial.println(result);
  Serial.print("AIN0 vs. AIN1 (raw): ");  // raw data
  Serial.println(longResult);
  
  ads.setCompareChannels(ADS1220_MUX_0_AVSS);
  result = ads.getVoltage_mV();
  Serial.print("AIN0 vs. AVSS  [mV]: ");
  Serial.println(result);

  ads.setCompareChannels(ADS1220_MUX_1_AVSS);
  result = ads.getVoltage_mV();
  Serial.print("AIN1 vs. AVSS  [mV]: ");
  Serial.println(result);
  
  ads.setCompareChannels(ADS1220_MUX_2_AVSS);
  result = ads.getVoltage_muV();    // request result in microvolts
  Serial.print("AIN2 vs. AVSS  [µV]: ");
  Serial.println(result);
  
  ads.setCompareChannels(ADS1220_MUX_3_AVSS);
  result = ads.getVoltage_mV();
  Serial.print("AIN3 vs. AVSS  [mV]: ");
  Serial.println(result);
  
  ads.setCompareChannels(ADS1220_MUX_AVDD_M_AVSS_4); // voltage supply / 4
  result = ads.getVoltage_mV() * 4.0;
  Serial.print("Supply Voltage [mV]: ");
  Serial.println(result);

  result = ads.getVRef_V();                 // get the reference voltage
  Serial.print("Reference       [V]: ");
  Serial.println(result,3); 
  
  Serial.print("Temperature    [°C]: ");    // get the temperature
  result = ads.getTemperature();
  Serial.println(result);
  
  Serial.println();
 
  delay(2000);
}

 

Erklärungen zum Sketch

Ich gehe nur auf die Teile ein, die meines Erachtens noch näher erläutert werden müssen (sonst fragt!):

  • Mit init() initialisiert ihr den ADS1220. Die Funktion führt unter anderem ein Reset durch.
  • setCompareChannels() legt fest, welche Eingänge für die Messung herangezogen werden. 
  • Der Parameter ADS1220_MUX_REFPX_REFNX_4 bewirkt, dass die Referenzspannung REFP0/REFN0 oder REFP1/REFN1 gemessen wird. Unabhängig von euren Einstellungen wird dabei der PGA umgangen, die Verstärkung beträgt 1 und es wird gegen die interne Referenz gemessen. Damit das auch bei Referenzspannungen größer 2.048 Volt funktioniert, wird die Spannung vor der Messung auf ein Viertel reduziert. 
  • Bei Verwendung des Parameters ADS1220_MUX_AVDD_M_AVSS_4 messt ihr ein Viertel der Versorgungsspannung.
  • ADS1220_MUX_AVDD_P_AVSS_2 liefert als Ergebnis (VAVDD – VAVSS) / 2. Damit bestimmt ihr den Offset bei bipolarer Spannungsversorgung.
  • Die Funktionen getVoltage_mV() / getVoltage_muV() liefern das Ergebnis in Millivolt und Mikrovolt. Es wird immer gewartet, bis DRDY auf LOW geht. Dadurch ist sichergestellt, dass ihr ein „frisches“ Ergebnis erhaltet. Im Single-Shot Modus müssen die Messungen initiiert werden. Auch das übernehmen die getVoltage-Funktionen.
  • Alternativ fragt ihr den Rohwert der A/D-Wandlung mit getRawData() ab.
  • Mit getVRef_V() erhaltet ihr die Referenzspannung in Volt. Das ist nicht das Ergebnis einer Messung, sondern eurer Festlegung. Die Voreinstellung ist 2.048 Volt.
  • getTemperature() liefert euch die Temperatur in Grad Celsius.

Ausgabe

So kann die Ausgabe des Sketches aussehen:

Ausgabe von ADS1220_basic_example.ino
Ausgabe von ADS1220_basic_example.ino

Wie ihr seht, ist VAIN0/AIN1 = VAIN0/AVSS  – VAIN1/AVSS, was bei näherer Überlegung ja auch so sein muss.

Wechsel der Referenzspannung des ADS1220

Wie schon zuvor erwähnt, habt ihr vier Optionen für die Referenzspannung:

  1. Interne Referenzspannung (2.048 Volt)
  2. Externe Referenzspannung an REFP0/REFN0
  3. Externe Referenzspannung an REFP1/REFN1 (auf Kosten zweier Spannungseingänge)
  4. Versorgungsspannung (AVDD/AVSS) als Spannungsreferenz

Das folgende Schaltbild soll das verdeutlichen:

Beispielschaltung: Verwendung externer Referenzspannungen am ADS1220
Beispielschaltung: Verwendung externer Referenzspannungen am ADS1220

Ein Beispielsketch

Ich habe die oben abgebildete Schaltung verwendet und verschiedene Referenzspannungen an REFP0/REFN0 und REFP1/REFN1 angelegt. An AIN2 kam eine zu messende Testspannung.

Der folgende Sketch zeigt, wie ihr Referenzspannungen im ADS1220 einstellt bzw. wechselt:

#include <ADS1220_WE.h>
#include <SPI.h>

#define ADS1220_CS_PIN    7 // chip select pin
#define ADS1220_DRDY_PIN  6 // data ready pin 

/* Create your ADS1220 object */
ADS1220_WE ads = ADS1220_WE(ADS1220_CS_PIN, ADS1220_DRDY_PIN);
/* Alternatively you can also pass the SPI object as reference */
// ADS1220_WE ads = ADS1220_WE(&SPI, ADS1220_CS_PIN, ADS1220_DRDY_PIN);

void setup(){
  Serial.begin(9600);
  if(!ads.init()){
    Serial.println("ADS1220 is not connected!");
    while(1);
  }
}

/* For better readability of the code the setting options are explained here and not
 * in loop() where the functions are used.
 * 
 * You set the channels to be measured with setCompareChannels(); You
 * can choose the following parameters:
 * Parameter                  Pos. Input     Neg. Input         Comment
 * ADS1220_MUX_0_1              AIN0           AIN1
 * ADS1220_MUX_0_2              AIN0           AIN2
 * ADS1220_MUX_0_3              AIN0           AIN3
 * ADS1220_MUX_1_2              AIN1           AIN2
 * ADS1220_MUX_1_3              AIN1           AIN3
 * ADS1220_MUX_2_3              AIN2           AIN2
 * ADS1220_MUX_1_0              AIN1           AIN0
 * ADS1220_MUX_3_2              AIN3           AIN2
 * ADS1220_MUX_0_AVSS           AIN0           AVSS (=AGND)   single-ended
 * ADS1220_MUX_1_AVSS           AIN1           AVSS           single-ended  
 * ADS1220_MUX_2_AVSS           AIN2           AVSS           single-ended
 * ADS1220_MUX_3_AVSS           AIN3           AVSS           single-ended
 * ADS1220_MUX_REFPX_REFNX_4   REFP0/REFP1   REFN0/REFN1     (REFPX-REFNX)/4; PGA bypassed
 * ADS1220_MUX_AVDD_M_AVSS_4    AVDD           AVSS          (AVDD-AVSS)/4; PGA bypassed
 * ADS1220_MUX_AVDD_P_AVSS_2    AVDD           AVSS           AVDD+AVSS)/2
 * The last three modes use the internal reference (2.048 V) and gain = 1, independent of 
 * your settings.
 * 
 * setVRefSource() sets the the reference voltage source. Parameters are:
 * ADS1220_VREF_INT          int. reference 2.048 V (default)
 * ADS1220_VREF_REFP0_REFN0  ext. reference = Vrefp0 - Vrefn0
 * ADS1220_VREF_REFP1_REFN1  ext. reference = Vrefp1 - Vrefn1 (REFP1=AIN0, REFN1=AIN3)
 * ADS1220_VREF_AVDD_AVSS    ext. reference = supply voltage
 * 
 * If you use the above options you also have to set the value of vRef "manually":
 * setVRefValue_V(vRef in volts);
 * 
 * Alternatively, you can set the reference voltage source and let the ADS1220 measure 
 * the reference. Be aware that this is not a measurement with highest precision. "Calibration" 
 * might be a bit misleading. You should take the lowest data rate (default) for most accurate 
 * results. You can use the following functions:
 * setRefp0Refn0AsVefAndCalibrate();
 * setRefp1Refn1AsVefAndCalibrate();
 * setAvddAvssAsVrefAndCalibrate();
 * setIntVRef();
 * The latter function sets the default settings. 
 */

void loop(){
  float result = 0.0;
    
  Serial.println("1. Standard internal voltage reference");
  result = ads.getVRef_V();                 // get the reference voltage 
  Serial.print("Reference, default      [V]: ");
  Serial.println(result,3); 
  
  Serial.println("2. REFP0/REFN0 as voltage reference, vRef manually set");
  ads.setVRefSource(ADS1220_VREF_REFP0_REFN0);
  ads.setVRefValue_V(3.295);    // set reference voltage in volts
  result = ads.getVRef_V();
  Serial.print("Reference, manually set [V]: ");
  Serial.println(result,3); 

  Serial.println("3. REFP0/REFN0 as voltage reference, vRef measured by ADS1220");
  ads.setRefp0Refn0AsVefAndCalibrate();
  result = ads.getVRef_V();
  Serial.print("Reference, measured     [V]: ");
  Serial.println(result,3); 

  Serial.println("4. REFP1/REFN1 as voltage reference, vRef measured by ADS1220");
  ads.setRefp1Refn1AsVefAndCalibrate();
  result = ads.getVRef_V();
  Serial.print("Reference, measured     [V]: ");
  Serial.println(result,3);
  
  Serial.println("5. AVDD/AVSS as voltage reference, vRef measured by ADS1220");
  ads.setAvddAvssAsVrefAndCalibrate();
  result = ads.getVRef_V();
  Serial.print("Reference, measured     [V]: ");
  Serial.println(result,3); 
 
  Serial.println("With 5. you can measure up to AVDD, just try.");
  ads.setCompareChannels(ADS1220_MUX_2_AVSS);
  result = ads.getVoltage_mV();
  Serial.print("AIN2 vs. AVSS          [mV]: ");
  Serial.println(result);

  ads.setIntVRef(); // switch back to standard vRef
  Serial.println();
 
  delay(2000);
}

 

Die Ausgabe

So sah die Ausgabe bei mir aus:

Ausgabe von ADS1220_set_voltage_references.ino
Ausgabe von ADS1220_set_voltage_references.ino

Erklärungen zum Sketch

In der Voreinstellung verwendet der ADS1220 die interne Referenzspannung. Wenn ihr zu einer externen Referenzspannung wechseln wollt, dann habt ihr zwei Möglichkeiten:

  1. Ihr legt mit setVRefSource() die Referenz-Spannungsquelle fest und gebt mit setVRefValue() ihren Wert an.
  2. Mit einer einzigen Anweisung legt ihr die Referenz-Spannungsquelle fest und lasst den ADS1220 den Referenzspannungswert messen. Dafür stehen euch die folgenden Funktionen zur Verfügung:
    1. setRefp0Refn0AsVefAndCalibrate(): REFP0/REFN0
    2. setRefp1Refn1AsVefAndCalibrate(): REFP1/REFN1
    3. setAvddAvssAsVrefAndCalibrate(): AVDD/AVSS

Das Datenblatt des ADS1220 gibt an, dass die Messung der externen Referenzen weniger genau ist als eine reguläre Messung. Man sollte diesen Fehler nicht überbewerten, denn auch die interne Referenzspannung hat einen gewissen Fehler (bis zu 3 Millivolt). Allerdings würde ich die Funktionen 2a-2c nicht zu häufig aufrufen, da der Referenzwert dann immer ein wenig schwankt.

Ich habe eine Versorgungsspannung (AVDD/AVSS) von ca. 5 Volt angelegt und die Spannung mit meinem Multimeter und über setAvddAvssAsVrefAndCalibrate() und getVRef_V() überprüft. Die Abweichung lag bei maximal einem Millivolt.

Wozu diese ganzen Referenzen?

Bei vielen Anwendungen, wie Wheatstone Brücken oder Widerstandsthermometern, ist man gar nicht an absoluten Spannungswerten interessiert, sondern vielmehr an Widerständen, die über Spannungsverhältnisse und Referenzwiderstände ermittelt werden. Indem man einen Strom über den gesuchten Widerstand und über den Referenzwiderstand schickt und den Spannungsabfall über dem Referenzwiderstand als Referenzspannung verwendet, lässt sich der gesuchte Widerstand unabhängig von Stromschwankungen ermitteln. Das wird Thema des Folgebeitrages.

Einstellung von PGA und Gain

Die zu wandelnde Spannung kann im PGA (Programmable Gain Amplifier) verstärkt werden. Es stehen euch Verstärkungsfaktoren zwischen 1 und 128 zur Verfügung. Wie schon weiter oben beschrieben, verlasst ihr den linearen Bereich, wenn sich die Ausgangsspannung des PGA auf weniger als 200 Millivolt an AVDD oder AVSS nähert. Diese Beschränkung könnt ihr umgehen, indem ihr den PGA umgeht. Verwirrend ist, dass dann trotzdem Verstärkungen von 1, 2 oder 4 möglich sind.

Dies sind die Funktionen:

  • setGain(): setzt den Verstärkungsfaktor (1, 2, 4, 8, 16, 32, 64 oder 128).
  • getGainFactor(): liefert den tatsächlichen Verstärkungsfaktor als Byte-Wert. Der tatsächliche Verstärkungsfaktor kann unter Umständen von dem von euch gesetzten Wert abweichen (s.u.).
  • bypassPGA(true/false): umgeht den PGA bzw. schaltet ihn wieder zu.
  • isPGABypassed(): liefert true, wenn der PGA umgangen wird, sonst false. Auch hier kann der Wert von eurer Vorgabe abweichen.

Dabei gibt es noch ein paar Dinge zu beachten:

  • Wenn ihr einen Verstärkungsfaktor >= 8 setzt, wird der PGA nicht umgangen, und zwar unabhängig von euren Einstellungen, die ihr mit bypassPGA() vorgenommen habt.
  • Führt ihr eine single-ended Messung (AINx = AVSS) durch, dann muss der PGA umgangen werden und es sind nur Verstärkungen von 1, 2 und 4 möglich. Die Bibliothek stellt sicher, dass diese Regeln eingehalten werden. Ihr müsst euch nicht darum kümmern, solltet euch der Beschränkung aber bewusst sein.
  • Auch bei der Messung externer Referenzspannungen oder der Versorgungsspannung wird der PGA zwangsweise umgangen. Der Verstärkungsfaktor wird auf 1 reduziert.

Beispielsketch

Um zu veranschaulichen, wie sich der PGA Status und der Verstärkungsfaktor ändern, habe ich den folgenden Sketch geschrieben. Wie ihr seht, werden unter gewissen Bedingungen zwangsweise Änderungen der Einstellungen vor-, aber auch wieder zurückgenommen.

#include <ADS1220_WE.h>
#include <SPI.h>

#define ADS1220_CS_PIN    7
#define ADS1220_DRDY_PIN  6

ADS1220_WE ads = ADS1220_WE(ADS1220_CS_PIN, ADS1220_DRDY_PIN);

void setup(){
  Serial.begin(9600);
  ads.init();
  ads.setGain(ADS1220_GAIN_8); // set gain to 8
  ads.bypassPGA(false);  // redundant, since this is default, but I wanted to highlight this setting. 
}

void loop(){
  ads.setCompareChannels(ADS1220_MUX_0_1);
  float result = ads.getVoltage_mV();
  byte gain = ads.getGainFactor();  // queries the (effective) gain
  Serial.print("AIN0/AIN1 [mV]: ");
  Serial.println(result);
  Serial.print("PGA bypassed (0: no, 1: yes): ");  
  Serial.println(ads.isPGABypassed()); // queries if PGA is bypassed, here the result will be 0
  Serial.print("Current gain: ");  // 
  
  Serial.println(gain);
  Serial.println();
   
  ads.setCompareChannels(ADS1220_MUX_2_AVSS);
  result = ads.getVoltage_mV();
  gain = ads.getGainFactor();
  Serial.print("AIN2/AVSS [mV]: ");
  Serial.println(result);
  Serial.print("PGA bypassed (0: no, 1: yes): "); // the PGA will be bypassed
  Serial.println(ads.isPGABypassed());
  Serial.print("Current gain: ");  // gain will be reduced to 4. 
  Serial.println(gain);
  Serial.println();

  ads.setCompareChannels(ADS1220_MUX_AVDD_M_AVSS_4);
  result = ads.getVoltage_mV();
  gain = ads.getGainFactor();
  Serial.print("AVDD/AVSS [mV]: ");
  Serial.println(result*4.0);
  Serial.print("PGA bypassed (0: no, 1: yes): "); // the PGA will be bypassed
  Serial.println(ads.isPGABypassed());
  Serial.print("Current gain: ");  // gain will be reduced to 1. 
  Serial.println(gain);
  Serial.println();
 
  delay(2000);
}

 

Hier noch die Ausgabe:

Ausgabe von ADS1220_WE_pga_settings.ino
Ausgabe von ADS1220_WE_pga_settings.ino

IDAC Einstellungen

Mit dem ADS1220 könnt ihr an zwei Pins konstante Ströme zwischen 10 Mikroampere und 1.5 Milliampere einstellen. Diese Ströme werden als IDAC1 und IDAC2 bezeichnet. Weil der Strom bekannt ist und der ADS1220 die Spannung misst, die notwendig ist, um den Strom zu erzeugen, ermöglicht euch das die bequeme Messung von Widerständen. Eine typische Anwendung ist die Messung der Temperatur mit Widerstandsthermometern.

Ich möchte die Funktion anhand eines ganz einfachen Beispiels erklären. Dazu setzt Ihr einen 10 kΩ Widerstand zwischen AIN0 und AIN1 bzw. zwischen AIN2 und  AIN1. AIN1 verbindet ihr mit AVSS.

ADS1220 IDAC Test - schematische Darstellung
IDAC Test – schematische Darstellung

Dann ladet den folgenden Sketch hoch:

#include <ADS1220_WE.h>
#include <SPI.h>

#define ADS1220_CS_PIN    7 // chip select pin
#define ADS1220_DRDY_PIN  6 // data ready pin 

ADS1220_WE ads = ADS1220_WE(ADS1220_CS_PIN, ADS1220_DRDY_PIN);

void setup() {
  Serial.begin(9600);
  if (!ads.init()) {
    Serial.println("ADS1220 is not connected!");
    while (1);
  }
  //  ads.setCompareChannels(ADS1220_MUX_0_1); default seeting for this test
  ads.setGain(ADS1220_GAIN_1);
  ads.bypassPGA(true); // since the negative voltage is AVSS, we have to bypass PGA 

  /* The ADS1220 can provide two excitation currents, IDAC1 and IDAC2. It takes up to 200µs
     until the current is set up. The library includes a delay, so you don't have to add one.
     You can switch IDAC1 and IDAC2 on and off individually but the current is the same for 
     both.
     The ADS1220 will try to provide the voltage needed for the current you have chosen. This 
     voltage shall not exceed AVDD - 0.9V.

     ADS1220_IDAC_OFF         // default
     ADS1220_IDAC_10_MU_A     // set IDAC1/IDAC2 to 10 µA
     ADS1220_IDAC_50_MU_A     // 50 µA
     ADS1220_IDAC_100_MU_A    // 100 µA
     ADS1220_IDAC_250_MU_A    // 250 µA
     ADS1220_IDAC_500_MU_A    // 500 µA
     ADS1220_IDAC_1000_MU_A   // 1000 µA
     ADS1220_IDAC_1500_MU_A   // 1500 µA
  */
 ads.setIdacCurrent(ADS1220_IDAC_100_MU_A);

  /* You can choose to which pin IDAC1 and IDAC2 are directed. The parameters are self-explaining.
     ADS1220_IDAC_NONE
     ADS1220_IDAC_AIN0_REFP1
     ADS1220_IDAC_AIN1
     ADS1220_IDAC_AIN2
     ADS1220_IDAC_AIN3_REFN1
     ADS1220_IDAC_REFP0
     ADS1220_IDAC_REFN0
  */
  //  ads.setIdac1Routing(ADS1220_IDAC_AIN0_REFP1);
  //  ads.setIdac2Routing(ADS1220_IDAC_AIN2);

} // end of setup()

void loop() {
  float result = 0.0;

  ads.setIdac1Routing(ADS1220_IDAC_AIN0_REFP1);
  result = ads.getVoltage_mV(); 
  Serial.print("Voltage, caused by IDAC1 [mV]:           ");
  Serial.println(result, 3);  // will be roughly 1000 mA, according to Ohm's law

  ads.setIdac2Routing(ADS1220_IDAC_AIN2);
  result = ads.getVoltage_mV(); 
  Serial.print("Voltage, caused by IDAC1 and IDAC2 [mV]: ");
  Serial.println(result, 3);  // will be roughly 2000 mA, according to Ohm's law

  ads.setIdac1Routing(ADS1220_IDAC_NONE);
  ads.setIdac2Routing(ADS1220_IDAC_NONE);
  Serial.println();
  
  delay(2000);
}

 

Ausgabe

Und so oder ähnlich sollte dann eure Ausgabe aussehen:

Ausgabe von ADS1220_IDAC_test.ino
Ausgabe von ADS1220_IDAC_test.ino

Erklärungen

Im Prinzip ist der Umgang mit den IDACs also sehr einfach:

  • setIdacCurrent(ADS1220_IDAC_100_MU_A); stellt den Strom auf den gewünschten Wert ein, hier sind es 100 Mikroampere. IDAC1 und IDAC2 sind hinsichtlich der Stromstärke identisch.
  • setIdac1Routing(ADS1220_IDAC_AIN0_REFP1); legt den Pin AIN0/REFP1 als Stromquelle für IDAC1 fest. Für IDAC2 geht ihr entsprechend vor, sofern ihr zwei Ströme braucht. Mit dem Parameter ADS1220_IDAC_NONE stellt ihr den Strom ab.

Dann messt ihr einfach die Spannung am Stromausgangspin und errechnet den Widerstand. Wie ihr seht, könnt ihr IDAC1 und IDAC2 auch zusammenführen.

Dabei müsst ihr beachten, dass ihr den Strom nur so hoch wählt, dass der ADS1220 die dazu notwendige Spannung auch tatsächlich erzeugen kann. Die IDAC-Spannung soll laut Datenblatt AVDD minus 0.9 Volt nicht überschreiten.

Die IDACs haben laut Datenblatt eine typische Abweichung von einem Prozent. Das ist also keine Präzisionsmessung. Besser ist die Verwendung von Referenzwiderständen (siehe nächster Beitrag).

Weitere Einstellungen des ADS1220

Die weiteren Einstellungen des ADS1220 haben weniger „Nebenwirkungen“. Ich habe sie deswegen in einem Sketch zusammengefasst und auch die schon behandelten Funktionen noch einmal mit aufgenommen. Das sollte eine ganz komfortable Vorlage für eure eigenen Sketche sein. Ihr müsst lediglich die entsprechenden Funktionen entkommentieren und eure Parameter einfügen. Der Sketch heißt ADS1220_WE_all_settings.ino und ist, so wie die vorherigen Beispiele, fester Teil der Bibliothek

Der Sketch

Hier zunächst der Sketch:

#include <ADS1220_WE.h>
#include <SPI.h>

#define ADS1220_CS_PIN    7 // chip select pin
#define ADS1220_DRDY_PIN  6 // data ready pin 

/* Create your ADS1220 object */
ADS1220_WE ads = ADS1220_WE(ADS1220_CS_PIN, ADS1220_DRDY_PIN);
/* Alternatively you can also pass the SPI object as reference */
// ADS1220_WE ads = ADS1220_WE(&SPI, ADS1220_CS_PIN, ADS1220_DRDY_PIN);

void setup() {
  Serial.begin(9600);
  if (!ads.init()) {
    Serial.println("ADS1220 is not connected!");
    while (1);
  }

  /* General settings / commands */
  //  ads.start(); // wake up from power down and start measurement
  //  ads.reset(); // resets the ADS1220; all settings will change to default
  //  ads.powerDown(); // send the ADS1220 to sleep
  //  ads.setSPIClockSpeed(8000000); // set SPI clock speed, default is 4 MHz

  /* You set the channels to be measured with setCompareChannels(); You
     can choose the following parameters:
     Parameter                  Pos. Input     Neg. Input         Comment
     ADS1220_MUX_0_1              AIN0           AIN1             default
     ADS1220_MUX_0_2              AIN0           AIN2
     ADS1220_MUX_0_3              AIN0           AIN3
     ADS1220_MUX_1_2              AIN1           AIN2
     ADS1220_MUX_1_3              AIN1           AIN3
     ADS1220_MUX_2_3              AIN2           AIN2
     ADS1220_MUX_1_0              AIN1           AIN0
     ADS1220_MUX_3_2              AIN3           AIN2
     ADS1220_MUX_0_AVSS           AIN0           AVSS (=AGND)   single-ended
     ADS1220_MUX_1_AVSS           AIN1           AVSS           single-ended
     ADS1220_MUX_2_AVSS           AIN2           AVSS           single-ended
     ADS1220_MUX_3_AVSS           AIN3           AVSS           single-ended
     ADS1220_MUX_REFPX_REFNX_4   REFP0/REFP1   REFN0/REFN1     (REFPX-REFNX)/4; PGA bypassed
     ADS1220_MUX_AVDD_M_AVSS_4    AVDD           AVSS          (AVDD-AVSS)/4; PGA bypassed
     ADS1220_MUX_AVDD_P_AVSS_2    AVDD           AVSS           AVDD+AVSS)/2
     The last three modes use the internal reference (2.048 V) and gain = 1, independent of
     your settings.
  */
  //  ads.setCompareChannels(ADS1220_MUX_0_3);

  /* You can choose a gain between 1 (default) and 128 using setGain() if PGA is enabled
     (default). If PGA is disabled you can still choose a gain factor up to 4. If PGA is
     enabled, the amplified voltage shall be between AVSS + 200mV and AVDD - 200mV. Outside
     this range linearity drops. For details check the data sheet, section 8.3.2.1.

     If you apply a single-ended mode (negative AINx = AVSS), PGA must be bypassed. Accordingly,
     the maximum gain is 4. The library does these settings automatically.

     For the measurement of reference voltages / supply voltage PGA will also be bypassed. In
     this case gain is 1.

     The parameters you can choose for setGain() are:
     ADS1220_GAIN_X with X = 1,2,4,8,16,32,64 or 128

     With getGainFactor() you can query the gain. The function returns the effective gain and
     not the gain set in the register. Under certian conditions thes are are different. For
     example, the effective gain is set to 1 when external references are measured.
  */
  ads.setGain(ADS1220_GAIN_1);
  //  ads.getGainFactor(); // returns the effective gain as a byte value
  //  ads.bypassPGA(true); // true disables PGA, false enables PGA
  //  ads.isPGABypassed(); // returns true, if PGA is bypassed

  /* The data rate level with setDataRate(). The data rate itself also depends on the operating
     mode and the clock. If the internal clock is used or an external clock with 4.096 MHz the data
     rates are as follows (per second):

      Level               Normal Mode      Duty-Cycle      Turbo Mode
     ADS1220_DR_LVL_0          20               5               40         (default)
     ADS1220_DR_LVL_1          45              11.25            90
     ADS1220_DR_LVL_2          90              22.5            180
     ADS1220_DR_LVL_3         175              44              350
     ADS1220_DR_LVL_4         330              82.5            660
     ADS1220_DR_LVL_5         600             150             1200
     ADS1220_DR_LVL_6        1000             250             2000

     The higher the data rate, the higher the noise (tables are provided in section 7.1 in the
     data sheet). In single-shot mode the conversion times equal the times in Normal Mode.
  */
  //  ads.setDataRate(ADS1220_DR_LVL_2);

  /* Using setOperatingMode() you choose the operating mode. Possible parameters are:
     ADS1220_NORMAL_MODE      ->  Normal Mode
     ADS1220_DUTY_CYCLE_MODE  ->  Duty cycle mode. Saves power, but noise is higher.
     ADS1220_TURBO_MODE       ->  Turbo Mode for fast measurements
  */
  //  ads.setOperatingMode(ADS1220_DUTY_CYCLE_MODE);

  /*  You can choose between a continuous and a single-shot (on demand) mode with
      setConversionMode(). Parameters are:
      ADS1220_SINGLE_SHOT (default)
      ADS1220_CONTINUOUS
  */
  // ads.setConversionMode(ADS1220_CONTINUOUS);

  /* In order to obtain temperature values, choose enableTemperatureSensor(true); false will
     disable the temperature sensor. As long as the temperature sensor is enabled the ADS1220
     is blocked for this task. To obtain voltage values, you have to switch the sensor off. The
     temperature is queried with getTemperature();
  */
  //  ads.enableTemperatureSensor(true);
  //  ads.getTemperature(); // returns temperature as float

  /*
     setVRefSource() sets the the reference voltage source. Parameters are:
     ADS1220_VREF_INT          int. reference 2.048 V (default)
     ADS1220_VREF_REFP0_REFN0  ext. reference = Vrefp0 - Vrefn0
     ADS1220_VREF_REFP1_REFN1  ext. reference = Vrefp1 - Vrefn1 (REFP1=AIN0, REFN1=AIN3)
     ADS1220_VREF_AVDD_AVSS    ext. reference = supply voltage

     If you use the above options you also have to set the value of vRef "manually":
     setVRefValue_V(vRef in volts);

     Alternatively, you can set the reference voltage source and let the ADS1220 measure
     the reference. Be aware that this is not a measurement with highest precision.
     "Calibration" might be a bit misleading. You should take the lowest data rate (default)
     for most accurate results. You can use the following functions:
     setRefp0Refn0AsVefAndCalibrate();
     setRefp1Refn1AsVefAndCalibrate();
     setAvddAvssAsVrefAndCalibrate();
     setIntVRef();
     The latter function sets the default settings.

     Be aware that VREFPx must be >= VREFNx + 0.75V.
  */
  //  ads.setVRefSource(ADS1220_VREF_REFP0_REFN0);
  //  ads.setVRefValue_V(3.312);  // just an example
  //  or:
  //  ads.setRefp0Refn0AsVefAndCalibrate(); //or:
  //  ads.setRefp1Refn1AsVefAndCalibrate(); //or:
  //  ads.setAvddAvssAsVrefAndCalibrate(); //or:
  //  ads.setIntVRef();
  //  to query VRef:
  //  ads.getVRef_V(); // returns VRef as float

  /* You can set a filter to reduce 50 and or 60 Hz noise with setFIRFilter(); Parameters:
     ADS1220_NONE       no filter (default)
     ADS1220_50HZ_60HZ  50Hz and 60Hz filter
     ADS1220_50HZ       50Hz filter
     ADS1220_60HZ       60Hz filter
  */
  //  ads.setFIRFilter(ADS1220_50HZ_60HZ);

  /* When data is ready the DRDY pin will turn from HIGH to LOW. In addition, also the DOUT pin
     can be set as a data ready pin. The function is setDrdyMode(), parameters are:
     ADS1220_DRDY        only DRDY pin is indicating data readiness  (default);
     ADS1220_DOUT_DRDY   DRDY and DOUT pin indicate data readiness
  */
  //  ads.setDrdyMode(ADS1220_DOUT_DRDY);


  /* There is a switch between AIN3/REFN1 and AVSS. You can use this option to save power in
     bridge sensor applications.
     ADS1220_ALWAYS_OPEN    The switch is always open.
     ADS1220_SWITCH         Switch automatically closes when the START/SYNC command is sent
                            and opens when the POWERDOWN command is issued.
  */
  // ads.setLowSidePowerSwitch(ADS1220_SWITCH);

  /* The ADS1220 can provide two excitation currents, IDAC1 and IDAC2. It takes up to 200µs
     until the current is set up. The library includes a delay, so you don't have to add one.
     You can switch IDAC1 and IDAC2 on and off individually but the current is the same for 
     both.
     The ADS1220 will try to provide the voltage needed for the current you have chosen. This 
     voltage shall not exceed AVDD - 0.9V.

     ADS1220_IDAC_OFF         // default
     ADS1220_IDAC_10_MU_A     // set IDAC1/IDAC2 to 10 µA
     ADS1220_IDAC_50_MU_A     // 50 µA
     ADS1220_IDAC_100_MU_A    // 100 µA
     ADS1220_IDAC_250_MU_A    // 250 µA
     ADS1220_IDAC_500_MU_A    // 500 µA
     ADS1220_IDAC_1000_MU_A   // 1000 µA
     ADS1220_IDAC_1500_MU_A   // 1500 µA
  */
  // ads.setIdacCurrent(ADS1220_IDAC_50_MU_A);

  /* You can choose to which pin IDAC1 and IDAC2 are directed. The parameters are self-explaining.
     ADS1220_IDAC_NONE
     ADS1220_IDAC_AIN0_REFP1
     ADS1220_IDAC_AIN1
     ADS1220_IDAC_AIN2
     ADS1220_IDAC_AIN3_REFN1
     ADS1220_IDAC_REFP0
     ADS1220_IDAC_REFN0
  */
  //  ads.setIdac1Routing(ADS1220_IDAC_AIN0_REFP1);
  //  ads.setIdac2Routing(ads1220IdacRouting route);

} // end of setup()

void loop() {
  float result = 0.0;

  /* The following functions query measured values from the ADS1220. If you request values in
     single-shot mode, the conversion will be initiated automatically. The value will be delivered
     once DRDY goes LOW. This ensures that you will receive "fresh" data. This is particularly
     important when you change channels.
  */
  result = ads.getVoltage_mV(); // get result in millivolts
  //  ads.getVoltage_muV(); // get result in microvolts
  //  ads.getRawData();  // get raw result (signed 24 bit as long int)
  //  ads.getTemperature();  // get temperature (you need to enable the T-sensor before);
  Serial.println(result, 3);
  
  delay(2000);
}

 

Erklärungen zum Sketch bzw. zu den Einstellungen

Allgemeine Einstellungen / Funktionen

  • start(): weckt den ADS1220 aus dem Power Down Modus. Überdies startet die Funktion einzelne oder kontinuierliche Messungen. Allerdings braucht ihr sie nicht für diesen Zweck, da start() automatisch aufgerufen wird, wenn ihr Messwerte anfordert.
  • reset(): führt einen Reset des ADS1220 durch.
  • powerDown(): schickt den ADS1220 in den Power Down Modus. In diesem Zustand sinkt der Stromverbrauch auf typischerweise 0.1 Mikroampere.
  • setSPIClockSpeed(): stellt die SPI Taktfrequenz ein. Voreinstellung ist 4 MHz.

Einstellung des Betriebsmodus

Ihr wählt den Betriebsmodus mit setOperatingMode(). Folgende Parameter stehen euch zur Verfügung:

  • ADS1220_NORMAL_MODE
  • ADS1220_DUTY_CYCLE_MODE
  • ADS1220_TURBO_MODE

Der ADS1220 arbeitet nach dem Oversampling Prinzip. Vereinfacht ausgedrückt werden die Spannungen in hoher Frequenz (Modulatorfrequenz) gewandelt und das Ergebnis gemittelt. Je mehr Messungen zu Grunde liegen, desto geringer das Rauschen.

Voreingestellt ist der „Normal Mode“. Die Modulatorfrequenz beträgt im Normal Mode ein 16tel der Frequenz des ADS1220. Bei Verwendung des internen 4.096 MHz Oszillators ist die Modulatorfrequenz entsprechend 256 kHz.

Im Duty-Cycle Mode arbeitet der ADS1220 wie im Normal Mode, aber mit einem Duty-Cycle von 25 %. Die restlichen 75 % der Zeit befindet sich der ADS1220 im Power Down Mode. Das spart erheblich Energie, erhöht aber das Rauschen. Bei einer Datenrate x im Duty-Cycle Mode ist das Rauschen vergleichbar mit dem Rauschen bei einer Datenrate von 4x im Normal Mode.

Im Turbo Mode wird die Modulatorfrequenz verdoppelt. Dadurch werden mehr Wandlungen in derselben Zeit durchgeführt. Entsprechend nimmt das Rauschen gegenüber dem Normal Mode ab, sofern ihr vergleichbare Datenraten anwendet. Außerdem könnt ihr im Turbo Mode die höchsten Datenraten erzielen.

Die Datenrate einstellen

Die Datenrate legt ihr mit setDataRate() fest.  Als Parameter stehen euch die Level ADS1220_DR_LVL_x mit x = 0 bis 6 zur Verfügung. Die tatsächliche Datenrate in SPS hängt vom Level und dem Betriebsmodus ab. Eine Tabelle findet ihr im Sketch (oder im Datenblatt).

Die maximale Datenrate beträgt 2000 SPS. Ich habe das überprüft, indem ich die benötigte Zeit für 2000 Messungen im kontinuierlichen Modus benötigte Zeit bestimmt habe. Das Ergebnis waren 1031 Millisekunden, also nur ein kleines bisschen langsamer als theoretisch erwartet. Ich habe den kontinuierlichen Modus gewählt, da dort die Zeit für die Anfrage des nächsten Messwertes keine Rolle spielt. Im Single-Shot Modus betrug die Zeit für 2000 Messungen 1327 Millisekunden.

Generell gilt: je höher die Datenrate, desto geringer das Oversampling und desto höher das Rauschen. Im Datenblatt gibt es dazu eine Reihe von Tabellen. Besonders aufschlussreich finde ich dabei die Angabe in ENOB (=effective Number of Bits), also die tatsächlich rauschfreien Bits. Bei langsamster Datenrate im Normal Mode beträgt die Auflösung ca. 20 Bit.

Den Konversionsmodus einstellen

Den Konversionsmodus stellt ihr mit setConversionMode() ein. Die beiden möglichen Parameter sind:

  • ADS1220_SINGLE_SHOT: Single-Shot = „Auf Anfrage“
  • ADS1220_CONTINUOUS: kontinuierlich

Grundsätzlich ist der Single-Shot Modus vorzuziehen, da er energetisch günstiger ist. Das liegt daran, dass der ADS1220 nach der jeweiligen Messung in den Power Down Modus geht.

In beiden Modi wartet der Mikrocontroller nach der Anfrage, bis der Messwert vorliegt. In einer zukünftigen Version der Bibliothek werde ich einen Interrupt-basierten Ansatz hinzufügen.

50 und 60 Hz Filter

Ihr könnt einen 50 Hz Filter, einen 60 Hz Filter oder beide Filter gleichzeitig zuschalten. Die Funktion lautet setFIRFilter(). Dabei steht FIR für „finite impulse response“. Die Parameter findet ihr im Sketch weiter oben. Für weitere Details schaut bitte in das Datenblatt.

DRDY Einstellungen

Standardmäßig geht der DRDY Pin auf LOW, wenn ein Messwert zur Abfrage vorliegt. Zusätzlich lässt sich der DOUT Pin (=MISO) so konfigurieren, dass auch er auf LOW geht. Die Funktion dazu heißt setDrdyMode(), die zur Auswahl stehenden Parameter sind:

  • ADS1220_DRDY: nur der DRDY Pin zeigt einen neuen Messwert an.
  • ADS1220_DOUT_DRDY: DRDY und DOUT zeigen einen neuen Messwert an.

Messungen bei bipolarer Spannungsversorgung

Wenn ihr eine bipolarer Spannungsversorgung verwendet und einen symmetrischen Spannungsbereich für eure Messungen abdecken wollt, dann müsst ihr Differenzmessungen durchführen. Hier eine Beispielschaltung:

Beispielschaltung für Messungen bei bipolarer Spannungsversorgung
Beispielschaltung für Messungen bei bipolarer Spannungsversorgung

Für diese Schaltung wäre der Parameter ADS1220_MUX_0_1 für die Kanäle zu wählen.

Der ADS1220 – Fazit und Ausblick

Der ADS1220 ist ein leistungsfähiger A/D-Wandler mit hoher Auflösung, hoher Verstärkung, hoher Datenrate, geringem Rauschen und geringem Stromverbrauch. Um den ADS1220 auszureizen und keinen „Blödsinn“ zu messen, muss man sich aber tiefer mit seinen technischen Details auseinandersetzen als das beispielsweise bei dem 16-Bit A/D-Wandler ADS1115 der Fall ist. 

Seine Vorzüge spielt der ADS1220 bei Anwendungen aus, bei denen geringe Spannungsdifferenzen gemessen werden sollen, wie Wheatstone Brücken oder Thermoelemente. Im nächsten Beitrag werde ich diese Anwendungen vorstellen.

Gegenüber dem 24-Bit A/D-Wandler HX711 ist der ADS1220 universeller einsetzbar. Den HX711 dürften die meisten kennen, die sich schon mal mit Wägezellen beschäftigt haben. Sein Messbereich ist je nach Einstellung auf 20 oder 40 mV begrenzt. 

19 thoughts on “ADS1220 4-Kanal, 24-Bit A/D-Wandler

  1. Hallo Wolfgang,

    Vielen Dank für deine Antwort.

    Aleso, ich habe jetzt mit der Formel, die du geschrieben hast, meine Rohdaten in Volt berechnet, nachdem ich die Referenzspannung gemessen habe.
    Die Versorgungsspannung des A/D-Wandlers ist 3,295 V, aber die gemessene Referenzspannung ist 3,114 V.
    Eigentlich habe ich kein Problem mit dem Kalibrierungsfaktor und der Kalibrierungsmethode.
    Am Anfang wusste ich nicht, was meine Messwerte bedeuten, aber jetzt nach deiner Hilfe hat sich alles geklärt.

    Viele Grüßen
    Hadi

  2. Hallo Herr Ewald,

    erst einmal vielen Dank für Ihre Beiträge, die mir bei meiner Masterarbeit sehr geholfen haben.
    Ich verwende DMS von HBM mit Vollbrückenschaltung, um die Druckkraft auf einen Stab zu messen.
    Dazu verwende ich Arduiuno 4 und SparkFun Qwiic Scale – NAU7802 (https://www.sparkfun.com/products/15242).
    Die NAU7802 ist über I2C mit QWICI an Arduino angeschlossen.
    Da ich eine wissenschaftliche Arbeit schreibe, muss ich meine Messdaten bzw. bei der Kalibrierung mit analytischen Berechnungen vergleichen.
    Außerdem habe ich keine Referenz (z.B. beim Wägen kann ich nach der Kalibrierung bekannte Gewichte auflegen, um zu sehen, ob mein Konzept richtig misst oder richtig kalibriert).

    Nun habe ich eine Frage zu den Ausgabedaten meines NAU7802.
    Ich erhalte immer 7-stellige Werte:

    bei 0 N ist es 2356251,914
    bei 45,74403 N (oder 4,66 kg) ist es 2361284,754

    1- Kann ich die Formel, die Sie geschrieben haben, verwenden, um die Ausgangsspannung zu berechnen?
    2- Kann ich die 3,3 Volt (Versorgungsspannung) als Referenzspannung betrachten?
    Da die Einstellung von NAU7802 ähnlich wie ADS1220.

    Anmerkung: Ich bin Maschinenbauer und habe keine Erfahrung mit Mikrocontrollern.
    Vielen Dank im Voraus für Ihre Antwort.

    Mit freundlichen Grüßen
    Hadi

    1. Hallo Hadi,

      vorausgesetzt, dass die Biegung des Stabes sich linear mit der auf ihn wirkenden Kraft ändert, solltest du (ich sage mal „du“ – ich bin Wolfgang!) einfach einen linearen Ansatz machen können. Du sagst, du hast keine Referenz, aber die hast ja doch nämlich Nullwert und den Wert bei 45,744307 N. Da du zwei Punkte hast, für 0 und für 45,74403 N, sollte die Formel für unbekannte Kräfte sein:

      Kraft [N] = 45,74403 * (Messwert – 2356251,914) / (2361284,754 – 2356251,914)

      Messwerte ohne Fehlerbetrachtung sind allerdings heikel. Idealerweise hast du noch mehr Messwerte mit bekannten Kräften und machst eine lineare Regression.

      Womit man oft zu kämpfen hat, sind schleichende Nullpunktverschiebungen durch dauerhafte Verformung des Stabes, thermische Einflüsse, Alterung des Klebers, usw. Wie bei einer Waage solltest du nach Möglichkeit eine Option vorsehen den Nullpunkt neu festzulegen (wie die Tara bei einer Waage). Die Steigung sollte auch auf Dauer konstant bleiben.

      Hoffe, das hilft. Viel Erfolg und hoffentlich auch Spaß!

      VG, Wolfgang

  3. Hallo Wolfgang Ewald,
    vielen dank für die genaue Beschreibung des ADS1220 ! Ich hab ihn für eine Logbox für eine Albedo Sensor Messstation an der Uni verwendet in Kombination mit dem lilygo-t-sim7000g-esp32. Wäre es möglich die Fritzing Datei für den ADS120 zu teilen ? Ich muss noch einen Elektro Schaltplan erstellen und leider gibt es den nicht in der Fritzing Libary und ich bin tatsächlich nicht in der Lage zu checken wie man neue Komponenten selbst erstellt. Viele Grüße und Großen Dank,
    Tanja

    1. Hi Tanja, das würde ich gerne tun, allerdings habe ich selbst auch keine Fritzing Datei. Ich habe die Grundlage der Schaltungen mit Fritzing erstellt und dann den ADS1220 mit Photoshop „hineingepixelt“, sprich als Ebene eingefügt. Da ich in Photoshop eine gewisse Routine habe, geht das bei mir wesentlich schneller als neue Fritzing Bauteile zu erstellen. Ich kann die gerne eine Photoshop Datei zusenden, falls dir das etwas nützt. Viel Erfolg mit deiner Messstation!
      VG, Wolfgang

  4. Hallo Wolfgang,

    super hilfreicher und toller Artikel, vielen Dank für deine Arbeit!

    Habe mir gerade deine Lib von Git geladen und mir deine Beispiele angesehen, da ist mir auf Anhieb etwas aufgefallen wo ich nicht sicher bin ob das passen kann:

    In deiner lib in der Methode setIntVref(), da verwendest du dann „ADS1220_VREF_REFP1_REFN1“ für deine setVRefSource Funktion:

    void ADS1220_WE::setIntVRef(){
    setVRefSource(ADS1220_VREF_REFP1_REFN1);
    vRef = 2.048;
    }

    In deinem Header von deiner Biespieldatei „ADS1220_WE_set_reference_voltage.ino“ steht allerdings:

    * setVRefSource() sets the the reference voltage source. Parameters are:
    * ADS1220_VREF_INT int. reference 2.048 V (default)
    * ADS1220_VREF_REFP0_REFN0 ext. reference = Vrefp0 – Vrefn0
    * ADS1220_VREF_REFP1_REFN1 ext. reference = Vrefp1 – Vrefn1 (REFP1=AIN0, REFN1=AIN3)
    * ADS1220_VREF_AVDD_AVSS ext. reference = supply voltage

    –> der default value für die Referenzspannung wäre also die internen 2.048V, aber in deiner Methode übergibst du dem Register 0x80, das wäre dann ja nicht mehr die interne 2.048V Spannung?!

    Hab ich einen Denkfehler?

    Schöne Grüße,
    Markus

    1. Hallo Markus, du hast absolut recht. Da muss in setIntVRef natürlich ADS1220_VREF_INT übergeben werden. Der Beispielsketch ist so gestrickt, dass der Fehler nicht zutage tritt. Aber interessant, dass es noch nirgendwo zu einem Problem geführt hat – oder es hat sich nur niemand gemeldet. Auf jeden Fall: Vielen Dank! Die korrigierte Version liegt schon auf GitHub. Es dauert im Normalfall ein paar Stunden, bis die Arduino IDE auf das Update hinweist.
      VG, Wolfgang

      1. Super,

        danke fürs schnelle fixen! Nichtsdestotrotz bin ich froh, dass ich hier eine Library komplett selbst schreiben muss 🙂

        Lg,
        Markus

  5. Hallo Herr Ewald.

    Ich habe mal eine grundsätzlich Frage.
    Mit dem Arduino kann ich ja schon Spannungen messen, wenn auch nur mit 10 Bit Auflösung (immerhin). Bei dem umgekehrten Weg eine beliebige Spannung als Ausgabe an einem Pin zu verfügung zu stellen, wie mache ich das? Mit analogWrite() bekomme ich nur eine Pulsweitenmodulation, also abwechselnd 5V und 0V. Ich will aber beispielsweise genau 2 Volt am Pin ausgegeben bekommen.
    Ich dachte so ein DA Wandler kann auch in die andere Richtung digitale Werte in analoge Werte zur Verfügung stellen, geht aber scheinbar nicht, oder wie mach ich das?
    Gruß,
    U.Knopf.

    1. Hallo Herr Knopf,

      der Arduino kann kein echtes analoges Signal erzeugen, sondern tatsächlich nur ein PWM Signal. Für viele Anwendungen ist das OK, z.B. wenn man eine LED dimmen möchte. Wenn Sie ein echtes anologes Signal brauchen und Ihnen eine Spannung zwischen 0 und 3.3 Volt reicht, dann könnten Sie einen ESP32 nehmen:
      https://wolles-elektronikkiste.de/esp32-mit-arduino-code-programmieren#analog_pins

      Alternativ können Sie das PWM Signal mit einem RC-Glied glätten.

      Oder Sie besorgen sich einen externen DAC wie den MCP4725. Den gibt es auch fertig als Modul.

      VG, Wolfgang Ewald

  6. Hi,

    vorab möchte ich mich (wieder) für Deine Arbeit bedanken, ich schaue oft und gern auf Deine informative Seite!
    Ich habe gestern sehr lange das Datenblatt zum ADS1220 studiert, und habe für mich letztendlich die Bedingungen
    für VCM (MIN) für mich als Stolperfalle identifiziert.

    Liege ich richtig mit der Aussage:
    Um auch sehr kleine Spannungen VCM (MIN) im Differential Modus messen zu können, bleibt als Lösung nur,
    den ADS1220 mit einer negativen AVSS (-2.5V) und AVDD (+2.5V) zu betreiben!?

    VCM (MIN) ≥ AVSS + 0.2 V + ½ Gain · VIN (MAX)
    VCM (MAX) ≤ AVDD – 0.2 V – ½ Gain · VIN (MAX)

    Zusatzbedingung:
    VCM (MIN) ≥ AVSS + ¼ (AVDD – AVSS)

    Gruß André

    1. Hallo André,

      ja das ist in der Tat ein Nachteil des ADS1220, wenn man ihn als universellen A/D-Wandler einsetzen möchte. Er ist wirklich vorrangig für Thermoelemente, Widerstandsthermometer und Wheatstone Brücken konstruiert. Auf diese Anwendungen gehe ich im nächsten Beitrag ein. Unter Umständen, aber das hängt von der Anwendung ab, gibt es Möglichkeiten, das Problem zu umgehen. Im Datenblatt ist das Thermoelement als Anwendungsbeispiel mit Schaltung aufgeführt (Abschnitt 9.2.1). Das Thermoelement liefert selbst bei hohen Temperaturen (bzw. Differenzen) nur wenige Millivolt als. Der Trick besteht darin, die zwei Ausgänge des Thermoelemente mit sehr großen Widerständen (> 1 MOhm) gegen GND bzw. AVDD in einen mittleren Spannungsbereich zu zwingen. Wie zuvor erwähnt, im Detail komme ich dazu im nächsten Beitrag.
      VG, Wolfgang

      1. Hi,

        wenn ich in der Formel VCM (MIN) ≥ AVSS + 0.2 V + ½ Gain · VIN (MAX)
        AVSS zu -2.5V setze, dann darf VCM auch einen Pegel um den GND haben, also eine sehr kleine Spannung sein. Ich muss halt in den saueren Apfel beißen, anstatt einer Betriebsspannung dann 2 oder 3 Betriebsspannungen zu benötigen (-2.5V/ +2.5V für AVSS/AVDD && evtl. 3.3V oder 5.0V für die Digitalseite.
        Freilich geht das mit anderen ADCs einfacher …

        Gruß André

        1. Wie schon gesagt: man kann keine kleinen Spannungen messen, wenn man sich AVSS bzw. AVDD auf weniger als 200 mV nähert. Das gilt für die Ausgangsspannung des PGA, um genau zu sein. Ohne PGA gilt die Einschränkung nicht, allerdings ist die Verstärkung dann auf max. Faktor 4 beschränkt. Also ja, wenn man Spannungen gegen AVSS um 0 Volt herum messen möchte, wäre eine Möglichkeit eine bipolare Spannungsversorgung zu verwenden. Das heißt aber nicht, dass man generell zu dieser Lösung greifen muss, wenn man kleine Spannungsdifferenzen messen möchte. Ein Beispiel habe ich genannt, das Datenblatt liefert noch mehr und im Detail ist das Gegenstand des nächsten Beitrages.

          Ich kann nur noch einmal betonen: der ADS1220 spielt seine Vorteile bei bestimmten Anwendungen aus. Er ist ein Spezialist. Wer einen A/D-Wandler sucht, der universell zwischen 0 und 3.3 oder 5 Volt sucht und sich keine Gedanken um Einschränkungen machen will, der soll lieber z.B. zum ADS1115 greifen. Wer sehr präzise Messungen mit RTCs, NTCs, Thermoelementen oder Wheatstone Brücken durchführen möchte, dem empfehle ich den ADS1220.

  7. Hallo Wolfgang,
    ein sehr interessanter und für mich nützlicher Beitrag.
    Wie immer super aufbereitet und sehr verständlich erklärt.
    Vielen Dank und Grüße,
    Karl Zapf

  8. 👍
    Beim Lesen kam mir folgende Frage.
    Ich benutze einen ESP 32 um Daten von einem Sensor BME 680 auszulesen.
    Versorge ich den ESP 32 über den USB Anschluss (Voltage Regulator) und setze zwischen den Messintervallen ein Time Delay, gibt der BME680 Gas Resistance Werte von 10 kOhm zurück.
    Nutze ich eine Batterie und setze Deep Sleep (Hibernation) zwischen den Messintervallen liegt der Wert Gas Resistance bei 2 kOhm.
    Ich habe keine Erklärung dafür. Könnte es an dem hier beschriebenen ‚schlechten‘ internen A/D Wandlern des ESP liegen?

    1. Hallo Peter, der A/D Wandler des ESP32 ist nur in einem begrenzten Bereich linear und selbst in dem linearen Bereich liegen die Messwerte ca. 15 Milliampere neben den realen Werten. Das habe ich hier beschrieben, falls es dich interessiert:

      https://wolles-elektronikkiste.de/esp32-mit-arduino-code-programmieren#analog_pins

      Allerdings dürfte das nichts mit dem Problem zu tun haben, da der A/D-Wandler des ESP32 ja eigentlich gar nicht zum Einsatz kommt. Die Messwerte werden per I2C oder SPI aus den Registern des BME680 ausgelesen. Ich würde eher vermuten, dass es mit dem Deep Sleep zu tun hat. Geht denn der BME680 auch schlafen, wenn der ESP32 in den Deep Sleep geht? Unter Umständen braucht der Gassensor schlicht etwas Zeit bis er auf Temperatur kommt und sich stabilisiert. Ich würde als erstes mal probieren, nach dem Aufwachen mehrere Messwerte in bestimmten Zeitabständen zu messen, bevor du ihn wieder schlafen schickst. Z.B. alle 15 Sekunden und das ganze über ein paar Minuten hinweg. Tut sich dann etwas beim Messwert? Ich habe nicht so viel Erfahrung mit dem BME680, aber ich habe mal ins Datenblatt geschaut. Es gibt für den Gasssensor verschiedene Modi, nämlich Continuous, Low Power und Ultra Low Power. Falls du einen der Low Power Modi eingestellt hast, kannst du mal testweise auf Continuous umstellen (wenn die Bibliothek die Einstellung zulässt)? Bekommst du dann evtl. schneller stabile Werte? Wie gesagt, alles Vermutungen.
      Viel Erfolg und viele Grüße!

      1. Ja, die Daten kommen natürlich über den I2C rein. Insofern macht meine Frage kein Sinn. Mittlerweile bin ich aber am Ende meines Lateins und greife nach jedem denkbaren Strohhalm.
        Die Stromversorgung des BME680 hatte ich auch schon im Verdacht, da ich festgestellt habe, dass die Stabilität sich auf die Messwerte auswirkt.
        Ich schalte die Stromversorgung über einen NPN Transistor, der durch eine PIN am ADC2 des ESP32 gesteuert wird. Da das Ganze etwas „wackelig“ war (ein Artikel von Dir hat mich auf die Spur gebracht – Danke Dir!!), habe ich das Gate des Transistors nochmal gegen Masse gezogen. Jetzt „wackelt“ da nichts mehr.
        Über ein längeres Warmup habe ich auch schon nachgedacht. Ich nutze die original BSEC library von Bosch, die eine Stable Variable hat. Der zufolge, sollte das eigentlich so passen.
        Allerdings habe ich noch ein weiteres Problem – ich versorge das Ganze mit einer Batterie, bei einem ESP32 wohl eher eine schlechte Idee. Habe mir gerade ESP8266 bestellt, die im Deep Sleep weniger Strom verbrauchen und werden dann das längere Warmup ausprobieren.

Schreibe einen Kommentar

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