Update README with per-module schematic screenshots and BOM
Replace the single full-schematic screenshot with individual per-module screenshots (audio, MIDI/Arduino, power delivery, USB, top case 3D). Add a Bill of Materials section auto-generated from the KiCad netlist using parse_netlist.py (sexpdata), listing 160 components grouped by schematic sheet with human-readable descriptions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
67b4acbdad
commit
f4d8a61963
5699
CDJ-MainBoard_2026-02-17.net
Normal file
5699
CDJ-MainBoard_2026-02-17.net
Normal file
File diff suppressed because it is too large
Load Diff
124
README.md
124
README.md
@ -106,9 +106,129 @@ kicad-thirdparty-footprints/ Third-party footprints (CM5, DAC, RCA jacks)
|
|||||||
- **JLC2KiCad** — importing JLCPCB component libraries into KiCad
|
- **JLC2KiCad** — importing JLCPCB component libraries into KiCad
|
||||||
- **JLCPCB** — PCB fabrication and assembly
|
- **JLCPCB** — PCB fabrication and assembly
|
||||||
|
|
||||||
## Schematic
|
## Schematics
|
||||||
|
|
||||||

|
### Audio Outputs
|
||||||
|

|
||||||
|
|
||||||
|
### MIDI / Arduino Controller
|
||||||
|

|
||||||
|
|
||||||
|
### Power Delivery
|
||||||
|

|
||||||
|
|
||||||
|
### USB Ports
|
||||||
|

|
||||||
|
|
||||||
|
### Top Case 3D Render
|
||||||
|

