Frage zu TON Timern in Codesys

Zuviel Werbung?
-> Hier kostenlos registrieren
Was für eine andere Task? Läuft die frei oder auch in einem festen Zeitraster? Und was genau machst Du in der einen und was in der anderen Task?
Poste doch mal Deine Taskeinstellungen und den kompletten aktuellen Code.
 
Ich verstehe auch noch nicht, warum die Variable nur alle 100ms aktualisiert werden soll. Wenn der Timer weggelassen wird, ergibt sich ein maximaler Fehler von einer halben Zykluszeit, das Ergebnis wird dadurch aber weniger stufig.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Gilt das auch für alle anderen Variablen?
FlankenErkennungen nicht bedingt aufzurufen, sondern zyklisch, gilt eigentlich immer, so wie es weiter oben schon für den Aufruf von Timern erklärt worden war.
"Eigentlich" soll dabei bedeuten: Man ist damit auf der sicheren Seite, um auch kurze Impulse bzw. kurze ImpulsPausen nicht zu übersehen.
Ausnahmen von dieser Regel mögen zwar hin und wieder zweckmässig sein, müssen aber gründlich überlegt werden.

Das stroboskopartige Kopieren der Eingänge in ihr ProzessAbbild und das Kopieren des AusgangsProzessAbbildes auf die Ausgänge "zwischen" zwei Zyklen (also nach dem Durchlaufen eines Zyklus und vor dem nächsten ZyklusDurchlauf) bewirkt eine MomentAufnahme der Zustände in einem bestimmten ZeitRaster und sorgt für "klare Verhältnisse" und ein wenig Entprellung, aber auch dafür, dass Änderungen erst einige MilliSekunden später erkannt werden können, als es auf anderem Wege möglich wäre.
Das dürfte in den meisten Fällen genau das sein, was man haben will. Wenig Aufwand, weil das BetriebsSystem sich um die ProzessAbbilder kümmert und eine ausreichende Entprellung ausser in extrem hartnäckigen Fällen.

Ob dies, was hier speziell für die Eingänge gesagt wurde, auch für alle anderen ("die ganz normalen") Variablen gilt?
So richtig bedenkenlos vergleichbar sind die Variablen ja nicht mit Eingängen, die die Zustände von prellenden Relais-, Taster oder Schalter-Kontakten entsprechen.
Aber, wenn die Variablen ebenfalls nur in einem ZeitRaster ihren Zustand ändern können, sind gewisse Ähnlichkeiten auch nicht von der Hand zu weisen.
Aber ist das überhaupt relevant? Wofür?

Zyklus 2:
High-Signal an Variable 1 durch externen Tastendruck oder internes ändern des Bool Werts
Aufruf der Funktion R_TRIG
Vergleich mit aktuellen Variablen mit Abbild aus vor Zustand --> Flankenerkennung: TRUE

Wäre das so in etwa der Prozess?

Dann verstehe ich, dass wenn die Zeit zwischen zwei Tasks so groß ist, dass in dieser Zeit eine Variable den Zustand von LOW auf HIGH und wieder LOW wechselt, die SPS das überhaupt nicht merkt.

Ist das soweit korrekt?
Der umgekehrte Fall, dass ein Signal von Hi auf Lo wechselt und nach sehr kurzer Zeit wieder nach Hi zurück, ist genauso relevant.
Denn eine positivie Flanke kann nur erkannt werden, wenn zuvor der Lo-Zustand des Signals zuverlässig erkannt wurde und eine negative Flanke nur, wenn zuvor der Hi-Zustand des Signals zuverlässig zur Kenntnis genommen wurde.
Wenn ein (zu) kurzer Implus oder eine (zu) kurze ImpulsPause vom Programm nicht erkannt werden kann, gerät die FlankenErkennung "aus dem Trott".
Für die Funktion von Timern ist die Erkennung der Flanken von IN ganz entscheidend:
- die EinschaltVerzögerung (TON) startet die ZeitMessung mit der positiven Flanke und
- die AusschaltVerzögerung (TOF) startet die ZeitMessung mit der negativen Flanke von IN.

