Step 7 Probleme mit SFC14 und 15 in SCL

b.weyand85

Level-2
Beiträge
35
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
SFC14 und 15 in SCL in einem Zyklus laufen lassen

Bei einem DP-Slave möchte ich mittels dem SFC15 Daten an ein Slave via Profinet senden und mit SFC14 einlesen. Eigentlich wollte ich, dass Schreiben und Lesen direkt nacheinander ausgeführt werden und die entsprechenden Daten in einem DB gespeichert werden sollen.

Mein Problem hierbei ist, dass wenn ich beides hintereinander ausführe (also S_CMD) drücke, dann schreibt er mir die gelesenen Daten erst beim zweiten Betätigen dieses Taster in den DB. Mache ich dies hingegen einzeln (erst S_SCHREIBEN und dann S_LESEN drücken), dann schreibt er die Daten beim drücken von S_LESEN in den Datenbaustein.

Kann mir mal jemand erklären warum dies mit dem direkt hintereinander nicht funktioniert? Liegt das vielleicht daran, dass diese zu schnell hintereinander ausgeführt werden und dies bei der einzelnen Betätigung funktioniert weil dann ein zweiter Zyklus begonnen hat?

Gibt es eine Lösung, das beide hintereinander ausgeführt werden?



Code:
IF S_CMD = TRUE THEN         
    STROBEID_SPS := STROBEID_SPS+1;            
    DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
    Error1 := Schreiben_SFC15();
    Error2 := Lesen_SFC14();
    DB2.DBX0.0 := FALSE;
END_IF;

IF S_LESEN = TRUE THEN
    Error2 := Lesen_SFC14();
    DB2.DBX0.4 := FALSE;
END_IF;

IF S_SCHREIBEN = TRUE THEN
    STROBEID_SPS := STROBEID_SPS+1; 
    DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
    Error1 := Schreiben_SFC15();
    DB2.DBX0.3 := FALSE;
END_IF;
 
Zuletzt bearbeitet:
Hallo,
ist in deinem Script "Schreiben_SFC15" der Aufruf des SFC15 und "Lesen_SFC14" der Aufruf des SFC14 ?
Wenn ja, wo werden dann die Übergabe-Parameter an den Baustein wie LADDR und RECORD versorgt ?
Bei mir sieht so etwas z.B. so aus :
Code:
ret_wert_SFC14 := SFC14 (LADDR := Per_word , RECORD := Input_Data) ;
und ... man kann die beiden Baustein durchaus im selben Zyklus und hintereinander aufrufen ...

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Oh sorry, die Information hätte ich natürlich mitliefern sollen.

Ich habe dazu zwei einzelne Funktionen erstellt, die die jeweilige SFC aufruft. Das Ganze sieht dann wie folgt aus:

Code:
FUNCTION Lesen_SFC14: INT

VAR
    
    Error1 :INT;
    Pointer_Lesen : STRUCT
        ANY_id :BYTE;
        Datentyp : BYTE;
        Laenge : WORD;
        DB_Nr : WORD;
        Byte_Pointer : DWORD;
    END_STRUCT;
    
    pAny AT Pointer_Lesen : ANY;
END_VAR


//Versorgung der 10 Byte Buffer für den ANY-Pointer
Pointer_Lesen.ANY_id:= b#16#10;                 //Pufferlänge
Pointer_Lesen.Datentyp:= 2;                         //Code für Datentyp BYTE
Pointer_Lesen.Laenge:= INT_TO_WORD(32);             //Anzahl der Nutzdaten-Byte
Pointer_Lesen.DB_Nr:= INT_TO_WORD(1);          //Nummer des Ziel-DBs für die Nutzdaten
Pointer_Lesen.Byte_Pointer1 := dw#16#84000000;

