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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
"""Datasheet-built 3D models (CadQuery) for the LAN8770M (32-VQFN 5x5, Microchip C04-1268 Rev A) and the
TMC2262 (30L FC2QFN 6x6, ADI 21-100708 Rev A).
../.venv-cq/bin/python tools/build_models.py <out_dir>
Frame: Adom Z-up, millimetres, the part seated on z = 0 (the terminals' solder faces), centred on the footprint
origin. KiCad footprints are Y-down, so footprint (x, y) is model (x, -y): pin 1 sits at the top-left seen from
above. Nominal dimensions from each drawing's table; every value is cited in the provenance JSON this writes.
Mould compound is near-black, terminals matte tin. No part number is modelled (the virtual mark is added later by
the mark planner); the pin-1 dimple is the only top feature.
"""
import json, math, os, sys
import cadquery as cq
BODY = cq.Color(0.10, 0.10, 0.11)
TIN = cq.Color(0.80, 0.80, 0.82)
def rect(x0, y0, x1, y1, z0, z1):
return cq.Workplane('XY').box(abs(x1 - x0), abs(y1 - y0), z1 - z0, centered=False).translate((min(x0, x1), min(y0, y1), z0))
def lead_rounded(cx, edge, inward, width, length, z0, z1, axis):
"""A QFN terminal of `width` running `length` inward from the package edge, inner end a full radius.
axis 'y': edge is a y value, inward is -1/+1 along y; axis 'x' likewise."""
r = width / 2
if axis == 'y':
y_in = edge + inward * (length - r)
box = rect(cx - r, edge, cx + r, y_in, z0, z1)
cap = cq.Workplane('XY').circle(r).extrude(z1 - z0).translate((cx, y_in, z0))
else:
x_in = edge + inward * (length - r)
box = rect(edge, cx - r, x_in, cx + r, z0, z1)
cap = cq.Workplane('XY').circle(r).extrude(z1 - z0).translate((x_in, cx, z0))
return box.union(cap)
def save(parts, body, out, name):
asm = cq.Assembly(name=name)
asm.add(body, name='body', color=BODY)
for i, (n, p) in enumerate(parts):
asm.add(p, name='pin_%s' % n, color=TIN)
path = os.path.join(out, name + '.step')
asm.save(path)
return path
# ------------------------------------------------------------------------------------------------ LAN8770M
def lan8770m(out):
d = dict(D=5.00, E=5.00, A=0.85, A1=0.02, A3=0.203, b=0.25, L=0.40, e=0.50, D2=3.10, E2=3.10,
A4=0.125, D3=0.04, pin1_dimple=0.50, ep_chamfer=0.30)
src = 'Microchip DS00002550C Figure 3-1/3-2, drawing C04-1268 Rev A (nominal)'
h = d['D'] / 2
z0, z1 = 0.0, d['A3']
pins = []
for i in range(8):
c = 1.75 - i * d['e'] # model y of pins 1..8 (left side, top to bottom)
pins.append((str(1 + i), lead_rounded(c, -h, +1, d['b'], d['L'], z0, z1, 'x')))
pins.append((str(9 + i), lead_rounded(-1.75 + i * d['e'], -h, +1, d['b'], d['L'], z0, z1, 'y'))) # bottom, left to right
pins.append((str(17 + i), lead_rounded(-1.75 + i * d['e'], h, -1, d['b'], d['L'], z0, z1, 'x'))) # right, bottom to top
pins.append((str(25 + i), lead_rounded(1.75 - i * d['e'], h, -1, d['b'], d['L'], z0, z1, 'y'))) # top, right to left
# wettable flank: a step cut D3 deep, A4 high at the outer end of every terminal (section A-A)
cut = []
for n, p in pins:
bb = p.val().BoundingBox()
if abs(bb.xmin + h) < 1e-6:
cut.append(rect(-h, bb.ymin, -h + d['D3'], bb.ymax, 0, d['A4']))
elif abs(bb.xmax - h) < 1e-6:
cut.append(rect(h - d['D3'], bb.ymin, h, bb.ymax, 0, d['A4']))
elif abs(bb.ymin + h) < 1e-6:
cut.append(rect(bb.xmin, -h, bb.xmax, -h + d['D3'], 0, d['A4']))
else:
cut.append(rect(bb.xmin, h - d['D3'], bb.xmax, h, 0, d['A4']))
pins = [(n, p.cut(c)) for (n, p), c in zip(pins, cut)]
# exposed pad with its pin-1 chamfer (the drawing's isometric view), pad 33 = VSS
s, c = d['D2'] / 2, d['ep_chamfer']
ep = (cq.Workplane('XY').polyline([(-s + c, s), (s, s), (s, -s), (-s, -s), (-s, s - c)]).close().extrude(z1))
pins.append(('33', ep))
body = rect(-h, -h, h, h, 0, d['A'])
for n, p in pins:
body = body.cut(p)
body = body.cut(cq.Workplane('XY').circle(d['pin1_dimple'] / 2).extrude(0.03).translate((-h + 0.65, h - 0.65, d['A'] - 0.03)))
path = save(pins, body, out, 'LAN8770M')
return path, d, src
# ------------------------------------------------------------------------------------------------ TMC2262
def tmc2262(out):
d = dict(D=6.0, E=6.0, A=0.75, A1=0.203, b=0.25, L=0.40, b1=0.40, L1=0.65, L2=0.675, b2=0.54, L3=1.95,
e=0.5, D2=1.4, E2=4.0, ep_chamfer=0.20, pin1_dimple=0.50)
src = 'ADI package outline 21-100708 Rev A, 30L FC2QFN 6x6x0.75 (nominal); positions from land pattern 90-100245 Rev B'
h = d['D'] / 2
z0, z1 = 0.0, d['A1']
small_x = [-1.9, -1.4, -0.9, -0.4, 0.4, 0.9, 1.4, 1.9]
bar_y = [1.73, 0.855, 0.0, -0.855, -1.73] # model y (pins 1..5 left top to bottom; 16..20 right bottom to top reversed)
pins = []
for i, y in enumerate(bar_y): # pins 1..5, left side, top (model +y) to bottom
pins.append((str(1 + i), rect(-h, y - d['b2'] / 2, -h + d['L3'], y + d['b2'] / 2, z0, z1)))
for i, y in enumerate(reversed(bar_y)): # pins 16..20, right side, bottom to top
pins.append((str(16 + i), rect(h - d['L3'], y - d['b2'] / 2, h, y + d['b2'] / 2, z0, z1)))
for i, x in enumerate(small_x): # pins 7..14 bottom row left to right; 22..29 top row right to left
pins.append((str(7 + i), lead_rounded(x, -h, +1, d['b'], d['L'], z0, z1, 'y')))
pins.append((str(29 - i), lead_rounded(x, h, -1, d['b'], d['L'], z0, z1, 'y')))
def corner(sx, sy):
"""L terminal: a b1-wide leg from the top/bottom edge L1 long at x = sx*2.525, plus a leg to the side edge
L2 long, b1 high, both legs meeting at the inner corner (21-100708 bottom view)."""
xc = sx * 2.525
x_in = xc - sx * d['b1'] / 2
y_edge, y_in = sy * h, sy * (h - d['L1'])
leg1 = rect(xc - d['b1'] / 2, y_in, xc + d['b1'] / 2, y_edge, z0, z1)
leg2 = rect(x_in, y_in, sx * h, y_in + sy * d['b1'], z0, z1)
return leg1.union(leg2)
pins.append(('6', corner(-1, -1)))
pins.append(('15', corner(+1, -1)))
pins.append(('21', corner(+1, +1)))
pins.append(('30', corner(-1, +1)))
sx, sy, c = d['D2'] / 2, d['E2'] / 2, d['ep_chamfer']
ep = cq.Workplane('XY').polyline([(-sx + c, sy), (sx, sy), (sx, -sy), (-sx, -sy), (-sx, sy - c)]).close().extrude(z1)
pins.append(('EP', ep))
body = rect(-h, -h, h, h, 0, d['A'])
for n, p in pins:
body = body.cut(p)
body = body.cut(cq.Workplane('XY').circle(d['pin1_dimple'] / 2).extrude(0.03).translate((-h + 0.7, h - 0.7, d['A'] - 0.03)))
path = save(pins, body, out, 'TMC2262AFV+')
return path, d, src
if __name__ == '__main__':
out = sys.argv[1] if len(sys.argv) > 1 else '.'
os.makedirs(out, exist_ok=True)
rec = {}
for fn in (lan8770m, tmc2262):
path, dims, src = fn(out)
bb = cq.importers.importStep(path).val().BoundingBox()
rec[os.path.basename(path)] = dict(source=src, dimensions_mm=dims, frame='Adom Z-up, mm, seated on z = 0, footprint origin, pin 1 top-left',
bbox_mm=[round(v, 4) for v in (bb.xmin, bb.ymin, bb.zmin, bb.xmax, bb.ymax, bb.zmax)],
tool='CadQuery %s (tools/build_models.py)' % cq.__version__)
print('OK', path, rec[os.path.basename(path)]['bbox_mm'])
json.dump(rec, open(os.path.join(out, 'models.provenance.json'), 'w'), indent=1)