Translate

20 Mai 2016

I²C- AD-Wandler ADS1115 bzw ADS1015 sowie MCP3428 auslesen. Diesmal mit Bascom

Hallo allerseits,

Nachdem es neulich problemlos klappte, ein Platinchen mit einem
ADS1015 an einem Arduino in Betrieb zu nehmen soll das jetzt auch
in Bascom programmiert werden.
Anfangs hat mich das ein bisschen "gefoppt", aber es lag nur an einer winzigen
Kleinigkeit...........

ein bisschen Info am Rande:
Vor einiger Zeit habe ich ja schon mal erfolgreich den MCP3428 (16bit I²C AD-Wandler)
in Betrieb genommen, und damit hervorragende Messergebisse erreicht.
Die ganz entscheidende Eigenschaft ist das Vorhandensein von differentiellen Eingängen!
Es wird also nicht nur gegen GND gemessen, sondern gegen einen Referenz-Pin.
Das ist die Entscheidende Eigenschaft, um mit dem LEM HASS200-S hochpräzise zu messen.

Der ADS1015(12Bit) bzw ADS1115(bis 16Bit) und seine Verwandten sind eigentlich sehr
ähnlich wie der MCP3428 und dessen Verwandte.

Im Vergleich zum MCP3428 ist der ADS1115 jedoch wesentlich schneller!
Während der MCP bei 12Bit 240Messungen, bei 14Bit 60Messungen und bei 16Bit
nur noch 15 Messungen pro Sekunde schafft macht der ADS1115 selbst im 16Bit-Modus
noch gewaltige 860 Messungen pro Sekunde!!!!!

Für den geplanten Einsatz in der "Franzbox" ist so viel Leistung gar nicht nötig, aber schaden tuts auch nicht.Mehr als 14Bit Auflösung ist hier auch gar nicht nötig.
Öfter zu messen als ca 15 Mal pro Sekunde ist auch nicht nötig.insofern ist der MCP3428
auch schon ausreichend schnell

Beide Wandler arbeiten mit einer internen Referenz von 2,048V und haben einen
jederzeit umkonfigurierbaren "Gain amplifier"
Die Referenz 2,048 sorgt für direkt verwendbare Ausgangswerte in Volt
Der Einstellbereich reicht beim ADS von 1/3 ; 1/2 ; 1x ; 2x ; 4x bis 8x
also von +-6,144V bis +-0,256V
Beim MCP ist 1x ; 2x ; 4x ; 8x einstellbar  also +- 2,048 bis +- 0,256V

Es ist hierbei zu beachten, dass kein Pegel das Potential der Versorgung überschreiten darf,
also nicht unter 0V und über der Versorgungsspannung (ca2V-5,5V je nach Typ)

Der maximale Ausgangswert beträgt dabei:
12Bit:  -2048 bis 2047   entspricht  x 1mV
14Bit:  -8192 bis 8191   entspricht  x 250µV
16Bit -32768 bis 32767  entspricht x 62,5µV
....das alles bei Gain = 1

Die bestmögliche Auflösung bei 16Bit und Gain = x8 beträgt somit 7,8125µV!
Das sind 0,000007815V je Digit bei +-0,256V Messbereich
Das reicht auch jederzeit um einen Shunt direkt zu messen und auszuwerten.
Hierbei ist natürlich "galvanische Trennung" zwingend nötig, was bei I²C nicht
so ganz trivial ist. Hier wäre SPI von Vorteil.

In den meisten Bereichen hat der ADS die Nase vorn, was kann denn der MCP besser?

In erster Linie hat der MCP vier differentielle Eingänge auf acht pins während der
ADS nur vier Eingangspins hat und so entweder vier mal gegen GND
bzw pin0 gegen Pin1 und Pin2 gegen Pin3 oder Pin0 bis Pin2 gegen Pin3 messen kann.
Ausserdem ist die Ansteuerung des MCP ein bisschen einfacher.
Es müssen etwas weniger Daten übertragen werden, aber letztendlich geben beide
die gleichen Werte aus