Error1:= DPRD_DAT(LADDR:= w#16#100, RECORD:= pAny);  //Daten lesen mit SFC14

Lesen_SFC14 := Error1;

END_FUNCTION

FUNCTION Schreiben_SFC15: INT

VAR
    Error :INT;
    Pointer_Schreiben : STRUCT
        ANY_id :BYTE;
        Datentyp : BYTE;
        Laenge : WORD;
        DB_Nr : WORD;
        Byte_Pointer2 : DWORD;
    END_STRUCT;
    
    pAny AT Pointer_Schreiben : ANY;
END_VAR


//Versorgung der 10 Byte Buffer für den ANY-Pointer
Pointer_Schreiben.ANY_id2:= b#16#10;                 //Pufferlänge
Pointer_Schreiben.Datentyp2:= 2;                         //Code für Datentyp BYTE
Pointer_Schreiben.Laenge2:= INT_TO_WORD(32);             //Anzahl der Nutzdaten-Byte
Pointer_Schreiben.DB_Nr2:= INT_TO_WORD(1);          //Nummer des Ziel-DBs für die Nutzdaten
Pointer_Schreiben.Byte_Pointer2 := dw#16#84000130;
Error:= DPWR_DAT(LADDR:= w#16#100, RECORD:= pAny);  //Daten schreiben mit SFC15

Schreiben_SFC15 := Error;

END_FUNCTION


Beide Errors liefern auch eine 0 zurück, die Ausführung ist somit in Ordnung. Mich wundert halt nur, dass es erst beim zweiten Mal funktioniert, wenn ich die Taste drücke...
 
Woher weisst du denn, dass es erst beim 2. Mal S_CMD funktioniert ?
Hast du überlegt, dass dein PN-Slave vielleicht nicht unmittelbar auf deine Daten reagiert ?

Warum hast du übrigens den Code auf mehrere Bausteine aufgeteilt - das macht das Ganze m.E. eher unübersichtlich ...

Gruß
Larry
 
Ich wähle ein Programm vor, welches ich mit einem entsprechenden Kommando und durch das erhöhen der STROBEID übertrage. Dies läuft in der Visualisierung ab, deshalb habe ich das hier auch nicht eingefügt.
Dazu schreibe ich in DB1 ab BYTE 38 (deswegen dw#16#84000130) entsprechende Daten und sende diese an das Slave. Die empfangenen Daten sollen dann an die ab BYTE 0 geschrieben werden. Beim ersten Drücken wird nichts an diese Stelle geschrieben aber beim zweiten betätigen erhalte ich dort die Werte, die ich auch erwarte.

Dass der Slave nicht unmittelbar auf die Daten reagiert habe ich noch nicht bedacht - ich weiß aber auch nicht genau wie ich eine Wartezeit einfüge...

Den Code habe ich aufgeteilt, damit ich dies einzeln probieren konnte.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
... dann solltest du es vielleicht so machen, dass du das Empfangen immer laufen läßt und nur das Senden asynchron durchführst - wobei streng genommen macht auch das eigentlich keinen Sinn - du sparst dir dadurch nichts. Von der Funktion her ist der Daten-Handshake (wie du es ja selber auch schon beschreibst) darauf ausgelegt, dass sich etwas ändern muss.

Lass doch einfach die SFC14-SFC15-Geschichte permanent laufen und du änderst nur Ereignis-gestützt (z.B.) die jeweiligen Speicherinhalte.
Von was für einem PN-Slave reden wir da übrigens ?

Gruß
Larry
 
Es handelt sich um einen Sinius Inverter.

Problem bei dem permanenten Ablauf ist, wenn ich mehrere Programmsegmente übertrage (wo sich die Parameter wie Anfangsstrom, Endstrom, etc. ändern) und ich dies mit einer for-Schleife realisiere, wird mir nachher nur das letzte Segment richtig übertragen. Ich müsste dann schon so etwas wie eine Wartezeit einfügen und warten bis die Daten empfangen wurden.
 
Wenn du die unterschiedlichen Parameter immer in die selbe Adresse des Koppelbereichs schreiben mußt und über einen weiteren Parameter festlegst, was du da überträgst, dann wirst du um solche "Wartezeiten" (allerdings nicht innerhalb der Schleife) nicht herumkommen.

Gruß
Larry
 
@Ralle: Ja das stimmt, weil:

-Speicherber. -| nix |- Byteadresse -|Bitadresse
dw#2#1000 (=8) 0100 (=4) 0000 0000 0000 0001 0011 0000 = 38.0
=> dw#16#84000130


@Larry:
Ja, ich schreibe die an die selbe Stelle des Übertragungsbausteins. Wie realisiere ich denn eine solche Wartezeit einfach nur mit einem Timer, den ich dann an dieser Stelle einfüge?
 
@Ralle: Ja das stimmt, weil:

-Speicherber. -| nix |- Byteadresse -|Bitadresse
dw#2#1000 (=8) 0100 (=4) 0000 0000 0000 0001 0011 0000 = 38.0
=> dw#16#84000130


@Larry:
Ja, ich schreibe die an die selbe Stelle des Übertragungsbausteins. Wie realisiere ich denn eine solche Wartezeit einfach nur mit einem Timer, den ich dann an dieser Stelle einfüge?

Yep, stimmt, da war ich auf dem ganz falschen Fuß! :)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
BYTE 38 (deswegen dw#16#84000130) btw stimmt das???
@Ralle: Ja das stimmt, weil:

Statt der Hexadezimalzahl kann man auch eine Formel schreiben wie der Wert sich zusammensetzt. Das kann man später besser nachvollziehen. Wenn in der Formel nur Konstanten sind, dann schreibt der SCL-Compiler direkt das Ergebnis der Formel ins Programm (DW#16#84000130):
Code:
Pointer_Schreiben.Byte_Pointer:= DW#16#84000000 OR INT_TO_DWORD(38*8);

oder auch
Code:
CONST
    Any_Bereich_DB := DW#16#84000000 ; // ANY Bereichskennung "DB"
END_CONST
...
Pointer_Schreiben.Byte_Pointer:= Any_Bereich_DB OR INT_TO_DWORD(38*8);

Harald
 
@Larry:
Ja, ich schreibe die an die selbe Stelle des Übertragungsbausteins. Wie realisiere ich denn eine solche Wartezeit einfach nur mit einem Timer, den ich dann an dieser Stelle einfüge?

Hallo,
vielleicht legst du mal etwas mehr offen, wie dein Übertragungsbereich gestaltet ist und was du alles übertragen willst - dann würde ich dir ggf. einen Vorschlag machen können. So in etwa bin ich wahrscheinlich in der richtigen Spur (deswegen hatte ich auch schon nach dem Gerät gefragt weil ich gehofft hatte, dass es eines ist, das ich schon kenne) .

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Von der Datenstruktur her, sende ich 32 Byte an den Inverter und bekomme dann auch 32 Byte zurück.

Beide Sachen habe ich im DB1 stehen. Von Byte 0 bis 31 stehen die Daten, die vom Inverter kommen und von Byte 38 bis 69 sind die Daten, die an den Inverter gesendet werden. Dazwischen habe ich noch Spannungs- und Stromwerte eingefügt, bei denen High und Low-Byte vertauscht wurden, damit der Wert korrekt angezeigt wird.

Einen weiteren Datenbaustein habe ich erstellt um dort die Schweißprogramme abzuspeichern. Diese werden über ein Touchpanel für die einzelnen Programmsegmente (10 möglich) eingegeben. In DB6 sind dann diese 10 Segmente (wo z.B. Anfangs-, Endstrom, Dauer, etc. drin stehen) angelegt. Segment 1 geht von Byte 0 bis 19 und das nächste Segment fängt dann bei Byte 32 Segment 2 an. Dazwischen befinden sich wieder Platzhalter zum Tausch von High und Low-Byte.

Ich habe das bisher so gelöst, dass die in DB6 gespeicherten Daten mit SFC20 an die bestimmte Stelle in DB1 geschrieben werden. Hierzu zählt dann auch noch die Nr. des Programmsegmentes und die Kommando-ID. Das Kommando wird dann ausgeführt, wenn die Strobe-ID verändert wird (ich erhöhe diese dann um 1). Wenn das Kommando ausgeführt wurde steht in den Daten, die ich vom Inverter bekomme unter Result das Kommando, welches ausgeführt wurde und auch die Strobe-ID ist dann gleich.

Reicht das so an Informationen?
 
Ja ... so in etwa habe ich es mir auch schon vorgestellt ...
Du möchtest nun nacheinander mehrere Datenblöcke übertragen ?
Wenn ja, dann sähe dein Ablauf dafür wie folgt aus : (SFC14 und SFC15 laufen hierfür IMMER komplett mit)

1. Zuweisen der Daten auf den Übertragungs-Bereich
2. Setzen der Kommando-ID
3. Ändern der Strobe-ID
4. Warten bis die Strobe-ID und die Kommando-ID reflektiert worden sind (das kann u.U. mehrere SPS-Zyklen dauern)
5. weiter mit 1. - allerdings mit dem nächsten Datensatz

Das Ganze würde ich als eine Art Schrittkette in dem SCL-Baustein umsetzen (z.B. mit Select Case) ...

Anders wird das sonst wohl nicht sauber funktionieren.
Der ganze Ablauf kann nun so viele Datensätze übertragen, wie du brauchst und kann (bei entsprechender Ausführung) mit nur einem Trigger-Bit von "Aussen" angestossen werden. Wenn es fertig ist könntest du dann ja ein "Done" nach "Aussen" zurückgeben.

Konntest du dem Ganzen folgen ?

Gruß
Larry
 
Danke Larry, so werde ich das mal versuchen. Bisher hatte ich das ja mit einer for-Schleife versucht und das hatte nicht geklappt. Das mit der Schrittkette wird dann wohl die bessere Lösung sein.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe mal eine Schrittkette programmiert, doch leider geht die SPS immer in STOP, wenn ich den Programm-übertragen-Taster drücke.

Das Programm sieht folgendermaßen aus:

Code:
IF S_PROG_UEBERTRAGEN = TRUE THEN
    SCHRITT := 1;
    DB2.DBX0.2 := FALSE;
END_IF;


    CASE SCHRITT OF
        1:  
            DB1.DBB43 := INT_TO_BYTE(6); //Kommando-ID auf 6 setzen: Programmsegment übertragen
            Quelle := 20;
            Ziel := 0;
            PROGSEG := 0;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            Pointer1.ANY_id:= 16#10;  //Angabe der Syntax-ID
            Pointer1.Quelle_Datentyp:= 16#02; //Code für den Datentyp
            Pointer1.Quelle_Laenge:= 16#10;
            Pointer1.Quelle_DB_Nummer:= 16#06;                                    
            Pointer1.Quelle_Byte_Pointer:= dw#16#84000000; //Byte und Bitadresse des Datenbausteins hier 0.0
            //    -Speicherber.     -|  nix |-  Byteadresse    -|Bitadresse
            //dw#2#1000 (=8) 0100 (=4) 0000 0000 0000 0000 0101 0000                                                                          
            Pointer2.ANY_id:= 16#10;  //Vorbelegen der Ziel-ANY-Pointer Variablen
            Pointer2.Ziel_Datentyp:= 16#02;
            Pointer2.Ziel_Laenge:= 16#10;
            Pointer2.Ziel_DB_Nummer:= 16#01;
            //    -Speicherber.     -|  nix |-  Byteadresse    -|Bitadresse
            //dw#2#1000 (=8) 0100 (=4) 0000 0000 0000 0001 1000 0000 = 48.0 
            Pointer2.Ziel_Byte_Pointer:= dw#16#84000180;
            erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von SFC20 Blockmove um Teile des Datenbausteins zu kopieren
            STROBEID_SPS := 1;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            IF STROBEID_SPS = STROBEID_SINIUS THEN
            SCHRITT := SCHRITT +1;
            END_IF;
            
        2:
            PROGSEG := 1;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Quelle := Quelle + 32;
            Ziel := Ziel + 32;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]);
            STROBEID_SPS := 2;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            IF STROBEID_SPS = STROBEID_SINIUS THEN
            SCHRITT := SCHRITT + 1;
            END_IF; 
        
        3: 
            PROGSEG := 2;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Quelle := Quelle + 32;
            Ziel := Ziel + 32;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]);
            STROBEID_SPS := 3;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            IF STROBEID_SPS = STROBEID_SINIUS THEN
            SCHRITT := SCHRITT + 1;
            END_IF; 


