Skip to content
5 changes: 4 additions & 1 deletion edg/electronics_interfaces/CanPort.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ def contents(self) -> None:
class CanControllerPort(Port[CanLogicLink]):
link_type = CanLogicLink

def __init__(self, model: Optional[DigitalBidir] = None) -> None:
def __init__(self, model: Optional[DigitalBidir] = None, *, bitrate_limit: RangeLike = RangeExpr.ALL) -> None:
# 0-1 Mbit/s are standard CANbus
# 1-8 Mbit/s imply CAN-FD
super().__init__()
if model is None: # ideal by default
model = DigitalBidir()
self.txd = self.Port(DigitalSource.from_bidir(model))
self.rxd = self.Port(DigitalSink.from_bidir(model))
self.bitrate_limit = self.Parameter(RangeExpr(bitrate_limit))


class CanTransceiverPort(Port[CanLogicLink]):
Expand Down
31 changes: 25 additions & 6 deletions edg/electronics_interfaces/I2cPort.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from ..electronics_model import *
from .DigitalPorts import DigitalSink, DigitalSource, DigitalBidir, DigitalBidirBridge, DigitalSinkBridge
from ..util import deprecated_param_remap


class I2cLink(Link):
Expand All @@ -24,6 +25,7 @@ def __init__(self) -> None:
self.addresses = self.Parameter(
ArrayIntExpr(self.targets.flatten(lambda x: x.addresses).concat(self.controller.addresses))
)
self.frequency = self.Parameter(RangeExpr())

self.has_pull = self.Parameter(BoolExpr(self.pull.any_connected()))

Expand All @@ -35,6 +37,11 @@ def contents(self) -> None:
self.require(self.controller.has_pullup.implies(self.pull.length() == 0), "redundant pullup with controller")

self.require(self.addresses.all_unique(), "conflicting addresses on I2C bus")
self.assign(
self.frequency,
self.controller.frequency_limit.intersect(self.targets.intersection(lambda x: x.frequency_limit)),
)

