Übersicht

Projektziele

Rahmenbedingungen

AD7606 Board

Pin-Belegung

Mittelwertbildung (Oversampling)

Anschluss/Timing

Steuerung des Messvorgangs

Daten auslesen 16-Bit-Parallel

Daten auslesen 8-Bit-Parallel

Daten auslesen seriell (SPI)

STM32F1 (Bluepill)

8-Bit-parallel oder Byte-serielle Übertragung

Timing

Konkrete Umsetzung

Serielle Übertragung (SPI)

Timing

Konkrete Umsetzung

ESP32 (WROOM-32D)

8-Bit-parallel oder Byte-serielle Übertragung

Timing

Konkrete Umsetzung

 

Übersicht

Der AD7606 ist ein leistungsfähiger, preiswerter AD-Wandler. Entwicklungsboards sind ab etwa 20 € erhältlich. Er besitzt acht Kanäle mit einer Auflösung von 16 Bit. Es können Spannungen im Bereich von ±5 V oder ±10 V mit einer Rate 200 kSpS (kilo samples per second) gemessen werden. Dies entspricht einer Messdauer von 5 μs. Bereits im Chip kann der Mittelwert aus bis zu 64 Messungen ermittelt werden. Das erhöht den Rauschabstand, verringert allerdings die Durchsatzrate.

Die Messungen werden extern angestoßen. Die Messrate kann also einfach an die Belange des Projekts angepasst werden. Die acht Kanäle sind zu zwei Blöcken von je vier Kanälen zusammengefasst, die getrennt gestartet werden können (max. zeitl. Abstand: 0,5 ms, Signale CVA und CVB). Wenn die Eingänge der Blöcke parallel geschaltet werden, können somit Messungen mit einer Auflösung von 2,5 μs realisiert werden, wenn der zweite Block entsprechend verzögert gestartet wird.

Es stehen drei Schnittstellenversionen zur Übermittlung der Messwerte zur Verfügung:

Die Spannungsversorgungen für den Analog- und den Digitalteil sind getrennt. Der Analogteil wird mit 5 V versorgt. Ein gute Siebung der analogen Versorgungsspannung garantiert gute Messergebnisse. Der Digitalteil kann mit einer Spannung zwischen 2,3 V und 5 V betrieben werden, so dass keine separate Pegelanpassung beim Anschluss an einen μC notwendig ist.

Wenn man mit der maximal möglichen Sample-Rate fahren möchte, werden hohe Anforderungen an den steuernden μC gestellt. Es muss gut geprüft werden, ob der verwendete μC ausreicht.

Projektziele

Der AD7606 soll für zwei Projekte verwendet werden:

Rahmenbedingungen

Die Projekte sollen im Wesentlichen in der Arduino-Bibliothek entwickelt werden. Hier stehen bereits viele, einfach benutzbare Module zur Verfügung. Außerdem gibt es eine große Community, die bei Fragen weiter hilft. Von Nachteil ist, dass die Arduino-Klassen nicht auf Geschwindigkeit optimiert sind.

Die Verarbeitung einer Messrate von 200kSpS erfordert einen leistungsfähigen Microcontroller. Bei der Auswahl der μC spielt neben der Leistungsfähigkeit eine große Rolle, in wie weit man sich selbst mit diesen auskennt, wie gut sie dokumentiert sind und ob es eine große Community gibt, die bei Problemen weiter helfen kann. Es ist zu erwarten, dass einige der Standardfunktionen zu langsam sind und prozessorspezifisch ersetzt werden müssen.

Für das Auslesen des AD7606 gibt es drei Möglichkeiten. Wenn mit max. Sample-Rate gefahren werden soll, muss das Auslesen erfolgen, während bei die nächste Umwandlung läuft. Dafür stehen knapp 4μs zur Verfügung.

Zusätzlich zu den Datenleitungen werden mindestens 3 Signalleitungen weitere benötigt: CVA/CVB (gemeinsam), CS, RD (clock). Sinnvoll ist auch RST (reset). Der μC muss eine entsprechende Anzahl von Pins zur Verfügung stellen.

AD7606 Board

Pin-Belegung

Auf dem Board sind 27 Steuer- und Datenleitungen herausgeführt. Hinzu kommen Masse, die analoge und die digitale Stromversorgung. 

