# Programmierung einer zyklischen Beckhoff SPS mit ST



## DerBenutzer (19 Dezember 2014)

Hallo ich habe mal wieder ein kleines Problem.

Ich bin derzeit dabei ein Fließband zu steuern und zwar wie folgt:

In der Mitte des Fließbandes befindet sich ein Sensor, wenn unter diesem etwas hin und herfährt gibt mir der Sensor dies zurück.
Zu einem festen Zyklus fällt nun in einem vordefinierten Bereich x, links oder rechts vom Sensor, ein Paket auf das Fließband.
Nun soll sich das Band zu jedem Zyklus arretieren, sprich, das Paket fällt runter und das Band fährt einmal nach links und rechts,
damit der Sensor merkt ob ein Paket auf dem Band liegt.

Ist ein Paket da sollen die ersten 5 Pakete nach rechts transportiert werden. Danach sollen die nächste 5 nach links transportiert werden und das wiederholt sich dann immer.
Die Richtung kann man steuern, sowie den Motor an oder aus machen.

Ich habe das Programm eigentlich schon fast fertig, es fehlt lediglich noch der Teil, wo das Paket zu Beginn eines jeden Zyklus nach links und rechts fährt, damit der Sensor sieht ob etwas da ist.
Anmerkung: Es ist immer nur ein Paket gleichzeitig auf dem Fließband.


```
[COLOR=#000000]//Deklarierung der Variablen-Eingänge[/COLOR]//Tasten für Steuerrichtungen, Signal der Lichtschranke und Motor Ein-/Auschalten
VAR_INPUT
TasteRechts:BOOL;
TasteLinks:BOOL;
TasteMotor:BOOL;
Lichtschranke:BOOL;
END_VAR

//Ausgänge, Motoransteuerung
VAR_OUTPUT
Links:BOOL;
Rechts:BOOL;
MotorStart:BOOL;
END_VAR

//Wird bei einem Stromausfall gesichert
VAR RETAIN
Zaehler:BYTE;
END_VAR

//Initialisierung
//Am Anfang ist der Motor aus und keine Drehrichtung eingestellt
Motorstart:=False;
Links:=False;
Rechts:=False;

//Wenn die Motortaste gedrückt wird und der Motor aus ist
//wird der Motor eingeschaltet
IF TasteMotor AND NOT Motorstart THEN
    
    MotorStart:=True;

//Wenn die Motortaste gedrückt wird und der Motor an ist
//wird der Motor ausgeschaltet
ELSIF TasteMotor AND Motorstart THEN
    
    Motorstart:=False;
END_IF

//Wenn die Taste für Rechtsbetrieb gedrücht wird
//wird die der Motor in Rechtsbetrieb geschaltet
IF TasteRechts THEN
    
    Links:=False;
    Rechts:=True;

//Wenn die Taste für Linksbetrieb gedrücht wird
//wird die der Motor in Linksbetrieb geschaltet
ELSIF TasteLinks THEN
    
    Rechts:=False;
    Links:=True;

END_IF


//Solange der Motor an ist und wenn genau eine Richtung vorgegeben wurde
//wird die Schleife ausgeführt
WHILE MotorStart AND (Rechts XOR Links) DO

//Wenn die Lichtschranke ausgelöst wird, erhöht sich der Zähler um 1

    IF Lichtschranke THEN
        
        Zaehler:=Zaehler+1;

//Erreicht der Zähler 5 und die Drehrichtung ist links
//ändert sich die Richtung auf rechts und der Zähler wird zurückgesetzt.
//Ist der Zähler kleiner als 5 und es wird die rechte Taste
//betätigt, wird ebenfalls die Richtung geändert.
//Dies ist notwendig um im laufenden Betrieb die Richtung wechseln zu können.
        ELSIF (Zaehler>=5 OR TasteRechts) AND Links THEN
        
                Links:=False;
                Rechts:=True;
                Zaehler:=0;
                
        ELSIF (Zaehler>=5 OR TasteLinks) AND Rechts THEN    
                
                Links:=True;
                Rechts:=False;
                Zaehler:=0;
                
    END_IF;

 [COLOR=#000000]END_WHILE;[/COLOR]
```

Als ich den Code so abgegeben habe wurde angemerkt, dass es so nicht funktionieren würde, mit Verweis darauf, dass bei einem neuen Zyklus alle lokal initialisierten variablen wieder resettet werden.
Das wäre sehr schlecht weil ich dann nicht weiss wie ich den Zähler realisieren soll. 

Zusätzlich habe ich das Problem das ich nicht weiss wie das mit den Zyklen genau funktioniert und/oder wie ich einen Timer einsetzten kann, der dem Motor sagt das er sich bei jedem Zyklus um einen festen Zeitwert nach links oder rechts drehen soll. Vielleicht blickt ja einer von euch da mehr durch.

