Examples / TI op amp handbook / Differential amplifiers
Subtractor
SBOA092B page 74, Subtractor: the differential input amplifier of page 67 with every resistor 10 kΩ. E1 goes through RI into the inverting input with RO back from the output; E2 is divided by RI over RO onto the non-inverting input.
E_O = E2 - E1The circuit
Section titled “The circuit”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/subtractor.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 is fang’s own projection. It names the parts as the program does, so it reads against the code below.
What the program says
Section titled “What the program says”The figure gives every value, so nothing is chosen. Two constraints: the two
legs have the same R_O/R_I ratio, which is what makes the circuit subtract,
and that ratio is the gain, a_d = 1.
What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the decks under
out/spice/:
| Run | Measured | Claimed |
|---|---|---|
difference, E1 = 1.5 V, E2 = 4 V: E_O | 2.5 V | 2.5 V, holds |
difference: E_O / (E2 - E1) | 1 | 1 (a_d), holds |
common_mode, E1 = E2 = 2 V: E_O | 0 V | 0 V ± 100 µV, holds |
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/differential_amplifiers/subtractor/subtractor.pypython examples/regenerate.py ti_opamp_handbook/differential_amplifiers/subtractor # needs ngspiceThe whole program
Section titled “The whole program”"""The subtractor, SBOA092B page 74.Show 11 more lines
E_O = E2 - E1
The differential input amplifier of page 67 with every resistor 10 kOhm: E1through R_I into the inverting input with R_O back from the output, E2through R_I and R_O as a divider onto the non-inverting input. With R_O equalto R_I the difference gain is 1, and what comes out is the subtraction itself.
The figure gives every value, so nothing was chosen. The bench drives adifference, and then the same voltage on both inputs."""
import sysfrom 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, requirefrom fang.parts import Resistorfrom fang.rationale import Citesfrom fang.simulation import OperatingPoint
from handbook import ( Bench, Claim, Ground, OpAmp, Run, Terminal, equals, over, ratio,)
class Subtractor(System): """E1 into the - input, E2 divided onto the + input, all resistors 10 kOhm."""
figure = Cites( "E_O = E2 - E1. Direct subtraction of two inputs", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 74, Subtractor", )
a_d = Parameter("1", default=1 * ratio, description="E_O / (E2 - E1)")
e1 = Terminal() e2 = Terminal() common = Terminal() e_out = Terminal() r_in_top = Resistor(resistance=10 * kOhm) r_out_top = Resistor(resistance=10 * kOhm) r_in_bottom = Resistor(resistance=10 * kOhm) r_out_bottom = Resistor(resistance=10 * kOhm) amp = OpAmp() ground = Ground()
def architecture(self): # The top leg: E1 into the summing point, R_O back from E_O. self.e1.probe >> self.r_in_top.p1 self.r_in_top.p2 >> self.amp.inverting.signal self.amp.inverting.signal >> self.r_out_top.p1 self.r_out_top.p2 >> self.amp.output.signal self.amp.output.signal >> self.e_out.probe
# The bottom leg: E2 divided by R_I over R_O onto the + input. self.e2.probe >> self.r_in_bottom.p1 self.r_in_bottom.p2 >> self.amp.non_inverting.signal self.amp.non_inverting.signal >> self.r_out_bottom.p1 self.r_out_bottom.p2 >> self.ground.node
# The grounded terminal between E1 and E2. self.common.probe >> self.ground.node
def constraints(self): # The two legs' ratios match, which is what makes it a subtractor, and # the matched ratio is the gain. require( equals( over(self.r_out_top.resistance, self.r_in_top.resistance), over(self.r_out_bottom.resistance, self.r_in_bottom.resistance), ) ) require(equals(self.a_d, over(self.r_out_top.resistance, self.r_in_top.resistance)))
BENCH = Bench( page=74, title="Subtractor", runs=[ Run( "difference", OperatingPoint(), drive={"e1": "DC 1.5", "e2": "DC 4"}, measure={ "e_o": "v({e_out.1})", "gain": "v({e_out.1}) / (v({e2.1}) - v({e1.1}))", }, claims=[ Claim("e_o", 2.5, within=0.001, unit="V", note="4 V - 1.5 V"), Claim("gain", "a_d", within=0.001), ], ), Run( "common_mode", OperatingPoint(), drive={"e1": "DC 2", "e2": "DC 2"}, measure={"e_o": "v({e_out.1})"}, claims=[Claim("e_o", 0, within=1e-4, absolute=True, unit="V", note="2 V on both inputs: nothing to subtract")], ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
GND1 Ground -R1 10 kOhm -R2 10 kOhm -R3 10 kOhm -R4 10 kOhm -TP1 Terminal -TP2 Terminal -TP3 Terminal -TP4 Terminal -U1 OpAmp -Net-(GND1-Pad1) GND1.1 R3.2 TP1.1Net-(R1-Pad1) R1.1 TP3.1Net-(R1-Pad2) R1.2 R3.1 U1.IN+Net-(R2-Pad1) R2.1 TP2.1Net-(R2-Pad2) R2.2 R4.1 U1.IN-Net-(R4-Pad2) R4.2 TP4.1 U1.OUTEvery check that ran, and every one left undecided.
2 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 10 component 20 connection 2 constraint 1 evidence 3 interface 16 pin 16 port 69 totalsnapshot sha256:d53408bb388f09984f7a3dd043386ff6cd40bcfcbd18a6f6b7f39af636526968All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/differential_amplifiers/subtractor/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/differential_amplifiers/subtractor/subtractor.py