Sequentieller Ablauf von Unterprogrammen in der MAIN, TWINCAT ST

kon86

Level-1
Beiträge
22
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Benutze Hardware Beckhoff CX-8190 mit TwinCat3.
Geschrieben wird in Strukturierter Text.

Hallo zusammen,

Ich habe folgendes Problem, Ich habe eine Anzahl von Unterprogrammen welche mit der Hardware (Ein- und Ausgänge) interagieren.
Soweit so gut, nun will Ich dass in der MAIN diese Unterprogramme nicht auf einmal aufgerufen werden sondern nacheinander.

Das ist der ungewollte Teil:
Code:
SW_Modul_CAN_Airbag
SW_Modul_CAN_Antrieb();
SW_Modul_CAN_CON();

Mein Idee war nun mit einem Variable vom Typ INTIGER namens "modulStep" dies zu steuern, Ich nutze dafür eine CASE -Abfrage :

Deklarationsteil MAIN:
Code:
PROGRAM MAIN
VAR
    
    modulStep : INT := 1; // Steuert den Ablauf der Modulprüfungen.
    


END_VAR



MAIN:
Code:
CASE modulStep OF

1:     SW_Modul_Airbag();
        modulStep:= modulStep +1; 

2:     SW_Modul_CAN_Antrieb();
        modulStep:= modulStep +1;

3:    SW_Modul_CAN_CON();
       modulStep:= modulStep +1;

END_CASE

Die Funktionsaufrufe der Module klappen leider nicht er führt diese nicht aus sondern erhöht nur die Variable
Habt ihr eine Lösung für dieses Problem.

Ich bin leider nicht sehr heimisch in der PLC Programmierung und ST, eher bei C und C++ zu Hause.

Danke im Vorraus
 
Wie viele SPS Zyklen soll/muss denn deine unterlagerte Funktion aktiv sein? In deinem Beispiel ruft er bestimmt auch kurz den Funktionsbaustein SW_Modul_Airbag() auf. Danach wird jedoch direkt dein modulStep inkrementiert und im nächsten Zyklus wird dein Funktionsbaustein daher nicht mehr aufgerufen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin kon86,

bei SIEMENS sind die meisten azyklischen Funktion inzwischen standardmäßig mit den Ausgängen "BUSY", "DONE" und "ERROR" versehen.
An Deiner Stelle würde ich so etwas auch in Deine Unterfunktionen einprogrammieren, damit Du im aufrufenden Baustein weißt, wann eine Funktion fertig ist (z.B. DONE). Das Done-Bit dann nehmen, um deinen Zähler zu inkrementieren.

VG

MFreiberger
 
Was meinst du mit "nicht gleichzeitig"? Sollen die Bausteine nicht im selben Zyklus aufgerufen werden?
Woran machst du fest, das die FBs nicht abgearbeitet werden? modulStep wird bei dir im Code auch wieder auf 1 gesetzt oder bleibt der Wert auf 4?

Wenn du uns mitteilst was du vorhast (und warum) können wir dir hier besser helfen. Das was du hier mit der CASE Verzweigung machst, sieht erstmal nicht so gut aus.

Sollen Modul-FBs einen Vorgang fertig abarbeitet bevor der nächste FB aufgerufen wird? Greifen diese FBs auf die selben Ressourcen zu (Kommunikation / Bus)?
 
Daß ein PLC Programm zyklisch immer wieder ausgeführt wird hast Du schon verinnerlicht?

Was meinst Du mit "nacheinander"? Die Unterprogramme werden so oder so nacheinander aufgerufen - ob nun alle 3 nacheinander im selben Zyklus oder in jedem Zyklus ein anderes ist kein großer Unterschied (nur das Aufrufintervall ist mit dem Aufrufverteiler 3x so groß wie die Task-Zeit).

Wie lange sollen die Unterprogramme jeweils aufgerufen werden? Bis irgendein Ergebnis/Ereignis erreicht ist und dann das nächste? Dann dürftest Du nicht stumpf modulStep weiterschalten sondern müßtest einen fertig-Status zurückgeben und nur dann modulStep weiterschalten. Bei PLC Programmierung ist es weiters eine ganz falsche Idee, in einem Programmteil in einer Schleife länger als die Task-Zeit auf ein Ereignis zu warten. Auf etwas warten macht man, indem das Programm einstweilen "nichts" tut und im nächsten Durchlauf wieder nachschaut ob das Ereignis mittlerweile eingetreten ist.

So wie es ist, tut Dein Programm nach 3 Durchläufen (Zyklen) nichts mehr. Es fehlt mindestens noch ein Zurückschalten des Aufrufverteilers auf den ersten CASE:
Code:
IF modulStep > 3 THEN
  modulStep := 1;
