Skip to content
copperhead.sh
Get started

Examples / TI op amp handbook / Summers

Adder

SBOA092B page 64, Adder: E1, E2 and E3 each through 10 kΩ into the summing point, 10 kΩ from the output back to it, and the non-inverting input on ground.

E_O = -(E1 + E2 + E3)
Z_in = 10 kΩ for each input
the schematic, drawn by copperhead from the circuit's netlist
The schematic, drawn by copperhead from the circuit's netlist

The schematic is drawn by copperhead’s drafting engine from this circuit’s netlist, with KiCad’s own library symbols, and it opens in KiCad as figure/adder.kicad_sch. The op amp is KiCad’s generic one, since the handbook’s are ideal, and each terminal is a test point named as the program names it. KiCad reads back from the sheet exactly the connections the circuit has; draw_figures.py refuses to write one that does not.

the interconnect view, fang's own projection
The interconnect view, fang's own projection

The interconnect view is fang’s own projection. It names the parts as the program does, so it reads against the code below.

Each input is an inverting amplifier of gain a_v = -1, and because the summing point is a virtual ground each source sees only its own resistor, z_in = 10 kΩ. Both hold for every input:

for r_n in (self.r_1, self.r_2, self.r_3):
require(equals(self.a_v, negative(over(r_o, r_n.resistance))))
require(equals(self.z_in, r_n.resistance))

The figure gives every value, so nothing was chosen.

out/simulation.txt, from the deck under out/spice/. One run drives E1..E3 with 1, 2 and -0.5 V:

Measured in sumMeasuredClaimed
E_O-2.5 V-2.5 V, holds
E_O / (E1 + E2 + E3)-1-1 (a_v), holds
E1 / I110 kΩ10 kΩ (z_in), holds
E2 / I210 kΩ10 kΩ (z_in), holds
E3 / I310 kΩ10 kΩ (z_in), holds

The impedances hold with all three inputs driven at once, which is what “the inputs are effectively isolated from each other” means.

