Arduinos have analog outputs, but they generate a pulse-width modulation (PWM) signal rather than a voltage. There are a number of circuits to create a voltage from a bit representation. One of the simplest and most elegant is the R/2R network, which only needs two resistors per bit.
A Bit of Theory
The R/2R network gets its name from the fact that it uses resistors with only two different values, one base value and one with twice the base value. Since the resistors are set up as voltage dividers, the actual value doesn’t matter. What does matter, though, is the tolerance, which has to be as low as possible.
This is not entirely without its pitfalls, though. Standard resistors don’t come in values that are multiples of two, so the 2R part has to be made from two resistors. The typical 1/4W resistors are 5% tolerance, metal film resistors have 1% or better tolerances. Wikipedia says that using 1% resistors, only up to five bits can be represented without variations leading to supposedly higher values being turned into lower voltages, and with 5%.
Building the DAC
So with that in mind, I decided to build one. I had a set of 10KΩ metal film (1%) resistors and a shift register to put out five bits of data. The whole thing was driven from an Arduino. Here’s the setup in all its Fritzing glory.
And a picture of what the breadboard actually looks like.
As you can see, not all my resistors are of the 1% type, since I only had 10 of those. The 5% resistors are all used for the lowest bits, where any deviation from the actual value does the least damage.
How It Works
The heart of this is a shift register that is controlled by an Arduino. The microcontroller shifts a number into the shift register bit by bit, then tells it to show those bits at its outputs. From there, this circuit is exactly the R/2R network shown at the beginning, with each input being one of the output pins of the shift register.
For simplicity, I’m simply connecting them directly here, assuming that the shift register will pull its outputs up all the way to 5V for a 1 and down all the way to 0V for a 0. That seems to be working, as we’ll see in a moment.
The output of the resistor network/DAC is connected back to an analog input pin of the Arduino. This allows me to measure how well the converter actually works.
I wrote a little program that cycled through all 32 possible five-bit values 100 times and measured the DAC’s output via an analog pin. The output was sent to my Mac via the serial/USB connection. Below is the result, showing the measured values against what was expected. Since the Arduino gives me 10-bit samples, I had to multiply the five-bit numbers by 32 (or shift left by five bits) to know what value I’d expect coming in.
Very good! Very linear, with small deviations that are smallest in the middle and larger at the ends of the range.
Looking at error specifically, here is the difference between measured and expected values, the size of the bubbles shows how common each of the values is. This is on the 10-bit scale.
Amazingly accurate. We’re at the edge of the resolution of the Arduino’s ADC here, with the bulk of the measurements straddling the boundary between two neighboring values.
The systematic error is not surprising: it’s easy to lose a millivolt or two from the power supply to the breadboard and across the transistors inside the shift register.
Building a DAC is all good and well, but what now? The next step is to use it to do stuff, like make noise (which you can’t as easily with a PWM signal). Or add bits and see how well we do when we’re trying to represent eight or ten bits.
R/2R network illustration by Wikipedia user Lsibilla, used under Creative Commons.