Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion edg/circuits/PowerConditioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def __init__(
self,
pull_resistance: RangeLike = 10 * kOhm(tol=0.05),
amp_resistance: RangeLike = 10 * kOhm(tol=0.05),
diode_drop: RangeLike = (0, 0.4) * Volt,
diode_drop: RangeLike = (0, 0.6) * Volt,
):
super().__init__()
self.pwr_in = self.Port(VoltageSink.empty(), [Input])
Expand Down
16 changes: 7 additions & 9 deletions edg/electronics_model/NetlistGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class Net(NamedTuple):


class Netlist(NamedTuple):
refdes_prefix: str
subboard: TransformUtil.Path
blocks: List[NetBlock]
nets: List[Net]

Expand Down Expand Up @@ -371,13 +373,6 @@ def scope_to_netlist(self, scope: BoardScope) -> Netlist:
key=lambda pair: path_ordering[pair[0].port_component(must_have_port=False)],
)

board_refdes_prefix = self._design.get_value(("refdes_prefix",))
if board_refdes_prefix is not None:
assert isinstance(board_refdes_prefix, str)
net_prefix = board_refdes_prefix
else:
net_prefix = ""

def port_ignored_paths(path: TransformUtil.Path) -> bool: # ignore link ports for netlisting
return bool(path.links) or any(
[block.startswith("(adapter)") or block.startswith("(bridge)") for block in path.blocks]
Expand All @@ -386,7 +381,7 @@ def port_ignored_paths(path: TransformUtil.Path) -> bool: # ignore link ports f
netlist_footprints = [footprint for path, footprint in scope.footprints.items()]
netlist_nets = [
Net(
net_prefix + str(name),
str(name),
sorted(
list(chain(*[scope.pins[port] for port in net if port in scope.pins])),
key=lambda pin: ((path_ordering[pin.block_path]), pin.pin_name),
Expand All @@ -397,7 +392,10 @@ def port_ignored_paths(path: TransformUtil.Path) -> bool: # ignore link ports f
]
netlist_nets = [net for net in netlist_nets if net.pins] # prune empty nets

return Netlist(netlist_footprints, netlist_nets)
board_refdes_prefix = self._design.get_value(("refdes_prefix",))
board_refdes_prefix = board_refdes_prefix if board_refdes_prefix is not None else ""
assert isinstance(board_refdes_prefix, str)
return Netlist(board_refdes_prefix, scope.path, netlist_footprints, netlist_nets)

def run(self) -> Dict[TransformUtil.Path, Netlist]:
self.transform_design(self._design.design)
Expand Down
12 changes: 9 additions & 3 deletions edg/electronics_model/footprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .NetlistGenerator import Netlist, NetBlock, Net, PathShortener
from .. import edgir
from ..core import TransformUtil


class RefdesMode(Enum):
Expand Down Expand Up @@ -143,7 +144,7 @@ def gen_net_pin(block_name: str, pin_name: str) -> str:
return "(node (ref {}) (pin {}))".format(block_name, pin_name)


def net_exp(nets: List[Net], blocks: List[NetBlock]) -> str:
def net_exp(refdes_prefix: str, subboard: TransformUtil.Path, nets: List[Net], blocks: List[NetBlock]) -> str:
"""Given a dictionary of net names (strings) as keys and a list of connected Pins (namedtuples) as corresponding values

Example:
Expand All @@ -161,7 +162,12 @@ def net_exp(nets: List[Net], blocks: List[NetBlock]) -> str:

result = "(nets"
for i, net in enumerate(nets):
result += "\n" + gen_net_header(i + 1, net.name)
# apply subboard block and refdes prefix to ensure uniqueness when panelizing designs
net_name = net.name
if subboard.blocks:
net_name = f"{subboard}_{net_name}"
net_name = f"{refdes_prefix}{net_name}"
result += "\n" + gen_net_header(i + 1, net_name)
for pin in net.pins:
result += "\n " + gen_net_pin(block_dict[pin.block_path].refdes, pin.pin_name)
result += ")"
Expand All @@ -180,7 +186,7 @@ def generate_netlist(netlist: Netlist, refdes_mode: RefdesMode) -> str:
+ "\n"
+ block_exp(netlist.blocks, shortener, refdes_mode)
+ "\n"
+ net_exp(netlist.nets, netlist.blocks)
+ net_exp(netlist.refdes_prefix, netlist.subboard, netlist.nets, netlist.blocks)
+ "\n"
+ ")"
)
1 change: 1 addition & 0 deletions edg/parts/microcontroller/Ch32v003.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ def generate(self) -> None:
mfr="WCH",
part="CH32V003F4P6",
datasheet="https://www.wch-ic.com/downloads/CH32V003DS0_PDF.html",
pnp_rot=-90,
)
self.assign(self.lcsc_part, "C5187096")
self.assign(self.actual_basic_part, False)
Expand Down
1 change: 1 addition & 0 deletions edg/parts/microcontroller/Ch32v203.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ def generate(self) -> None:
mfr="WCH",
part="CH32V203K8T6",
datasheet="https://www.wch-ic.com/downloads/CH32V203DS0_PDF.html",
pnp_rot=-90,
)
self.assign(self.lcsc_part, "C5372188")
self.assign(self.actual_basic_part, False)
Expand Down
67 changes: 66 additions & 1 deletion edg/parts/power/converter/LinearRegulators.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,71 @@ def contents(self) -> None:
self.connect(self.pwr_out, self.ic.pwr_out, self.out_cap.pwr)


class Ams1117_Device(InternalSubcircuit, LinearRegulatorDevice, GeneratorBlock, JlcPart, FootprintBlock):
def __init__(self, output_voltage: RangeLike):
super().__init__()

# no recommended operating range given, though most parameters are spec'd to this range
self.assign(self.pwr_in.voltage_limits, (1.5, 12) * Volt)
self.assign(self.pwr_out.current_limits, (0, 0.9) * Amp) # minimum rating, below the headline 1A spec
self.assign(self.actual_quiescent_current, (5, 11) * mAmp)
self.assign(self.actual_dropout, (1.1, 1.3) * Volt) # typ to max

self.output_voltage = self.ArgParameter(output_voltage)
self.generator_param(self.output_voltage)

@override
def generate(self) -> None:
super().generate()
parts = [
(Range(1.455, 1.545), "AMS1117-1.5", "C16172", False),
(Range(1.746, 1.854), "AMS1117-1.8", "C6185", False),
(Range(2.425, 2.575), "AMS1117-2.5", "C12087", False),
(Range(2.7645, 2.9344), "AMS1117-2.85", "C14791", False),
(Range(3.201, 3.399), "AMS1117-3.3", "C6186", True),
(Range(4.850, 5.150), "AMS1117-5.0", "C6187", True),
]
suitable_parts = [part for part in parts if part[0] in self.get(self.output_voltage)]
assert suitable_parts, "no regulator with compatible output"
part_output_voltage, part_number, jlc_number, jlc_basic_part = suitable_parts[0]

self.assign(self.pwr_out.voltage_out, part_output_voltage * Volt)
self.footprint(
"U",
"Package_TO_SOT_SMD:SOT-223-3_TabPin2",
{
"1": self.gnd,
"2": self.pwr_out,
"3": self.pwr_in,
},
mfr="Advanced Monolithic Systems",
part=part_number,
datasheet="http://www.advanced-monolithic.com/pdf/ds1117.pdf",
pnp_rot=180,
)
self.assign(self.lcsc_part, jlc_number)
self.assign(self.actual_basic_part, jlc_basic_part)


class Ams1117(LinearRegulator):
"""15Vin, 1A fixed output low dropout linear regulators in SOT-223.
3.3 and 5.0v variants are JLC basic parts.
"""

@override
def contents(self) -> None:
with self.implicit_connect(
ImplicitConnect(self.gnd, [Common]),
) as imp:
self.ic = imp.Block(Ams1117_Device(self.output_voltage))
self.in_cap = imp.Block(DecouplingCapacitor(capacitance=0.1 * uFarad(tol=0.2)))
# note, a tantalum capacitor is recommended but there isn't a formal ESR spec
self.out_cap = imp.Block(DecouplingCapacitor(capacitance=22 * uFarad(tol=0.2)))

self.connect(self.pwr_in, self.ic.pwr_in, self.in_cap.pwr)
self.connect(self.pwr_out, self.ic.pwr_out, self.out_cap.pwr)


class Ap2204k_Device(InternalSubcircuit, LinearRegulatorDevice, GeneratorBlock, JlcPart, FootprintBlock):
def __init__(self, output_voltage: RangeLike):
super().__init__()
Expand Down Expand Up @@ -696,7 +761,7 @@ def generate(self) -> None:
mfr="Texas Instruments",
part=part_number,
datasheet="https://www.ti.com/lit/ds/symlink/tlv757p.pdf",
pnp_rot=180,
pnp_rot=90,
)
self.assign(self.lcsc_part, lcsc)
self.assign(self.actual_basic_part, False)
Expand Down
1 change: 1 addition & 0 deletions edg/parts/power/converter/TexasInstruments_Buck.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ def contents(self) -> None:
mfr="Texas Instruments",
part="LMR38020SDDAR",
datasheet="https://www.ti.com/lit/ds/symlink/lmr38020.pdf",
pnp_rot=-90,
)
self.assign(self.lcsc_part, "C3192337")
self.assign(self.actual_basic_part, False)
Expand Down
2 changes: 1 addition & 1 deletion edg/parts/power/converter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .LinearRegulators import Ld1117, Ldl1117, Ap2204k, Ap7215, Xc6206p, Xc6209, Ap2210, Lp5907, Tlv757p, L78l
from .LinearRegulators import Ld1117, Ldl1117, Ams1117, Ap2204k, Ap7215, Xc6206p, Xc6209, Ap2210, Lp5907, Tlv757p, L78l
from .TexasInstruments_Buck import Tps561201, Tps54202h, Lmr38020
from .Mp2722 import Mp2722
from .Ap3418 import Ap3418
Expand Down
1 change: 1 addition & 0 deletions edg/parts/sensor/DistanceArray_Vl53l5cx.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def contents(self) -> None:
mfr="STMicroelectronics",
part="VL53L5CXV0GC/1",
datasheet="https://www.st.com/resource/en/datasheet/vl53l5cx.pdf",
pnp_rot=180,
)
self.assign(self.lcsc_part, "C3178303")
self.assign(self.actual_basic_part, False)
Expand Down
1 change: 1 addition & 0 deletions edg/parts/sensor/Mag_A1304.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def contents(self) -> None:
mfr="Allegro MicroSystems",
part="A1304ELHLX-T",
datasheet="https://www.allegromicro.com/~/media/Files/Datasheets/A1304-Datasheet.ashx",
pnp_rot=90,
)

self.assign(self.lcsc_part, "C545185")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ def contents(self) -> None:

class Qmc5883l(Magnetometer, DefaultExportBlock):
"""3-axis magnetometer.

This part may be obsolete and harder to source.

This part seems to be a licensed semi-copy of the HMC5883L which is no longer in production.
It might be hardware drop-in compatible though the firmware protocol differs."""
It might be hardware drop-in compatible though the firmware protocol differs.
"""

def __init__(self) -> None:
super().__init__()
Expand All @@ -83,3 +87,64 @@ def contents(self) -> None:
self.connect(self.set_cap.pos, self.ic.setp)
self.connect(self.set_cap.neg, self.ic.setc)
self.c1 = self.Block(DecouplingCapacitor(4.7 * uFarad(tol=0.2))).connected(self.gnd, self.ic.c1)


class Qmc5883p_Device(InternalSubcircuit, FootprintBlock, JlcPart):
def __init__(self) -> None:
super().__init__()
self.vdd = self.Port(
VoltageSink(
voltage_limits=(2.5, 3.6) * Volt,
current_draw=(22, 2200) * uAmp, # suspend to continuous
)
)
self.gnd = self.Port(Ground())

dio_model = DigitalBidir.from_supply(
self.gnd,
self.vdd,
voltage_limit_tolerance=(-0.3, 0.3) * Volt, # assumed form Vdd absolute maximum rating
input_threshold_factor=(0.3, 0.7),
)
self.i2c = self.Port(I2cTarget(dio_model))
self.c1 = self.Port(VoltageSource(voltage_out=self.vdd.link().voltage, current_limits=(0, 0) * Amp)) # assumed

@override
def contents(self) -> None:
self.footprint(
"U",
"Package_LGA:LGA-16_3x3mm_P0.5mm",
{
"1": self.i2c.scl,
"2": self.vdd,
# 3-8: NC
"9": self.gnd,
"10": self.c1,
"11": self.gnd,
# 12-15: NC
"16": self.i2c.sda,
},
mfr="QST",
part="QMC5883P",
datasheet="https://www.qstcorp.com/upload/pdf/202512/2C939E5AA0704285BC3BE71132B8629B.pdf",
pnp_rot=-90,
)
self.assign(self.lcsc_part, "C2847467")
self.assign(self.actual_basic_part, False)


class Qmc5883p(Magnetometer, Block):
"""3-axis magnetometer, seemingly the successor to the QMC5883L which may be harder to source."""

def __init__(self) -> None:
super().__init__()
self.ic = self.Block(Qmc5883p_Device())
self.gnd = self.Export(self.ic.gnd, [Common])
self.pwr = self.Export(self.ic.vdd, [Power])
self.i2c = self.Export(self.ic.i2c)

@override
def contents(self) -> None:
super().contents()
self.vdd_cap = self.Block(DecouplingCapacitor(0.1 * uFarad(tol=0.2))).connected(self.gnd, self.ic.vdd)
self.c1 = self.Block(DecouplingCapacitor(4.7 * uFarad(tol=0.2))).connected(self.gnd, self.ic.c1)
2 changes: 1 addition & 1 deletion edg/parts/sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from .Mag_A1304 import A1304
from .MagSwitch_Ah1806 import Ah1806
from .Mag_Qmc5883l import Qmc5883l
from .Mag_Qmc5883 import Qmc5883l, Qmc5883p

from .FlirLepton import FlirLepton
from .Camera_Ov2640_Fpc24 import Ov2640, Ov2640_Fpc24
Expand Down
Empty file added edg/utils/__init__.py
Empty file.
Empty file added edg/utils/jlc_pcba/__init__.py
Empty file.
File renamed without changes.
12 changes: 6 additions & 6 deletions edg/vendor_parts/jlc/JlcLed.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ class JlcLed(PartsTableSelectorFootprint, JlcTableSelector, TableLed):
# because the description formatting is so inconsistent, the table is just hardcoded here
# instead of trying to parse the parts table
PART_COLOR_MAP = {
"C2286": Led.Red,
"C2290": Led.White,
"C2286": Led.Red, # 0603, basic
"C2290": Led.White, # 0603, basic
"C2293": Led.Blue,
"C2296": Led.Yellow,
"C2297": Led.Green,
"C34499": Led.White,
"C2296": Led.Yellow, # 0805, basic
"C2297": Led.Green, # 0805, basic
"C34499": Led.White, # 0805, basic
"C72038": Led.Yellow,
"C72041": Led.Blue,
"C72043": Led.Green, # "emerald"
"C84256": Led.Red,
"C84256": Led.Red, # 0805, basic
}

@classmethod
Expand Down
Loading
Loading