Wie liest man denn nun den ADS1015 bzw ADS1115 in Bascom aus?
Der Einfachheit halber nur die entscheidenden Sequenzen.
(den eigentliche Programmaufbau setze ich einfach mal als bekannt voraus)
Die jeweiligen Konfigurationen sind aus meinem Programm übernommen
und können natürlich an die jeweiligen Bedürfnisse angepasst werden.

Die Einzelheiten sind im Datenblatt mehr oder weniger verständlich erklärt.
Ich will da jetzt nicht allzusehr ins Detail gehen, sondern alles nur mal
ganz einfach und verständlich beschreiben wie es grundsätzlich geht.


Ini-Sequenz:

erst mal I²C starten:

Enable Interrupts

Config Sda = Portc.4                  'Ports bestimmen
Config Scl = Portc.5
I2cinit                                        I²C starten

.........hier nur "Software-SPI" aber schon mal die Hardware-Pins beim Atmega XX8


dann Konfiguration senden:
z.B.:

I2cstart
I2cwbyte &B10010000                 'Adresse  senden  an Schreib-Adresse                
I2cwbyte &B00000001                 'Config-Register ansprechen
I2cwbyte &B00000100                 'MSB des Config-Register senden
I2cwbyte &B10000011                 'LSB des Config-Register senden
I2cstop

wieder umschalten auf Lese-Modus

I2cstart
I2cwbyte &B10010000                  'Adresse senden
I2cwbyte &B00000000                  'Lese-Register ansprechen
I2cstop

dann auslesen:

I2cstart
I2cwbyte &B10010001                 'Adresse senden an Lese-Adresse
I2crbyte Read1a , Ack                   'MSB des Messwertes lesen
I2crbyte Read1b , Nack                 'LSB des Messwertes lesen
I2cstop

.........das wars eigentlich schon.......
Die Konfiguration und das Umschalten in den Lesemodus müssen zwar nur einmalig
erfolgen, es schadet aber nicht, das gelegentlich "aufzufrischen"
Auch bei einem Wechsel des Eingangs oder sonstiger Einstellungen
ist die komplette Sequenz nötig

Umrechnen der Werte in positive und negative Zahlen

Read1a wird als Byte gelesen, ist aber für die nachfolgende Berechnung als Word definiert
Read1b im Beispiel sind als Byte definiert
Read1aint ist als Integer definiert

Die beiden Byte müssen erst mal zu einem "Word" zusammengefügt werden
"zu Fuß" geht das so:   (geht auch eleganter, ist dann aber nicht so verständlich)
Read1a = Read1a * 256           'High-Byte nach oben schieben
Read1a = Read1a + Read1b      'Low-Byte einfügen
Read1aint = Read1a                 'Word in Integer ergibt Vorzeichen

Fertig! Der Messwert kann nun weiterverarbeitet werden


Der selbe Lesevorgang bei einem MCP3426

Konfiguration senden:

I2cstart
I2cwbyte &B11010000                 'Adresse  senden  an Schreib-Adresse
I2cwbyte &B00010000                 'Konfiguration senden (hier: Kanal1, 12Bit)
I2cstop

......Hier gibt es nur ein (1) Kofigurationsbyte!
Umschalten in Lesemodus ist hier nicht nötig!

Lesen:
I2cstart
I2cwbyte &B11010001                 'Adresse  senden  an Lese--Adresse
I2crbyte Read1a , Ack                   'MSB des Messwertes lesen
I2crbyte Read1b , Ack                 'LSB des Messwertes lesen
I2crbyte Read1c , Nack                 'Konfigurationsbyte lesen
I2cstop

..........Beim Lesen gibt es hier auch noch ein drittes Byte
Inhalt: Konfiguration und ein "Ready-Bit" das muss nicht immer
ausgewertet werden, ist z.B. nützlich bei Einzelmessungen um sicher zu gehen, dass
der Wert aktuell ist