Ich suche auch noch immer nach guten Büchern, leider ist die Doku zu ST auf der Beckhoff Seite selbst nicht sehr umfangreich und zu Timern finde ich generell nichts. Ich hatte bisher auch nur mit Ereignisbasierten Sprachen im klassischen PC bereich zu tun, sprich c++ etc. deswegen will mir dieses zyklus konzept nicht ganz einleuchten.

Lg

der Benutzer


----------



## weißnix_ (19 Dezember 2014)

Mir hat beim Einstieg sehr schnell geholfen:

SPS-Programmierung nach IEC 61131-3 (4. Auflage mit DVDs) - Mit Beispielen für CoDeSys und Step 7 von Heinrich Lepers.

Damit  habe ich eigentlich schnell den Einstieg bekommen und kurz danach auch  meine erste Anlage auf die Beine gestellt. Ist bei Amazon oder so  bestimmt günstig als Remittend zu bekommen.
Außerdem sind zu ST bei Beckhoff im Infosys doch schon einige Dokumente verstreut.

Das Konzept Deiner Anlage scheint ein sehr theoretisches zu sein. Nun ja...
Was  Dein Problem mit der zyklischen Codeausführung angeht: Ist doch auf  anderen Plattformen fast gleich. Arbeitet das PC-Proggi nicht zyklisch  in einer LOOP?
Das wesentlich andere ist die Sache mit dem  Prozessabbild. Also die Abbarbeitung des Programms erfolgt bei der SPS  mit einem konsistenten Abbild der Eingänge (PAE). Das schreiben der  Ausgangswerte auf die Ausgänge erfolgt erst am Ende des Programms  automatisch (PAA).

Bei Beckhoff erfolgt die zyklische Abbarbeitung in einem festen  Zeitraster. Das Entspricht bei Deiner PC-Loop einer Pause am Ende vor  dem Rücksprung zum Anfang. So dolle anders ist es eigentlich nicht.


----------



## DerBenutzer (20 Dezember 2014)

Na was heißt theoretisch, diese Aufgabenstellung habe ich leider so bekommen ...
Das sie eher nicht so sinnvoll ist will ich gar nicht anzweifeln.
Ist halt dämlich wenn die Uni einem drei Wochen Zeit gibt sich in ein Thema einzulesen und eine SPS STeuerung zu realisieren, wenn man
das noch nie vorher gemacht hat.

Mit dem Buch hatte ich mich auch schon beschäftigt, allerdings war das einfach etwas zu viel, und vor allem in der Regel wenig ST sondern mehr AWL und FBS.


----------



## StructuredTrash (20 Dezember 2014)

Da hast Du ein grosses Problem, weil
a) Du mit der zyklischen Programmbearbeitung einer SPS nicht vertraut bist,
b) die Qualität der Anmerkung, die Du zu Deinem Programm bekommen hast, auf Mircrosoft-Support-Niveau liegt.
Ich versuche mal, Dir einen schnellen Einstieg zu geben. Das SPS-Betriebssystem stellt Dir etwa Folgendes zur Verfügung:

```
WHILE SystemRun DO
   Copy_Hardware_Inputs_to_Input_Variables;
   Call UserProgram;   (* Heisst bei einem neu angelegten Projekt MAIN *)
   Copy_Output_Variables_to_Hardware_Outputs;
   Suspend_Control_Task;   (* Wird vom Timer-Interrupthandler in einem festen Zeitraster (Zykluszeit) wieder aktiviert *)
END_WHILE
```
Du brauchst nur das User_Program MAIN schreiben. Nehmen wir mal an, dass ein Antrieb ein- und ausgeschaltet werden soll. Als Ablaufprogramm, ähnlich wie Du Dein Programm geschrieben hast,
würde das etwa so aussehen:

```
REPEAT
UNTIL StartTaster;
END_REPEAT
Antrieb:=TRUE;
REPEAT
UNTIL StopTaster;
END_REPEAT
Antrieb:=FALSE;
```
Das würde schon deshalb nicht funktionieren, weil der Datenaustausch zwischen Hardware und I/O-Variablen ja nur einmal in der Betriebssystem-Schleife erfolgt. Vor allem hätte es aber den Nachteil, dass keine weitere Steuerungsfunktion bearbeitet werden könnte, solange das Programm in einer der REPEAT UNTIL-Schleifen festhängt. Deshalb schreibt man das anders:

```
IF StopTaster THEN
   Antrieb:=FALSE;
ELSIF StartTaster THEN
   Antrieb:=TRUE;
END_IF
```
Bei dieser Art der Programmierung kannst Du problemlos weitere Steuerungsfunktionen einbinden:

