Solder coat only on the solder side of a THT joint
Build PCM package / build (push) Successful in 6s
Build PCM package / build (push) Successful in 6s
The pad face is wetted where the joint is: the protrusion side, opposite the component (already read from the owning footprint). The component-side pad face stays bare - contact_solder_buildups and tht_joint_buildups no longer coat both outer layers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -89,10 +89,11 @@ SWIG API. Requires KiCad **10.0.1+**.
|
|||||||
**single-layer runs too** (drill mouths perforate a lone plane).
|
**single-layer runs too** (drill mouths perforate a lone plane).
|
||||||
THT-pad copper and drills remain outside the model, but every
|
THT-pad copper and drills remain outside the model, but every
|
||||||
**populated THT pad** of the net carries its full **soldered
|
**populated THT pad** of the net carries its full **soldered
|
||||||
joint**: a solder-filled barrel (SAC305 core in parallel with the
|
joint** on its SOLDER side (opposite the component; the
|
||||||
plating), the average-thickness solder coat over a pad-diameter disc
|
component-side pad face stays bare): a solder-filled barrel (SAC305
|
||||||
on the outer layers, and the protruding-lead cone on the side
|
core in parallel with the plating), the average-thickness solder
|
||||||
opposite its footprint (see barrel contacts below). Whether a hole
|
coat over a pad-diameter disc, and the protruding-lead cone (see
|
||||||
|
barrel contacts below). Whether a hole
|
||||||
is a via or a THT pad, the owning footprint's side, and its **Do not
|
is a via or a THT pad, the owning footprint's side, and its **Do not
|
||||||
populate** flag are all read from KiCad — DNP pads stay plating-only
|
populate** flag are all read from KiCad — DNP pads stay plating-only
|
||||||
with no joint. At f > 0 the thickness scaling is applied
|
with no joint. At f > 0 the thickness scaling is applied
|
||||||
@@ -130,12 +131,14 @@ SWIG API. Requires KiCad **10.0.1+**.
|
|||||||
of the result (both contact models; verified against
|
of the result (both contact models; verified against
|
||||||
R = ρ/(π·t)·acosh(d/2a) for two circular contacts on a sheet). A
|
R = ρ/(π·t)·acosh(d/2a) for two circular contacts on a sheet). A
|
||||||
soldered **THT joint** additionally assumes the **hole is filled with
|
soldered **THT joint** additionally assumes the **hole is filled with
|
||||||
solder** (core in parallel with the plating) and the **pad face
|
solder** (core in parallel with the plating) and the **pad face on
|
||||||
carries an average-thickness solder coat** (`SOLDER_THICKNESS_UM`,
|
the solder side carries an average-thickness solder coat**
|
||||||
50 µm) over the modeled copper under the pad shape. The **clipped
|
(`SOLDER_THICKNESS_UM`, 50 µm) over the modeled copper under the pad
|
||||||
lead protrudes** `THT_LEAD_PROTRUSION_MM` (1.5 mm, 0 = off) on the
|
shape — the solder side is the side opposite the component (taken
|
||||||
side opposite the component (taken from the owning footprint;
|
from the owning footprint; assumed `B.Cu` if it cannot be found),
|
||||||
assumed `B.Cu` if it cannot be found) and a **solder cone** wraps
|
and the component-side pad face stays bare. There the **clipped
|
||||||
|
lead protrudes** `THT_LEAD_PROTRUSION_MM` (1.5 mm, 0 = off)
|
||||||
|
and a **solder cone** wraps
|
||||||
it: full protrusion height at the drill wall, tapering linearly to
|
it: full protrusion height at the drill wall, tapering linearly to
|
||||||
zero at the pad edge, applied as extra conduction-equivalent copper
|
zero at the pad edge, applied as extra conduction-equivalent copper
|
||||||
per cell. The tall solder column at the wall pulls the joint
|
per cell. The tall solder column at the wall pulls the joint
|
||||||
|
|||||||
+29
-24
@@ -105,8 +105,10 @@ class Electrode:
|
|||||||
lead/wire soldered into the hole), so the contact cells are the
|
lead/wire soldered into the hole), so the contact cells are the
|
||||||
copper ring at the drill wall, not the whole pad face. `solder`
|
copper ring at the drill wall, not the whole pad face. `solder`
|
||||||
additionally models a soldered THT joint: the hole is filled with
|
additionally models a soldered THT joint: the hole is filled with
|
||||||
solder and the pad face carries an average-thickness solder coat
|
solder and the pad face on the SOLDER side (protrusion_side,
|
||||||
(Problem.solder_thickness_nm over `polygons`)."""
|
opposite the component) carries an average-thickness solder coat
|
||||||
|
(Problem.solder_thickness_nm over `polygons`) plus the
|
||||||
|
protruding-lead cone."""
|
||||||
rect: Rect # bounding box (labels/summary)
|
rect: Rect # bounding box (labels/summary)
|
||||||
contact: str = "all"
|
contact: str = "all"
|
||||||
polygons: list[Polygon] | None = None
|
polygons: list[Polygon] | None = None
|
||||||
@@ -217,21 +219,23 @@ class Problem:
|
|||||||
|
|
||||||
|
|
||||||
def contact_solder_buildups(problem: Problem) -> list[str]:
|
def contact_solder_buildups(problem: Problem) -> list[str]:
|
||||||
"""Soldered THT-joint contacts: the pad face is covered in solder of
|
"""Soldered THT-joint contacts: the pad face on the SOLDER side (the
|
||||||
average thickness solder_thickness_nm. Adds one SurfaceBuildup per
|
protrusion side, opposite the component - the component-side face
|
||||||
outer layer for every `solder` electrode's pad shape (the buildup
|
stays bare) is covered in solder of average thickness
|
||||||
machinery intersects with actual copper at raster time). Returns the
|
solder_thickness_nm. Adds one SurfaceBuildup there for every
|
||||||
affected layer names. Called once when the problem is built."""
|
`solder` electrode's pad shape (the buildup machinery intersects
|
||||||
|
with actual copper at raster time). Returns the affected layer
|
||||||
|
names. Called once when the problem is built."""
|
||||||
included = {l.layer_name for l in problem.layers}
|
included = {l.layer_name for l in problem.layers}
|
||||||
outer = [n for n in ("F.Cu", "B.Cu") if n in included]
|
|
||||||
touched = []
|
touched = []
|
||||||
for e in problem.electrodes1 + problem.electrodes2:
|
for e in problem.electrodes1 + problem.electrodes2:
|
||||||
if not e.solder or not e.polygons:
|
if not e.solder or not e.polygons \
|
||||||
|
or e.protrusion_side not in included:
|
||||||
continue
|
continue
|
||||||
for name in outer:
|
problem.buildups.append(
|
||||||
problem.buildups.append(
|
SurfaceBuildup(layer_name=e.protrusion_side,
|
||||||
SurfaceBuildup(layer_name=name, polygons=list(e.polygons)))
|
polygons=list(e.polygons)))
|
||||||
touched.append(name)
|
touched.append(e.protrusion_side)
|
||||||
return sorted(set(touched))
|
return sorted(set(touched))
|
||||||
|
|
||||||
|
|
||||||
@@ -245,25 +249,26 @@ def _disc_polygon(x_nm: float, y_nm: float, r_nm: float,
|
|||||||
|
|
||||||
def tht_joint_buildups(problem: Problem) -> list[str]:
|
def tht_joint_buildups(problem: Problem) -> list[str]:
|
||||||
"""Solder coat of the net's populated STITCHING through-hole pads
|
"""Solder coat of the net's populated STITCHING through-hole pads
|
||||||
(ViaLink kind 'pad' with solder_filled): one pad-diameter disc per
|
(ViaLink kind 'pad' with solder_filled): one pad-diameter disc on
|
||||||
outer layer - the exact pad shape is unknown for non-contact pads,
|
the pad's SOLDER side (the protrusion side, opposite the component;
|
||||||
and the coat intersects the modeled copper at raster time anyway.
|
the component-side face stays bare). The exact pad shape is unknown
|
||||||
Contact pads are skipped: contact_solder_buildups already coats
|
for non-contact pads, and the coat intersects the modeled copper at
|
||||||
them with the exact pad shape. Returns the affected layer names."""
|
raster time anyway. Contact pads are skipped:
|
||||||
|
contact_solder_buildups already coats them with the exact pad
|
||||||
|
shape. Returns the affected layer names."""
|
||||||
included = {l.layer_name for l in problem.layers}
|
included = {l.layer_name for l in problem.layers}
|
||||||
outer = [n for n in ("F.Cu", "B.Cu") if n in included]
|
|
||||||
contacts = {e.center for e in problem.electrodes1 + problem.electrodes2
|
contacts = {e.center for e in problem.electrodes1 + problem.electrodes2
|
||||||
if e.drill_nm > 0 and e.center is not None}
|
if e.drill_nm > 0 and e.center is not None}
|
||||||
touched = []
|
touched = []
|
||||||
for v in problem.vias:
|
for v in problem.vias:
|
||||||
if v.kind != "pad" or not v.solder_filled \
|
if v.kind != "pad" or not v.solder_filled \
|
||||||
or v.pad_nm <= v.drill_nm or (v.x, v.y) in contacts:
|
or v.pad_nm <= v.drill_nm or (v.x, v.y) in contacts \
|
||||||
|
or v.protrusion_side not in included:
|
||||||
continue
|
continue
|
||||||
disc = _disc_polygon(v.x, v.y, v.pad_nm / 2.0)
|
disc = _disc_polygon(v.x, v.y, v.pad_nm / 2.0)
|
||||||
for name in outer:
|
problem.buildups.append(
|
||||||
problem.buildups.append(
|
SurfaceBuildup(layer_name=v.protrusion_side, polygons=[disc]))
|
||||||
SurfaceBuildup(layer_name=name, polygons=[disc]))
|
touched.append(v.protrusion_side)
|
||||||
touched.append(name)
|
|
||||||
return sorted(set(touched))
|
return sorted(set(touched))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -125,15 +125,21 @@ def test_solder_filled_barrel_resistance():
|
|||||||
|
|
||||||
def test_contact_solder_coat():
|
def test_contact_solder_coat():
|
||||||
"""A soldered THT contact adds an average-thickness solder buildup
|
"""A soldered THT contact adds an average-thickness solder buildup
|
||||||
over the pad face on the outer layers, lowering the spreading
|
over the pad face on its SOLDER side only (opposite the component),
|
||||||
resistance vs the bare barrel contact."""
|
lowering the spreading resistance vs the bare barrel contact."""
|
||||||
def prob():
|
def prob():
|
||||||
p = make_problem([(PLATE20, [])],
|
p = make_problem([(PLATE20, [])],
|
||||||
rect1_mm=(0, 0, 1, 20), rect2_mm=(19, 0, 20, 20))
|
rect1_mm=(0, 0, 1, 20), rect2_mm=(19, 0, 20, 20))
|
||||||
p.electrodes1 = [_barrel(10, 10, drill_mm=1.0, pad_mm=2.4,
|
p.electrodes1 = [_barrel(10, 10, drill_mm=1.0, pad_mm=2.4,
|
||||||
solder=True, polygons=[_disc(10, 10, 1.2)])]
|
solder=True, polygons=[_disc(10, 10, 1.2)])]
|
||||||
|
p.electrodes1[0].protrusion_side = "F.Cu"
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
# solder side not among the included layers -> no coat there
|
||||||
|
q = prob()
|
||||||
|
q.electrodes1[0].protrusion_side = "B.Cu"
|
||||||
|
assert contact_solder_buildups(q) == []
|
||||||
|
|
||||||
p = prob()
|
p = prob()
|
||||||
assert contact_solder_buildups(p) == ["F.Cu"]
|
assert contact_solder_buildups(p) == ["F.Cu"]
|
||||||
assert len(p.buildups) == 1 and p.buildups[0].layer_name == "F.Cu"
|
assert len(p.buildups) == 1 and p.buildups[0].layer_name == "F.Cu"
|
||||||
|
|||||||
Reference in New Issue
Block a user