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:
1 parent
28c666506e
commit
cdf24e0b4b
15 files changed
+5917
-4730
No files matched your search
@@ -0,0 +1,72 @@
|
||||
# Plasma DMD — notes for Claude
|
||||
|
||||
The goal: drive a second-hand 128×32 pinball **plasma** DMD panel standalone from a **Wemos D1 mini ESP32**,
|
||||
powered from **12 V DC only**. The panel was originally on a Williams WPC controller and was bought on
|
||||
2021-09-18 for €37. No original controller or PSU came with it.
|
||||
|
||||
## Status (keep this section up to date)
|
||||
|
||||
- **PAUSED (2026-10-24)** at the user's request, because of the cost and the safety risk. If resuming, consider the
|
||||
cheaper, no-high-voltage alternative discussed: a 128×32 HUB75 LED matrix driven by the same ESP32 (about €30–50).
|
||||
|
||||
- Design docs in `docs/`, PSU schematic and routed PCB rev 0.1 in `hardware/kicad/`, firmware skeleton in `firmware/`.
|
||||
- Not yet known: the panel's make and part number, its power and data connector pinouts, and the
|
||||
polarity of its control signals. **J2's pinout is a placeholder (Stern CN2 order).**
|
||||
- Nothing is built or tested on real hardware.
|
||||
- **Proposed rev 0.2: module-only PSU** (no linear regulators or heatsinks), reviewed in `docs/design-review.md`.
|
||||
It's waiting on the must-fix items M1–M4 there. The schematic and PCB are still rev 0.1.
|
||||
|
||||
## Design decisions (don't silently change these)
|
||||
|
||||
- **PSU:**
|
||||
- A 12 V brick feeds a Mini560 (5 V) and two NCH6100HV boost modules: one at 90 V, and one at
|
||||
130 V fed through an isolated DC-DC, so its output floats and becomes −130 V.
|
||||
- Those raw rails feed a copy of the regulator section of the Stern 520-5138-00 (zener-referenced
|
||||
followers), giving about +65 V, −112 V and −100 V.
|
||||
- The zeners set the panel voltages, not the module trim pots.
|
||||
- **Modules sit off-board** on screw terminals. The PCB is a carrier board.
|
||||
- **Safety first:**
|
||||
- A relay switches the 12 V feed to the HV modules.
|
||||
- The relay is driven by a hardware charge-pump watchdog, toggled by GPIO21 once per scanned row
|
||||
(about 2 kHz). If toggling stops, the HV drops.
|
||||
- Bleeders and fuses on every HV rail.
|
||||
- **Clearances** (`hardware/kicad/plasma_dmd_psu.kicad_dru`):
|
||||
- 3 mm between HV and LV, 1.3 mm between HV nets.
|
||||
- Documented exceptions only inside or next to transistor and connector pins.
|
||||
- **TO-220 tabs and heatsinks are live.** Each heatsink pin is on its collector net.
|
||||
- **Track widths** come from IPC-2221 against the fuse currents (see `hardware/kicad/README.md`).
|
||||
|
||||
## Workflow and tools
|
||||
|
||||
- KiCad 10: `C:/Users/patri/AppData/Local/Programs/KiCad/10.0/bin/` (`kicad-cli.exe`, and `python.exe` with `pcbnew`).
|
||||
- Freerouting 2.5.0 jar: `tools/freerouting/`, git-ignored. It needs **Java 25**:
|
||||
`C:/Program Files/Eclipse Adoptium/jre-25.0.4.101-hotspot/bin/java.exe`.
|
||||
- Pipeline (KiCad must be closed while the scripts run):
|
||||
1. `python hardware/kicad/gen_kicad.py` writes the schematic and the project net classes from `PARTS`.
|
||||
It **keeps symbol UUIDs**, so the PCB stays linked.
|
||||
2. `python hardware/kicad/check_netlist.py` verifies every pin's net through kicad-cli.
|
||||
3. `<kicad python> hardware/kicad/layout_pcb.py` adds, swaps and places footprints from `PLACE` and draws
|
||||
the outline and silkscreen. **It deletes tracks and zones.**
|
||||
4. `<kicad python> hardware/kicad/route_pcb.py [passes]` runs Freerouting:
|
||||
- It routes with oversized tracks and vias, then shrinks them, which yields the clearances.
|
||||
- It adds J2 escape stubs and heals T-junctions.
|
||||
- It adds the bottom GND pour.
|
||||
5. `kicad-cli pcb drc --schematic-parity --refill-zones hardware/kicad/plasma_dmd_psu.kicad_pcb`.
|
||||
The target is 0 errors. The 9 silkscreen warnings (TO-220 outline over the heatsink pins) are known.
|
||||
- Once the user starts hand-editing the schematic or PCB in KiCad, stop regenerating with the scripts.
|
||||
|
||||
## Gotchas learned the hard way
|
||||
|
||||
- Bash heredocs passed through the tool can collapse `\\` and `\n` escapes. For Python with regexes
|
||||
or escapes, use the Write/Edit tools, not `python - <<EOF`.
|
||||
- pcbnew: use `board.Delete(item)`, not `Remove` (Remove leads to segfaults). `GetTracks()` throws
|
||||
`TypeError` when empty. Copy coordinates out of an item before deleting it.
|
||||
- KiCad schematic text needs `\n` escapes, not raw newlines, or the file won't load.
|
||||
- Freerouting puts fixed stubs back in the SES (duplicates). Its SES can silently omit a net when routing
|
||||
is incomplete, so always check DRC for unconnected pads.
|
||||
- MKDS terminal blocks: wire entry is on the footprint's +y side.
|
||||
|
||||
## User preferences
|
||||
|
||||
- Commit only when the user asks. Initial commit `dbc1f00`, PCB rev 0.1 `28c6665`. The repo has no remote.
|
||||
- Before changing a file the user may have edited, check their edits first and keep them.
|
||||
@@ -24,11 +24,16 @@ Stern/Sega display PSU (520-5138-00). The original transformer and AC front end
|
||||
| [docs/safety.md](docs/safety.md) | Safety design (HV watchdog, fusing, layout, bring-up procedure) |
|
||||
| [docs/esp32-interface.md](docs/esp32-interface.md) | ESP32 pin plan, 74HCT245 level shifter, panel signals |
|
||||
| [docs/bom.md](docs/bom.md) | Bill of materials |
|
||||
| [docs/design-review.md](docs/design-review.md) | Design review of the module-only PSU (rev 0.2) and cost estimate |
|
||||
| [reference/](reference/) | Original Stern 520-5138-00 display PSU schematic |
|
||||
| [firmware/](firmware/) | PlatformIO project for the ESP32 |
|
||||
| [CLAUDE.md](CLAUDE.md) | Project notes for Claude: decisions, workflow, tool paths, gotchas |
|
||||
|
||||
## Status
|
||||
|
||||
> **Paused (2026-10-24)** because of budget and safety concerns. See [docs/design-review.md](docs/design-review.md)
|
||||
> for the latest review and cost estimate.
|
||||
|
||||
- [ ] Identify panel: manufacturer/part number from its label
|
||||
- [ ] Map the panel's power connector pins (WPC panel ≠ Stern CN2 pinout) and check if +12 V is used
|
||||
- [ ] Map the panel's 14-pin data connector
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ Prices are rough estimates.
|
||||
| 2 | 330 kΩ ½ W | R6, R11 | R6, R7 |
|
||||
| 1 | 2 kΩ 5 W | R13 | R8 |
|
||||
| 3 | 0.1 µF 500 V film | C6, C8, C9 | C3, C4, C7 |
|
||||
| 3 | TO-220 heatsink | for Q3, Q5, Q6 | |
|
||||
| 3 | Aavid 576802B03900G clip-on heatsink (~20 K/W), **live**: one per transistor, must not touch | HS1–HS3 on Q3, Q5, Q6 | |
|
||||
|
||||
## Additions
|
||||
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
# Design review and cost estimate
|
||||
|
||||
*Review date 2026-10-24. Covers the proposed **module-only** PSU (rev 0.2), compared with the routed rev 0.1
|
||||
board, which used linear regulators copied from the Stern supply.*
|
||||
|
||||
## Verdict
|
||||
|
||||
The module-only design is **a better fit for this project** than rev 0.1:
|
||||
|
||||
- No linear losses, no transistors, no heatsinks.
|
||||
- About 20 fewer custom PCB parts.
|
||||
- A board roughly half the size.
|
||||
- It also makes the −100 V / −112 V relationship more robust (see §3).
|
||||
|
||||
It is **not ready to build yet**. Four must-fix items (§8) block it: the unknown panel pinout, a weak point
|
||||
in the watchdog drive, TVS clamp levels that depend on the panel's ratings, and buying genuine modules.
|
||||
|
||||
Estimated cost: **about €90–190, typically about €120** (§9).
|
||||
|
||||
---
|
||||
|
||||
## 1. Scope and assumptions
|
||||
|
||||
- **Panel:** a 128×32 plasma DMD from a WPC game. Make, part number and pinouts are **still unknown**.
|
||||
- **Rails needed** (PinWiki / Stern supply): about +62–65 V, −112 V, and −100 V. The −100 V rail is a
|
||||
fixed 12 V offset from −112 V. Plus +5 V logic, and +12 V if the panel uses it.
|
||||
- **Currents are unknown.** The design is sized against the fuse limits. Real currents get measured at bring-up.
|
||||
- **Use:** a hobby device in a closed enclosure. It is not a certified product.
|
||||
|
||||
## 2. Architecture (rev 0.2, module-only)
|
||||
|
||||
```
|
||||
12 V brick (certified) ─F1 3A─┬─ TVS ─ Mini560 ──────────────── +5 V ─PTC─ TVS ─► ESP32, 74HCT245, panel logic
|
||||
├──────────────────────────────── +12 V ─JP1─► panel (if used)
|
||||
└─ relay (watchdog) ─ +12V_HV ─┬─F2─ 45–390 V boost (65 V) ─F4─ RC ─ TVS ─► +65 V
|
||||
└─F3─ iso 12→12 V ─ NCH6100HV (112 V, + to GND)
|
||||
─F5─ RC ─ TVS ─► −112 V
|
||||
└─ B1212S stacked on −112 V ─ min-load ─► −100 V
|
||||
```
|
||||
|
||||
## 3. Rail-by-rail review
|
||||
|
||||
| Rail | Source | Regulation | Protection | Findings |
|
||||
|---|---|---|---|---|
|
||||
| +5 V | Mini560, fixed 5 V | Good (buck, fixed) | Add a PTC (about 1.5 A) and an SMBJ5.0A | Without the TVS, an HV-to-5V fault reaches the ESP32 and USB |
|
||||
| +12 V | Brick, via F1 | Brick | F1 3 A, P6KE15A | OK |
|
||||
| +65 V | 45–390 V boost board (8–32 V in, ≤0.2 A, 40 W) | Regulated, but uses **burst mode** at light load (jumpy ripple) | F4 100 mA, TVS clamp at about 72–78 V, RC filter, bleeder | Module is oversized but the most common one that reaches 65 V. **Bench-test the ripple and the no-load voltage** before connecting the panel. |
|
||||
| −112 V | Isolated 12→12 V (about 6 W) + NCH6100HV at 112 V, floating (+ to GND) | NCH6100HV regulates | F5 100 mA, TVS clamp (P6KE130A class, check against the panel maximum), RC, bleeder | The 6 W isolated converter limits this rail to about **40–45 mA**. Check against the measured current. |
|
||||
| −100 V | B1212S-1W stacked: − on −112 V, + gives −100 V | **Unregulated**, about ±10% → offset 10.8–13.2 V | 15 V zener across it, minimum-load resistor (about 10% load) | **Advantage:** the 12 V offset holds during start-up and shutdown too, because both rails move together. Rev 0.1 couldn't guarantee that. Consider a regulated 1–2 W isolated module if the offset tolerance matters. |
|
||||
|
||||
Current direction checked: in the original circuit, Q6 *sourced* current into the −100 V node from GND, and the
|
||||
panel returns it at −112 V. The B1212S sources current from its + output into −100 V, and it returns to its −
|
||||
output at −112 V. Same direction, so the B1212S is never back-driven.
|
||||
|
||||
## 4. Safety review
|
||||
|
||||
Under IEC 62368-1 (AV/ICT equipment), anything above 120 V DC is class **ES3**, the highest-hazard class.
|
||||
This design has −112 V, and up to about 180 V between +65 V and −112 V.
|
||||
|
||||
| # | Topic | Status |
|
||||
|---|---|---|
|
||||
| 4.1 | Mains isolation | OK. Provided by the certified brick (Class II). |
|
||||
| 4.2 | HV and LV **share one ground** (same as the original pinball supplies) | Only the 3 mm spacing (basic insulation, about 2.5 mm needed at 250 V, pollution degree 2) protects the 5 V side. **Needs:** the TVS on +5 V (a fault blows F4/F5), and no USB connected while HV is on, or a USB isolator. |
|
||||
| 4.3 | Exposed HV | The HV modules (including the floating isolated converter output and the NCH6100HV at −112 V) and **the panel's own driver boards** are live. **Needs a tool-only enclosure with HV labels.** |
|
||||
| 4.4 | Fault current | Each HV rail has a 100 mA fast fuse, and the modules limit current. A TVS clamp plus fuse turns a runaway module into a blown fuse, not a damaged panel. |
|
||||
| 4.5 | Stored charge | Bleeders discharge the rails to below 60 V within about 2 s. |
|
||||
| 4.6 | Fail-safe HV enable | The relay is normally open and driven by the watchdog. **But see 6.1: the drive is weak.** |
|
||||
| 4.7 | Live heatsinks | **Gone** in rev 0.2 (no linear regulators). |
|
||||
| 4.8 | Counterfeit modules | The NCH6100HV designer warns that the fakes are poor quality and **a fire hazard**. Buy from an authorized seller. |
|
||||
|
||||
## 5. Thermal and efficiency
|
||||
|
||||
| | rev 0.1 (linear) | rev 0.2 (modules) |
|
||||
|---|---|---|
|
||||
| Regulator losses at 20 mA per rail | about 0.5 W (Q3) + 0.4 W (Q5) + up to 2 W (Q6/R13) | 0 (only about 20 mW in the RC filters) |
|
||||
| Worst case (fuse-limited) | about 10 W of heat, plus 3 heatsinks | Only module losses |
|
||||
| Total heat, typical | about 4–6 W | about 1–3 W (boost modules at about 80–88% efficiency, isolated chain at about 70%) |
|
||||
| Heatsinks / active cooling | 3 heatsinks, no fan | **None needed**. Ventilation slots are enough. |
|
||||
|
||||
Power budget, typical: about 3–5 W for the panel HV, about 1–2 W for panel logic and the ESP32, plus losses.
|
||||
That's about **8–12 W from the brick (≈ 1 A)**. The 36 W / 3 A brick has plenty of margin.
|
||||
|
||||
## 6. Control and firmware
|
||||
|
||||
1. **The watchdog gate drive is marginal (must fix).**
|
||||
- The charge pump runs from the ESP32's 3.3 V output. After two diode drops it gives about 2.1–2.3 V at the 2N7000 gate.
|
||||
- The 2N7000's turn-on threshold ranges from 0.8 to **3.0 V**. Some parts will never switch the relay module (about 5–10 mA).
|
||||
- That fails safe (the HV stays off), but it isn't reliable.
|
||||
- **Fix (no extra parts):** feed the charge pump from a spare 74HCT245 channel, which swings 5 V. That gives about 3.8 V at the gate.
|
||||
- **Alternative:** replace the 2N7000 with a 2N3904/BC547 and a 10 kΩ base resistor.
|
||||
2. The watchdog drop-out is about 1 s (C4 × R1). Using C4 = 1 µF gives about 0.1 s, which is less time with a stuck row on the panel.
|
||||
3. **GPIO16/17** are free on ESP32-WROOM modules, but are used for PSRAM on WROVER modules. Check which module your Wemos board has.
|
||||
4. The firmware scans in a busy loop for now. Wi-Fi interrupts will cause jitter and flicker. Later, move the scanning to a high-priority task, or to I2S/DMA output.
|
||||
5. The panel signal polarities (enable, latch, row data) are still unverified.
|
||||
|
||||
## 7. PCB and mechanics
|
||||
|
||||
- **rev 0.1** (170 × 125 mm, routed, DRC clean) is obsolete if rev 0.2 is adopted.
|
||||
- **rev 0.2 estimate:** about 100 × 80 mm.
|
||||
- On the board: terminal blocks, 5 fuses, TVS clamps, RC filters, bleeders, the watchdog, the 74HCT245, the B1212S (SIP-4) and the panel connector.
|
||||
- Off the board: the Mini560, the boost modules, the isolated converter and the relay module.
|
||||
- Keep the 3 mm HV/LV spacing, the DRC rules and the routing scripts. They carry over.
|
||||
- Mount the modules on standoffs inside the enclosure, keep the HV wires short, and give each module its own fuse.
|
||||
|
||||
## 8. Findings
|
||||
|
||||
| ID | Priority | Finding | Action |
|
||||
|---|---|---|---|
|
||||
| M1 | **Must** | The panel make, power and data pinouts are unknown. J2 follows the Stern CN2 order. | Photograph the label and connectors and map every pin. Wrong wiring can destroy the panel. |
|
||||
| M2 | **Must** | The watchdog gate drive is marginal (6.1). | Drive the charge pump from the 74HCT245 (5 V), or use an NPN transistor. |
|
||||
| M3 | **Must** | The TVS clamp levels depend on the panel's maximum voltages. | Choose them once the panel is identified. Clamp above the setpoint, below the panel maximum. |
|
||||
| M4 | **Must** | Counterfeit NCH6100HV modules are a fire hazard. | Buy from an authorized seller. |
|
||||
| S1 | Should | +5 V has no fuse or clamp. | Add a PTC of about 1.5 A and an SMBJ5.0A. |
|
||||
| S2 | Should | Shared ground with USB. | No USB while HV is on, or use a USB isolator. |
|
||||
| S3 | Should | Exposed HV on the modules and the panel. | Tool-only, vented enclosure with HV labels. |
|
||||
| S4 | Should | The B1212S is unregulated. | Fit a minimum-load resistor and a 15 V zener, or use a regulated isolated module. |
|
||||
| S5 | Should | Module quality varies (burst mode, no-load overshoot). | Bench-test each module on a dummy load before connecting the panel. |
|
||||
| S6 | Should | The 6 W isolated converter limits −112 V to about 40–45 mA. | Measure the panel current. If it's higher, use a 10 W converter. |
|
||||
| N1 | Nice | 1 s watchdog drop-out. | C4 = 1 µF for about 0.1 s. |
|
||||
| N2 | Nice | GPIO16/17 on WROVER modules. | Check the module type. |
|
||||
| N3 | Nice | Scan jitter with Wi-Fi. | Scan from a high-priority task or I2S/DMA. |
|
||||
| N4 | Nice | RC filter values are a guess. | Tune after measuring with a scope. |
|
||||
| N5 | Nice | Extra margin on the HV side. | Conformal coating. |
|
||||
|
||||
## 9. Cost estimate (rev 0.2)
|
||||
|
||||
Prices are rough, as of October 2026, in euros including VAT, for a single unit. AliExpress items vary.
|
||||
|
||||
### Modules and power
|
||||
|
||||
| Item | Qty | Estimate |
|
||||
|---|---|---|
|
||||
| 12 V 3 A brick, certified (Mean Well GST36E12 / GST36B12) | 1 | €17–25 |
|
||||
| Mini560 5 V buck | 1 | €1–2 |
|
||||
| 45–390 V adjustable boost module (+65 V) | 1 | €6–12 |
|
||||
| NCH6100HV, **genuine** (−112 V) | 1 | €8–20 |
|
||||
| Isolated DC-DC 12→12 V, 6 W, regulated (Mornsun YMD-6WR3 class) | 1 | €7–10 |
|
||||
| B1212S-1W isolated (−100 V offset) | 1 | €1–3 (€4–6 for a regulated type) |
|
||||
| Relay module, 5 V, opto | 1 | €1–2 |
|
||||
| Wemos D1 mini ESP32 | 1 | €4–8 |
|
||||
| **Subtotal** | | **€45–84** |
|
||||
|
||||
### Board parts (Mouser / Farnell / TME / LCSC)
|
||||
|
||||
| Item | Estimate |
|
||||
|---|---|
|
||||
| 5 × 5×20 mm fuse holder + fuses (3 A T, 2 × 1 A T, 2 × 100 mA F) + spares | €5–8 |
|
||||
| TVS: P6KE15A, SMBJ5.0A, HV clamps × 2, 15 V zener | €2–3 |
|
||||
| PTC 1.5 A | €0.5 |
|
||||
| RC filters: 47 Ω 1 W + 22 µF/250 V, × 2–3 | €3–4 |
|
||||
| Bleeders, minimum load, 2 LEDs | €1–2 |
|
||||
| Watchdog parts (diodes, caps, transistor, resistors) | €1 |
|
||||
| 74HCT245 + socket + decoupling | €1–2 |
|
||||
| Terminal blocks × 7–8 (clone vs Phoenix) | €4–10 |
|
||||
| Molex KK-396 8-pin header + housing + crimps | €2–4 |
|
||||
| Header, jumper, standoffs, screws | €2 |
|
||||
| **Subtotal** | **€22–37** |
|
||||
|
||||
### Build
|
||||
|
||||
| Item | Estimate |
|
||||
|---|---|
|
||||
| PCB, 5 pcs, 2-layer, about 100 × 80 mm (JLCPCB/PCBWay) + shipping | €10–25 |
|
||||
| Vented plastic enclosure | €8–15 |
|
||||
| Wire, ferrules, heat-shrink, 14-pin IDC ribbon for the panel data | €5–10 |
|
||||
| Shipping / small-order fees (distributors, AliExpress) | €0–20 |
|
||||
| **Subtotal** | **€23–70** |
|
||||
|
||||
### Total
|
||||
|
||||
| | Low | Typical | High |
|
||||
|---|---|---|---|
|
||||
| **rev 0.2, module-only** | **≈ €90** | **≈ €120** | **≈ €190** |
|
||||
| rev 0.1, linear (for comparison) | +€15–25 for transistors, zeners, 3 heatsinks, 5 W resistor, plus about €5–15 for the larger PCB | | |
|
||||
| Original route (replacement DMD PSU board) | €50–170 for the board, **plus** an AC transformer and mains wiring | | |
|
||||
|
||||
## 10. Next steps
|
||||
|
||||
1. Identify the panel (M1). That unblocks M3 and the J2 pinout.
|
||||
2. Order the modules (genuine NCH6100HV, M4). Bench-test each one on a dummy load (S5).
|
||||
3. Update the schematic, PCB, BOM and docs to rev 0.2, including M2, S1 and S4.
|
||||
4. First power-up: series resistors and current measurements (S6). Then tune the RC filters (N4).
|
||||
|
||||
## Sources
|
||||
|
||||
- NCH6100HV price and counterfeit warning: [eBay listing](https://www.ebay.de/itm/296810265488), [neonixie-l (designer)](https://groups.google.com/g/neonixie-l/c/e8nm73UTEyk/m/e-skGAfqBAAJ), [Elecrow NCH8200HV](https://www.elecrow.com/nch8200hv-nixie-dc-dc-high-voltage-boost-power-converter.html)
|
||||
- 45–390 V boost module: [AR5388 datasheet](https://uelectronics.com/wp-content/uploads/2026/07/AR5388-Convertidor-Boost-8-32V-a-45-390V-5A-Datasheet.pdf), [Sigmanortec](https://sigmanortec.ro/en/voltage-booster-module-zvs-dc-dc-45-390v-adjustable)
|
||||
- B1212S-1W: [DigiKey B1212S-1WR3](https://digikey.cn/en/products/detail/mornsun-america-llc/B1212S-1WR3/13968708), [Robu](https://st.robu.in/product/b1212s-1wr3-dc-power-supply-module-1d1212-dc-dc-isolation-module-12v-to-12v1w-isolation-module)
|
||||
- Mornsun 6 W isolated: [smbom URA2412YMD-6WR3](https://www.smbom.com/product/ura2412ymd-6wr3-3774098), [YMD-6WR3 datasheet](https://cdn.shopify.com/s/files/1/1180/9692/files/URA_YMD-6WR3.pdf)
|
||||
- Mean Well GST36: [Farnell GST36E12](https://ie.farnell.com/mean-well/gst36e12-p1j/adaptor-ac-dc-12v-3a/dp/2815899), [RS GST36B12](https://de.rs-online.com/web/p/steckernetzteile/1176146)
|
||||
- Rail voltages: [PinWiki Williams WPC](https://www.pinwiki.com/wiki/index.php?title=Williams_WPC)
|
||||
@@ -29,6 +29,9 @@ Wire the isolated DC-DC output straight to NCH6100HV #2's input, off-board. Both
|
||||
| `check_netlist.py` | Exports the netlist with kicad-cli and checks every pin against `PARTS` |
|
||||
| `plasma_dmd_psu.kicad_sch` | Generated schematic (labels-per-pin style: connectivity is by net label) |
|
||||
| `plasma_dmd_psu.pdf` | Plot of the schematic |
|
||||
| `plasma_dmd.pretty/`, `fp-lib-table` | Project footprint library (courtyard-less heatsink) |
|
||||
| `layout_pcb.py`, `route_pcb.py` | Placement and autorouting scripts (see below) |
|
||||
| `plasma_dmd_psu.kicad_dru` | Custom clearance rules |
|
||||
|
||||
```bash
|
||||
python hardware/kicad/gen_kicad.py
|
||||
@@ -43,7 +46,8 @@ stop using the generator once you start editing the schematic in KiCad.
|
||||
| Class | Nets | Clearance | Track |
|
||||
|---|---|---|---|
|
||||
| HV | +90V_RAW, +65V, −130V_RAW, −112V, −100V, HVP/HVN_MOD and all regulator internals | 1.3 mm (3.0 mm to LV via custom rules) | 0.6 mm |
|
||||
| Power | GND, +12V*, +5V, +12V_PANEL | 0.3 mm | 1.0 mm |
|
||||
| Power3A | +12V_IN, +12V (behind the 3 A fuse F1) | 0.3 mm | 1.5 mm |
|
||||
| Power | GND (plus the bottom pour), +12V_HV/HVP/HVN, +5V, +12V_PANEL | 0.3 mm | 1.0 mm |
|
||||
| Default | Watchdog signals, LED cathode/anode nodes | 0.25 mm | 0.4 mm |
|
||||
|
||||
## PCB (rev 0.1)
|
||||
@@ -58,10 +62,49 @@ DRC is clean: 0 violations, 0 unconnected pads, schematic parity OK.
|
||||
A dashed silkscreen line marks the boundary, with DANGER text on both sides.
|
||||
- **J2 (panel)** straddles the boundary. Its LV pins (GND, +5V, +12V) leave towards the left, its HV pins towards the right.
|
||||
- **Terminal blocks** sit on the top and bottom edges, with wire entries facing outwards.
|
||||
- **Heatsink space** is left above Q3, Q5 and Q6.
|
||||
- **Heatsinks** HS1–HS3 (Aavid 576802B03900G clip-on, about 20 K/W) sit on Q3, Q5 and Q6.
|
||||
|
||||
> ⚠️ **The TO-220 tabs are live.** Q3's tab is at +90 V, Q5's at −130 V and Q6's at Q6_C.
|
||||
> Give each transistor its own heatsink, keep the heatsinks apart, and don't let them touch anything.
|
||||
> ⚠️ **The heatsinks are live.** Each one clips straight onto its transistor's tab: HS1 is at +90 V,
|
||||
> HS2 at −130 V and HS3 at Q6_C. That's why each heatsink's solder pin is on the collector net,
|
||||
> so the HV clearance rules also protect it. Never let the heatsinks touch each other or anything else.
|
||||
|
||||
### Heatsinks
|
||||
|
||||
- **Footprint:** `plasma_dmd:Heatsink_AAVID_576802B03900G_NoCourtyard` in the project library
|
||||
`plasma_dmd.pretty`. It's a copy of KiCad's footprint with the courtyard and silkscreen outline
|
||||
removed, because the clip wraps around the transistor and would always overlap its courtyard.
|
||||
The outline is kept on F.Fab, and the footprint is marked "exempt from courtyard requirement".
|
||||
- **Placement:** `layout_pcb.py` places each heatsink relative to its transistor (`heatsink_for()`),
|
||||
with its tab face on the TO-220 tab.
|
||||
- **Dissipation (estimate):** about 1–1.5 W per transistor. Bare TO-220 runs at about 62 K/W
|
||||
(60–90 °C rise); with the heatsink, about 20 K/W (about 30 °C rise).
|
||||
- **Project setting:** "PTH inside courtyard" is set to ignore, because each heatsink pin sits just
|
||||
behind its tab, inside the transistor courtyard. Courtyard *overlaps* are still checked for all
|
||||
other parts.
|
||||
- **Remaining DRC items:** the 9 silkscreen warnings are the TO-220 outlines over the heatsink pins.
|
||||
The fab clips silkscreen off pads, so they're harmless.
|
||||
|
||||
### Current capacity
|
||||
|
||||
Track widths follow IPC-2221 for 1 oz outer copper. Currents are estimated from the fuses.
|
||||
|
||||
| Net | Max current (fuse) | Width | Capacity at 10 °C rise |
|
||||
|---|---|---|---|
|
||||
| +12V_IN, +12V | 3 A (F1) | 1.5 mm | ≈ 3.2 A |
|
||||
| GND return | 3 A | 1.0 mm + solid bottom pour on the LV side | well above 3 A |
|
||||
| +12V_HV | 2 A (F2 + F3) | 1.0 mm | ≈ 2.4 A |
|
||||
| +12V_HVP, +12V_HVN | 1 A (F2, F3) | 1.0 mm | ≈ 2.4 A |
|
||||
| +5V | about 1 A (unfused, from the Mini560) | 1.0 mm | ≈ 2.4 A |
|
||||
| HV nets | 100 mA (F4, F5) | 0.6 mm | ≈ 1.7 A (width set by clearance, not current) |
|
||||
| Signals | mA | 0.4 mm | ≈ 1.2 A |
|
||||
|
||||
The 12 V input path, GND and +5V have no vias.
|
||||
|
||||
- **Note on F3:** if the negative rail ever drew the full 100 mA that F5 allows (about 13 W), the
|
||||
isolated converter plus the boost module would pull more than 1 A at 12 V. That would blow F3.
|
||||
Measure the real HV current at bring-up.
|
||||
- **Note on +5V:** the 5 V side isn't fused on its own. Consider a polyfuse (about 1.5 A) if the panel
|
||||
or the ESP32 wiring could short.
|
||||
|
||||
### Clearance rules
|
||||
|
||||
@@ -98,7 +141,7 @@ Once you start hand-editing the board in KiCad, stop running these scripts. They
|
||||
|
||||
- [ ] Check every footprint against the parts you actually buy: fuse holders, the 12.5 mm caps, the R13 5 W size, and the terminal block pitch.
|
||||
- [ ] Map J2 to your panel's power connector. The current order follows the Stern CN2 connector.
|
||||
- [ ] Check heatsink size and clearance for Q3/Q5/Q6.
|
||||
- [ ] Check the heatsink orientation against the Aavid 5768 drawing. KiCad has no 3D model for it, so it isn't visible in the render.
|
||||
- [ ] Consider conformal coating on the HV side.
|
||||
- [ ] Get a second pair of eyes on the HV layout.
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
(fp_lib_table
|
||||
(version 7)
|
||||
(lib (name "plasma_dmd")(type "KiCad")(uri "${KIPRJMOD}/plasma_dmd.pretty")(options "")(descr "Project footprints"))
|
||||
)
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 941 KiB After Width: | Height: | Size: 950 KiB |
@@ -0,0 +1,219 @@
|
||||
(footprint "Heatsink_AAVID_576802B03900G_NoCourtyard"
|
||||
(version 20260206)
|
||||
(generator "pcbnew")
|
||||
(generator_version "10.0")
|
||||
(layer "F.Cu")
|
||||
(descr "Courtyard and silkscreen outline removed (outline kept on F.Fab): the clip wraps around its TO-220, so its courtyard would always overlap it. Heatsink, 14.48x12.7x19.05mm, TO-220/ TO-262, https://www.boydcorp.com/aavid-datasheets/Board-Level-Cooling-Plug-In-5768.pdf")
|
||||
(tags "heatsink")
|
||||
(property "Reference" "REF**"
|
||||
(at 6.35 -8.5 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "07f046b7-ee46-436d-a8d8-54e735bd514a")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "Heatsink_AAVID_576802B03900G"
|
||||
(at 6.35 8.5 0)
|
||||
(layer "F.Fab")
|
||||
(uuid "0c915f93-8a39-4587-9dc9-356a06b61836")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "715aca18-8f8d-4d48-a4e1-5c8463d0d1a0")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "3dce55db-d6b0-43d7-b0f4-0d716ceb00b9")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(attr through_hole allow_missing_courtyard)
|
||||
(duplicate_pad_numbers_are_jumpers no)
|
||||
(fp_line
|
||||
(start 0.405 -1.85)
|
||||
(end 1.675 -1.85)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "Dwgs.User")
|
||||
(uuid "e2130da5-6bfd-4cff-a6e0-ce36fa9ed320")
|
||||
)
|
||||
(fp_line
|
||||
(start 0.405 1.85)
|
||||
(end 1.675 1.85)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "Dwgs.User")
|
||||
(uuid "fad026d3-0846-44b8-ac9b-fc5c71aeb7d2")
|
||||
)
|
||||
(fp_line
|
||||
(start 0.45 -5)
|
||||
(end 4.45 -5)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "Dwgs.User")
|
||||
(uuid "684b317f-96b2-4563-94b7-a9f9dbce19d3")
|
||||
)
|
||||
(fp_line
|
||||
(start 1.675 -5)
|
||||
(end 1.675 5)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "Dwgs.User")
|
||||
(uuid "e1c3e282-b6b0-4028-8ab4-94f132389afb")
|
||||
)
|
||||
(fp_line
|
||||
(start 4.45 -5)
|
||||
(end 4.45 5)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "Dwgs.User")
|
||||
(uuid "2be7b066-c2ff-480b-85f2-398b9dc58fb3")
|
||||
)
|
||||
(fp_line
|
||||
(start 4.45 5)
|
||||
(end 0.45 5)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "Dwgs.User")
|
||||
(uuid "f5f503b5-f93a-4eb0-a9b7-af9692dd025f")
|
||||
)
|
||||
(fp_line
|
||||
(start -0.405 -6.35)
|
||||
(end -0.405 6.35)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "dd94d98b-8847-4ce0-b30f-d31c6f77bc25")
|
||||
)
|
||||
(fp_line
|
||||
(start -0.405 -6.35)
|
||||
(end 12.295 -7.24)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "b22575e4-0838-4d53-a202-9782ea057b6e")
|
||||
)
|
||||
(fp_line
|
||||
(start 0.405 -5.597)
|
||||
(end 0.405 5.597)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "a9099b8e-83d3-42a9-8fc6-6ccc4acfec5a")
|
||||
)
|
||||
(fp_line
|
||||
(start 0.405 -5.597)
|
||||
(end 12.295 -6.43)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "872fc3e3-6f14-4f00-aac2-46697acceeb4")
|
||||
)
|
||||
(fp_line
|
||||
(start 12.295 -7.24)
|
||||
(end 12.295 7.24)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "8f66f8ce-b782-479c-bb13-b0f17be7fa48")
|
||||
)
|
||||
(fp_line
|
||||
(start 12.295 7.24)
|
||||
(end -0.405 6.35)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "8bb6ea79-3367-4e22-b8d7-6f275fda6f15")
|
||||
)
|
||||
(fp_line
|
||||
(start 12.3 6.43)
|
||||
(end 0.405 5.597)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "ec19e519-279b-4d08-8288-be58fe8a8d44")
|
||||
)
|
||||
(fp_text user "${REFERENCE}"
|
||||
(at 6.35 0 0)
|
||||
(layer "F.Fab")
|
||||
(uuid "6ecf39c3-7bd4-4dc2-b3d8-015e1ce8efe0")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pad "1" thru_hole oval
|
||||
(at 0 0)
|
||||
(size 2.6 3.78)
|
||||
(drill oval 1.1 2.28)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "ec39e68d-6144-43b8-abb3-6034313acdbd")
|
||||
)
|
||||
(embedded_fonts no)
|
||||
(model "${KICAD10_3DMODEL_DIR}/Heatsink.3dshapes/Heatsink_AAVID_576802B03900G.step"
|
||||
(offset
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
+4517
-4166
File diff suppressed because it is too large.
Load diff
@@ -112,7 +112,7 @@
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "error",
|
||||
"padstack": "warning",
|
||||
"pth_inside_courtyard": "error",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "warning",
|
||||
@@ -542,13 +542,32 @@
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Power",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 1,
|
||||
"priority": 2,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 1.0,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 1.0,
|
||||
"via_drill": 0.5,
|
||||
"wire_width": 6
|
||||
},
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.3,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Power3A",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 1,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 1.5,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 1.0,
|
||||
"via_drill": 0.5,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
@@ -622,17 +641,17 @@
|
||||
"pattern": "/Q6_C"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "/GND"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"netclass": "Power3A",
|
||||
"pattern": "/+12V"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"netclass": "Power3A",
|
||||
"pattern": "/+12V_IN"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "/GND"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "/+12V_HV"
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -26,7 +26,8 @@ PASSES = int(sys.argv[1]) if len(sys.argv) > 1 else 100
|
||||
# (routing clearance + half the width difference) from its neighbours. This gets 3 mm HV <-> LV
|
||||
# and 1.3 mm HV <-> HV while tracks can still squeeze out between transistor pins.
|
||||
# class: (routing width um, routing clearance um, final width mm)
|
||||
ROUTE = {"kicad_default": (1800, 250, 0.4), "Power": (2400, 300, 1.0), "HV": (2100, 650, 0.6)}
|
||||
ROUTE = {"kicad_default": (1800, 250, 0.4), "Power": (2400, 300, 1.0), "Power3A": (2900, 300, 1.5),
|
||||
"HV": (2100, 650, 0.6)}
|
||||
VIA_ROUTE_UM, VIA_FINAL_MM, VIA_DRILL_MM = 2300, 1.0, 0.5 # same trick for vias
|
||||
HV_TO_LV_UM = 2450 # + (2.1 - 0.6) / 2 after shrinking = 3.2 mm
|
||||
|
||||
@@ -62,7 +63,7 @@ def add_connector_stubs(board, ref="J2", length=5.0):
|
||||
"""
|
||||
fp = board.FindFootprintByReference(ref)
|
||||
# final widths: a router track must end on the stub copper itself, which still holds after shrinking
|
||||
widths = {"HV": ROUTE["HV"][2], "Power": ROUTE["Power"][2]}
|
||||
widths = {k: v[2] for k, v in ROUTE.items()}
|
||||
for pad in fp.Pads():
|
||||
net = pad.GetNet()
|
||||
name = net.GetNetname() if net else ""
|
||||
@@ -81,6 +82,55 @@ def add_connector_stubs(board, ref="J2", length=5.0):
|
||||
board.Add(t)
|
||||
|
||||
|
||||
def _closest_on_segment(p, a, b):
|
||||
ax, ay, bx, by = a.x, a.y, b.x, b.y
|
||||
dx, dy = bx - ax, by - ay
|
||||
L2 = dx * dx + dy * dy
|
||||
u = 0.0 if L2 == 0 else max(0.0, min(1.0, ((p.x - ax) * dx + (p.y - ay) * dy) / L2))
|
||||
cx, cy = ax + u * dx, ay + u * dy
|
||||
return int(round(cx)), int(round(cy)), ((p.x - cx) ** 2 + (p.y - cy) ** 2) ** 0.5
|
||||
|
||||
|
||||
def heal_t_junctions(board, reach_mm=1.6):
|
||||
"""After shrinking, a track that joined another track mid-way (a T-junction) can end beside
|
||||
the narrowed copper. Snap such dangling ends onto the centre line of the same-net track."""
|
||||
traces = [t for t in board.GetTracks() if t.Type() == pcbnew.PCB_TRACE_T]
|
||||
vias = [t for t in board.GetTracks() if t.Type() == pcbnew.PCB_VIA_T]
|
||||
pads = [p for f in board.GetFootprints() for p in f.Pads()]
|
||||
reach = pcbnew.FromMM(reach_mm)
|
||||
fixed = 0
|
||||
for t in traces:
|
||||
for is_start in (True, False):
|
||||
p = t.GetStart() if is_start else t.GetEnd()
|
||||
net, layer = t.GetNetCode(), t.GetLayer()
|
||||
if any(pd.GetNetCode() == net and pd.IsOnLayer(layer) and pd.HitTest(p) for pd in pads):
|
||||
continue
|
||||
if any(v.GetNetCode() == net and v.GetPosition() == p for v in vias):
|
||||
continue
|
||||
best, touching = None, False
|
||||
for o in traces:
|
||||
if o is t or o.GetNetCode() != net or o.GetLayer() != layer:
|
||||
continue
|
||||
if p in (o.GetStart(), o.GetEnd()):
|
||||
touching = True
|
||||
break
|
||||
cx, cy, d = _closest_on_segment(p, o.GetStart(), o.GetEnd())
|
||||
if d <= o.GetWidth() / 2:
|
||||
touching = True
|
||||
break
|
||||
if d <= reach and (best is None or d < best[2]):
|
||||
best = (cx, cy, d)
|
||||
if touching or best is None:
|
||||
continue
|
||||
q = pcbnew.VECTOR2I(best[0], best[1])
|
||||
if is_start:
|
||||
t.SetStart(q)
|
||||
else:
|
||||
t.SetEnd(q)
|
||||
fixed += 1
|
||||
return fixed
|
||||
|
||||
|
||||
def unconnected(board):
|
||||
board.BuildConnectivity()
|
||||
return board.GetConnectivity().GetUnconnectedCount(False)
|
||||
@@ -199,6 +249,7 @@ def main():
|
||||
if not pcbnew.ImportSpecctraSES(board, str(ses)):
|
||||
raise SystemExit("SES import failed")
|
||||
shrink_tracks(board)
|
||||
print("healed T-junctions:", heal_t_junctions(board))
|
||||
remove_dangling_stubs(board)
|
||||
add_gnd_plane(board)
|
||||
pcbnew.ZONE_FILLER(board).Fill(board.Zones())
|
||||
|
||||
Reference in new issue
Block a user