Examples / TI op amp handbook / References
Presettable voltage source
SBOA092B page 52, Presettable Voltage Source: the cell Eref from the node RI and RO share to the inverting input, RO (a decade box) on to the output, RI to ground, and the non-inverting input on ground. The op amp is a TLC265x.
E_O = (R_I + R_O) / R_I x 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/presettable_voltage_source.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 inverting input sits at ground, so the shared node stands at +Eref and
R_I carries Eref / R_I, all of it from the output through R_O. The figure
sizes R_I at 1000 x Eref ohms, which makes that current 1 mA whatever the
cell is: each ohm on the decade box is a millivolt of E_O above Eref. The
handbook does not say so; the program records it as a calculation (scale)
and a constraint (i_set = 1 mA).
Recorded choices: cell, a Weston cell at 1.0183 V, so R_I = 1018.3 Ω;
dial, R_O at 8981.7 Ω for E_O = 10.000 V; and decade_box, the decade box
modelled as a potentiometer with its wiper on its far end, so a run can turn
the dial. The op amp has a chopper’s 1 µV offset.
What the simulation found
Section titled “What the simulation found”| Run | R_O | Measured | Claimed |
|---|---|---|---|
ten_volts, E_O | 8981.7 Ω | 10.000 V | 10 V (e_out), holds |
ten_volts, current in R_I | 1 mA | 1 mA (i_set), holds | |
ten_volts, cell current | 1e-17 A | 0 (i_cell) ± 1 nA, holds | |
five_volts, E_O | 3981.7 Ω | 5.000 V | 5 V, holds |
dial_at_zero, E_O | 0 Ω | 1.0183 V | Eref, holds |
The outputs are held to 100 ppm: a noise gain near 10 costs 10 ppm of loop-gain error and the offset another 1 ppm. The lowest output the circuit gives is Eref itself.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/references/presettable_voltage_source/presettable_voltage_source.pypython examples/regenerate.py ti_opamp_handbook/references/presettable_voltage_source # needs ngspiceThe whole program
Section titled “The whole program”"""The presettable voltage source, SBOA092B page 52.Show 20 more lines
E_O = (R_I + R_O) / R_I x Eref
The cell's - terminal is on the - input, which the loop holds at ground, soits + terminal, the node R_I and R_O share, stands at +Eref. R_I then carriesEref / R_I to ground, all of it from the output through R_O, and the outputsettles at Eref (1 + R_O / R_I). The cell carries only what the - input draws.
The figure sizes R_I as "1000 x Eref" ohms. That makes the current through it1 mA whatever the cell is, so the decade box reads directly: every ohm of R_Ois a millivolt of E_O above Eref. The program records that as a calculationof its own (`scale`), since the handbook does not say it.
Open: the cell's value, and R_O's dial. `cell` records a Weston cell,1.0183 V, so R_I is 1018.3 Ohm. `dial` sets R_O to 8981.7 Ohm, for 10.000 V.The decade box is drawn as a variable resistor, so the program models it as apotentiometer with its wiper on one end (`decade_box`), and a run can turn it:3981.7 Ohm for 5 V, and 0 Ohm for Eref itself. The op amp is the TLC265x thefigure names, with a chopper's 1 uV offset."""
import sysfrom decimal import Decimalfrom 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, Ohm, Parameter, System, V, mA, require, uVfrom fang.parts import Resistorfrom fang.rationale import Calculates, Chooses, Citesfrom fang.simulation import OperatingPoint
from handbook import ( Bench, Cell, Claim, Ground, OpAmp, Potentiometer, Run, Terminal, equals, over, product, ratio, total,)
class PresettableVoltageSource(System): """Eref from the R_I/R_O node to the - input; R_O on to the output, R_I to ground."""
figure = Cites( "E_O = (R_I + R_O) / R_I x Eref. Gives wide range of very stable " "reference voltages.", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 52, Presettable Voltage Source", )
cell = Chooses( "What is Eref?", selected="a saturated Weston cell, 1.0183 V, so R_I = 1000 x Eref = 1018.3 Ohm", alternatives=[ { "option": "a 1.2 V bandgap", "reason": "the page's other circuits are standard-cell circuits", }, ], rationale=("the figure labels the cell Eref and gives no value",), )
dial = Chooses( "Where is the decade box set?", selected="8981.7 Ohm, for E_O = 10.000 V", alternatives=[ { "option": "a round 9000 Ohm", "reason": "gives 10.0183 V; the dial exists to land on a round output", }, ], rationale=( "the figure draws a decade box and no setting", "10 V is the reference such a circuit is usually asked for", ), )
decade_box = Chooses( "How is a decade box drawn in parts?", selected="a potentiometer with its wiper tied to its far end, set to full travel", alternatives=[ { "option": "a fixed resistor", "reason": "a run could not turn the dial without editing the program", }, ], rationale=( "a rheostat is a potentiometer with its wiper on one end", "the bench can override a potentiometer's resistance for one run", ), )
scale = Calculates( "i_set = Eref / R_I, with R_I = 1000 x Eref", inputs=("e_ref", "r_in"), result=( "1 mA whatever the cell, so E_O = Eref + R_O x 1 mA: each ohm on " "the decade box is a millivolt above Eref" ), )
e_out = Parameter("V", default=10 * V, description="E_O") i_set = Parameter("A", default=1 * mA, description="the current through R_I and R_O") i_cell = Parameter("A", default=0 * A, description="what flows through the cell")
e_ref = Cell(voltage=Decimal("1.0183") * V) r_in = Resistor(resistance=Decimal("1018.3") * Ohm) r_out = Potentiometer(resistance=Decimal("8981.7") * Ohm, setting=1 * ratio) amp = OpAmp(input_offset=1 * uV) out = Terminal() out_return = Terminal() ground = Ground()
def architecture(self): # The node the cell's + terminal, R_I and R_O share. self.e_ref.p1 >> self.r_in.p1 self.r_in.p1 >> self.r_out.end_a self.e_ref.p2 >> self.amp.inverting.signal # The decade box: its wiper on its far end, so all of it is in circuit. self.r_out.wiper >> self.r_out.end_b self.r_out.end_b >> self.amp.output.signal self.amp.output.signal >> self.out.probe self.r_in.p2 >> self.ground.node self.amp.non_inverting.signal >> self.ground.node self.out_return.probe >> self.ground.node
def constraints(self): require( equals( self.e_out, product( over(total(self.r_in.resistance, self.r_out.resistance), self.r_in.resistance), self.e_ref.voltage, ), ) ) # The handbook's sizing rule, and what it buys. require(equals(self.r_in.resistance, product(self.e_ref.voltage, over(1000 * Ohm, 1 * V)))) require(equals(self.i_set, over(self.e_ref.voltage, self.r_in.resistance))) require(equals(self.i_cell, 0 * A))
BENCH = Bench( page=52, title="Presettable Voltage Source", runs=[ Run( "ten_volts", OperatingPoint(), measure={ "e_out": "v({out.1})", "i_set": "v({r_in.1}) / 1018.3", "i_cell": "i(v1)", }, claims=[ Claim( "e_out", "e_out", within=1e-4, unit="V", note=( "Held to 100 ppm: a noise gain near 10 costs 10 ppm " "of loop-gain error, and the 1 uV offset another 1 ppm." ), ), Claim("i_set", "i_set", within=1e-4, unit="A"), 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.", ), ], units={"e_out": "V", "i_set": "A", "i_cell": "A"}, ), Run( "five_volts", OperatingPoint(), settings={"r_out": {"resistance": 3981.7}}, measure={"e_out": "v({out.1})"}, claims=[ Claim( "e_out", 5.0, within=1e-4, unit="V", note="The dial at 3981.7 Ohm: 3981.7 mV above Eref.", ) ], units={"e_out": "V"}, ), Run( "dial_at_zero", OperatingPoint(), settings={"r_out": {"resistance": 0}}, measure={"e_out": "v({out.1})"}, claims=[ Claim( "e_out", 1.0183, within=1e-4, unit="V", note="The dial at zero: the lowest E_O the circuit gives is Eref.", ) ], units={"e_out": "V"}, ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
GND1 Ground -R1 1018.3 Ohm -RV1 Potentiometer -TP1 Terminal -TP2 Terminal -U1 OpAmp -V1 1.0183 V -Net-(GND1-Pad1) GND1.1 R1.2 TP2.1 U1.IN+Net-(R1-Pad1) R1.1 RV1.1 V1.+Net-(RV1-Pad2) RV1.2 RV1.3 TP1.1 U1.OUTNet-(U1-PadIN-) U1.IN- V1.-Every check that ran, and every one left undecided.
4 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 1 calculation 7 component 18 connection 4 constraint 3 decision 1 evidence 3 interface 13 pin 13 port 64 totalsnapshot sha256:8c24ff5f503fd7003f8f06734f6ecda5906e666a85c982f37852b57367dd2063All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/references/presettable_voltage_source/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/references/presettable_voltage_source/presettable_voltage_source.py