self.scl = self.connect(
self.pull.map_extract(lambda device: device.scl),
self.controller.scl,
Expand Down Expand Up @@ -92,7 +99,12 @@ class I2cController(Port[I2cLink]):
bridge_type = I2cControllerBridge

def __init__(
self, model: Optional[DigitalBidir] = None, *, has_pullup: BoolLike = False, addresses: ArrayIntLike = []
self,
model: Optional[DigitalBidir] = None,
*,
has_pullup: BoolLike = False,
addresses: ArrayIntLike = [],
frequency_limit: RangeLike = RangeExpr.ALL,
) -> None:
super().__init__()
if model is None:
Expand All @@ -101,7 +113,7 @@ def __init__(
self.sda = self.Port(model)

self.addresses = self.Parameter(ArrayIntExpr(addresses))
self.frequency = self.Parameter(RangeExpr(RangeExpr.ZERO))
self.frequency_limit = self.Parameter(RangeExpr(frequency_limit))
self.has_pullup = self.Parameter(BoolExpr(has_pullup))


Expand All @@ -116,7 +128,7 @@ def __init__(self) -> None:
def contents(self) -> None:
super().contents()

self.outer_port.init_from(I2cTarget(DigitalBidir.empty(), self.inner_link.link().addresses))
self.outer_port.init_from(I2cTarget(DigitalBidir.empty(), addresses=self.inner_link.link().addresses))

self.scl_bridge = self.Block(DigitalSinkBridge())
self.connect(self.outer_port.scl, self.scl_bridge.outer_port)
Expand All @@ -131,15 +143,22 @@ class I2cTarget(Port[I2cLink]):
link_type = I2cLink
bridge_type = I2cTargetBridge

def __init__(self, model: Optional[DigitalBidir] = None, addresses: ArrayIntLike = []) -> None:
"""Addresses specified excluding the R/W bit (as a 7-bit number, as directly used by Arduino)"""
@deprecated_param_remap((2, "addresses"))
def __init__(
self,
model: Optional[DigitalBidir] = None,
*,
addresses: ArrayIntLike = [],
frequency_limit: RangeLike = RangeExpr.ALL,
) -> None:
"""Addresses specified excluding the R/W bit (as a 7-bit number, as directly used by Arduino and Rust embedded-hal)"""
super().__init__()
if model is None:
model = DigitalBidir() # ideal by default
self.scl = self.Port(DigitalSink.from_bidir(model))
self.sda = self.Port(model)

self.frequency_limit = self.Parameter(RangeExpr(RangeExpr.ALL)) # range of acceptable frequencies
self.frequency_limit = self.Parameter(RangeExpr(frequency_limit)) # range of acceptable frequencies
self.addresses = self.Parameter(ArrayIntExpr(addresses))


Expand Down
4 changes: 3 additions & 1 deletion edg/electronics_interfaces/I2sPort.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ class I2sController(Port[I2sLink]):

link_type = I2sLink

def __init__(self, model: Optional[DigitalBidir] = None) -> None:
def __init__(self, model: Optional[DigitalBidir] = None, *, bitrate_limit: RangeLike = RangeExpr.ALL) -> None:
super().__init__()
if model is None:
model = DigitalBidir() # ideal by default
self.sck = self.Port(DigitalSource.from_bidir(model))
self.ws = self.Port(DigitalSource.from_bidir(model))
self.sd = self.Port(model) # bidirectional

self.bitrate_limit = self.Parameter(RangeExpr(bitrate_limit)) # bitrate


class I2sTargetReceiver(Port[I2sLink]):
"""Target means SCK and WS are inputs, receiver means SD is input"""
Expand Down
9 changes: 6 additions & 3 deletions edg/electronics_interfaces/SpiPort.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from ..electronics_model import *
from .DigitalPorts import DigitalSink, DigitalSource, DigitalBidir
from ..util import deprecated_param_remap


class SpiLink(Link):
Expand Down Expand Up @@ -37,22 +38,24 @@ def contents(self) -> None:
class SpiController(Port[SpiLink]):
link_type = SpiLink

def __init__(self, model: Optional[DigitalBidir] = None, frequency: RangeLike = RangeExpr.ZERO) -> None:
@deprecated_param_remap(("frequency", "frequency_limit"), (2, "frequency_limit"))
def __init__(self, model: Optional[DigitalBidir] = None, *, frequency_limit: RangeLike = RangeExpr.ALL) -> None:
super().__init__()
if model is None:
model = DigitalBidir() # ideal by default
self.sck = self.Port(DigitalSource.from_bidir(model))
self.mosi = self.Port(DigitalSource.from_bidir(model))
self.miso = self.Port(DigitalSink.from_bidir(model))

self.frequency = self.Parameter(RangeExpr(frequency))
self.frequency_limit = self.Parameter(RangeExpr(frequency_limit))
self.mode = self.Parameter(RangeExpr()) # modes supported, in [0, 3] TODO: what about sparse modes?


class SpiPeripheral(Port[SpiLink]):
link_type = SpiLink

def __init__(self, model: Optional[DigitalBidir] = None, frequency_limit: RangeLike = RangeExpr.ALL) -> None:
@deprecated_param_remap((2, "frequency_limit"))
def __init__(self, model: Optional[DigitalBidir] = None, *, frequency_limit: RangeLike = RangeExpr.ALL) -> None:
super().__init__()
if model is None:
model = DigitalBidir() # ideal by default
Expand Down
5 changes: 2 additions & 3 deletions edg/electronics_interfaces/UartPort.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ def contents(self) -> None:
class UartPort(Port[UartLink]):
link_type = UartLink

def __init__(self, model: Optional[DigitalBidir] = None) -> None:
def __init__(self, model: Optional[DigitalBidir] = None, *, baud_limit: RangeLike = RangeExpr.ALL) -> None:
super().__init__()
if model is None:
model = DigitalBidir() # ideal by default
self.tx = self.Port(DigitalSource.from_bidir(model))
self.rx = self.Port(DigitalSink.from_bidir(model))

self.baud = self.Parameter(RangeExpr(RangeExpr.ZERO))
self.baud_limit = self.Parameter(RangeExpr(RangeExpr.INF))
self.baud_limit = self.Parameter(RangeExpr(baud_limit))
2 changes: 1 addition & 1 deletion edg/electronics_interfaces/test_i2c_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self) -> None:
class I2cTargetBlock(Block):
def __init__(self, address: IntLike):
super().__init__()
self.port = self.Port(I2cTarget(DigitalBidir(), [address]))
self.port = self.Port(I2cTarget(DigitalBidir(), addresses=[address]))


class I2cTest(DesignTop):
Expand Down
4 changes: 3 additions & 1 deletion edg/parts/analog/dac/Mcp4728.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ def __init__(self) -> None:
voltage_limit_tolerance=(-0.3, 0.3) * Volt,
input_threshold_factor=(0.3, 0.7), # for Vdd >= 2.7v
)
self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x60])) # TODO 3LSBs EEPROM programmable
self.i2c = self.Port(
I2cTarget(dio_model, addresses=[0x60], frequency_limit=(100, 3400) * kHertz)
) # TODO 3LSBs EEPROM programmable
self.ldac = self.Port(DigitalSink.from_bidir(dio_model))
self.rdy = self.Port(DigitalSource.low_from_supply(self.vss), optional=True)

Expand Down
117 changes: 0 additions & 117 deletions edg/parts/analog/dac/Mcp47f.py

This file was deleted.

1 change: 0 additions & 1 deletion edg/parts/analog/dac/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
from .Mcp4901 import Mcp4921
from .Mcp47f import Mcp47f
from .Mcp4728 import Mcp4728
4 changes: 3 additions & 1 deletion edg/parts/analog/opamp/Ina219.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ def __init__(self, addr_lsb: IntLike):
self.vs = self.Port(VoltageSink(voltage_limits=(3, 5.5) * Volt, current_draw=(6 * uAmp, 1 * mAmp)))
self.gnd = self.Port(Ground())

