Skip to content
copperhead.sh
Get started

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 = Eref
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/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, 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.

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.

out/simulation.txt:

RunMeasuredClaimed
chopper, E_O1.0183 V1.0183 V (e_out) ± 10 ppm, holds
chopper, cell current1e-18 A0 (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.

Terminal window
fang check examples/ti_opamp_handbook/references/buffer_variation/buffer_variation.py
python examples/regenerate.py ti_opamp_handbook/references/buffer_variation # needs ngspice
examples/ti_opamp_handbook/references/buffer_variation/buffer_variation.py
"""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, and
the + input is on ground. The loop holds the - input at ground, so the output
stands one cell voltage above it, and the only current through the cell is
what the - input draws. The figure names the op amp, a TLC265x, a
chopper-stabilized part, and the text's point is its low drift: whatever
offset 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 drawing
does 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 the
1 uV offset a chopper-stabilized part is specified to (`amp`). A second run
gives the same circuit a general-purpose part's 5 mV offset, to show what the
chopper buys.
"""
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 A, Parameter, System, V, require, uV
from fang.rationale import Chooses, Cites
from 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 parts, then the nets and the pads on them.

out/netlist.txt
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.

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

What the elaborated graph contains, by entity kind.

out/graph.txt
1 block
5 component
10 connection
2 constraint
2 decision
1 evidence
3 interface
8 pin
8 port
40 total
snapshot sha256:da447c3c5a68c37e20e7e375fff6f37d311e22a5e1954b92f0a7bb91aa5ab370

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

Terminal window
fang build examples/ti_opamp_handbook/references/buffer_variation/buffer_variation.py