Step 7 Probleme mit SFC14 und 15 in SCL

Zuviel Werbung?
-> Hier kostenlos registrieren
Im Diagnosepuffer steht


Ereignis 1 von 500: Ereignis-ID 16# 4562
STOP durch Programmierfehler (OB nicht geladen oder nicht möglich, bzw. kein FRB vorhanden )
FC-Nummer: 4
Bausteinadresse: 898
Bisheriger Betriebszustand: RUN
Angeforderter Betriebszustand: STOP (intern)
interner Fehler, kommendes Ereignis
11:03:11.132 08.09.2014
(Kodierung: 16# 4562 FF84 8C00 0101 0004 0382)


Ereignis 2 von 500: Ereignis-ID 16# 2522
Bereichslängenfehler beim Lesen
Instanz-DB, Wortzugriff, Zugriffsadresse: 340
Angeforderter OB: Programmierfehler-OB (OB 121)
OB nicht vorhanden oder gesperrt oder nicht startbar im aktuellen Betriebszustand
interner Fehler, kommendes Ereignis
11:03:11.068 08.09.2014
(Kodierung: 16# 2522 FE79 0025 0154 0000 0000)


Ereignis 3 von 500: Ereignis-ID 16# 4302
Betriebszustandsübergang von ANLAUF nach RUN
Anlaufinformation:
- Uhr für Zeitstempel bei letztem NETZ-EIN gepuffert
- Einprozessorbetrieb
Aktuelle/letzte durchgeführte Anlaufart:
- Neustart (Warmstart) über Betriebsartenschalter; letzter NETZ-EIN gepuffert
Zulässigkeit bestimmter Anlaufarten:
- manueller Neustart (Warmstart) zulässig
- automatischer Neustart (Warmstart) zulässig
Letzte gültige Bedienung oder Einstellung der automatischen Anlaufart bei NETZ-EIN:
- Neustart (Warmstart) über Betriebsartenschalter; letzter NETZ-EIN gepuffert
Bisheriger Betriebszustand: ANLAUF (Neustart/Warmstart)
Angeforderter Betriebszustand: RUN
kommendes Ereignis
11:01:26.628 08.09.2014
(Kodierung: 16# 4302 FF68 C700 0000 0813 7713)

Ich habe den Code in einer Funktion programmiert. Und die Variablen sind unter VAR deklariert.

Die Funktion tauschen sieht folgendermaßen aus:

Code:
FUNCTION Tauschen :VOID
VAR_INPUT
    X1:WORD;      
END_VAR

VAR
    X2:WORD;
    UEBERGABE : BYTE;
    Y AT X2: ARRAY[0..1] OF BYTE;
END_VAR

VAR_OUTPUT
    X3:WORD;
    END_VAR

BEGIN
    X2:= X1;
    UEBERGABE := Y[0];
    Y[0]:= Y[1];
    Y[1] := UEBERGABE;
    X3 := X2;
END_FUNCTION
 
Die Deklaration sieht folgendermaßen aus:

Code:
VAR
    Pointer1: STRUCT  //ANY Struktur 1 anlegen            
        ANY_id: BYTE;  
        Quelle_Datentyp: BYTE; 
        Quelle_Laenge: WORD;
        Quelle_DB_Nummer: WORD;
        Quelle_Byte_Pointer: DWORD;
        END_STRUCT;
    Pointer2: STRUCT  //ANY Struktur 2 anlegen
        ANY_id: BYTE;
        Ziel_Datentyp: BYTE;
        Ziel_Laenge: WORD;
        Ziel_DB_Nummer: WORD;
        Ziel_Byte_Pointer: DWORD;
        END_STRUCT;
    //Deklaration ANY Pointer für Quell-DB
    pAny_Quelle AT Pointer1: ANY;  
    //Deklaration ANY Pointer für Ziel-DB
    pAny_Ziel AT Pointer2: ANY;
END_VAR

Der DB existiert in der entsprechenden Größe.
 
Existiert der DB auch in der SPS ?
Ansonsten vielleicht mal die Schrittkette nach meinem Vorschlag umändern und dann Schritt für Schritt die Ergebnisse anschauen (so würde ich da vorgehen).

Mußt du eigentlich die Daten für den Blockmove in einem externen DB halten ? Ich würde die in der Instanz des eigenen Bausteins anlegen - dann brauchst du dir den ANY auch nicht kompliziert basteln - das macht SCL dann für dich ... (und du kannst es m.E. besser debuggen).

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe den Code jetzt mal folgendermaßen geändert:

Code:
CASE SCHRITT OF
        1:  
            PROGSEG := 0;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG); 
            DB1.DBB43 := INT_TO_BYTE(6);//Kommando-ID auf 6 setzen: Programmsegment übertragen       
            Quelle := 20;
            Ziel := 0;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            
            erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von SFC20 Blockmove um Teile des Datenbausteins zu kopieren

            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            STROBEID_SPS := 1;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            SCHRITT := 2;

            
        2:          
            IF STROBEID_SINIUS = 1 AND RESULT = CMDID THEN
            SCHRITT := 3;
            END_IF;
        
        3:
            PROGSEG := 1;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Quelle := Quelle + 32;
            Ziel := Ziel + 32;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]);
            POINTER_UEBERTRAGEN := POINTER_UEBERTRAGEN + DWORD_TO_DINT(dw#16#84000100);
            erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von SFC20 Blockmove um Teile des Datenbausteins zu kopieren
            STROBEID_SPS := 2;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            SCHRITT := 4;
            
            
         4:
            IF STROBEID_SINIUS = 2 AND RESULT = CMDID THEN
            SCHRITT := 5;
            END_IF;

END_CASE;

SPS bleibt in RUN und der Schritt wird bis 5 hoch gezählt. Jedoch steht nachher in DB1 nur der Stromwert von Segment 0.

Zu den Fragen zuvor: Ja, der DB ist in der SPS vorhanden. Ich hätte schon gerne einen eigenen DB, in dem die verschiedenen Programmsegmente drin stehen. Wie würde das denn sonst aussehen? Wird der DB dann mit der Funktion deklariert?

Edit: Normalerweise läuft doch der grüne Balken bei RUN immer von links nach rechts durch, wenn der Baustein aufgerufen wird. Beim DB1 passiert dies auch immer, jedoch wenn ich mir die SCL-Quelle (also FC4) anschaue passiert dies nicht. Warum? Call FC1 sowie Call FC4 steht allerdings in OB1.
 
Zuletzt bearbeitet:
Na ... die Sachen mal der Reihe nach ...

Koppelbereich in der eigenen Instanz :
Code:
VAR 
meineDaten : struct
   meinWert1  : int ;
   meinWert2  : int ;
   meinWert3  : int ;
   meinWert4  : int ;
end_struct ;


... und im Code :
pAny_Quelle := meineDaten ;

Aktualwert :
was liesst du denn wann zurück und wo wird das hingeschrieben ?
Also : was sollte in deinen Empfangsdaten drin stehen ?

SCL-Status-Anzeige:
Das ist "aus der Ferne" ein bißchen schlecht zu sagen. Es kommt aber auf jeden Fall darauf an (gerade bei der Select case-Geschichte) wo du dich mit dem Cursor im Programmcode befindest. Die Status-Anzeige kann dir die bedingte Bearbeitung des Select case-Blocks wahrscheinlich nicht korrekt auflösen solange sie dynamisch ist (also durchläuft).

Dein Programm stürzt nun nicht mehr ab ?

Gruß
Larry
 
Code:
FUNCTION FC4 : VOID


VAR
    Pointer1: STRUCT  //ANY Struktur 1 anlegen            
        ANY_id: BYTE;  
        Quelle_Datentyp: BYTE; 
        Quelle_Laenge: WORD;
        Quelle_DB_Nummer: WORD;
        Quelle_Byte_Pointer: DWORD;
        END_STRUCT;
    Pointer2: STRUCT  //ANY Struktur 2 anlegen
        ANY_id: BYTE;
        Ziel_Datentyp: BYTE;
        Ziel_Laenge: WORD;
        Ziel_DB_Nummer: WORD;
        Ziel_Byte_Pointer: DWORD;
        END_STRUCT;
    //Deklaration ANY Pointer für Quell-DB
    pAny_Quelle AT Pointer1: ANY;  
    //Deklaration ANY Pointer für Ziel-DB
    pAny_Ziel AT Pointer2: ANY;  
    erg: INT;  //Rückgabewert
    STROBEID_SPS, STROBEID_SINIUS, STROBE_ALT: INT;
    CMDID, RESULT: INT;
    S_PROG_UEBERTRAGEN:BOOL; //Schalter Kommando ausführen, Errorflag, Schalter Programm übertragen
    PROGSEG, PROGSEG_SINIUS, SCHRITT : INT;
    QUELLE, ZIEL :INT;
    POINTER_UEBERTRAGEN :DINT;
END_VAR

BEGIN
CMDID := BYTE_TO_INT(DB1.DBB43);
STROBEID_SPS := BYTE_TO_INT(DB1.DBB42);
STROBEID_SINIUS := BYTE_TO_INT(DB1.DBB6);
RESULT := BYTE_TO_INT(DB1.DBB7);
S_CMD := DB2.DBX0.0;
ERRFLG := DB1.DBX7.7;
S_PROG_HOLEN := DB2.DBX0.1;
S_PROG_UEBERTRAGEN := DB2.DBX0.2;             
PROGSEG_SINIUS := BYTE_TO_INT(DB1.DBB9);



IF S_PROG_UEBERTRAGEN = TRUE THEN
            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;
            SCHRITT := 1;
            POINTER_UEBERTRAGEN := DWORD_TO_DINT(Pointer1.Quelle_Byte_Pointer);
            DB2.DBX0.2 := FALSE;            
END_IF;

CASE SCHRITT OF
        1:  
            PROGSEG := 0;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG); 
            DB1.DBB43 := INT_TO_BYTE(6);//Kommando-ID auf 6 setzen: Programmsegment übertragen       
            Quelle := 20;
            Ziel := 0;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            
             erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von  SFC20 Blockmove um Teile des Datenbausteins zu kopieren

            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            STROBEID_SPS := 1;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            SCHRITT := 2;

            
        2:          
            IF STROBEID_SINIUS = 1 THEN
            SCHRITT := 3;
            END_IF;
        
        3:
            PROGSEG := 1;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Quelle := Quelle + 32;
            Ziel := Ziel + 32;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]);
            POINTER_UEBERTRAGEN := POINTER_UEBERTRAGEN + DWORD_TO_DINT(dw#16#84000100);
             erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von  SFC20 Blockmove um Teile des Datenbausteins zu kopieren
            STROBEID_SPS := 2;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            SCHRITT := 4;
            
            
         4:
            IF STROBEID_SINIUS = 2 THEN
            SCHRITT := 5;
        END_IF;
        
                5:
            PROGSEG := 2;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Quelle := Quelle + 32;
            Ziel := Ziel + 32;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]);
            POINTER_UEBERTRAGEN := POINTER_UEBERTRAGEN + DWORD_TO_DINT(dw#16#84000100);
             erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von  SFC20 Blockmove um Teile des Datenbausteins zu kopieren
            STROBEID_SPS := 2;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            SCHRITT := 6;
            
            
         6:
            IF STROBEID_SINIUS = 2 THEN
            SCHRITT := 7;
        END_IF;
        


END_CASE;

END_FUNCTION

Das hier ist mal die komplette Funktion, wie sie momentan aussieht.

Zum eigentlichen Ablauf: Ich gebe über die Visualisierung erstmal nur Werte für den Anfangsstrom ein (z.B. Segment 0: 1, Segment 1: 2, Segment 2: 3). Diese werden in DB6 an den entsprechenden Stellen gespeichert (das funktioniert auch korrekt - die Werte werden mir dann in DB6 angezeigt). Wenn ich nun übertragen will, müssen HIGH und LOW-Byte dieser Werte zuerst einmal getauscht werden - dazu wird die Funktion tauschen aufgerufen. Danach werden die Daten von DB6 in DB1 an die Stelle für den Anfangsstrom geschrieben. Progseg (0) und CMDID (6) werden in DB1 geschrieben. Die StrobeID wird verändert indem der Wert auf 1 gesetzt wird. Und dann wird das nächste Segment übertragen mit der gleichen Vorgehensweise allerdings mit anderen Daten.

Edit: Nein, das Programm stürzt nun nicht mehr ab.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
... ich bin die ganze Zeit davon ausgegangen, dass du dir einen FB erstellst.
Ein FC hat den kleinen Haken, das er sich NICHTS merken kann. Deine Variablen, wie z.B. Schritt und Andere, haben nur zufällig noch den richtigen Wert (von Zyklus zu Zyklus).
Die müssen alle Statisch sein (also STAT) - das kann aber nur ein FB - das solltest du erstmal ändern.

Gruß
Larry

Nachsatz:
erstaunlich, dass dein Compiler das mit dem VAR an Stelle von VAR_TEMP in der Deklaration angenommen hat ...
 
Ereignis 2 von 500: Ereignis-ID 16# 2522
Bereichslängenfehler beim Lesen
Instanz-DB, Wortzugriff, Zugriffsadresse: 340
Das kommt vermutlich nicht vom selbstgebastelten ANY-Pointer, weil der eine Bereichskennung 16#84 hat (haben sollte) - das ist "DB", der Zugriffsfehler kommt aber bei Zugriff auf "DI"... hast Du vielleicht noch eine weitere ANY-Baustelle?

Vorher würde ich aber erstmal dieses große Problem abstellen:
Ich habe den Code in einer Funktion programmiert. Und die Variablen sind unter VAR deklariert.
In einer Function ist VAR = VAR_TEMP. TEMP-Variablen haben kein Gedächtnis, die Variablen SCHRITT, STROBEID_SPS ... und was sonst noch da deklariert ist, haben beim nächsten Durchlauf einen unbestimmten Inhalt. Du kannst nicht erwarten, daß da ein Wert drinsteht, den Du im Durchlauf vorher reingeschrieben hast.

Du solltest den Code statt in einer Function (FC) besser in einem Function_Block (FB) programmieren.


POINTER_UEBERTRAGEN := POINTER_UEBERTRAGEN + DWORD_TO_DINT(dw#16#84000100);
Das kann man so nicht machen, weil dadurch die Bereichskennung mitaddiert wird. Das darf nicht sein.

Wenn Du schon in SCL programmierst:
Ist das nicht möglich, die Bereiche auf die Du zugreifen willst, irgendwie symbolisch anzusprechen?
(die werden sich doch irgendwie symbolisch deklarieren lassen, z.B. als Array OF Datensatz...)
Das erspart Dir eine Menge Fehler und das symbolische Zugreifen ohne fehlerträchtiges ANY-Gebastel ist gerade eine der Stärken von SCL gegenüber AWL.

Harald
 
Ich habe die Funktion in einen FB umgewandelt jedoch wird mir nun der Fehler "Deklaration einer Aufrufinstanz oder verwendeter Parameterdatentyp in diesem Vereinbarungsblock nicht zulässig". Und zwar an dieser Stelle:


Code:
    pAny_Quelle AT Pointer1: ANY;  
    pAny_Ziel AT Pointer2: ANY;

Was hat es damit auf sich?


Wie funktioniert das denn mit dem symbolischen ansprechen? Hast du da vielleicht mal ein Beispiel? Ich habe auch erst vor kurzem angefangen mit SCL zu programmieren und kenne mich da noch nicht so gut mit aus...


Edit: Ich habe diese beiden nun in VAR_OUTPUT geschrieben und dadurch mal zumindest keinen Fehler mehr (ob das aber so richtig ist, weiß ich nicht). Jetzt wird mir beim FC "Tauschen" - "Fehler beim Zugriff auf den Baustein (offline) angezeigt :confused:
 
Zuletzt bearbeitet:
Mit VAR_TEMP funktioniert dies nicht. Wenn ich anstelle dessen VAR_INPUT benutze, dann werden alle 10 Programmsegmente übertragen. Ich kann die Werte dann auch erfolgreich abrufen. Wie ich diese abrufe soll an dieser Stelle nicht weiter interessieren.

Mein Code sieht jetzt folgendermaßen aus:

Code:
FUNCTION_BLOCK FB1
VAR
    erg: INT;  //Rückgabewert
    STROBEID_SPS, STROBEID_SINIUS, STROBE_ALT: INT;
    CMDID, RESULT: INT;
    S_CMD, ERRFLG, S_PROG_HOLEN, S_PROG_UEBERTRAGEN:BOOL; //Schalter Kommando ausführen, Errorflag, Schalter Programm übertragen
    PROGSEG, PROGSEG_SINIUS, SCHRITT : INT;
    QUELLE, ZIEL :INT;
    POINTER_UEBERTRAGEN :DINT;
END_VAR
    VAR_INPUT
        Pointer1: STRUCT  //ANY Struktur 1 anlegen            
        ANY_id: BYTE;  
        Quelle_Datentyp: BYTE; 
        Quelle_Laenge: WORD;
        Quelle_DB_Nummer: WORD;
        Quelle_Byte_Pointer: DWORD;
        END_STRUCT;
    Pointer2: STRUCT  //ANY Struktur 2 anlegen
        ANY_id: BYTE;
        Ziel_Datentyp: BYTE;
        Ziel_Laenge: WORD;
        Ziel_DB_Nummer: WORD;
        Ziel_Byte_Pointer: DWORD;
        END_STRUCT;//Deklaration ANY Pointer für Quell-DB
    pAny_Quelle AT Pointer1: ANY;  
    //Deklaration ANY Pointer für Ziel-DB
    pAny_Ziel AT Pointer2: ANY;  
  
END_VAR

BEGIN
CMDID := BYTE_TO_INT(DB1.DBB43);
STROBEID_SPS := BYTE_TO_INT(DB1.DBB42);
STROBEID_SINIUS := BYTE_TO_INT(DB1.DBB6);
RESULT := BYTE_TO_INT(DB1.DBB7);
S_CMD := DB2.DBX0.0;
ERRFLG := DB1.DBX7.7;
S_PROG_HOLEN := DB2.DBX0.1;
S_PROG_UEBERTRAGEN := DB2.DBX0.2;             
PROGSEG_SINIUS := BYTE_TO_INT(DB1.DBB9);

IF S_PROG_UEBERTRAGEN = TRUE THEN
            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;
            SCHRITT := 1;
            DB2.DBX0.2 := FALSE;            
END_IF;


CASE SCHRITT OF
        1:  
            PROGSEG := 0;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG); 
            DB1.DBB43 := INT_TO_BYTE(6);//Kommando-ID auf 6 setzen: Programmsegment übertragen       
            Quelle := 20;
            Ziel := 0;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            Quelle := Quelle +2;
            Ziel := Ziel +2;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            Quelle := Quelle +2;
            Ziel := Ziel +2;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von SFC20 Blockmove um Teile des Datenbausteins zu kopieren

            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            STROBEID_SPS := 1;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            SCHRITT := 2;

            
        2:          
            IF STROBEID_SINIUS = STROBEID_SPS THEN
            SCHRITT := 3;
            END_IF;
        
        3:
            PROGSEG := 1;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            Quelle := Quelle + 28;
            Ziel := Ziel + 28;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]);
            Quelle := Quelle +2;
            Ziel := Ziel +2;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            Quelle := Quelle +2;
            Ziel := Ziel +2;
            Tauschen(X1:= DB6.DBW[Quelle], X3:= DB6.DBW[Ziel]); //HIGH- und LOW-Byte tauschen
            //    -Speicherber.     -|  nix |-  Byteadresse    -|Bitadresse
            //dw#2#1000 (=8) 0100 (=4) 0000 0000 0000 0001 0000 0000 = 32.0
            Pointer1.Quelle_Byte_Pointer:= dw#16#84000100;
            erg:= SFC20(srcblk:= pAny_Quelle, dstblk:= pAny_Ziel); //Aufruf von SFC20 Blockmove um Teile des Datenbausteins zu kopieren
            STROBEID_SPS := 2;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            SCHRITT := 4;
            
            
         4:
            IF STROBEID_SINIUS = STROBEID_SPS THEN
            SCHRITT := 5;
