Inhaltsverzeichnis:

Einen besseren DAC mit ESP32 herstellen und testen - Gunook
Einen besseren DAC mit ESP32 herstellen und testen - Gunook

Video: Einen besseren DAC mit ESP32 herstellen und testen - Gunook

Video: Einen besseren DAC mit ESP32 herstellen und testen - Gunook
Video: DIY Multi-Room-Audio mit ESP32 / Android / Linux / Windows (Squeezelite / LMS mit Anleitung) 2024, Juli
Anonim
So erstellen und testen Sie einen besseren DAC mit ESP32
So erstellen und testen Sie einen besseren DAC mit ESP32
So erstellen und testen Sie einen besseren DAC mit ESP32
So erstellen und testen Sie einen besseren DAC mit ESP32

Der ESP32 verfügt über 2 8-Bit-Digital-Analog-Wandler (DACs). Diese DACs ermöglichen es uns, beliebige Spannungen innerhalb eines bestimmten Bereichs (0-3,3 V) mit 8 Bit Auflösung zu erzeugen. In diesem Instructable zeige ich Ihnen, wie Sie einen DAC bauen und seine Leistung charakterisieren sowie mit dem ESP32 DAC vergleichen. Die Leistungsindizes, die ich mir ansehen werde, umfassen

  • Geräuschpegel
  • Bandbreite
  • Integrale Nichtlinearität
  • Differentielle Nichtlinearität

Um diese Indizes zu testen, verwende ich den ADS1115.

Es ist wichtig zu beachten, dass Ihre Bewertung all dieser Indizes nur so genau ist wie Ihr Referenzgerät (in diesem Fall das ADS115). Zum Beispiel hat der ADS115 keine 16-Bit-Präzision, wenn es um Spannungs-Offset und Verstärkung geht. Diese Fehler können bis zu 0,1 % betragen. Bei vielen Systemen können diese Fehler ignoriert werden, wenn die absolute Genauigkeit von begrenzter Bedeutung ist.

Lieferungen

  • ADS1115
  • ESP32-Platine
  • Steckbrett
  • Überbrückungsdrähte
  • 5 kOhm Widerstand
  • 1 Mikro-Farad-Keramikkondensator

Schritt 1: Auslegen des Steckbretts

Das Steckbrett auslegen
Das Steckbrett auslegen

Verdrahten Sie die folgenden Pins

Zwischen ESP32 und ADS1115

3v3 VDD

GND GND

GPIO22 SCL

GPIO21 SDA

Beim ADS1115

ADDR GND (ADS115)

Herstellung des DAC

Es gibt viele Möglichkeiten, einen DAC zu erstellen. Die einfachste ist die Tiefpassfilterung eines PWM-Signals mit einem Widerstand und einem Kondensator. Ich hätte hier einen Operationsverstärker als Puffer hinzufügen können, wollte aber die Dinge einfach halten. Dieses Design ist einfach und kostengünstig mit jedem Mikrocontroller zu implementieren, der PWM unterstützt. Ich werde hier nicht auf die Theorie des Designs eingehen (google PWM DAC).

Schließen Sie einfach GPIO255 KOhm Widerstand 1 microFarad Capacitor Gnd an

Verbinden Sie nun ein Überbrückungskabel von dem Punkt, an dem der Widerstand auf den Kondensator trifft, mit A0 auf dem ADS115.

Schritt 2: Bewerten Sie den Signal-Rausch-Pegel

Bewerten Sie den Signal-Rausch-Pegel
Bewerten Sie den Signal-Rausch-Pegel

Um den Geräuschpegel zu beurteilen, führen Sie einfach das folgende Skript aus. Um dies zu beurteilen, belassen wir den DAC einfach auf einem festen Wert und messen, wie die Spannung über die Zeit schwingt.

Aufgrund des Designs des DAC ist das Rauschen am größten, wenn das PWM-Signal ein Tastverhältnis von 50 % hat. Deshalb werden wir es hier bewerten. Wir werden auch den ESP32 auf diesem Signalpegel bewerten. Wir werden auch den ESP32 DAC mit dem gleichen Tiefpassfilter filtern, um die Messung vergleichbar zu machen.

Für mich war die Ausgabe klar. Das PWM-Design hatte ein >6dB besseres SNR (das ist 2-mal besser). Ein klarer Sieg für den neuen DAC. Ein kleiner Irrtum ist, dass in den ADC Filter eingebaut sind, die das SNR definitiv verbessern. Daher können die absoluten Werte schwer zu interpretieren sein. Hätte ich einen Filter zweiter Ordnung verwendet, wäre dies nicht der Fall.

Wie auch immer, Code ist unten

#enthalten

