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;