Examples / TI op amp handbook / Integrators
Differential integrator
SBOA092B page 59, Differential Integrator: E1 through RI (100 kΩ) to the - input with CO (1 µF) to the output, and E2 through a second RI to the + input with a second CO to ground. The figure names a TLC265x.
E_O = -1/(R_I C_O) ∫ (E1 - E2) dt = 10 ∫ (E2 - E1) dtThe 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/differential_integrator.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”One parameter, rate = 10 /s. Two constraints tie it to each side’s R_I C_O,
so the two sides have to match. The E2 side is an RC low-pass whose capacitor
integrates E2. The E1 side integrates about that voltage, so the output
integrates the difference. The figure draws no reset, so every run starts all
three capacitor nodes at zero with .ic (start).
What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the decks under out/spice/:
| Run | Measured | Claimed |
|---|---|---|
difference, E1 = 0.1 V, E2 = 0.3 V, slope over (E2 - E1) | 10 /s | 10 /s (rate), holds |
common_mode, E1 = E2 = 0.5 V, output at 1 s | 0 V | 0 V ± 1 mV, holds |
Where the handbook is off
Section titled “Where the handbook is off”The first line of the printed formula has “(E_I - E_2)”, where E_I should be E_1. The second line gives the sign the circuit has, and that is the line the program claims.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/integrators/differential_integrator/differential_integrator.pypython examples/regenerate.py ti_opamp_handbook/integrators/differential_integrator # needs ngspiceThe whole program
Section titled “The whole program”"""The differential integrator, SBOA092B page 59 (top).Show 15 more lines
E_O = -1/(R_I C_O) integral (E1 - E2) dt = 10 integral (E2 - E1) dt
E1 goes through R_I (100 kOhm) to the inverting input, with C_O (1 uF) fromthere to the output. E2 goes through a second R_I to the non-inverting input,with a second C_O from there to ground. The E2 side is an RC low-pass whosecapacitor voltage is (1/(R_I C_O)) integral E2 dt; the E1 side integratesabout that voltage, so the output is 10 integral (E2 - E1) dt.
The handbook's first line reads "(E_I - E_2)", a subscript slip for E_1; itssecond line has the sign the figure gives. The figure draws no reset, so thebench starts all three capacitor nodes at zero with `.ic`. The figure names aTLC265x, a chopper-stabilized part; the default op amp, with no offset, standsin for it."""
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, UnitLiteral, kOhm, require, uFfrom fang.parts import Capacitor, Resistorfrom fang.rationale import Chooses, Citesfrom fang.simulation import Transient
from handbook import ( Bench, Claim, Ground, OpAmp, Run, Terminal, equals, over, product, ratio,)
#: A rate: volts of output per second, for each volt of input.per_second = UnitLiteral("1/s")
#: Both capacitors and the output start at zero; the figure has no reset.START = ".ic v({amp.OUT})=0 v({amp.IN-})=0 v({amp.IN+})=0"
class DifferentialIntegrator(System): """E1 into an integrator's summing point, E2 into an RC on the non-inverting input."""
figure = Cites( "E_O = -1/(R_I C_O) integral (E_I - E_2) dt = 10 integral (E_2 - E_1) dt. " "Integrates difference between two signals.", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 59, Differential Integrator", )
start = Chooses( "Where does the output start, with no reset drawn?", selected="at zero: `.ic` on the output and both op amp inputs", alternatives=[ { "option": "add a reset switch", "reason": "the figure has none, and a reset for the E2 side would need a second one", }, ], rationale=("the starting charge on both capacitors is part of the answer, so it is stated",), )
rate = Parameter( "1/s", default=10 * per_second, description="dE_O/dt for each volt of E2 - E1: 1/(R_I C_O)", )
e1 = Terminal() e2 = Terminal() e_out = Terminal() r_minus = Resistor(resistance=100 * kOhm) c_feedback = Capacitor(capacitance=1 * uF) r_plus = Resistor(resistance=100 * kOhm) c_ground = Capacitor(capacitance=1 * uF) amp = OpAmp() ground = Ground()
def architecture(self): self.e1.probe >> self.r_minus.p1 self.r_minus.p2 >> self.amp.inverting.signal self.amp.inverting.signal >> self.c_feedback.p1 self.c_feedback.p2 >> self.amp.output.signal self.amp.output.signal >> self.e_out.probe
self.e2.probe >> self.r_plus.p1 self.r_plus.p2 >> self.amp.non_inverting.signal self.amp.non_inverting.signal >> self.c_ground.p1 self.c_ground.p2 >> self.ground.node
def constraints(self): # The two sides are matched: each has the same time constant. require( equals( self.rate, over(1 * ratio, product(self.r_minus.resistance, self.c_feedback.capacitance)), ) ) require( equals( self.rate, over(1 * ratio, product(self.r_plus.resistance, self.c_ground.capacitance)), ) )
BENCH = Bench( page=59, title="Differential Integrator", runs=[ Run( "difference", Transient(stop="1", step="1m"), drive={"e1": "DC 0.1", "e2": "DC 0.3"}, cards=[START], measure={ "e_early": "find v({e_out.1}) at=0.1", "e_late": "find v({e_out.1}) at=1", "rate_per_volt": "(e_late - e_early) / 0.9 / 0.2", }, claims=[Claim("rate_per_volt", "rate", within=0.001, unit="/s")], units={"e_early": "V", "e_late": "V"}, note=( "E2 - E1 = 0.2 V, so the output rises 2 V/s. The slope over " "0.1 s to 1 s, divided by 0.2 V, is the rate." ), ), Run( "common_mode", Transient(stop="1", step="1m"), drive={"e1": "DC 0.5", "e2": "DC 0.5"}, cards=[START], measure={ "e_late": "find v({e_out.1}) at=1", "plus_input": "find v({amp.IN+}) at=1", }, claims=[ Claim("e_late", 0, within=1e-3, absolute=True, unit="V", note="equal inputs: nothing to integrate, the output stays at zero"), ], units={"plus_input": "V"}, note=( "Both inputs at 0.5 V. Both op amp inputs charge together toward " "0.5 V, and the output does not move." ), ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
C1 1 uF -C2 1 uF -GND1 Ground -R1 100 kOhm -R2 100 kOhm -TP1 Terminal -TP2 Terminal -TP3 Terminal -U1 OpAmp -Net-(C1-Pad1) C1.1 R1.2 U1.IN-Net-(C1-Pad2) C1.2 TP3.1 U1.OUTNet-(C2-Pad1) C2.1 R2.2 U1.IN+Net-(C2-Pad2) C2.2 GND1.1Net-(R1-Pad1) R1.1 TP1.1Net-(R2-Pad1) R2.1 TP2.1Every check that ran, and every one left undecided.
2 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 9 component 18 connection 2 constraint 1 decision 1 evidence 3 interface 15 pin 15 port 65 totalsnapshot sha256:614afa90b47e39fe3d6a5156ad65b30e372cc4270f16d5a8c5fc5a374bbf10c0All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/integrators/differential_integrator/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/integrators/differential_integrator/differential_integrator.py