-
Notifications
You must be signed in to change notification settings - Fork 12
Ramp limiter generator #420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
61add01
6f69192
9cf80cd
624ff80
d8978f5
4fb6387
cba7e86
e761549
fa7c97f
961464c
ae8cfa7
c0959b3
516a8d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,10 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from ..electronics_model import * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .Resettable import Resettable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .AbstractResistor import Resistor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .AbstractFets import SwitchFet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .AbstractFets import SwitchFet, Fet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .AbstractCapacitor import Capacitor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .GateDrivers import HalfBridgeDriver, HalfBridgeDriverIndependent, HalfBridgeDriverPwm | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .ResistiveDivider import VoltageDivider, ResistiveDivider | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .Categories import PowerConditioner | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -118,3 +120,109 @@ def generate(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.connect(self.pwm_ctl, self.driver.with_mixin(HalfBridgeDriverPwm()).pwm_in) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self.get(self.reset.is_connected()): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.connect(self.reset, self.driver.with_mixin(Resettable()).reset) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class RampLimiter(KiCadSchematicBlock): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """PMOS-based ramp limiter that roughly targets a constant-dV/dt ramp. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The cgd should be specified to swamp (10x+) the parasitic Cgd of the FET to get more controlled parameters. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The target ramp rate is in volts/second, and for a capacitive load this can be calculated from a target current with | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| I = C * dV/dt => dV/dt = I / C | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The actual ramp rate will vary substantially, the values calculated are based on many assertions. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A target Vgs can also be specified, this is the final Vgs of the FET after the ramp completes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The FET will be constrained to have a Vgs,th below the minimum of this range and a Vgs,max above the maximum. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A capacitive divider with Cgs will be generated so the target initial Vgs at less than half the FET Vgs,th | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (targeting half Vgs,th at Vin,max). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TODO: allow control to be optional, eliminating the NMOS with a short | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HOW THIS WORKS: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| When the input voltage rises, the capacitive divider of Cgs, Cgd brings the gate to a subthreshold voltage. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The gate voltage charges via the divider until it gets to the threshold voltage. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| At around the threshold voltage, the FET begins to turn on, with current flowing into (and charging) the output. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| As the output rises, Cgd causes the gate to be pulled up with the output, keeping Vgs roughly constant. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (this also keeps the current roughly constant, mostly regardless of transconductance) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| During this stage, if we assume Vgs is constant, then Cgs is constant and can be disregarded. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For the output to rise, Vgd must rise, which means Cgd must charge, and the current must go through the divider. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assuming a constant Vgs (and absolute gate voltage), the current into the divider is constant, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and this is how the voltage ramp rate is controlled. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Once the output gets close to the input voltage, Cgd stops charging and Vgs rises, turning the FET fully on. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Note that Vgs,th is an approximate parameter and the ramp current is likely larger than the Vgs,th current. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Vgs also may rise during the ramp, meaning some current goes into charging Cgs. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| References: https://www.ti.com/lit/an/slva156/slva156.pdf, https://www.ti.com/lit/an/slyt096/slyt096.pdf, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| https://youtu.be/bOka13RtOXM | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Additional more complex circuits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| https://electronics.stackexchange.com/questions/294061/p-channel-mosfet-inrush-current-limiting | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @init_in_parent | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __init__(self, *, cgd: RangeLike = 10*nFarad(tol=0.5), target_ramp: RangeLike = 1000*Volt(tol=0.25), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| target_vgs: RangeLike = (4, 10)*Volt, max_rds: FloatLike = 1*Ohm, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _cdiv_vgs_factor: RangeLike = (0.05, 0.75)): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super().__init__() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.gnd = self.Port(Ground.empty(), [Common]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.pwr_in = self.Port(VoltageSink.empty(), [Input]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.pwr_out = self.Port(VoltageSource.empty(), [Output]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.control = self.Port(DigitalSink.empty()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.cgd = self.ArgParameter(cgd) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.target_ramp = self.ArgParameter(target_ramp) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.target_vgs = self.ArgParameter(target_vgs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.max_rds = self.ArgParameter(max_rds) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._cdiv_vgs_factor = self.ArgParameter(_cdiv_vgs_factor) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def contents(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super().contents() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pwr_voltage = self.pwr_in.link().voltage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.drv = self.Block(SwitchFet.PFet( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| drain_voltage=pwr_voltage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| drain_current=self.pwr_out.link().current_drawn, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gate_voltage=(0 * Volt(tol=0)).hull(self.target_vgs.upper()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gate_threshold_voltage=(0 * Volt(tol=0)).hull(self.target_vgs.lower()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rds_on=(0, self.max_rds) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.cap_gd = self.Block(Capacitor( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| capacitance=self.cgd, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| voltage=(0 * Volt(tol=0)).hull(self.pwr_in.link().voltage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # treat Cgs and Cgd as a capacitive divider with Cgs on the bottom | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.cap_gs = self.Block(Capacitor( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| capacitance=( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (1/(self.drv.actual_gate_drive.lower()*self._cdiv_vgs_factor)).shrink_multiply(self.pwr_in.link().voltage) - 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).shrink_multiply( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.cap_gd.actual_capacitance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| voltage=(0 * Volt(tol=0)).hull(self.pwr_in.link().voltage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+196
to
+202
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| capacitance=( | |
| (1/(self.drv.actual_gate_drive.lower()*self._cdiv_vgs_factor)).shrink_multiply(self.pwr_in.link().voltage) - 1 | |
| ).shrink_multiply( | |
| self.cap_gd.actual_capacitance | |
| ), | |
| voltage=(0 * Volt(tol=0)).hull(self.pwr_in.link().voltage) | |
| )) | |
| capacitance=self._calculate_gs_capacitance( | |
| self.drv.actual_gate_drive.lower(), | |
| self._cdiv_vgs_factor, | |
| self.pwr_in.link().voltage, | |
| self.cap_gd.actual_capacitance | |
| ), | |
| voltage=(0 * Volt(tol=0)).hull(self.pwr_in.link().voltage) | |
| )) | |
| def _calculate_gs_capacitance(self, actual_gate_drive_lower, cdiv_vgs_factor, pwr_in_voltage, cap_gd_actual_capacitance): | |
| """ | |
| Calculates the gate-source capacitance (Cgs) for the capacitive divider. | |
| Formula derivation: | |
| Treats Cgs and Cgd as a capacitive divider with Cgs on the bottom. | |
| The calculation is: | |
| Cgs = [ (1 / (actual_gate_drive_lower * cdiv_vgs_factor)) * pwr_in_voltage - 1 ] * cap_gd_actual_capacitance | |
| where: | |
| - actual_gate_drive_lower: The lower bound of the gate drive voltage. | |
| - cdiv_vgs_factor: A scaling factor for the divider. | |
| - pwr_in_voltage: The input power rail voltage. | |
| - cap_gd_actual_capacitance: The actual gate-drain capacitance. | |
| Returns the calculated Cgs value. | |
| """ | |
| divider = (1 / (actual_gate_drive_lower * cdiv_vgs_factor)) | |
| result = (divider.shrink_multiply(pwr_in_voltage) - 1).shrink_multiply(cap_gd_actual_capacitance) | |
| return result |
Copilot
AI
Sep 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The impedance calculation is complex and spans multiple lines. Consider extracting this calculation into a helper method with documentation explaining the relationship between target_ramp, gate_drive, and capacitance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change from positional to keyword-only arguments for frequency and drive_current may break existing code that calls this constructor with positional arguments. This is a breaking API change that should be documented or handled with backward compatibility.