Examples / TI op amp handbook / Differential amplifiers
Differential input output
SBOA092B page 75, Differential Input-Output: the balanced output amplifier of page 69 with values. RI = 1 kΩ and RO = 10 kΩ in both legs; E1 into the inverting input with RO to the top output, E2 into the non-inverting input with RO to the bottom output. The page says only "for use in driving floating loads. Input may be floating source." The gain is page 69's:
E_O = (R_O / R_I)(E2 - E1) = 10 (E2 - E1)The 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/differential_input_output.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 resistors are the figure’s. The program cites page 69 for the gain
(gain_rule) and requires the two legs to match and a_d = R_O / R_I = 10.
The page’s point is a load across the two outputs with no ground on it. The
figure draws none, so the program records the one the bench adds (load):
1 kΩ between the output terminals, in one run. The bench’s outputs are ideal,
so the load shows only that nothing needs a ground, not how a real part’s
outputs would sag.
Where the two outputs sit together is set inside the op amp (page 69 says so), and the model holds it at ground; the bottom output is reported, not claimed.
What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the decks under
out/spice/:
| Run | Measured | Claimed |
|---|---|---|
difference, E1 = 0.1 V, E2 = 0.4 V: gain | 10 | 10 (a_d), holds |
difference: bottom output | -1.5 V | not a claim |
floating_load, 1 kΩ across the outputs: E_O | 3 V | 3 V, holds |
floating_load: load current | 3 mA | not a claim |
common_mode, E1 = E2 = 1 V: E_O | 2.3 fV | 0 V ± 100 µV, holds |
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/differential_amplifiers/differential_input_output/differential_input_output.pypython examples/regenerate.py ti_opamp_handbook/differential_amplifiers/differential_input_output # needs ngspiceThe whole program
Section titled “The whole program”"""The differential input-output amplifier, SBOA092B page 75.Show 15 more lines
E_O = (R_O / R_I)(E2 - E1) = 10 (E2 - E1)
The balanced output amplifier of page 69 with values: R_I = 1 kOhm andR_O = 10 kOhm in both legs. E1 goes through R_I into the inverting inputwith R_O to the top output, E2 through R_I into the non-inverting input withR_O to the bottom output. The page prints no formula, only "for use indriving floating loads"; the gain is page 69's, with these values 10.
The output is the difference between the two output terminals, and neither ofthem is ground, so a load goes across them. The figure draws none, so `load`records the one the bench puts there for one run: 1 kOhm, across the outputsand touching nothing else. Where the outputs sit together is set inside the opamp, as page 69 says, and the model holds that level at ground."""
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 Chooses, Citesfrom fang.simulation import OperatingPoint
from handbook import ( Bench, Claim, DifferentialOpAmp, Ground, Run, Terminal, equals, over, ratio,)
class DifferentialInputOutput(System): """Two inverting legs around one op amp with two outputs, R_I 1k and R_O 10k."""
figure = Cites( "For use in driving floating loads. Input may be floating source", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 75, Differential Input-Output", )
gain_rule = Cites( "E_O = (R_O/R_I)(E2 - E1)", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 69, The Differential (Balanced) Output Amplifier", )
load = Chooses( "What load does the bench put across the outputs?", selected="1 kOhm, across the two output terminals and nowhere near ground, in one run", alternatives=[ { "option": "no load at all", "reason": ( "the page's point is a floating load, and a run with one " "shows the difference does not need a ground to be measured" ), }, { "option": "a load from each output to ground", "reason": "that is two grounded loads, not the floating one the page names", }, ], rationale=( "the figure draws no load", "the bench's outputs are ideal, so the load shows only that nothing " "needs a ground, not how a real part's outputs sag under it", ), )
a_d = Parameter("1", default=10 * ratio, description="E_O / (E2 - E1)")
e1 = Terminal() e2 = Terminal() e_o = Terminal() e_p = Terminal() common = Terminal() r_in_top = Resistor(resistance=1 * kOhm) r_out_top = Resistor(resistance=10 * kOhm) r_in_bottom = Resistor(resistance=1 * kOhm) r_out_bottom = Resistor(resistance=10 * kOhm) amp = DifferentialOpAmp() ground = Ground() def architecture(self): # The top leg: E1 into the - input, R_O to the output that falls when # that input rises. 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_o.probe
# The bottom leg: E2 into the + input, R_O to the other output. 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.amp.output_minus.signal self.amp.output_minus.signal >> self.e_p.probe
# Nothing in the figure touches ground but the grounded terminal # between E1 and E2: the op amp's common level is its own. self.common.probe >> self.ground.node
def constraints(self): require(equals(self.r_in_top.resistance, self.r_in_bottom.resistance)) require(equals(self.r_out_top.resistance, self.r_out_bottom.resistance)) require(equals(self.a_d, over(self.r_out_top.resistance, self.r_in_top.resistance)))
BENCH = Bench( page=75, title="Differential Input-Output", runs=[ Run( "difference", OperatingPoint(), drive={"e1": "DC 0.1", "e2": "DC 0.4"}, measure={ "gain": "(v({e_o.1}) - v({e_p.1})) / (v({e2.1}) - v({e1.1}))", "e_p": "v({e_p.1})", }, claims=[Claim("gain", "a_d", within=0.001)], units={"e_p": "V"}, note=( "E_O is the top output less the bottom one. Where the bottom " "output sits is set inside the op amp; this model centres the " "pair on ground, so it is -E_O/2 here and not a handbook claim." ), ), Run( "floating_load", OperatingPoint(), drive={"e1": "DC 0.1", "e2": "DC 0.4"}, measure={ "e_o": "v({e_o.1}) - v({e_p.1})", "i_load": "(v({e_o.1}) - v({e_p.1})) / 1000", }, cards=["RLOAD {e_o.1} {e_p.1} 1k"], claims=[ Claim("e_o", 3.0, within=0.001, unit="V", note="10 x 0.3 V, with 1 kOhm across the outputs and no ground on it"), ], units={"i_load": "A"}, note=( "The load (see `load`) is a card, since the figure draws none. " "The model's outputs are ideal, so the load cannot pull them; " "a real part's output resistance would." ), ), Run( "common_mode", OperatingPoint(), drive={"e1": "DC 1", "e2": "DC 1"}, measure={"e_o": "v({e_o.1}) - v({e_p.1})"}, claims=[Claim("e_o", 0, within=1e-4, absolute=True, unit="V", note="1 V on both inputs gives no difference out")], ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
GND1 Ground -R1 1 kOhm -R2 1 kOhm -R3 10 kOhm -R4 10 kOhm -TP1 Terminal -TP2 Terminal -TP3 Terminal -TP4 Terminal -TP5 Terminal -U1 DifferentialOpAmp -Net-(GND1-Pad1) GND1.1 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-(R3-Pad2) R3.2 TP5.1 U1.OUT-Net-(R4-Pad2) R4.2 TP4.1 U1.OUT+Every check that ran, and every one left undecided.
3 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 11 component 22 connection 3 constraint 1 decision 2 evidence 3 interface 18 pin 18 port 79 totalsnapshot sha256:b603d3da4785c8569fde1930a2dd5051f6cc5c2571892541af41eba98ec1a384All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/differential_amplifiers/differential_input_output/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/differential_amplifiers/differential_input_output/differential_input_output.py