Skip to content

Commit 33b0fb1

Browse files
authored
Refactor Neopixel to add decoupling caps, use consistent pwr port naming (#428)
The old .vdd port naming is kept as an alias, eventually needs a proper deprecation warning on access. Cleans up Neopixel docstrings to be less redundant. Also changes svg-pcb generation to use the first matching footprint if there are multiple in a subtree. This unbreaks examples from changes. Convention is the first block (recursively) is the anchor block, eg the main chip. Updates generated netlists. Resolves #424
1 parent 29fef8f commit 33b0fb1

File tree

15 files changed

+3845
-1526
lines changed

15 files changed

+3845
-1526
lines changed

edg/electronics_model/SvgPcbTemplateBlock.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def _svgpcb_refdes_of(self, block_ref: List[str]) -> Tuple[str, int]:
4747
block_path = self._svgpcb_pathname_data.append_block(*block_ref)
4848
candidate_blocks = [block for block in self._svgpcb_netlist.blocks
4949
if block.full_path.startswith(block_path)]
50-
assert len(candidate_blocks) == 1
50+
assert len(candidate_blocks) > 0
5151
refdes = candidate_blocks[0].refdes
5252
assert isinstance(refdes, str)
5353
assert refdes is not None
@@ -60,19 +60,19 @@ def _svgpcb_refdes_of(self, block_ref: List[str]) -> Tuple[str, int]:
6060

6161
def _svgpcb_footprint_block_path_of(self, block_ref: List[str]) -> TransformUtil.Path:
6262
"""Infrastructure method, given the name of a container block, returns the block path of the footprint block.
63-
Asserts there is exactly one."""
63+
Picks the first one, which is assumed to be the main / anchor device."""
6464
block_path = self._svgpcb_pathname_data.append_block(*block_ref)
6565
candidate_blocks = [block for block in self._svgpcb_netlist.blocks
6666
if block.full_path.startswith(block_path)]
67-
assert len(candidate_blocks) == 1
67+
assert len(candidate_blocks) > 0
6868
return candidate_blocks[0].full_path
6969

7070
def _svgpcb_footprint_of(self, path: TransformUtil.Path) -> str:
7171
"""Infrastructure method, returns the footprint for the output of _svgpcb_footprint_block_path_of.
7272
If _svgpcb_footprint_block_path_of returned a value, this will return the footprint; otherwise crashes."""
7373
candidate_blocks = [block for block in self._svgpcb_netlist.blocks
7474
if block.full_path == path]
75-
assert len(candidate_blocks) == 1
75+
assert len(candidate_blocks) > 0
7676
return self._svgpcb_footprint_to_svgpcb(candidate_blocks[0].footprint)
7777

7878
def _svgpcb_pin_of(self, block_ref: List[str], pin_ref: List[str]) -> str:

edg/parts/Neopixel.py

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,22 @@
66

77
@abstract_block_default(lambda: Ws2812b)
88
class Neopixel(Light, Block):
9-
"""Abstract base class for Neopixel-type LEDs including the Vdd/Gnd/Din/Dout interface."""
9+
"""Abstract base class for individually-addressable, serially-connected Neopixel-type
10+
(typically RGB) LEDs and defines the pwr/gnd/din/dout interface."""
1011
def __init__(self) -> None:
1112
super().__init__()
12-
self.vdd = self.Port(VoltageSink.empty(), [Power])
13+
self.pwr = self.Port(VoltageSink.empty(), [Power])
14+
self.vdd = self.pwr # deprecated alias
1315
self.gnd = self.Port(Ground.empty(), [Common])
1416
self.din = self.Port(DigitalSink.empty(), [Input])
1517
self.dout = self.Port(DigitalSource.empty(), optional=True)
1618

1719

1820
class Ws2812b(Neopixel, FootprintBlock, JlcPart):
21+
"""5050-size Neopixel RGB. Specifically does NOT need extra filtering capacitors."""
1922
def __init__(self) -> None:
2023
super().__init__()
21-
self.vdd.init_from(VoltageSink(
24+
self.pwr.init_from(VoltageSink(
2225
voltage_limits=(3.7, 5.3) * Volt,
2326
current_draw=(0.6, 0.6 + 12*3) * mAmp,
2427
))
@@ -30,15 +33,15 @@ def __init__(self) -> None:
3033
# note that a more restrictive input_threshold_abs of (1.5, 2.3) was used previously
3134
))
3235
self.dout.init_from(DigitalSource.from_supply(
33-
self.gnd, self.vdd,
36+
self.gnd, self.pwr,
3437
current_limits=0*mAmp(tol=0),
3538
))
3639

3740
def contents(self) -> None:
3841
self.footprint(
3942
'D', 'LED_SMD:LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm',
4043
{
41-
'1': self.vdd,
44+
'1': self.pwr,
4245
'2': self.dout,
4346
'3': self.gnd,
4447
'4': self.din
@@ -54,25 +57,23 @@ def contents(self) -> None:
5457
self.assign(self.actual_basic_part, False)
5558

5659

57-
class Sk6812Mini_E(Neopixel, FootprintBlock):
58-
"""SK6812MINI-E reverse-mount Neopixel RGB LED, commonly used for keyboard lighting.
59-
Note: while listed as JLC C5149201, it seems non-stocked and is standard assembly only."""
60+
class Sk6812Mini_E_Device(InternalSubcircuit, JlcPart, FootprintBlock):
6061
def __init__(self) -> None:
6162
super().__init__()
62-
self.vdd.init_from(VoltageSink(
63+
self.vdd = self.Port(VoltageSink(
6364
voltage_limits=(3.7, 5.5) * Volt,
6465
current_draw=(1, 1 + 12*3) * mAmp, # 1 mA static type + up to 12mA/ch
6566
))
66-
self.gnd.init_from(Ground())
67-
self.din.init_from(DigitalSink.from_supply(
67+
self.gnd = self.Port(Ground())
68+
self.din = self.Port(DigitalSink.from_supply(
6869
self.gnd, self.vdd,
6970
voltage_limit_tolerance=(-0.5, 0.5),
7071
input_threshold_factor=(0.3, 0.7),
7172
))
72-
self.dout.init_from(DigitalSource.from_supply(
73+
self.dout = self.Port(DigitalSource.from_supply(
7374
self.gnd, self.vdd,
7475
current_limits=0*mAmp(tol=0),
75-
))
76+
), optional=True)
7677

7778
def contents(self) -> None:
7879
self.footprint(
@@ -86,26 +87,39 @@ def contents(self) -> None:
8687
mfr='Opsco Optoelectronics', part='SK6812MINI-E',
8788
datasheet='https://cdn-shop.adafruit.com/product-files/4960/4960_SK6812MINI-E_REV02_EN.pdf'
8889
)
90+
self.assign(self.lcsc_part, 'C5149201')
91+
self.assign(self.actual_basic_part, False)
8992

9093

91-
class Sk6805_Ec15(Neopixel, JlcPart, FootprintBlock):
92-
"""SK6805-EC15 Neopixel RGB LED in 1.5x1.5 (0606)."""
94+
class Sk6812Mini_E(Neopixel):
95+
"""Reverse-mount (through-board) Neopixel RGB LED, commonly used for keyboard lighting."""
9396
def __init__(self) -> None:
9497
super().__init__()
95-
self.vdd.init_from(VoltageSink(
98+
self.device = self.Block(Sk6812Mini_E_Device())
99+
self.cap = self.Block(DecouplingCapacitor(0.1*uFarad(tol=0.2)))
100+
self.connect(self.gnd, self.device.gnd, self.cap.gnd)
101+
self.connect(self.pwr, self.device.vdd, self.cap.pwr)
102+
self.connect(self.din, self.device.din)
103+
self.connect(self.dout, self.device.dout)
104+
105+
106+
class Sk6805_Ec15_Device(InternalSubcircuit, JlcPart, FootprintBlock):
107+
def __init__(self) -> None:
108+
super().__init__()
109+
self.vdd = self.Port(VoltageSink(
96110
voltage_limits=(3.7, 5.5) * Volt,
97111
current_draw=(1, 1 + 5*3) * mAmp, # 1 mA static type + up to 5mA/ch
98112
))
99-
self.gnd.init_from(Ground())
100-
self.din.init_from(DigitalSink.from_supply(
113+
self.gnd = self.Port(Ground())
114+
self.din = self.Port(DigitalSink.from_supply(
101115
self.gnd, self.vdd,
102116
voltage_limit_tolerance=(-0.5, 0.5),
103117
input_threshold_factor=(0.3, 0.7),
104118
))
105-
self.dout.init_from(DigitalSource.from_supply(
119+
self.dout = self.Port(DigitalSource.from_supply(
106120
self.gnd, self.vdd,
107121
current_limits=0*mAmp(tol=0),
108-
))
122+
), optional=True)
109123

110124
def contents(self) -> None:
111125
self.footprint(
@@ -123,24 +137,35 @@ def contents(self) -> None:
123137
self.assign(self.actual_basic_part, False)
124138

125139

126-
class Sk6812_Side_A(Neopixel, FootprintBlock):
127-
"""SK6812-SIDE-A side-emitting Neopixel LED."""
140+
class Sk6805_Ec15(Neopixel):
141+
"""0606-size (1.5mm x 1.5mm) size Neopixel RGB LED."""
128142
def __init__(self) -> None:
129143
super().__init__()
130-
self.vdd.init_from(VoltageSink(
144+
self.device = self.Block(Sk6805_Ec15_Device())
145+
self.cap = self.Block(DecouplingCapacitor(0.1*uFarad(tol=0.2)))
146+
self.connect(self.gnd, self.device.gnd, self.cap.gnd)
147+
self.connect(self.pwr, self.device.vdd, self.cap.pwr)
148+
self.connect(self.din, self.device.din)
149+
self.connect(self.dout, self.device.dout)
150+
151+
152+
class Sk6812_Side_A_Device(InternalSubcircuit, FootprintBlock):
153+
def __init__(self) -> None:
154+
super().__init__()
155+
self.vdd = self.Port(VoltageSink(
131156
voltage_limits=(3.5, 5.5) * Volt,
132157
current_draw=(1, 1 + 12*3) * mAmp, # 1 mA static type + up to 12mA/ch
133158
))
134-
self.gnd.init_from(Ground())
135-
self.din.init_from(DigitalSink.from_supply(
159+
self.gnd = self.Port(Ground())
160+
self.din = self.Port(DigitalSink.from_supply(
136161
self.gnd, self.vdd,
137162
voltage_limit_tolerance=(-0.5, 0.5),
138163
input_threshold_factor=(0.3, 0.7),
139164
))
140-
self.dout.init_from(DigitalSource.from_supply(
165+
self.dout = self.Port(DigitalSource.from_supply(
141166
self.gnd, self.vdd,
142167
current_limits=0*mAmp(tol=0),
143-
))
168+
), optional=True)
144169

145170
def contents(self) -> None:
146171
self.footprint(
@@ -157,14 +182,27 @@ def contents(self) -> None:
157182
# potentially footprint-compatible with C2890037
158183

159184

185+
class Sk6812_Side_A(Neopixel):
186+
"""Side-emitting Neopixel LED, including used for keyboard edge lighting."""
187+
def __init__(self) -> None:
188+
super().__init__()
189+
self.device = self.Block(Sk6812_Side_A_Device())
190+
self.cap = self.Block(DecouplingCapacitor(0.1*uFarad(tol=0.2)))
191+
self.connect(self.gnd, self.device.gnd, self.cap.gnd)
192+
self.connect(self.pwr, self.device.vdd, self.cap.pwr)
193+
self.connect(self.din, self.device.din)
194+
self.connect(self.dout, self.device.dout)
195+
196+
160197
class NeopixelArray(Light, GeneratorBlock):
161198
"""An array of Neopixels"""
162199
@init_in_parent
163200
def __init__(self, count: IntLike):
164201
super().__init__()
165202
self.din = self.Port(DigitalSink.empty(), [Input])
166203
self.dout = self.Port(DigitalSource.empty(), [Output], optional=True)
167-
self.vdd = self.Port(VoltageSink.empty(), [Power])
204+
self.pwr = self.Port(VoltageSink.empty(), [Power])
205+
self.vdd = self.pwr # deprecated alias
168206
self.gnd = self.Port(Ground.empty(), [Common])
169207

170208
self.count = self.ArgParameter(count)
@@ -178,7 +216,7 @@ def generate(self):
178216
for led_i in range(self.get(self.count)):
179217
led = self.led[str(led_i)] = self.Block(Neopixel())
180218
self.connect(last_signal_pin, led.din)
181-
self.connect(self.vdd, led.vdd)
219+
self.connect(self.pwr, led.pwr)
182220
self.connect(self.gnd, led.gnd)
183221
last_signal_pin = led.dout
184222
self.connect(self.dout, last_signal_pin)

0 commit comments

Comments
 (0)