#include Adafruit_ADS1115-Anzeigen; // Adafruit-Bibliothek für adc int16_t adc0; // Void setup (void) { Serial.begin (115200); // Serielles ads.setGain(GAIN_TWO) starten; // 2x Verstärkung +/- 2,048 V 1 Bit = 0,0625 mV ads.begin (); // ADC-Float beginnen M = 0; // anfängliche mittlere Gleitkommazahl Mp = 0; // vorherige mittlere Gleitkommazahl S = 0; // anfängliche Varianz-Float Sp = 0; // vorherige Varianz const int reps = 500; // Anzahl der Wiederholungen int n = 256; // Anzahl der Proben ledcSetup (0, 25000, 8); // PWM-Frequenz = 25000 Hz bei 8-Bit-Auflösung einstellen ledcAttachPin (25, 0); // PWM auf Pin 25 setzen ledcWrite (0, 128); // setze es auf den halben Arbeitszyklus (größtes Rauschen) Verzögerung (3000); // Einschwingzeit abwarten float snrPWM[reps]; // Array von snrs für PWM float snrDAC[reps]; // Array von snrs für DAC for (int i = 0; i < reps; i++) { // Schleife über Wiederholungen für (int k = 1; k < (n + 1); k++) { // Schleife über Samples adc0 = ads.readADC_SingleEnded(0); // Lesen M = Mp + (adc0 - Mp) / k; // berechne den gleitenden Mittelwert Mp = M; // Vorherigen Mittelwert setzen S = Sp + (adc0 - Mp) * (adc0 - M); // berechne die rollierende Varianz Sp = S; // vorherige Varianz setzen} // snr in dB snrPWM = 20 * log10(3.3 / (sqrt(S / n) *.0625 *.001)); // Werte zurücksetzen M = 0; Mp = 0; S = 0; Sp = 0; } ledcDetachPin(25); // PWM von Pin 25 trennen dacWrite (25, 128); // in die DAC-Verzögerung schreiben (3000); // warten, um sich für (int i = 0; i < reps; i++) zu entscheiden { // dasselbe wie PWM-Schleife für (int k = 1; k < (n + 1); k++) { adc0 = ads.readADC_SingleEnded(0); M = Mp + (adc0 - Mp)/k; Mp = M; S = Sp + (adc0 - Mp) * (adc0 - M); Sp = S; } snrDAC = 20 * log10(3,3 / (sqrt(S / n) *.0625 *.001)); M = 0; Mp = 0; S = 0; Sp = 0; } // SNRs in einem Diagramm für (int i = 1; i < reps; i++) {Serial.print ("PWM_SNR (dB):"); Serial.print (snrPWM ); Serial.print (", "); Serial.print ("ESP32_SNR (dB):"); Serial.println (snrDAC); } } Leere Schleife (void) {}

Schritt 3: Integrale Nichtlinearität und differentielle Nichtlinearität

Integrale Nichtlinearität und differentielle Nichtlinearität
Integrale Nichtlinearität und differentielle Nichtlinearität

Die integrale Nichtlinearität ist ein Maß dafür, wie groß die Abweichung zwischen Ihrer DAC-Ausgangsspannung und einer geraden Linie ist. Je größer das ist, desto schlimmer ist es…

Die differentielle Nichtlinearität ist ein Maß dafür, wie stark die beobachtete Spannungsänderung (von einem Code zum nächsten) von dem abweicht, was man von einer geraden Linie erwarten würde.

Die Ergebnisse hier waren wirklich interessant. Zunächst einmal haben beide weniger als 0,5 lbs Fehler (bei 8-Bit-Auflösung), was gut ist, aber die PWM hat eine viel bessere integrale Linearität. Beide haben eine vergleichbare differentielle Nichtlinearität, aber der ESP32 DAC hat einige sehr seltsame Spitzen. Darüber hinaus weist das PWM-Verfahren eine gewisse Fehlerstruktur auf. Im Wesentlichen über- und unterschreitet es die richtige Spannung abwechselnd.

Mein Verdacht ist, dass dies ein seltsamer Rundungsfehler bei der Erzeugung eines 8-Bit-PWM-Signals auf dem ESP32 ist.

Eine Möglichkeit, dies zu korrigieren, besteht darin, mit der PWM schnell zwischen zwei benachbarten Codes (z. B. 128, 129) zu wechseln. Bei einem analogen Tiefpassfilter werden die resultierenden Fehler auf Null gemittelt. Ich habe dies in der Software simuliert und tatsächlich sind alle Fehler verschwunden. Jetzt hat die PWM-Methode eine Linearität, die auf 16 Bit genau ist!

Anywho der Code zum Generieren der Daten ist unten. Die Ausgabe erfolgt auf dem seriellen Monitor im.csv-Format. Zur weiteren Verarbeitung einfach in eine Textdatei kopieren.

#enthalten

#include Adafruit_ADS1115-Anzeigen; /* Verwenden Sie dies für die 16-Bit-Version */ int16_t adc0; Void setup (void) { Serial.begin (115200); ads.setGain(GAIN_ONE); // 2x Verstärkung +/- 2,048 V 1 Bit = 1 mV 0,0625 mV ads.begin (); ledcSetup (0, 25000, 8); ledcAttachPin(25, 0); Serial.println ("Erwartet, beobachtet"); ledcWrite(0, 2); Verzögerung (3000); für (int i = 2; i < 255; i ++) {ledcWrite (0, i); Verzögerung (100); adc0 = ads.readADC_SingleEnded(0); Float erwartet = (i / 256,0 * 3,3) / 4,096 * 32767; Serial.print (erwartet); Serial.print (", "); Serial.println (adc0); } } Leere Schleife (void) {}

