Matlab Code auf S7 implementieren

T-Rex22

Level-1
Beiträge
11
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Leute

Ich bin gerade dabei eine Art Detektionsalgorithmus für Instabilitäten zu entwickeln. Dieser wurde zunächst in MATLAB geschrieben und getestet nun soll er noch auf eine SPS implementieren werden. Hatte zwar vor einigen Jahren ein wenig in FUP, KOP und AWL gelernt aber das hab ich seit dem nie wieder gebraucht und somit auch das meiste vergessen. Da ich aber case Anweisungen benötige muss ich hier auf scl zurückgreifen was für mich totales Neuland ist.

Dabei geht es um Folgendes, eine Prozessgröße soll gemessen werden und hinsichtlich ihrer Stabilität überwacht werden, dh. die Einhaltung einer oberen und unteren Grenze wird überwacht. Werden obere und untere Grenze innerhalb eines bestimmten Zeitintervalls verletzt, ist das ein Zeichen für die Instabilität der Prozessgröße. Die Grenzwerte sollen darüber hinaus der Prozessgröße folgen, dh. wird zB. die untere Grenze unterschritten, wird die neue untere Grenze (also für den nächsten Messwert) auf den aktuellen Wert gesetzt und die obere Grenze entsprechend dazu angepasst.

Wenn ein Grenzwert mehrmals hintereinander verletzt wird soll die Zeit immer wieder neu gestartet werden. Je nach Zustand werden dann verschiedene Anweisungen durchgeführt und ein neuer Zustand gesetzt.

Dabei stellen sich mir ein paar Fragen:
Was für Bausteine brauche ich dafür, und was gehört wo rein?

Wie muss ich die Zeit/Intervallüberprüfung implementieren?
Hab mich da schon mal umgeschaut, was es denn für Zeitfunktionen gibt und ich denke ein verlängerter Impuls sollte der richtige für meine Anwendung sein. Wie kann ich dann in meiner case Anweisung prüfen ob die Zeit bereits abgelaufen ist oder nicht?

Hier mal der Code-Auszug aus Matlab:

Code:
% Variablen & Signale
% oG_alarm = Merker/Alarm das obere Grenze verletzt
% uG_alarm = Merker/Alarm das untere Grenze verletzt
% Bd = halbe Bandbreite = halbe Differenz zwischen oberer und unterer Grenze (fester Wert)
% oberer_Grenzwert = obere Grenze (Gleitkomma)
% unterer_Grenzwert = untere Grenze (Gleitkomma)
% Messwert = aktueller Messwert der Prozessgröße (Gleitkomma)
% Zustand = Zustand (0...4) normaler Betrieb, obere Grenze wurde verletzt, untere Grenze wurde verletzt, Instabilität 
% time = Zeit
% time_end = Intervallgrenze der Zeit = max. Zeit zwischen 2 Verletzungen der Grenzen.

oG_alarm = 0;
uG_alarm = 0;

% Grenzwertnachfuehrung
If Messwert < unterer_Grenzwert
    unterer_Grenzwert = Messwert;
    oberer_Grenzwert = unterer_Grenzwert + 2*Bd;
    uG_alarm = 1;
    oG_alarm = 0;

elseif Messwert > oberer_Grenzwert
    oberer_Grenzwert = Messwert;
    unterer_Grenzwert = oberer_Grenzwert - 2*Bd;
    uG_alarm = 0;
    oG_alarm = 1;
end

if (time > time_end)
    time= 0;
    Zustand= 0;
elseif Zustand==1 || Zustand==2 || Zustand==3 || % Zustand ist 1 oder 2 oder 3
    time=time +1
end

switch Zustand
case 0 
    if oG_alarm == 1
        Zustand =1;
    elseif uG_alarm ==1
        Zustand == 2;
    elseif Zustand == 1
        Zustand = 1;
    elseif Zustand == 2
        Zustand = 2;
    elseif Zustand == 3
        Zustand = 3;
    end

case 1 
    if oG_alarm == 1     % wenn erneut Alarm (oben) ausgelöst wird bleibe in Zustand 1 und starte Intervall neu
        Zustand = 1;
        time = 0;
    elseif uG_alarm ==1    % wenn Alarm (unten) ausgelöst wird, wechsle in Zustand 3 
        time = 0;           
        Zustand == 3;
    elseif time > time_end   % wenn Zeit abgelaufen ist gehe zurück in (normalen) Zustand 0
        time = 0;
        Zustand = 0;
    elseif oG_alarm == 0 && time< time_end     % solange kein neuer Alarm (oben) ausgelöst wurde und die Zeit noch nicht abgelaufen ist bleibe in % Zustand 1
        Zustand = 1;         
    else 
        Zustand = 0;
    end

