Skip to content

Commit

Permalink
Categorization / general cleanup (#344)
Browse files Browse the repository at this point in the history
Mostly cleanup of the categorization superclasses.

Also other minor cleanup:
- Remove nonfunctional mounting holes and labels, these should be added
to the layout as non-schematic footprints
  - Categorized into a new DeprecatedBlock
- Remove type from component names for IMU and magnetometer
  - Add deprecation aliases
- Add MultipackOpamp abstract block and MultipackOpampGenerator skeleton
base block
- Remove switch controller and fox project test cases, those probably
aren't contributing to coverage but still taking time
  • Loading branch information
ducky64 authored Apr 24, 2024
1 parent f7edaae commit 0788698
Show file tree
Hide file tree
Showing 94 changed files with 657 additions and 54,336 deletions.
12 changes: 11 additions & 1 deletion edg/BoardTop.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,17 @@ class BoardTop(BaseBoardTop):
pass


class JlcToolingHoles(Mechanical, Block):
class JlcToolingHole(InternalSubcircuit, FootprintBlock):
def contents(self):
super().contents()
self.footprint(
'H', 'edg:JlcToolingHole_1.152mm',
{},
datasheet='https://support.jlcpcb.com/article/92-how-to-add-tooling-holes-for-smt-assembly-order'
)


class JlcToolingHoles(InternalSubcircuit, Block):
def contents(self):
super().contents()
self.th1 = self.Block(JlcToolingHole())
Expand Down
2 changes: 1 addition & 1 deletion electronics_abstract_parts/AbstractAntenna.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


@abstract_block
class Antenna(Block):
class Antenna(Interface, Block):
@init_in_parent
def __init__(self, frequency: RangeLike, impedance: RangeLike = Range.all(), power: RangeLike = (0, 0*Watt)):
super().__init__()
Expand Down
55 changes: 54 additions & 1 deletion electronics_abstract_parts/AbstractOpamp.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Mapping
from typing import Mapping, Tuple, List, NamedTuple

from electronics_model import *
from .Categories import Analog
Expand Down Expand Up @@ -29,3 +29,56 @@ def __init__(self) -> None:

class OpampElement(Opamp):
"""Packed opamp element"""


@abstract_block
class MultipackOpamp(Analog, MultipackBlock):
"""Base class for packed opamps - devices that have multiple opamps in a single package,
with shared power and ground connections. Typically used with the multipack feature to
fit individual opamps across the design hierarchy into one of these."""
def __init__(self):
super().__init__()
self.elements = self.PackedPart(PackedBlockArray(OpampElement()))
self.pwr = self.PackedExport(self.elements.ports_array(lambda x: x.pwr))
self.gnd = self.PackedExport(self.elements.ports_array(lambda x: x.gnd))
self.inp = self.PackedExport(self.elements.ports_array(lambda x: x.inp))
self.inn = self.PackedExport(self.elements.ports_array(lambda x: x.inn))
self.out = self.PackedExport(self.elements.ports_array(lambda x: x.out))


@non_library
class MultipackOpampGenerator(MultipackOpamp, GeneratorBlock):
"""Skeleton base class that provides scaffolding for common packed opamp definitions"""
class OpampPorts(NamedTuple):
gnd: VoltageSink
pwr: VoltageSink
amps: List[Tuple[AnalogSink, AnalogSink, AnalogSource]] # amp-, amp+, out

def __init__(self):
super().__init__()
self.generator_param(self.pwr.requested(), self.gnd.requested(),
self.inn.requested(), self.inp.requested(), self.out.requested())

def _make_multipack_opamp(self) -> OpampPorts:
"""Generates the opamp as a block in self, including any application circuit components like decoupling capacitors.
Returns (gnd, pwr, [(in-, in+, out)])."""
raise NotImplementedError # implement me

def generate(self):
super().generate()
amp_ports = self._make_multipack_opamp()

self.gnd_merge = self.Block(PackedVoltageSource())
self.pwr_merge = self.Block(PackedVoltageSource())
self.connect(self.gnd_merge.pwr_out, amp_ports.gnd)
self.connect(self.pwr_merge.pwr_out, amp_ports.pwr)

requested = self.get(self.pwr.requested())
assert self.get(self.gnd.requested()) == self.get(self.inp.requested()) == \
self.get(self.inn.requested()) == self.get(self.out.requested()) == requested
for i, (amp_neg, amp_pos, amp_out) in zip(requested, amp_ports.amps):
self.connect(self.pwr.append_elt(VoltageSink.empty(), i), self.pwr_merge.pwr_ins.request(i))
self.connect(self.gnd.append_elt(VoltageSink.empty(), i), self.gnd_merge.pwr_ins.request(i))
self.connect(self.inn.append_elt(AnalogSink.empty(), i), amp_neg)
self.connect(self.inp.append_elt(AnalogSink.empty(), i), amp_pos)
self.connect(self.out.append_elt(AnalogSource.empty(), i), amp_out)
29 changes: 24 additions & 5 deletions electronics_abstract_parts/Categories.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ class Sensor(Block):
pass


@abstract_block
class CurrentSensor(Sensor):
pass


@abstract_block
class Accelerometer(Sensor):
pass
Expand All @@ -200,13 +205,13 @@ class Magnetometer(Sensor):


@abstract_block
class DistanceSensor(Sensor):
class Camera(Sensor):
"""Imaging sensors, including visible / RGB, IR, and thermal."""
pass


@abstract_block
class Mechanical(Block):
"""Nonelectrical footprint, including plated and NPTH mounting holes."""
class DistanceSensor(Sensor):
pass


Expand Down Expand Up @@ -285,6 +290,20 @@ def contents(self):


@abstract_block
class Label(Block):
"""Nonfunctional footprint, including copper and silkscreen labels."""
class DeprecatedBlock(InternalBlock):
"""Base class for blocks that are deprecated and planned to be removed"""
pass


@abstract_block
class Label(DeprecatedBlock):
"""DEPRECATED: non-circuit footprints should be added in layout as non-schematic items.
Nonfunctional footprint, including copper and silkscreen labels."""
pass


@abstract_block
class Mechanical(DeprecatedBlock):
"""DEPRECATED: non-circuit footprints should be added in layout as non-schematic items.
Nonelectrical footprint, including plated and NPTH mounting holes."""
pass
7 changes: 5 additions & 2 deletions electronics_abstract_parts/OpampCurrentSensor.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from typing import Dict

from electronics_abstract_parts import CurrentSenseResistor, DifferentialAmplifier
from .Categories import Sensor
from .Categories import CurrentSensor
from .DummyDevices import ForcedAnalogSignal
from electronics_model import *


class OpampCurrentSensor(Sensor, KiCadImportableBlock, Block):
class OpampCurrentSensor(CurrentSensor, KiCadImportableBlock, Block):
"""Current sensor block using a resistive sense element and an opamp-based differential amplifier.
For a positive current (flowing from pwr_in -> pwr_out), this generates a positive voltage on the output.
Output reference can be floating (eg, at Vdd/2) to allow bidirectional current sensing.
Discrete diffamp circuits generally have poor accuracy as a result of resistor tolerances, including
very poor common-mode rejection.
"""
@init_in_parent
def __init__(self, resistance: RangeLike, ratio: RangeLike, input_impedance: RangeLike):
Expand Down
3 changes: 2 additions & 1 deletion electronics_abstract_parts/PowerCircuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from .AbstractResistor import Resistor
from .AbstractFets import SwitchFet
from .GateDrivers import HalfBridgeDriver, HalfBridgeDriverIndependent, HalfBridgeDriverPwm
from .Categories import PowerConditioner


@abstract_block_default(lambda: FetHalfBridgeIndependent)
class HalfBridge(Block):
class HalfBridge(PowerConditioner, Block):
"""Half bridge circuit with logic-level inputs and current draw calculated from the output node.
Two power rails: logic power (which can be used to power gate drivers), and the power rail."""
def __init__(self):
Expand Down
14 changes: 14 additions & 0 deletions electronics_abstract_parts/TouchPad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from electronics_model import *
from .Categories import HumanInterface


class FootprintToucbPad(FootprintBlock, HumanInterface):
@init_in_parent
def __init__(self, touch_footprint: StringLike):
super().__init__()
self.pad = self.Port(TouchPadPort(), [Input])
self.touch_footprint = self.ArgParameter(touch_footprint)

def contents(self):
super().contents()
self.footprint('U', self.touch_footprint, {'1': self.pad})
8 changes: 5 additions & 3 deletions electronics_abstract_parts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
from .Categories import PowerConditioner, PowerSwitch, MotorDriver, BrushedMotorDriver, BldcDriver
from .Categories import PowerSource, Connector, ProgrammingConnector
from .Categories import HumanInterface, Display, Lcd, Oled, EInk, Light
from .Categories import Sensor, Accelerometer, Gyroscope, Magnetometer, DistanceSensor, EnvironmentalSensor, LightSensor
from .Categories import Label, Testing, TypedJumper, TypedTestPoint, InternalSubcircuit, Mechanical
from .Categories import Sensor, CurrentSensor, Accelerometer, Gyroscope, Magnetometer, DistanceSensor, Camera, \
EnvironmentalSensor, LightSensor
from .Categories import Label, Testing, TypedJumper, TypedTestPoint, InternalSubcircuit, DeprecatedBlock, Mechanical

from .ESeriesUtil import ESeriesUtil
from .SmdStandardPackage import SmdStandardPackage, SmdStandardPackageSelector
Expand Down Expand Up @@ -55,7 +56,7 @@
from .AbstractSwitch import Switch, TactileSwitch, MechanicalKeyswitch, DigitalSwitch
from .AbstractSwitch import RotaryEncoder, RotaryEncoderSwitch, DigitalRotaryEncoder, DigitalRotaryEncoderSwitch
from .AbstractSwitch import DirectionSwitch, DirectionSwitchCenter, DigitalDirectionSwitch, DigitalDirectionSwitchCenter
from .AbstractOpamp import Opamp, OpampElement
from .AbstractOpamp import Opamp, OpampElement, MultipackOpamp, MultipackOpampGenerator
from .OpampCircuits import OpampFollower, Amplifier, DifferentialAmplifier, IntegratorInverting
from .AbstractSpiMemory import SpiMemory, SpiMemoryQspi
from .OpampCurrentSensor import OpampCurrentSensor
Expand All @@ -77,6 +78,7 @@
from .AbstractTestPoint import AnalogRfTestPoint
from .AbstractJumper import Jumper, DigitalJumper
from .PassiveConnector import PassiveConnector, FootprintPassiveConnector
from .TouchPad import FootprintToucbPad

from .UsbConnectors import UsbConnector, UsbHostConnector, UsbDeviceConnector, UsbEsdDiode
from .CanTransceiver import CanTransceiver, IsolatedCanTransceiver, CanEsdDiode
Expand Down
8 changes: 5 additions & 3 deletions electronics_lib/BatteryProtector_S8261A.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .JlcPart import JlcPart


class BatteryProtector_S8261A_Device(InternalSubcircuit, JlcPart, FootprintBlock):
class S8261A_Device(InternalSubcircuit, JlcPart, FootprintBlock):
def __init__(self) -> None:
super().__init__()

Expand Down Expand Up @@ -35,12 +35,14 @@ def contents(self) -> None:
self.assign(self.lcsc_part, 'C28081')
self.assign(self.actual_basic_part, False)

class BatteryProtector_S8261A(PowerConditioner, Block):
class S8261A(PowerConditioner, Block):
"""1-cell LiIon/LiPo Battery protection IC protecting against overcharge, overdischarge, over current.
"""
@init_in_parent
def __init__(self) -> None:
super().__init__()

self.ic = self.Block(BatteryProtector_S8261A_Device())
self.ic = self.Block(S8261A_Device())

self.pwr_out = self.Port(VoltageSource.empty())
self.gnd_out = self.Port(GroundSource.empty())
Expand Down
2 changes: 1 addition & 1 deletion electronics_lib/BootstrapVoltageAdder.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from electronics_abstract_parts import *


class BootstrapVoltageAdder(KiCadSchematicBlock, Block):
class BootstrapVoltageAdder(KiCadSchematicBlock, PowerConditioner, Block):
"""Bipolar (positive and negative) voltage adder using a switched cap circuit.
"""
@init_in_parent
Expand Down
20 changes: 13 additions & 7 deletions electronics_lib/Camera_Ov2640_Fpc24.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,9 @@ def __init__(self) -> None:
self.connect(self.sio.sda, self.conn.pins.request('22').adapt_to(dio_model))


class Ov2640_Fpc24(Sensor, GeneratorBlock):
"""OV2640 camera as a 24-pin FPC bottom contact connector, as seems to be common on ESP32 with camera boards.
Electrical parameters from https://www.uctronics.com/download/OV2640_DS.pdf
Pinning and interface circuit from https://github.com/Freenove/Freenove_ESP32_WROVER_Board/blob/f710fd6976e76ab76c29c2ee3042cd7bac22c3d6/Datasheet/ESP32_Schematic.pdf
and https://www.waveshare.com/w/upload/9/99/OV2640-Camera-Board-Schematic.pdf
On many boards, Y0 and Y1 (LSBs) are left unconnected to save IOs."""
@abstract_block_default(lambda: Ov2640_Fpc24)
class Ov2640(Camera, Block):
"""OV2640 digital camera with DVP interface, commonly used with ESP32 devices"""
def __init__(self) -> None:
super().__init__()
self.device = self.Block(Ov2640_Fpc24_Device())
Expand All @@ -77,6 +74,16 @@ def __init__(self) -> None:

self.pwdn = self.Port(DigitalSink.empty(), optional=True)
self.reset = self.Port(DigitalSink.empty(), optional=True)


class Ov2640_Fpc24(Ov2640, GeneratorBlock):
"""OV2640 camera as a 24-pin FPC bottom contact connector, as seems to be common on ESP32 with camera boards.
Electrical parameters from https://www.uctronics.com/download/OV2640_DS.pdf
Pinning and interface circuit from https://github.com/Freenove/Freenove_ESP32_WROVER_Board/blob/f710fd6976e76ab76c29c2ee3042cd7bac22c3d6/Datasheet/ESP32_Schematic.pdf
and https://www.waveshare.com/w/upload/9/99/OV2640-Camera-Board-Schematic.pdf
On many boards, Y0 and Y1 (LSBs) are left unconnected to save IOs."""
def __init__(self) -> None:
super().__init__()
self.generator_param(self.pwdn.is_connected(), self.reset.is_connected())

def contents(self):
Expand Down Expand Up @@ -104,7 +111,6 @@ def contents(self):
self.connect(self.dvp8.y6, self.device.y.request('8'))
self.connect(self.dvp8.y7, self.device.y.request('9'))


def generate(self):
super().generate()
if self.get(self.pwdn.is_connected()):
Expand Down
4 changes: 2 additions & 2 deletions electronics_lib/CurrentSense_Ad8418.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .JlcPart import JlcPart


class Ad8418a_Device(JlcPart, FootprintBlock):
class Ad8418a_Device(JlcPart, FootprintBlock, InternalSubcircuit):
GAIN = Range.from_tolerance(20, 0.0015)
@init_in_parent
def __init__(self, in_diff_range: RangeLike):
Expand Down Expand Up @@ -55,7 +55,7 @@ def contents(self):
self.assign(self.actual_basic_part, False)


class Ad8418a(Sensor, KiCadImportableBlock, Block):
class Ad8418a(CurrentSensor, KiCadImportableBlock, Block):
def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
assert symbol_name == 'edg_importable:DifferentialAmplifier'
return {
Expand Down
12 changes: 6 additions & 6 deletions electronics_lib/Distance_Vl53l0x.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def contents(self):
self.assign(self.actual_basic_part, False)


@abstract_block_default(lambda: Vl53l0xApplication)
class Vl53l0x(DistanceSensor, Block):
@abstract_block_default(lambda: Vl53l0x)
class Vl53l0xBase(DistanceSensor, Block):
"""Abstract base class for VL53L0x application circuits"""
def __init__(self) -> None:
super().__init__()
Expand All @@ -83,7 +83,7 @@ def __init__(self) -> None:
self.gpio1 = self.Port(DigitalBidir.empty(), optional=True)


class Vl53l0xConnector(Vl53l0x_DeviceBase, Vl53l0x, GeneratorBlock):
class Vl53l0xConnector(Vl53l0x_DeviceBase, Vl53l0xBase, GeneratorBlock):
"""Connector to an external VL53L0X breakout board.
Uses the pinout from the Adafruit product: https://www.adafruit.com/product/3317
This has an onboard 2.8v regulator, but thankfully the IO tolerance is not referenced to Vdd"""
Expand Down Expand Up @@ -112,7 +112,7 @@ def generate(self):
self.connect(self.pwr.as_digital_source(), self.conn.pins.request('6').adapt_to(gpio_model))


class Vl53l0xApplication(Vl53l0x, GeneratorBlock):
class Vl53l0x(Vl53l0xBase, GeneratorBlock):
"""Board-mount laser ToF sensor"""
def contents(self):
super().contents()
Expand Down Expand Up @@ -154,9 +154,9 @@ def __init__(self, count: IntLike, *, first_xshut_fixed: BoolLike = False):

def generate(self):
super().generate()
self.elt = ElementDict[Vl53l0x]()
self.elt = ElementDict[Vl53l0xBase]()
for elt_i in range(self.get(self.count)):
elt = self.elt[str(elt_i)] = self.Block(Vl53l0x())
elt = self.elt[str(elt_i)] = self.Block(Vl53l0xBase())
self.connect(self.pwr, elt.pwr)
self.connect(self.gnd, elt.gnd)
self.connect(self.i2c, elt.i2c)
Expand Down
6 changes: 3 additions & 3 deletions electronics_lib/Imu_Lsm6ds3trc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .JlcPart import JlcPart


class Imu_Lsm6ds3trc_Device(InternalSubcircuit, FootprintBlock, JlcPart):
class Lsm6ds3trc_Device(InternalSubcircuit, FootprintBlock, JlcPart):
def __init__(self) -> None:
super().__init__()
self.vdd = self.Port(VoltageSink(
Expand Down Expand Up @@ -53,10 +53,10 @@ def contents(self) -> None:
self.assign(self.actual_basic_part, False)


class Imu_Lsm6ds3trc(Accelerometer, Gyroscope, Block):
class Lsm6ds3trc(Accelerometer, Gyroscope, Block):
def __init__(self):
super().__init__()
self.ic = self.Block(Imu_Lsm6ds3trc_Device())
self.ic = self.Block(Lsm6ds3trc_Device())
self.vdd = self.Export(self.ic.vdd, [Power])
self.vddio = self.Export(self.ic.vddio, [Power])
self.gnd = self.Export(self.ic.gnd, [Common])
Expand Down
Loading

0 comments on commit 0788698

Please sign in to comment.