diff --git a/hardware/eda/circuits/indicator.py b/hardware/eda/circuits/indicator.py new file mode 100644 index 0000000..84f7870 --- /dev/null +++ b/hardware/eda/circuits/indicator.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""PM_K-1 SIG/CLIP indicator (SKiDL): peak-detect + LM393 -> SIG_LED / CLIP_LED. + +Run INSIDE the EDA container: + cd hardware/eda && ./run.sh python3 ../eda/circuits/indicator.py +Outputs ERC + hardware/kicad/indicator.net. + +Signal-present detect taps STAGE1_OUT; clip detect taps MIX_OUT (pre-driver). Each is +peak-rectified (Schottky + hold cap + bleed R) into an LM393 comparator vs a threshold +divider. Outputs are open-collector, pulled to +3V3, so they drive the face LED line AND +read cleanly on an RP2350 GPIO (SIG_LED=GPIO19, CLIP_LED=GPIO20 in the core). + +LM393 powered from +15V (handles the audio common-mode range); open-collector pulled to ++3V3 keeps the logic level RP2350-safe. Threshold-divider values are TUNABLE (set the +signal-present and clip points at bring-up). DNP/optional -- a face fits the LEDs. +LM393 pinout standard: 1=OUT1 2=-IN1 3=+IN1 4=V- 5=+IN2 6=-IN2 7=OUT2 8=V+. +""" +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") +DS = Part("Device","D_Schottky", dest=TEMPLATE, footprint="Diode_SMD:D_SOD-323") + +p15, p3v3, gnd = Net("+15V"), Net("+3V3"), Net("GND") +for n in (p15, p3v3): n.drive = POWER +gnd.drive = POWER +stage1_out, mix_out = Net("STAGE1_OUT"), Net("MIX_OUT") # taps (shared by name) +sig_led, clip_led = Net("SIG_LED"), Net("CLIP_LED") # open-collector -> GPIO + face LED + +LM393 = Part(name="LM393", tool=SKIDL, dest=TEMPLATE, ref_prefix="U", + footprint="Package_SO:SOIC-8_3.9x4.9mm_P1.27mm", + pins=[Pin(num=1,name="OUT1",func=P.OPENCOLL),Pin(num=2,name="-IN1",func=P.INPUT),Pin(num=3,name="+IN1",func=P.INPUT), + Pin(num=4,name="V-",func=P.PWRIN),Pin(num=5,name="+IN2",func=P.INPUT),Pin(num=6,name="-IN2",func=P.INPUT), + Pin(num=7,name="OUT2",func=P.OPENCOLL),Pin(num=8,name="V+",func=P.PWRIN)]) +u = LM393(ref="U11") +u["V+"] += p15; u["V-"] += gnd +cdec = C("100nF"); p15 += cdec[1]; cdec[2] += gnd + +def peak_detect(src): + d = DS(value="BAT54"); ch = C("100nF"); rb = R(value="100k") + node = Net() + src += d[2]; d[1] += node # Schottky pin1=K,2=A: anode at src, cathode->node (rectify +peaks) + ch[1] += node; ch[2] += gnd; rb[1] += node; rb[2] += gnd + return node + +def threshold(div_top, div_bot): + n = Net(); rt = R(value=div_top); rb = R(value=div_bot) + p15 += rt[1]; rt[2] += n; rb[1] += n; rb[2] += gnd + return n + +# signal-present: low threshold ; clip: high threshold (TUNABLE) +u["+IN1"] += peak_detect(stage1_out); u["-IN1"] += threshold("1Meg","10k") # ~0.15V trip +u["+IN2"] += peak_detect(mix_out); u["-IN2"] += threshold("100k","68k") # higher (clip) +rs = R(value="10k"); u["OUT1"] += sig_led; sig_led += rs[1]; rs[2] += p3v3 # open-coll pull-ups to 3V3 +rc = R(value="10k"); u["OUT2"] += clip_led; clip_led += rc[1]; rc[2] += p3v3 + +ERC() +out = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "kicad", "indicator.net")) +generate_netlist(file_=out) +print("Indicator netlist ->", out) diff --git a/hardware/eda/circuits/speaker.py b/hardware/eda/circuits/speaker.py new file mode 100644 index 0000000..21e10f0 --- /dev/null +++ b/hardware/eda/circuits/speaker.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""PM_K-1 monitor speaker amp (SKiDL): PAM8302A class-D -> SPK_P/SPK_N. DNP/optional. + +Run INSIDE the EDA container: + cd hardware/eda && ./run.sh python3 ../eda/circuits/speaker.py +Outputs ERC + hardware/kicad/speaker.net. + +Filterless 2.5W mono class-D for a built-in monitor (populated only on form factors that +want one). Fed from MIX_OUT (the mixed click+input, single-ended into IN+; IN- AC-coupled +to GND). Gain = 20*log(150k/(10k+RIN)); RIN=68k -> ~+5.7dB. SD pulled high = enabled +(route SPK_SD to a GPIO if software shutdown is wanted). Output is BTL (filterless) to the +speaker via the analog interconnect. + +PAM8302A SO-8 pinout (Diodes, verified): 1=SD 2=NC 3=IN+ 4=IN- 5=VO+ 6=VDD 7=GND 8=VO-. +""" +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, fp="Capacitor_SMD:C_0402_1005Metric"): return Part("Device","C", value=v, footprint=fp) + +p5, p3v3, gnd = Net("+5V"), Net("+3V3"), Net("GND") +for n in (p5, p3v3): n.drive = POWER +gnd.drive = POWER +mix_out, spk_p, spk_n, spk_sd = Net("MIX_OUT"), Net("SPK_P"), Net("SPK_N"), Net("SPK_SD") + +PAM = Part(name="PAM8302A", tool=SKIDL, dest=TEMPLATE, ref_prefix="U", + footprint="Package_SO:SOIC-8_3.9x4.9mm_P1.27mm", + pins=[Pin(num=1,name="SD",func=P.INPUT),Pin(num=2,name="NC",func=P.NOCONNECT),Pin(num=3,name="IN+",func=P.INPUT), + Pin(num=4,name="IN-",func=P.INPUT),Pin(num=5,name="VO+",func=P.OUTPUT),Pin(num=6,name="VDD",func=P.PWRIN), + Pin(num=7,name="GND",func=P.PWRIN),Pin(num=8,name="VO-",func=P.OUTPUT)]) +u = PAM(ref="U12") + +# supply +u["VDD"] += p5; u["GND"] += gnd +cb = C("1uF","Capacitor_SMD:C_0805_2012Metric"); p5 += cb[1]; cb[2] += gnd +cd = C("100nF"); p5 += cd[1]; cd[2] += gnd +# enable (SD high); route SPK_SD to a GPIO if desired +rsd = R(value="100k"); spk_sd += rsd[1]; rsd[2] += p5; u["SD"] += spk_sd +# single-ended input: MIX_OUT -> coupling cap + RIN -> IN+ ; IN- coupled to gnd (matched) +cin = C("1uF","Capacitor_SMD:C_0805_2012Metric"); rin = R(value="68k") # ~+5.7dB +mix_out += cin[1]; cin[2] += rin[1]; rin[2] += u["IN+"] +cinm = C("1uF","Capacitor_SMD:C_0805_2012Metric"); u["IN-"] += cinm[1]; cinm[2] += gnd +# filterless BTL output to speaker (via interconnect); add EMI ferrite/cap at layout if needed +u["VO+"] += spk_p; u["VO-"] += spk_n + +ERC() +out = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "kicad", "speaker.net")) +generate_netlist(file_=out) +print("Speaker netlist ->", out)