circuits/midi.py, a do-not-populate option (USB-MIDI is the default). H11L1 opto-isolated IN (breaks the MIDI ground loop), 74LVC14-buffered OUT (TX through two inverters) and THRU (re-buffered IN). Connector is a face choice; core exposes the loop nets MIDI_IN/OUT/THRU_A/B plus MIDI_TX/RX to the RP2350 UART. ERC 0 errors; netlist 0 errors. CONFIRM: H11L1 pinout (standard; datasheet fetch timed out) + 3.3V-MIDI series-R values. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
79 lines
4.3 KiB
Python
79 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""PM_K-1 MIDI block (SKiDL) -- DNP populate-option: opto-isolated IN + buffered OUT + THRU.
|
|
|
|
Run INSIDE the EDA container:
|
|
cd hardware/eda && ./run.sh python3 ../eda/circuits/midi.py
|
|
Outputs ERC + hardware/kicad/midi.net.
|
|
|
|
USB-MIDI is the DEFAULT (firmware). This hardware DIN/TRS MIDI is a DO-NOT-POPULATE
|
|
option for laptop-free sync to standalone gear (a "stage" face fits the connector + parts).
|
|
|
|
IN : opto-isolated (H11L1) -- breaks the ground loop the MIDI spec requires.
|
|
OUT : RP2350 UART TX -> 74LVC14 buffer (2 inverters = non-inverting) -> series R.
|
|
THRU: a buffered copy of the received IN signal (re-transmit), same buffer chip.
|
|
|
|
PINOUTS
|
|
* 74LVC14 hex Schmitt inverter: standard 14-pin (1A/1Y..6A/6Y, GND=7, VCC=14).
|
|
* H11L1 6-pin Schmitt opto: standard 1=Anode 2=Cathode 3=NC 4=GND 5=VO 6=VCC
|
|
(universal across makers of this part; datasheet fetch timed out -- reconfirm at layout).
|
|
CONFIRM: the series-resistor values for 3.3V MIDI per the MIDI Association spec (placeholders
|
|
below). Connector (TRS-MIDI Type-A vs DIN-5) is a FACE choice; the core exposes the loop nets.
|
|
"""
|
|
import os
|
|
from skidl import *
|
|
set_default_tool(KICAD9)
|
|
P = Pin.types
|
|
R = Part("Device","R", dest=TEMPLATE, footprint="Resistor_SMD:R_0402_1005Metric")
|
|
def C(v): return Part("Device","C", value=v, footprint="Capacitor_SMD:C_0402_1005Metric")
|
|
D = Part("Device","D", dest=TEMPLATE, footprint="Diode_SMD:D_SOD-323")
|
|
|
|
p3v3, gnd = Net("+3V3"), Net("GND")
|
|
p3v3.drive = POWER; gnd.drive = POWER
|
|
midi_tx, midi_rx = Net("MIDI_TX"), Net("MIDI_RX") # to/from RP2350 UART (assign free GPIO at integ.)
|
|
in_a, in_b = Net("MIDI_IN_A"), Net("MIDI_IN_B") # isolated current loop -> face connector
|
|
out_a, out_b = Net("MIDI_OUT_A"), Net("MIDI_OUT_B")
|
|
thru_a, thru_b = Net("MIDI_THRU_A"), Net("MIDI_THRU_B")
|
|
|
|
OPTO = Part(name="H11L1", tool=SKIDL, dest=TEMPLATE, ref_prefix="U",
|
|
footprint="Package_DIP:DIP-6_W7.62mm",
|
|
pins=[Pin(num=1,name="A",func=P.PASSIVE),Pin(num=2,name="C",func=P.PASSIVE),Pin(num=3,name="NC",func=P.NOCONNECT),
|
|
Pin(num=4,name="GND",func=P.PWRIN),Pin(num=5,name="VO",func=P.OPENCOLL),Pin(num=6,name="VCC",func=P.PWRIN)])
|
|
BUF = Part(name="74LVC14", tool=SKIDL, dest=TEMPLATE, ref_prefix="U",
|
|
footprint="Package_SO:TSSOP-14_4.4x5mm_P0.65mm",
|
|
pins=[Pin(num=1,name="1A",func=P.INPUT),Pin(num=2,name="1Y",func=P.OUTPUT),Pin(num=3,name="2A",func=P.INPUT),
|
|
Pin(num=4,name="2Y",func=P.OUTPUT),Pin(num=5,name="3A",func=P.INPUT),Pin(num=6,name="3Y",func=P.OUTPUT),
|
|
Pin(num=7,name="GND",func=P.PWRIN),Pin(num=8,name="4Y",func=P.OUTPUT),Pin(num=9,name="4A",func=P.INPUT),
|
|
Pin(num=10,name="5Y",func=P.OUTPUT),Pin(num=11,name="5A",func=P.INPUT),Pin(num=12,name="6Y",func=P.OUTPUT),
|
|
Pin(num=13,name="6A",func=P.INPUT),Pin(num=14,name="VCC",func=P.PWRIN)])
|
|
opto = OPTO(ref="U8"); buf = BUF(ref="U9")
|
|
|
|
# ---- MIDI IN (opto-isolated) ----
|
|
rin = R(value="220"); dprot = D(value="1N4148WS")
|
|
in_a += rin[1]; rin[2] += opto["A"] # current-limit into LED
|
|
opto["C"] += in_b # LED return (isolated side)
|
|
dprot[2] += opto["C"]; dprot[1] += opto["A"] # reverse-protection across the LED (pin1=K,2=A)
|
|
opto["VCC"] += p3v3; opto["GND"] += gnd
|
|
rvo = R(value="10k"); opto["VO"] += midi_rx; midi_rx += rvo[1]; rvo[2] += p3v3 # pull-up; received data
|
|
copt = C("100nF"); p3v3 += copt[1]; copt[2] += gnd
|
|
|
|
# ---- MIDI OUT: TX -> two inverters -> series R ----
|
|
buf["1A"] += midi_tx; buf["1Y"] += Net("MIDI_OUT_N") # first invert
|
|
buf["2A"] += buf["1Y"] # second invert -> non-inverted
|
|
ro_b = R(value="33"); buf["2Y"] += ro_b[1]; ro_b[2] += out_b
|
|
ro_a = R(value="33"); p3v3 += ro_a[1]; ro_a[2] += out_a # current-source leg
|
|
|
|
# ---- MIDI THRU: re-buffer the received signal ----
|
|
buf["3A"] += midi_rx; buf["3Y"] += Net("MIDI_THRU_N")
|
|
buf["4A"] += buf["3Y"]
|
|
rt_b = R(value="33"); buf["4Y"] += rt_b[1]; rt_b[2] += thru_b
|
|
rt_a = R(value="33"); p3v3 += rt_a[1]; rt_a[2] += thru_a
|
|
|
|
# unused inverters parked, supply
|
|
buf["5A"] += gnd; buf["6A"] += gnd
|
|
buf["VCC"] += p3v3; buf["GND"] += gnd
|
|
cbuf = C("100nF"); p3v3 += cbuf[1]; cbuf[2] += gnd
|
|
|
|
ERC()
|
|
out = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "kicad", "midi.net"))
|
|
generate_netlist(file_=out)
|
|
print("MIDI netlist ->", out)
|