Pin Datenbl. Funktion Pin Datenbl. Funktion
GND AGND / GND Masse +5V AVcc Analoge Versorgungsspannung
OS1 OS[1] Mittelwertbildung (Oversampling) OS0 OS[0] Mittelwertbildung (Oversampling)
RAGE RANGE Auswahl des Analogeingangsbereichs ±5 oder ±10 V OS2 OS[2] Mittelwertbildung (Oversampling)
CVB CONVST B Konvertierungsstart Eingang B (V1..V4) CVA CONVST A Konvertierungsstart Eingang A
RD RD / SCLK paralleles Lesen / SPI Clock RST RESET Zurück setzen
BUSY BUSY Konvertierung läuft CS CS Chip Select
FRST FRSTDATA Daten von Kanal 1 (V1) werden gelesen VIO Vdrive Logikstromversorgung (μC)
DB1 DB1 Parallel Output Data Bit DB0 DB0 Parallel Output Data Bit
...     ...    
DB7 DB7 / DOUTA DB7 / SPI SDO DB6 DB6 Parallel Output Data Bit
DB9 DB9 Parallel Output Data Bit DB8 DB8̈ / DOUTB DB8 / SPI SDO
...     ...    
DB15 DB15 / BYTE SEL DB15 / Auswahl des Parallel-Byte-Modus DB14 DB14 / HBEN DB14 / Byte-Reihenfolge
Lötbrücke PAR / SER / BYTE SEL Parallel / Serial / Byte Interface Auswahl  

Mittelwertbildung (Oversampling)

Über die Pins OS0..OS2 kann eine Mittelwertbildung über mehrere Messwerte eingestellt werden. Dadurch sinkt natürlich die Konvertierungsrate (SPS = Samples per Second).

OS(2:0) Anz. kSpS
000 No OS 200
001 2 100
010 4 50
011 8 25
100 16 12,5
101 32 6,25
110 64 3,125
111 Invalid

Anschluss/Timing

Auf dem Board sind 27 Steuer- und Datenleitungen herausgeführt. Will man den vollen Funktionsumfang des AD7606 nutzen, müssen alle Leitungen bedient werden. Dies überfordert die meisten Microcontroller. Einige der Leitungen können, je nach Projekt, fest belegt werden. Die Anzahl der notwendigen Datenleitungen lässt such durch die Wahl der Schnittstelle festlegen.

PAR / SER / BYTE SEL wird über die Lötbrücke eingestellt. OS0, OS1, OS2, RAGE können fest eingestellt werden. Die Auswertung von FRST ist optional. CVA und CVB können gemeinsam angesteuert werden.

Steuerung des Messvorgangs

Zur Steuerung des Messvorgangs müssen die Signale RESET, CONVST A, CONVST B (ggf. gemeinsam) und BUSY bedient werden. CS leitet die Datenübertragung ein.

Auslesen nach der Messung

Auslesen während der Messung

Daten auslesen 16-Bit-Parallel

PAR / SER / BYTE SEL (Lötbrücke) in Position 8080.

RST, CVA, CVB, BUSY, CS, RD, DB0..DB15 müssen an den μC angeschlossen werden. CVA und CVB ggf. gemeinsam an einen Output-Pin.

 

Daten auslesen 8-Bit-Parallel

PAR / SER / BYTE SEL (Lötbrücke) in Position SPI. DB14 (HBEN) steuert Byte-Reihenfolge, DB15 (BYTE SEL) = HIGH.

RST, CVA, CVB, BUSY, CS, RD, DB0..DB7 müssen an den μC angeschlossen werden. CVA und CVB ggf. gemeinsam an einen Output-Pin.

Daten auslesen seriell (SPI)

PAR / SER / BYTE SEL (Lötbrücke) in Position SPI. DB14 = LOW, DB15 (BYTE SEL) = LOW

RST, CVA, CVB, BUSY, CS, RD (SCLK), DB7 oder DB8 (DOUT, SDO) müssen an den μC angeschlossen werden. CVA und CVB ggf. gemeinsam an einen Output-Pin. DB15 (BYTE SEL) muss auf LOW liegen. DB14 (HBEN) sollte auf LOW gelegt werden

.

STM32F1 (Bluepill)

Der AD7606 schafft 200kSPS, eine Wandlung dauert demzufolge nominal etwa 5μs. Die Signal-Analyse mit einem Logic-Analyzer hat ergeben, dass die eigentliche Wandlung, d.h. die Zeit, in das BUSY-Signal auf HIGH-Level liegt, sogar knapp unter 4μs liegt.

