12-Bit Digital-Analog-Converter (DAC) MCP4725

Arduinos, wie der Uno oder der Mega besitzen keinen "echten" Analogausgang (Spannungsausgang), sondern nur Quasi-Analogausgänge mittels Pulsweitenmodulation PWM, wo durch schnelles Ein- und Ausschalten von Digitalausgängen mit definierten Impuls- und Pausenlängen sich ein über die Zeit gemittelter Spannungswert ergibt. Ab einer gewissen PWM-Frequenz können z.B. durch die Trägheit des Auges oder der Massenträgheit eines DC-Motors damit LEDs gedimmt oder DC-Motoren drehzahlgeregelt werden, ohne dass funktionell ein Unterschied zu einer echten Analogspannung ersichtlich ist.

Wird allerdings als Ausgangssignal eine "echte" Analogspannung benötigt, muss man auf einen Digital-Analog-Converter (DAC) zurückgreifen. DACs sind in verschiedenen Ausführungen (z.B. Ansteuerung parallel - je Bit eine Anschlussleitung oder seriell über SPI oder I2C-Bus, Auflösung 8, 10, 12, 14 Bit oder höher, ein- oder mehrkanalig, etc.) erhältlich.

Für meine "Spielereien" verwende ich den DAC MCP4725 auf einem Breakout Board, ein Converter mit I2C-Schnittstelle, 12 Bit Auflösung, einer typischen Wandlungszeit von 6 us und einem integrierten 14 Bit EEPROM zur Speicherung des Power-Down-Modus und des Start-Spannungswertes (ohne I2C-Verbindung zum Arduino). Der MCP4725 kann in 4 Moden betrieben werden:

  • DAC-Normal-Modus
  • DAC-Normal-Modus mit Beschreiben des EEPROMs
  • DAC-Fast-Modus
  • Power-Down-Modus


