15-Bit Resistor DAC Output Formula

1. Introduction

Today, I derived the output formula for an 8-bit resistor DAC, calculating the impact of the microcontroller’s IO port resistance on the DAC’s linear accuracy. Now that I have some time left, I will patiently derive the analytical formula for the output voltage of a 15-bit DAC to verify whether the microcontroller’s IO resistance affects the DAC in relation to the number of channels.

15-Bit Resistor DAC Output Formula

2. Derivation Process

The theoretical formula for the DAC was still generated using the LCA software package from this morning, only this time the circuit structure was increased to 15 channels. This is the circuit diagram of the R2R resistor DAC drawn by LCA. After that, it generated the analytical expression for the output of the final circuit. This solving process took about 10 minutes on the computer.

15-Bit Resistor DAC Output Formula
#!/usr/local/bin/python
# -*- coding: gbk -*-
#******************************
# TEST1.PY                   - by Dr. ZhuoQing 2024-04-03
#
# Note:
#******************************

from headm import *
from sympy                  import symbols,simplify,expand,print_latex
from sympy                  import *
from lcapy                  import *
from sympy                  import symbols,simplify,expand,print_latex
from sympy                  import *

#------------------------------------------------------------
cct = Circuit("""

V1   1_0 0_1 dc V1  ; down
W0   0  0_1         ; right
R01  0  0_0   R2    ; up
R11  1_0 1    R2    ; up
W00  1  0_0         ; left

R12  1 2      R1    ; right
W1   0_1 0_2        ; right
V2   2_0 0_2 dc V2  ; down
R21  2_0 2    R2    ; up

R22  2 3      R1    ; right
W2   0_2 0_3        ; right
V3   3_0 0_3 dc V3  ; down
R31  3_0 3    R2    ; up

R32  3 4      R1    ; right
W3   0_3 0_4        ; right
V4   4_0 0_4 dc V4  ; down
R41  4_0 4    R2    ; up

R42  4 5      R1    ; right
W4   0_4 0_5        ; right
V5   5_0 0_5 dc V5  ; down
R51  5_0 5    R2    ; up

R52  5 6      R1    ; right
W5   0_5 0_6        ; right
V6   6_0 0_6 dc V6  ; down
R61  6_0 6    R2    ; up

R62  6 7      R1    ; right
W6   0_6 0_7        ; right
V7   7_0 0_7 dc V7  ; down
R71  7_0 7    R2    ; up

R72  7 8      R1    ; right
W7   0_7 0_8        ; right
V8   8_0 0_8 dc V8  ; down
R81  8_0 8    R2    ; up

R82  8 9      R1    ; right
W8   0_8 0_9        ; right
V9   9_0 0_9 dc V9  ; down
R91  9_0 9    R2    ; up

R92  9 10     R1    ; right
W9   0_9 0_10        ; right
V10  10_0 0_10 dc V10 ; down
R101 10_0 10    R2    ; up

R102 10 11     R1    ; right
W10  0_10 0_11      ; right
V11  11_0 0_11 dc V11 ; down
R111 11_0 11    R2    ; up

R112 11 12     R1    ; right
W11  0_11 0_12      ; right
V12  12_0 0_12 dc V12 ; down
R121 12_0 12    R2    ; up

R122 12 13     R1    ; right
W12  0_12 0_13      ; right
V13  13_0 0_13 dc V13 ; down
R131 13_0 13    R2    ; up

R132 13 14     R1    ; right
W13  0_13 0_14      ; right
V14  14_0 0_14 dc V14 ; down
R141 14_0 14    R2    ; up

R142 14 15     R1    ; right
W14  0_14 0_15      ; right
V15  15_0 0_15 dc V15 ; down
R151 15_0 15    R2    ; up

""")

#------------------------------------------------------------
import pyautogui

schfn = r'd:\temp\cct.png'
cct.draw(schfn)

rect = tspgetwindowrect(schfn.split('\\')[-1])
pyautogui.FAILSAFE = False
if sum(rect) != 0:
    pyautogui.click(rect[2]-30, rect[1]+15)
os.startfile(schfn)
printf('\a')
tspfocuswindow("TEASOFT:3")

