Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
91d9a65
Update test_iot_thermal_camera.py
ducky64 Jun 17, 2026
2da02a3
Update test_iot_thermal_camera.py
ducky64 Jun 17, 2026
233d4a8
Update test_iot_thermal_camera.py
ducky64 Jun 17, 2026
cd29e50
Update test_iot_thermal_camera.py
ducky64 Jun 17, 2026
b3747cc
Update test_iot_thermal_camera.py
ducky64 Jun 17, 2026
833637e
wip ethernet terminations
ducky64 Jun 18, 2026
8700445
wip
ducky64 Jun 18, 2026
8fd3970
create bundled ethernet link
ducky64 Jun 18, 2026
c698925
iterate on naming
ducky64 Jun 18, 2026
219eec1
Update test_iot_thermal_camera.py
ducky64 Jun 18, 2026
9484a6c
poe controller
ducky64 Jun 20, 2026
6dc6cd0
Update test_iot_thermal_camera.py
ducky64 Jun 20, 2026
ae9630e
Update test_iot_thermal_camera.py
ducky64 Jun 20, 2026
b7d371a
jumper
ducky64 Jun 20, 2026
8069ded
ready for layout, probably
ducky64 Jun 20, 2026
012d0ea
wip layout
ducky64 Jun 20, 2026
cf4360c
Update IotThermalCamera.kicad_pcb
ducky64 Jun 20, 2026
c693d73
floorplanning
ducky64 Jun 20, 2026
b095259
Update IotThermalCamera.kicad_pcb
ducky64 Jun 20, 2026
0885f98
cleanup routing
ducky64 Jun 21, 2026
ce1b511
wip
ducky64 Jun 21, 2026
c6e5ac6
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
fec2f70
wip
ducky64 Jun 21, 2026
b185691
wip
ducky64 Jun 21, 2026
32b1b86
wip
ducky64 Jun 21, 2026
ffc3f57
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
e19c0c6
Fix fusb302b vdd cap value
ducky64 Jun 21, 2026
56c4b98
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
dc8f1b4
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
65eb115
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
025b463
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
e917354
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
0e3ace5
Update IotThermalCamera.kicad_pcb
ducky64 Jun 21, 2026
69867ee
wip
ducky64 Jun 21, 2026
5848d01
wip almost done
ducky64 Jun 22, 2026
526b98e
Update IotThermalCamera.kicad_pcb
ducky64 Jun 22, 2026
61d4c7e
Update IotThermalCamera.kicad_pro
ducky64 Jun 22, 2026
f09f162
update reference netlists
ducky64 Jun 22, 2026
c19dfe0
fix types
ducky64 Jun 22, 2026
2bf6abf
fix naming, ground unused pin
ducky64 Jun 22, 2026
86fbbdc
Update test_iot_thermal_camera.py
ducky64 Jun 22, 2026
359aeff
Update Fusb302b.kicad_pcb
ducky64 Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions edg/abstract_parts/Jumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,41 @@ def __init__(self) -> None:
self.b = self.Port(Passive())


class GroundJumper(TypedJumper, Block):
def __init__(self) -> None:
super().__init__()
self.input = self.Port(Ground(), [Input])
self.output = self.Port(Ground(), [Output])

@override
def contents(self) -> None:
super().contents()
self.device = self.Block(Jumper())
self.connect(self.input.net, self.device.a)
self.connect(self.output.net, self.device.b)


class VoltageJumper(TypedJumper, Block):
def __init__(self) -> None:
super().__init__()
self.input = self.Port(VoltageSink(current_draw=RangeExpr(), reverse_voltage_out=RangeExpr()), [Input])
self.output = self.Port(
VoltageSource(
voltage_out=self.input.link().voltage, reverse_current_draw=self.input.link().reverse_current_drawn
),
[Output],
)