|
||||||
|
|
||||||
|
## Bill of Materials by Module
|
||||||
|
|
||||||
|
*Auto-generated from the KiCad netlist — 160 components total.*
|
||||||
|
|
||||||
|
### Root — Raspberry Pi CM5 compute module, status LEDs, fan connector, RTC battery, and top-level glue logic
|
||||||
|
|
||||||
|
| Ref | Value | Description |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| BT1 | CR2032-BS-6-1 | CR2032 coin cell holder (RTC backup) |
|
||||||
|
| D1 | PSC-1608U52GC-G4 | Green LED 780mcd |
|
||||||
|
| D2 | XL-1608UBC-04 | Blue LED |
|
||||||
|
| J1 | J_FAN_PWM | 4-pin JST connector for PWM fan |
|
||||||
|
| SW1 | DSWB05LHGET | 5-position DIP switch |
|
||||||
|
| SW2 | PWR_BUT_CM5 | Push button (CM5 power) |
|
||||||
|
| U1 | Compute_Module_5_Functional | Raspberry Pi Compute Module 5 |
|
||||||
|
|
||||||
|
Plus 1 capacitors, 10 resistors.
|
||||||
|
|
||||||
|
### Arduino MIDI — ATmega32U4 USB MIDI controller, 16 MHz crystal, JST connectors for buttons/encoders/jog wheel
|
||||||
|
|
||||||
|
| Ref | Value | Description |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| J16 | J_JOG | 4-pin JST — jog wheel quadrature encoder |
|
||||||
|
| J17 | J_PLAY | 6-pin JST — Play + Cue buttons and LEDs |
|
||||||
|
| J18 | J_ENCODER_BACK | 7-pin JST — browse rotary encoder + Back button + LED |
|
||||||
|
| J19 | J_TOUCH | 2-pin JST — jog wheel capacitive touch sensor |
|
||||||
|
| J20 | J_LOOP | 8-pin JST — Loop In/Out/Reloop buttons and LEDs |
|
||||||
|
| J21 | J_MASTER_TEMPO | 4-pin JST — Master Tempo button + LED |
|
||||||
|
| J22 | J_TEMPO | 5-pin JST — tempo fader (analog) + Tempo Reset button + LED |
|
||||||
|
| SW4 | RESET_BTN | Push button (ATmega reset) |
|
||||||
|
| U14 | ATmega32U4-A | 16 MHz, 32 kB Flash, USB 2.0, TQFP-44 — MIDI controller |
|
||||||
|
| X1 | X322516MLB4SI | 16 MHz SMD crystal, 9 pF |
|
||||||
|
|
||||||
|
Plus 10 capacitors, 2 resistors.
|
||||||
|
|
||||||
|
### Audio Outputs — I2S DAC (PCM5242), RCA stereo pair, 6.35 mm and 3.5 mm headphone jacks, and analog output filtering
|
||||||
|
|
||||||
|
| Ref | Value | Description |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| FB1 | ~ | 600 Ω @ 100 MHz ferrite bead (power filtering) |
|
||||||
|
| J2 | PJ-611E | 6.35 mm (1/4") headphone jack |
|
||||||
|
| J3 | J_RCA_L (white) | RCA jack — left channel |
|
||||||
|
| J4 | J_RCA_R (red) | RCA jack — right channel |
|
||||||
|
| J5 | PJ-320D | 3.5 mm headphone jack |
|
||||||
|
| U2 | PCM5122PW | TI PCM5122 — 32-bit 384 kHz stereo DAC, 112 dB DNR |
|
||||||
|
|
||||||
|
Plus 12 capacitors, 3 resistors.
|
||||||
|
|
||||||
|
### HDMI and Ethernet — Dual micro-HDMI outputs (touchscreen + debug), gigabit Ethernet via RJ45
|
||||||
|
|
||||||
|
| Ref | Value | Description |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| J12 | HR911130A | RJ45 Ethernet jack with integrated magnetics |
|
||||||
|
| J13 | PI HDMI_0 | Micro-HDMI Type D connector (touchscreen) |
|
||||||
|
| J14 | PI HDMI_1 | Micro-HDMI Type D connector (debug/secondary) |
|
||||||
|
|
||||||
|
Plus 2 resistors.
|
||||||
|
|
||||||
|
### Power Delivery — USB-C PD input (CH224K negotiation), 20 V-to-5 V buck (SY8368AQQC), 3.3 V LDO (AP2112K), USB power switches (AP2553W)
|
||||||
|
|
||||||
|
| Ref | Value | Description |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| D3 | KT-0603R | Red LED (power indicator) |
|
||||||
|
| D4 | BZT52C3V3S | 3.3 V Zener diode (voltage clamping) |
|
||||||
|
| D5 | PSC-1608U52GC-G4 | Green LED (power-good indicator) |
|
||||||
|
| D6 | KT-0603R | Red LED (fault indicator) |
|
||||||
|
| J15 | USB_C_Receptacle_USB2.0_16P | USB-C receptacle (power input) |
|
||||||
|
| SW3 | SW_MAIN_POWER | Main power toggle switch |
|
||||||
|
| U10 | AP2112K-3.3 | 3.3 V LDO — digital power supply (600 mA) |
|
||||||
|
| U11 | AP2112K-3.3 | 3.3 V LDO — clean analog supply for PCM5242 |
|
||||||
|
| U12 | CH224K | USB PD 3.0/2.0 sink controller (negotiates 20 V) |
|
||||||
|
| U13 | SY8368AQQC | Synchronous buck converter (20 V → 5 V) |
|
||||||
|
|
||||||
|
Plus 25 capacitors, 9 resistors, 1 inductors.
|
||||||
|
|
||||||
|
### Test Points — Debug and measurement test points for key signals
|
||||||
|
|
||||||
|
| Ref | Value | Description |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| H1–H4 | MountingHole | PCB mounting holes (×4) |
|
||||||
|
| J6 | J_ISP_ATMEGA | 6-pin ISP header for ATmega programming |
|
||||||
|
| J7 | J_I2C | 4-pin I2C debug header |
|
||||||
|
| TP1 | TP_20V_USB_RAIL | 20 V USB power rail |
|
||||||
|
| TP2 | TP_20V_USB | 20 V post-switch |
|
||||||
|
| TP3 | TP_3.3_DGTL | 3.3 V digital rail |
|
||||||
|
| TP4 | TP_5V | 5 V rail |
|
||||||
|
| TP5 | TP_3.3_ANALOG | 3.3 V analog rail |
|
||||||
|
| TP6 | TP_I2C_SDA | I2C SDA line |
|
||||||
|
| TP7 | TP_GND | Ground reference |
|
||||||
|
| TP8 | TP_I2C_SCL | I2C SCL line |
|
||||||
|
| TP9 | TP_I2S_BCK | I2S bit clock |
|
||||||
|
| TP10 | TP_I2S_LRCK | I2S left/right clock |
|
||||||
|
| TP11 | TP_I2S_DIN | I2S data in |
|
||||||
|
|
||||||
|
### USB DJ Ports — USB-A 3.0 (Rekordbox sticks), USB-A power-only, USB-C 2.0, orientation mux (HD3SS3220), signal switch (HD3SS3212), and ESD protection
|
||||||
|
|
||||||
|
| Ref | Value | Description |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| J10 | USB_DJ_STICK | USB 3.0 Type-A — Rekordbox USB sticks |
|
||||||
|
| J11 | USB_SCREEN | USB 3.0 Type-A — touchscreen USB interface |
|
||||||
|
| J8 | AUX POWER USB_A | USB Type-A — auxiliary power output (no data) |
|
||||||
|
| J9 | USB_C_Receptacle_USB2.0_16P | USB-C receptacle (general connectivity) |
|
||||||
|
| U3–U5, U9 | AP2553W6-7 | USB power switches with current limiting (×4) |
|
||||||
|
| U6–U8 | USBLC6-2SC6 | ESD protection diodes, 2 data lines (×3) |
|
||||||
|
|
||||||
|
Plus 10 capacitors, 11 resistors.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
182
parse_netlist.py
Normal file
182
parse_netlist.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.10"
|
||||||
|
# dependencies = ["sexpdata"]
|
||||||
|
# ///
|
||||||
|
"""Parse KiCad netlist (.net) and output per-module component summary as Markdown."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import sexpdata
|
||||||
|
|
||||||
|
|
||||||
|
def sym_str(val):
|
||||||
|
"""Convert sexpdata Symbol or string to plain string."""
|
||||||
|
if isinstance(val, sexpdata.Symbol):
|
||||||
|
return val.value()
|
||||||
|
return str(val)
|
||||||
|
|
||||||
|
|
||||||
|
def find_entries(sexpr, key):
|
||||||
|
"""Find all sub-lists whose first element matches key."""
|
||||||
|
results = []
|
||||||
|
if isinstance(sexpr, list):
|
||||||
|
if len(sexpr) > 0 and sym_str(sexpr[0]) == key:
|
||||||
|
results.append(sexpr)
|
||||||
|
for item in sexpr:
|
||||||
|
results.extend(find_entries(item, key))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def find_entry(sexpr, key):
|
||||||
|
"""Find first sub-list whose first element matches key."""
|
||||||
|
entries = find_entries(sexpr, key)
|
||||||
|
return entries[0] if entries else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_value(sexpr, key):
|
||||||
|
"""Get the string value of (key value) from a list."""
|
||||||
|
entry = find_entry(sexpr, key)
|
||||||
|
if entry and len(entry) > 1:
|
||||||
|
return sym_str(entry[1])
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_property(comp, prop_name):
|
||||||
|
"""Get a named property value from a component."""
|
||||||
|
for item in comp:
|
||||||
|
if isinstance(item, list) and len(item) >= 3:
|
||||||
|
if sym_str(item[0]) == "property":
|
||||||
|
name_entry = find_entry(item, "name")
|
||||||
|
val_entry = find_entry(item, "value")
|
||||||
|
if name_entry and val_entry:
|
||||||
|
if sym_str(name_entry[1]) == prop_name:
|
||||||
|
return sym_str(val_entry[1])
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def parse_netlist(net_path: Path):
|
||||||
|
with open(net_path) as f:
|
||||||
|
data = sexpdata.loads(f.read())
|
||||||
|
|
||||||
|
# Parse sheet names from the design section
|
||||||
|
design = find_entry(data, "design")
|
||||||
|
sheets = find_entries(design, "sheet")
|
||||||
|
sheet_map = {} # tstamp path -> sheet name
|
||||||
|
for sheet in sheets:
|
||||||
|
name = get_value(sheet, "name")
|
||||||
|
sheet_map[name] = name
|
||||||
|
|
||||||
|
# Parse components
|
||||||
|
components_section = find_entry(data, "components")
|
||||||
|
comps = find_entries(components_section, "comp")
|
||||||
|
|
||||||
|
modules = defaultdict(list)
|
||||||
|
for comp in comps:
|
||||||
|
ref = get_value(comp, "ref")
|
||||||
|
value = get_value(comp, "value")
|
||||||
|
description = get_value(comp, "description")
|
||||||
|
footprint = get_value(comp, "footprint")
|
||||||
|
|
||||||
|
# Get sheet path name
|
||||||
|
sheetpath = find_entry(comp, "sheetpath")
|
||||||
|
sheet_name = get_value(sheetpath, "names") if sheetpath else "/"
|
||||||
|
|
||||||
|
# Get libsource part name
|
||||||
|
libsource = find_entry(comp, "libsource")
|
||||||
|
part = get_value(libsource, "part") if libsource else ""
|
||||||
|
|
||||||
|
modules[sheet_name].append({
|
||||||
|
"ref": ref,
|
||||||
|
"value": value,
|
||||||
|
"part": part,
|
||||||
|
"description": description,
|
||||||
|
"footprint": footprint,
|
||||||
|
})
|
||||||
|
|
||||||
|
return modules
|
||||||
|
|
||||||
|
|
||||||
|
SHEET_DESCRIPTIONS = {
|
||||||
|
"/": "Root — Raspberry Pi CM5 compute module, status LEDs, fan connector, RTC battery, and top-level glue logic",
|
||||||
|
"/Audio Outputs/": "Audio Outputs — I2S DAC (PCM5242), RCA stereo pair, 6.35mm and 3.5mm headphone jacks, and analog output filtering",
|
||||||
|
"/Test Points/": "Test Points — Debug and measurement test points for key signals",
|
||||||
|
"/USB DJ Ports/": "USB DJ Ports — USB-A 3.0 (Rekordbox sticks), USB-A power-only, USB-C 2.0, orientation mux (HD3SS3220), signal switch (HD3SS3212), and ESD protection",
|
||||||
|
"/HDMI and Ethernet/": "HDMI and Ethernet — Dual micro-HDMI outputs (touchscreen + debug), gigabit Ethernet via RJ45",
|
||||||
|
"/Power Delivery/": "Power Delivery — USB-C PD input (CH224K negotiation), 20V-to-5V buck (SY8368AQQC), 3.3V LDO (AP2112K), USB power switches (AP2553W)",
|
||||||
|
"/Arduino MIDI/": "Arduino MIDI — ATmega32U4 USB MIDI controller, 16MHz crystal, JST connectors for buttons/encoders/jog wheel",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def format_markdown(modules: dict) -> str:
|
||||||
|
"""Format parsed modules as a Markdown section."""
|
||||||
|
lines = []
|
||||||
|
lines.append("## Bill of Materials by Module")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(f"*Auto-generated from the KiCad netlist — {sum(len(v) for v in modules.values())} components total.*")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Sort: Root first, then alphabetical
|
||||||
|
def sort_key(name):
|
||||||
|
if name == "/":
|
||||||
|
return (0, "")
|
||||||
|
return (1, name)
|
||||||
|
|
||||||
|
for sheet_name in sorted(modules.keys(), key=sort_key):
|
||||||
|
comps = modules[sheet_name]
|
||||||
|
desc = SHEET_DESCRIPTIONS.get(sheet_name, sheet_name.strip("/"))
|
||||||
|
lines.append(f"### {desc}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Group by reference prefix (letter part) for a summary
|
||||||
|
by_prefix = defaultdict(list)
|
||||||
|
for c in comps:
|
||||||
|
prefix = "".join(ch for ch in c["ref"] if ch.isalpha())
|
||||||
|
by_prefix[prefix].append(c)
|
||||||
|
|
||||||
|
# Filter out purely passive groups from detailed listing
|
||||||
|
# but still show notable ICs/connectors
|
||||||
|
notable = []
|
||||||
|
passives = defaultdict(int)
|
||||||
|
|
||||||
|
for c in sorted(comps, key=lambda x: x["ref"]):
|
||||||
|
prefix = "".join(ch for ch in c["ref"] if ch.isalpha())
|
||||||
|
if prefix in ("R", "C", "L"):
|
||||||
|
passives[prefix] += 1
|
||||||
|
else:
|
||||||
|
notable.append(c)
|
||||||
|
|
||||||
|
passive_summary = []
|
||||||
|
prefix_names = {"R": "resistors", "C": "capacitors", "L": "inductors"}
|
||||||
|
for p in ("C", "R", "L"):
|
||||||
|
if passives[p]:
|
||||||
|
passive_summary.append(f"{passives[p]} {prefix_names[p]}")
|
||||||
|
|
||||||
|
if notable:
|
||||||
|
lines.append("| Ref | Value | Description |")
|
||||||
|
lines.append("|-----|-------|-------------|")
|
||||||
|
for c in notable:
|
||||||
|
desc_text = c["description"] or c["part"]
|
||||||
|
# Truncate very long descriptions
|
||||||
|
if len(desc_text) > 80:
|
||||||
|
desc_text = desc_text[:77] + "..."
|
||||||
|
lines.append(f"| {c['ref']} | {c['value']} | {desc_text} |")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
if passive_summary:
|
||||||
|
lines.append(f"Plus {', '.join(passive_summary)}.")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
net_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("CDJ-MainBoard_2026-02-17.net")
|
||||||
|
modules = parse_netlist(net_path)
|
||||||
|
print(format_markdown(modules))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
BIN
screenshots/audio_2026-02-17.png
Normal file
BIN
screenshots/audio_2026-02-17.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
screenshots/midi_arduino_2026-02-17.png
Normal file
BIN
screenshots/midi_arduino_2026-02-17.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
BIN
screenshots/power_delivery_2026-02-17.png
Normal file
BIN
screenshots/power_delivery_2026-02-17.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
BIN
screenshots/top_case_3d_2026-02-17.png
Normal file
BIN
screenshots/top_case_3d_2026-02-17.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
screenshots/usb_2026-02-17.png
Normal file
BIN
screenshots/usb_2026-02-17.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
Loading…
x
Reference in New Issue
Block a user