END_IF;

CASE modulStep OF

1:     SW_Modul_Airbag();
        modulStep:= modulStep +1; 

2:     SW_Modul_CAN_Antrieb();
        modulStep:= modulStep +1;

3:    SW_Modul_CAN_CON();
       modulStep:= modulStep +1;

END_CASE

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Harald,

ja das Habe ich verinnerlicht, jedoch bin Ich kein erfahrener Nutzer(leider).
Lass mich mal Kurz ausholen : meine einzelnen Module (Unterprogramme) sollen einfach nur folgendes tun ( Abfragen ob ein Ausgangssignal mit einem Eingangssignal True sind).
Die Variablen sind mit Ausgangsklemmen (EL 2809) und Eingangsklemmen ( EL 1809).

Wenn beide TRUE liefern dann in den String die Meldung schreiben das es Erfolgreich war. Wenn nicht dann halt den Misserfolg melden.
Das funktionert einwandfrei.

Nun will ich die einzelnen Module einzeln abarbeiten. Sie sollen nach einem Durchlauf "nie wieder" aufgerufen werden .
Außer Ich setzte den modulStep manuell zurück auf 1 und alle jeweiligen testSteps.


Die jeweiligen Module (Unterprogramme) sollen exakt einmal bis zum Ende durchlaufen und dann soll das nächste starten.

Deinen Einwurf mit dem Fertigstatus würde ich gerne näher erläutert haben! Ich würde gerne nur modulStep erhöhen wenn das Modul meldet das es durchgelaufen ist.