Übrigens:
Von der FlankenErkennung erwartet man, dass das Ergebnis genau 1 Zyklus lang auf TRUE geht. D.h. aber auch, dass die nächste Flanke frühestens im übernächsten Zyklus erkannt/gemeldet werden kann. Das bedeutet aber auch, dass eine Flanke, die in einer Task erkannt wird, nicht sinnvoll in einer anderen Task (z.B. mit kürzerer oder längerer ZyklusZeit) ausgewertet werden kann.
Nein, es ist kein Allheilmittel, die zu bearbeitenden Aufgaben auf möglichst viele, von einander (scheinbar) unabhängige Task zu verteilen, denn die einzelnen Zweige müssen i.A. wieder "mühsam" zusammengeführt werden.
Die seit zig Jahren gängige Praxis, möglichst viele oder besser alle Aufgaben in einem einzigen TaktZyklus (oft "OB1-Zyklus" genannt) abzuhandeln, ist lange nicht so kontraproduktiv und auch nicht so unübersichtlich, wie ihr oft nachgesagt wird.

FlankenErkennungen werden oft benutzt, um Impulse eines EingangsSignals zu zählen. Das bisher Gesagte gilt für Zähler selbstverständlich auch.
Manchmal genügt es aber nicht, nur in jedem zweiten Zyklus weiterzählen zu können. Manchmal hat man Fälle, in denen in jedem Zyklus weitergezählt werden muss oder sogar mehrfach pro Zyklus.
Das geht auch, indem man das IN-Signal manipuliert. Jedesmal, wenn gezählt werden soll, den Zähler zweimal aufrufen: einmal mit IN := FALSE und einmal mit IN := TRUE.

Wenn es sehr genau sein muss, funktioniert das mit den Timern nicht.
Stimmt. Aber was heisst "sehr genau"? Wo soll sich die sehr genaue Zeit auswirken?
An einem Ausgang der SPS?
Im Programm in einer bestimmten (anderen?) Task?
Wir haben es hier mit der digitalen Verarbeitung von irgendwas zu tun.
Digital ist aber nicht automatisch genauer als analog.
Zählen gelingt mit FestPunktZahlen sehr genau.
Das Digitalisieren von analogen Grössen verschlechtert aber die Genauigkeit, insofern Stufen in das Signal eingebaut werden, die im analogen Original nicht vorhanden sind.
 
Zuletzt bearbeitet:
Ich wollte das jetzt "einfach" mal ausprobieren, ob es möglich ist, derartig genau zu arbeiten. Aber ich verstehe, dass es in der Praxis nicht wichtig ist.
 
Es kommt nicht nur darauf an, ob man zeitlich sehr genau eine Signalfolge in einer Variable erzeugen kann, sondern auch noch, was man damit machen will. Falls man einen Hardware-Ausgang schalten will, dann kommt noch dazu, wann der Schaltbefehl am Hardware-Ausgang ankommt. Wenn die Aktualisierung des Hardware-Ausgangs nur zyklisch passiert, dann hat man wieder die Ungenauigkeit von ca. einem halben Zyklus. Man müsste dann schon direkt auf den Peripherie-Ausgang bzw. die Ausgangs-Klemme zugreifen (ich weiß nicht ob und wie das in Codesys geht). Möglicherweise ist das auch ein dezentraler Feldbus-Ausgang und man kann nicht beeinflussen, wann der Bus-Master mit dem Bus-Gerät spricht - da kommt dann auch wieder eine schwankende Ungenauigkeit/Jitter hinzu, dadurch daß der Bus-IO-Zyklus azyklisch zum Programmzyklus läuft. Wenn man es wirklich unbedingt sehr genau braucht, dann gibt es durchaus aufwendige Lösungen mit und ohne Hardware-Unterstützung. In der normalen Praxis braucht man das aber meistens nicht so genau. Theoretisch kann man z.B. bei Siemens S7-1200/1500 sehr aufwendig auf 10µs = 0.01ms genau normale Digitalausgänge schalten - das braucht aber kein übliches Programm.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was für eine andere Task? Läuft die frei oder auch in einem festen Zeitraster? Und was genau machst Du in der einen und was in der anderen Task?
Poste doch mal Deine Taskeinstellungen und den kompletten aktuellen Code.