...

Ich habe hier der Übersicht halber nur die ersten beiden Programmsegmente notiert. Insgesamt besteht die Schrittkette jetzt aus 21 Schritten und die Segmente werden schon recht schnell übertragen. Bis hierhin schonmal Danke für die Unterstützung.

Kann man den Code denn noch reduzieren? Im Prinzip passiert hierbei immer das Gleiche - es ändern sich halt nur die Adressen zum Tauschen und Kopieren.
Das mit dem symbolischen Ansprechen verstehe ich noch nicht so ganz.
 
Hmm, wo anfangen?

Zunächst mal Kritik:

Du hast noch so viele Defizite, willst aber gleich das maximale Profi-Programm ...
SCL hast Du erst vor kurzem angefangen, ANY noch nicht richtig verstanden, die Unterschiede zwischen FB und FC und Variablen-Speicherorte kennst Du nicht, die zyklische Arbeitsweise der SPS hast Du anscheinend auch noch nicht richtig verinnerlicht, in Schrittketten teilst Du die Schritte falsch ein ... das ist einfach zuviel zum Korrigieren und Helfen. Wir können hier nicht einen Lehrgang für Dich veranstalten um Dir die Basics der SPS-Programmierung beizubringen.

Tut mir leid, daß ich Dich hiermit so runterziehe...
Ich kann Dir nur empfehlen: lerne in kleineren Schritten, programmiere zunächst kleine überschaubare Programmteile und teste häufiger und verstehe, was dabei passiert.