```
IF StopTaster THEN
   Antrieb:=FALSE;
ELSIF StartTaster THEN
   Antrieb:=TRUE;
END_IF
IF StopTaster2 THEN
   Antrieb2:=FALSE;
ELSIF StartTaster2 THEN
   Antrieb2:=TRUE;
END_IF
```
Die Laufzeit der Schleife wird zwar etwas länger, aber die paar Mikrosekunden (wenn es überhaupt so viel ist), machen den Kohl nicht fett. Von aussen betrachtet würde es immer noch so aussehen, als ob beide Steuerungsfunktionen gleichzeitig bearbeitet würden. Und genau darauf kommt es bei einer SPS an.

Ich hoffe, Dir damit etwas weiter zu helfen, auch wenn sicher noch einige Stolpersteine im Weg liegen werden.


----------



## weißnix_ (20 Dezember 2014)

Das ist eine ausgezeichnete Kurzbeschreibung. Respekt.


----------



## DerBenutzer (21 Dezember 2014)

Super vielen Dank jetzt verstehe ich es und das macht die Sache auch ziemlich leicht denke ich.
Es bleibt nur eins was ich noch nicht gefunden habe. Wenn wir das ganze realistisch betrachten, wird
ja ein Antrieb hier von links nach rechtsbetrieb umgeschaltet. Unter Umständen wäre es dann ja nötig,
dass man bevor man von links nach rechts umschaltet, einen kleinen timer einbaut, der ein paar sekunden wartet,
damit der motor erst in den leerlauf springen kann. Ansonsten könnte es auf Dauer das Getriebe etwas mitnehmen.

Gibt es eine interne vordefinierte Methode/Funktion mit der ich dem Programm innerhalb des Zyklus sagen kann beispielweise
20 Millisekunden auf etwas zu warten oder für einen gewissen zeitraum das band nach links oder rechts zu fahren?


----------



## weißnix_ (21 Dezember 2014)

Das mit der Pause beim reversieren ist auf jeden Fall eine gute Idee.
Die vordefinierte Funktion heisst TON bzw. TOF, je nach Realisierungsweise.

Mir scheint, du solltest noch etwas über die zyklisch kontinuierliche Programmabarbeitung nachdenken.

In  der SPS-Welt ist es, wie weiter oben schon erwähnt, wichtig ein  Programm immer vollständig abzuarbeiten, da innerhalb des Programms eine  Vielzahl von Steuerungsfunktionen quasi gleichzeitig aktiv sind. Nur  weil an einer STelle auf eine Reaktion oder eine Beendigung gewartet  wird, darf der Rest nicht unterbrochen werden.

Daher sieht das dann mit den Timern grob gesagt so aus:


```
var
timer:TON;
bewegung_rechts:bool;
end_var;

timer(pt=t#1s);    (*zykl. Aufruf des Timers*)
if  bewegung_rechts then timer.in:=true; else timer.in:=false; end_if;   (*par example: wenn also die bewegung nach rechts aktiv ist, timer  starten*)
if timer.q then bewegung_rechts:=false; end_if;          (*wenn der timer abgelaufen ist, die bewegung stoppen*)
```


----------



## weißnix_ (21 Dezember 2014)

Bei Beckhoff ist die Demo machine.pro dabei. Das solltest Du Dir mal ansehen.

send wia hendi


----------



## StructuredTrash (22 Dezember 2014)

DerBenutzer schrieb:


> Gibt es eine interne vordefinierte Methode/Funktion mit der ich dem Programm innerhalb des Zyklus sagen kann beispielweise
> 20 Millisekunden auf etwas zu warten oder für einen gewissen zeitraum das band nach links oder rechts zu fahren?


Nochmal: Du kannst im Zyklus nicht auf irgendetwas warten, weil
a) die I/Os während der Bearbeitung Deines Programms gar nicht aktualisiert werden,
b) alle anderen Steuerungsfunktionen dann auch warten müssten.

Auch der Ablauf eines SPS-Timers erstreckt sich über mehrere Zyklen. In dem Zyklus, in dem der Timer gestartet wird (Timer.In wird True), merkt sich der Timer nur die Systemzeit als Startzeit. In den folgenden Zyklen wird er immer wieder aufgerufen und vergleicht dabei die Systemzeit mit der gespeicherten Startzeit. In irgendeinem Zyklus wird dann die Differenz zwischen System- und Startzeit >= der Sollzeit. In diesem Zyklus wird der Ausgang Timer.Q True. Nachdem Du Timer.Q ausgewertet hast, kannst Du Timer.In wieder auf False setzen, um den Timer für den nächsten Zeitablauf zu resetten.


----------



## DerBenutzer (6 Januar 2015)

Sooo nach den Feiertagen hab ich mich mal wieder drangesetzt. Ich hab das Problem etwas vereinfacht um den vielleicht besser Herr zu werden und eure Vorschläge so gut ich kann berücksichtigt.

