300 Minuten ablaufen lassen und über einen Analogausgang an das Leitsystem übertragen

LukeSkywalker

Level-1
Beiträge
9
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
folgendes Problem (S7-315 + Step7 5.4):

ich möchte zu bestimmten Ereignissen eine Zeit von 300, 120 oder 10 Minuten ablaufen lassen (immer nur eine zur Zeit) und diese dann über einen Analogausgang an das Leitsystem zur Anzeige senden (300 Minuten = 20 mA und 0 Minuten = 4 mA).
Hatte schon eine Lösung mit S5TIME-Format an S_EVERZ und über meinen Skalierbaustein auf das PAW (INT), habe aber leider nicht daran gedacht, dass S5TIME nur 2h46min kann.
Evtl. könnte man zwei S_EVERZ kaskadieren, nur wird die aktuelle Zeit nur an den Ausgängen "Dual" angezeigt, wenn auch der Eingang gesetzt ist und die Zeit abläuft (sonst hätte man evtl. die beiden Timerwerte in ein Doppelwort schreiben können, aber da das eine Ausgangswort "Dual" erst den parametrieten Wert ausgibt wenn der Setzeingang true ist geht das so auch nicht).

Wäre nett wenn mir jemand genau sagen könnte wie es so zu programmieren geht (Umwandlungen, in was für ein Wort/Doppelwort mit welchem Typ schreibe ich den Wert, Bytedreher?, ...) oder das Beste gibt es schon fertige Bausteine dafür oder gibt es noch eine ganz andere Lösung?

Gruss und vielen Dank
LukeSkywalker
 
Schau dir mal SFB4/5 (TON/TOF) aus der Standard-Library an, die können länger. ;)

Noch ein Link mit Beispiel und falls du das mit PCLSIM testen willst, bitte alles lesen.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
als quelle
Code:
ORGANIZATION_BLOCK OB 1
TITLE = "Main Program Sweep (Cycle)"
VERSION : 0.1

VAR_TEMP
  OB1_EV_CLASS : BYTE ; //Bits 0-3 = 1 (Coming event), Bits 4-7 = 1 (Event class 1)
  OB1_SCAN_1 : BYTE ; //1 (Cold restart scan 1 of OB 1), 3 (Scan 2-n of OB 1)
  OB1_PRIORITY : BYTE ; //Priority of OB Execution
  OB1_OB_NUMBR : BYTE ; //1 (Organization block 1, OB1)
  OB1_RESERVED_1 : BYTE ; //Reserved for system
  OB1_RESERVED_2 : BYTE ; //Reserved for system
  OB1_PREV_CYCLE : INT ; //Cycle time of previous OB1 scan (milliseconds)
  OB1_MIN_CYCLE : INT ; //Minimum cycle time of OB1 (milliseconds)
  OB1_MAX_CYCLE : INT ; //Maximum cycle time of OB1 (milliseconds)
  OB1_DATE_TIME : DATE_AND_TIME ; //Date and time OB1 started
END_VAR
BEGIN
NETWORK
TITLE =Sägezahngenerator an Analogausgabe
      L     #OB1_PREV_CYCLE; 
      L     "Zeitzaehler"; // Doppelwort
      +D    ; 
      T     "Zeitzaehler"; 
      L     L#18000000; // 300 Minuten in Millisekunden
      <=D   ; 
      SPB   m001; 
      L     0; // Zähler neu starten, wenn Ende erreicht
      T     "Zeitzaehler"; 
m001: NOP   0; 
      L     "Zeitzaehler"; // skalieren für Ausgabe
      SSD   10; // mache aus 18000000 (DINT) 17578 (INT)
      L     25770; // mache aus 17578 27648
      *I    ; 
      SSD   14; 
      T     "Analogausgabe"; 
