I’m building an r2r ladder which is a way to convert a group of binary voltage inputs to a continuous analog output. For example, an 8-bit r2r ladder would output a single voltage from 0-Vcc at 255 discrete voltage points that are about 20 mV apart.

This is a digital-to-analog conversion (DAC), and I want one in order to build an arbitrary waveform generator (like this one or this one). The DAC will take 8 pins from the arduino, treat them like the bits of a byte, then output a voltage between GND and whatever the Arduino is outputting (Vcc = 3.3 or 5v). That is, we use the DAC to go from a set of digital values in a given waveform to an analog voltage. We need that for any waveform that has a progression from 0-Vcc, such as a sawtooth, a triangle, or a sine.

So I built it, added 8 pins from an Arduino, wrote a sketch to step through 0-255 and write the resulting byte to the 8 pins. I measured the result, and it wasn’t quite what I hoped. First, here’s what it looks like on an oscilloscope.

The problem is those occasional big dropouts, the vertical yellow lines that seem to dribble down off the main diagonal. I wrote the data to a file and started analyzing it. First, the voltage-by-byte. I ran over the 0-255 values 30 times to get a decent distribution. It’s not quite the same as the oscilloscope because the ina260 I used to measure the voltage does two things: first, it measures for 140us, which is about 7000 times slower than the oscope. Second, it measures 16 times and then reports the mean of the measures, so in practice, the ina260 is more than 100,000 times slower than the oscope.

This looks mostly good, except for the two notches which turn out to be at 129 and 178. Yikes, and yuck. If we’re concerned about a stable increment between bytes, let’s look at the first differences, that is, (voltage at byte_k) – (voltage at byte_{k-1}). These are called lagged diffs.

As we might expect, the difference clusters around 20mV, which makes plenty of sense. Unfortunately, there’s a second small cluster between -40 and -20. Those turn out to belong to bytes 129 and 174. There’s absolutely nothing interesting about those values, in a binary sense, which is a clue that this isn’t about a wiring error. If the glitches happened at 32 or 64 or 128, that would suggest that I’d done something wrong at a specific resistor.

Looking at the histogram just for 129, 174 there’s also nothing special in the distribution of lagged diffs.

Back to basics: what’s an r2r network anyway? Basically it’s a big voltage divider, and the stability of the output depends on the ratios among the resistances.

The reason we use the r2r pattern is specifically because the combination of 8 voltage dividers in one circuit allows each bit to contribute a unique chunk of voltage to the output. For example, the least significant bit of an 8-bit ladder contributes b/2^8 volts, where b is either 0 (if the bit is 0) or Vcc if the bit is 1. The next-least-significant bit contributes b/2^7 volts, and the one after that contributes b/2^6 volts, etc. The sum of these 8 contributions creates 255 unique voltage values.

That transformation works because in the voltage divider, all the r values are the same, and the 2r values are the same and = 2*r. But unfortunately, that’s not how most resistors really are. The ones I have on hand are 5% tolerances, so r is around 10K, and 2r is in the neighborhood of 20K. I started testing the resistances, and they came in between 9766 and 10055 ohms; the 2r values came in 19789-20129 ohms. These bits of imprecision yield enough weirdness to create the notches and nonlinearities in the graphs above.

Hmm. After a little reading, it became clear that this is how r2r networks fail: inconsistent values, and even more, inconsistent r/2r ratios, lead to nonlinearities –glitches — in the progression of output voltages. This is exactly what I’m seeing. There’s even a Hackaday post about trimming resistors to make them more consistent for this purpose.

I’m not going to do that because I’m lazy. I ordered a few 10K and 20K resistors with 0.1% tolerances from Mouser, and I’ll try again when they arrive.