Ich schätze, der SPS-Stop wegen Zugriffsfehler auf Instanz-DB kam in Deinem Schritt 2 beim Lesen des DB6.DBW[Quelle], weil Du da, solange Schritt 2 auf die Weiterschaltung wartete, in jedem Zyklus unbegrenzt den Index Quelle erhöht hast und beim Lesen von DB6.DBW[Quelle] für den Aufruf von Tauschen(... kracht es dann wenn das Ende des DB6 erreicht ist ... Ganz davon abgesehen daß die Rückgabe an DB6.DBW[Ziel] auch ungewollt quer durch den DB6 schmiert. Und ganz davon abgesehen, daß Du wohl mächtig Glück hattest, wenn in Quelle und Ziel überhaupt noch Werte von vorher drinstanden.
Code:
        2:
            PROGSEG := 1;
            DB1.DBB45 := INT_TO_BYTE(PROGSEG);
            [COLOR="#FF0000"]Quelle := Quelle + 32;
            Ziel := Ziel + 32;[/COLOR]
            Tauschen(X1:= [COLOR="#FF0000"]DB6.DBW[Quelle][/COLOR], X3:= [COLOR="#FF0000"]DB6.DBW[Ziel][/COLOR]);
            STROBEID_SPS := 2;
            DB1.DBB42 := INT_TO_BYTE(STROBEID_SPS);
            [COLOR="#FF0000"]IF[/COLOR] STROBEID_SPS = STROBEID_SINIUS [COLOR="#FF0000"]THEN
            SCHRITT := SCHRITT + 1;[/COLOR]
            END_IF;

Übrigens kann man Übergabeparametern und Funktionen auch aussagekräftige Namen geben:
Tauschen(IN:= DB6.DW[Quelle], OUT:= DB6.DW[Ziel]);
liest sich viel besser als die Variante mit X1 und X3 (sind diese Namen aus einem Zufallsgenerator?;))