#------------------------------------------------------------
result = cct[15].V(t)
printf(result)
exit()

#------------------------------------------------------------
mstr = latex(result)
printf(mstr)
_=tspexecutepythoncmd("msg2latex")

#------------------------------------------------------------
#        END OF FILE : TEST1.PY
#******************************

15-Bit Resistor DAC Output Formula

▲ Figure 1.2.1 15-Bit DAC Structure

Using LCA to derive the output voltage formula, I converted it into a Python program. This allows for the calculation of the output of the resistor network under different DAC values. It can be seen that with R1 and R2 being 10k and 20k ohms respectively, the output voltage shows a strict linear relationship with the DAC value. The error between the output voltage and the theoretical DAC value is the same as the theoretical value, aside from minor errors caused by the computer’s floating-point precision.

15-Bit Resistor DAC Output Formula
#!/usr/local/bin/python
# -*- coding: gbk -*-
#******************************
# TEST3.PY                   - by Dr. ZhuoQing 2024-04-03
#
# Note:
#******************************

from headm import *

def Dr1r2(R1,R2):
    return 2*R1**14 + 55*R1**13*R2 + 676*R1**12*R2**2 + 4900*R1**11*R2**3 + 23276*R1**10*R2**4 +\
           76153*R1**9*R2**5 + 175560*R1**8*R2**6 + 286824*R1**7*R2**7 + 329460*R1**6*R2**8 +\
           260338*R1**5*R2**9 + 136136*R1**4*R2**10 + 44200*R1**3*R2**11 + 8008*R1**2*R2**12 + 665*R1*R2**13 + 16*R2**14

def N1(R1,R2):
    return R2**14

def N10(R1,R2):
    return (2*R1**9*R2**5 + 33*R1**8*R2**6 + 225*R1**7*R2**7 + 819*R1**6*R2**8 + 1716*R1**5*R2**9 +
            2079*R1**4*R2**10 + 1386*R1**3*R2**11 + 450*R1**2*R2**12 + 54*R1*R2**13 + R2**14)

def N11(R1,R2):
    return (2*R1**10*R2**4 + 37*R1**9*R2**5 + 289*R1**8*R2**6 + 1240*R1**7*R2**7 + 3185*R1**6*R2**8 +
            5005*R1**5*R2**9 + 4719*R1**4*R2**10 + 2508*R1**3*R2**11 + 660*R1**2*R2**12 + 65*R1*R2**13 + R2**14)

def N12(R1,R2):
    return (2*R1**11*R2**3 + 41*R1**10*R2**4 + 361*R1**9*R2**5 + 1785*R1**8*R2**6 + 5440*R1**7*R2**7 +
            10556*R1**6*R2**8 + 13013*R1**5*R2**9 + 9867*R1**4*R2**10 + 4290*R1**3*R2**11 + 935*R1**2*R2**12 + 77*R1*R2**13 + R2**14)

def N13(R1,R2):
    return (2*R1**12*R2**2 + 45*R1**11*R2**3 + 441*R1**10*R2**4 + 2470*R1**9*R2**5 + 8721*R1**8*R2**6 +
            20196*R1**7*R2**7 + 30940*R1**6*R2**8 + 30888*R1**5*R2**9 + 19305*R1**4*R2**10 + 7007*R1**3*R2**11 +
            1287*R1**2*R2**12 + 90*R1*R2**13 + R2**14)

def N14(R1,R2):
    return (2*R1**13*R2 + 49*R1**12*R2**2 + 529*R1**11*R2**3 + 3311*R1**10*R2**4 + 13300*R1**9*R2**5 +
            35853*R1**8*R2**6 + 65892*R1**7*R2**7 + 82212*R1**6*R2**8 + 68068*R1**5*R2**9 + 35750*R1**4*R2**10 +
            11011*R1**3*R2**11 + 1729*R1**2*R2**12 + 104*R1*R2**13 + R2**14)

def N15(R1,R2):
    return (2*R1**14 + 53*R1**13*R2 + 625*R1**12*R2**2 + 4324*R1**11*R2**3 + 19481*R1**10*R2**4 + 59983*R1**9*R2**5 +
            128877*R1**8*R2**6 + 193800*R1**7*R2**7 + 201552*R1**6*R2**8 + 140998*R1**5*R2**9 + 63206*R1**4*R2**10 +
            16744*R1**3*R2**11 + 2275*R1**2*R2**12 + 119*R1*R2**13 + R2**14)