END_CASE;

Wenn S_Uebertragen gedrückt wird, dann wird Schritt 1 gesetzt. In diesem werden die ganzen Vordefinitionen für Pointer etc. getrofffen. Nach dem Drücken funktioniert dies bis Schritt = 2 ist und auch Progseg wird = 1, jedoch geht dann die SPS in Stop. Wo liegt hier mein Denkfehler?

Weise ich der Strobe-ID := Strobe-ID + 1 zu, so bleibt die SPS im RUN, jedoch wird nie ein Schritt weitergeschaltet, weil StrobeID der SPS nie gleich der StrobID des Inverters wird...
 
Hallo,
interessant wäre es, zu wissen welchen Fehler du bekommst und wo das Programm abstürzt ...

Was mir generell zu deinem Code einfällt :

die Schrittkette würde ich so machen :
Code:
      2:
            PROGSEG := 1;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Quelle := Quelle + 32;
            Ziel := Ziel + 32;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]);
            STROBEID_SPS := 2;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            Schritt := 3 ;
3 :
            IF STROBEID_SPS = STROBEID_SINIUS THEN
            SCHRITT := 4;
            END_IF; 

usw.

Wie sieht der Code von "Tauschen" aus ?

Gruß
Larry
 
Auf die Schnelle:
- Wo hast Du den Code programmiert? Wo sind die Variablen SCHRITT, STROBEID... deklariert? FC und TEMP haben kein Gedächtnis.
- Müsste der Schritt 1 nicht eigentlich in 2 Schritte aufgeteilt werden? So wie jetzt wird das Tauschen und Kopieren immer wieder wiederholt.
- Der Code bei Tauschen(... und der Kommentar passen scheinbar nicht zusammen. (Word, Byte). Und steht da wirklich DB6.DBW[ ??
- CPU-Stop: was steht im Diagnosepuffer?

Ist das zusammenbasteln der konstanten Any wirklich nötig?

Harald
 
Zurück
Oben