Die Aufgabe soll es sein, ein Paket links oder rechts von einer Lichtschranke in einem vordefinierten Bereich abzulegen. Zu Beginn eines jeden Zyklus, beispielsweise alle 30 Sekunden, soll die Steuerung nun erkennen
ob ein Paket auf dem Band liegt. Dafür muss das Fließband eine bestimmte Zeitdauer (5s) nach links fahren, damit das Paket die Lichtschranke passiert. Liegt das Paket unglücklicherweise jedoch auf der anderen Seite des Sensors
muss das Fließband nach Rechts fahren, dieses mal jedoch 5s + 5s=10s da es das Paket ja zuvor bereits 5s in die falsche Richtung befördert hat.

Schlägt der Sensor bei einem der beiden Passiervorgänge aus, wird das Paket zuerst 5 mal nach links, danach 5 mal nach Rechts vom Fließband befördert, wobei wir davon ausgehen das dies selbst im schlechtesten Fall innerhalb von 20s
passiert ist.

Ich habe jetzt das Programm dazu geschrieben und würde mich natürlich über ein paar Kommentare/Ratschläge freuen! (Bei der Zählvariable bin ich mir nicht ganz sicher wie ich sie initialisieren soll, da sie ihren Wert ja
nicht verlieren darf, aber einmal ganz am Anfang eigentlich auf 0 Gesetzt werden müsste...)


```
VARLinks:BOOL;
Rechts:BOOL;
Leerlauf:BOOL;
Richtung:BOOL;
Motor:BOOL;
Lichtschranke:BOOL;
timer1:TON;
timer2:TON;
timer3:TON;
zaehler:BYTE;


(*Initialisierung der Variablen*)
Links:=False;
Rechts:=False;
Leerlauf:=False;
Motor:=False;


(*Die Timer*)
timer1.pt:=T#5s;
timer2.pt:=T#10s;
timer3.pt:=t#20s;
END_VAR
(*Das Programm startet und das Fließband geht für 5 Sekunden nach links*)
Motor:=True;
Links:=True;


timer1.IN:=True;


(*Solange die pt Zeit bon timer1 nicht abgelaufen ist und wenn die Lichtschranke aktiviert wird*)
if timer1.Q=false AND Lichschranke=True THEN


	(*Solange noch keine 5 Pakte nach links gefahren wurden wird dies ausgelöst*)
	if zaehler=<4 THEN
	Links:=True;
	timer3.IN=True;
	zaehler:=zaehler+1;
	(*Ist der Timer abgelaufen und das Paket sicher vom Band wird das Programm beendet und auf den nächsten Zyklus gewartet*)
	if timer3.Q=true THEN EXIT; END_IF;
	elsif zaehler=10 THEN
	zaehler:=0;
	else
	(*Solange 5=<zaehler<10 wird das Paket nach links befördert*)
	Links:=False;
	Rechts:=True;
	timer3.IN=True;
	zaehler=zaehler+1;
	if timer3.Q=true THEN EXIT; END_IF;
	end_if;
(*Ist die Zeit von timer1 abgelaufen und die Lichtschranke hat nicht ausgelöst*)
(*wird timer1 resettet und timer2 geht an und die richtung des fließbands wird gewechselt*)
else timer1.Q=True AND Lichtschranke=False 


timer1.IN=false;
timer2.IN=True;
Links:=False;
Rechts:=True;


(*Solange die pt Zeit von timer2 nicht abgelaufen ist und wenn die Lichtschranke aktiviert wird*)
	if timer2.Q=False AND Lichtschranke=True THEN
	
		if zaehler=<4 THEN
		Links:=True;
		timer3.IN=True;
		zaehler:=zaehler+1;
		if timer3.Q=true THEN EXIT; END_IF;
		elsif zaehler=10 THEN
		zaehler:=0;
		else
		Links:=False;
		Rechts:=True;
		timer3.IN=True;
		zaehler=zaehler+1;
		if timer3.Q=true THEN EXIT; END_IF;
		end_if;
		
(*Wenn die Lichtschranke nicht ausgelöst hat, sprich kein Paket aufs Band gelegt wurde geht der Motor aus*)	
	else 
	
	Motor:=False;
	
	end_if;
	
end_if;
```


----------



## StructuredTrash (6 Januar 2015)

Das ist immer noch ein Ablaufprogramm. Du gehst immer noch davon aus, dass das Programm an bestimmten Stellen wartet, bis die gerade ausgeführte Funktion beendet ist, aber das tut es nicht und darf es auch nicht.
Was Du brauchst, ist eine Schrittkette, die nacheinander die Schritte "Paket suchen nach links", "Paket suchen nach rechts" und "Paket abschieben" ausführt. Das könnte so aussehen:

```
VAR CONSTANT
   WartenAufStart:USINT:=0;
   SuchenLinks:USINT:=1;
   SuchenRechts:USINT:=2;
   Abschieben:USINT:=3;
END_VAR
VAR
   AktuellerSchritt:USINT;
END_VAR

CASE AktuellerSchritt OF
   WartenAufStart:
      (* Reset aller Ausgänge *)
      IF StartFreigabe THEN   (* per Taster, Timer oder sonstwie *)
         (* Linkslauf einschalten *)
         AktuellerSchritt:=SuchenLinks;
      END_IF
   SuchenLinks:
      IF Lichtschranke THEN
         (* In Abhängigkeit vom Paketzähler Links- oder Rechtslauf einschalten *)
         AktuellerSchritt:=Abschieben;
      ELSIF Timer1.Q THEN
         (* Rechtslauf einschalten *)
         AktuellerSchritt:=SuchenRechts;
      END_IF
   SuchenRechts:
      IF Lichtschranke THEN
         (* In Abhängigkeit vom Paketzähler Links- oder Rechtslauf einschalten *)
         AktuellerSchritt:=Abschieben;
      ELSIF Timer2.Q THEN
         AktuellerSchritt:=WartenAufStart;
      END_IF
   Abschieben:
      IF Timer3.Q THEN
         IF Zaehler<10 THEN
            Zaehler:=Zaehler+1;
         ELSE
            Zaehler:=0;
         END_IF
         AktuellerSchritt:=WartenAufStart;
      END_IF
END_CASE

Timer1(IN:=AktuellerSchritt=SuchenLinks,
           PT:=T#5s);
Timer2(IN:=AktuellerSchritt=SuchenRechts,
           PT:=T#10s);
Timer3(IN:=AktuellerSchritt=Abschieben,
           PT:=T#20s);
```


----------



## DerBenutzer (6 Januar 2015)

Wie funktioniert die Timerfunktion denn dann? Ich hatte es so verstanden:

timer.pt:=T#10s // Timer wird initialisiert mit 10 sekunden 


timer.IN:=true; //Timer wird gestartet und unterbricht das Programm


timer.Q=false //Solange der Timer noch nicht abgelaufen ist
timer.Q=true //Wenn der Timer abgelaufen ist


timer.IN:=false; //timer wird "resettet"

Oder muss ich das so verstehen, dass wenn ein Timer ausgeführt wird, der Rest des Programms erstmal abgearbeitet wird aber am Ende, bevor der neue Zyklus startet, eben die Zeit des Timers gewartet wird?

Noch ein paar Fragen zu deinem Programm:

1. Die Timerzuweisung am Ende ist mir so nicht geläufig. Steht dort praktisch das Timer1 bis Timer3 mit den entsprechenden Werten neu initialisiert werden, wenn die Bedingung "Aktuellerschrit:=Abschieben etc." wahr ist?
Muss Timer.In nicht auch dann im Laufe des Programms nochmal auf False gesetzt werden um ihn zu resetten oder geschieht das durch die erneute Zuweisung am ende des Programms automatisch?

Aber mal zum Verständnis, was ist eigentlich der Vorteil davon wenn das Programm zyklisch funktioniert und praktisch dauernd aufgerufen wird? Wäre es nicht effizienter so eine Steuerung ereignisgesteuert zu programmieren, sodass
auf bestimmte Sensoren reagiert werden kann? Auch sehe ich generell nicht den Unterschied, warum es überhaupt so etwas wie SPS gibt. Ich könnte das ganze ja auch mit einem Programm in C#,C,C++ oder was auch immer
vom PC starten. Da bräuchte ich dann auch keine Zyklen...


----------



## StructuredTrash (6 Januar 2015)

DerBenutzer schrieb:


> Wie funktioniert die Timerfunktion denn dann?


So wie ich es in Post #9 beschrieben habe. Das erklärt vielleicht auch die Art der Timeraufrufe in meinem Beispiel.



DerBenutzer schrieb:


> Aber mal zum Verständnis, was ist eigentlich der Vorteil davon wenn das Programm zyklisch funktioniert und praktisch dauernd aufgerufen wird? Wäre es nicht effizienter so eine Steuerung ereignisgesteuert zu programmieren, sodass auf bestimmte Sensoren reagiert werden kann?


Bei den Maschinen, für die ich Programme schreibe, liegt die Anzahl der Signaleingänge im unteren 3-stelligen Bereich. Es wird hier im Forum aber auch Anwender geben, für die das eher Kleinkram ist. Selbst wenn man eine Hardwareplattform hätte, die soviele Hardware-Interruptkanäle bereitstellt, wäre die Aufgabe, die Interrupts in sinnvoller Weise zu priorisieren, nahezu unlösbar. Die Gefahr, dass ein niedrig priorisierter Interrupt nicht rechtzeitig behandelt wird, weil er andauernd durch höher priorisierte Interrupts unterbrochen wird, wäre zu gross. Dann lieber nur ein Interrupt in einem festen Zeittakt, mit dem alle Signale per Polling abgefragt werden. Natürlich wird damit jede Menge Rechenzeit verbraten, weil in vielen Programmzyklen gar keine Signaländerungen auftreten, aber das eindeutig vorhersehbare Zeitverhalten ist wichtiger. Ausserdem bestehen zwischen einzelnen Steuerungsfunktionen oft Abhängigkeiten, die wesentlich einfacher und sicherer zu handhaben sind, wenn man eine eindeutig festgelegte Bearbeitungsreihenfolge hat.