Um die volle Umwandlungsrate zu nutzen, müssen die Messwerte ausgelesen werden, während die nächste Messung läuft (s. Grafik Auslesen während der Messung). Dies muss also innerhalb von 4μs geschehen.

8-Bit-parallel oder Byte-serielle Übertragung

Timing

Um den AD7606 mit der höchst möglichen Sample-Rate zu betreiben, müssen die Daten ausgelesen werden, während die nächste Konvertierung läuft. Hierzu stehen knapp 4μs zur Verfügung. Es gilt also zunächst zu untersuchen, ob es der Bluepill schafft, die Daten in dieser Zeit auszulesen.

Beim Bluepill sind u.a. die Signale PA0..PA7 heraus geführt, bieten also die Möglichkeit ein Byte einzulesen. Mit einem einfachen Programm wurde das Zeitverhalten aufgenommen. Dabei wurden die Funktion zum generieren der Chip-Select- und Clock-Signale und auch das Einlesen der Pins PA0..PA7 zur Zeit-Optimierung über direkten Zugriff auf die Register realisiert. Die Kompileroptimierung war auf O3 (fastest) eingestellt.

static inline void readData(uint8_t* dest) {
   GPIOB->regs->BSRR = (1U << (ssPin - 16)) << 16;; // Beginn der Übertragung [digitalWrite(ssPin, LOW)]

   for (auto i = 0; i < 16; i++) {
      GPIOB->regs->BSRR = (1U << (clockPin - 16)) << 16; // Bit im High-Word des BSRR-Registers setzen [digitalWrite(clockPin, LOW);]
      *dest++ = (uint8_t)(GPIOA->regs->IDR); // Byte einlesen
      GPIOB->regs->BSRR = (1U << (clockPin - 16)); // Bit im Low-Word des BSRR-Registers setzen [digitalWrite(clockPin, HIGH);]
   }
   GPIOB->regs->BSRR = (1U << (ssPin - 16)); // Ende der Übertragung [digitalWrite(ssPin, HIGH)]
}

Der Aufruf der Routine wurde von einem Trigger-Signal eigeschlossen, so dass auch der Aufruf-Overhead sichtbar wurde. Die Interrupts wurden ebenfalls abgeschaltet, damit sie keine Störungen verursachen.

noInterrupts();
uint8_t buffer[16];
UrsAD7606bs.setTrigger();     // Trigger-Signal für den Logic-Analyser
UrsAD7606bs.readData(buffer); // Zeitaufnahme für diese Funktion
UrsAD7606bs.resetTrigger();   // Trigger-Signal für den Logic-Analyser
interrupts();

Das erfreuliche Ergebnis:

Der eigentliche Übertragungsvorgang dauert 1,90μs - 0,60μs = 1,84μs. Der Gesamte Vorgang (Trigger-Signal) dauert 1,95μs. Zum Einlesen stehen knapp 4μs zur Verfügung. Die benötigte Zeit liegt also deutlich darunter.

Das Taktsignal ist dabei nicht symmetrisch. Die High-Zeit beträgt 30ns (≙16,6 MHz, wenn dies Teil eines symmetrischen Pulses wäre), die Low-Zeit etwa 80ns (≙6,3 MHz). Die Gesamt-Taktzeit beträgt 110ns (≙9,1 MHz)

Konkrete Umsetzung

Im Beispielprojekt STM32F1-AD7606-bytepar wird eine Klasse für die Steuerung eines AD7606 durch einen STM32F1-Prozessor (Bluepill) beschrieben. Das Projekt zeigt, wie man eine Messreihe mit einer definierten Samplerate aufnehmen kann.

Das Ergebnis: Das Beispielprogramm zeigt, dass mit dem Bluepill eine Messwertaufnahme mit einer Dauer von minimal 6μs (entsprechenden 167kSpS) erfolgen kann. Dies ist etwas langsamer als die möglichen 200kSpS.

Serielle Übertragung (SPI)

Das Auslesen des AD7606 über das Byte-Serielle-Interface ist ein guter Kompromiss zwischen Geschwindigkeit und Anzahl benötigter Signalleitungen. Benötigt man für andere Funktionen möglichst viele freie Pins bietet die Datenübertragung per SPI eine Alternative. Hier werden nur drei Pins benötigt CS, SCLK und MISO. MOSI wird nicht benötigt, da keine Daten zum Slave übertragen werden.

