OSCAT Forum
oscat.lib => Bestehende Module / Existing Modules => Thema gestartet von: brewmaster am 06. Juni 2008, 16:21:10
-
Hallo zusammen,
bei dem heutigen telefonat mit der Beckhoff-Hotline wurde mir gesagt, dass der Fehler Page-Fault durch einen unerlaubten Speicherzugriff kommt.
Da dieser Fehler nur dann auftritt, wenn ich beim FB Actuator_3P einen Wert zwischen 0 und 1 eintrage (nicht bei einem ganzzahligen Wert) erklärte mir der Techniker, dass ein CX9000 kein eigenes Real Zahlen Managment besitzt ( ich glaube er sagte was von FPU oder so), und es daher beim Rechnen zu Problemen kommt.
Jetzt meine Frage: Besitzt irgendjemand auch noch einen CX9000 und hat das gleiche Problem? Besteht die Möglichkeit den Bausteineingang als INT zu deklarieren (würde mir persönlich von der Genauigkeit vollkommen langen)?
-
den CX9000 haben wir auch, und der kann definitiv REAL
was stimmt ist das er keine FPU (Floating Point Unit ) hat die REAL in Hardware rechnen würde.
beim CX9000 wird real per firmware emuliert, das macht bereits codesys Twincat
wenn ein controller kein real kann dann kannst du das programm erst gar nicht kompilieren
kann es vielleicht sein das dein speicher zu voll ist und bei laufzeit der aufruf komplexer funktionen keinen speicher mehr bekommt?
-
Hy Hugo,
kannst du mir sagen, wo und wie ich das mit dem Speicher überprüfen kann?
Ist die Berechnung des Actuator_3p so aufwändig, dass es da zu Problemen kommen könnte?
Danke und Gruß
Brewmaster
-
nein actuator_3p ist nicht sonderlich aufwendig,
meist liegt so ein problem auch gar nicht an einem bestimmten modul
beim download auf die steuerung bekommst due in der statuszeile die speicherbelegung gesagt.
beginne doch mal mit einem einfachen projekt nur actuator und aus eingänge und dann bastle zug um zug mehr dazu
erst kleine häppchen austesten und dann wieder neues dazupacken
-
hy,
das habe ich mitlerweile schon probiert, doch das Problem tritt sogar auf, obwohl nur der Actuator als Programm laufen soll. Ich bin ratlos-und am verzweifeln...
-
kannst du mir bitte malö dein projekt hier einstellen das versagt und die genauen fehlermeldungen
-
Hy,
anbei 3 Screenshots von meinem Programm, der Fehlermeldung und dem Systemmanager
(http://www.kleinebrauerei.de/actuator-prog.jpg)
(http://www.kleinebrauerei.de/actuator-fehlermeldung.jpg)
(http://www.kleinebrauerei.de/system-manager.jpg)
-
Hi brewmaster,
ich hatte auch vor kurzem dieses Problem gehabt. Mein Workaround war alle Unterprogramme auskommentieren, dann neu übersetzen und übertragen. Meist ging es dann auch noch nicht. Steuerung löschen und übertragen. Irgend wann ging es wieder. Dann habe ich wieder alle Unterpragramme frei geschaltet und es ging alles wieder. Z.Zt. habe ich keine Probleme. Ich setze einen CX9010 ein. DIE Lösung habe ich also auch nicht. Hast Du schon was neues dazu?
-
nee, was neues dazu habe ich auch nicht.
das problem trat bei mir je wie gesagt nur in kombination des cx9000 und dem actuator_3p auf. ich hab das problem jetzt einfach so gelöst, dass ich die controler_toolbox von beckhoff verwende. mit diesem baustein trat nie ein fehler auf...
gruß
-
Ich habe eine Lösung gefunden!
Lösung:
Zeile
ELSIF differ(LIMIT(0,in,1), val, TIME_TO_REAL(min_ontime) / TIME_TO_REAL(max_runtime)) OR in = 0 OR in = 1 THEN
durch die Zeile
ELSIF (ABS(LIMIT(0.0,in,1.0) - val) > TIME_TO_REAL(min_ontime) / TIME_TO_REAL(max_runtime)) OR in = 0 OR in = 1 THEN
ersetzen!
Erklärung:
Die Beckhoff CX9010 hat Probleme mit dem Funktionsaufruf "differ()" in Kombination mit der Funktion "LIMIT()". Beides nacheinander aufrufen funktioniert ohne Probleme. Auch funktioniert es auf der TwinCAT PLC auf den Computer (dem Programmiersystem)
um den Fehler zu reproduzieren (auf der Beckhoff CX9010):
VAR
a:BOOL;
b:REAL;
C:REAL;
END_VAR
b:=0.5;
c:=0.0;
a:=differ(LIMIT(0,b,1), c, 1.3);
FUNCTION differ : BOOL
VAR_INPUT
in1: REAL;
in2: REAL;
X: REAL;
END_VAR
differ := ABS(in1 - in2) > X;
-
Wenn man dieses Problem gelöst hat, so taucht schon das nächste auf. Der Funktionsbaustein auf der Beckhoff CX9010 lässt das Stellglied nun immer von der einen Endlage zu der anderen Endlage pendeln.
Lösung:
Alle im Quelltext angegebenen Zahlen des Datentyps REAL müssen mit einer Nachkommastelle (besser gesagt mit dem amerikanischen Punkt) versehen werden. Nur so wendet die CX9010 auch wirklich eine REAL-Datenverarbeitung an!
Hier der Quelltext:
(nicht vergessen auch die übergebenen Variablen mit einer Nachkommastelle zu versehen!)
Tn := DWORD_TO_TIME(T_PLC_MS());
tx := Tn - last;
end(clk := end_pos);
(* init only when first started *)
IF state = 0 THEN
runtime_1 := max_runtime;
runtime_2 := max_runtime;
state := 1;
rout(KR := 1000.0 / TIME_TO_REAL(runtime_1), KF := 1000.0 / TIME_TO_REAL(runtime_1) );
tx := t#0s;
last := Tn;
last_diag := last;
END_IF;
(* check for end_sw and stop if reached *)
IF switch_available AND out1 AND end.Q THEN
(* upper limit has been reached *)
out1 := 0;
rout(rmp := 0, in := 1.0);
rout.Rmp := 1;
ELSIF switch_available AND out2 AND end.Q THEN
(* lower limit has been reached *)
out2 := 0;
rout(rmp := 0, in := 0.0);
rout.Rmp := 1;
END_IF;
(* check for min_offtime and min_ontime and return if it has not elapsed *)
IF NOT busy AND tx < min_offtime THEN
RETURN;
ELSIF busy AND tx < min_ontime THEN
RETURN;
(* check for last diag cycle and start diag if auto_diag_time has elapsed *)
ELSIF (tn - last_diag > auto_diag_time AND state > 10 AND auto_diag_time > t#0s) OR (diag AND state >= 10) THEN state := 1;
(* check if force pin is high *)
ELSIF force THEN state := 10;
(* check if calibration is necessary *)
ELSIF cal_timer > cal_runtime AND state = 99 AND cal_runtime > t#0s THEN state := 20;
END_IF;
(* state machine for operation *)
CASE state OF
1 : (* diagnostic start *)
(* all data is reset and out2 is turned on to make sure we start from top position *)
last_diag := tn;
diag := 0;
state := 2;
pos := 0.0;
busy := 1;
last := tn;
out1 := 0;
out2 := 1;
RETURN;
2: (* wait for max_runtime or end_pos to be reached *)
IF tx < max_runtime AND NOT end.Q THEN RETURN; END_IF;
state := 3;
last := tn;
out1 := 1;
out2 := 0;
RETURN;
3: (* measure runtime_2 *)
IF tx < max_runtime AND NOT end.Q THEN RETURN; END_IF;
state := 4;
runtime_1 := tx;
last := tn;
out1 := 0;
out2 := 1;
RETURN;
4: (* measure runtime_1 *)
IF tx < max_runtime AND NOT end.Q THEN RETURN; END_IF;
state := 99;
runtime_2 := tx;
last := tn;
out2 := 0;
busy := 0;
(* reset the output ramp generator to the 0% position as the valve is after diags *)
rout(rmp := 0, in := 0.0);
rout.Rmp := 1;
cal_timer := t#0s;
IF DIFFER(TIME_TO_REAL(runtime_1), TIME_TO_REAL(runtime_2), TIME_TO_REAL(max_runtime) * 0.1) THEN error := TRUE;
ELSIF runtime_1 > MULTIME(max_runtime, 0.9) AND switch_available THEN error := TRUE;
ELSIF runtime_2 > MULTIME(max_runtime, 0.9) AND switch_available THEN error := TRUE;
END_IF;
RETURN;
10: (* force pin is active, outputs are forced on or off by the on pin *)
IF on AND NOT out2 THEN val := 1.0;
ELSIF NOT on AND NOT out1 THEN val := 0.0;
END_IF;
IF NOT force THEN state := 99; END_IF;
20 : (* calibrate to allow for correction of positioning error addig up *)
cal_timer := t#0s;
state := 21;
pos := 0.0;
busy := 1;
last := tn;
out1 := 0;
out2 := 1;
RETURN;
21: (* after end_sw or max_runtime we can be sure to have reached the 0% position *)
IF tx < max_runtime AND NOT end.Q THEN RETURN; END_IF;
state := 99;
out2 := 0;
busy := 0;
last := tn;
rout(rmp := 0, in := 0.0);
rout.Rmp := 1;
RETURN;
99: (* normal operation *)
(* only if input changes for an equivalent of min_ontime then change the output *)
(* if there is a directional change then wait until last step is finished *)
IF in < TIME_TO_REAL(min_ontime)/TIME_TO_REAL(max_runtime)/2.0 THEN val := 0.0;
ELSIF in > 1.0 - TIME_TO_REAL(min_ontime)/TIME_TO_REAL(max_runtime)/2.0 THEN val := 1.0;
ELSIF (ABS(LIMIT(0.0,in,1.0) - val) > TIME_TO_REAL(min_ontime) / TIME_TO_REAL(max_runtime)) OR in = 0 OR in = 1.0 THEN (* VERÄNDERTE ZEILE IM VERGLEICH ZUM ORGINAL! *)
IF in < val AND NOT out1 THEN val := in;
ELSIF in > val AND NOT out2 THEN val := in;
END_IF;
END_IF;
END_CASE;
(* output ramp generator *)
rout(in := val);
out1 := rout.busy AND rout.UD;
out2 := rout.busy AND NOT rout.UD;
pos := rout.out;
(* set the busy output and if busy goes low, then set the last time in order to check for min_offtime *)
IF NOT busy AND rout.busy THEN
last := tn;
ELSIF busy AND NOT rout.busy THEN
cal_timer := cal_timer + tx;
last := tn;
END_IF;
busy := rout.busy;
-
Das Problem mit dem Pendeln von einer Endlage in die Andere ist doch noch nicht behoben! Das Problem taucht unregelmäßig wieder an unterschiedlichen Mischern auf!