Zusammenfügen der Byte zu einem Integer-Wert wie oben beim ADS


Diese AD-Wandler (MCP und ADS) kann man jederzeit während des Betriebs "umparametrieren"
Man kann da also schön spielen! z.B. die Auflösung bei niedrigen Analogwerten
erhöhen etc......

Bis demnächst mal wieder!
Franz





03 Mai 2016

Verbesserte Strommessung per I²C AD-Wandler

........wenns schon mit meinem Triebwerk nicht weitergeht,
weil ich meine Nabe noch immer nicht habe......
oder einfach mal als kleines Lebenszeichen von mir!

Zwischendurch mal ein kleines bisschen Elektronik und Programmierung.
Diesmal ganz entgegen meiner sonstigen Gewohnheit auf einem Arduino!
Eine kleine Arduino-Übung für mich, und für die Allgemeinheit wohl besser
geeignet als Bascom, womit ich eigentlich lieber arbeite, weil man da insbesondere bei
PWM-Geschichten flexibler ist und viel problemloser Quarzfrequenzen und Vorteiler
an die persönlichen Bedürfnisse anpassen kann.
Na ja, alles hat seine Vor-und Nachteile.Ein ganz besonderer "Liebling" von mir ist ja der
Freeduino serial v2.0 den es z.B. bei Watterott günstigst gibt.
Die "echte" Serielle Schnittstelle hat was! Damit lässt sich so manches anfangen,
was mit USB nicht möglich ist,wenn man nicht gerade an einem PC hängt.

Die Problematik bei der Strommessung in der Franzbox ist nach wie vor
nicht behoben.
Zur Erinnerung: Ich messe den Strom mit einem LEM HASS200S Wandler.
Dieser wird an 5V betrieben und liefert ein Ausgangssignal von theoretisch
0V = -800A  2,5V= 0A und 5V=800A sowie eine Referenzspannung für den Nullpunkt
(Nur zum Berechnen, in der Praxis sind +-600A möglich, da die Ausgangsspannung
nicht bis 0V bzw 5V geht)
Ich habe die Referenz nicht verwendet, sondern nur auf 0A abgeglichen
und das Ausgangssignal direkt auf einen ADC-Eingang des Atmega gelegt.
Die Genauigkeit bei hohen Strömen (Entnahme) ist ausreichend,aber
bei geringeren Strömen (Ladevorgang) ist die Auflösung einfach zu grob
um wirklich verlässliche Werte zu bekommen.

es hätte hier auch keinen Sinn gemacht, die Referenzspannung auszuwerten.
Das Problem kann eigentlich nur mit einem deutlich feiner auflösenden ADC
mit differentiellem Eingang gelöst werden.
Ich habe vor einiger Zeit schon mal sehr erfolgreiche Versuche mit einem
solchen Baustein gemacht, aber die Verfügbarkeit sowie die winzige Bauform
waren nicht optimal.

Nun habe ich es nochmal mit einer Platine von Adafruit versucht.
Der 12Bit-Wandler ADS1015 ist für diese Aufgabe auch wunderbar geeignet und fertig verlötet
problemlos lieferbar.
Es gibt auch eine Pin-kompatible Version ADS1115 mit 16Bit Auflösung,
die hier noch Vorteilhaft sein könnte.
Der ADS1115 ist etwas langsamer, aber locker schnell genug.

I²C braucht nur zwei Leitungen, und ist notfalls auch "galvanisch getrennt" machbar.
Beim Stromwandler gehts ohne Trennung, aber wenn auch noch die Akkuspannung
gemessen werden sollte wird das ein Thema.
Der ADS1015 hat vier Analogeingänge die per internem Multiplexer wahlweise
gegen GND oder als zwei Differenzeingänge konfiguriert werden können.