case 2 
    if uG_alarm == 1
        Zustand = 2;
        time = 0;
    elseif oG_alarm ==1
        time = 0;
        Zustand = 3;
    elseif time > time_end
        time = 0;
        Zustand = 0; 
    elseif uG_alarm == 0 && time < time_end
        Zustand = 2;
    else 
        Zustand = 0;
    end

case 3
    if time < time_end;
        Zustand = 3;
    else 
        time = 0;
        Zustand = 0;
    end
end
 
Hier mal noch der Zustandsgraph.
Zustand.PNG
Ich hoffe ihr könnt mir die richtigen Denkanstöße geben.
Vielen Dank schon mal im Voraus.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
... scl zurückgreifen was für mich totales Neuland ist
Nimm ruhig das Optionspaket S7-SCL, denn Deinen jetzigen Code musst Du nur geringfügig umschreiben, da SCL bereits hochsprachenorientiert ist!
Wenn Du SCL hast, dann schau auch mal in die Hilfe, dort gibt es einen Punkt "Erste Schritte".

Was für Kenntnisse bezüglich der Programmierung hast Du überhaupt?

Welchen Typ von SPS setzt Du ein?

Wie muss ich die Zeit/Intervallüberprüfung implementieren? Hab mich da schon mal umgeschaut, was es denn für Zeitfunktionen gibt und ich denke ein verlängerter Impuls sollte der richtige für meine Anwendung sein. Wie kann ich dann in meiner case Anweisung prüfen ob die Zeit bereits abgelaufen ist oder nicht?

vermutlich würde ich das mit einer Einschaltverzögerung lösen, kommt es innerhalb der Überwachungszeit zu keinem Fehler so wird der Timer am Ende "WAHR"
 
Zuletzt bearbeitet:
Was steckt dahinter?

Soll einfach nur ein zeitkontinuierliches Signal auf Grenzwerte geprüft werden oder soll beispielsweise die Instabilität eines Regelkreises detektiert werden?
 
Hallo erst mal,
konnte leider nicht früher antworten, da ich krank war.

Soll einfach nur ein zeitkontinuierliches Signal auf Grenzwerte geprüft werden oder soll beispielsweise die Instabilität eines Regelkreises detektiert werden?
Prinzipiell wird ein zeitkontinuierliches Signal überwacht. Wird z.B. der untere Grenzwert verletzt so ist das an sich noch nicht weiter schlimm. Wird aber zB. innerhalb von 20s auch der obere Grenzwert verletzt, verhält sich die Größe instabil. Dieses Verhalten soll erkannt werden, und anschließend sollen weitere Maßnahmen ergriffen werden.

Was für Kenntnisse bezüglich der Programmierung hast Du überhaupt?
Was die SPS-Programmierung angeht, hab ich im Studium ein paar praktische Versuche gehabt, in denen meist nur ein paar Eingänge miteinander zu verknüpfen waren und dann entsprechend die Ausgangssignale gesetzt wurden.


Welchen Typ von SPS setzt Du ein?
Siemens S7 CPU 315-2 DP

