Examples / TI op amp handbook / Buffers
Voltage follower
SBOA092B page 49, The Voltage Follower: EI on the non-inverting input, the output wired straight back to the inverting input.
E_O = 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/voltage_follower.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”There are no resistors, so nothing is chosen. The whole output is fed back,
so the gain is 1, held as the parameter a_v = 1. The figure’s lower pair of
terminals are drawn as two terminals on the grounded return.
What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the three decks under
out/spice/:
| Run | Measured | Claimed |
|---|---|---|
gain, operating point, E_I = 1 V | 1 | 1 (a_v), holds |
large_signal, operating point, E_I = 10 V | 1 | 1 (a_v), holds |
bandwidth, gain at 1 kHz | 1 | 1 (a_v), holds |
bandwidth, -3 dB point | 9.976 MHz | not a claim |
With 120 dB of open-loop gain the follower’s gain is A / (1 + A), 1 less a part in 10^6. Its noise gain is 1, so its bandwidth is the op amp’s whole 10 MHz gain-bandwidth.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/buffers/voltage_follower/voltage_follower.pypython examples/regenerate.py ti_opamp_handbook/buffers/voltage_follower # needs ngspiceThe whole program
Section titled “The whole program”"""The voltage follower, SBOA092B page 49.Show 14 more lines
E_O = E_I
The output is wired straight back to the inverting input and the signal goesin at the non-inverting one, so the loop holds the two inputs together and theoutput repeats the input. The handbook reaches it by letting the open-loopgain go to infinity; with a finite gain A the follower gives A / (1 + A),which for the bench's 120 dB op amp is 1 less a part in 10^6.
There are no resistors and so nothing to choose. The bench drives E_I at 1 Vand at 10 V (a follower's gain is 1 everywhere inside the swing), and sweepsthe frequency to show where the claim stops: a follower's noise gain is 1, soits bandwidth is the op amp's gain-bandwidth."""
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, requirefrom fang.rationale import Citesfrom fang.simulation import ACSweep, OperatingPoint
from handbook import Bench, Claim, Ground, OpAmp, Run, Terminal, equals, ratio
class VoltageFollower(System): """E_I on the + input, the output wired back to the - input."""
figure = Cites( "E_O = E_I", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 49, The Voltage Follower", )
a_v = Parameter("1", default=1 * ratio, description="E_O / E_I")
e_in = Terminal() e_out = Terminal() # The figure's lower pair of terminals, both on the grounded return. e_in_return = Terminal() e_out_return = Terminal() amp = OpAmp() ground = Ground()
def architecture(self): self.e_in.probe >> self.amp.non_inverting.signal self.amp.output.signal >> self.amp.inverting.signal self.amp.output.signal >> self.e_out.probe # The figure's lower wire: the return both sides share. self.e_in_return.probe >> self.ground.node self.e_out_return.probe >> self.ground.node
def constraints(self): # No resistor sets the gain: the whole output is fed back, so the # fraction returned is 1 and the gain is its reciprocal. require(equals(self.a_v, 1 * ratio))
BENCH = Bench( page=49, title="The Voltage Follower", runs=[ Run( "gain", OperatingPoint(), drive={"e_in": "DC 1"}, measure={"gain": "v({e_out.1}) / v({e_in.1})"}, claims=[Claim("gain", "a_v", within=0.001)], ), Run( "large_signal", OperatingPoint(), drive={"e_in": "DC 10"}, measure={"gain": "v({e_out.1}) / v({e_in.1})"}, claims=[Claim("gain", "a_v", within=0.001)], note="10 V in, still inside the 13.5 V swing: the gain is the same.", ), Run( "bandwidth", ACSweep(points=20, start="10", stop="100meg"), drive={"e_in": "DC 0 AC 1"}, measure={ "gain_1k": "find vm({e_out.1}) at=1k", "f_3db": "when vdb({e_out.1})=-3 fall=1", }, claims=[Claim("gain_1k", "a_v", within=0.001)], units={"f_3db": "Hz"}, note=( "The -3 dB point is not a handbook claim: a follower's noise " "gain is 1, so it is the op amp's own 10 MHz gain-bandwidth." ), ), ],)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 -TP3 Terminal -TP4 Terminal -U1 OpAmp -Net-(GND1-Pad1) GND1.1 TP2.1 TP4.1Net-(TP1-Pad1) TP1.1 U1.IN+Net-(TP3-Pad1) TP3.1 U1.IN- U1.OUTEvery check that ran, and every one left undecided.
1 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 6 component 10 connection 1 constraint 1 evidence 3 interface 8 pin 8 port 38 totalsnapshot sha256:59c2e144d146b6c30cb82d608069d419104952da882738a3049c58a6b253e452All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/buffers/voltage_follower/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/buffers/voltage_follower/voltage_follower.py