Examples / TI op amp handbook / Differentiators
Differentiator with stop
SBOA092B page 61, With "Stop": the differentiator with a 1 kΩ RI in series with its 0.1 µF CI, and 100 kΩ RO from the output back to the summing point.
E_O / E_I = -j 2π f R_O C_I / (1 + j 2π f R_I C_I)The 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/differentiator_with_stop.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”Below f_high = 1/(2π R_I C_I) the circuit differentiates, its gain passing
unity at f_low = 1/(2π R_O C_I). Above f_high R_I outweighs C_I and the
gain stops rising at a_flat = R_O/R_I = 100: the “stop”. Each is a parameter
held to the parts:
require(within(self.f_high, corner(r_i, c_i), 0.00001))require(within(self.f_low, corner(r_o, c_i), 0.00001))require(equals(self.a_flat, over(r_o, r_i)))The figure gives every value, so nothing was chosen.
Where the handbook is off
Section titled “Where the handbook is off”The page prints the high frequency cutoff as 0.6 kHz and the low frequency
cutoff as 16 kHz. With its own parts, 1/(2π × 1 kΩ × 0.1 µF) is 1.59 kHz and
1/(2π × 100 kΩ × 0.1 µF) is 15.9 Hz. The program holds the computed values,
figure quotes the printed ones, and the simulation agrees with the computed
ones. “Low frequency cutoff” is also a loose name: 15.9 Hz is where the
derivative’s gain passes 1, not where anything is cut.
What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the deck under
out/spice/:
Measured in response | Measured | Claimed |
|---|---|---|
| unity-gain frequency | 15.92 Hz | 15.92 Hz (f_low), holds; printed 16 kHz |
| gain at 100 Hz | 6.271 | 6.271, holds |
| 3 dB corner | 1.567 kHz | 1.592 kHz (f_high) ± 2%, holds; printed 0.6 kHz |
| gain at 1.59 kHz | 71.27 | 70.71 (a_corner) ± 1%, holds |
| top of the plateau | 99.97 | 100 (a_flat) ± 0.5%, holds |
| where the op amp rolls it off | 100.6 kHz | not a claim |
The corner claims are looser than the rest, and the notes say why. At 1.59 kHz the loop gain is only about 88, and with the op amp’s 90° lag the finite gain lifts the gain 0.8% above the ideal 70.71 instead of lowering it, which moves the 3 dB crossing 1.6% lower. The op amp, closed for a noise gain of 101, then rolls the plateau off near 100 kHz. Unlike the plain differentiator, nothing peaks: the stop keeps the rising gain from reaching the op amp’s roll-off.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/differentiators/differentiator_with_stop/differentiator_with_stop.pypython examples/regenerate.py ti_opamp_handbook/differentiators/differentiator_with_stop # needs ngspiceThe whole program
Section titled “The whole program”"""The differentiator with "stop", SBOA092B page 61.Show 16 more lines
E_O / E_I = -j 2 pi f R_O C_I / (1 + j 2 pi f R_I C_I)
The plain differentiator above it on the page, with a 1 kOhm R_I in serieswith C_I. Below 1/(2 pi R_I C_I) C_I's reactance dominates R_I and thecircuit differentiates, its gain 2 pi f R_O C_I passing unity at1/(2 pi R_O C_I). Above it R_I dominates, and the circuit becomes an invertingamplifier of gain R_O/R_I = 100: the "stop" that keeps the gain from risinginto the op amp's roll-off, which is what made the plain circuit ring.
The page prints the two corners as 0.6 kHz and 16 kHz. Neither is what its ownformula gives for its own parts: 1/(2 pi 1k 0.1u) is 1.59 kHz and1/(2 pi 100k 0.1u) is 15.9 Hz. The program holds the values the formulas giveand the bench measures them; the printed numbers are quoted in `figure` andthe discrepancy is stated beside each claim."""
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 decimal import Decimal
from fang.lang import Hz, Parameter, System, kHz, kOhm, require, uFfrom fang.parts import Capacitor, Resistorfrom fang.rationale import Citesfrom fang.simulation import ACSweep
from handbook import ( Bench, Claim, Ground, OpAmp, Run, Terminal, corner, equals, over, ratio, within,)
class DifferentiatorWithStop(System): """E_I through R_I and C_I in series into the summing point, R_O back."""
figure = Cites( "Input resistor sets high frequency cutoff. High frequency cutoff: " "F_O = 1/(2 pi R_I C_I) = 0.6 kHz. Low frequency cutoff: " "F_I = 1/(2 pi R_O C_I) = 16 kHz", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 61, With \"Stop\"", )
f_high = Parameter( "Hz", default=Decimal("1.59155") * kHz, description="1/(2 pi R_I C_I): where the derivative stops and the gain flattens", ) f_low = Parameter( "Hz", default=Decimal("15.9155") * Hz, description="1/(2 pi R_O C_I): where the derivative's gain passes unity", ) a_flat = Parameter("1", default=100 * ratio, description="R_O / R_I, above f_high") a_corner = Parameter( "1", default=Decimal("70.7107") * ratio, description="R_O / R_I / sqrt(2): the gain at f_high", )
e_in = Terminal() common = Terminal() e_out = Terminal() r_in = Resistor(resistance=1 * kOhm) c_in = Capacitor(capacitance=Decimal("0.1") * uF) r_out = Resistor(resistance=100 * kOhm) amp = OpAmp() ground = Ground()
def architecture(self): self.e_in.probe >> self.r_in.p1 self.r_in.p2 >> self.c_in.p1 self.c_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 self.common.probe >> self.ground.node
def constraints(self): r_i, c_i, r_o = self.r_in.resistance, self.c_in.capacitance, self.r_out.resistance require(within(self.f_high, corner(r_i, c_i), 0.00001)) require(within(self.f_low, corner(r_o, c_i), 0.00001)) require(equals(self.a_flat, over(r_o, r_i))) # At f_high, R_I and C_I's reactance are equal, and |R_I + 1/(j w C_I)| # is sqrt(2) R_I, so the gain is the flat gain over sqrt(2). require(within(over(self.a_flat, self.a_corner), Decimal("1.41421356") * ratio, 0.000001))
BENCH = Bench( page=61, title="Differentiators, With \"Stop\"", runs=[ Run( "response", ACSweep(points=200, start="1", stop="1meg"), drive={"e_in": "DC 0 AC 1"}, measure={ "f_low": "when vm({e_out.1})=1", "gain_100": "find vm({e_out.1}) at=100", "f_high": "when vm({e_out.1})=70.7107", "gain_f_high": "find vm({e_out.1}) at=1591.55", "gain_peak": "max vm({e_out.1})", "f_3db_op_amp": "when vm({e_out.1})=70.7107 fall=1", }, claims=[ Claim("f_low", "f_low", within=0.001, unit="Hz", note="The handbook prints 16 kHz; 1/(2 pi R_O C_I) is 15.9 Hz."), Claim("gain_100", 6.2707, within=0.001, note=( "100 Hz: 2 pi f R_O C_I = 6.283, less the first touch of " "R_I, 1/sqrt(1 + (f/f_high)^2)" )), Claim("f_high", "f_high", within=0.02, unit="Hz", note=( "The handbook prints 0.6 kHz; 1/(2 pi R_I C_I) is 1.59 " "kHz. 2%: the gain at the corner comes out 0.8% high " "(below), and on a slope of half a decade per decade " "that moves the 3 dB crossing about 1.6% lower." )), Claim("gain_f_high", "a_corner", within=0.01, note=( "R_O/R_I over sqrt(2), 3 dB down at the corner. 1%: the " "loop gain there is only about 88, and with the op amp's " "90 degree lag the finite gain lifts |E_O/E_I| by 0.8% " "rather than lowering it." )), Claim("gain_peak", "a_flat", within=0.005, note=( "The top of the plateau, R_O/R_I. The op amp, closed for " "a noise gain of 101, rolls off near 100 kHz, only 60 " "times above f_high, so the plateau is a rounded top " "rather than a flat one." )), ], units={"f_3db_op_amp": "Hz"}, ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
C1 0.1 uF -GND1 Ground -R1 1 kOhm -R2 100 kOhm -TP1 Terminal -TP2 Terminal -TP3 Terminal -U1 OpAmp -Net-(C1-Pad1) C1.1 R1.2Net-(C1-Pad2) C1.2 R2.1 U1.IN-Net-(GND1-Pad1) GND1.1 TP1.1 U1.IN+Net-(R1-Pad1) R1.1 TP2.1Net-(R2-Pad2) R2.2 TP3.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 8 component 16 connection 4 constraint 1 evidence 3 interface 13 pin 13 port 59 totalsnapshot sha256:20a7ce65f1f45b9b2f57d471cda4483ada5daea4b6a396a123fed7368428262eAll of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/differentiators/differentiator_with_stop/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/differentiators/differentiator_with_stop/differentiator_with_stop.py