Statt SFC-Nummern kann man auch gerne die "sprechenden" symbolischen Namen benutzen.

Mich irritiert die Schreibweise Deiner Absolut-Zugriffe wie DB1.DBB45, DB6.DBW[Quelle], ...
Nach SCL-Dokumentation darf beim indizierten Zugriff auf Speicherbereiche der Adressangabe nach dem .D nur ein Größenpräfix X B W D folgen (also .DX .DB .DW .DD) Der SCL-Compiler scheint Deine undokumentierte Schreibweise jedoch zu schlucken - die solltest Du Dir aber besser wieder abgewöhnen. Irgendwann muß vielleicht mal jemand Code von Dir migrieren, der wird sich freuen über die viele nötige manuelle Nacharbeit ...

Du springst dauernd zwischen symbolischer und absoluter Adressierung hin und her. Greifst nach Belieben absolut auf externe globale Adressen zu, gerne auch unnötigerweise mehrfach ... das wirst Du selber in wenigen Wochen nicht mehr verstehen, geschweige denn ein anderer Programmierer.


Zu den ANY-Deklarationsproblemen:
Siemens hat nicht vorgesehen, daß man ANY-Variablen in STAT ablegt. Möglicherweise meint Siemens, daß man ANY nicht speichern soll.
ANY geht nur in VAR_INPUT, VAR_IN_OUT und VAR_TEMP. Wenn man ANY nicht in/aus dem Baustein übergeben will, dann gehört die Deklaration in VAR_TEMP, auch wenn TEMP kein Gedächtnis hat - ich würde niemals einen zusammengebastelten ANY aus dem Baustein entlassen und beim nächsten Durchlauf ungeprüft darauf vertrauen, daß er noch den selben Inhalt hat (womöglich nur um ein paar Zuweisungen zu sparen).

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
... und nun konstruktive Tips zum Bessermachen.