So hatte ich mir das vorgestellt mit einem separaten Task, der den Wert erhöht:
Task 1: Main-Programm (Alle 20 ms Zyklus)
Task 2: Programm zum Aktualisieren des Werts (Alle 100 ms Zyklus)



#############################################
Enum (E_DoorState):
#############################################

TYPE E_DoorState:
(
default:= 0,
closing,
opening
);
END_TYPE


#############################################
Globale Variablenliste (GVL):
#############################################

VAR_GLOBAL

value : REAL := 0.0;
state : E_DoorState := E_DoorState.default;
tDurX : TIME := T#100MS;
tDurY : TIME := T#5S+tDurX;
CNT : INT := 0;

END_VAR


#############################################
Funktion für Main-Task: (PLC.PRG): <Deklarationsteil>
#############################################

PROGRAM PLC_PRG

VAR_INPUT
bDriveRightMotor, bDriveLeftMotor : BOOL;
END_VAR

VAR_OUTPUT
END_VAR

VAR
timerMotorSimulation : TON;
timerIsExpired : BOOL;
triggerDetectionRightMotor, triggerDetectionLeftMotor : R_TRIG;
END_VAR


#############################################
Funktion für Main-Task: (PLC.PRG): <Anweisungsteil>
#############################################


triggerDetectionRightMotor(CLK := bDriveRightMotor);
triggerDetectionLeftMotor(CLK := bDriveLeftMotor);

IF triggerDetectionRightMotor.Q OR triggerDetectionLeftMotor.Q THEN

GVL.CNT := 0;

END_IF

IF NOT timerIsExpired THEN

timerMotorSimulation(IN := TRUE, PT := GVL.tDurY);

END_IF


IF bDriveRightMotor THEN

GVL.state := E_DoorState.closing;

IF timerMotorSimulation.Q THEN

timerMotorSimulation(IN := FALSE);
GVL.state := E_DoorState.default;
timerIsExpired := TRUE;

END_IF

END_IF


IF bDriveLeftMotor THEN

GVL.state := E_DoorState.opening;

IF timerMotorSimulation.Q THEN

timerMotorSimulation(IN := FALSE);
GVL.state := E_DoorState.default;
timerIsExpired := TRUE;

END_IF

END_IF



#############################################
Funktion für Main-Task: (PLC.PRG): <Deklarationsteil>
#############################################

PROGRAM Task_ZeitgenaueWertAnpassung

VAR
valueOld : REAL := 0;
END_VAR


#############################################
Funktion für Main-Task: (PLC.PRG): <Anweisungsteil>
#############################################



IF GVL.state = E_DoorState.closing THEN

IF (GVL.value + 100 * (TIME_TO_REAL(GVL.tDurX)/(TIME_TO_REAL(GVL.tDurY-GVL.tDurX)))) <= 100 THEN

GVL.value := GVL.value + 100 * (TIME_TO_REAL(GVL.tDurX)/(TIME_TO_REAL(GVL.tDurY-GVL.tDurX)));

ELSE

GVL.value := 100;

END_IF



END_IF

IF GVL.state = E_DoorState.opening THEN

IF (GVL.value - 100 * (TIME_TO_REAL(GVL.tDurX)/(TIME_TO_REAL(GVL.tDurY-GVL.tDurX)))) >= 0 THEN

GVL.value := GVL.value - 100 * (TIME_TO_REAL(GVL.tDurX)/(TIME_TO_REAL(GVL.tDurY-GVL.tDurX)));

ELSE

GVL.value := 0;

END_IF

END_IF

IF valueOld <> GVL.value THEN

GVL.CNT := GVL.CNT + 1;

END_IF


valueOld := GVL.value;


Ich glaube das Thema ist für mich jetzt abgeschlossen. Wenn kein besonderer Anwendungsfall besteht, ist die Genauigkeit des TON Timers ausreichend.
 
Guten Tag zusammen.

Ich würde das Thema gern aufwärmen, da ich gerade ein Problem mit 2 Timern habe.

