Ü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.
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:
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:
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:
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:
- Interne Referenzspannung (2.048 Volt)
- Externe Referenzspannung an REFP0/REFN0
- Externe Referenzspannung an REFP1/REFN1 (auf Kosten zweier Spannungseingänge)
- Versorgungsspannung (AVDD/AVSS) als Spannungsreferenz
Das folgende Schaltbild soll das verdeutlichen:
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:
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:
- Ihr legt mit
setVRefSource()
die Referenz-Spannungsquelle fest und gebt mitsetVRefValue()
ihren Wert an. - 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:
setRefp0Refn0AsVefAndCalibrate()
: REFP0/REFN0setRefp1Refn1AsVefAndCalibrate()
: REFP1/REFN1setAvddAvssAsVrefAndCalibrate():
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:
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.
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:
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 ParameterADS1220_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, dastart()
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:
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.
Moin Wolle,
ich erlaube mir dich anzufunken weil ich ein Problem habe und deine Projekte bis dato immer funktionierten. Nicht zuletzt wegen der guten Dokumentationen.
Eigentlich geht es darum, AC-Spannungen & -Ströme zu messen. Lösungen mit SCT-013, ZMPT101B oder PZEM-004T zeigten zu oft „Phantasiewerte“. Versuche mit der Library von EmonLib waren gut, aber die Auflösung ist murks. Also selbst schreiben und einen schnellen ADC nehmen. Die im Netz gefundenen „Lösungen“ mit AD7606, ADS8684, ADS8688, ADS8885, LTC1863, LTC1867 und MCP356x habe ich aber nicht zum Laufen bekommen bzw. kein fertiges „Modul“ gefunden; SMD selbst löten klappt bei mir nicht.
Kannst du einen ADC mit mind. 16Bit und mind. 50kS/s inkl. Modul empfehlen?
Grüße aus Lübeck
Dirk
Hi Dirk,
die Auswahl fertiger Module ist wirklich begrenzt, insbesondere wenn du 50 kSPS erreichen möchtest. Ich habe selbst auch mal (nicht zu intensiv) recherchiert. Hattest du den AD7606 nicht zum Laufen bekommen oder kein Modul gefunden? Also Module davon gäbe es davon auf AliExpress. Und eine Bibliothek gibt es z.B. hier: https://github.com/leollo98/AD7606. Wenn du das Modul nicht zum Laufen bekommst, dann könntest du den Autor der Bibliothek vielleicht um Rat fragen. Wenn 30 kSPS vielleicht doch ausreichen, dann könntest du den 24-Bit ADS1256 probieren. Auch davon gibt es Module und eine Bibliothek. Man muss allerdings sagen, dass man von solchen 24 Bit ADCs keine Absolutwerte in dieser Auflösung erwarten sollte. Meistens sind diese Teile (und dazu gehört auch der ADS1220) darauf getrimmt, diese Auflösung in vergleichenden Messungen zu externen Referenzspannungen zu erreichen, also z.B. bei Wheatstone-Brücken. Und selbst dann muss man noch einige Vorkehrungen treffen, z.B. RC-Filter an den Eingängen. Und letzte „Oberlehrer-Bemerkung“: bei 50 kSPS oder mehr muss man sich schon überlegen, ob wie man dieses Datenaufkommen übertragen und verarbeiten möchte. Das ist schon nicht mehr ganz trivial oder zumindest nicht außer Acht zu lassen. Mir fällt mir so spontan erst einmal nicht ein.
Schöne Grüße in den Norden aus dem tiefen Westen (bei Mönchengladbach)!
Wolfgang
Hast Du Erfahrungen mit mehreren ADS1220 auf einem SPI-Bus? Jeder braucht einen individuellen CS-Pin, das ist soweit klar. Braucht aber jeder auch einen individuellen DRDY-Pin? Oder darf ich die alle zusammenlegen?
Übrigens: Ich hatte Probleme, wenn auf dem gleichen SPI-Bus auch das SD-Karten-Modul liegt; das Problem gibt es wohl öfter – gibt es eine gute Standard-Lösung?
Noch ein Übrigens: Ausgesprochen hilfreiche Seiten, damit lässt es sich gut in die ESP32-Anwendung einarbeiten – insbesondere auch die wirklich interessanteren Anwendungsfälle. Danke!
Hi Wolfgang,
mehrere Module sollten funktionieren, probiert habe ich es aber nicht. Und auch mit nur einer DRDY Leitung müsste es eigentlich funktionieren. Entweder du nutzt den kontinuierlichen Modus, dann brauchst du die DRDY-Leitung ohnehin nicht unbedingt, da immer Daten zum Auslesen da sind oder du nutzt den Single Modus, dann sollte der gerade über SPI angesprochene ADS1220 in der Lage sein, die DRDY Leitung bei Vorhandensein von Daten auf LOW zu ziehen. Aber wie gesagt, probiert habe ich es nicht. Wenn du es probiert hast, kannst du ja mal deine Erfahrung teilen.
Von den SPI Problemen mit den SD-Kartenmodulen wusste ich bisher nicht, aber eine kurze Recherche zeigte, dass dem leider so ist. Hängt wohl irgendwie mit einem unglücklich angebrachten Level-Shifter zusammen. Ein paar Lösungsansätze gibt es hier:
https://forum.arduino.cc/t/sd-card-and-rfid-not-working-together-spi-conflict/995287
Ist aber alles nicht so wirklich gut, außer vielleicht der Rat, Module von Adafruit zu kaufen. Meistens sind die Dinge von Adafruit erheblich teurer als die Fernost-Billigware, aber meistens auch qualitativ besser und durchdachter.
VG, Wolfgang
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
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
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
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
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
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
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
Super,
danke fürs schnelle fixen! Nichtsdestotrotz bin ich froh, dass ich hier eine Library komplett selbst schreiben muss 🙂
Lg,
Markus
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.
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
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é
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
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é
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.
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
Herzlichen Dank für das wohltuende Feedback!
👍
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?
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!
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.