Wenn Du Deine DB1 und DB6 etwa so strukturieren würdest:
Code:
DATA_BLOCK DB1
  STRUCT
    Send : STRUCT                       //der zu sendende Datensatz (16 Byte)
      W1 : WORD ;                       //siehe DB6
      W2 : WORD ;
      W3 : WORD ;
      Data : ARRAY [1 .. 10] OF BYTE ;
    END_STRUCT ;
    irgendwas : ARRAY [16 .. 100] OF BYTE ; //irgendwelche weiteren DB-Bytes
  END_STRUCT ;
BEGIN
END_DATA_BLOCK

DATA_BLOCK DB6
  STRUCT
    DS : ARRAY [0 .. 9] OF STRUCT       //ein Array mit 10 Datensätzen
      Send : STRUCT                       //der zu sendende Datensatz (16 Byte)
        W1 : WORD ;                       //1. Wert mit H/L-Byte getauscht
        W2 : WORD ;                       //2. Wert mit H/L-Byte getauscht
        W3 : WORD ;                       //3. Wert mit H/L-Byte getauscht
        Data : ARRAY [1 .. 10] OF BYTE ;  //irgendwelche weiteren zu sendenden Datenbytes
      END_STRUCT ;
      Reserve1 : DWORD ;
      WQ1 : WORD ;                      //1. Wert wo H/L-Byte getauscht werden müssen
      WQ2 : WORD ;                      //2. Wert wo H/L-Byte getauscht werden müssen
      WQ3 : WORD ;                      //3. Wert wo H/L-Byte getauscht werden müssen
      Reserve2 : WORD ;
      Reserve3 : DWORD ;
    END_STRUCT ;
  END_STRUCT ;
