mirror of
https://github.com/pfalstad/circuitjs1.git
synced 2026-03-25 01:36:57 +01:00
add LM324 model to real op-amp
allow internal labeled nodes in subcircuits printing would often try to print the circuit before the image was loaded
This commit is contained in:
@@ -70,6 +70,7 @@ import com.google.gwt.user.client.ui.MenuBar;
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.Event;
|
||||
import com.google.gwt.user.client.Timer;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
@@ -5174,7 +5175,7 @@ MouseOutHandler, MouseWheelHandler {
|
||||
win.document.open();
|
||||
win.document.write('<img src="'+img+'"/>');
|
||||
win.document.close();
|
||||
win.print();
|
||||
setTimeout(function(){win.print();},1000);
|
||||
}-*/;
|
||||
|
||||
void doDCAnalysis() {
|
||||
@@ -5265,18 +5266,51 @@ MouseOutHandler, MouseWheelHandler {
|
||||
DiodeModel.clearDumpedFlags();
|
||||
Vector<ExtListEntry> extList = new Vector<ExtListEntry>();
|
||||
boolean sel = isSelection();
|
||||
|
||||
// mapping of node labels -> node numbers
|
||||
HashMap<String, Integer> nodeNameHash = new HashMap<String, Integer>();
|
||||
|
||||
// mapping of node numbers -> equivalent node numbers (if they both have the same label)
|
||||
HashMap<Integer, Integer> nodeNumberHash = new HashMap<Integer, Integer>();
|
||||
|
||||
// find all the labeled nodes, get a list of them, and create a node number map
|
||||
for (i = 0; i != elmList.size(); i++) {
|
||||
CircuitElm ce = getElm(i);
|
||||
if (sel && !ce.isSelected())
|
||||
continue;
|
||||
if (ce instanceof WireElm)
|
||||
continue;
|
||||
if (ce instanceof LabeledNodeElm) {
|
||||
String label = ((LabeledNodeElm) ce).text;
|
||||
LabeledNodeElm lne = (LabeledNodeElm) ce;
|
||||
String label = lne.text;
|
||||
Integer map = nodeNameHash.get(label);
|
||||
|
||||
// this node name already seen? map the new node number to the old one
|
||||
if (map != null) {
|
||||
Integer val = nodeNumberHash.get(lne.getNode(0));
|
||||
if (val != null && !val.equals(map)) {
|
||||
Window.alert("Can't have a node with two labels!");
|
||||
return null;
|
||||
}
|
||||
nodeNumberHash.put(lne.getNode(0), map);
|
||||
continue;
|
||||
}
|
||||
nodeNameHash.put(label, lne.getNode(0));
|
||||
// put an entry in nodeNumberHash so we can detect if we try to map it to something else later
|
||||
nodeNumberHash.put(lne.getNode(0), lne.getNode(0));
|
||||
if (lne.isInternal())
|
||||
continue;
|
||||
// create ext list entry for external nodes
|
||||
ExtListEntry ent = new ExtListEntry(label, ce.getNode(0));
|
||||
extList.add(ent);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// output all the elements
|
||||
for (i = 0; i != elmList.size(); i++) {
|
||||
CircuitElm ce = getElm(i);
|
||||
if (sel && !ce.isSelected())
|
||||
continue;
|
||||
if (ce instanceof WireElm || ce instanceof LabeledNodeElm)
|
||||
continue;
|
||||
if (ce instanceof GraphicElm)
|
||||
continue;
|
||||
int j;
|
||||
@@ -5284,13 +5318,12 @@ MouseOutHandler, MouseWheelHandler {
|
||||
nodeList += "\r";
|
||||
nodeList += ce.getClass().getSimpleName();
|
||||
for (j = 0; j != ce.getPostCount(); j++) {
|
||||
nodeList += " " + ce.getNode(j);
|
||||
int n = ce.getNode(j);
|
||||
Integer nobj = nodeNumberHash.get(n);
|
||||
int n0 = (nobj == null) ? n : nobj;
|
||||
nodeList += " " + n0;
|
||||
}
|
||||
|
||||
// String m = ce.dumpModel();
|
||||
// if (m != null && !m.isEmpty())
|
||||
// models += m + "\n";
|
||||
|
||||
// save positions
|
||||
int x1 = ce.x; int y1 = ce.y;
|
||||
int x2 = ce.x2; int y2 = ce.y2;
|
||||
|
||||
@@ -76,11 +76,6 @@ public class EditCompositeModelDialog extends DialogBox implements MouseDownHand
|
||||
});
|
||||
int i;
|
||||
int postCount = model.extList.size();
|
||||
for (i = 0; i != postCount-1; i++)
|
||||
if (model.extList.get(i).name.equals(model.extList.get(i+1).name)) {
|
||||
Window.alert(CirSim.LS("Input names must be unique"));
|
||||
return false;
|
||||
}
|
||||
|
||||
model.sizeX = 2;
|
||||
model.sizeY = (postCount+1)/2;
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.HashMap;
|
||||
|
||||
class LabeledNodeElm extends CircuitElm {
|
||||
final int FLAG_ESCAPE = 4;
|
||||
final int FLAG_INTERNAL = 1;
|
||||
|
||||
public LabeledNodeElm(int xx, int yy) {
|
||||
super(xx, yy);
|
||||
@@ -43,12 +44,13 @@ class LabeledNodeElm extends CircuitElm {
|
||||
}
|
||||
String dump() {
|
||||
flags |= FLAG_ESCAPE;
|
||||
return super.dump() + " " + " " + CustomLogicModel.escape(text);
|
||||
return super.dump() + " " + CustomLogicModel.escape(text);
|
||||
}
|
||||
|
||||
String text;
|
||||
static HashMap<String,Integer> nodeList;
|
||||
int nodeNumber;
|
||||
boolean isInternal() { return (flags & FLAG_INTERNAL) != 0; }
|
||||
|
||||
public static native void console(String text)
|
||||
/*-{
|
||||
@@ -134,11 +136,17 @@ class LabeledNodeElm extends CircuitElm {
|
||||
ei.text = text;
|
||||
return ei;
|
||||
}
|
||||
if (n == 1) {
|
||||
EditInfo ei = new EditInfo("", 0, -1, -1);
|
||||
ei.checkbox = new Checkbox("Internal Node", isInternal());
|
||||
return ei;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void setEditValue(int n, EditInfo ei) {
|
||||
if (n == 0) {
|
||||
if (n == 0)
|
||||
text = ei.textf.getText();
|
||||
}
|
||||
if (n == 1)
|
||||
flags = ei.changeFlag(flags, FLAG_INTERNAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.lushprojects.circuitjs1.client;
|
||||
public class OpAmpRealElm extends CompositeElm {
|
||||
|
||||
// from https://commons.wikimedia.org/wiki/File:OpAmpTransistorLevel_Colored_Labeled.svg
|
||||
private static String modelString =
|
||||
private static String model741String =
|
||||
"NTransistorElm 3 8 9\rNTransistorElm 2 8 10\rPTransistorElm 11 12 9\rPTransistorElm 11 13 10\rNTransistorElm 14 12 1\r" + // Q1-5
|
||||
"NTransistorElm 14 13 5\rNTransistorElm 12 7 14\rPTransistorElm 8 8 7\rPTransistorElm 8 11 7\rNTransistorElm 17 11 16\r" + // Q6-10
|
||||
"NTransistorElm 17 17 4\rPTransistorElm 18 18 7\rPTransistorElm 18 20 7\rNTransistorElm 20 7 25\rNTransistorElm 13 22 24\r" + // Q11-15
|
||||
@@ -12,10 +12,22 @@ public class OpAmpRealElm extends CompositeElm {
|
||||
"ResistorElm 15 6\rResistorElm 6 25\r" + // output resistors
|
||||
"ResistorElm 4 1\rResistorElm 4 14\rResistorElm 4 5\rResistorElm 4 16\rResistorElm 4 24\rResistorElm 4 23\rResistorElm 17 18\r" +
|
||||
"ResistorElm 22 21\rResistorElm 21 20\r";
|
||||
private static int[] modelExternalNodes = { 2, 3, 6, 7, 4 }; // , 1, 5 };
|
||||
private static int[] model741ExternalNodes = { 2, 3, 6, 7, 4 }; // , 1, 5 };
|
||||
// 0 = input -, 1 = input +, 2 = output, 3 = V+, 4 = V-, 5, 6 = offset null
|
||||
|
||||
private static double[] resistances = { 50, 25, 1e3, 50e3, 1e3, 5e3, 50e3, 50, 39e3, 7500, 4500 };
|
||||
private static String lm324ModelString =
|
||||
"TransistorElm 1 2 3\rCurrentElm 4 3\rTransistorElm 2 2 5\rTransistorElm 2 6 5\rCapacitorElm 6 7\rCurrentElm 4 8\rCurrentElm 4 7\rTransistorElm 8 4 9\r" +
|
||||
"TransistorElm 7 4 10\rTransistorElm 10 4 11\rTransistorElm 11 7 12\rResistorElm 11 12\rTransistorElm 7 5 12\rCurrentElm 12 5\rTransistorElm 6 5 8\r" +
|
||||
"ResistorElm 9 5\rTransistorElm 9 7 5\rTransistorElm 13 6 3";
|
||||
private static int[] lm324ExternalNodes = { 1, 13, 12, 4, 5 };
|
||||
private static String lm324ModelDump =
|
||||
"0 -1 -0 0 10000/0 0.000006/0 1 0 0 100/0 1 0 0 100/0 1e-11 0/0 0.000004/0 0.0001/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 25/0 -1 0 0 100/0 0.00005/" +
|
||||
"0 -1 0 0 100/0 10000/0 1 0 0 100/0 -1 0 0 10000";
|
||||
|
||||
static final int MODEL_741 = 0;
|
||||
static final int MODEL_324 = 1;
|
||||
|
||||
private static double[] model741resistances = { 50, 25, 1e3, 50e3, 1e3, 5e3, 50e3, 50, 39e3, 7500, 4500 };
|
||||
|
||||
int modelType;
|
||||
final int opheight = 16;
|
||||
@@ -23,40 +35,54 @@ public class OpAmpRealElm extends CompositeElm {
|
||||
double curCounts[];
|
||||
double slewRate;
|
||||
double currentLimit;
|
||||
double capValue;
|
||||
final double defaultCurrentLimit = .0231;
|
||||
final int FLAG_SWAP = 2;
|
||||
|
||||
public OpAmpRealElm(int xx, int yy) {
|
||||
super(xx, yy, modelString, modelExternalNodes);
|
||||
super(xx, yy); // , model741String, model741ExternalNodes);
|
||||
noDiagonal = true;
|
||||
slewRate = .6;
|
||||
currentLimit = defaultCurrentLimit;
|
||||
modelType = 741;
|
||||
init741();
|
||||
modelType = MODEL_741;
|
||||
initModel();
|
||||
}
|
||||
|
||||
public OpAmpRealElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) {
|
||||
super(xa, ya, xb, yb, f, null, modelString, modelExternalNodes);
|
||||
super(xa, ya, xb, yb, f); // , null, model741String, model741ExternalNodes);
|
||||
noDiagonal = true;
|
||||
slewRate = Double.parseDouble(st.nextToken());
|
||||
getCapacitor().voltdiff = Double.parseDouble(st.nextToken());
|
||||
capValue = Double.parseDouble(st.nextToken());
|
||||
currentLimit = defaultCurrentLimit;
|
||||
modelType = 741;
|
||||
modelType = MODEL_741;
|
||||
try {
|
||||
currentLimit = Double.parseDouble(st.nextToken());
|
||||
modelType = Integer.parseInt(st.nextToken());
|
||||
} catch (Exception e) {}
|
||||
init741();
|
||||
initModel();
|
||||
}
|
||||
|
||||
private void initModel() {
|
||||
flags |= FLAG_ESCAPE;
|
||||
if (modelType == MODEL_741)
|
||||
init741();
|
||||
else
|
||||
init324();
|
||||
curCounts = new double[5];
|
||||
setPoints();
|
||||
}
|
||||
|
||||
private void init741() {
|
||||
loadComposite(null, model741String, model741ExternalNodes);
|
||||
|
||||
// adjust capacitor value to get desired slew rate
|
||||
getCapacitor().capacitance = 30e-12 / (slewRate/.6);
|
||||
getCapacitor().voltdiff = capValue;
|
||||
|
||||
// set resistor values
|
||||
int i;
|
||||
for (i = 0; i != 11; i++)
|
||||
((ResistorElm) compElmList.get(21+i)).resistance = resistances[i];
|
||||
((ResistorElm) compElmList.get(21+i)).resistance = model741resistances[i];
|
||||
|
||||
// adjust output stage resistor values and transistor betas to increase current if desired
|
||||
double currentMult = currentLimit / defaultCurrentLimit;
|
||||
@@ -65,15 +91,31 @@ public class OpAmpRealElm extends CompositeElm {
|
||||
((TransistorElm) compElmList.get(13)).setBeta(currentMult * 100); // Q14
|
||||
((TransistorElm) compElmList.get(18)).setBeta(currentMult * 100); // Q20
|
||||
|
||||
curCounts = new double[5];
|
||||
}
|
||||
|
||||
private void init324() {
|
||||
StringTokenizer st = new StringTokenizer(lm324ModelDump, "/");
|
||||
loadComposite(st, lm324ModelString, lm324ExternalNodes);
|
||||
|
||||
// adjust capacitor value to get desired slew rate
|
||||
getCapacitor().capacitance = 10e-12 / (slewRate/.55);
|
||||
getCapacitor().voltdiff = capValue;
|
||||
|
||||
// adjust output stage resistor values and transistor betas to increase current if desired
|
||||
double currentMult = currentLimit / defaultCurrentLimit;
|
||||
((ResistorElm) compElmList.get(11)).resistance /= currentMult;
|
||||
((TransistorElm) compElmList.get(9)).setBeta(currentMult * 100);
|
||||
((TransistorElm) compElmList.get(10)).setBeta(currentMult * 100);
|
||||
((TransistorElm) compElmList.get(12)).setBeta(currentMult * 100);
|
||||
((TransistorElm) compElmList.get(16)).setBeta(currentMult * 100);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
super.reset();
|
||||
curCounts = new double[5];
|
||||
}
|
||||
|
||||
CapacitorElm getCapacitor() { return ((CapacitorElm) compElmList.get(20)); }
|
||||
CapacitorElm getCapacitor() { return ((CapacitorElm) compElmList.get(modelType == MODEL_741 ? 20 : 4)); }
|
||||
|
||||
public String dump() {
|
||||
return super.dumpWithMask(0) + " " + slewRate + " " + getCapacitor().voltdiff + " " + currentLimit + " " + modelType;
|
||||
@@ -159,7 +201,8 @@ public class OpAmpRealElm extends CompositeElm {
|
||||
}
|
||||
|
||||
void getInfo(String arr[]) {
|
||||
arr[0] = "op-amp (741)";
|
||||
String type = (modelType == MODEL_741) ? "LM741" : "LM324";
|
||||
arr[0] = "op-amp (" + type + ")";
|
||||
arr[1] = "V+ = " + getVoltageText(volts[1]);
|
||||
arr[2] = "V- = " + getVoltageText(volts[0]);
|
||||
arr[3] = "Vout = " + getVoltageText(volts[2]);
|
||||
@@ -176,21 +219,35 @@ public class OpAmpRealElm extends CompositeElm {
|
||||
ei.checkbox = new Checkbox("Swap Inputs", (flags & FLAG_SWAP) != 0);
|
||||
return ei;
|
||||
}
|
||||
if (n == 3) {
|
||||
EditInfo ei = new EditInfo("<a href=\"opampreal.html\">Model</a>", modelType);
|
||||
ei.choice = new Choice();
|
||||
ei.choice.add("LM741");
|
||||
ei.choice.add("LM324");
|
||||
ei.choice.select(modelType);
|
||||
return ei;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void setEditValue(int n, EditInfo ei) {
|
||||
if (n == 0) {
|
||||
slewRate = ei.value;
|
||||
init741();
|
||||
initModel();
|
||||
}
|
||||
if (n == 1) {
|
||||
currentLimit = ei.value;
|
||||
init741();
|
||||
initModel();
|
||||
}
|
||||
if (n == 2) {
|
||||
flags = ei.changeFlag(flags, FLAG_SWAP);
|
||||
setPoints();
|
||||
}
|
||||
if (n == 3) {
|
||||
modelType = ei.choice.getSelectedIndex();
|
||||
capValue = 0;
|
||||
initModel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
19
war/opampreal.html
Normal file
19
war/opampreal.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<html><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>#header + #content > #left > #rlblock_left,
|
||||
#content > #right > .dose > .dosesingle,
|
||||
#content > #center > .dose > .dosesingle
|
||||
{display:none !important;}</style><style>img[src="http://s05.flagcounter.com/count/pTvk/bg=FFFFFF/txt=000000/border=CCCCCC/columns=6/maxflags=36/viewers=0/labels=0/"]
|
||||
{display:none !important;}</style></head><body><h1>Real Op-Amps</h1>
|
||||
The ideal op-amp element uses an ideal approximation to op-amp behavior. It has infinite <a href="https://en.wikipedia.org/wiki/Slew_rate">slew rate</a>
|
||||
and output current.
|
||||
<p>
|
||||
The real op-amp element uses a subcircuit to emulate a real op-amp implementation with finite slew rate and output current. Presently,
|
||||
the two implementation options are LM741 and LM324.
|
||||
<p>
|
||||
You can also modify the slew rate or output current, which will modify the circuit to change these values from the default.
|
||||
<p>
|
||||
These subcircuits are complicated, so you may run problems with convergence, especially if you increase the slew rate. Try decreasing
|
||||
the time step size if this happens.
|
||||
<p>
|
||||
The LM324 can act very glitchy as well if the slew rate is increased. Decreasing the time step size proportionately should fix this.
|
||||
</body></html>
|
||||
@@ -14,4 +14,10 @@ Be sure to save this circuit (the subcircuit implementation), because it can't b
|
||||
Now you can create a circuit using the subcircuit model. <a href="circuitjs.html?cct=$+1+0.000005+10.20027730826997+59+5+50%0A.+fullrect+0+2+2+4+in%5Cp+1+0+2+in-+3+1+2+out%5Cp+2+0+3+out-+4+1+3+DiodeElm%5Cs1%5Cs2%5CrDiodeElm%5Cs3%5Cs2%5CrDiodeElm%5Cs4%5Cs1%5CrDiodeElm%5Cs4%5Cs3+2%5C%5Csdefault%5Cs2%5C%5Csdefault%5Cs2%5C%5Csdefault%5Cs2%5C%5Csdefault%0A410+688+288+784+368+1+fullrect+2%5Csdefault+2%5Csdefault+2%5Csdefault+2%5Csdefault%0Aw+688+288+688+240+0%0Aw+688+320+688+368+0%0Aw+688+368+640+368+0%0Aw+688+240+640+240+0%0Av+640+368+640+240+0+1+40+5+0+0+0.5%0Aw+784+288+784+240+0%0Aw+784+320+784+368+0%0Aw+784+368+832+368+0%0Ar+832+240+832+368+0+1000%0Aw+784+240+832+240+0%0Ao+9+64+0+4099+5+0.025+0+2+9+3%0A">Here is an example using the rectifier.</a> When you save/load this circuit, it will also save/load the subcircuit model so you can use it in other circuits.
|
||||
<p>
|
||||
You can also use subcircuits to rearrange pins on the built-in chips. <a href="circuitjs.html?cct=$+1+0.000005+10.20027730826997+50+5+50%0A165+800+368+848+368+6+0%0A207+864+336+864+288+0+Vin%0A207+800+400+752+400+0+dis%0A207+800+464+736+464+0+tr%0A207+800+496+736+496+0+th%0A207+864+528+864+592+0+ctl%0A207+896+528+896+592+0+gnd%0A207+928+432+992+432+0+out%0A207+928+400+992+400+0+rst%0A">Here is an example with the 555</a>. And <a href="circuitjs.html?cct=$+1+0.000005+5.023272298708815+64+7+50%0Aw+112+192+112+224+0%0Aw+112+224+176+224+0%0Ac+112+224+112+288+0+3e-7+6.326871610496845%0Ag+112+288+112+304+0%0Ar+112+192+112+64+0+1000000%0Aw+112+64+240+64+0%0AR+112+64+80+64+0+0+40+10+0+0+0.5%0AO+352+160+416+160+0%0Aw+240+64+304+64+0%0Aw+304+64+304+128+0%0Aw+352+160+352+32+0%0Ar+352+32+160+32+0+10000%0Aw+160+32+160+96+0%0Aw+112+192+160+192+0%0A.+555+0+2+4+8+ctl+5+3+3+dis+1+1+3+gnd+8+0+2+out+6+2+2+rst+7+3+2+th+3+2+3+tr+2+1+2+Vin+4+0+3+TimerElm%5Cs1%5Cs2%5Cs3%5Cs4%5Cs5%5Cs6%5Cs7%5Cs8+6%5C%5Cs0%0A410+208+128+224+160+1+555+6%5Cs9.999632723888661%0Aw+304+128+336+128+0%0Aw+336+128+336+272+0%0Aw+336+272+208+272+0%0Aw+160+192+176+192+0%0Aw+176+192+176+160+0%0Aw+176+160+208+160+0%0Aw+208+192+192+192+0%0Aw+176+224+176+288+0%0Aw+176+288+320+288+0%0Aw+320+288+320+192+0%0Aw+320+192+304+192+0%0Aw+160+96+160+192+0%0Aw+160+96+320+96+0%0Ag+208+128+208+144+0%0Aw+208+224+208+272+0%0Aw+192+192+192+320+0%0Aw+192+320+352+320+0%0Aw+352+320+352+160+0%0Ao+2+32+0+4099+10+0.00078125+0+2+2+3%0Ao+7+16+0+4106+10+0.00009765625+1+1%0A">here is that subcircuit model being used in a circuit.</a>
|
||||
<p>
|
||||
If you select part of a circuit before using File->Create Subcircuit, then only the selected elements will be considered part of
|
||||
the subcircuit.
|
||||
<p>
|
||||
If you want to use labeled nodes in the subcircuit definition but don't want them to be one of the inputs/outputs, then check the "Internal Node"
|
||||
checkbox in those labeled nodes' edit menus.
|
||||
</body></html>
|
||||
|
||||
Reference in New Issue
Block a user