component
TMC2262
Public Made by Adomby adom
Analog Devices TMC2262 65 V, 4 A RMS smart stepper driver with step/dir, SPI and encoder input, 30-pin FC2QFN 6x6. KiCad, Fusion and Altium libraries, datasheet-built 3D model, every pin cited to the data sheet.
main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
"""Proof figure: the 3D model's terminals (taken from the STEP itself) against the manufacturer land pattern (taken
from the footprint), at true scale in millimetres, seen from the top.
../.venv-cq/bin/python tools/proof.py <slug> <footprint.kicad_mod> <model.step> <out.png>
Grey fill: every model face on the seating plane (z = 0) facing down, i.e. the solderable terminal faces.
Teal dashed: every copper pad of the footprint (KiCad Y down, flipped to the model's Y up). Thin grey: body outline.
A terminal must sit inside its land with the drawing's toe and heel; pad 1 and the pin-1 dot must be top left.
"""
import math, re, sys
import numpy as np
import cadquery as cq
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Ellipse, FancyBboxPatch
slug, mod, step, out = sys.argv[1:5]
shape = cq.importers.importStep(step)
terms = []
solids = sorted(shape.solids().vals(), key=lambda s: s.Volume())
for sol in solids[:-1]: # every solid but the largest (the mould body) is a terminal
for f in sol.Faces():
c = f.Center()
if abs(c.z) > 1e-4 or f.geomType() != 'PLANE':
continue
try:
n = f.normalAt()
except Exception:
continue
if n.z > -0.9:
continue
vs, tris = f.tessellate(0.005)
for a, b, cc in tris:
terms.append([(vs[a].x, vs[a].y), (vs[b].x, vs[b].y), (vs[cc].x, vs[cc].y)])
bb = shape.val().BoundingBox()
t = open(mod).read()
pads = []
for m in re.finditer(r'\(pad "([^"]*)" (smd|thru_hole) (\w+) \(at ([-\d.]+) ([-\d.]+)\) \(size ([\d.]+) ([\d.]+)\)(.*?)\n \)?', t, re.S):
num, kind, shp, x, y, w, h, rest = m.group(1), m.group(2), m.group(3), *map(float, m.group(4, 5, 6, 7)), m.group(8)
if num == '' or kind == 'thru_hole':
continue
poly = None
if shp == 'custom':
q = re.search(r'\(gr_poly \(pts (.*?)\) \(width', t[m.start():m.start() + 2000], re.S)
poly = [(x + float(a), -(y + float(b))) for a, b in re.findall(r'\(xy ([-\d.]+) ([-\d.]+)\)', q.group(1))]
pads.append((num, shp, x, -y, w, h, poly))
fig, ax = plt.subplots(figsize=(9, 9), dpi=140)
fig.patch.set_facecolor('#0d1117'); ax.set_facecolor('#0d1117')
ax.add_patch(Rectangle((bb.xmin, bb.ymin), bb.xlen, bb.ylen, fill=False, ec='#8b949e', lw=1.0))
from matplotlib.collections import PolyCollection
ax.add_collection(PolyCollection(terms, facecolors='#c9d1d9', edgecolors='#c9d1d9', linewidths=0.2, alpha=0.85))
for num, shp, x, y, w, h, poly in pads:
kw = dict(fill=False, ec='#00d6cd', lw=1.3, ls=(0, (4, 2)))
if poly:
ax.add_patch(Polygon(poly, closed=True, **kw))
elif shp == 'oval':
r = min(w, h) / 2
ax.add_patch(FancyBboxPatch((x - w / 2 + r, y - h / 2 + r), w - 2 * r, h - 2 * r, boxstyle='round,pad=%f' % r, **kw))
else:
ax.add_patch(Rectangle((x - w / 2, y - h / 2), w, h, **kw))
if num in ('1', 'EP'):
ax.annotate(num, (x, y), color='#f0b429', fontsize=11, ha='center', va='center', weight='bold')
lim = max(abs(v) for p in pads for v in (p[2] + p[4] / 2, p[3] + p[5] / 2)) + 0.6
ax.set_xlim(-lim, lim); ax.set_ylim(-lim, lim); ax.set_aspect('equal')
ax.tick_params(colors='#8b949e'); [s.set_color('#30363d') for s in ax.spines.values()]
ax.grid(color='#21262d', lw=0.5)
ax.set_xlabel('mm', color='#8b949e'); ax.set_ylabel('mm', color='#8b949e')
ax.set_title('%s: model terminals (grey, from the STEP) inside the manufacturer land pattern (teal dashed), top view'
% slug.upper(), color='#e6edf3', fontsize=10)
fig.tight_layout(); fig.savefig(out, facecolor=fig.get_facecolor())
print('OK', out, len(solids) - 1, 'terminal solids,', len(pads), 'lands')