Terminal window
fang check examples/ti_opamp_handbook/summers/adder/adder.py
python examples/regenerate.py ti_opamp_handbook/summers/adder # needs ngspice
examples/ti_opamp_handbook/summers/adder/adder.py
"""The adder, SBOA092B page 64.
Show 13 more lines
E_O = -(E1 + E2 + E3), Z_in = 10 kOhm for each input
Three 10 kOhm resistors bring E1, E2 and E3 to the summing point, and a fourth
10 kOhm runs from the output back to it. Each input is an inverting amplifier
of gain -1 on its own, and because the summing point sits at ground, each
source sees only its own resistor: that is the 10 kOhm the page gives as the
input impedance, and why the inputs do not load one another.
The figure gives every value, so nothing was chosen. The bench drives the
three inputs with distinct voltages at once, reads E_O against their sum, and
reads each input's impedance from the current its source delivers.
"""
import sys
from pathlib import Path
# The handbook's shared parts and bench live in the folder above the sections.
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from fang.lang import Parameter, System, kOhm, require
from fang.parts import Resistor
from fang.rationale import Cites
from fang.simulation import OperatingPoint
from handbook import (
Bench,
Claim,
Ground,
OpAmp,
Run,
Terminal,
equals,
negative,
over,
ratio,
)
class Adder(System):
"""E1, E2, E3 each through 10 kOhm into the summing point, 10 kOhm back."""
figure = Cites(
"E_O = -(E1 + E2 + E3). Z_in = 10 kOhm for each input",
document="SBOA092B, Handbook of Operational Amplifier Applications",
locator="page 64, Adder",
)
a_v = Parameter("1", default=-1 * ratio, description="E_O / E_n, for each input")
z_in = Parameter("Ohm", default=10 * kOhm, description="what each source sees")
e1 = Terminal()
e2 = Terminal()
e3 = Terminal()
common = Terminal()
e_out = Terminal()
r_1 = Resistor(resistance=10 * kOhm)
r_2 = Resistor(resistance=10 * kOhm)
r_3 = Resistor(resistance=10 * kOhm)
r_feedback = Resistor(resistance=10 * kOhm)
amp = OpAmp()
ground = Ground()
def architecture(self):
self.e1.probe >> self.r_1.p1
self.e2.probe >> self.r_2.p1
self.e3.probe >> self.r_3.p1
self.r_1.p2 >> self.amp.inverting.signal
self.r_2.p2 >> self.amp.inverting.signal
self.r_3.p2 >> self.amp.inverting.signal
self.amp.inverting.signal >> self.r_feedback.p1
self.r_feedback.p2 >> self.amp.output.signal
self.amp.output.signal >> self.e_out.probe
self.amp.non_inverting.signal >> self.ground.node
self.common.probe >> self.ground.node
def constraints(self):
r_o = self.r_feedback.resistance
# Each input is an inverting amplifier of its own, all of the same gain.
for r_n in (self.r_1, self.r_2, self.r_3):
require(equals(self.a_v, negative(over(r_o, r_n.resistance))))
# The summing point is a virtual ground, so a source sees its resistor.
require(equals(self.z_in, r_n.resistance))
BENCH = Bench(
page=64,
title="Adder",
runs=[
Run(
"sum",
OperatingPoint(),
drive={"e1": "DC 1", "e2": "DC 2", "e3": "DC -0.5"},
measure={
"e_o": "v({e_out.1})",
"sum_gain": "v({e_out.1}) / (v({e1.1}) + v({e2.1}) + v({e3.1}))",
"z_in_e1": "-v({e1.1}) / i(vdrive_e1)",
"z_in_e2": "-v({e2.1}) / i(vdrive_e2)",
"z_in_e3": "-v({e3.1}) / i(vdrive_e3)",
},
claims=[
Claim("e_o", -2.5, within=0.001, unit="V",
note="-(1 + 2 - 0.5) = -2.5 V"),
Claim("sum_gain", "a_v", within=0.001),
Claim("z_in_e1", "z_in", within=0.001, unit="Ohm"),
Claim("z_in_e2", "z_in", within=0.001, unit="Ohm"),
Claim("z_in_e3", "z_in", within=0.001, unit="Ohm"),
],
note=(
"Each input's impedance is its drive voltage over the current its "
"source delivers, which flows out of the source's + terminal and so "
"reads negative in SPICE."
),
),
],
)

The parts, then the nets and the pads on them.

out/netlist.txt
GND1 Ground -
R1 10 kOhm -
R2 10 kOhm -
R3 10 kOhm -
R4 10 kOhm -
TP1 Terminal -
TP2 Terminal -
TP3 Terminal -
TP4 Terminal -
TP5 Terminal -
U1 OpAmp -
Net-(GND1-Pad1) GND1.1 TP1.1 U1.IN+
Net-(R1-Pad1) R1.1 TP2.1
Net-(R1-Pad2) R1.2 R2.2 R3.2 R4.1 U1.IN-
Net-(R2-Pad1) R2.1 TP3.1
Net-(R3-Pad1) R3.1 TP4.1
Net-(R4-Pad2) R4.2 TP5.1 U1.OUT

Every check that ran, and every one left undecided.

out/checks.txt
6 checks, 0 failed, 0 undecided

What the elaborated graph contains, by entity kind.

out/graph.txt
1 block
11 component
22 connection
6 constraint
1 evidence
3 interface
17 pin
17 port
78 total
snapshot sha256:415d4b23c1033a4299b2789cea9ad7184c14ebbf53f9e59aa580b9f457dbb862

All of it, including the KiCad netlist, is in examples/ti_opamp_handbook/summers/adder/out/. Rebuild it with:

Terminal window
fang build examples/ti_opamp_handbook/summers/adder/adder.py