@@ -377,3 +377,111 @@ def contents(self):
377377 ),
378378 'gnd' : Ground (),
379379 })
380+
381+
382+ class SoftPowerGate (PowerSwitch , KiCadSchematicBlock , Block ): # migrate from the multimater
383+ """A high-side PFET power gate that has a button to power on, can be latched on by an external signal,
384+ and provides the button output as a signal.
385+ """
386+
387+ @init_in_parent
388+ def __init__ (self , pull_resistance : RangeLike = 10 * kOhm (tol = 0.05 ), amp_resistance : RangeLike = 10 * kOhm (tol = 0.05 ),
389+ diode_drop : RangeLike = (0 , 0.4 ) * Volt ):
390+ super ().__init__ ()
391+ self .pwr_in = self .Port (VoltageSink .empty (), [Input ])
392+ self .pwr_out = self .Port (VoltageSource .empty (), [Output ], doc = "Gate controlled power out" )
393+ self .gnd = self .Port (Ground .empty (), [Common ])
394+
395+ self .btn_out = self .Port (DigitalSingleSource .empty (), optional = True ,
396+ doc = "Allows the button state to be read independently of the control signal" )
397+ self .btn_in = self .Port (DigitalBidir .empty (), doc = "Should be connected to a button output. Do not connect IO" )
398+ self .control = self .Port (DigitalSink .empty (), doc = "external control to latch the power on" ) # digital level control - gnd-referenced NFET gate
399+
400+ self .pull_resistance = self .ArgParameter (pull_resistance )
401+ self .amp_resistance = self .ArgParameter (amp_resistance )
402+ self .diode_drop = self .ArgParameter (diode_drop )
403+
404+ def contents (self ):
405+ super ().contents ()
406+ control_voltage = self .btn_in .link ().voltage .hull (self .gnd .link ().voltage )
407+ pwr_voltage = self .pwr_out .link ().voltage .hull (self .gnd .link ().voltage )
408+ pwr_current = self .pwr_out .link ().current_drawn .hull (RangeExpr .ZERO )
409+
410+ self .pull_res = self .Block (Resistor (
411+ resistance = self .pull_resistance
412+ ))
413+ self .pwr_fet = self .Block (Fet .PFet (
414+ drain_voltage = pwr_voltage ,
415+ drain_current = pwr_current ,
416+ gate_voltage = (- control_voltage .upper (), control_voltage .upper ()), # TODO this ignores the diode drop
417+ ))
418+
419+ self .amp_res = self .Block (Resistor (
420+ resistance = self .amp_resistance
421+ ))
422+ self .amp_fet = self .Block (Fet .NFet (
423+ drain_voltage = control_voltage ,
424+ drain_current = RangeExpr .ZERO , # effectively no current
425+ gate_voltage = (self .control .link ().output_thresholds .upper (), self .control .link ().voltage .upper ())
426+ ))
427+
428+ self .ctl_diode = self .Block (Diode (
429+ reverse_voltage = control_voltage ,
430+ current = RangeExpr .ZERO , # effectively no current
431+ voltage_drop = self .diode_drop ,
432+ reverse_recovery_time = RangeExpr .ALL
433+ ))
434+
435+ self .btn_diode = self .Block (Diode (
436+ reverse_voltage = control_voltage ,
437+ current = RangeExpr .ZERO , # effectively no current
438+ voltage_drop = self .diode_drop ,
439+ reverse_recovery_time = RangeExpr .ALL
440+ ))
441+
442+ self .import_kicad (self .file_path ("resources" , f"{ self .__class__ .__name__ } .kicad_sch" ),
443+ conversions = {
444+ 'pwr_in' : VoltageSink (
445+ current_draw = self .pwr_out .link ().current_drawn ,
446+ voltage_limits = RangeExpr .ALL ,
447+ ),
448+ 'pwr_out' : VoltageSource (
449+ voltage_out = self .pwr_in .link ().voltage ,
450+ current_limits = RangeExpr .ALL ,
451+ ),
452+ 'control' : DigitalSink (), # TODO more modeling here?
453+ 'gnd' : Ground (),
454+ 'btn_out' : DigitalSingleSource (
455+ voltage_out = self .gnd .link ().voltage ,
456+ output_thresholds = (self .gnd .link ().voltage .upper (), float ('inf' )),
457+ low_signal_driver = True
458+ ),
459+ 'btn_in' : DigitalBidir (
460+ voltage_out = self .gnd .link ().voltage ,
461+ output_thresholds = (self .gnd .link ().voltage .upper (), float ('inf' )),
462+ pullup_capable = True ,
463+ )
464+ })
465+
466+
467+ class SoftPowerSwitch (PowerSwitch , Block ):
468+ """A software power switch that adds a power button a user can turn on
469+ """
470+ @init_in_parent
471+ def __init__ (self , pull_resistance : RangeLike = 10 * kOhm (tol = 0.05 ), amp_resistance : RangeLike = 10 * kOhm (tol = 0.05 ),
472+ diode_drop : RangeLike = (0 , 0.4 ) * Volt ):
473+ super ().__init__ ()
474+ self .pwr_gate = self .Block (SoftPowerGate (pull_resistance , amp_resistance , diode_drop ))
475+ self .gnd = self .Export (self .pwr_gate .gnd , [Common ])
476+ self .pwr_in = self .Export (self .pwr_gate .pwr_in , [Input ])
477+ self .pwr_out = self .Export (self .pwr_gate .pwr_out , [Output ])
478+ self .btn_out = self .Export (self .pwr_gate .btn_out )
479+ self .control = self .Export (self .pwr_gate .control )
480+
481+ def contents (self ):
482+ super ().contents ()
483+ with self .implicit_connect (
484+ ImplicitConnect (self .gnd , [Common ]),
485+ ) as imp :
486+ self .btn = imp .Block (DigitalSwitch ())
487+ self .connect (self .pwr_gate .btn_in , self .btn .out )
0 commit comments