Habe mein MATLAB Code jetzt als FB programmiert:
Code:
FUNCTION_BLOCK FB1    
    (* Konstanten *)
    CONST
    Bd := 100;  
    END_CONST

    (* Eingangsparameter *)
    VAR_INPUT
        messwert: REAL; 
    END_VAR
    
    (* Ausgangsparameter *)
    VAR_OUTPUT
    
    END_VAR

    VAR_IN_OUT
        Zustand: INT:=0;        (* Zustand 
                                       0: Normal, 
                                       1: obere Grenze ueberschritten 
                                       2: untere Grenze unterrschritten
                                       3: Instabiles Verhalten entdeckt *)    
    END_VAR

    VAR
        oG_alarm: BOOL;              // Merker obere Grenze verletzt
        uG_alarm: BOOL;              // Merker untere Grenze verletzt
        unterer_Grenzwert: REAL;     // untere Grenze
        oberer_Grenzwert: REAL;      // obere Grenze
        zeit: S5TIME;        
    END_VAR
    
    BEGIN
        zeit:=S_PEXT (T_NO:=1, S:= M0.0, TV:= t#20s, R:= M0.1, Q:= M0.2); 
        
        // Merker zuruecksetzen
        uG_alarm := False ;
        oG_alarm := False;
        
        // Grenzwertnachfuehrung
        IF Messwert < unterer_Grenzwert THEN          
            unterer_Grenzwert := Messwert;                 // Setze neue untere Grenze auf aktuellen Messwert
            oberer_Grenzwert := unterer_Grenzwert + 2*Bd;  // Setze neue obere Grenze ausgehend von der neuen unteren Grenze
            uG_alarm := True;                    
            oG_alarm := False;                  
        ELSIF Messwert > oberer_Grenzwert THEN 
            oberer_Grenzwert := Messwert;                   // Setze neue obere Grenze auf aktuellen Messwert
            unterer_Grenzwert := oberer_Grenzwert - 2*Bd;  // Setze neue untere Grenze ausgehend von der neuen oberen Grenze
            uG_alarm := False;                             
            oG_alarm :=True;                    
        END_IF;
            

        //Zustandsautomat
        CASE Zustand OF
            // Normaler Zustand
            0:  IF oG_alarm = True THEN      // Wenn obere Grenze verletzt wird
                    Zustand := 1;            // Wechsle in Zustand 1
                     
                ELSIF uG_alarm = True THEN   // Wenn untere Grenze verletzt wird dann
                    Zustand := 2;            // Wechsle in Zustand 2
                    
                ELSIF Zustand = 1 THEN
                    Zustand := 1;                    
                ELSIF Zustand = 2 THEN
                    Zustand := 2;
                ELSIF Zustand = 3 THEN
                    Zustand := 3;               
                END_IF;
            ;
            // obere Grenze wurde verletzt
            1 : IF oG_alarm = True THEN     // Wenn obere Grenze erneut verletzt wird dann
                    Zustand := 1;           // bleibe in Zustand 1 und
                    M0.0 := False;              
                    M0.0 := True;           // starte Zeit erneut.
                    
                ELSIF uG_alarm = True THEN  // wenn untere Grenze verletzt wird dann
                    M0.0 :=False;           // starte Zeit und
                    M0.0 :=True;
                    Zustand := 3;           // wechsle in Zustand 3.
                    
                ELSIF M0.2 = True THEN      // Wenn Zeitintervall zur Detektion abgelaufen ist dann
                    M0.1 := True;           // setze Zeit zurück und
                    M0.1 := False;
                    Zustand := 0;           // wechsle in Zustand 0.
                    
                ELSIF oG_alarm = False AND M0.2 = 0 THEN // Wenn obere Grenze nicht erneut verletzt wird und das Intervall nicht abgelaufen ist dann
                    Zustand := 1;                        // bleibe in Zustand 1.
                ELSE
                    Zustand := 0;           // Sonst gehe zurück zu Zustand 0.                    
                END_IF;
            ;
            // untere Grenze wurde verletzt
            2 : IF uG_alarm = True THEN      // Wenn untere Grenze erneut verletzt wird dann
                    Zustand := 2;            // bleibe in Zustand 2 und
                    M0.0 := False;              
                    M0.0 := True;            // starte Zeit erneut.
                    
                ELSIF oG_alarm = True THEN   // Wenn obere Grenze verletzt wird dann
                    M0.0 :=False;            // starte Zeit und
                    M0.0 :=True;
                    Zustand := 3;            // wechsle in Zustand 3.
                    
                ELSIF M0.2 = True THEN       // Wenn Zeitintervall zur Detektion abgelaufen ist dann
                    M0.1 := True;            // setze Zeit zurück und
                    M0.1 := False;
                    Zustand := 0;            // wechsle in Zustand 0.
                    
                ELSIF uG_alarm = False AND M0.2 = False THEN // Wenn untere Grenze nicht erneut verletzt wird und das Intervall nicht abgelaufen ist dann
                    Zustand := 2;                            // bleibe in Zustand 2. 
                ELSE
                    Zustand := 0;            // Sonst gehe zurück zu Zustand 0. 
                END_IF;
            ;           
            // Instabilität erkannt            
            3 : IF M0.2 = False  THEN
                    Zustand := 3;
                ELSE
                    M0.1 := True;           // Setze Zeit zurück und
                    M0.1 := False;
                    Zustand := 0;           // wechsle in Zustand 0       
                END_IF;
            ;
         END_CASE;
END_FUNCTION_BLOCK
Hab jetzt noch ein paar Probleme bei denen ich nicht ganz weiter komme:

Wie rufe ich den FB1 im OB auf, das ich dann quasi den Zustand ausgegeben bekomme?

Bei meiner Symboltabelle bin ich mir auch nicht ganz sicher ob das alles so hinhaut oder nicht.
Muss ich z.B. die Merker für Starten und Rücksetzen der Zeit dort noch eintragen oder ist das alles mit dem Timer T 1 schon abgehandelt?
Wie sieht das mit den ganzen Merkern wegen der unterschiedlichen Speicherbedarfs (Für BOOL, BYTE und DWORD) aus, macht das S7 automatisch das sich am Ende nichts überlappt oder muss ich da selber entsprechend Platz lassen?

Symboltabelle:
SymbolAdresseDatentyp
Alarm_SSFC 18SFC 18
Alarm_SQSFC 17SFC 17
BdMD 4DWORD
Cycle ExecutionOB 1OB 1
messwertED 0DWORD
oberer_GrenzwertMD 3DWORD
oG_alarmM0.0BOOL
TIME_TCKSFC 64SFC 64
uG_alarmM0.1BOOL
unterer_GrenzwertMD 2DWORD
WR_USMSGSFC 52SFC 52
zeitT 1TIMER
ZustandMB 1BYTE
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
FUNCTION_BLOCK FB1    
    (* Konstanten *)
    CONST
    Bd := 100;  
    END_CONST

    (* Eingangsparameter *)
    VAR_INPUT
        messwert: REAL; 
    END_VAR
    
    (* Ausgangsparameter *)
    VAR_OUTPUT
    
    END_VAR

    VAR_IN_OUT
        Zustand: INT:=0;        (* Zustand 
                                       0: Normal, 
                                       1: obere Grenze ueberschritten 
                                       2: untere Grenze unterrschritten
                                       3: Instabiles Verhalten entdeckt *)    
    END_VAR

    VAR
        oG_alarm: BOOL;              // Merker obere Grenze verletzt
        uG_alarm: BOOL;              // Merker untere Grenze verletzt
        unterer_Grenzwert: REAL;     // untere Grenze
        oberer_Grenzwert: REAL;      // obere Grenze
        zeit: S5TIME;        
    END_VAR
    
    BEGIN
        zeit:=S_PEXT (T_NO:=1, S:= [COLOR=#ff0000]M0.0[/COLOR], TV:= t#20s, R:= [COLOR=#ff0000]M0.1[/COLOR], Q:= [COLOR=#ff0000]M0.2[/COLOR]); // [COLOR=#ff0000]wenn Du die nur intern benötigst, dann kannst Du auch STAT-BOOL-Variablen deklarieren[/COLOR]
        
        // Merker zuruecksetzen
        uG_alarm := False ;
        oG_alarm := False;
        
        // Grenzwertnachfuehrung
        IF Messwert < unterer_Grenzwert THEN          
            unterer_Grenzwert := Messwert;                 // Setze neue untere Grenze auf aktuellen Messwert
            oberer_Grenzwert := unterer_Grenzwert + 2*Bd;  // Setze neue obere Grenze ausgehend von der neuen unteren Grenze
            uG_alarm := True;                    
            oG_alarm := False;                  
        ELSIF Messwert > oberer_Grenzwert THEN 
            oberer_Grenzwert := Messwert;                   // Setze neue obere Grenze auf aktuellen Messwert
            unterer_Grenzwert := oberer_Grenzwert - 2*Bd;  // Setze neue untere Grenze ausgehend von der neuen oberen Grenze
            uG_alarm := False;                             
            oG_alarm :=True;                    
        END_IF;
            

        //Zustandsautomat
        CASE Zustand OF
            // Normaler Zustand
            0:  IF oG_alarm = True THEN      // Wenn obere Grenze verletzt wird
                    Zustand := 1;            // Wechsle in Zustand 1
                     
                ELSIF uG_alarm = True THEN   // Wenn untere Grenze verletzt wird dann
                    Zustand := 2;            // Wechsle in Zustand 2
                 [COLOR=#ff0000]   
                ELSIF Zustand = 1 THEN  // wozu? CASE Zustand OF 1, 2, 3?
                    Zustand := 1;            // Du änderst doch nur den Zustand von 0 auf 1 oder 2        
                ELSIF Zustand = 2 THEN  // Zustand 1, 2 oder 3 haben an dieser Stelle keinen Einfluss
                    Zustand := 2;
                ELSIF Zustand = 3 THEN
                    Zustand := 3;         [/COLOR]      
                END_IF;
            ;
            // obere Grenze wurde verletzt
            1 : IF [COLOR=#ff0000]oG_alarm[/COLOR] = True THEN     // Wenn obere Grenze erneut verletzt wird dann // [COLOR=#ff0000]wirst Du nie auf "1" abfragen können, da die CASE-Anweisung
                                                                                                                        // mit Zustand 1 erst beim 2. Zyklus abgefragt wird, jedoch der
                                                                                                                        // Merker zuvor (oben) zurückgesetzt wird[/COLOR]
                    Zustand := 1;           // bleibe in Zustand 1 und
                    [COLOR=#ff0000]M0.0 := False;          // den Merker setzt Du doch nachfolgend, wozu zurücksetzen, das bekommt kein weiterer Code mit!    [/COLOR]
                    M0.0 := True;           // starte Zeit erneut.
                    
                ELSIF uG_alarm = True THEN  // wenn untere Grenze verletzt wird dann
                   [COLOR=#ff0000] M0.0 :=False; [/COLOR]          // starte Zeit und [COLOR=#ff0000]siehe oben[/COLOR]
                    M0.0 :=True;
                    Zustand := 3;           // wechsle in Zustand 3.
                    
                ELSIF M0.2 = True THEN      // Wenn Zeitintervall zur Detektion abgelaufen ist dann
                    [COLOR=#ff0000]M0.1 := True; [/COLOR]          // setze Zeit zurück und [COLOR=#ff0000]siehe oben[/COLOR]
                    M0.1 := False;
                    Zustand := 0;           // wechsle in Zustand 0.
                    
                ELSIF oG_alarm = False AND M0.2 = 0 THEN // Wenn obere Grenze nicht erneut verletzt wird und das Intervall nicht abgelaufen ist dann
                    Zustand := 1;                        // bleibe in Zustand 1.
                ELSE
                    Zustand := 0;           // Sonst gehe zurück zu Zustand 0.                    
                END_IF;
            ;
            // untere Grenze wurde verletzt
            2 : IF uG_alarm = True THEN      // Wenn untere Grenze erneut verletzt wird dann
                    Zustand := 2;            // bleibe in Zustand 2 und
                 [COLOR=#ff0000]   M0.0 := False;     [/COLOR]      // [COLOR=#ff0000]siehe oben[/COLOR]   
                    M0.0 := True;            // starte Zeit erneut.
                    
                ELSIF oG_alarm = True THEN   // Wenn obere Grenze verletzt wird dann
                   [COLOR=#ff0000] M0.0 :=False;   [/COLOR]         // starte Zeit und [COLOR=#ff0000]siehe oben[/COLOR]
                    M0.0 :=True;
                    Zustand := 3;            // wechsle in Zustand 3.
                    
                ELSIF M0.2 = True THEN       // Wenn Zeitintervall zur Detektion abgelaufen ist dann
                [COLOR=#ff0000]    M0.1 := True;     [/COLOR]       // setze Zeit zurück und [COLOR=#ff0000]siehe oben[/COLOR]
                    M0.1 := False;
                    Zustand := 0;            // wechsle in Zustand 0.
                    
                ELSIF uG_alarm = False AND M0.2 = False THEN // Wenn untere Grenze nicht erneut verletzt wird und das Intervall nicht abgelaufen ist dann
                    Zustand := 2;                            // bleibe in Zustand 2. 
                ELSE
                    Zustand := 0;            // Sonst gehe zurück zu Zustand 0. 
                END_IF;
            ;           
            // Instabilität erkannt            
            3 : IF M0.2 = False  THEN
                    Zustand := 3;
                ELSE
               [COLOR=#ff0000]     M0.1 := True;      [/COLOR]     // Setze Zeit zurück und [COLOR=#ff0000]siehe oben[/COLOR]
                    M0.1 := False;
                    Zustand := 0;           // wechsle in Zustand 0       
                END_IF;
            ;
         END_CASE;
END_FUNCTION_BLOCK

Wie rufe ich den FB1 im OB auf, das ich dann quasi den Zustand ausgegeben bekomme?

Die Quelle übersetzen, es wird der FB1 im Bausteinordner generiert und dann im OB1 einfügen.
 
Hallo Michael,

Code:
  [COLOR=#ff0000]              ELSIF Zustand = 1 THEN  // wozu? CASE Zustand OF 1, 2, 3?
                    Zustand := 1;            // Du änderst doch nur den Zustand von 0 auf 1 oder 2        
                ELSIF Zustand = 2 THEN  // Zustand 1, 2 oder 3 haben an dieser Stelle keinen Einfluss
                    Zustand := 2;
                ELSIF Zustand = 3 THEN
                    Zustand := 3;[/COLOR]
Oh ja richtig das hatte ich vergessen rauszunehmen.

Code:
            1 : IF [COLOR=#ff0000]oG_alarm[/COLOR] = True THEN     // Wenn obere Grenze erneut verletzt wird dann 
// [COLOR=#ff0000]wirst Du nie auf "1" abfragen können, da die CASE-Anweisung
// mit Zustand 1 erst  beim 2. Zyklus abgefragt wird, jedoch der
// Merker zuvor (oben)  zurückgesetzt wird[/COLOR]

Im ersten Zyklus wird der obere Grenzwert verletzt, daraufhin setze ich neue Grenzwerte (oben & unten).
Bei der Case Anweisung soll nun Zustand von 0 (muss glaub ich noch als Startwert initialisiert werden) auf 1 gesetzt werden.
Dann ist der 1. Zyklus zu Ende.

Im nächsten Zyklus setze ich erst die Merker für die Grenzwerte zurück, jetzt prüfe ich erneut ob mein Messwert innerhalb der (neuen) Grenzen liegt. Wird der "neue" obere Grenzwert verletzt setze ich den Merker oG_alarm = True. Da Zustand 1 aus dem vorherigen Zyklus noch gesetzt ist gehe ich dorthin und führe die Aktion bei IF oG_alarm = True.

Code:
zeit:=S_PEXT (T_NO:=1, S:= [COLOR=#ff0000]M0.0[/COLOR], TV:= t#20s, R:= [COLOR=#ff0000]M0.1[/COLOR], Q:= [COLOR=#ff0000]M0.2[/COLOR]); 
// [COLOR=#ff0000]wenn Du die nur intern benötigst, dann kannst Du auch STAT-BOOL-Variablen deklarieren[/COLOR]

Also soll ich dann in der Variablendeklaration einfach schreiben:
Code:
S: BOOL;
R: BOOL;
Q: BOOL;

und dann später die entsprechenden Signale setzen und abfragen?

Ich werd mir das mit der Zeit nochmal genau anschauen müssen, hab nämlich auch die falschen Merker genutzt M0.0 und M0.1 sind ja schon für die Alarme der Grenzwerte reserviert.

Grüße Christian
 
Hi Christian,

Code:
        1 : IF [COLOR=#ff0000]oG_alarm[/COLOR] = True THEN     // Wenn obere Grenze erneut verletzt wird dann  // [COLOR=#ff0000]wirst Du nie auf "1" abfragen können, da die CASE-Anweisung // mit Zustand 1 erst  beim 2. Zyklus abgefragt wird, jedoch der // Merker zuvor (oben)  zurückgesetzt wird[/COLOR]

ja stimmt, da hatte ich mich vertan, sorry.

Also soll ich dann in der Variablendeklaration einfach schreiben:
Code:
S: BOOL;
R: BOOL;
Q: BOOL;

Ja z. B. vielleicht mit ner aussagekräftigeren Bezeichnung und wenn Du die Variablen nicht außerhalb des Bausteins brauchst.

Ach ja, wenn Du 'nen IEC-Timer nehmen würdest und alles Parameter über die IN / OUT-Schnittstelle laufen lässt, dann wär der Baustein auch Multi-Instanzfähig.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wo lege ich die Variablen für den IEC-Timer fest? Ich hab das jetzt so gemacht:
Code:
    VAR
        state: INT:=0;               (* Zustand 
                                       0: Normal, 
                                       1: obere Grenze ueberschritten 
                                       2: untere Grenze unterrschritten
                                       3: Instabiles Verhalten entdeckt *)
                                     
        same_low: BOOL;             // Merker obere Grenze verletzt
        same_up: BOOL;              // Merker untere Grenze verletzt
        lower_limit: REAL;          // untere Grenze
        upper_limit: REAL;          // obere Grenze

[COLOR=#ff0000]        zeit: SFB4;                 // IEC Timer TON (Einschaltverzögerung)
        Zeit_Start: BOOL;           // Timer starten
        Zeit_Wert: TIME := t#20s;   // Intervalllänge zur Detektion        
        Zeit_Status: BOOL;          // Wenn Status = True dann Zeit_Wert abgelaufen[/COLOR]
    END_VAR    
    BEGIN
        zeit(IN:= Zeit_Start, PT:= Zeit_Wert, Q:=Zeit_Status );
Oder muss ich die jeweils als VAR_INPUT bzw VAR_OUTPUT deklarieren?


Und die Sache mit dem Merker rücksetzen und setzen brauche ich weil der "TIMER als Einschaltverzögerung" auf eine positive Flanke reagiert um die Zeit zu starten/neuzustarten. Wie z.B. an der Stelle.

Code:
           1 : IF [COLOR=#ff0000]oG_alarm[/COLOR] = True THEN     // Wenn obere Grenze erneut verletzt wird dann 
                    Zustand := 1;           // bleibe in Zustand 1 und                     [COLOR=#ff0000]
                    M0.0 := False;          // den Merker setzt Du doch nachfolgend, wozu zurücksetzen, das bekommt kein weiterer Code mit!    [/COLOR]         
                    M0.0 := True;           // starte Zeit erneut.

Bei Zustand 1 läuft der Timer bereits, (Beim Wechsel von Zustand 0 auf Zustand 1 oder 2 muss ich den Timer starten, das hatte ich vergessen).
Wenn der obere Grenzwert wieder verletzt wird, soll der Timer neustarten. Also muss ich doch den Eingang erst zurücksetzen und dann wieder setzen oder nicht?

Die Sache mit dem Rücksetzen werd ich jetzt einfach realisieren in dem ich den Eingangswert des Timers auf False setze.
 
Oder muss ich die jeweils als VAR_INPUT bzw VAR_OUTPUT deklarieren?

Nein, genau das ist ja das schöne! Der IEC-Timer brauch keine Nummer, sondern läuft als Instanz. Die Steuersignale können wahlweise intern bzw. über IN / OUT deklariert werden, je nach Anwendung.

Die Einschaltverzögerung (der Timer) läuft solange, wie das Eingangssignal anliegt. Fällt das Eingangssignal vorzeitig weg, wird auch der Timer gestoppt. Mit neuem "1" Signal startet der Timer wieder von vorn.

Wenn der obere Grenzwert wieder verletzt wird, soll der Timer neustarten. Also muss ich doch den Eingang erst zurücksetzen und dann wieder setzen oder nicht?

Ja, jedoch läuft das Programm immer zeilenweise von oben nach unten durch und beginnt dann wieder von oben.

Code:
Besispiel := M0.0 // Die Variable "Beispiel" wird nur im 1. Programmzyklus "0" sein, danach ist die Variable M0.0 bereits "1"

M0.0 := False;  // Dieses Zurücksetzen ist nur in dieser Zeile wirksam, da in der Nächsten M0.0 wieder auf "1" gesetzt wird!
M0.0 := True;
 
Neustart des Timers

Hallo Michael,
Ja, jedoch läuft das Programm immer zeilenweise von oben nach unten durch und beginnt dann wieder von oben.

Code:
Besispiel := M0.0 // Die Variable "Beispiel" wird nur im 1. Programmzyklus "0" sein, danach ist die Variable M0.0 bereits "1"

M0.0 := False;  // Dieses Zurücksetzen ist nur in dieser Zeile wirksam, da in der Nächsten M0.0 wieder auf "1" gesetzt wird!
M0.0 := True;

Ja, ok verstehe das Problem.

Ich hab jetzt 2 verschiedene Ansätze um den Timer neuzustarten. Vielleicht klappts ja.

Variante 1: Um den Timer neuzustarten, rufe ich ihn zwischen setzen und rücksetzen des Starteingangs nochmals auf:
Code:
            // obere Grenze wurde verletzt
            1 : IF oG_alarm = True THEN     // Wenn obere Grenze erneut verletzt wird dann 
                    Zustand := 1;           // bleibe in Zustand 1 und
                    Zeit_Start := False;          
                    [COLOR=#ff0000]zeit(IN:= Zeit_Start, PT:= Zeit_Wert);[/COLOR]
                    Zeit_Start := True;     // starte Zeit erneut.
                    
                ELSIF uG_alarm = True THEN  // wenn untere Grenze verletzt wird dann
                    M0.0 :=False;           // starte Zeit erneut und 
                    [COLOR=#ff0000]zeit(IN:= Zeit_Start, PT:= Zeit_Wert);[/COLOR]
                    Zeit_Start :=True;
                    Zustand := 3;           // wechsle in Zustand 3.
                    
                ELSIF Zeit_Status = True THEN      // Wenn Zeitintervall zur Detektion abgelaufen ist dann
                    Zeit_Start := False;           // setze Zeit zurück und 

                    Zustand := 0;                  // wechsle in Zustand 0.
                    
                ELSIF oG_alarm = False AND Zeit_Status = 0 THEN // Wenn obere  Grenze nicht erneut verletzt wird & das Intervall nicht abgelaufen ist dann
                    Zustand := 1;                               // bleibe in Zustand 1.
                ELSE
                    Zustand := 0;                               // Sonst gehe zurück zu Zustand 0.                    
                END_IF;
            ;
            ...

         END_CASE;

Variante 2
Ich lege noch eine Variable für die abgelaufene Zeit (Ausgang ET des Timers) an:
Code:
zeit_abgelaufen: TIME;

und setze wenn ich die Zeit neustarten will den Ausgang auf 0 Sekunden:
Code:
zeit_abgelaufen := T#0s;

Würde der Timer in dem Fall einfach von 0s wieder hochzählen oder bleibt der Zeitwert stehen bis er wieder eine positive Flanke am Eingang bekommt?


Grüße Christian
 
Zuletzt bearbeitet:
Ich kann Deiner Lösung nicht ganz zustimmen,

1. Zeitglieder, auch wenn es so funktioniert, werden in der Regel möglichst immer permanent aufgerufen und nicht in einer Bedingung.

2. Den Ausgang setzen bringt gar nichts, denn dieser wird im nächsten Zyklus wieder mit der bereits gelaufenen Zeit überschrieben. Beachte die Variablendekaration: IN-Parameter können nur Werte einlesen! OUT-P. können nur Werte ausgeben! IN-/OUT-P. können beides, aber Du hast hier eine Systemfunktion, und die hat die Schnittstelle bereits festgelegt!

Bau Dir doch ein oder mehrere Flanken, mit der Du ein Signal setzt bzw. rücksetzt und gib das auf den IN des Timers!
 
Ich kann Deiner Lösung nicht ganz zustimmen,
Natürlich kannst du das nicht ;), hab mich in meinem letzten Post verschrieben. Es muss natürlich heißen "nicht realisierbar". Hatte da gerade nachgelesen wie die Parameter deklariert sind und festgestellt, dass ET eben nicht IN-/OUT-P ist, wie ich angenommen hatte.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Flanken als Eingang des Timers

Hallo Michael,

Bau Dir doch ein oder mehrere Flanken, mit der Du ein Signal setzt bzw. rücksetzt und gib das auf den IN des Timers!
Hab das jetzt mal probiert und mir eine positive und eine negative Flanke erzeugt.

Code:
...
Zeit_Eingang: BOOL := False;
pos_flanke : BOOL := False;
neg_flanke : BOOL := True;
END_VAR

Die Merker würde ich folgendermaßen in den case Anweisungen setzen,

Start (von Zustand 0 --> (1 oder 2) )
Code:
pos_flanke := True;
neg_flanke := False;

Reset (von (1, 2 oder 3 )--> 0)
Code:
pos_flanke := False;
neg_flanke := True;

Hinweis:
Die Bezeichnungen 1a, 2a, 1b & 2b dienen nur der Unterscheidung der Umschaltbedingung da ich doch jeweils 2 Möglichkeiten (Zeit weiterlaufen lassen oder Neustarten bei neuem Grenzwartalarm) habe um im aktuellen Zustand zu bleiben.

Weiterzählen (1 --> 1a oder 2 --> 2a oder 3 --> 3)
Code:
pos_flanke := False;
neg_flanke := False;

Neustart ( (1 oder 2) --> 3 oder 1 --> 1b oder 2 --> 2b)
Code:
pos_flanke := True;
neg_flanke := True;


Den Timer würde ich dann nach meiner Case Anweisung laufen lassen.
Code:
        // Timer bei negativer Flanke am Starteingang zuruecksetzen 
        zeit(IN:= Zeit_Eingang, PT:= Zeit_Wert);  
        Zeit_Eingang := NOT neg_Flanke;
        Zeit_Status := zeit.Q;
        
        // Timer bei positiver Flanke am Starteingang starten oder weiterzählen lassen
        zeit(IN:= Zeit_Eingang, PT:= Zeit_Wert);
        Zeit_Eingang := pos_Flanke                            // bei positiver Flanke starten
                      OR 
                      (NOT pos_Flanke AND NOT neg_Flanke);  // weiterzählen lassen   
        Zeit_Status := zeit.Q;

Ich hoffe das ist so richtig.
 
Mal so als Gedanken-Experiment. Ich starte im Zustand 0 gehe im nächsten Zyklus nach 1 (Start) dann nach 3 (Neustart) bleibe dort weil die Zeit noch nicht abgelaufen ist (weiterzählen) und gehe dann zurück nach 0 (Reset).

Zyklus 1:
In der Case-Anweisung geschieht folgendes:
Ich gehe von Zustand 0 nach 1 und setze die Merker neg_flanke = False und pos_flanke= True und verlasse die Case-Anweisung
Ich rufe den Timer auf. Am Eingang liegt False an (Initialisierung).
Dann lege ich das neue Signal (True = NOT neg_Flanke ) an den Eingang. Der Timer Startet
Jetzt Rufe ich den Timer wieder auf.
Lege ein neues Signal (True: pos_Flanke=True). Der Timer behält sein Eingangssignal und zählt weiter.


Zyklus 2:
In case Anweisung gehe ich von Zustand 1 nach 3 (Timer Neustarten)
dazu setze ich beide Flanken auf True:
jetzt rufe ich wieder den Timer auf (Eingang ist auf True aus vorherigem Zyklus).
ich setze den neuen Eingang auf False (NOT neg_Flanke). Die zeit wird angehalten.
Ich rufe den Timer wieder auf.
Dann lege ich ein neues Signal an den Eingang (True: pos_Flanke = True). Die Zeit startet neu.


Zyklus 3:
In case Anweisung gehe ich von Zustand 3 nach 3 (weiterzählen)
dazu setze ich beide Flanken auf False.
jetzt rufe ich wieder den Timer auf (Eingang ist auf True aus vorherigem Zyklus).
Ich setze den neuen Eingang auf True (NOT neg_Flanke). Die zeit läuft weiter.
Ich rufe den Timer wieder auf.
Dann lege ich ein neues Signal an den Eingang (True: NOT pos_Flanke AND NOT neg_Flanke) ). Die Zeit läuft weiter.


Zyklus 4:
In case Anweisung gehe ich von Zustand 3 nach 0 (Reset) und setze die Merker neg_flanke = True und pos_flanke= False und verlasse die Case-Anweisung
jetzt rufe ich wieder den Timer auf (Eingang ist auf True aus vorherigem Zyklus).
Ich setze den neuen Eingang auf False (NOT neg_Flanke). Die zeit wird zurückgesetzt.
Ich rufe den Timer wieder auf.
Dann lege ich ein neues Signal an den Eingang (False: NOT pos_Flanke AND NOT neg_Flanke = False) ). Die Zeit bleibt bei 0.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Michael,
hab heute endlich PLCSIM bereitgestellt bekommen und das Progamm getestet. Es funktioniert alles wie es soll.

Vielen Dank du hast mir wirklich sehr weitergeholfen.
 
Zurück
Oben