/* Copyright (C) Paul Falstad and Iain Sharp This file is part of CircuitJS1. CircuitJS1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. CircuitJS1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CircuitJS1. If not, see <http://www.gnu.org/licenses/>. */ package com.lushprojects.circuitjs1.client; //import java.awt.*; //import java.util.StringTokenizer; class MosfetElm extends CircuitElm { int pnp; int FLAG_PNP = 1; int FLAG_SHOWVT = 2; int FLAG_DIGITAL = 4; int FLAG_FLIP = 8; int FLAG_HIDE_BULK = 16; int FLAGS_GLOBAL = (FLAG_HIDE_BULK|FLAG_DIGITAL); double vt; // beta = 1/(RdsON*(Vgs-Vt)) double beta; static int globalFlags; MosfetElm(int xx, int yy, boolean pnpflag) { super(xx, yy); pnp = (pnpflag) ? -1 : 1; flags = (pnpflag) ? FLAG_PNP : 0; noDiagonal = true; beta = getDefaultBeta(); vt = getDefaultThreshold(); } public MosfetElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { super(xa, ya, xb, yb, f); pnp = ((f & FLAG_PNP) != 0) ? -1 : 1; noDiagonal = true; vt = getDefaultThreshold(); beta = getDefaultBeta(); try { vt = new Double(st.nextToken()).doubleValue(); beta = new Double(st.nextToken()).doubleValue(); } catch (Exception e) {} globalFlags = flags & (FLAGS_GLOBAL); } double getDefaultThreshold() { return 1.5; } double getDefaultBeta() { return .02; } boolean nonLinear() { return true; } boolean drawDigital() { return (flags & FLAG_DIGITAL) != 0; } boolean showBulk() { return (flags & (FLAG_DIGITAL|FLAG_HIDE_BULK)) == 0; } void reset() { lastv1 = lastv2 = volts[0] = volts[1] = volts[2] = curcount = 0; } String dump() { return super.dump() + " " + vt + " " + beta; } int getDumpType() { return 'f'; } final int hs = 16; void draw(Graphics g) { // pick up global flags changes if ((flags & FLAGS_GLOBAL) != globalFlags) setPoints(); setBbox(point1, point2, hs); // draw source/drain terminals setVoltageColor(g, volts[1]); drawThickLine(g, src[0], src[1]); setVoltageColor(g, volts[2]); drawThickLine(g, drn[0], drn[1]); // draw line connecting source and drain int segments = 6; int i; setPowerColor(g, true); double segf = 1./segments; boolean enhancement = vt > 0 && showBulk(); for (i = 0; i != segments; i++) { if ((i == 1 || i == 4) && enhancement) continue; double v = volts[1]+(volts[2]-volts[1])*i/segments; setVoltageColor(g, v); interpPoint(src[1], drn[1], ps1, i*segf); interpPoint(src[1], drn[1], ps2, (i+1)*segf); drawThickLine(g, ps1, ps2); } // draw little extensions of that line setVoltageColor(g, volts[1]); drawThickLine(g, src[1], src[2]); setVoltageColor(g, volts[2]); drawThickLine(g, drn[1], drn[2]); if (showBulk()) { setVoltageColor(g, pnp == -1 ? volts[2] : volts[1]); drawThickLine(g, pnp == -1 ? drn[0] : src[0], body[0]); drawThickLine(g, body[0], body[1]); } // draw arrow if (!drawDigital()) { setVoltageColor(g, pnp == -1 ? volts[2] : volts[1]); g.fillPolygon(arrowPoly); } if (sim.powerCheckItem.getState()) g.setColor(Color.gray); // draw gate setVoltageColor(g, volts[0]); drawThickLine(g, point1, gate[1]); drawThickLine(g, gate[0], gate[2]); if (drawDigital() && pnp == -1) drawThickCircle(g, pcircle.x, pcircle.y, pcircler); if ((flags & FLAG_SHOWVT) != 0) { String s = "" + (vt*pnp); g.setColor(whiteColor); g.setFont(unitsFont); drawCenteredText(g, s, x2+2, y2, false); } if ((needsHighlight() || sim.dragElm == this) && dy == 0) { g.setColor(Color.white); g.setFont(unitsFont); int ds = sign(dx); g.drawString("G", gate[1].x-10*ds, gate[1].y-5); g.drawString(pnp == -1 ? "D" : "S", src[0].x-3+9*ds, src[0].y+4); // x+6 if ds=1, -12 if -1 g.drawString(pnp == -1 ? "S" : "D", drn[0].x-3+9*ds, drn[0].y+4); } curcount = updateDotCount(-ids, curcount); drawDots(g, src[0], src[1], curcount); drawDots(g, src[1], drn[1], curcount); drawDots(g, drn[1], drn[0], curcount); drawPosts(g); } Point getPost(int n) { return (n == 0) ? point1 : (n == 1) ? src[0] : drn[0]; } double getCurrent() { return ids; } double getPower() { return ids*(volts[2]-volts[1]); } int getPostCount() { return 3; } int pcircler; Point src[], drn[], gate[], body[], pcircle; Polygon arrowPoly; void setPoints() { super.setPoints(); // these two flags apply to all mosfets flags &= ~FLAGS_GLOBAL; flags |= globalFlags; // find the coordinates of the various points we need to draw // the MOSFET. int hs2 = hs*dsign; if ((flags & FLAG_FLIP) != 0) hs2 = -hs2; src = newPointArray(3); drn = newPointArray(3); interpPoint2(point1, point2, src[0], drn[0], 1, -hs2); interpPoint2(point1, point2, src[1], drn[1], 1-22/dn, -hs2); interpPoint2(point1, point2, src[2], drn[2], 1-22/dn, -hs2*4/3); gate = newPointArray(3); interpPoint2(point1, point2, gate[0], gate[2], 1-28/dn, hs2/2); // was 1-20/dn interpPoint(gate[0], gate[2], gate[1], .5); if (showBulk()) { body = newPointArray(2); interpPoint(src[0], drn[0], body[0], .5); interpPoint(src[1], drn[1], body[1], .5); } if (!drawDigital()) { if (pnp == 1) { if (!showBulk()) arrowPoly = calcArrow(src[1], src[0], 10, 4); else arrowPoly = calcArrow(body[0], body[1], 12, 5); } else { if (!showBulk()) arrowPoly = calcArrow(drn[0], drn[1], 12, 5); else arrowPoly = calcArrow(body[1], body[0], 12, 5); } } else if (pnp == -1) { interpPoint(point1, point2, gate[1], 1-36/dn); int dist = (dsign < 0) ? 32 : 31; pcircle = interpPoint(point1, point2, 1-dist/dn); pcircler = 3; } } double lastv1, lastv2; double ids; int mode = 0; double gm = 0; void stamp() { sim.stampNonLinear(nodes[1]); sim.stampNonLinear(nodes[2]); } boolean nonConvergence(double last, double now) { double diff = Math.abs(last-now); // difference of less than 10mV is fine if (diff < .01) return false; // larger differences are fine if value is large if (sim.subIterations > 10 && diff < Math.abs(now)*.001) return false; // if we're having trouble converging, get more lenient if (sim.subIterations > 100 && diff < .01+(sim.subIterations-100)*.0001) return false; return true; } void doStep() { double vs[] = new double[3]; vs[0] = volts[0]; vs[1] = volts[1]; vs[2] = volts[2]; if (vs[1] > lastv1 + .5) vs[1] = lastv1 + .5; if (vs[1] < lastv1 - .5) vs[1] = lastv1 - .5; if (vs[2] > lastv2 + .5) vs[2] = lastv2 + .5; if (vs[2] < lastv2 - .5) vs[2] = lastv2 - .5; int source = 1; int drain = 2; // if source voltage > drain (for NPN), swap source and drain // (opposite for PNP) if (pnp*vs[1] > pnp*vs[2]) { source = 2; drain = 1; } int gate = 0; double vgs = vs[gate ]-vs[source]; double vds = vs[drain]-vs[source]; if (nonConvergence(lastv1, vs[1]) || nonConvergence(lastv2, vs[2])) sim.converged = false; lastv1 = vs[1]; lastv2 = vs[2]; double realvgs = vgs; double realvds = vds; vgs *= pnp; vds *= pnp; ids = 0; gm = 0; double Gds = 0; if (vgs > .5 && this instanceof JfetElm) { sim.stop("JFET is reverse biased!", this); return; } if (vgs < vt) { // should be all zero, but that causes a singular matrix, // so instead we treat it as a large resistor Gds = 1e-8; ids = vds*Gds; mode = 0; } else if (vds < vgs-vt) { // linear ids = beta*((vgs-vt)*vds - vds*vds*.5); gm = beta*vds; Gds = beta*(vgs-vds-vt); mode = 1; } else { // saturation; Gds = 0 gm = beta*(vgs-vt); // use very small Gds to avoid nonconvergence Gds = 1e-8; ids = .5*beta*(vgs-vt)*(vgs-vt) + (vds-(vgs-vt))*Gds; mode = 2; } double rs = -pnp*ids + Gds*realvds + gm*realvgs; //System.out.println("M " + vds + " " + vgs + " " + ids + " " + gm + " "+ Gds + " " + volts[0] + " " + volts[1] + " " + volts[2] + " " + source + " " + rs + " " + this); sim.stampMatrix(nodes[drain], nodes[drain], Gds); sim.stampMatrix(nodes[drain], nodes[source], -Gds-gm); sim.stampMatrix(nodes[drain], nodes[gate], gm); sim.stampMatrix(nodes[source], nodes[drain], -Gds); sim.stampMatrix(nodes[source], nodes[source], Gds+gm); sim.stampMatrix(nodes[source], nodes[gate], -gm); sim.stampRightSide(nodes[drain], rs); sim.stampRightSide(nodes[source], -rs); if (source == 2 && pnp == 1 || source == 1 && pnp == -1) ids = -ids; } @SuppressWarnings("static-access") void getFetInfo(String arr[], String n) { arr[0] = sim.LS(((pnp == -1) ? "p-" : "n-") + n); arr[0] += " (Vt = " + getVoltageText(pnp*vt) + ")"; arr[1] = ((pnp == 1) ? "Ids = " : "Isd = ") + getCurrentText(ids); arr[2] = "Vgs = " + getVoltageText(volts[0]-volts[pnp == -1 ? 2 : 1]); arr[3] = ((pnp == 1) ? "Vds = " : "Vsd = ") + getVoltageText(volts[2]-volts[1]); arr[4] = sim.LS((mode == 0) ? "off" : (mode == 1) ? "linear" : "saturation"); arr[5] = "gm = " + getUnitText(gm, "A/V"); } void getInfo(String arr[]) { getFetInfo(arr, "MOSFET"); } @Override String getScopeText(int v) { return sim.LS(((pnp == -1) ? "p-" : "n-") + "MOSFET"); } boolean canViewInScope() { return true; } double getVoltageDiff() { return volts[2] - volts[1]; } boolean getConnection(int n1, int n2) { return !(n1 == 0 || n2 == 0); } public EditInfo getEditInfo(int n) { if (n == 0) return new EditInfo("Threshold Voltage", pnp*vt, .01, 5); if (n == 1) return new EditInfo("Beta", beta, .01, 5); if (n == 2) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.checkbox = new Checkbox("Digital Symbol", drawDigital()); return ei; } if (n == 3) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.checkbox = new Checkbox("Show Bulk", showBulk()); return ei; } if (n == 4) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.checkbox = new Checkbox("Swap D/S", (flags & FLAG_FLIP) != 0); return ei; } return null; } public void setEditValue(int n, EditInfo ei) { if (n == 0) vt = pnp*ei.value; if (n == 1) beta = ei.value; if (n == 2) { globalFlags = (ei.checkbox.getState()) ? (globalFlags|FLAG_DIGITAL) : (globalFlags & ~FLAG_DIGITAL); setPoints(); } if (n == 3) { globalFlags = (!ei.checkbox.getState()) ? (globalFlags|FLAG_HIDE_BULK) : (globalFlags & ~FLAG_HIDE_BULK); setPoints(); } if (n == 4) { flags = (ei.checkbox.getState()) ? (flags | FLAG_FLIP) : (flags & ~FLAG_FLIP); setPoints(); } } double getCurrentIntoPoint(int xa, int ya) { if (xa == x && ya == y) return 0; if (xa == src[0].x && ya == src[0].y) return ids; return -ids; } }