@override
def contents(self) -> None:
super().contents()
self.device = self.Block(Jumper())
self.assign(self.input.current_draw, self.output.link().current_drawn)
self.assign(self.input.reverse_voltage_out, self.output.link().reverse_voltage)
self.connect(self.input.net, self.device.a)
self.connect(self.output.net, self.device.b)


class DigitalJumper(TypedJumper, Block):
def __init__(self) -> None:
super().__init__()
Expand Down
2 changes: 1 addition & 1 deletion edg/abstract_parts/Resistor.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def symbol_pinning(self, symbol_name: str) -> Mapping[str, BasePort]:
def __init__(self, resistance: RangeLike) -> None:
super().__init__()

self.gnd = self.Port(Ground())
self.gnd = self.Port(Ground(), [Common])
self.io = self.Port(AnalogSink(impedance=RangeExpr()), [InOut])

voltage = self.io.link().voltage - self.gnd.link().voltage
Expand Down
2 changes: 1 addition & 1 deletion edg/abstract_parts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
CanDiffTestPoint,
)
from .TestPoint import AnalogCoaxTestPoint
from .Jumper import Jumper, DigitalJumper
from .Jumper import Jumper, GroundJumper, VoltageJumper, DigitalJumper
from .PassiveConnector import PassiveConnector, FootprintPassiveConnector

from .UsbConnectors import UsbConnector, UsbHostConnector, UsbDeviceConnector, UsbEsdDiode
Expand Down
2 changes: 1 addition & 1 deletion edg/parts/interface/UsbPd_Fusb302b.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,6 @@ def contents(self) -> None:
# and the bulk capacitor, which we hope will be elsewhere
self.vdd_cap = ElementDict[DecouplingCapacitor]()
self.vdd_cap[0] = self.Block(DecouplingCapacitor(0.1 * uFarad(tol=0.2))).connected(self.gnd, self.pwr)
self.vdd_cap[1] = self.Block(DecouplingCapacitor(10 * uFarad(tol=0.2))).connected(self.gnd, self.pwr)
self.vdd_cap[1] = self.Block(DecouplingCapacitor(1.0 * uFarad(tol=0.2))).connected(self.gnd, self.pwr)

# Crecv is specified in the reference schematic, but doesn't show up on open-source example designs
124 changes: 124 additions & 0 deletions edg/parts/power/converter/TexasInstruments_Buck.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any

from typing_extensions import override

from ....circuits import *
Expand Down Expand Up @@ -221,3 +223,125 @@ def generate(self) -> None:
self.en_res = self.Block(PullupResistor(resistance=510 * kOhm(tol=0.05))).connected(
self.pwr_in_zener_clamp.pwr_out, self.ic.en
)


class Lmr38020_Device(InternalSubcircuit, JlcPart, FootprintBlock):
def __init__(self) -> None:
super().__init__()
self.gnd = self.Port(Ground(), [Common])
self.vin = self.Port(VoltageSink(voltage_limits=(4.2, 80) * Volt, current_draw=RangeExpr()), [Power])
self.sw = self.Port(VoltageSource(voltage_out=self.vin.link().voltage.hull(self.gnd.link().voltage)))
self.fb = self.Port(AnalogSink(impedance=(10, float("inf")) * MOhm)) # assumed given RFbb maximum spec
self.boot = self.Port(
VoltageSink(
voltage_limits=self.sw.link().voltage + (-0.3, 5.5) * Volt,
reverse_voltage_out=(3.8, 5.5) * Volt, # assumed from UVLO to BOOT-SW abs max
reverse_current_limits=0 * Amp(tol=0),
)
)
self.en = self.Port(
DigitalSink.from_supply(
self.gnd, self.vin, voltage_limit_tolerance=(-0.3, 0.3) * Volt, input_threshold_abs=(0.95, 1.4) * Volt
)
)
self.rt = self.Port(AnalogSource()) # for timing
self.pg = self.Port(DigitalSource.low_from_supply(self.gnd), optional=True) # may be left open

