Skip to content
copperhead.sh
Get started

Examples / TI op amp handbook / DC amplifiers

Gain control

SBOA092B page 73, Gain Control: EI on the + input, a 10 kΩ potentiometer from the output to ground, and its wiper on the - input.

E_O / E_I = 1 / k, k = the fraction of the pot below the wiper

The page prints no formula, only that this is the non-inverting amplifier with both resistors replaced by the pot; 1/k is that formula with R_O = (1 - k) R and R_I = k R.

the schematic, drawn by copperhead from the circuit's netlist
The schematic, drawn by copperhead from the circuit's netlist

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/gain_control.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, fang's own projection
The interconnect view, fang's own projection

The interconnect view is fang’s own projection. It names the parts as the program does, so it reads against the code below.

setting records that the pot’s setting counts from the grounded end, so it is k itself, and that the claim is held at k = 0.1, a gain of 10. One constraint:

require(equals(self.a_v, over(1 * ratio, self.pot.setting)))

out/simulation.txt, operating points:

kE_IGainClaimed
0.11 V1010 (a_v), holds
11 V11, holds
0.51 V22, holds
0.251 V44, holds
0.020.2 V5050, holds

At k = 0.02 the drive is cut to 0.2 V so that E_O, 10 V, stays inside the swing. The gain is not linear in the setting, and it heads for infinity as the wiper nears ground.

Terminal window
fang check examples/ti_opamp_handbook/dc_amplifiers/gain_control/gain_control.py
python examples/regenerate.py ti_opamp_handbook/dc_amplifiers/gain_control # needs ngspice
examples/ti_opamp_handbook/dc_amplifiers/gain_control/gain_control.py
"""Gain control, SBOA092B page 73.
Show 16 more lines
E_O / E_I = 1 / k, k = the fraction of the pot below the wiper
A non-inverting amplifier whose two resistors are the two halves of one
10 kOhm potentiometer: it runs from the output to ground, and the wiper feeds
the - input. The wiper sits at k E_O, the loop holds it at E_I, so the gain
is 1/k, from 1 with the wiper at the output towards infinity as it nears
ground. The page prints no formula; it says the circuit is equivalent to
replacing both resistors of the non-inverting amplifier, and the formula here
is that one with R_O = (1 - k) R and R_I = k R.
The program had to decide which end the pot's setting counts from and where
the claim is held (`setting`). The bench moves the wiper through four
settings, and keeps E_I small enough at the top one that the output stays
inside its swing.
"""
import sys
from decimal import Decimal
from 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, require
from fang.rationale import Chooses, Cites
from fang.simulation import OperatingPoint
from handbook import (
Bench,
Claim,
Ground,
OpAmp,
Potentiometer,
Run,
Terminal,
equals,
over,
ratio,
)
class GainControl(System):
"""E_I on the + input, the pot from output to ground, its wiper on the - input."""
figure = Cites(
"Equivalent to replacing both resistors in the non-inverting amplifier. "
"Observe common mode voltage limit.",
document="SBOA092B, Handbook of Operational Amplifier Applications",
locator="page 73, Gain Control",
)
setting = Chooses(
"Which end does the pot's setting count from, and where is the claim held?",
selected=(
"end 1 on ground and end 3 on the output, so the setting is k, the "
"fraction below the wiper; the claim is held at k = 0.1, a gain of 10"
),
alternatives=[
{
"option": "end 1 on the output",
"reason": "the setting would be 1 - k, and the gain 1 / (1 - setting) reads less directly",
},
{
"option": "hold the claim at mid travel, a gain of 2",
"reason": "the page's other amplifiers are shown at a gain of 10; mid travel is checked on the bench",
},
],
rationale=(
"the figure gives the pot's value and no setting",
"counting from ground makes the setting the feedback fraction itself",
),
)
a_v = Parameter("1", default=10 * ratio, description="E_O / E_I at the chosen setting")
e_in = Terminal()
e_out = Terminal()
pot = Potentiometer(resistance=10 * kOhm, setting=Decimal("0.1") * ratio)
amp = OpAmp()
ground = Ground()
def architecture(self):
self.e_in.probe >> self.amp.non_inverting.signal
self.amp.output.signal >> self.pot.end_b
self.pot.wiper >> self.amp.inverting.signal
self.pot.end_a >> self.ground.node
self.amp.output.signal >> self.e_out.probe
def constraints(self):
# The wiper divides E_O by k and the loop holds it at E_I.
require(equals(self.a_v, over(1 * ratio, self.pot.setting)))
def _setting(k: float, drive: float) -> Run:
return Run(
f"k_{int(k * 100):03d}",
OperatingPoint(),
drive={"e_in": f"DC {drive:g}"},
settings={"pot": {"setting": k}},
measure={"gain": "v({e_out.1}) / v({e_in.1})"},
claims=[Claim("gain", 1 / k, within=0.001, note=f"1 / k = 1 / {k:g}")],
)
BENCH = Bench(
page=73,
title="Gain Control",
runs=[
Run(
"chosen",
OperatingPoint(),
drive={"e_in": "DC 1"},
measure={"gain": "v({e_out.1}) / v({e_in.1})", "e_out": "v({e_out.1})"},
claims=[Claim("gain", "a_v", within=0.001)],
units={"e_out": "V"},
),
_setting(1.0, 1.0),
_setting(0.5, 1.0),
_setting(0.25, 1.0),
Run(
"k_002",
OperatingPoint(),
drive={"e_in": "DC 0.2"},
settings={"pot": {"setting": 0.02}},
measure={"gain": "v({e_out.1}) / v({e_in.1})"},
claims=[
Claim(
"gain",
50,
within=0.001,
note="1 / 0.02; E_I is 0.2 V here so that E_O, 10 V, stays inside the swing",
)
],
),
],
)

The parts, then the nets and the pads on them.

out/netlist.txt
GND1 Ground -
RV1 Potentiometer -
TP1 Terminal -
TP2 Terminal -
U1 OpAmp -
Net-(GND1-Pad1) GND1.1 RV1.1
Net-(RV1-Pad2) RV1.2 U1.IN-
Net-(RV1-Pad3) RV1.3 TP2.1 U1.OUT
Net-(TP1-Pad1) TP1.1 U1.IN+

Every check that ran, and every one left undecided.

out/checks.txt
1 checks, 0 failed, 0 undecided

What the elaborated graph contains, by entity kind.

out/graph.txt
1 block
5 component
10 connection
1 constraint
1 decision
1 evidence
3 interface
9 pin
9 port
40 total
snapshot sha256:8114e0693bd3e59d6f593eeaee1515a859d5b1635ba27f6ca76f7af4b0d5e196

All of it, including the KiCad netlist, is in examples/ti_opamp_handbook/dc_amplifiers/gain_control/out/. Rebuild it with:

Terminal window
fang build examples/ti_opamp_handbook/dc_amplifiers/gain_control/gain_control.py