Hardware ist die TBEN-L5-PLC-11 von TURCK mit Codesys 3.5 SP 16 Patch 3

Es ist mein erstes Projekt mit Codesys sowie mit ST.

Da ich mehrere Funksensoren durch die wechselnden Werte der Temperatur auf Ausfall überprüfe, soll die Zeit für den Timer von Außen als STRING kommen. Dies scheint aber wie man sieht nicht zu funktionieren.

Ebenfalls ist mir nicht klar, weshalb zB. die "Var_01" False ist, der "IN" des Timers aber True.


1725019186949.png

Aufruf des FB:
1725019220756.png

Vielen Dank für die Unterstützung.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ton_01_ST wird mit einer Bedingung ausgeführt.. die immer gesetzt wird wenn deine Bedingung true ist und dann immer gelöscht wird also ist Var_01 immer false. Der FB-Eingang wird aber nur bearbeitet wenn sie True ist, ergo ist der Eingang des FBs wenn er ausgeführt wird immer true.

Prinzipiell wenn du Messungen machen willst sollte das, was Zeit misst natürlich dauernd laufen, dein Timer ist aber außerhalb der Bedingung nicht aktiv. Außerdem wenn du Zeiten messen willst solltest du vielleicht time() benutzen.
 
Die Timer mußt Du immer aufrufen, so wie Du das schreibst, bekommen die ja gar keinen Zustandswechsel mit.
Die solltest Du hinter den IFs aufrufen.
Funktioniert der STRING_TO_TIME? Falls nicht, schreibe nicht "Time#1h", sondern "T#1h".
 
Moin und danke für die Tipps.

Wie im angehängten Bild zu sehen, habe ich eure Ratschläge befolgt - leider jedoch keine Änderung festgestellt - der Timer ist immer noch eingefroren.

Auch mit umgekehrten TRUE/FALSE ist es das gleiche Ergebnis.
 

Anhänge

  • 1726143098772.png
    1726143098772.png
    25,9 KB · Aufrufe: 7
  • 1726143263430.png
    1726143263430.png
    26,8 KB · Aufrufe: 6
Zuviel Werbung?
-> Hier kostenlos registrieren
Verstehe ich nicht, was Du mit eingefroren meinst.... der Ausgang TON.Q ist im ersten Bild aktiv, im zweiten nicht, genauso TOF.Q.
Also der TON und der TOF schalten beide.

Schreibst Du eigentlich IN := Var_01=1 ?
Das ist unschön und nicht notwendig.
Erstmal ist VAR_01 vermutlich eine boolesche Variable, da solltest Du mit TRUE und FALSE arbeiten.
Zum anderen ist IN auch ein boolescher Eingang, da mußt Du keinen Vergleich hinter schreiben, weil Du Var_01 direkt zuweisen kannst.

Im TOF stehen in beiden Screenshots 67ms. Funktioniert dort Deine String-Konvertierung? Wie schon geschrieben: Als String T#1h übergeben und nicht Time#1h.

Erkläre uns bitte einmal, was Du mit "eingefroren" meinst.
 
Sobald ich das Programm einspiele und die "In" Variable auf TRUE ist, sollten doch die 3 Minuten anfangen zu laufen oder seh ich das falsch?
Das im Statusfeld die 3 Minuten fest stehen, sehe ich als "einfrieren" an.


In einem FUP-Netzwerk funktioniert es doch auch - weshalb also nicht im ST?

Edit: ich seh grad - es ist PT und ich ET - wie kann ich mir ET anzeigen lassen?
 

Anhänge

  • 1726144416658.png
    1726144416658.png
    9,8 KB · Aufrufe: 4
  • 1726144540596.png
    1726144540596.png
    20,7 KB · Aufrufe: 4
PT ist der Eingang, an dem Du vorgibst, wie lange die Zeit laufen soll. Das ist ein fester Parameter, natürlich ist der Wert konstant, sofern Du den nicht mit neuen Werten versorgst.
Wenn Du eine Zeit ablaufen sehen willst, mußt Du Dir den Ausgang ET (Elapsed Time) ansehen.
 
Zurück
Oben