@override
def contents(self) -> None:
super().contents()
self.assign(
self.vin.current_draw, self.sw.link().current_drawn + (3, 40) * uAmp
) # shutdown to non-switching Iq
self.footprint(
"U",
"Package_SO:HSOP-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.1mm_ThermalVias",
{
"1": self.gnd,
"2": self.en,
"3": self.vin,
"4": self.rt,
"5": self.fb,
"6": self.pg,
"7": self.boot,
"8": self.sw,
"9": self.gnd, # EP
},
mfr="Texas Instruments",
part="LMR38020SDDAR",
datasheet="https://www.ti.com/lit/ds/symlink/lmr38020.pdf",
)
self.assign(self.lcsc_part, "C3192337")
self.assign(self.actual_basic_part, False)


class Lmr38020(VoltageRegulatorEnableWrapper, DiscreteBuckConverter, GeneratorBlock):
"""4.2-80 V, 2 A synchronous buck converter in SO-8 with thermal pad.
Adjustable frequency, defaulting to 400kHz per the datasheet example."""

def __init__(self, *, frequency: RangeLike = 400 * kHertz(tol=0.1), **kwargs: Any) -> None:
super().__init__(**kwargs)
self.frequency = self.ArgParameter(frequency)
self.generator_param(self.frequency)

self.ic = self.Block(Lmr38020_Device())
self.connect(self.pwr_in, self.ic.vin)
self.connect(self.gnd, self.ic.gnd)

self.pg = self.Export(self.ic.pg, optional=True, doc="Open-drain active-high power-good flag")

@override
def _generator_inner_reset_pin(self) -> Port[DigitalLink]:
return self.ic.en

@override
def generate(self) -> None:
super().generate()

with self.implicit_connect(
ImplicitConnect(self.pwr_in, [Power]),
ImplicitConnect(self.gnd, [Common]),
) as imp:
self.fb = imp.Block(
FeedbackVoltageDivider(
output_voltage=(0.985, 1.015) * Volt, # across temperature range
impedance=(10, 100) * kOhm,
assumed_input_voltage=self.output_voltage,
)
)
self.connect(self.fb.input, self.pwr_out)
self.connect(self.fb.output, self.ic.fb)

self.hf_cap = imp.Block(DecouplingCapacitor(capacitance=0.1 * uFarad(tol=0.2)))
self.boot_cap = self.Block(BootstrapCapacitor(capacitance=0.1 * uFarad(tol=0.2))).connected(
self.ic.sw, self.ic.boot
)

target_frequency = self.get(self.frequency)
rt_resistance = (
30970 * ((target_frequency.upper / 1000) ** -1.027) * 1000,
30970 * ((target_frequency.lower / 1000) ** -1.027) * 1000,
)
self.rt = imp.Block(AnalogSetpointResistor(rt_resistance)).connected(io=self.ic.rt)
self.assign(
self.actual_frequency, 30970 / (self.rt.actual_resistance / 1000) * 1000
) # TODO add exponent term when the infrastructure supports it