END_ORGANIZATION_BLOCK
oder nur code:
Code:
      L     #OB1_PREV_CYCLE
      L     "Zeitzaehler"               // Doppelwort
      +D    
      T     "Zeitzaehler"
      L     L#18000000                  // 300 Minuten in Millisekunden
      <=D   
      SPB   m001
      L     0                           // Zähler neu starten, wenn Ende erreicht
      T     "Zeitzaehler"
m001: NOP   0
      L     "Zeitzaehler"               // skalieren für Ausgabe
      SSD   10                          // mache aus 18000000 (DINT) 17578 (INT)
      L     25770                       // mache aus 17578 27648
      *I    
      SSD   14
      T     "Analogausgabe"
 
Berechnung

Vielen Dank! Hat funktioniert, aber verrate mir noch bitte mal wie du auf den Wert 25770 gekommen bist?

Gruss LukeSkywalker



als quelle
Code:
ORGANIZATION_BLOCK OB 1
TITLE = "Main Program Sweep (Cycle)"
VERSION : 0.1
 
VAR_TEMP
  OB1_EV_CLASS : BYTE ; //Bits 0-3 = 1 (Coming event), Bits 4-7 = 1 (Event class 1)
  OB1_SCAN_1 : BYTE ; //1 (Cold restart scan 1 of OB 1), 3 (Scan 2-n of OB 1)
  OB1_PRIORITY : BYTE ; //Priority of OB Execution
  OB1_OB_NUMBR : BYTE ; //1 (Organization block 1, OB1)
  OB1_RESERVED_1 : BYTE ; //Reserved for system
  OB1_RESERVED_2 : BYTE ; //Reserved for system
  OB1_PREV_CYCLE : INT ; //Cycle time of previous OB1 scan (milliseconds)
  OB1_MIN_CYCLE : INT ; //Minimum cycle time of OB1 (milliseconds)
  OB1_MAX_CYCLE : INT ; //Maximum cycle time of OB1 (milliseconds)
  OB1_DATE_TIME : DATE_AND_TIME ; //Date and time OB1 started
END_VAR
BEGIN
NETWORK
TITLE =Sägezahngenerator an Analogausgabe
      L     #OB1_PREV_CYCLE; 
      L     "Zeitzaehler"; // Doppelwort
      +D    ; 
      T     "Zeitzaehler"; 
      L     L#18000000; // 300 Minuten in Millisekunden
      <=D   ; 
      SPB   m001; 
      L     0; // Zähler neu starten, wenn Ende erreicht
      T     "Zeitzaehler"; 
m001: NOP   0; 
      L     "Zeitzaehler"; // skalieren für Ausgabe
      SSD   10; // mache aus 18000000 (DINT) 17578 (INT)
      L     25770; // mache aus 17578 27648
      *I    ; 
      SSD   14; 
      T     "Analogausgabe"; 
END_ORGANIZATION_BLOCK
oder nur code:
Code:
      L     #OB1_PREV_CYCLE
      L     "Zeitzaehler"               // Doppelwort
      +D    
      T     "Zeitzaehler"
      L     L#18000000                  // 300 Minuten in Millisekunden
      <=D   
      SPB   m001
      L     0                           // Zähler neu starten, wenn Ende erreicht
      T     "Zeitzaehler"
m001: NOP   0
      L     "Zeitzaehler"               // skalieren für Ausgabe
      SSD   10                          // mache aus 18000000 (DINT) 17578 (INT)
      L     25770                       // mache aus 17578 27648
      *I    
      SSD   14
      T     "Analogausgabe"
 
18000000 entsprechen 27648 (20 mA)
Also muss du deinen aktuellen Wert umskalieren auf 4-20mA (0-27648 )

18000000 / 2^10 * 25770 / 2^14 ergeben ca. 27648

Das hätte man auch anders rechnen können, aber er hat es so gemacht
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Wie 25770 berechnet?

Vielen Dank, aber das hatte ich bis dahin schon verstanden.
Meine Frage ist, wie man den Wert 25770 berechnet?