----------



## MasterOhh (6 Januar 2015)

Moin.

Ein Timer unterbricht dein Programm nicht. Das wäre sehr ungünstig wenn er es täte, da eine SPS bei einer Zykluszeitverletzung idR in den Störzustand wechselt (Endlosschleifen sind hier der Klassiker).
Timer funktionieren wie alle anderen Bausteine im Programm. Sie müssen zyklisch abgearbeitet werden. In jeden Zyklus wird im Timer-Baustein die Differenz aus Startzeit und aktueller Zeit mit der eingestellten Auslösezeit verglichen. Ist die Differenz >= der eingestellten Zeit, wird die durch das Ausgangssignal (Q) angezeigt (je nach Timer-Typ halt).

Bei den Timeraufrufen am Ende von StructuredTrashs Beispiel hat sich ein kleiner Fehler eingeschlichen.
Richtig:

```
Timer1(IN:=AktuellerSchritt = SuchenLinks,            PT:=T#5s); 
Timer2(IN:=AktuellerSchritt = SuchenRechts,            PT:=T#10s); 
Timer3(IN:=AktuellerSchritt = Abschieben,            PT:=T#20s);
```

AktuellerSchritt = SuchenLinks ist ein Boolscher Ausdruck. Timer1.IN erhält den Wert des Vergleichs von AktuellerSchritt und SuchenLinks. Hat AktuellerSchritt den gleichen Wert wie SuchenLinks dann ist Timer1.IN = true, wenn nicht dann ist er false.
Alternativ und zum besseren Verständnis kann man auch schreiben:

```
If AktuellerSchritt = SucheLinks THEN
  Timer1(IN := TRUE, PT := t#5s);
ELSE
  Timer1(IN := FALSE, PT := t#5s);
END_IF
```


Der zyklische Ablauf von Programm für Industriesteuerungen wurde mit Sicherheit ganz bewusst gewählt 
Ein wichtiger Grund ist z.B. die Konsistenz der Ein- und Ausgangsdaten und die Konsistenz der Programmabarbeitung. Wenn du deine Eingangssignale verarbeitest möchtest du alle Signale als Schnappschuss von EINEM definiertem Zeitpunkt haben. Außerdem arbeitest du ja nicht nur mit Einzelsignalen sondern verknüpfst auch die Zustände mehrerer Signale miteinander, das ist mit Ereignissen garnicht mehr zu bewerkstelligen, wenn diese immer nur den Zustandswechsel von einem Eingang anzeigen. 
Versuche mal das Beispiel von StruckturedTrash als Ereignisgesteuertes Programm umzusetzen und du wirst sehen, dass man selbst bei solchen einfachen Sachen schon an die Grenze stößt.

PC Programme arbeiten idR auch nur dann rein Ereignisgesteuert, wenn sie nur auf wenige Eingaben (User-Input) reagieren müssen. Aber Bediener können selten mehrere hundert Eingaben gleichzeitig und das x-mal pro Sekunde durchführen. In einer Maschine ist das aber üblich. Alles andere was bei einem PC im Hintergrund läuft wird auch zyklisch abgearbeitet....


----------



## StructuredTrash (6 Januar 2015)

Danke für die Korrektur. Sch... Doppelpunkt.


----------



## DerBenutzer (6 Januar 2015)

Ok das man die Hardware für eine solche Anzahl an Signalen braucht, hatte ich nicht bedacht und das übliche PC Programme auch durchgehend z.B. die Position des Cursors im Hintergrund pollen stimmt ja auch. Es ist zwar alles ein bisschen abstrakt, weil ich mir generell nicht vorstellen kann, rein von der Rechenkapazität und der Geschwindigkeit, dass ein PC der viele Milliarden Operationen pro Sekunde bewältigen kann, diese Signal softwaretechnisch nicht verarbeiten könnte, aber das kann man gerade nicht ändern, da fehlt einfach die Erfahrung im Umgang mit diesen Maschinen bei mir.

Die Frage ist wenn Schleifen für den Zyklus einer SPS so verheerend sein könnten wegen Störungen, wieso es diese überhaupt gibt. Wenn ich da wenig Spielraum im Bereich von Millisekunden habe, müsste ich ja praktisch die Komplexität und Mächtigkeit jeder Schleife genau beurteilen bevor ich sie überhaupt anwende...

