Diode-based pull-up/pull-down calculator
by eddyb
HTML
<h1>Diode-based pull-up/pull-down calculator</h1>
<form>
<table><tr><td colspan="2"><h2>Standard circuit</h2></td></tr>
<tr><td>Preset: </td><td><select name="preset">
<option value="V=3.3,R_pu=1.5,R_pd=15,V_hi_min=2.8,V_lo_max=0.3">USB D-/D+</option>
<option value="V=3.3,R_pu=36,R_pd=5.1,V_hi_min=1.04,V_lo_max=0.075">USB-C CC</option>
</select></td></tr>
<tr><td>V<sub>cc</sub> = </td><td><input size="4" name="V"> V</td></tr>
<tr><td>R<sub>pull-up</sub> = </td><td><input size="4" name="R_pu"> kΩ</td></tr>
<tr><td>R<sub>pull-down</sub> = </td><td><input size="4" name="R_pd"> kΩ</td></tr>
<tr><td colspan="2"><h2/></td></tr>
<tr><td>V<sub>idle</sub> = </td><td><input size="4" name="V_idle" readonly> V</td></tr>
<tr><td>I<sub>idle</sub> = </td><td><input size="4" name="I_idle" readonly> mA</td></tr>
<tr><td colspan="2"><h2/></td></tr>
<tr><td colspan="2"><b>Drivers</b></td></tr>
<tr><td>V<sub>logic high</sub> ≥ </td><td><input size="4" name="V_hi_min"> V</td></tr>
<tr><td>V<sub>logic low</sub> ≤ </td><td><input size="4" name="V_lo_max"> V</td></tr>
<tr><td colspan="2"><h2/></td></tr>
<tr><td>Z<sub>drive high</sub> ≤ </td><td><input size="4" name="Z_hi_max" readonly> kΩ</td></tr>
<tr><td>Z<sub>drive low</sub> ≤ </td><td><input size="4" name="Z_lo_max" readonly> kΩ</td></tr>
</table>
<table><tr><td colspan="2"><h2>Diode-based circuit</h2></td></tr>
<tr><td>V<sub>pull-up diode drop</sub> = </td><td><input size="4" name="V_d_pu" value="0.220"> V</td></tr>
<tr><td>R<sub>pull-up</sub> = </td><td><input size="4" name="R_d_pu" value="1"> kΩ</td></tr>
<tr><td>R<sub>pull-down</sub> = </td><td><input size="4" name="R_d_pd" value="1"> kΩ</td></tr>
<tr><td>V<sub>pull-down diode drop</sub> = </td><td><input size="4" name="V_d_pd" value="0.220"> V</td></tr>
<tr><td colspan="2"><h2/></td></tr>
<tr><td colspan="2"><b>Match V<sub>idle</sub> exactly</b> <button type="button" name="use_match_V_idle">Use</button></td></tr>
...
CSS
table {
display: inline-block;
border: 1px solid grey;
vertical-align: top;
}
tr > td > h2 {
margin: 0.5em;
border-bottom: 1px solid grey;
}
tr > td:first-child, input {
text-align: right;
}
tr > td:first-child[colspan] {
text-align: center;
}
JavaScript
function voltage_divider(pus, pds) {
let V = Math.max(...pus.map(pu => pu.V));
let R_pu = 1/pus.map(pu => 1/pu.R).reduce((a, b) => a + b);
let R_pd = 1/pds.map(pd => 1/pd.R).reduce((a, b) => a + b);
// Figure out the equivalent extra currents, that would
// pass through resistors, if no diodes were present.
let I_pu_bypass = pus.map(pu => (V - pu.V) / pu.R).reduce((a, b) => a + b);
let I_pd_bypass = pds.map(pd => pd.V / pd.R).reduce((a, b) => a + b);
return (V / R_pu - I_pu_bypass + I_pd_bypass) * (1/(1/R_pd + 1/R_pu));
}
function compute({V, R_pu, R_pd, V_hi_min, V_lo_max, V_d_pu, R_d_pu, R_d_pd, V_d_pd}) {
let pus = [{V, R: R_pu}, {V: V - V_d_pu, R: R_d_pu, suffix: '_d_pu'}];
let pds = [{V: 0, R: R_pd}, {V: V_d_pu, R: R_d_pd, suffix: '_d_pd'}];
let o = {};
for(let pu of pus)
for(let pd of pds) {
let V_idle = voltage_divider([pu], [pd]);
let suffix = (pu.suffix || '') + (pd.suffix || '') + '_idle';
o['V' + suffix] = V_idle;
o['I' + suffix] = (V_idle - pd.V) / pd.R;
}
// FIXME: deduplicate these.
o.R_d_pu_match_V_idle = R_pd * ((V - V_d_pu - o.V_idle) / o.V_idle);
o.R_d_pd_match_V_idle = R_pu * ((o.V_idle - V_d_pd) / (V - o.V_idle));
o.R_d_pu_match_V_idle_hi = R_pd * ((V - V_d_pu - V_hi_min) / V_hi_min);
o.R_d_pd_match_V_idle_hi = R_pu * ((V_hi_min - V_d_pd) / (V - V_hi_min));
o.R_d_pu_match_V_idle_lo = R_pd * ((V - V_d_pu - V_lo_max) / V_lo_max);
o.R_d_pd_match_V_idle_lo = R_pu * ((V_lo_max - V_d_pd) / (V - V_lo_max));
// NOTE: commented out formulas are for PU+PD+Z_{hi,lo}, as oposed to PD+Z_lo and PU+Z_hi.
o.Z_hi_max = R_pd * ((V - V_hi_min) / V_hi_min); // (1 / (1 / (R_pd * ((V - V_hi_min) / V_hi_min)) - 1 / R_pu));
o.Z_lo_max = R_pu * (V_lo_max / (V - V_lo_max)); // (1 / (1 / (R_pu * (V_lo_max / (V - V_lo_max))) - 1 / R_pd));
for(let side of ['hi', 'lo']) {
let Z_drive = o['Z_' + side + '_max'];
for(let pu of pus) {
let drive_pus = [pu];
if(side == 'hi')
...