Heatsinks, wider 12 V tracks, design review; pause project

- PCB rev 0.1: Power3A class (1.5 mm) for the 12 V input path, Aavid
  clip-on heatsinks on Q3/Q5/Q6 (project footprint library), T-junction
  healing and via shrink in the router; DRC 0 errors
- Current-capacity and heatsink documentation
- CLAUDE.md with project decisions, workflow and gotchas
- docs/design-review.md: review of a module-only PSU (rev 0.2) and cost
  estimate
- Project paused for budget and safety reasons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-24 15:07:58 +02:00
1 parent 28c666506e
commit cdf24e0b4b
15 files changed
+5917 -4730

No files matched your search

+69 -19
View File
@@ -60,22 +60,23 @@ PLACE = {
"Q5": (81.0, 70.0, 0), # heatsink space above (tab side)
"Q6": (99.0, 70.0, 0),
"R13": (114.0, 62.0, 0), # 5 W, runs warm: keep clear of caps
"D7": (74.0, 80.5, 0),
"D8": (90.0, 79.0, 0),
"R12": (106.0, 79.0, 0),
"C8": (122.0, 79.0, 0),
"D6": (68.0, 89.0, 0),
"R10": (82.0, 89.0, 0),
"R9": (105.0, 89.0, 0),
"Q4": (120.0, 90.0, 0),
"R11": (130.0, 89.0, 0),
"C9": (68.0, 99.0, 0),
"C7": (92.0, 101.0, 0),
"R7": (106.0, 99.0, 0),
"R8": (122.0, 99.0, 0),
"LED2": (144.0, 99.0, 0),
"F5": (120.0, 111.0, 0),
"D7": (74.0, 83.5, 0),
"D8": (90.0, 83.0, 0),
"R12": (106.0, 83.0, 0),
"C8": (122.0, 83.0, 0),
"D6": (68.0, 93.0, 0),
"R10": (82.0, 93.0, 0),
"R9": (105.0, 93.0, 0),
"Q4": (120.0, 94.0, 0),
"R11": (130.0, 93.0, 0),
"C9": (68.0, 103.0, 0),
"C7": (92.0, 105.0, 0),
"R7": (106.0, 103.0, 0),
"R8": (122.0, 103.0, 0),
"LED2": (144.0, 103.0, 0),
"F5": (120.0, 113.5, 0),
"J7": (70.0, 118.5, 0), # NCH6100HV #2 output
# ---- heatsinks: see heatsink_for() ------------------------------------------------
# ---- mounting holes ------------------------------------------------------------
"H1": (4.0, 4.0, 0),
"H2": (W - 4.0, 4.0, 0),
@@ -83,13 +84,26 @@ PLACE = {
"H4": (W - 4.0, H - 4.0, 0),
}
HEATSINKS = {"HS1": "Q3", "HS2": "Q5", "HS3": "Q6"}
def heatsink_for(q):
"""Aavid 576802B03900G on a TO-220-3_Vertical at rotation 0 (tab towards -y)."""
x, y, _ = PLACE[q]
return (x + 2.54, y - 3.6, 270)
for _hs, _q in HEATSINKS.items():
PLACE[_hs] = heatsink_for(_q)
SILK = [
# (text, x, y, size, layer)
("PLASMA DMD PSU rev 0.1", 24.0, 102.0, 1.5, "F.SilkS"),
("J1 12V IN", 13.5, 13.6, 1.0, "F.SilkS"),
("J3 MINI560", 31.6, 13.6, 1.0, "F.SilkS"),
("J8 ESP32", 52.1, 13.6, 1.0, "F.SilkS"),
("J5 HV MOD 1", 71.1, 13.6, 1.0, "F.SilkS"),
("J5 HV MOD 1", 68.0, 13.6, 1.0, "F.SilkS"),
("J4 RELAY MODULE", 21.2, 111.2, 1.0, "F.SilkS"),
("J6 ISO DC-DC IN", 41.5, 111.2, 1.0, "F.SilkS"),
("J7 HV MOD 2 OUT", 72.5, 111.2, 1.0, "F.SilkS"),
@@ -109,6 +123,12 @@ def pt(x, y):
return pcbnew.VECTOR2I(mm(OX + x), mm(OY + y))
def lib_dir(lib):
"""Project library (plasma_dmd.pretty next to the board) or a KiCad standard library."""
local = HERE / f"{lib}.pretty"
return local if local.exists() else FP_DIR / f"{lib}.pretty"
FP_DIR = Path(pcbnew.__file__).parents[3] / "share/kicad/footprints" # <KiCad>/10.0/share/...
@@ -131,7 +151,7 @@ def sync_footprints(board):
if not fpid or fpid == old.GetFPIDAsString():
continue
lib, name = fpid.split(":")
new = pcbnew.FootprintLoad(str(FP_DIR / f"{lib}.pretty"), name)
new = pcbnew.FootprintLoad(str(lib_dir(lib)), name)
new.SetFPID(pcbnew.LIB_ID(lib, name))
new.SetReference(ref)
new.SetValue(old.GetValue())
@@ -148,8 +168,33 @@ def sync_footprints(board):
print("swapped", ref, "->", fpid)
def add_missing_footprints(board):
"""Add footprints for schematic symbols that are not on the board yet (like Update PCB, F8)."""
import gen_kicad
_, uuids = gen_kicad.existing_uuids()
present = {f.GetReference() for f in board.GetFootprints()}
for ref, lib_id, value, fpid, conns, _g in gen_kicad.PARTS:
if ref in present:
continue
lib, name = fpid.split(":")
fp = pcbnew.FootprintLoad(str(lib_dir(lib)), name)
fp.SetFPID(pcbnew.LIB_ID(lib, name))
fp.SetReference(ref)
fp.SetValue(value)
fp.SetPath(pcbnew.KIID_PATH("/" + uuids[ref]))
fp.SetSheetname("/")
fp.SetSheetfile("plasma_dmd_psu.kicad_sch")
board.Add(fp)
for pad in fp.Pads():
net = conns.get(pad.GetNumber())
if net:
pad.SetNet(board.FindNet("/" + net))
print("added", ref, "->", fpid)
def main():
board = pcbnew.LoadBoard(str(BOARD))
add_missing_footprints(board)
sync_footprints(board)
# clean previous layout output (outline, our silkscreen, tracks, vias, zones)
@@ -175,9 +220,14 @@ def main():
f.Flip(f.GetPosition(), False)
f.SetOrientationDegrees(rot)
f.SetPosition(pt(x, y))
f.SetLocked(ref.startswith(("J", "H")))
if ref.startswith(("J", "H")):
fixed = ref.startswith("J") or (ref.startswith("H") and ref[1:].isdigit())
f.SetLocked(fixed)
if fixed:
f.Reference().SetVisible(False) # connectors are labelled by the SILK texts
if ref in HEATSINKS.values():
f.Reference().SetPosition(pt(x + 2.54, y + 3.2)) # below the pins, clear of the heatsink pin
if ref in HEATSINKS:
f.Reference().SetPosition(pt(x + 9.5, y + 2.0)) # beside the heatsink
# board outline with 2 mm corner radius
r = 2.0