Versuche mit der SPI des Bluepill haben ergeben:

Timing

 Die folgende Grafik zeigt einen optimierten Auslesevorgang. Die meisten der Registerzugriffe wurden unter Umgehung der Arduino-Funktionen direkt kodiert.

Das eigentliche Auslesen des AD7606 dauert gut 3,5μs (grüner Pfeil), würde also in die zur Verfügung stehende Zeit von knapp 4μs passen. Allerdings sind die Rüstzeiten, d.h. sie notwendig jedes mal durchzuführenden Konfigurationen des SPI- und des DMA-Interfaces recht lang (rote Pfeile, ca. 1,2μs). Insgesamt dauert das Auslesen (von einer fallenden CS-Flanke bis zur nächsten) knapp 5,5μs.

Test-Code | Test-Code

void readingTest() {
   AD7606DataSet t;
   ursAD7606Spi.readData(&t);
   ursAD7606Spi.readData(&t);
}

void readData(AD7606DataSet* dest) {
   beginTransfer(dest); // SPI und DMA vorbereiten

   startReading(); // Übertragung starten.

   // Warten bis die Übertragung abgeschlossen ist. Das TICF-Flag auf dem TX-DMA-Kanal gesetzt wird.
   while ((ad7606DmaDev->regs->ISR & ad7606DmaTICFMask) == 0) {}

   // Warten bis TXE=1 und dann bis BSY=0 bevor die SPI deaktiviert wird.
   while (spi_is_tx_empty(ad7606SpiDev) == 0); // Warten bis TXE=1 (spi_is_tx_empty() ist als inline deklariert).
   while (spi_is_busy(ad7606SpiDev) != 0);     // Warten bis BSY=0 (spi_is_busy() ist als inline deklariert).

   endTransfer(); // SPI und DMA abschalten.
}

void beginTransfer(AD7606DataSet* dest) {
   *pinCsBSRR = (1U << (pinCS)) << 16;; // CS auf LOW.

   // DMA konfigurieren
   ad7606RxDmaChannel->CMAR = (uint32_t)dest; // Ziel-Adresse
   ad7606RxDmaChannel->CNDTR = ad7606ChannelsToRead; // Anzahl Kanäle, die eingelesen werden sollen.
   ad7606TxDmaChannel->CNDTR = ad7606ChannelsToRead;
   ad7606RxDmaChannel->CCR |= DMA_CCR_EN; // DMA anschalten.
   ad7606TxDmaChannel->CCR |= DMA_CCR_EN;
}

void startReading() {
   // DMA im SPI-Device anschalten
   ad7606SpiDev->regs->CR2 |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN; // Erzeugt ersten DMA-Request.
}

void endTransfer() {
   // DMA muss abgeschaltet werden, ansonsten können die notwendigen Register nicht beschrieben werden.

   ad7606SpiDev->regs->CR2 &= ~(SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN); // DMA im SPI-Device abschalten.

   ad7606RxDmaChannel->CCR &= ~DMA_CCR_EN; // DMA abschalten.
   ad7606TxDmaChannel->CCR &= ~DMA_CCR_EN;

   dma_clear_isr_bits(ad7606DmaDev, ad7606TxDmaChannelNo); // Interrupt-Flags löschen
   dma_clear_isr_bits(ad7606DmaDev, ad7606RxDmaChannelNo); // Interrupt-Flags löschen

   *pinCsBSRR = (1U << (pinCS)); // CS auf HIGH.
}

Es besteht natürlich die Möglichkeit, dass sich Messung und Auslesen teilweise überschneiden. Man kann mit dem Auslesen beginnen, nachdem das BUSY-Signal wieder auf LOW geht. Das Auslesen muss abgeschlossen sein bevor BUSY das nächste Mal auf LOW. Mit der fallenden Flanke von BUSY werden die neuen Messdaten in den Ausgabe-Puffer geschrieben. Weiterhin kann das Vor- und Nachbereiten des Auslesens, also die SPI- und DMA-Konfiguration ebenfalls während des Messvorgangs geschehen. Das Beispiel STM32F1-AD7606-SPI zeigt, wie man interruptgesteuert eine Sample-Rate von 125kSpS erreichen kann (Taktzeit 8μs). Eine kürzere Taktzeit wäre theoretisch möglich, jedoch lässt die Arduino-Umgebung das nicht zu. Diese fordert zwischendurch Rechenzeit an (z.B. zur Fortschreibung von millis). Bei kürzeren Taktzeiten als 8μs steht nicht mehr genügend freie Rechenzeit zur Verfügung und der Auslesevorgang wird gestört. Das folgende Bild zeigt die Folgen einer solchen Störung bei einer Taktzeit von 7μs.