BEGIN
END_DATA_BLOCK

... dann könntest Du den Code in Deinen Schritten schön symbolisch ohne jedes ANY-Gebastel so schreiben:
Code:
FUNCTION_BLOCK FBxxx
...
BEGIN
...
CASE SCHRITT OF
      1:
          progseg := 0 ;
          ...
          Tauschen(IN:= DB6.DS[0].WQ1, OUT:=DB6.DS[0].Send.W1); //HIGH- und LOW-Byte tauschen
          Tauschen(IN:= DB6.DS[0].WQ2, OUT:=DB6.DS[0].Send.W2); //HIGH- und LOW-Byte tauschen
          Tauschen(IN:= DB6.DS[0].WQ3, OUT:=DB6.DS[0].Send.W3); //HIGH- und LOW-Byte tauschen
          DB1.Send := DB6.DS[0].Send ;
...
      3:
          progseg := 1 ;
          ...
          Tauschen(IN:= DB6.DS[1].WQ1, OUT:=DB6.DS[1].Send.W1); //HIGH- und LOW-Byte tauschen
          Tauschen(IN:= DB6.DS[1].WQ2, OUT:=DB6.DS[1].Send.W2); //HIGH- und LOW-Byte tauschen
          Tauschen(IN:= DB6.DS[1].WQ3, OUT:=DB6.DS[1].Send.W3); //HIGH- und LOW-Byte tauschen
          DB1.Send := DB6.DS[1].Send ;