Schritt 4: Bandbreite

Bandbreite
Bandbreite

Ich werde Bandbreite wie hier als die Frequenz definieren, bei der die Ausgabe des DAC um 3 dB abfällt. Dies ist eine Konvention und bis zu einem gewissen Grad willkürlich. Zum Beispiel gibt der DAC am 6-dB-Punkt immer noch ein Signal aus, dessen Amplitude nur ~50% beträgt.

Um dies zu messen, leiten wir einfach Sinuswellen mit steigender Frequenz vom DAC zum ADC und messen ihre Standardabweichung. Es überrascht nicht, dass der 3dB-Punkt bei 30Hz liegt (1/(2*pi*5000*1e-6)).

Der ESP32 kann 1 Mega-Sample pro Sekunde durchführen. Dies ist ein zweifelloser Gewinn für den ESP32. Seine Amplitude fällt im Testbereich der Bandbreite von 100 Hz überhaupt nicht ab.

Der folgende Code kann die PWM-DAC-Bandbreite testen.

#enthalten

#include Adafruit_ADS1115-Anzeigen; /* Verwenden Sie dies für die 16-Bit-Version */ int16_t adc0; int16_t adc1; Void-Setup (Void) { Float M; Schwimmer Mp = 0; Schwimmer S = 0; Schwimmer Sp = 0; Serial.begin(115200); ads.setGain(GAIN_ONE); // 1x Verstärkung +/- 4,096 V 1 Bit = 2 mV 0,125 mV ads.begin (); ledcSetup (0, 25000, 8); ledcAttachPin(25, 0); Verzögerung (5000); Serial.println ("Frequenz, Amplitude"); for (int i = 1; i < 100; i++) { unsigned long start = millis(); vorzeichenloses langes T = millis(); Sp = 0; S = 0; M = 0; Mp = 0; intk = 1; Schwimmernorm; während ((T - Start) < 1000) { Int out = 24 * sin (2 * PI * i * (T - Start) / 1000,0) + 128; ledcWrite(0, aus); adc0 = ads.readADC_SingleEnded(0); M = Mp + (adc0 - Mp)/k; Mp = M; S = Sp + (adc0 - Mp) * (adc0 - M); Sp = S; T = Millis(); k++; } Wenn (i == 1) {norm = sqrt (S / k); } Serial.print (i); Serial.print (", "); Serial.println (sqrt (S / k) / Norm, 3); k = 0; } } Leere Schleife (void) {}

Und dieser Code testet die ESP32-Bandbreite. Stellen Sie sicher, dass Sie den Kondensator entfernen, oder die Ergebnisse sind bei beiden Methoden gleich.

#enthalten

#include Adafruit_ADS1115-Anzeigen; /* Verwenden Sie dies für die 16-Bit-Version */ int16_t adc0; int16_t adc1; Void-Setup (Void) { Float M; Schwimmer Mp = 0; Schwimmer S = 0; Schwimmer Sp = 0; Serial.begin(115200); ads.setGain(GAIN_ONE); // 1x Verstärkung +/- 4,096 V 1 Bit = 2 mV 0,125 mV ads.begin (); Verzögerung (5000); Serial.println ("Frequenz, Amplitude"); for (int i = 1; i < 100; i++) { unsigned long start = millis(); vorzeichenloses langes T = millis(); Sp = 0; S = 0; M = 0; Mp = 0; intk = 1; Schwimmernorm; während ((T - Start) < 1000) {int out = 24 * sin (2 * PI * i * (T - Start) / 1000,0) + 128; dacWrite(25, aus); adc0 = ads.readADC_SingleEnded(0); M = Mp + (adc0 - Mp)/k; Mp = M; S = Sp + (adc0 - Mp) * (adc0 - M); Sp = S; T = Millis(); k++; } wenn (i == 1) {norm = sqrt (S / k); } Serial.print (i); Serial.print (", "); Serial.println (sqrt (S / k) / Norm, 3); k = 0; } } Leere Schleife (void) {}

Schritt 5: Gedanken zum Schluss

Das neue DAC-Design gewinnt an Linearität und Rauschen, verliert jedoch an Bandbreite. Je nach Anwendung kann einer dieser Indizes wichtiger sein als der andere. Mit diesen Testverfahren sollten Sie diese Entscheidung objektiv treffen können!

Ich denke auch, dass es hier erwähnenswert ist, dass es möglich sein sollte, mit dem PWM-Ausgang einen viel höher auflösenden DAC mit dem PWM-Ausgang (vielleicht sogar 16-Bit-Präzision) zu konstruieren, da der PWM-Ausgang rauscharm ist. Das wird etwas Arbeit kosten. Bis dahin verabschiede ich mich!

Empfohlen: