Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
2126c3284This file is pinned to the selected release. Download the original to open it in your design tool.
"""Voltage-regulator feedback network calculators.
Two topologies:
* ``adj3`` — 3-terminal adjustable regulator (LM317 family):
``Vout = Vref·(1 + R2/R1) + Iadj·R2`` with R1 from OUT to ADJ and R2 from
ADJ to GND. Reference: TI LM317 datasheet, SLVS044Y (2020), §8.2
("Typical Application", output-voltage equation); Vref 1.20–1.30 V
(1.25 typ), Iadj 50 µA typ / 100 µA max per §6.5.
* ``fb`` — fixed-reference FB-pin divider (most LDOs / switchers):
``Vout = Vref·(1 + R_top/R_bottom)``, R_top from VOUT to FB, R_bottom FB to
GND. Reference: e.g. TI SLVA966 (feedback-divider design) — the equation is
the plain divider inverse.
Worst-case analysis enumerates every corner of Vref, Iadj and the resistor
tolerance band (exhaustive corner evaluation — no monotonicity assumptions).
"""
from __future__ import annotations
import itertools
from .eseries import SERIES, series_values, snap
from .registry import CalcError, Param, Result, register
_REF = ("TI LM317 datasheet SLVS044Y §8.2 (adj3); FB-divider per plain "
"divider inverse, cf. TI SLVA966 (fb)")
def _vout(kind: str, r1: float, r2: float, vref: float, iadj: float) -> float:
if kind == "adj3":
return vref * (1 + r2 / r1) + iadj * r2
return vref * (1 + r1 / r2) # fb: r1 = top, r2 = bottom
def _corners(kind: str, r1: float, r2: float, tol_pct: float, vref_min: float,
vref_max: float, iadj_min: float,
iadj_max: float) -> tuple[float, float]:
t = tol_pct / 100.0
vals: list[float] = []
for f1, f2, vr, ia in itertools.product(
(1 - t, 1 + t), (1 - t, 1 + t),
(vref_min, vref_max), (iadj_min, iadj_max)):
vals.append(_vout(kind, r1 * f1, r2 * f2, vr, ia))
return min(vals), max(vals)
@register(
"regulator", "Adjustable-regulator output voltage (worst case)", "power",
_REF,
(Param("kind", "", "topology", default="adj3", choices=("adj3", "fb")),
Param("r1", "Ω", "adj3: OUT→ADJ resistor; fb: VOUT→FB (top)"),
Param("r2", "Ω", "adj3: ADJ→GND resistor; fb: FB→GND (bottom)"),
Param("vref", "V", "reference voltage (typ)", default=1.25),
Param("vref_min", "V", "reference min (0 = use typ)", default=0.0),
Param("vref_max", "V", "reference max (0 = use typ)", default=0.0),
Param("iadj", "A", "ADJ pin current typ (adj3 only)", default=50e-6),
Param("iadj_max", "A", "ADJ pin current max", default=100e-6),
Param("tol", "%", "resistor tolerance", default=1.0)),
)
def _calc_regulator(kind: str, r1: float, r2: float, vref: float,
vref_min: float, vref_max: float, iadj: float,
iadj_max: float, tol: float) -> list[Result]:
if r1 <= 0 or r2 <= 0:
raise CalcError("resistors must be positive")
if kind == "fb":
iadj = iadj_max = 0.0
vmin_ref = vref_min or vref
vmax_ref = vref_max or vref
typ = _vout(kind, r1, r2, vref, iadj)
lo, hi = _corners(kind, r1, r2, tol, vmin_ref, vmax_ref,
0.0 if kind == "adj3" else 0.0, iadj_max)
return [Result("vout_typ", typ, "V"),
Result("vout_min", lo, "V", "worst-case corner"),
Result("vout_max", hi, "V", "worst-case corner"),
Result("tolerance_pct", (hi - lo) / (2 * typ) * 100, "%",
"± about typ")]
@register(
"regulator-design", "Feedback resistor pair for a target Vout", "power",
_REF + "; value snapping per IEC 60063:2015",
(Param("kind", "", "topology", default="adj3", choices=("adj3", "fb")),
Param("vout", "V", "wanted output voltage"),
Param("vref", "V", "reference voltage (typ)", default=1.25),
Param("iadj", "A", "ADJ pin current typ (adj3 only)", default=50e-6),
Param("r_fixed", "Ω", "chosen fixed resistor (adj3: R1; fb: R_bottom)",
default=240.0),
Param("series", "", "E series", default="E96", choices=tuple(SERIES))),
)
def _calc_regulator_design(kind: str, vout: float, vref: float, iadj: float,
r_fixed: float, series: str) -> list[Result]:
if vout <= vref:
raise CalcError(f"vout must exceed vref ({vref:g} V)")
if kind == "adj3":
# solve R2 from Vout = Vref(1 + R2/R1) + Iadj·R2
ideal = (vout - vref) / (vref / r_fixed + iadj)
else:
ideal = r_fixed * (vout - vref) / vref # top resistor
iadj = 0.0
r_std, _ = snap(ideal, series)
actual = _vout(kind, r_fixed if kind == "adj3" else r_std,
r_std if kind == "adj3" else r_fixed, vref, iadj)
other = "r2" if kind == "adj3" else "r_top"
return [Result("r_fixed", r_fixed, "Ω",
"R1 (OUT→ADJ)" if kind == "adj3" else "R_bottom (FB→GND)"),
Result(f"{other}_ideal", ideal, "Ω"),
Result(f"{other}_standard", r_std, "Ω"),
Result("vout_actual", actual, "V",
f"{(actual - vout) / vout * 100:+.4f}% vs target")]