Gruß LukeSkywalker





18000000 entsprechen 27648 (20 mA)
Also muss du deinen aktuellen Wert umskalieren auf 4-20mA (0-27648 )

18000000 / 2^10 * 25770 / 2^14 ergeben ca. 27648

Das hätte man auch anders rechnen können, aber er hat es so gemacht
 
18000000 entsprechen 27648 (20 mA)
Also muss du deinen aktuellen Wert umskalieren auf 4-20mA (0-27648 )

18000000 / 2^10 * 25770 / 2^14 ergeben ca. 27648

Das hätte man auch anders rechnen können, aber er hat es so gemacht

Durch Umstellen dieser Formel, da 27648 ja bekannt ist. Da kommt dann 25770 heraus.
 
Ein aufmerksamer Leser hat mir gerade mitgeteilt, das du vielleicht wissen wieso das 2^10 und 2^14 ist.

SSD 10 heisst: schiebe vorzeichenrichtig 10 Stellen nach rechts.
Eine Stelle nach rechts schieben, heisst teilen durch 2 (2^1)
zwei Stellen nach rechts schieben, heisst teilen durch 4 (2^2)
drei Stellen nach rechts schieben, heisst teilen durch 8 (2^3)

-> SSD 10 heisst teilen durch 2^10

nach links schieben ist dann mutliplizieren
 
Das ist schon etwas tricky, denke mal es stammt noch aus alten Zeiten. ;)
Da hat einer halt entweder nicht mit Real rechnen wollen oder können (Code stammte evtl. mal aus einer S5) und wollte wenig Zykluszeit verbrauchen. Bei den alten S5 kosteten normale Rechenoperationen wirklich viel Zykluszeit. Die schnellste Rechenoperation war *2 und /2 weil das ein einfacher Schiebebefehl ist. Nun hat er überlegt, wie man das am Besten machen kann und am Genausten. Also möglichst nur einmal Multiplizieren, da kommt man nicht drumherum und ansonsten Schiebeoperationen.

Los gehts.

die normale Formel:

18000000 * y = 27648

Formel um Faktor x erweitert:

18000000 / x * y = 27648 / x

Nun hat er erstmal 18000000 solange durch 2 geteilt, das ein hinreichend kleiner Wert zum Rechnen herauskam, dieser aber noch nicht zu ungenau war.

18000000 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 = 17578,125 in Int also 17578
18000000 / 2^10 = 17578

Formel nun:

18000000 / x * y = 27648 / x mit x=2^10

berechnen

17578 * y = 27648 / 2^10

umstellen nach y:

y = 27648 / 17578 / 2^10

y = 1,57287518..... / 2^10

aber 1,57287518..... ist keine gute Zahl, schon gar keine Int

Formel wieder erweitern:

y * z = 1,57287518..... * z / 2^10

für z nun soviele 2-er-Potenzen, bis statt 1,57287518..... ein ordentlicher Int-Wert entsteht

nehmen wir doch mal 2^14 für z

v = 1,57287518..... * z
v = 1,57287518..... * 2^14 = 25769,987029241096825577426328365
= 25770

so nun noch einsetzen:

y * z = 1,57287518..... * z / 2^10

y * 2^14 = 25770 / 2^10

y = 25770 / 2^10 / 2^14

Jetzt wieder die Startformel hernehmen:

18000000 * y = 27648

einsetzen und prüfen.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
2 hoch 10 + 2 hoch 14

Danke, aber das ist mir wohl alles sehr bewusst, nur
woher weiß ich, dass ich durch 10 und 14 teilen muss?
wie komme ich auf die werte 10 und 14?

Gruß LukeSkywalker




Ein aufmerksamer Leser hat mir gerade mitgeteilt, das du vielleicht wissen wieso das 2^10 und 2^14 ist.