Wenn man den nun übersichtlichen Code anschaut, dann sieht man relativ leicht, wo man den Code eines Schrittes als parametrierbaren Code zusammenfassen oder auslagern könnte - er unterscheidet sich je Schritt ja nur im Index des Datensatzes:
Code:
          progseg := 0 ;
          ...
          Tauschen(IN:= DB6.DS[progseg].WQ1, OUT:=DB6.DS[progseg].Send.W1); //HIGH- und LOW-Byte tauschen
          Tauschen(IN:= DB6.DS[progseg].WQ2, OUT:=DB6.DS[progseg].Send.W2); //HIGH- und LOW-Byte tauschen
          Tauschen(IN:= DB6.DS[progseg].WQ3, OUT:=DB6.DS[progseg].Send.W3); //HIGH- und LOW-Byte tauschen
          DB1.Send := DB6.DS[progseg].Send ;

Wenn Du das so aufbaust, dann kannst Du einen Schritt auch mehrmals durchlaufen, weil der variable Index im Schritt nicht relativ verändert wird.

Für die Strukturen "Send" in DB1 und DB6 könntest Du auch einen UDT verwenden, dann mußt Du nur einmal beschreiben, wie die Struktur aussieht.

Harald
 
Hi Harald,
zuerst einmal danke für die konstruktive Kritik ;)

Die Basics zum SPS programmieren sollten bei mir eigentlich schon vorhanden sein. Wenn man aber 2 Jahre nichts mehr damit zu tun hatte, fällt es anfänglich etwas schwer dort wieder hinein zu kommen. Und wenn man vorher fast ausschließlich in FUP programmiert hat und nun mit SCL anfängt muss man sich auch dort erstmal zurecht finden. Dass dies hier nicht zu einem Lehrgang werden soll ist mir auch klar, deshalb werde ich mir in den nächsten Tagen das Buch „Automatisieren mit STEP 7 in AWL und SCL“ - von Hans Berger ausleihen und mir das zu Gemüte führen. Gibt es sonst noch Bücher oder andere Hilfsmittel, die jemand empfehlen kann?

Mich irritiert die Schreibweise Deiner Absolut-Zugriffe wie DB1.DBB45, DB6.DBW[Quelle], ...
Dies habe ich aus FUP übernommen wo der Any-Pointer z.B. mit P#DB1.DBX 0.0 Byte 100 deklariert wird. Dass dies anders geschrieben wird wusste ich nicht. Ich habe es demzufolge mal geändert. Auch den Tipp mit den Übergabeparametern und symbolischen Namen der SFC habe ich übernommen.

Du springst dauernd zwischen symbolischer und absoluter Adressierung hin und her. Greifst nach Belieben absolut auf externe globale Adressen zu, gerne auch unnötigerweise mehrfach ... das wirst Du selber in wenigen Wochen nicht mehr verstehen, geschweige denn ein anderer Programmierer.
Was meinst du damit genau? Was kann ich dabei besser machen?