Bild: MCP4725 auf Breakout Board


  • Die Wandlungszeit von 6 us hört sich ja recht gut an, wären das ja immerhin 166.666 Wandlungen pro Sekunde, wäre da nicht die Zeit, die zur Übertragung des auszugebenden Wertes über die I2C-Schnittstelle noch zu addieren ist und die ist um ein Vielfaches höher. Wobei gesagt werden muss, dass der MCP4725 laut Datenblatt für eine I2C-Datenrate von 3,4 MBit/s (High-Speed-Modus) geeignet ist, der Arduino Uno (und/oder die Wire.Library) allerdings nur für Standard-Modus (100 kBit/s) und Fast-Modus (400 kBit/s) geeignet ist. Bei meinem Testaufbau (wie nachfolgend gezeigt) erreichte ich im Fast-Modus eine Übertragungsrate von 800 kBit/s. Die von mir ermittelten Übertragungszeiten in Abhängigkeit des DAC-Modus und der Übertragungsfrequenz zeigt nachfolgende Tabelle:

  • Die Übertragungszeit für den DAC-Normal-Modus mit Beschreiben des EEPROMs habe ich nicht ermittelt, da dieser Modus ja nur jeweils als Einzelereignis stattfinden sollte (begrenzte EEPROM-Lebensdauer im Schreibmodus, siehe nachfolgenden Hinweis unter" Library MyMCP4725").


  • Achtung: Sowohl bei den Betriebsmoden des DAC als auch bei den Moden der I2C-Übertragung gibt es den "Fast-Modus". Sollte es aus dem Text nicht eindeutig hervergehen, verwende ich für den Fast-Modus die Bezeichnung "I2C-Fast Modus" oder "DAC-Fast-Modus".


  • I2C-Adresse:

  • Ohne weitere Beschaltung ist die I2C-Adresse des Converters auf 0x62 eingestellt und kann durch Beschalten des A0-Pins auf VDD auf 0x63 geändert werden.


  • Testaufbau:

  • Verwendete Bauteile:

    • 1 Arduino Uno
    • 1 DAC MCP4725 Breakout Board
    • 1 LM358
    • 1 Poti 10 kOhm
    • 1 Netzgerät 7 - 12 V
    • 1 Voltmeter bzw. Oszilloskop



    Da der Ausgang des DAC MCP4725 strommäßig kaum belastbar ist (1 - 3 mA), habe ich dem DAC-Ausgang einen Operationsverstärker LM358 als Spannungsfolger (Impedanzwandler) nachgeschaltet. Dieser hat als Ausgangsspannung dieselbe Spannung wie der DAC, allerdings kann er strommäßig etwas höher, mit ca. 20 - 35 mA, abhängig von der Höhe der DAC-Spannung belastet werden, ohne das die Ausgangsspannung merkbar einbricht. Damit der Spannungsfolger allerdings die Spannung des DAC-Ausgangs über den ganzen Bereich (0 - 5 V) wiedergeben kann, muss die Versorgungsspannung des LM358 mindestens um ca. 1,5 V höher sein, als die auszugebende Spannung.


    Library MyMCP4725:

    Obwohl es von Adafruit eine Library für den MCP4725 gibt (github.com/adafruit/Adafruit_MCP4725), habe ich für mich eine neue Lib geschrieben und ihren Funktionsumfang gegenüber der genannten Library etwas erweitert:


    • Abfrage, ob der Baustein über I2C ansprechbar ist
    • Setzen der Referenzspannung 1)
    • Setzen des DAC-Registers (= Ausgangsspannung) im Fast-Modus
    • ERWEITERT: Setzen des DAC-Registers (= Ausgangsspannung) im Normal-Modus mit oder ohne Beschreiben des EEPROMs 2)
    • NEU: Die Ausgabewert kann nun auch direkt in Volt angegeben werden.
    • GEÄNDERT: Rücklesen des DAC-Registers und des EEPROMs (jetzt in 2 getrennten Funktionen)
    • NEU: Setzen des Betriebsmodus - Normalbetrieb oder Schlafmodus (Power-Down-Modus) 3)
    • GELÖSCHT: Setzen der I2C-Frequenz


    1)  Der MCP4725 verwendet als Referenzspannung Vref die Versorgungsspannung VDD des DAC. Das bedeutet, dass Werte im DAC-Register (0 bis 4095) als Spannungswerte zwischen 0 V und Vref ausgegeben werden. Da die Library aber auch die Vorgabe des auszugebenen Spannungswertes in Volt ermöglicht, muss der Library der Referenzspannungswert bekannt sein. Standardmäßig sind in der Library dafür 5,0 V vorgegeben. Bei abweichender Versorgungsspannung oder zur Erhöhung der Genauigkeit bei der Berechnung des 12-Bit-DAC-Registerwertes aus dem Wert der auszugebenden Spannung kann die Referenzspannung daher vorgegeben werden.

    2)  Achtung: Das Setzen des DAC-Register im Normalmodus mit Beschreiben des EEPROMs sollte nur ganz geziehlt erfolgen. Laut Datenblatt kann das EEPROM ca. 1 Million mal beschrieben werden. Vergißt man den Schreibmodus auszuschalten, wären bei z.B. 20 Schreibzyklen pro Sekunde diese Anzahl nach ca. 14 Stunden bereits überschritten!!

    3)  Im Schlafmodus wird der Analogausgang ausgeschaltet und softwaremäßig über 1, 100 oder 500 kOhm auf Masse geschaltet. Der Stromverbrauch des MCP4725 reduziert sich dabei auf eine Größenordnung von 1 µA.


    Die Library kann hier heruntergeladen werden:

    Sollte die Library jemand verwenden oder testen, würde ich mich über eine Rückmeldung sehr freuen!

    Version 1.5

    MyMCP4725.cpp.txt

    MyMCP4725.h.txt

    keywords.txt

    Leider kann ich hier keine "cpp"- oder "h"-Files hochladen, daher zum Verwenden der Library das Suffix ".txt" aus diesen Dateinamen entfernen und in einem neuen Verzeichnis mit dem Namen "MyMCP4725" im Sketchbook-Ordner im Ordner "libraries" speichern.


    Zur Auflistung der Funktionen der Library geht es hier: Funktionen


    Programmbeispiel MyMCP4725:

    Das nachfolgende Testprogramm liest im Sekundenzyklus den Wert des Potentiometer ein (Auflösung 10 Bit => Wertebereich 0 bis 1023) und gibt die auf einen Bereich von 0 bis 4095 "gemappte" Zahl (12 Bit) als Analogwert (Spannungsbereich 0 bis VDD) wieder aus. Der eingelesene Wert sowie die Zeit für die Übertragung zum DAC über den I2C-Bus wird am Seriellen Monitor angezeigt. Ebenso wird der Wert des DAC-Registers und des EEPROMs ausgelesen und am Seriellen Monitor angezeigt.

    //Testprogramm fuer DAC MCP4725
    //Code fuer Arduino
    //Author Retian
    //Version 2.2


    #include <MyMCP4725.h>
    MyMCP4725 DAC(0x62); //I2C-Adress = 0x62


    #define potiPin 0

    int16_t potiWert;
    unsigned long startTime, endTime;
    bool dacStatus = false;


    void setup() {
      Serial.begin(115200);


      if (DAC.isReady())
      {
        Serial.println("\nDAC in Ordnung!");
        dacStatus = true;
      }
      else Serial.println("DAC Fehler!");


      //Startausgabewert des DAC auf 0 V (Grundeinstellung = VDD/2)
      //DAC.setVoltage(0, MCP4725_WRITE_EEPROM); //EEPROM wird beschrieben!!

    }


    void loop() {
      if (dacStatus)
      {
        //Einlesen des Poti
        potiWert = analogRead(potiPin);
        potiWert = map(potiWert, 0, 1023, 0, 4095);
        Serial.print("\nPotiwert: ");
        Serial.println(potiWert);
        startTime = micros();


        //Ausgabe des Potiwertes im Normal-Modus
        DAC.setVoltage(potiWert);
        endTime = micros();
        Serial.print("Zeit: ");
        Serial.print(endTime - startTime);
        Serial.println(" us");


        //Rücklesen des DAC-Register
        Serial.print("DAC   : ");
        Serial.println(DAC.readRegister());


        //Rücklesen des EEPROM
        Serial.print("EEPROM: ");
        Serial.println(DAC.readEEPROM());


        delay(1000);
      }
    }