SSD 10 heisst: schiebe vorzeichenrichtig 10 Stellen nach rechts.
Eine Stelle nach rechts schieben, heisst teilen durch 2 (2^1)
zwei Stellen nach rechts schieben, heisst teilen durch 4 (2^2)
drei Stellen nach rechts schieben, heisst teilen durch 8 (2^3)

-> SSD 10 heisst teilen durch 2^10

nach links schieben ist dann mutliplizieren
 
@luke

Siehe Rechnung oben, welchen Wert du genau nimmst, bleibt deiner Intuition überlassen. Es soll halt immer hinreichend genau, aber rechentechnisch möglichst schnell und einfach gehen.
 
Zuletzt bearbeitet:
Huch, was habe ich getan?

@Ralle: Danke! Sehr gut erklärt! (spart mir jetzt diese ellenlange Erklärung:ROFLMAO: )

Ja, ich gehöre tatsächlich zu denen, die aus kleinen Prozessoren gerne viel rauskitzeln. Diese Rechnerei hab ich übrigens bei der S5 CPU945 gelernt. Die konnte nämlich keine Doubleintegerdivision - aber dafür zwei Integer zu einem Doubleinteger zusammenmultiplizieren. Und Real-Arithmetik, da werden auch heutige CPUs noch zu lahmen Heizlüftern;)

Hab anlässlich der Diskussion mal wieder die Operationslisten rausgekramt und nachgelesen. Dabei stellte ich fest: je nach CPU variiert die Programmformulierung mit der geringsten Abarbeitungszeit.

Im vorliegenden Fall wäre folgende Formulierung recht brauchbar:
Code:
L     "Zeitzaehler"
L 651                          // 18000000/27648=651,0417
/D
T     "Analogausgabe"
Wenn man aber in Betracht zieht, dass 651 prinzipiell nur eine Genauigkeit von 9,3 Bits aufweist, wäre auch hier über eine Anpassung der Dimensionen für die Berechnung nachzudenken.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
...
Hab anlässlich der Diskussion mal wieder die Operationslisten rausgekramt und nachgelesen. Dabei stellte ich fest: je nach CPU variiert die Programmformulierung mit der geringsten Abarbeitungszeit.
...
... dabei stellte ich nun fest:
SVD ist bei der CPU945 ein sehr billiger Befehl (was Laufzeit anbetrifft). Ebenso SSD bei CPU 318. Aber bei CPU 317 ist der Befehl vergleichsweise teuer (wie auch bei anderen 31x-CPUs). Besonders erstaunt hat mich, dass die Doubleintegerdivision bei den kleineren 31x teilweise (je nach Operationsliste aus den Jahren 1995 bis heute), also insbesondere in der jüngsten mir vorliegenden, billiger ist, als die DINT-Multiplikation. Aber *I ist immer noch schneller als /D:D

Ich stelle mal die Codesequenzen
Code:
SSD n
L k16
*I
SSD n
und
Code:
SLD n
L k16
/D
gegenüber (die Zeit für die Ladeoperation der Konstanten lass ich weg). Zeiten in µs:

945: 0,55 / DINT-Division geht nicht
312/13: 13,4 / 24,6
314: 11,1 / 18,2
315: 8,8 / 16,2
314IFM: 7,9 / 9,4
318: 1,0 / 1,4
312C: 6,3 / 7,3
31xC: 3,4 / 4,9
312: 7,2 / 8,2 (neueste mir vorliegende OpLi)
31x/147/151: 3,7 / 4,1
317: 0,78 / 0,69

Fazit: je moderner die CPUs, desto geringer wird der Vorteil der Integer-Multiplikation. Mutmaßliche Ursachen: RISC-CPUs mit nur 16Bit? Operationen werden mit langen Codesequenzen nachgestellt?

EDIT: oder 32-Bit CPUs, die für 16Bit-Operationen nicht mehr gebaut sind und daher die 16-Bit-Operationen per Software nachgebildet werden?
 
Zuletzt bearbeitet:
Zurück
Oben