avdweb

  • Increase font size
  • Default font size
  • Decrease font size
Home Arduino Hardware interfacing Simple 10 bit DAC

Simple 10 bit DAC for the Arduino

DAC on the oscilloscope

1. Intro

If you need a DAC for the Arduino, this 10 bit DAC is accurate, cheap and uses only few components. The Arduino processor, the ATmega328 / Atmega168, has ADC inputs but unfortunately no DAC outputs. Although the internal ADC contains a 10 bit DAC, this DAC can’t be used stand alone. Therefore I developed a 10 bit DAC, which is build with an integrator.

2. Details

  • Resolution and accuracy 10 bit
  • Output voltage 0 … 10V
  • Settling time 20ms max.
  • Uses 1 arbitrary digital I/O pin and 1 arbitrary analog I/O pin.

3. Hardware

The output voltage range is 5V * (R4 + R5) / R5 and can be changed by R4 and R5. The TLC272CN is a high input impedance precision opamp.
Because the opamp output isn’t rail to rail, the supply voltage must be higher than the maximum DAC output voltage. For an output voltage of 10V the supply voltage is 12V. We can use a single 5V power supply when it is no problem that the maximum DAC output voltage is limited.

Simple 10 bit Arduino dac schematic 

4. Libraries needed

Unzip the Dac library folder and place it in the standard Arduino library subfolder \libraries\. The libraries Streaming.h and Flash.h from Mikal Hart should also be installed. Download these libraries here: http://arduiniana.org.

5. Test software

Unzip the DacDemo project file. It contains three files. The DacDemo project tests the Dac application and shows how to use the Dac. See here for Troubleshooting.

6. Further improvements

Although the DAC works fine it should be possible to further improve it in size and speed. If you have any improvement ideas, please put a reply here.

7. Dac.cpp

/* Simple 10 bit DAC for the Arduino
Version 1.0
write(): Write a value to the DAC. 0 = 0V, 1023 = 10V.
read():  Read the actual output voltage.
refresh(): Because of leakage current, refresh the DAC periodically (10 sec. for 1 LSB error).
The settling time is max. 20ms.
Don't touch C1 / R1 during run.
 
               C1 100nF 10% MKT  
                _____||____
               |     ||    |  TLC272CN (VDD=12V, GND=0V)
           R1  |  |\       |
   I/O 2--56k-----|-\      |
                  |  \ ____|____ DAC out 0 ... 10V
           R2     |  /    |     
     5V- -10k-----|+/     |
               |  |/  R4 10k
               |          |_____
           R3 10k         |     |
               |      R5 10k    |
               |          |     |
              GND        GND    |
    ADC 0-----------------------
         
            5V |      _       _
               |     | |     | |
  I/O 2        |     | |     | |
          2.5V |_____| |_____| |______
               |
               |
            0V |______________________
          
               |______
               |      \_______
 DAC out       |              \_______ 
 (not to scale)| 
               |______________________
*/
 
#include <Arduino.h>
#include "Dac.h"
 
Dac::Dac():
dacUpdownPin(2), UDacPin(0), overshoot(5)
{ write(512); // set to 2,5V 
  write(512); // the first conversion can be wrong 
}
 
bool Dac::write(int val)
{ targetVal = val;
  if(targetVal > 1023) targetVal = 1023;
  if(targetVal < 0) targetVal = 0;   
  
  if(abs(read() - targetVal) > overshoot) // avoid overshoot from setDac() for small value changes
    if(!setDac()) return false; 
  if(!fineTune()) return false;
  if(abs(read() - targetVal) > 1) return false; // final error check
  return true;
 
bool Dac::refresh()
{ if(!fineTune()) return false;
  return true;
 
int Dac::read() const// not inline
{ return analogRead(UDacPin);
}
 
inline int Dac::fastRead() const
{ return analogRead(UDacPin);
}
  
/* To increase the DAC speed, the overshoot is reduced by an overshoot value (5). 
   However, this mechanism has a small influence on the accuracy. The DAC error is 0 or 1. 
   When the overshoot value is changed to 0 the DAC error is mostly 0. This mechanism can perhaps be improved.
*/
 
bool Dac::setDac()  
{ const byte timeout1 (255); // maxSettlingTime1 = 195
  int targetCorr; 
 
  if(read() == targetVal) return true; 
  if(read() < targetVal) 
  { targetCorr = targetVal - overshoot; // reduce overshoot caused by adc delay
    dacUp();
    for(settlingTime1=0; settlingTime1 < timeout1; settlingTime1++) 
    { if(fastRead() >= targetCorr) 
      { dacHold(); 
        break;
      }
    }
  } else  
  { targetCorr = targetVal + overshoot;
    dacDown();
    for(settlingTime1=0; settlingTime1 < timeout1; settlingTime1++)
    { if(fastRead() <= targetCorr)  
      { dacHold(); 
        break;
      }
    }
  }
  dacHold(); // end always with hold, in case of timeout
  if(settlingTime1 >= timeout1) return false;
  else return true;
}
 
bool Dac::fineTune() // produces no overshoot 
{ const byte timeout2 (80); // maxSettlingTime2 ~ 20
  const byte halfLsbCorrection (1);
  
  if(read() == targetVal) return true; // avoid ripple at refresh()
  if(read() < targetVal) 
  { for(settlingTime2=0; settlingTime2 < timeout2; settlingTime2++)
    { dacUp(); dacHold(); // finetuning with short pulse 
      if(fastRead() >= targetVal) 
      { for(int i=0; i<halfLsbCorrection; i++) dacUp(); // reduce error to 0
        break;
      }
    }     
  } else  
  { for(settlingTime2=0; settlingTime2 < timeout2; settlingTime2++)
    { dacDown(); dacHold(); // finetuning with short pulse 
      if(fastRead() <= targetVal) 
      { for(int i=0; i<halfLsbCorrection; i++) dacDown(); // reduce error to 0
        break;
      }
    }
  }
  dacHold(); // end always with hold, in case of timeout
  if(settlingTime2 >= timeout2) return false;
  else return true;
}
 
void Dac::dacUp() const
{ digitalWrite(dacUpdownPin, LOW);
  pinMode(dacUpdownPin, OUTPUT); 
}
void Dac::dacDown() const
{ digitalWrite(dacUpdownPin, HIGH);
  pinMode(dacUpdownPin, OUTPUT);
}
 
void Dac::dacHold() const
{ pinMode(dacUpdownPin, INPUT); // high impedance tristate 
  digitalWrite(dacUpdownPin, LOW); // disable pull up resistor 1*)
}  
 

Google Translate

English Dutch French German Italian Portuguese Russian Spanish