Frage bez. FOR-Schleife (Beckhoff CX9000-001)

Zuviel Werbung?
-> Hier kostenlos registrieren
Erzeugung einer vorgegebenen Pulsanzahl

Wenn ein Vorwärtszähler vorgeschrieben ist, dann würde ich das Programm so schreiben:
Code:
VAR_INPUT
    Enable            : BOOL;
    myNumber          : INT;
END_VAR
VAR
    En_Trig           : R_TRIG;
    Timer_Puls        : TON;
    Timer_Pause       : TON;
    Counter           : INT;
    greenLightControl : BOOL;
END_VAR


En_Trig (CLK := Enable);

IF En_Trig.Q THEN
    Counter := 0;
END_IF;

Timer_Puls (IN := Enable AND (Counter < MyNumber) AND NOT Timer_Pause.Q, PT := T#1s);
Timer_Pause (IN := Timer_Puls.Q, PT := T#1s);

IF Timer_Pause.Q THEN
    Counter := Counter + 1;
END_IF;

greenLightControl := Enable AND (Counter < MyNumber) AND NOT Timer_Puls.Q;
Vorwärtszählen hat die Besonderheit, daß sich der Vergleichswert myNumber über den ganzen Impulsausgabezyklus nicht ändern darf, es sei denn, man will absichtlich noch während laufendem Impulsausgabezyklus nachträglich die Impulszahl ändern. Wenn myNumber sich ungewünscht ändern kann, dann muß man beim Impulszyklus-Start myNumber in eine Kopie speichern und dann mit dieser Kopie arbeiten.


Dieses Problem umgeht man elegant, wenn man mit einem Rückwärtszähler arbeitet, da wird myNumber im Counter gespeichert und danach nicht mehr beachtet:
Code:
VAR_INPUT
    Enable            : BOOL;
    myNumber          : INT;
END_VAR
VAR
    En_Trig           : R_TRIG;
    Timer_Puls        : TON;
    Timer_Pause       : TON;
    Counter           : INT;
    greenLightControl : BOOL;
END_VAR


En_Trig (CLK := Enable);

IF En_Trig.Q THEN
    Counter := myNumber;
END_IF;

IF NOT Enable THEN
    Counter := 0;
END_IF;

Timer_Puls (IN := (Counter <> 0) AND NOT Timer_Pause.Q, PT := T#1s);
Timer_Pause (IN := Timer_Puls.Q, PT := T#1s);

IF Timer_Pause.Q AND (Counter <> 0) THEN
    Counter := Counter - 1;
END_IF;

greenLightControl := (Counter <> 0) AND NOT Timer_Puls.Q;
Ein weiterer Vorteil des Rückwärtszählers ist, daß man den Zählerstand direkt zur Anzeige eines Countdowns benutzen kann.


Wenn Pulszeit und Pausezeit gleich lang sind, dann braucht man nur 1 Timer. Mein Favorit ist in dem Fall ein Rückwärtszähler, der die Pulse und die Pausen zählt und die Pulsausgabe wird nur bei ungeraden Zählerständen TRUE:
Code:
VAR_INPUT
    Enable            : BOOL;
    myNumber          : INT;
END_VAR
VAR
    En_Trig           : R_TRIG;
    Timer             : TON;
    Counter           : INT;
    greenLightControl : BOOL;
END_VAR


En_Trig (CLK := Enable);

IF En_Trig.Q AND (myNumber > 0) THEN
    Counter := myNumber + myNumber - 1;
END_IF;

IF NOT Enable THEN
    Counter := 0;
END_IF;

Timer (IN := (Counter <> 0) AND NOT Timer.Q, PT := T#1s);

IF Timer.Q AND (Counter <> 0) THEN
    Counter := Counter - 1;
END_IF;

greenLightControl := WORD_TO_BOOL(INT_TO_WORD(Counter) AND 1);
[SIZE="1"]
(* greenLightControl := Counter.0; darf man in ST wahrscheinlich nicht schreiben? *)[/SIZE]

Harald
 
Ich würds so machen:
FUNCTION_BLOCK _BLINK
VAR_INPUT
BLINK_COUNT:INT;
BLINK_ON_TIME:TIME;
BLINK_OFFTIME:TIME;
END_VAR
VAR_OUTPUT
THE_BLINKER:BOOL;
END_VAR
VAR
TIMER:TON;
END_VAR


IF BLINK_COUNT>0 THEN
IF NOT TIMER.IN THEN
IF THE_BLINKER THEN
TIMER.PT:=BLINK_ON_TIME;
ELSE
TIMER.PT:=BLINK_OFFTIME;
END_IF;
END_IF;
TIMER(IN:=NOT TIMER.Q);
IF TIMER.Q THEN
THE_BLINKER:=NOT THE_BLINKER;
IF NOT THE_BLINKER THEN
BLINK_COUNT:=BLINK_COUNT-1;
END_IF;
END_IF;
ELSE
TIMER(IN:=FALSE);
THE_BLINKER:=FALSE;
END_IF;
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich würds so machen:
Ist das in ST so üblich, daß man VAR_INPUT wie interne Variablen benutzt (Beschreiben und Werte für den nächsten Zyklus merken)?
Da darf man ja die Inputs gar nicht beschalten und braucht 2 verschiedene Aufrufe des Bausteins oder muß zum Starten außerhalb des Aufrufs den Input-Parameter BLINK_Instanz.BLINK_COUNT beschreiben. Das finde ich gar nicht schön.

Zusätzlich wäre es besser, wenn das Programm sofort mit der Impulsausgabe beginnen würde statt mit der Impulspause.

Tip:
Für bessere Lesbarkeit kann man Programmcode in eine Code-Box einfügen. Das erreicht man mit den Code-Tags [CODE] und [/CODE] bzw. im Beitragseditor mit dem #-Button. Dein Code könnte dann so aussehen:
Code:
FUNCTION_BLOCK _BLINK
VAR_INPUT
    BLINK_COUNT:INT;
    BLINK_ON_TIME:TIME;
    BLINK_OFFTIME:TIME;
END_VAR
VAR_OUTPUT
    THE_BLINKER:BOOL;
END_VAR
VAR
    TIMER:TON;
END_VAR


IF BLINK_COUNT>0 THEN
    IF NOT TIMER.IN THEN
        IF THE_BLINKER THEN
            TIMER.PT:=BLINK_ON_TIME;
        ELSE
            TIMER.PT:=BLINK_OFFTIME;
        END_IF;
    END_IF;
    TIMER(IN:=NOT TIMER.Q);
    IF TIMER.Q THEN
        THE_BLINKER:=NOT THE_BLINKER;
        IF NOT THE_BLINKER THEN
            BLINK_COUNT:=BLINK_COUNT-1;
        END_IF;
    END_IF;
ELSE
    TIMER(IN:=FALSE);
    THE_BLINKER:=FALSE;
END_IF;

Harald
 
Ist das in ST so üblich, daß man VAR_INPUT wie interne Variablen benutzt (Beschreiben und Werte für den nächsten Zyklus merken)?
Da darf man ja die Inputs gar nicht beschalten und braucht 2 verschiedene Aufrufe des Bausteins oder muß zum Starten außerhalb des Aufrufs den Input-Parameter BLINK_Instanz.BLINK_COUNT beschreiben. Das finde ich gar nicht schön.

... das sehe ich genau so - unabhängig ob ST oder SCL.
Vor Allem auch deswegen, da ich bei solchen Bausteinen die IN-Parameter auch gerne mal mit einer Konstante (also einem Festwert) beschalte - das entspräche hier ja sogar auch dem Sinn des Bausteins ...

Gruß
Larry
 
... und weil es nur eine nette Ablenkung war muß man sich wohl auch keine Mühe geben ... :rolleyes:

Was mich generell an diesem ST stört:
Es verführt unerfahrene Programmierer unheimlich zu ereignisorientierter Programmierung mit überwiegend bedingten Zuweisungen, was zu lauter solchen undurchdachten und unvollständigen Programmen mit logischen Fehlern führt, wie hier und in anderen Threads leider immer wieder zu sehen ist. Es wird ohne Plan einfach losgetippt - "nur schnell gebastelt" - der Anwender wird zum ewigen Betatester, weil diese Programme aus dem Entwurfsstadium nie herauskommen - auch wenn noch so viel nachgeflickt wird. :(

Das sehe ich nicht so ...
Natürlich sollte man sich Mühe geben - da hast du Recht. Auf der anderen Seite geht es hier aber nicht darum "dem Anderen seine Arbeit zu machen" sondern aus meiner Sicht eher "dem Anderen einen Start zu ermöglichen". Dazu ist kein 100% fehlerfreies Code-Beispiel oder am Besten schon die ganze erledigte Arbeit notwendig. Das würde im Gegenteil dem Anderen auch gar nicht helfen, da es niemanden wirklich hilft, mit etwas zu arbeiten das man nicht wirklich verstanden hat.
Dem entsprechend sehe ich meine Tätigkeit hier (im Forum) unter der Prämisse des Helfens verstehen zu lernen.

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wie kann denn der Counter einr FOR-Schleife beeinflusst werden?
Da bin ich jetzt ehrlichgesagt überfragt. Dafür verwende ich FOR/WHILE
zu selten, zumindest in zyklischen SPS-Programmen.
mit "EXIT":

Code:
VAR
  i: INT;
  Limit : INT := 50;
END_VAR

FOR i := 0 TO 100 DO
  IF i > Limit  THEN
    [B]EXIT[/B];
  END_IF
END_FOR
Hier würde die FOR-Schleife bei i = 51 abgebrochen werden
 
Klar, wenn man 19 Jahre C++ programmiert, ist es schwer, sich davon zu lösen :)

Wenn man einmal verstanden hat wie eine SPS arbeitet, kann man mit jeder Sprache verständliche und funktionierende Programme schreiben.

In FUP oder AWL kann man genauso schlecht oder gut programmieren.

Dein Problem kann man auch ohne verwendung von Timern und Bedingungen programmieren.

Code:
counter :=(counter + 1)MOD NumberOfCycles;
boOut:= boOut XOR (counter <= 0  );

Gruß

dummy
 
Zuletzt bearbeitet:
Zurück
Oben