Examples / TI op amp handbook / References
Buffer variation
SBOA092B page 52, Buffer Variation: the cell Eref in the feedback path, between the output and the inverting input, with the non-inverting input on ground. The op amp is a TLC265x, a chopper-stabilized part.
E_O = ErefThe 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/buffer_variation.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 loop holds the inverting input at ground, so the output stands one cell
voltage above it, and the cell only carries what that input draws. Two things
are open and recorded as choices: cell, a Weston cell at 1.0183 V with its
- terminal on the output (the output terminal is labelled Eref, not -Eref),
and
amp_model, a 1 µV offset, what a TLC2652A is specified to. Whatever offset the op amp has lands on E_O undivided, which is why the figure names a chopper.
What the simulation found
Section titled “What the simulation found”| Run | Measured | Claimed |
|---|---|---|
chopper, E_O | 1.0183 V | 1.0183 V (e_out) ± 10 ppm, holds |
chopper, cell current | 1e-18 A | 0 (i_cell) ± 1 nA, holds |
general_part, E_O - Eref with a 5 mV offset | -5.001 mV | -5 mV, holds |
The second run is not a handbook claim. It gives the same circuit a general-purpose part’s 5 mV offset, and the output is off by all of it, 0.5% of Eref. The chopper’s 1 µV is 1 ppm.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/references/buffer_variation/buffer_variation.pypython examples/regenerate.py ti_opamp_handbook/references/buffer_variation # needs ngspiceThe whole program
Section titled “The whole program”"""The buffer variation, SBOA092B page 52.Show 17 more lines
E_O = Eref
The cell sits in the feedback path, between the output and the - input, andthe + input is on ground. The loop holds the - input at ground, so the outputstands one cell voltage above it, and the only current through the cell iswhat the - input draws. The figure names the op amp, a TLC265x, achopper-stabilized part, and the text's point is its low drift: whateveroffset the op amp has adds straight to E_O, since nothing divides it.
Two things are open. The cell has no value, and its polarity in the drawingdoes not say which plate is which, so `cell` records a Weston cell with its +terminal on the output, for E_O = +1.0183 V. The op amp is modelled with the1 uV offset a chopper-stabilized part is specified to (`amp`). A second rungives the same circuit a general-purpose part's 5 mV offset, to show what thechopper buys."""
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 A, Parameter, System, V, require, uVfrom fang.rationale import Chooses, Citesfrom fang.simulation import OperatingPoint
from handbook import Bench, Cell, Claim, Ground, OpAmp, Run, Terminal, equals
class BufferVariation(System): """The cell from the - input to the output, the + input on ground."""
figure = Cites( "Low current drift of chopper - stabilized amplifiers improves stability " "and cell protection.", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 52, Buffer Variation", )
cell = Chooses( "What is Eref, and which way round is it?", selected=( "a saturated Weston cell, 1.0183 V, + terminal on the output, so " "E_O = +Eref" ), alternatives=[ { "option": "+ terminal on the - input", "reason": ( "gives E_O = -Eref; the figure labels the output Eref, " "not -Eref" ), }, ], rationale=( "the figure gives the cell no value and its plates are not marked", "the output terminal is labelled Eref", ), )
amp_model = Chooses( "What offset does the TLC265x have?", selected="1 uV, the maximum a chopper-stabilized TLC2652A is specified to", alternatives=[ { "option": "the bench default of 0 V", "reason": ( "the figure names a chopper-stabilized part because its " "offset is small, not zero; E_O carries all of it" ), }, ], rationale=("the figure names the TLC265x", "offset adds to E_O undivided"), )
e_out = Parameter("V", default=1.0183 * V, description="the output, which is Eref") i_cell = Parameter("A", default=0 * A, description="what flows through the cell")
e_ref = Cell(voltage=1.0183 * V) amp = OpAmp(input_offset=1 * uV) out = Terminal() out_return = Terminal() ground = Ground()
def architecture(self): self.e_ref.p2 >> self.amp.inverting.signal self.e_ref.p1 >> self.amp.output.signal self.amp.output.signal >> self.out.probe self.amp.non_inverting.signal >> self.ground.node self.out_return.probe >> self.ground.node
def constraints(self): # The - input sits at ground, so the output is one cell above it. require(equals(self.e_out, self.e_ref.voltage)) # The cell's far end is the - input, which draws nothing. require(equals(self.i_cell, 0 * A))
BENCH = Bench( page=52, title="Buffer Variation", runs=[ Run( "chopper", OperatingPoint(), measure={"e_out": "v({out.1})", "i_cell": "i(v1)"}, claims=[ Claim( "e_out", "e_out", within=1e-5, unit="V", note="Held to 10 ppm: the 1 uV offset is 1 ppm of Eref.", ), Claim( "i_cell", "i_cell", within=1e-9, absolute=True, unit="A", note=( "Held to 1 nA, absolute. The model's - input draws no " "bias current; a TLC2652 draws a few picoamps." ), ), ], units={"e_out": "V", "i_cell": "A"}, ), Run( "general_part", OperatingPoint(), settings={"amp": {"input_offset": 0.005}}, measure={"error": "v({out.1}) - 1.0183"}, claims=[ Claim( "error", -0.005, within=1e-3, unit="V", note=( "Not a handbook claim: with a 5 mV offset (the model's " "offset is in series with the + input) the output " "is off by the whole 5 mV, 0.5% of Eref, because " "nothing in the loop divides it." ), ), ], units={"error": "V"}, ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
GND1 Ground -TP1 Terminal -TP2 Terminal -U1 OpAmp -V1 1.0183 V -Net-(GND1-Pad1) GND1.1 TP2.1 U1.IN+Net-(TP1-Pad1) TP1.1 U1.OUT V1.+Net-(U1-PadIN-) U1.IN- V1.-Every check that ran, and every one left undecided.
2 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 5 component 10 connection 2 constraint 2 decision 1 evidence 3 interface 8 pin 8 port 40 totalsnapshot sha256:da447c3c5a68c37e20e7e375fff6f37d311e22a5e1954b92f0a7bb91aa5ab370All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/references/buffer_variation/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/references/buffer_variation/buffer_variation.py