component
MCP2562FD-E/SN - CAN FD Transceiver
Public Made by Adomby adom
Microchip MCP2562FD-E/SN: high-speed CAN FD transceiver with a VIO pin for 1.8 V to 5.5 V logic, ISO 11898-2/-5, extended temperature, SOIC-8. KiCad symbol and footprint, Fusion and Altium libraries, 3D model and datasheet.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
#!/usr/bin/env python3
"""Build a Microchip SN (SOIC-8, 3.90 mm body) gull-wing model from the package drawing values
(DS20005284B p.23-24, drawing C04-00057-SN Rev L), in KiCad's 3D frame: Z up, seated on z = 0, centred on the
footprint origin, pins along x, pin 1 at (-x, +y) (footprint pad 1 at (-x, -y) in KiCad's Y-down footprint).
Bend radii, lead angle, mold draft and the pin-1 dimple follow KiCad's own SOIC-8 library model so the leads keep
radiused gull-wing bends; only the flat foot is longer, so the gauge-plane foot L lands inside 0.40 .. 1.27.
usage: build_soic8.py out.step [params.json] (writes a two-part assembly: body (black epoxy), pins (tin))"""
import sys, json, math
import cadquery as cq
P = dict(
N=8, e=1.27, E=6.00, E1=3.90, D=4.90, A=1.75, A1=0.10,
b=0.41, c=0.20, # lead width / thickness (drawing: b 0.31..0.51, c 0.17..0.25)
parting=0.825, # parting line height (KiCad library model; not printed)
top_inset=0.19, bot_inset=0.115, # mold draft insets per side (KiCad model: 11.6 deg top, 9.0 deg bottom; drawing 5..15)
r_in=0.10, # inner bend radius (drawing R, R1 >= 0.07; KiCad 0.10)
lead_angle=9.3, # rise off vertical, degrees (KiCad model)
z_shoulder=0.925, # lead centreline height where it leaves the body (KiCad model)
foot_flat=0.39, # flat bottom length at z = 0, tuned so gauge-plane L = target
pin1=dict(x=-1.11, y=1.61, d=0.60, depth=0.05),
detail="full", # full | low (bends as 3 straight segments) | superlow (1 segment, undrafted body)
)
if len(sys.argv) > 2:
P.update(json.load(open(sys.argv[2])))
def lead_profile_path(sign):
"""centreline of one lead in the XZ plane, from inside the body to the tip, for the -x row (sign=-1)."""
c, r = P["c"], P["r_in"] + P["c"] / 2 # centreline radius of both bends
tip = P["E"] / 2
zc_foot = c / 2
a = math.radians(P["lead_angle"])
# bottom bend: horizontal foot (going outward) <- rise. Work outward in |x|.
x_fb = tip - P["foot_flat"] # where the flat (centreline) foot starts, |x|
# bottom bend centre above the foot start
cb = (x_fb, zc_foot + r)
# rise direction (going up and inward): from bottom bend end
# point on bottom arc where tangent equals rise direction: angle from centre
pb_end = (cb[0] - r * math.cos(a), cb[1] - r * math.sin(a))
# top bend: centre below shoulder centreline
ct_z = P["z_shoulder"] - r
pt_start_z = ct_z + r * math.sin(a)
# rise line from pb_end going up-inward with angle a off vertical
dz = pt_start_z - pb_end[1]
pt_start = (pb_end[0] - dz * math.tan(a), pt_start_z)
ct = (pt_start[0] - r * math.cos(a), ct_z)
x_sh = ct[0] # shoulder end (|x|) where top bend begins
inner = P["E1"] / 2 - 0.35 # start well inside the body
pts = dict(inner=inner, x_sh=x_sh, ct=ct, pt_start=pt_start, pb_end=pb_end, cb=cb, x_fb=x_fb, tip=tip, r=r)
return pts
def arc(wp, c, r, t0, t1, s, top):
"""bend from angle t0 to t1: top bend points (c.x + r sin t, c.z + r cos t); bottom (c.x - r cos t, c.z - r sin t).
full detail = true arc; low = 3 chords; superlow = 1 chord."""
pt = (lambda t: (c[0] + r * math.sin(t), c[1] + r * math.cos(t))) if top else (lambda t: (c[0] - r * math.cos(t), c[1] - r * math.sin(t)))
if P["detail"] == "full":
m, e = pt((t0 + t1) / 2), pt(t1)
return wp.threePointArc((s * m[0], m[1]), (s * e[0], e[1]))
n = 3 if P["detail"] == "low" else 1
for i in range(1, n + 1):
q = pt(t0 + (t1 - t0) * i / n); wp = wp.lineTo(s * q[0], q[1])
return wp
def make_lead(y, sign):
q = lead_profile_path(sign)
r, a = q["r"], math.radians(P["lead_angle"])
s = sign
zc = P["z_shoulder"]
# build path in XZ (workplane XZ: local x = global x, local y = global z)
wp = cq.Workplane("XZ").moveTo(s * q["inner"], zc).lineTo(s * q["x_sh"], zc)
# top bend: from (x_sh, zc) to pt_start, centre ct; mid angle
m = math.radians(90 - P["lead_angle"]) / 2 + math.radians(P["lead_angle"])
mid_t = (q["ct"][0] + r * math.sin(math.pi / 2 - (math.pi / 2 - a) / 2) , q["ct"][1] + r * math.cos(math.pi / 2 - (math.pi / 2 - a) / 2))
# parametric: top-bend arc points at angle phi from +z axis rotating outward: (ct.x + r sin phi, ct.z + r cos phi), phi 0 -> 90-a
phi_end = math.pi / 2 - a
pm = (q["ct"][0] + r * math.sin(phi_end / 2), q["ct"][1] + r * math.cos(phi_end / 2))
pe = (q["ct"][0] + r * math.sin(phi_end), q["ct"][1] + r * math.cos(phi_end))
wp = arc(wp, q["ct"], r, 0.0, phi_end, s, top=True)
wp = wp.lineTo(s * q["pb_end"][0], q["pb_end"][1])
# bottom bend: centre cb, from pb_end (angle a from -x... ) to (x_fb, c/2)
cb = q["cb"]
# points on bottom arc: (cb.x - r cos t, cb.z - r sin t), t from a -> 90deg
t_mid = (a + math.pi / 2) / 2
pm2 = (cb[0] - r * math.cos(t_mid), cb[1] - r * math.sin(t_mid))
wp = arc(wp, cb, r, a, math.pi / 2, s, top=False)
wp = wp.lineTo(s * q["tip"], P["c"] / 2)
path = wp.wire().val()
# profile: rectangle b (along y) x c (normal to path) at the path start, plane normal = path tangent (x)
start = cq.Vector(s * q["inner"], y, zc)
prof = cq.Workplane(cq.Plane(origin=start, xDir=(0, 1, 0), normal=(s, 0, 0))).rect(P["b"], P["c"])
return prof.sweep(cq.Workplane().add(path), transition="round" if P["detail"] == "full" else "right").val()
def body():
E1, D, A, A1, pz = P["E1"], P["D"], P["A"], P["A1"], P["parting"]
if P["detail"] == "superlow":
b = cq.Workplane("XY").workplane(offset=A1).rect(E1, D).extrude(A - A1)
p1 = P["pin1"]
dimple = cq.Workplane("XY").workplane(offset=A - p1["depth"]).center(p1["x"], p1["y"]).circle(p1["d"] / 2).extrude(p1["depth"] + 0.01)
return b.cut(dimple).val()
lo = cq.Workplane("XY").workplane(offset=A1).rect(E1 - 2 * P["bot_inset"], D - 2 * P["bot_inset"]) \
.workplane(offset=pz - A1).rect(E1, D).loft()
hi = cq.Workplane("XY").workplane(offset=pz).rect(E1, D) \
.workplane(offset=A - pz).rect(E1 - 2 * P["top_inset"], D - 2 * P["top_inset"]).loft()
b = lo.union(hi)
p1 = P["pin1"]
dimple = cq.Workplane("XY").workplane(offset=A - p1["depth"]).center(p1["x"], p1["y"]).circle(p1["d"] / 2).extrude(p1["depth"] + 0.01)
return b.cut(dimple).val()
def main(out):
n2 = P["N"] // 2
ys = [(n2 - 1) / 2 * P["e"] - i * P["e"] for i in range(n2)] # +y .. -y (pin 1 at +y)
leads = []
for i, y in enumerate(ys):
leads.append(make_lead(y, -1)) # pins 1..4 on -x, pin 1 at +y
for i, y in enumerate(reversed(ys)):
leads.append(make_lead(y, +1)) # pins 5..8 on +x, pin 5 at -y, pin 8 at +y
bd = body()
# trim the lead stubs that sit inside the body so body and pins do not overlap
pins = cq.Compound.makeCompound([l.cut(bd) for l in leads])
asm = cq.Assembly(name="SOIC-8_SN")
asm.add(bd, name="body", color=cq.Color(0.1, 0.1, 0.1))
asm.add(pins, name="pins", color=cq.Color(0.82, 0.82, 0.80))
asm.save(out, exportType="STEP")
print(json.dumps({"out": out, "params": P}, default=str))
if __name__ == "__main__":
main(sys.argv[1])