self.i2c = self.Port(I2cTarget(DigitalBidir.empty(), addresses=[0x40 + self.addr_lsb]))
self.i2c = self.Port(
I2cTarget(DigitalBidir.empty(), addresses=[0x40 + self.addr_lsb], frequency_limit=(1, 2560) * kHertz)
)
self.i2c.sda.init_from(
DigitalBidir.from_supply(
self.gnd, self.vs, voltage_limit_abs=(-0.3, 6.0) * Volt, input_threshold_factor=(0.3, 0.7)
Expand Down
2 changes: 1 addition & 1 deletion edg/parts/display/EInk_Er_Epd027_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def __init__(self) -> None:
self.dc = self.Port(din_model, optional=True)
self.csb = self.Port(din_model)

self.spi = self.Port(SpiPeripheral(dio_model))
self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 20) * MHertz)) # 2.5MHz read mode

self.conn = self.Block(Fpc050Bottom(length=24)).connected(
{
Expand Down
8 changes: 6 additions & 2 deletions edg/parts/display/lcd/Ch280qv10_Ct.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,11 @@ def __init__(self) -> None:
self.cs = self.Port(din_model)
# pin 39 TE out unused

# CST026-A, https://cdn-shop.adafruit.com/product-files/1947/CST0x6_DS.pdf
ctp_dio_model = DigitalBidir.from_supply(self.gnd, self.iovcc, input_threshold_abs=(1.0, 1.9) * Volt)
self.ctp_i2c = self.Port(I2cTarget(ctp_dio_model, [0x38]), optional=True)
self.ctp_i2c = self.Port(
I2cTarget(ctp_dio_model, addresses=[0x38], frequency_limit=(0, 400) * kHertz), optional=True
)
# pin 46 is CTQ IRQ, unused (semantics not defined)
self.ctp_res = self.Port(DigitalSink.from_bidir(ctp_dio_model))
self.require(self.ctp_i2c.is_connected().implies(self.ctp_res.is_connected()))
Expand Down Expand Up @@ -123,7 +126,8 @@ def __init__(self) -> None:
self.device = self.Block(Ch280qv10_Ct_Device())
self.gnd = self.Export(self.device.gnd, [Common])
self.pwr = self.Export(self.device.iovcc, [Power])
self.spi = self.Port(SpiPeripheral.empty())
# SPI capable of 10 MHz in write mode, slower (as modeled) in read mode
self.spi = self.Port(SpiPeripheral(DigitalBidir.empty(), frequency_limit=(0, 6.6) * MHertz))
self.cs = self.Export(self.device.cs)
self.dc = self.Export(self.device.wr_rs)

Expand Down
6 changes: 4 additions & 2 deletions edg/parts/display/lcd/Er_Tft1_28_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ def __init__(self) -> None:
self.rs = self.Port(din_model)

# Control pins
self.spi = self.Port(SpiPeripheral(dio_model))
# SPI up to 100 MHz in write mode, slower (as modeled) in read mode
self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 6.6) * MHertz))
self.cs = self.Port(din_model)
self.rst = self.Port(din_model)

# Capacitive Touch Panel (CTP)
# CST816S, https://www.buydisplay.com/download/ic/DS-CST816S_DS_V1.3.pdf
self.ctp_vdd = self.Port(VoltageSink(voltage_limits=(2.7, 3.6) * Volt, current_draw=(5 * uAmp, 2.5 * mAmp)))
self.ctp_i2c = self.Port(I2cTarget(dio_model, addresses=[0x15]))
self.ctp_i2c = self.Port(I2cTarget(dio_model, addresses=[0x15], frequency_limit=(10, 1000) * kHertz))

self.ctp_rst = self.Port(din_model)
self.ctp_int = self.Port(din_model)
Expand Down
5 changes: 3 additions & 2 deletions edg/parts/display/lcd/Qt096t_if09.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def __init__(self) -> None:
self.rs = self.Port(din_model) # data / command selection pin
self.cs = self.Port(din_model)

self.spi = self.Port(SpiPeripheral(dio_model))
# up to 15 MHz in write mode, slower (as modeled) in read mode
self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 6.6) * MHertz))
self.leda = self.Port(Passive())

self.conn = self.Block(Fpc050Bottom(length=8)).connected(
Expand All @@ -47,7 +48,7 @@ def __init__(self) -> None:


class Qt096t_if09(Lcd, Resettable, Block):
"""ST7735S-based LCD module with a 8-pin 0.5mm-pitch FPC connector"""
"""ST7735S-based LCD module with an 8-pin 0.5mm-pitch FPC connector, like ER-TFT0.96-4."""

def __init__(self) -> None:
super().__init__()
Expand Down
Loading