Im markierten Bereich findet solch eine Störung statt. Das CS-Signal wird verspätet gesetzt und ist außerdem verlängert. Das führt dazu, dass das Auslesen einer Messung ausgelassen wird (gelb: der tatsächlich Signalverlauf, orange: erwarteter Verlauf).

Konkrete Umsetzung

Im Beispielprojekt STM32F1-AD7606-SPI wird eine Klasse für die Steuerung eines AD7606 durch einen STM32F1-Prozessor (Bluepill) beschrieben. Das Projekt zeigt, wie man eine Messreihe mit einer definierten Samplerate über das SPI-Interface aufnehmen kann.

Das Ergebnis: Das Beispielprogramm zeigt, dass mit dem Bluepill eine Messwertaufnahme mit einer Dauer von minimal 8μs (entsprechenden 125kSpS) erfolgen kann. Dies ist etwa halb so schnell wie die möglichen 200kSpS.

 

ESP32 (WROOM-32D)

Wenn die Daten über ein WLAN übertragen werden sollen, bietet sich an, einen ESP32 zu benutzen. Die Boards gibt es in unterschiedlichen Bauformen. Man muss darauf achten, dass acht zusammenhänge (fortlaufende Nummern) GPIOs zur Verfügung stehen. Ansonsten müssen die einzelnen Bits 'zusammengefummelt' werden. Damit lassen sich dann aber die hohen Sampleraten nicht mehr erreichen.

8-Bit-parallel oder Byte-serielle Übertragung

Timing

Um den AD7606 mit der höchst möglichen Sample-Rate zu betreiben, müssen die Daten ausgelesen werden, während die nächste Konvertierung läuft. Hierzu stehen knapp 4μs zur Verfügung. Es gilt also wieder zunächst zu untersuchen, ob es der ESP32 schafft, die Daten in dieser Zeit auszulesen.

Beim ESP32 die einzige fortlaufende GPIO-Serie die Folge GPIO12..GPIO19. Sie werden an die Datenleitungen DB0..DB7 angeschlossen. Mit der folgenden Methode lassen sich so alle acht Kanäle in knapp 3,1μs auslesen, also deutlich unter 4μs.

inline void UrsAD7606bp<pinRST, pinCVA, pinBUSY, pinRD, pinCS, pinDB0, range>::readData(AD7606DataSet* dest) {
   uint8_t* destByte = (uint8_t*)dest; // Es wird byteweise eingelesen

   GPIO.out_w1tc = ((uint32_t)1 << pinCS);    // fallende Flanke des CS-Signals

   for (uint8_t i = 0; i < 16; i++) {
      GPIO.out_w1tc = ((uint32_t)1 << pinRD); // fallende Flanke des Takt-Signals
      GPIO.out_w1ts = ((uint32_t)1 << pinRD); // steigende Flanke des Takt-Signals
      uint32_t temp = GPIO.in;                         // Übernahme nach steigender Flanke
      destByte[i] = (uint8_t) ((temp >> pinDB0) & 0xFF); // Input verarbeiten
   }

   GPIO.out_w1ts = ((uint32_t)1 << pinCS);       // steigende Flanke des CS-Signals
}

Konkrete Umsetzung

Im Beispielprojekt ESP32-AD7606-bytepar wird eine Klasse für die Steuerung eines AD7606 durch einen ESP32-Prozessor beschrieben. Das Projekt zeigt, wie man eine Messreihe mit einer definierten Samplerate aufnehmen kann.

Das Ergebnis: Das Beispielprogramm zeigt, dass mit dem ESP32 eine Messwertaufnahme mit 200kSpS möglich ist. Allerdings ist Taktdauer nicht präzise. Abweichungen von 1μs müssen in Kauf genommen werden. Ab einer Taktdauer von 6μs (entsprechend 167kSpS) funktioniert das Timing tadellos.