Resistor DACs


There are a several techniques that are used.
One consists of 8 resistors, with each resistor twice the resistance of the previous one. You might for example connect them to port B on an AVR as shown below. The lowest resistance goes on the most significant bit. The highest resistance goes to the least significant bit. Connect each port pin to the appropriate resistor. Then connect the other side of all 8 resistors together. This common line becomes your output:
 PB7 ------ R ------+
 PB6 ----- 2R ------+
 PB5 ----- 4R ------+
 PB4 ----- 8R ------+
 PB3 ---- 16R ------+----- Vout
 PB2 ---- 32R ------+
 PB1 ---- 64R ------+
 PB0 --- 128R ------+
 

The overall output resistance is about R/2 ohms. Vout = (N / 255) * Vcc, where N is the number you output to port B. For the best accuracy the lowest value resistors should be accurate. The higher values are not so important.

You will have three constraints on your choice of R. First, the first few values should maintain the 2:1 ratio as closely as possible. Second, start with R low enough so that the 128R resistor isn't too big to find (less that 10M for 128R). And third, start with R high enough so that the output pins - particularly PB7 - are not loaded down too much.

For example you might choose the following series: 10.0K 1%, 20.0K 1%, 40.2K 1%, 80.6K 1%, 160K 5%, 330K 5%, 620K 5%, 1.3M 10%. For +/- 1 LSB accuracy, you need to select the 10.0K and the 20.0K resistors to be better than 1% tolerance.

A more common approach is to use 16 resistors in an R - 2R resistance ladder. This uses twice as many resistors but it has the advantage of using only two different resistor values instead of 8 different values.

Thanks to Paul Ostby for this.

R-2R ladder:


       20k    10k    10k    10k    10k    10k    10k    10k
Gnd o-VVVV-+-VVVV-+-VVVV-+-VVVV-+-VVVV-+-VVVV-+-VVVV-+-VVVV-+---o Out
           |      |      |      |      |      |      |      |
           >      >      >      >      >      >      >      >
           > 20k  > 20k  > 20k  > 20k  > 20k  > 20k  > 20k  > 20k
           >      >      >      >      >      >      >      >
           |      |      |      |      |      |      |      |
        D0 o   D1 o   D2 o   D3 o   D4 o   D5 o   D6 o   D7 o

 
Output impedance is 10k.

The port you would like to use for the 8 DAC bits also happens to be the comparator inputs. OK, let's just diddle a bit...

Let's use PORTB, 2 to 7 as the high six bits of the ADC, and PORTD 0 and 1 as the low two bits. I shall assume that those bits are set as outputs, and PORTB 0 and 1 as inputs. PORTB,0 is the analogue input and PORTB,1 is connected to the R-2R network.

.def    adcval  =r16

.macro  ripple
; four instructions, five or six cycles
        nop
        sbis    ACSR,ACO        ; If lower,
        cbi     PORTB,#0        ; clear that bit
        sbi     PORTB,#1        ; set next anyway
.endmacro

        clr     adcval
        out	ACSR,adcval
        ldi     adcval,$80
        out	PORTB,adcval
        cbi	PORTD,0
        cbi	PORTD,1         ;  Initialised.
        ripple  7,6
        ripple  6,5
        ripple  5,4
        ripple  4,3
        ripple  3,2
        nop
        sbis    ACSR,ACO
        cbi     PORTB,2
        sbi     PORTD,1
        in      adcval,PORTB
        sbis    ACSR,ACO
        cbi     PORTD,1
        sbi     PORTD,0
        andi	adcval,$FC      ; Mask off low bits
        sbic    ACSR,ACO        ; If higher,
        inc     adcval          ; Add one
        sbic    PORTD,1         ; Add in
        ori     adcval,2        ; bit 1

I count 39 instructions, and between 48 and 55 cycles which is more than I bargained for but ... it could be worse!

Thanks to Paul Webster for this.