component
TPS16850VMAR - 9 V to 80 V, 20 A eFuse
Public Made by Adomby adom
Texas Instruments TPS16850VMAR: 9 V to 80 V, 20 A stackable integrated hot-swap eFuse with a 3.5 mOhm FET, fast current monitor and auto-retry fault response, LQFN-CLIP-23 (VMA, 6.0 x 5.0 mm). KiCad symbol and footprint, Fusion and Altium libraries, datasheet-built 3D model and datasheet.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
#!/usr/bin/env python3
"""Build the TPS16850VMAR footprint from TI's recommended land pattern (SLVSHA1D p.55, LAND PATTERN EXAMPLE for
VMA0023A, 4230225/A 11/2023) and solder paste example (p.56).
KiCad frame: Y down, origin = package centre (TI's 0.000 PKG centre lines). TI draws the land pattern as a top
view with Y up, so every TI y is negated here.
Step 1 writes the plain pads (rectangles, the exposed pad, TI's 12 vias) as an adom-footprint create JSON and runs
adom-footprint 1.0.26. Step 2 adds what adom-footprint create cannot express:
* pads 6 and 17: L-shaped custom pads (non solder mask defined, like the plain pads);
* pads 7, 10 and 22: TI draws a copper rectangle under solder mask (blue dashed, "METAL UNDER SOLDER MASK") with
a comb-shaped solder mask opening (green). Written as two pads with the same number: a copper-only rectangle
(F.Cu) and a comb custom pad (F.Cu F.Mask F.Paste, mask margin 0);
* exposed pad 24: copper and mask only; the paste is TI's six 0.98 x 1.04 mm windows (p.56, 76 % coverage),
written as unnumbered F.Paste apertures;
* silkscreen outline 0.2 mm outside the copper, pin-1 dot left of pad 1, fab outline with the pin-1 chamfer,
3D model path; corner radius of the plain pads R0.05 as TI prints it.
Step 3 writes tps16850vmar-lbr-input.kicad_mod for adom-lbr 2.47.5, which drops custom-pad primitives and
paste-only pads: the same copper as rectangles (every custom shape here is rectilinear, so the union of the
rectangles is exact), copper-only rectangles with mask and paste margin -1.2 mm (no opening), no paste-only pads.
usage: build_fp.py outdir
"""
import json, os, re, subprocess, sys
out = sys.argv[1]
NAME = "tps16850vmar"
MODEL = "${KIPRJMOD}/tps16850vmar-etched.step"
R = 0.05
def rr(w, h): return round(R / min(w, h), 4)
pads = []
def pad(n, x, y, w, h):
pads.append({"number": str(n), "x": x, "y": y, "dx": w, "dy": h, "shape": "roundrect"})
for n, ty in zip(range(1, 5), (0.5, 0.0, -0.5, -1.0)): # left 1-4: (0.65) long, 21X (0.25) wide
pad(n, -2.875, -ty if ty else 0.0, 0.65, 0.25)
pad(5, -2.85, 1.5, 0.70, 0.25) # pin 5 (0.7) long
pad(8, 0.225, 2.325, 0.25, 0.75) # 2X (0.75) at x 0.225 / 1.275, TI y -2.325
pad(9, 1.275, 2.325, 0.25, 0.75)
for n, ty in zip(range(11, 17), (-0.925, -0.425, 0.075, 0.575, 1.075, 1.575)): # right, 10X (0.65)
pad(n, 2.875, -ty, 0.65, 0.25)
for n, x in zip(range(18, 22), (1.85, 1.35, 0.85, 0.35)): # top, 4X 0.6 at TI y 2.4
pad(n, x, -2.4, 0.25, 0.60)
pad(23, -2.55, -1.55, 1.30, 0.25) # (1.3) at TI y 1.55
pads.append({"number": "24", "x": 0.0, "y": -0.075, "dx": 3.4, "dy": 2.35, "shape": "rect", "layers": '"F.Cu" "F.Mask"'})
VIAS = [(x, y) for y in (-0.875, -0.075, 0.725) for x in (-1.45, -0.59, 0.59, 1.45)] # (0.2) TYP VIA
for x, y in VIAS:
pads.append({"number": "24", "x": x, "y": y, "dx": 0.5, "dy": 0.5, "shape": "circle", "mount": "thru_hole",
"drill": 0.2, "layers": '"*.Cu"'})
spec = {"footprintName": NAME,
"description": "TI VMA0023A LQFN-CLIP-23, 6.0 x 5.0 mm, 1.5 mm max height; land pattern and stencil per "
"TI SLVSHA1D p.55-56 (TPS1685x eFuse)",
"tags": "TPS1685 TPS16850 VMA0023A LQFN-CLIP QFN eFuse",
"pads": pads, "body": {"x1": -3.0, "y1": -2.5, "x2": 3.0, "y2": 2.5},
"courtyard": {"x1": -3.6, "y1": -3.1, "x2": 3.6, "y2": 3.1}}
os.makedirs(out, exist_ok=True)
jp = os.path.join(out, "footprint-create.json")
json.dump(spec, open(jp, "w"), indent=1)
r = subprocess.run(["adom-footprint", "create", "--file", jp, "--out", os.path.join(out, "gen")], capture_output=True, text=True)
if r.returncode: sys.exit(r.stdout + r.stderr)
base = open(os.path.join(out, "gen", NAME + ".kicad_mod")).read()
base = re.sub(r'\s*\(fp_line [^\n]*\n\s*\(stroke [^\n]*\(layer "F.SilkS"\)\)', "", base)
base = re.sub(r'\s*\(fp_rect \(start -3 -2.5\) \(end 3 2.5\)\n\s*\(stroke [^\n]*\(layer "F.Fab"\)\)', "", base)
base = base.replace('(drill 0.2)\n (layers "*.Cu"))', '(drill 0.2)\n (layers "*.Cu") (property pad_prop_heatsink))')
base = base.replace("(roundrect_rratio 0.25))", "(roundrect_rratio 0.2))") # r = 0.05 on 0.25 mm pads
base = re.sub(r'\(fp_text reference "REF\*\*" \(at 0 [-\d.]+\)', '(fp_text reference "REF**" (at 0 -4)', base)
base = re.sub(r'\(fp_text value "[^"]*" \(at 0 [-\d.]+\)', '(fp_text value "TPS16850VMAR" (at 0 4)', base)
def rect(x1, y1, x2, y2): return [(x1, y1), (x2, y1), (x2, y2), (x1, y2)]
ALL = '"F.Cu" "F.Paste" "F.Mask"'
# custom shapes (KiCad y)
P6 = [(-3.2, 1.875), (-2.5, 1.875), (-2.5, 2.7), (-2.8, 2.7), (-2.8, 2.125), (-3.2, 2.125)]
P17 = [(2.225, -2.7), (2.475, -2.7), (2.475, -2.15), (3.2, -2.15), (3.2, -1.9), (2.225, -1.9)]
C22 = [(-3.2, -1.925), (-1.0, -1.925), (-1.0, -2.7), (-1.25, -2.7), (-1.25, -2.25), (-1.5, -2.25), (-1.5, -2.7),
(-1.75, -2.7), (-1.75, -2.25), (-2.0, -2.25), (-2.0, -2.7), (-2.25, -2.7), (-2.25, -2.25), (-2.5, -2.25),
(-2.5, -2.7), (-2.75, -2.7), (-2.75, -2.175), (-3.2, -2.175)]
C7 = [(-1.7, 1.95), (-0.15, 1.95), (-0.15, 2.7), (-0.35, 2.7), (-0.35, 2.25), (-0.6, 2.25), (-0.6, 2.7), (-0.8, 2.7),
(-0.8, 2.25), (-1.05, 2.25), (-1.05, 2.7), (-1.25, 2.7), (-1.25, 2.25), (-1.5, 2.25), (-1.5, 2.7), (-1.7, 2.7)]
C10 = [(1.65, 1.85), (3.2, 1.85), (3.2, 2.25), (2.4, 2.25), (2.4, 2.7), (2.15, 2.7), (2.15, 2.25), (1.9, 2.25),
(1.9, 2.7), (1.65, 2.7)]
CU = {"22": (-3.25, -2.75, -0.95, -1.875), "7": (-1.75, 1.9, -0.1, 2.75), "10": (1.6, 1.8, 3.25, 2.75)}
ANCH = {"6": (-2.65, 2.0), "17": (2.35, -2.025), "22": (-2.1, -2.05), "7": (-0.925, 2.1), "10": (2.4, 2.05)}
# the same shapes as rectangles (for adom-lbr)
RECTS = {"6": [(-3.2, 1.875, -2.5, 2.125), (-2.8, 1.875, -2.5, 2.7)],
"17": [(2.225, -2.7, 2.475, -1.9), (2.225, -2.15, 3.2, -1.9)],
"22": [(-3.2, -2.175, -2.75, -1.925), (-2.75, -2.25, -1.0, -1.925)] +
[(c - 0.125, -2.7, c + 0.125, -2.25) for c in (-2.625, -2.125, -1.625, -1.125)],
"7": [(-1.7, 1.95, -0.15, 2.25)] + [(c - 0.1, 2.25, c + 0.1, 2.7) for c in (-1.6, -1.15, -0.7, -0.25)],
"10": [(1.65, 1.85, 3.2, 2.25)] + [(c - 0.125, 2.25, c + 0.125, 2.7) for c in (1.775, 2.275)]}
SHAPE = {"6": P6, "17": P17, "22": C22, "7": C7, "10": C10}
def custom(num, pts, layers, extra=""):
ax, ay = ANCH[num]
rel = " ".join(f"(xy {round(x - ax, 4):g} {round(y - ay, 4):g})" for x, y in pts)
return (f' (pad "{num}" smd custom (at {ax:g} {ay:g}) (size 0.2 0.2)\n (layers {layers}){extra}\n'
f' (options (clearance outline) (anchor rect))\n (primitives (gr_poly (pts {rel}) (width 0) (fill yes))))\n')
def rpad(num, x1, y1, x2, y2, layers, extra=""):
return (f' (pad "{num}" smd rect (at {(x1 + x2) / 2:g} {(y1 + y2) / 2:g}) (size {round(x2 - x1, 4):g} {round(y2 - y1, 4):g})'
f' (layers {layers}){extra})\n')
graphics = (' (fp_poly (pts (xy -2.5 -2.5) (xy 3 -2.5) (xy 3 2.5) (xy -3 2.5) (xy -3 -2)) '
'(stroke (width 0.1) (type default)) (fill none) (layer "F.Fab"))\n')
for a, b in (((-3.45, -2.95), (3.45, -2.95)), ((3.45, -2.95), (3.45, 2.95)), ((3.45, 2.95), (-3.45, 2.95)),
((-3.45, 2.95), (-3.45, -2.95))):
graphics += f' (fp_line (start {a[0]:g} {a[1]:g}) (end {b[0]:g} {b[1]:g})\n (stroke (width 0.1) (type default)) (layer "F.SilkS"))\n'
graphics += ' (fp_circle (center -3.8 -0.5) (end -3.68 -0.5)\n (stroke (width 0.1) (type default)) (fill solid) (layer "F.SilkS"))\n'
model = f' (model "{MODEL}"\n (offset (xyz 0 0 0)) (scale (xyz 1 1 1)) (rotate (xyz 0 0 0)))\n'
main = ""
for n in ("6", "17"): main += custom(n, SHAPE[n], ALL)
for n in ("22", "7", "10"):
x1, y1, x2, y2 = CU[n]
main += custom(n, rect(x1, y1, x2, y2), '"F.Cu"')
main += custom(n, SHAPE[n], ALL, " (solder_mask_margin 0)")
for y in (-0.695, 0.545):
for x in (-1.18, 0.0, 1.18):
main += f' (pad "" smd roundrect (at {x:g} {y:g}) (size 0.98 1.04) (layers "F.Paste") (roundrect_rratio {rr(0.98, 1.04)}))\n'
lbr = ""
for n in ("6", "17"):
for r_ in RECTS[n]: lbr += rpad(n, *r_, ALL)
for n in ("22", "7", "10"):
lbr += rpad(n, *CU[n], '"F.Cu"', " (solder_mask_margin -1.2) (solder_paste_margin -1.2)")
for r_ in RECTS[n]: lbr += rpad(n, *r_, ALL, " (solder_mask_margin 0)")
def finish(extra):
i = base.rstrip().rfind(")")
return base[:i] + graphics + extra + model + ")\n"
open(os.path.join(out, f"{NAME}.kicad_mod"), "w").write(finish(main))
open(os.path.join(out, f"{NAME}-lbr-input.kicad_mod"), "w").write(finish(lbr))
json.dump({"vias": VIAS, "rects": RECTS, "copper_only": CU, "ep": {"x": 0, "y": -0.075, "w": 3.4, "h": 2.35},
"ep_paste": [[x, y, 0.98, 1.04] for y in (-0.695, 0.545) for x in (-1.18, 0.0, 1.18)]},
open(os.path.join(out, "fp-extra.json"), "w"), indent=1)
print("OK", NAME, "and lbr-input written to", out)