Building A Better DAC

I’ve had a lot of fun playing with my PWM Oscillator but at the end of the day it’s a bit limited. 10Hz is barely going to get us very far and the waveforms aren’t exactly smooth. Surely we can do better!

Today I’d like to start exploring something a bit more interesting. An R2R Digital to Analog Converter. Here I will use multiple digital pins and a resistor ladder to create a much more versatile analog oscillator. For now I am going start with a very basic version of the converter to hammer out the logic. Once this is done I can start building the circuit and code up to see what it can do.

The Circuit

4-Bit R2R DAC

Looking at the circuit diagram gives some indication of where this converter gets it’s name. The circuit is made up of a ladder of resistors composed of 2R from each digital pin and R connecting them together (Where R is any resistor value). For mine I’ve used 200 and 100 ohms respectively.

These resistors function as a chain of Voltage Dividers. The math here gets a bit complicated but how it all pans out is this; The signal from the pin closest to the output (Pin 11 above) will give a voltage of approximately half your range at the output, each subsequent pin will reach the output with half of the voltage of the previous pin. This means if you had an output range of 5V, pin 11 would output 2.5V, pin 10 would output 1.25V, 9 would output 0.625V and 8 would output 0.3125V. When multiple pins are set high at once these voltages are added together. This gives us a wide range of voltages to play with.

It’s important to note that these are idealized values. Dependent on the tolerance of the resistors you use there will be variances between the resistor values. These variances will skew these numbers and distort your output. For the time being I will let this slide and use what I have on hand as a proof of concept. As I refine this circuit however, I will have to make allowances for this. This can be done by either testing a large number of resistors to find ones which most closely match your target resistance or adding trimmer pots to fine tune the resistances in the ladder.

Test Code

I feel like the functionality of this converter becomes clear when you see it in action. To that end I want to set up some code to test it out.

#define PIN_8 8
#define PIN_9 9
#define PIN_10 10
#define PIN_11 11

int DAC_bus[] = {PIN_8, PIN_9, PIN_10, PIN_11};

void test_run();

First up are some definitions and declarations. The first set of definitions set up my digital pins. Once they are defined I place the pins in an array to keep things organized. Finally I define a test_run function which I will use to run a simple test on the converter.

void setup() {
  for (int k = 0; k < 4; k++){
    pinMode(DAC_bus[k], OUTPUT);
  }

In the setup function I iterate through the DAC_bus and set each pin to output.

void loop() {
  test_run();
}

void test_run(){
  for (int i = 0; i < 4; i++){
    digitalWrite(DAC_bus[i], HIGH);
    delay(5);
    }
  for (int i = 0; i < 4; i++){
    digitalWrite(DAC_bus[4-i], LOW);
    delay(5);
    }
  }

Finally we create the test_run function and call it in the main loop. The test run function sets each of the pins to high 5ms apart then sets each pin back to low.

Test 1 Output

Looking at the output of this simple test you can distinctly see the pins being turned on and off and the corresponding changes in voltage.

Saw Tooth

These voltage levels only begin to tell the story. By turning different combinations of pins on and off we can actually achieve 16 distinct voltage levels (including 0V). To show this I wrote a second test function which draws a saw tooth wave.

void test_run2(){
  for (int i = 0; i < 15; i++){
    int value = i;
    for (int k = 0; k < 4; k++){
      digitalWrite(DAC_bus[k], value%2);
      value = value/2;
    }
    delay(1);
  }
}

This one may look a bit strange if you haven’t worked extensively with binary numbers. This is something I’ll explore further when we start drawing wave forms. Essentially what’s happening though is we are counting from zero to 15, converting each number to binary and writing those binary numbers to the digital pins. The output I received running this second test were as follows:

Test 2 Output

Here we can see the wide variety of voltages we can obtain with only 4 digital channels. With a little filtering we could turn this into a fairly workable saw wave.

This output also illustrates the issue with high tolerance resistors. In an idealized version of this converter this saw wave would look like a perfect staircase. Each step up would be of equal voltage. In our output we can see this is not the case. This is likely due to variations in the resistor values I used build the converter. In future posts I will look at refining my circuit to improve this performance.

The Code

I’m going to finish things off today by providing the full code to get this up and running. There are a lot of places I can go from here but I hope this introduction gives a brief illustration of how R2R DACs work and provides some idea of what we can accomplish with them.

#define PIN_8 8
#define PIN_9 9
#define PIN_10 10
#define PIN_11 11

int DAC_bus[] = {PIN_8, PIN_9, PIN_10, PIN_11};

void test_run();

void test_run2();

void setup() {
  for (int k = 0; k < 4; k++){
    pinMode(DAC_bus[k], OUTPUT);
  }

}

void loop() {
  test_run2();  
}


void test_run(){
  for (int i = 0; i < 4; i++){
    digitalWrite(DAC_bus[i], HIGH);
    delay(5);
    }
  for (int i = 0; i < 4; i++){
    digitalWrite(DAC_bus[4-i], LOW);
    delay(5);
    }
  }

//saw tooth test function
void test_run2(){
  for (int i = 0; i < 15; i++){
    int value = i;
    for (int k = 0; k < 4; k++){
      digitalWrite(DAC_bus[k], value%2);
      value = value/2;
    }
    delay(1);
  }
}