self.power_path = imp.Block(
BuckConverterPowerPath(
self.pwr_in.link().voltage,
self.fb.actual_input_voltage,
self.actual_frequency,
self.pwr_out.link().current_drawn,
(0, 1.8) * Amp, # low-side min switch current limit
input_voltage_ripple=self.input_ripple_limit,
output_voltage_ripple=self.output_ripple_limit,
dutycycle_limit=(0, 0.97), # goes into PFM at low duty cycles
)
)
# ForcedVoltage needed to provide a voltage value so current downstream can be calculated
# and then the power path can generate
(self.forced_out,), _ = self.chain(
self.power_path.pwr_out, self.Block(ForcedVoltage(self.fb.actual_input_voltage)), self.pwr_out
)
self.connect(self.power_path.switch, self.ic.sw)
2 changes: 1 addition & 1 deletion edg/parts/power/converter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .LinearRegulators import Ld1117, Ldl1117, Ap2204k, Ap7215, Xc6206p, Xc6209, Ap2210, Lp5907, Tlv757p, L78l
from .TexasInstruments_Buck import Tps561201, Tps54202h
from .TexasInstruments_Buck import Tps561201, Tps54202h, Lmr38020
from .Mp2722 import Mp2722
from .Ap3418 import Ap3418
from .AnalogDevices_Boost import Ltc3429
Expand Down
6 changes: 3 additions & 3 deletions examples/IotFan/IotFan.net.ref
Original file line number Diff line number Diff line change
Expand Up @@ -446,14 +446,14 @@
(tstamps "15a803ba"))
(comp (ref "FC25")
(value "pd.vdd_cap[1]")
(footprint "Capacitor_SMD:C_0805_2012Metric")
(footprint "Capacitor_SMD:C_0603_1608Metric")
(property (name "Sheetname") (value "pd"))
(property (name "Sheetfile") (value "edg.parts.interface.UsbPd_Fusb302b.Fusb302b"))
(property (name "edg_path") (value "pd.vdd_cap[1].cap"))
(property (name "edg_short_path") (value "pd.vdd_cap[1]"))
(property (name "edg_refdes") (value "FC25"))
(property (name "edg_part") (value "CL21A106KAYNNNE (Samsung Electro-Mechanics)"))
(property (name "edg_value") (value "X5R 25V ±10% 10uF 0805 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS"))
(property (name "edg_part") (value "CL10A105KB8NNNC (Samsung Electro-Mechanics)"))
(property (name "edg_value") (value "50V 1uF X5R ±10% 0603 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS"))
(sheetpath (names "/pd/") (tstamps "/014600d5/"))
(tstamps "15aa03bb"))
(comp (ref "FR19")
Expand Down
6 changes: 3 additions & 3 deletions examples/IotFan/IotFan.svgpcb.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,12 @@ const FU8 = board.add(WQFN_14_1EP_2_5x2_5mm_P0_5mm_EP1_45x1_45mm, {
})
// pd.vdd_cap[0].cap
const FC24 = board.add(C_0603_1608Metric, {
translate: pt(2.366, 1.021), rotate: 0,
translate: pt(2.193, 1.021), rotate: 0,
id: 'FC24'
})
// pd.vdd_cap[1].cap
const FC25 = board.add(C_0805_2012Metric, {
translate: pt(2.202, 1.031), rotate: 0,
const FC25 = board.add(C_0603_1608Metric, {
translate: pt(2.349, 1.021), rotate: 0,
id: 'FC25'
})
// spk_dac.rc.r
Expand Down
6 changes: 3 additions & 3 deletions examples/IotIron/IotIron.net.ref
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,14 @@
(tstamps "15a803ba"))
(comp (ref "IC12")
(value "pd.vdd_cap[1]")
(footprint "Capacitor_SMD:C_0805_2012Metric")
(footprint "Capacitor_SMD:C_0603_1608Metric")
(property (name "Sheetname") (value "pd"))
(property (name "Sheetfile") (value "edg.parts.interface.UsbPd_Fusb302b.Fusb302b"))
(property (name "edg_path") (value "pd.vdd_cap[1].cap"))
(property (name "edg_short_path") (value "pd.vdd_cap[1]"))
(property (name "edg_refdes") (value "IC12"))
(property (name "edg_part") (value "CL21A106KAYNNNE (Samsung Electro-Mechanics)"))
(property (name "edg_value") (value "X5R 25V ±10% 10uF 0805 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS"))
(property (name "edg_part") (value "CL10A105KB8NNNC (Samsung Electro-Mechanics)"))
(property (name "edg_value") (value "50V 1uF X5R ±10% 0603 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS"))
(sheetpath (names "/pd/") (tstamps "/014600d5/"))
(tstamps "15aa03bb"))
(comp (ref "IU5")
Expand Down
Loading
Loading