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

+16 -2
View File
@@ -35,6 +35,8 @@ FP_CP125 = "Capacitor_THT:CP_Radial_D12.5mm_P5.00mm"
FP_HDR2 = "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical"
FP_KK8 = "Connector_Molex:Molex_KK-396_A-41791-0008_1x08_P3.96mm_Vertical"
FP_HOLE = "MountingHole:MountingHole_3.2mm_M3"
# Clip-on TO-220 heatsink, ~20 K/W, solder pin. The tab is live, so the pin sits on the collector net.
FP_HS = "plasma_dmd:Heatsink_AAVID_576802B03900G_NoCourtyard" # project lib, see plasma_dmd.pretty
def tb(n):
@@ -79,6 +81,7 @@ PARTS = [
("D5", "Device:D_Zener", "1N4760A 68V", FP_D41, {"K": "Q3_B", "A": "GND"}, 2),
("Q3", "Transistor_BJT:Q_NPN_BCE", "MJE15030", FP_TO220, {"C": "+90V_RAW", "B": "Q3_B", "E": "+65V"}, 2),
("C6", "Device:C", "0.1uF 500V", FP_CFILM, {"1": "+65V", "2": "GND"}, 2),
("HS1", "Mechanical:Heatsink_Pad", "Aavid 576802B03900G (Q3, live +90V)", FP_HS, {"1": "+90V_RAW"}, 2),
("R6", "Device:R", "330k", FP_R, {"1": "+65V", "2": "GND"}, 2),
# 4. Negative HV
("J6", "Connector:Screw_Terminal_01x02", "Isolated DC-DC in: VIN GND", tb(2),
@@ -103,6 +106,8 @@ PARTS = [
("Q6", "Transistor_BJT:Q_NPN_BCE", "MJE15030", FP_TO220, {"B": "Q6_B", "C": "Q6_C", "E": "-100V"}, 3),
("R13", "Device:R", "2k 5W", FP_R5W, {"1": "GND", "2": "Q6_C"}, 3),
("C9", "Device:C", "0.1uF 500V", FP_CFILM, {"1": "GND", "2": "-100V"}, 3),
("HS2", "Mechanical:Heatsink_Pad", "Aavid 576802B03900G (Q5, live -130V)", FP_HS, {"1": "-130V_RAW"}, 3),
("HS3", "Mechanical:Heatsink_Pad", "Aavid 576802B03900G (Q6, live Q6_C)", FP_HS, {"1": "Q6_C"}, 3),
# 5. Panel connector (Stern 520-5138 CN2 order; verify against your panel!)
("J2", "Connector_Generic:Conn_01x08", "Panel power (Stern CN2 order)", FP_KK8,
{"1": "-112V", "2": "-100V", "3": None, "4": "GND", "5": "GND", "6": "+5V", "7": "+12V_PANEL", "8": "+65V"}, 4),
@@ -216,7 +221,10 @@ def snap(v):
# N_LED1/N_LED2 sit a LED drop away from GND, so they stay in Default.
HV_NETS = ["+90V_RAW", "+65V", "HVP_MOD", "Q2_E", "REF_P", "Q3_B",
"-130V_RAW", "-112V", "-100V", "HVN_MOD", "Q4_E", "REF_N", "Q5_B", "REF_M", "Q6_B", "Q6_C"]
POWER_NETS = ["GND", "+12V", "+12V_IN", "+12V_HV", "+12V_HVP", "+12V_HVN", "+5V", "+12V_PANEL"]
# Track widths from IPC-2221 (1 oz outer copper): 1.5 mm carries ~3.2 A at 10 C rise, 1.0 mm ~2.4 A.
POWER3A_NETS = ["+12V", "+12V_IN"] # behind F1 (3 A)
# GND: 1.0 mm tracks plus the bottom-layer pour, which carries the 3 A return on the LV side
POWER_NETS = ["GND", "+12V_HV", "+12V_HVP", "+12V_HVN", "+5V", "+12V_PANEL"] # <= 2 A
def netclass(name, clearance, track, priority):
@@ -236,15 +244,21 @@ def write_project():
data["net_settings"] = {
"classes": [
netclass("Default", 0.25, 0.4, 2147483647),
netclass("Power", 0.3, 1.0, 1),
netclass("Power3A", 0.3, 1.5, 1),
netclass("Power", 0.3, 1.0, 2),
netclass("HV", 1.3, 0.6, 0) # HV <-> LV 3 mm comes from plasma_dmd_psu.kicad_dru,
],
"meta": {"version": 5},
"net_colors": None,
"netclass_assignments": None,
"netclass_patterns": [{"netclass": "HV", "pattern": "/" + n} for n in HV_NETS]
+ [{"netclass": "Power3A", "pattern": "/" + n} for n in POWER3A_NETS]
+ [{"netclass": "Power", "pattern": "/" + n} for n in POWER_NETS],
}
# The heatsink pins sit just behind the TO-220 tab, inside the transistor courtyard. The heatsinks
# are the only courtyard-less parts, so this check adds nothing beyond courtyards_overlap.
sev = data.setdefault("board", {}).setdefault("design_settings", {}).setdefault("rule_severities", {})
sev["pth_inside_courtyard"] = "ignore"
pro.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")