Grundsätzlich habe ich das Programm jetzt nochmal etwas angepasst, nachdem ich in ein paar .pdf Dateien die Funktionsweise von TON, TOF und TS angeschaut habe, wusste z.B. gar nicht das es außer TON noch was anderes gibt...
Geändert habe ich Folgendes, erstmal oben die Variablen deklariert für rechtsdrehen und linksdrehen als logisches signal, die drei Timer und den Zähler.
Danach noch die Bedingung für Rechts und Linkslauf in den CASE SuchenLinks und SuchenRechts eingefügt. Jetzt müsste es ja alles seinen Lauf nehmen wie es sollte.

Eine kleine Sache bleibt allerdings noch.
Wie verhält es sich mit dem Zähler?
Der befindet sich ja in keinem definierten Zustand am Anfang, das heißt eigentlich
würde der erste Vergleich *IF zaehler<=4 THEN*dann eine Fehlermeldung ausspucken.
Das problem ist ich kann den zaehler auch nicht mit *zaehler:=0; *
bei den globalen Variablen initialisieren, weil ich dann ja immer den Zähler nach einem Zyklus zurücksetzen würde.

Deswegen bin ich gerade leicht überfragt wie man einen Zähler überhaupt machen soll.

```
VAR CONSTANT   WartenAufStart:USINT:=0;
   SuchenLinks:USINT:=1;
   SuchenRechts:USINT:=2;
   Abschieben:USINT:=3;
END_VAR
VAR
   AktuellerSchritt:USINT;
   Timer1:TON;
   Timer2:TON;
   Timer3:TON;
   zaehler:BYTE;
   RichtungRechts:BOOL;
   RichtungLinks:BOOL;
END_VAR


CASE AktuellerSchritt OF
   WartenAufStart:
      (* Reset aller Ausgänge *)
      IF StartFreigabe THEN   (* per Taster, Timer oder sonstwie *)
         (* Linkslauf einschalten *)
         AktuellerSchritt:=SuchenLinks;
      END_IF
   SuchenLinks:
      IF Lichtschranke THEN
         (* In Abhängigkeit vom Paketzähler Links- oder Rechtslauf einschalten *)
     IF zaehler<=4 THEN
        RichtungRechts:=False;
        RichtungLinks:=True;
     ELSE
        RichtungLinks:=False;
        RichtungRechts:=True;
     END_IF
         AktuellerSchritt:=Abschieben;
      ELSIF Timer1.Q THEN
         (* Rechtslauf einschalten *)
         AktuellerSchritt:=SuchenRechts;
      END_IF
   SuchenRechts:
      IF Lichtschranke THEN
         (* In Abhängigkeit vom Paketzähler Links- oder Rechtslauf einschalten *)
     IF zaehler<=4 THEN
        RichtungRechts:=False;
        RichtungLinks:=True;
     ELSE
        RichtungLinks:=False;
        RichtungRechts:=True;
     END_IF
         AktuellerSchritt:=Abschieben;
      ELSIF Timer2.Q THEN
         AktuellerSchritt:=WartenAufStart;
      END_IF
   Abschieben:
      IF Timer3.Q THEN
         IF Zaehler<10 THEN
            Zaehler:=Zaehler+1;
         ELSE
            Zaehler:=0;
         END_IF
         AktuellerSchritt:=WartenAufStart;
      END_IF
END_CASE


Timer1(IN:=AktuellerSchritt=SuchenLinks,
           PT:=T#5s);
Timer2(IN:=AktuellerSchritt=SuchenRechts,
           PT:=T#10s);
Timer3(IN:=AktuellerSchritt=Abschieben,
           PT:=T#20s);
```


----------



## StructuredTrash (6 Januar 2015)

DerBenutzer schrieb:


> Ok das man die Hardware für eine solche Anzahl an Signalen braucht, hatte ich nicht bedacht und das übliche PC Programme auch durchgehend z.B. die Position des Cursors im Hintergrund pollen stimmt ja auch. Es ist zwar alles ein bisschen abstrakt, weil ich mir generell nicht vorstellen kann, rein von der Rechenkapazität und der Geschwindigkeit, dass ein PC der viele Milliarden Operationen pro Sekunde bewältigen kann, diese Signal softwaretechnisch nicht verarbeiten könnte, aber das kann man gerade nicht ändern, da fehlt einfach die Erfahrung im Umgang mit diesen Maschinen bei mir.


Es geht dabei ja auch nicht nur um schiere Rechenleistung, wie MasterOhh und ich schon geschrieben haben.



DerBenutzer schrieb:


> Die Frage ist wenn Schleifen für den Zyklus einer SPS so verheerend sein könnten wegen Störungen, wieso es diese überhaupt gibt. Wenn ich da wenig Spielraum im Bereich von Millisekunden habe, müsste ich ja praktisch die Komplexität und Mächtigkeit jeder Schleife genau beurteilen bevor ich sie überhaupt anwende...


Stimmt. Deshalb findet man in SPS-Programmen Schleifen auch nicht so oft.



DerBenutzer schrieb:


> Grundsätzlich habe ich das Programm jetzt nochmal etwas angepasst, nachdem ich in ein paar .pdf Dateien die Funktionsweise von TON, TOF und TS angeschaut habe, wusste z.B. gar nicht das es außer TON noch was anderes gibt...
> Geändert habe ich Folgendes, erstmal oben die Variablen deklariert für rechtsdrehen und linksdrehen als logisches signal, die drei Timer und den Zähler.
> Danach noch die Bedingung für Rechts und Linkslauf in den CASE SuchenLinks und SuchenRechts eingefügt. Jetzt müsste es ja alles seinen Lauf nehmen wie es sollte.


Die Richtung stimmt, aber Weg ist noch lang. Da sind noch einige Kommentare, die Du durch konkreten Code ersetzen musst.



DerBenutzer schrieb:


> Eine kleine Sache bleibt allerdings noch.
> Wie verhält es sich mit dem Zähler?
> Der befindet sich ja in keinem definierten Zustand am Anfang, das heißt eigentlich
> würde der erste Vergleich *IF zaehler<=4 THEN*dann eine Fehlermeldung ausspucken.
> ...


Variablen können im Deklarationsteil mit Werten vorbelegt werden, z. B.

```
VAR
   Zaehler:BYTE:=0;
END_VAR
```
Ist in Deinem Fall aber nicht notwendig, weil alle Variablen ohne eine solche Vorgabe beim Programmstart auf 0 gesetzt werden.


----------



## DerBenutzer (6 Januar 2015)

Hmm... also ich seh jetzt nicht wo man da noch was ändern müsste. Ich habe mal alles rot markiert was ich gemacht habe. Die StartFreigabe kann man eigentlich immer als True annehmen, habe sie nur pro forma drinnen gelassen, falls man doch mal einen An/Aus Knopf möchte. Danke für den Hinweis mit dem Zähler, das hatte ich gemeint.


```
VAR CONSTANT   
    WartenAufStart:USINT:=0;
    SuchenLinks:USINT:=1;
    SuchenRechts:USINT:=2;
    Abschieben:USINT:=3;
END_VAR
VAR
   AktuellerSchritt:USINT;
[COLOR=#ff0000]   Timer1:TON;
   Timer2:TON;
   Timer3:TON;
   zaehler:BYTE;
   RichtungRechts:BOOL;
   RichtungLinks:BOOL;[/COLOR]
END_VAR




CASE AktuellerSchritt OF
   WartenAufStart:
            
      IF StartFreigabe THEN   (* per Taster, Timer oder sonstwie *)
         [COLOR=#ff0000]RichtungRechts:=False;
         RichtungLinks:=True;[/COLOR]
         AktuellerSchritt:=SuchenLinks;
      END_IF
   SuchenLinks:
      IF Lichtschranke THEN
         
         [COLOR=#ff0000]IF zaehler<=4 THEN
            RichtungRechts:=False;
            RichtungLinks:=True;
         ELSE
            RichtungLinks:=False;
            RichtungRechts:=True;
         END_IF[/COLOR]
         AktuellerSchritt:=Abschieben;
      ELSIF Timer1.Q THEN
         [COLOR=#ff0000]RichtungLinks:=False;
         RichtungRechts:=True;
         Timer1.IN:=False;[/COLOR]
         AktuellerSchritt:=SuchenRechts;
      END_IF
   SuchenRechts:
      IF Lichtschranke THEN
         
         [COLOR=#ff0000]IF zaehler<=4 THEN
            RichtungRechts:=False;
            RichtungLinks:=True;
         ELSE
            RichtungLinks:=False;
            RichtungRechts:=True;
         END_IF[/COLOR]
         AktuellerSchritt:=Abschieben;
      ELSIF Timer2.Q THEN
         [COLOR=#ff0000]Timer2.IN:=False;[/COLOR]
         AktuellerSchritt:=WartenAufStart;
      END_IF
   Abschieben:
      IF Timer3.Q THEN
         [COLOR=#ff0000]Timer3.IN:=False;[/COLOR]
         IF Zaehler<10 THEN
            Zaehler:=Zaehler+1;
         ELSE
            Zaehler:=0;
         END_IF
         AktuellerSchritt:=WartenAufStart;
      END_IF
END_CASE
```


----------



## StructuredTrash (6 Januar 2015)

Ja, dann wird es wohl Zeit, das Programm mal laufen zu lassen. Die Anzahl der I/Os ist ja recht überschaubar, mit ein paar Schalt- und Anzeigeflächen auf einer Visu-Seite sollte sich das Ganze gut simulieren lassen.


----------



## DerBenutzer (6 Januar 2015)

Ok dann schonmal vielen Dank für die ausführliche Hilfe und die ganzen erklärungen, ich werde meine Uni mal fragen ob die es organisiert bekommen mir so etwas zur Verfügung zu stellen.


----------