def N2(R1,R2):
    return (2*R1*R2**13 + R2**14)

def N3(R1,R2):
    return (2*R1**2*R2**12 + 5*R1*R2**13 + R2**14)

def N4(R1,R2):
    return (2*R1**3*R2**11 + 9*R1**2*R2**12 + 9*R1*R2**13 + R2**14)

def N5(R1,R2):
    return (2*R1**4*R2**10 + 13*R1**3*R2**11 + 25*R1**2*R2**12 + 14*R1*R2**13 + R2**14)

def N6(R1,R2):
    return (2*R1**5*R2**9 + 17*R1**4*R2**10 + 49*R1**3*R2**11 + 55*R1**2*R2**12 + 20*R1*R2**13 + R2**14)

def N7(R1,R2):
    return (2*R1**6*R2**8 + 21*R1**5*R2**9 + 81*R1**4*R2**10 + 140*R1**3*R2**11 + 105*R1**2*R2**12 + 27*R1*R2**13 + R2**14)

def N8(R1,R2):
    return (2*R1**7*R2**7 + 25*R1**6*R2**8 + 121*R1**5*R2**9 + 285*R1**4*R2**10 + 336*R1**3*R2**11 +
            182*R1**2*R2**12 + 35*R1*R2**13 + R2**14)

def N9(R1,R2):
    return (2*R1**8*R2**6 + 29*R1**7*R2**7 + 169*R1**6*R2**8 + 506*R1**5*R2**9 + 825*R1**4*R2**10 + 714*R1**3*R2**11 +
            294*R1**2*R2**12 + 44*R1*R2**13 + R2**14)

#------------------------------------------------------------
def DAC15(number, r1, r2):
    D = Dr1r2(r1,r2)
    Ndim = [N15(r1,r2), N14(r1,r2), N13(r1,r2),
            N12(r1,r2), N11(r1,r2),
            N10(r1,r2), N9(r1,r2),
            N8(r1,r2), N7(r1,r2), N6(r1,r2),
            N5(r1,r2), N4(r1,r2), N3(r1,r2),
            N2(r1,r2), N1(r1,r2)]

    nstr = bin(number)[2:]
    nstr = '0'*(15-len(nstr)) + nstr

    number = [int(s)*n/D for s,n in zip(nstr, Ndim)]
    return sum(number)

#------------------------------------------------------------
r1 = 10e3
r2 = 20e3+50
v = DAC15(0x8000, r1, r2)
Vmax = 3.3

ddim = arange(0, 0x7fff, 0x10)
vdim = [DAC15(d,r1,r2)*Vmax for d in ddim]
odim = [d*Vmax/0x8000 for d in ddim]
edim = [v1-v2 for v1,v2 in zip(vdim, odim)]

plt.plot(ddim, edim, lw=3)

plt.xlabel("DAC(N)")
plt.ylabel("Voltage(V)")
plt.grid(True)
plt.tight_layout()
plt.show()

#------------------------------------------------------------
printf("\a")

#------------------------------------------------------------
#        END OF FILE : TEST3.PY
#******************************

This calculates the DAC output error by increasing R2 by 50 ohms to simulate the output resistance of the microcontroller’s IO port. The error is approximately 13mV, which is the same as the error corresponding to the 8-channel case this morning. This indicates that the absolute value of the linear error caused by the microcontroller’s IO port resistance is independent of the number of channels.

15-Bit Resistor DAC Output Formula

▲ Figure 1.2.2 Linear Error After Increasing R2 by 50 Ohms

※ Conclusion ※

This article derived the output voltage expression for a 15-bit resistor DAC. By increasing the R2 resistance by 50 ohms, the output error of the DAC was calculated, verifying that the R2R resistor network is affected by the output resistance of the microcontroller’s IO port. The magnitude of the error is unrelated to the number of channels. The simulation Python program and data can be found in the CSDN article. This lays a foundation for further theoretical analysis of the resistor DAC.

Leave a Comment