Zu deinem 2. Post:
Also wenn ich die DBs direkt in SFC deklariere und so aufbaue, dann kann ich direkt auf die einzelnen Bytes/Wörter zugreifen und kann den ganzen Kram mit der ANY-Pointer-Deklaration weglassen?
 
Ich habe mal den Tipp befolgt und das Programm umgeschrieben, so dass die Any-Pointer bei der Programmsegmentübertragung rausfallen dies funktioniert auch. Das ist schon eine schöne Sache, jedoch habe ich jetzt ein anderes Problem...

Der DB1 ist dabei an DB6 angepasst zur Übertragung von Programmsegmenten. Bei Byte 4 der kommandospezifischen Daten (Kommando4) können allerdings verschiedene Modi durch setzen der jeweiligen Bits ausgewählt werden.

Das mit der Zuweisung funktioniert ja nur, wenn die Struktur Send genauso aufgebaut ist, wie der Datensatz in DB6. Gibt es eine Möglichkeit Byte 4 in 7 Boolesche Werte aufzuteilen um weiterhin mit der Zuweisung arbeiten zu können?

Das hier steht momentan in DB1

Code:
Send : STRUCT            //zu sendender Datensatz
        Kommando0 : WORD;   
        Kommando1 : WORD;       
        Kommando2 : WORD;      
        Kommando3 : BYTE;            
        Kommando4 : BYTE;
    END_STRUCT;

Ich habe mir ungefähr so etwas in DB6 dann vorgestellt:

Code:
DATA_BLOCK DB6
  STRUCT  
    Segment : ARRAY [0 .. 9] OF STRUCT   //ein Array mit 10 Datensätzen
      Send : STRUCT                 //der zu sendende Datensatz 16 Byte              
        Kommando0 : WORD;           //Anfangsstrom I1 mit getauschtem HIGH- und LOW-Byte
        Kommando1 : WORD;           //Endstrom I2 mit getauschtem HIGH- und LOW-Byte
        Kommando2 : WORD;           //Schweißdauer mit getauschtem HIGH- und LOW-Byte
        Kommando3 : BYTE;           //Druck
      
      Flags : STRUCT
            KSR : BOOL;
            NOCOMP : BOOL;
            EXTSOLL : BOOL;
            NAHT : BOOL;
            EXTANALOG : BOOL;
            NC : BOOL;
            NC2: BOOL;
            MULTIWELD : BOOL;
      END_STRUCT;
      END_STRUCT;

Beim kompilieren wird mir dann, verständlicherweise, an der Stelle:
Code:
DB1.SPS_an_Sinius.Send := DB6.Segment[1].Send;
ungültiger Datentyp angezeigt.

Weiterhin ist bei anderen Kommandos die Struktur der kommandospezifischen Daten anders aufgebaut als bei der Programmsegmentübertragung - vermutlich lässt sich das aber am besten über den SFC20 und die Verwendung von Any-Pointern lösen. Dabei ist die Struktur ja egal und es werden ohne Rücksicht auf Verluste die Bytes übertragen, die angegeben werden. Oder gibt es vielleicht doch noch eine andere Lösung?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
diese Zuweisung :
Code:
DB1.SPS_an_Sinius.Send := DB6.Segment[1].Send;
... funktioniert nur dann, wenn 1. die symbolischen Namen der DB's korrekt angelegt sind und auch so heißen und 2. der Quell- und der Zieldatentyp identisch sind - und zwar nicht nur von der Speicherbelegung her sondern komplett im Aufbau.

Ich vermute, dass es erstmal so heißen müßte :
Code:
"DB1".SPS_an_Sinius.Send := "DB6".Segment[1].Send;
Gruß
Larry
 
Das funktioniert leider nicht. Mir wird dabei angezeigt, dass das globale Symbol nicht angezeigt wird. Was hat es damit auf sich?

Edit:
Ersteres hat sich erledigt - ich musste die Symbolnamen nehmen, die ich vergeben habe. Jedoch erscheint weiterhin die Meldung "Ungültiger Datentyp"...
 
Zuletzt bearbeitet:
Du mußt schon ALLES lesen, was ich geschrieben habe :
... und 2. der Quell- und der Zieldatentyp identisch sind - und zwar nicht nur von der Speicherbelegung her sondern komplett im Aufbau ...
... das heißt, das in beiden DB's die letzte Struktur vom gleichen Datentyp (UDT) sein müssen. Ansonsten mußt du dir auf einen der DB's eine AT-Sicht erstellen, die das wieder passend macht - die AT-Sicht dann aber für den kompletten DB ...

Gruß
Larry
 
Zurück
Oben