in Default-Konfiguration beträgt die Auflösung 3mV, das ist nicht sehr viel besser,
als die bisherigen ca 5mV, aber da auch der Bereich von +-2,048V bezogen auf die
Referenz von 2,5V ausreicht, um den LEM-Sensor auszulesen kommt man dann
auf eine Auflösung von 1mV, und das ist dann das fünffache (korrekt 1/5 ;-)  )an Auflösung, verglichen mit dem Atmega-Onboard-ADC bzw  0.0625mV Auflösung bei
Verwendung des ADS1115. (Das ist dann eine ganz andere Liga!)

Für den ersten Funktionstest Test habe ich es mir einfach gemacht und ein Demo-Programm
angepasst. Alles nur mal auf dem Breadboard zusammengesteckt und getestet.
Der "Freeduino serial V2.0" links im Bild dient hier übrigens nur als 5V-Netzteil.
Ein Arduino Nano war auf dem Steckbrett deutlich besser zu handhaben.

Mein Versuchsaufbau:


































Das Arduino-Programm:

Ich habe hier nur ein Demoprogramm von Adafruit, das die Daten seriell
über USB/serial ausgibt um eine LCD-Anzeige ergänzt.
Die Library gibts bei Adafruit zum Downloaden.
Achtung! die Zeilenumbrüche könnten hier im kopierten Text etwasverschoben sein,
und das Einfügen von Text in spitzen Klammern nach include  der Libs klappt hier nicht!

#include  Wire.h
#include  Adafruit_ADS1015.h
#include  LiquidCrystal.h



LiquidCrystal lcd(11, 10, 2, 3, 4, 5);
// Adafruit_ADS1115 ads;  /* Use this for the 16-bit version */
Adafruit_ADS1015 ads;     /* Use thi for the 12-bit version */

void setup(void)
{
lcd.begin(16, 2);
lcd.setCursor(0, 0);

  Serial.begin(9600);
  Serial.println("Hello!");

  Serial.println("Getting differential reading from AIN0 (P) and AIN1 (N)");
  Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115)");

  // The ADC input range (or gain) can be changed via the following
  // functions, but be careful never to exceed VDD +0.3V max, or to
  // exceed the upper and lower limits if you adjust the input range!
  // Setting these values incorrectly may destroy your ADC!
  //                                                                ADS1015  ADS1115
  //                                                                -------  -------
  // ads.setGain(GAIN_TWOTHIRDS);  // 2/3x gain +/- 6.144V  1 bit = 3mV      0.1875mV (default)
  // ads.setGain(GAIN_ONE);        // 1x gain   +/- 4.096V  1 bit = 2mV      0.125mV
  // ads.setGain(GAIN_TWO);        // 2x gain   +/- 2.048V  1 bit = 1mV      0.0625mV
  // ads.setGain(GAIN_FOUR);       // 4x gain   +/- 1.024V  1 bit = 0.5mV    0.03125mV
  // ads.setGain(GAIN_EIGHT);      // 8x gain   +/- 0.512V  1 bit = 0.25mV   0.015625mV
  // ads.setGain(GAIN_SIXTEEN);    // 16x gain  +/- 0.256V  1 bit = 0.125mV  0.0078125mV

  ads.begin();
}

void loop(void)
{
  int16_t results;

  /* Be sure to update this value based on the IC and the gain settings! */
  float   multiplier = 3.0F;    /* ADS1015 @ +/- 6.144V gain (12-bit results) */
  //float multiplier = 0.1875F; /* ADS1115  @ +/- 6.144V gain (16-bit results) */

  results = ads.readADC_Differential_0_1();
 
  Serial.print("Differential: "); Serial.print(results); Serial.print("("); Serial.print(results * multiplier); Serial.println("mV)");


lcd.setCursor(0, 0);
lcd.print("ADC Wert:");
lcd.print(results);
lcd.print("  ");
lcd.setCursor(0, 1);
lcd.print(results*multiplier);
lcd.print(" mV  ");

  delay(100);