Examples / TI op amp handbook / DC amplifiers
Chopper stabilized
SBOA092B page 70, Chopper Stabilized: the simple inverting amplifier above it (RI = 1 kΩ, RO = 100 kΩ, + input on ground) with the op amp named, a TLC265x chopper-stabilized part. The page claims only "improved drift and stability".
E_O = -(R_O / R_I) E_I = -100 E_IThe 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/chopper_stabilized.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”In a DC amplifier, drift shows up as the input offset voltage multiplied by
the noise gain, 1 + R_O/R_I = 101 (not the signal gain of 100). The page gives
no offsets, so the program chooses two (offsets): 1 µV for the TLC2652, its
datasheet maximum at 25 °C, set on the op amp part; and 2 mV for a
general-purpose op amp in the same socket, which stands for the class rather
than a named part. The model carries an offset at one temperature and no
temperature coefficient, so the comparison is of offsets, and drift is those
offsets moving.
The constraints hold a_v = -R_O/R_I, noise_gain = 1 + R_O/R_I, and each
output error to its offset times the noise gain.
What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the decks under
out/spice/:
| Run | Measured | Claimed |
|---|---|---|
gain, E_I = 0.1 V | -99.99 | -100 (a_v) ± 0.1%, holds |
offset_chopper, E_I = 0, 1 µV offset: abs(E_O) | 101 µV | 101 µV (error_chopper), holds |
offset_general, E_I = 0, 2 mV offset: abs(E_O) | 202 mV | 202 mV (error_general), holds |
The general-purpose part’s error is 2000 times the chopper’s, and at a gain of 100 it is already 2% of full scale for a 0.1 V input.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/dc_amplifiers/chopper_stabilized/chopper_stabilized.pypython examples/regenerate.py ti_opamp_handbook/dc_amplifiers/chopper_stabilized # needs ngspiceThe whole program
Section titled “The whole program”"""The chopper-stabilized inverting amplifier, SBOA092B page 70.Show 14 more lines
E_O = -(R_O / R_I) E_I = -100 E_I "Improved drift and stability."
The same circuit as the simple inverting amplifier above it on the page,R_I = 1 kOhm and R_O = 100 kOhm, with the op amp named: a TLC265x, achopper-stabilized part. The page's only claim for it is improved drift, anda DC amplifier's drift shows up as its input offset voltage multiplied by thenoise gain, 1 + R_O/R_I = 101.
The page gives no offsets, so `offsets` records the two used: 1 uV for theTLC2652 (its datasheet maximum at 25 C) and 2 mV for a general-purpose op ampin the same socket. The bench runs the gain once, then E_I = 0 with eachoffset, and claims the output error is 101 times the offset in both."""
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, mV, uV, requirefrom fang.parts import Resistorfrom fang.rationale import Chooses, Citesfrom fang.simulation import OperatingPoint
from handbook import ( Bench, Claim, Ground, OpAmp, Run, Terminal, equals, negative, over, product, ratio, total,)
class ChopperStabilized(System): """E_I through R_I into a chopper-stabilized op amp's summing point, R_O back."""
figure = Cites( "E_O = -(R_O/R_I) E_I (R_I 1 kOhm, R_O 100 kOhm, TLC265x). " "Improved drift and stability", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 70, Chopper Stabilized", )
offsets = Chooses( "What input offsets does the comparison use?", selected=( "1 uV for the TLC265x, and 2 mV for a general-purpose op amp put in " "its place for one run" ), alternatives=[ { "option": "the model's default of no offset", "reason": "then both parts give 0 V out and the page's claim shows nothing", }, { "option": "a drift in uV per degree, swept over temperature", "reason": ( "the model has no temperature dependence; an offset at one " "temperature is what it can carry, and drift is that offset " "moving" ), }, ], rationale=( "1 uV is the TLC2652's maximum input offset at 25 C in its datasheet " "(typically about 0.5 uV)", "2 mV is the order of a general-purpose bipolar or JFET op amp's " "offset; it stands for the class, not a named part", ), )
a_v = Parameter("1", default=-100 * ratio, description="E_O / E_I") noise_gain = Parameter( "1", default=101 * ratio, description="1 + R_O / R_I: what an input offset is multiplied by" ) vos_general = Parameter( "V", default=2 * mV, description="a general-purpose op amp's offset, for comparison" ) error_chopper = Parameter( "V", default=101 * uV, description="|E_O| at E_I = 0 with the TLC265x" ) error_general = Parameter( "V", default=202 * mV, description="|E_O| at E_I = 0 with the general-purpose part" )
e_in = Terminal() e_out = Terminal() r_in = Resistor(resistance=1 * kOhm) r_out = Resistor(resistance=100 * kOhm) amp = OpAmp(input_offset=1 * uV) ground = Ground()
def architecture(self): self.e_in.probe >> self.r_in.p1 self.r_in.p2 >> self.amp.inverting.signal self.amp.inverting.signal >> self.r_out.p1 self.r_out.p2 >> self.amp.output.signal self.amp.output.signal >> self.e_out.probe self.amp.non_inverting.signal >> self.ground.node
def constraints(self): require(equals(self.a_v, negative(over(self.r_out.resistance, self.r_in.resistance)))) require( equals( self.noise_gain, total(1 * ratio, over(self.r_out.resistance, self.r_in.resistance)), ) ) # An offset at either input is amplified by the noise gain, not the # signal gain: 101, not 100. require(equals(self.error_chopper, product(self.amp.input_offset, self.noise_gain))) require(equals(self.error_general, product(self.vos_general, self.noise_gain)))
BENCH = Bench( page=70, title="Chopper Stabilized", runs=[ Run( "gain", OperatingPoint(), drive={"e_in": "DC 0.1"}, measure={"gain": "v({e_out.1}) / v({e_in.1})"}, claims=[ Claim("gain", "a_v", within=0.001, note="the 1 uV offset moves E_O by 101 uV of its -10 V"), ], ), Run( "offset_chopper", OperatingPoint(), drive={"e_in": "DC 0"}, measure={"error": "abs(v({e_out.1}))"}, claims=[Claim("error", "error_chopper", within=0.001, unit="V")], note="E_I at 0 V: everything at the output is the TLC265x's 1 uV times 101.", ), Run( "offset_general", OperatingPoint(), drive={"e_in": "DC 0"}, settings={"amp": {"input_offset": 0.002}}, measure={"error": "abs(v({e_out.1}))"}, claims=[Claim("error", "error_general", within=0.001, unit="V")], note=( "The same circuit with a 2 mV general-purpose op amp in the " "socket: 2000 times the error." ), ), ],)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 100 kOhm -TP1 Terminal -TP2 Terminal -U1 OpAmp -Net-(GND1-Pad1) GND1.1 U1.IN+Net-(R1-Pad1) R1.1 TP1.1Net-(R1-Pad2) R1.2 R2.1 U1.IN-Net-(R2-Pad2) R2.2 TP2.1 U1.OUTEvery check that ran, and every one left undecided.
4 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 6 component 12 connection 4 constraint 1 decision 1 evidence 3 interface 10 pin 10 port 48 totalsnapshot sha256:25b388b1c6bf7dcb6f59d325eb7c983674249f30cd2f2b648dbeeb38b1d32eb4All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/dc_amplifiers/chopper_stabilized/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/dc_amplifiers/chopper_stabilized/chopper_stabilized.py