BerkeleyHCI/PolymorphicBlocks

Software Power Switch using Dirctional Switch

Closed this issue · 1 comments

Current implementation

FetPowerGateNoBtn

class FetPowerGateNoBtn(PowerSwitch, KiCadSchematicBlock, Block):
"""A high-side PFET power gate that has a button to power on, can be latched
on by an external signal, and provides the button output as a signal.
"""
def __init__(self):
super().__init__()
self.pwr_in = self.Port(VoltageSink.empty(), [Input])
self.pwr_out = self.Port(VoltageSource.empty(), [Output])
self.gnd = self.Port(Ground.empty(), [Common])
self.btn_out = self.Port(DigitalSingleSource.empty())
self.control = self.Port(DigitalSink.empty()) # digital level control - gnd-referenced NFET gate
def contents(self):
super().contents()
max_voltage = self.control.link().voltage.upper()
max_current = self.pwr_out.link().current_drawn.upper()
self.pull_res = self.Block(Resistor(
resistance=10*kOhm(tol=0.05) # TODO kind of arbitrary
))
self.pwr_fet = self.Block(Fet.PFet(
drain_voltage=(0, max_voltage),
drain_current=(0, max_current),
gate_voltage=(max_voltage, max_voltage), # TODO this ignores the diode drop
))
self.amp_res = self.Block(Resistor(
resistance=10*kOhm(tol=0.05) # TODO kind of arbitrary
))
self.amp_fet = self.Block(Fet.NFet(
drain_voltage=(0, max_voltage),
drain_current=(0, 0), # effectively no current
gate_voltage=(self.control.link().output_thresholds.upper(), self.control.link().voltage.upper())
))
self.ctl_diode = self.Block(Diode(
reverse_voltage=(0, max_voltage),
current=RangeExpr.ZERO, # effectively no current
voltage_drop=(0, 0.4)*Volt, # TODO kind of arbitrary - should be parameterized
reverse_recovery_time=RangeExpr.ALL
))
self.import_kicad(self.file_path("resources", f"{self.__class__.__name__}.kicad_sch"),
conversions={
'pwr_in': VoltageSink(
current_draw=self.pwr_out.link().current_drawn,
voltage_limits=RangeExpr.ALL,
),
'pwr_out': VoltageSource(
voltage_out=self.pwr_in.link().voltage,
current_limits=RangeExpr.ALL,
),
'control': DigitalSink(), # TODO more modeling here?
'gnd': Ground(),
'btn_out': DigitalSingleSource(
voltage_out=self.gnd.link().voltage, # TODO model diode drop,
output_thresholds=(self.gnd.link().voltage.upper(), float('inf')),
low_signal_driver=True
)
})

Original FetPowerGate

  • With added switch
    class FetPowerGate(FetPowerGateNoBtn):
    def contents(self):
    max_voltage = self.control.link().voltage.upper()
    self.btn_diode = self.Block(Diode(
    reverse_voltage=(0, max_voltage),
    current=RangeExpr.ZERO, # effectively no current
    voltage_drop=(0, 0.4)*Volt, # TODO kind of arbitrary - should be parameterized
    reverse_recovery_time=RangeExpr.ALL
    ))
    self.btn = self.Block(Switch(voltage=0*Volt(tol=0))) # TODO - actually model switch voltage
    super().contents()

With DirSwitch

  • Using FetPowerGateNoBtn, adding directional switch to be used as the power button
    class OrPowerGateDirSw(OrPowerGate):
    @init_in_parent
    def __init__(self, diode_voltage_drop: RangeLike, fet_rds_on: RangeLike) -> None:
    super().__init__(diode_voltage_drop, fet_rds_on)
    self.dir = self.Block(DigitalDirectionSwitch())
    self.dir_a = self.Export(self.dir.a)
    self.dir_b = self.Export(self.dir.b)
    self.dir_c = self.Export(self.dir.c)
    self.dir_d = self.Export(self.dir.d)
    def contents(self):
    Block.contents(self) # TODO: generalize the base class, currently completely overwriting it
    # POWER
    with self.implicit_connect(
    ImplicitConnect(self.gnd, [Common]),
    ) as imp:
    (self.fuse, self.gate, self.prot, self.tp), _ = self.chain(
    self.pwr_lo,
    imp.Block(SeriesPowerPptcFuse((2, 4)*Amp)),
    imp.Block(FetPowerGateNoBtn()),
    imp.Block(ProtectionZenerDiode(voltage=(4.5, 6.0)*Volt)),
    self.Block(VoltageTestPoint()))
    self.vbatt = self.connect(self.gate.pwr_out) # downstream of fuse
    self.connect(self.dir.gnd, self.gnd)
    max_voltage = self.gate.btn_out.link().voltage.upper()
    self.btn_diode = imp.Block(Diode(
    reverse_voltage=(0, max_voltage),
    current=RangeExpr.ZERO, # effectively no current
    voltage_drop=(0, 0.4)*Volt, # TODO kind of arbitrary - should be parameterized
    reverse_recovery_time=RangeExpr.ALL
    ))
    self.gate_btn_out = self.connect(self.gate.btn_out, self.btn_diode.cathode.adapt_to(DigitalSink()))
    self.connect(self.dir.with_mixin(DigitalDirectionSwitchCenter()).center, self.gate.btn_out)
    self.connect(self.btn_diode.anode.adapt_to(DigitalSink()), self.btn_out)
    self.pwr_or = self.Block(PriorityPowerOr( # also does reverse protection
    self.diode_voltage_drop, self.fet_rds_on
    )).connected_from(self.gnd, self.pwr_hi, self.gate.pwr_out)
    self.connect(self.pwr_or.pwr_out, self.pwr_out)
    # Power gait
    self.connect(self.gate.control, self.control)

Usage

self.gate = self.Block(OrPowerGateDirSw((0, 1)*Volt, (0, 0.1)*Ohm)).connected_from(self.gnd, self.vusb, self.batt.pwr)
self.pwr = self.connect(self.gate.pwr_out)

#334 merged