Hier ein Auszug aus meinem Code:
Code:
CASE testStep OF


         
1:         Signal.Out_FP_DSUB_7 := TRUE;
        delay1(IN :=TRUE, PT :=T#1S); 
        
        IF delay1.Q THEN
            
            IF Signal.Out_FP_DSUB_7 AND Signal.In_CAS1_A14 THEN
                StringList.Str_CAS1_A14 := 'FP DSUB7 nach CAS1 A14 : Erfolgreich geprüft';
            
            ELSIF Signal.Out_FP_DSUB_7 AND NOT Signal.In_CAS1_A14 THEN
                StringList.Str_CAS1_A14 := 'FP DSUB7 nach CAS1 A14 : Fehler aufgetreten.';
            END_IF
        
            delay1(IN := FALSE);
            Signal.Out_FP_DSUB_7 := FALSE; 
            testStep := testStep +1; 
        END_IF    




2:      Signal.Out_FP_DSUB_2 := TRUE; 
      delay2(IN :=TRUE, PT :=T#1S); // DELAY 1 SEKUNDE
        
      IF delay2.Q THEN
            
          IF Signal.Out_FP_DSUB_2 AND Signal.In_ICAS1_A13 THEN
                StringList.Str_ICAS1_A13  := 'FP DSUB2 nach ICAS1 A13 : Erfolgreich geprüft';
            
            ELSIF Signal.Out_FP_DSUB_2 AND NOT Signal.In_ICAS1_A13 THEN
                StringList.Str_ICAS1_A13  := 'FP DSUB2 nach ICAS1 A13 : Fehler aufgetreten.';
            END_IF
    
            delay2(IN := FALSE);
            Signal.Out_FP_DSUB_2 := FALSE;         testStep := testStep +1; 
        END_IF    
        
END_CASE
 
wenn ich die einzelnen Module alle in der MAIN aufrufe dann laufen diese parallel, das will ich aber nicht. Der Wert von modulStep bleibt auf 4 ( so will ich es auch).
Woran machst du fest, das die FBs nicht abgearbeitet werden?

ganz einfach wenn ich mir mich einlogge und das mir angucke was die Unterprogramme treiben laufen diese nicht zum Ende durch.

wenn ich sie so aufrufe :
Code:
programmA();
programmB();
programmC();

dann machen Sie was sie sollen und laufen bis zum Ende durch.


Sollen Modul-FBs einen Vorgang fertig abarbeitet bevor der nächste FB aufgerufen wird?...

Ja genau das wäre mein Ziel.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Exakt ein Zyklus sprich einen Durchlauf.

Da liegt dein Denkfehler.
Wenn du Code nur in einem Zyklus durchläufts, wird er auch nur in diesem einen Zyklus ausgeführt. D.h. z.B. deine Timer-Bausteine werden nie ablaufen, weil du sie nicht mehr aufrufst.
Auf einer SPS läuft kein Code im "Hintergrund" und feuert Events wenn er fertig ist. Rufst du einem Baustein nicht auf, wird auch der gesamte darin enthaltende Code nicht ausgeführt, inkl aller Bausteine, die aus diesem Baustein heraus aufgerufen werden.

Überlege dir Kontrollstrukturen für deine Module. (z.B. Execute, Busy, Done)
Starte die Ausführung des Codes im Baustein wenn eine positive Flanke bei Execute ankommt. Setzte den Ausgang Busy auf true solange der Baustein arbeitet. Wenn der FB fertig ist (alle Timer abgelaufen, etc. pp.) setzte Busy auf false und Done auf true. Warte dann auf die nächste positive Flanke von Execute.
 
Moin kon86,

Deinen Einwurf mit dem Fertigstatus würde ich gerne näher erläutert haben! Ich würde gerne nur modulStep erhöhen wenn das Modul meldet das es durchgelaufen ist.[/OUOTE]

Na da muss deine Funktion einen Rückgabewert liefert, der anzeigt, dass die Abarbeitung fertig durchlaufen ist.
Vielleicht einfach ein Bit (Fertig / Done / etc.).

Bei SIEMENS sind (soweit ich das weiß) FCs ohne diese Statusbits ausgeführt, da die FCs idR nur innerhalb eines Zykluses, d.h. zyklisch durchlaufen werden. Für Aufgaben, die sich über mehrere Zyklen hinweg erstrecken können (azyklischer Aufruf), werden überlicherweise FBs verwendet (u.a. um den aktuellen Bearbeitungsstatus anzuzeigen).

Also:
BUSY: wird TRUE, solange im Baustein die Bearbeitung stattfindet (unabhängig davon, ober er aufgerufen wird). BTW: Um die Bearbeitung zu starten, gibt es idR einen REQ (Request)-Eingang an dem Baustein
DONE: Wenn der Baustein mit der Bearbeitung fertig ist, für einen Zyklus auf TRUE
ERROR: Wenn es bei der Bearbeitung einen Fehler gab, für einen Zyklus auf TRUE

VG

MFreiberger
 
Code:
rtrigExecute(CLK := Execute);
if rtrigExecute.Q and not Busy then
  Busy := true;
  Done := false;
  testStep := 1;
end_if

if Busy then

  CASE testStep OF


         
  1:         Signal.Out_FP_DSUB_7 := TRUE;
          delay1(IN :=TRUE, PT :=T#1S); 
          
          IF delay1.Q THEN
             
             IF Signal.Out_FP_DSUB_7 AND Signal.In_CAS1_A14 THEN
                 StringList.Str_CAS1_A14 := 'FP DSUB7 nach CAS1 A14 : Erfolgreich geprüft';
             
             ELSIF Signal.Out_FP_DSUB_7 AND NOT Signal.In_CAS1_A14 THEN
                  StringList.Str_CAS1_A14 := 'FP DSUB7 nach CAS1 A14 : Fehler aufgetreten.';
              END_IF
          
             delay1(IN := FALSE);
             Signal.Out_FP_DSUB_7 := FALSE; 
             testStep := testStep +1; 
         END_IF    
 
 
 
 
 2:      Signal.Out_FP_DSUB_2 := TRUE; 
        delay2(IN :=TRUE, PT :=T#1S); // DELAY 1 SEKUNDE
         
        IF delay2.Q THEN
             
            IF Signal.Out_FP_DSUB_2 AND Signal.In_ICAS1_A13 THEN
                  StringList.Str_ICAS1_A13  := 'FP DSUB2 nach ICAS1 A13 : Erfolgreich geprüft';
              
              ELSIF Signal.Out_FP_DSUB_2 AND NOT Signal.In_ICAS1_A13 THEN
                  StringList.Str_ICAS1_A13  := 'FP DSUB2 nach ICAS1 A13 : Fehler aufgetreten.';
              END_IF
    
              delay2(IN := FALSE);
              Signal.Out_FP_DSUB_2 := FALSE;         testStep := testStep +1; 
          END_IF 

  3: Busy := false;
     Done := true;   
        
  END_CASE

end_if
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Okay das bringt mich schon mal in die richtige Richtung. Stecke leider null in der SPS - Geschichte drin, wie würde denn ein Konstrukt aussehen welches mir einen Rückgabewert zum Vergleichen liefern könnte?

Was ich einfach nur will ist : WENN modul1 FERTIG dann inkrementiere Variable und Starte nächsten Baustein. Es muss doch irgendwie ein Rückgabewert das sein den man als Bedingung abfragen lassen kann.
 
Hast du da mal ein Code Beispiel ? Muss gestehen die Beckhoff Seite bringt mir nichts und die restlichen Infos sind auch rar gesäht.
 
Zurück
Oben