TIA PLC-Datentypen (UDT) als Aktualparameter IN_OUT einem FB übergeben

MrSpexx

Level-1
Beiträge
34
Reaktionspunkte
6
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Zusammen,

ist es in TIA möglich einen UDT als Aktualparamter einem FB/FC zu übergeben und diesen UDT sowohl zu lesen und zu beschreiben?

In Step7 war das bei großen oder häufig verwendeten Variablen der reinste Zykluszeitkiller.

Hat jemand von Euch schon Erfahrung damit gesammelt?
 
Hi,

Du kannst einem Baustein eine Variable als PLC-Datentyp übergeben.
Lesen und Schreiben --> InOut...
kein Zykluszeitkiller, da bei InOut nur die Referenz übergeben wird.
Ich hoffe, deine Frage damit beantwortet zu haben
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Doch "Zykluszeitkiller": Gerade deshalb weil nur eine Referenz übergeben wird, muß zur Laufzeit bei jedem Zugriff auf den UDT erst aufwändige Adressberechnung ausgeführt werden.
Mögliche Abhilfe: zu Beginn des FB den gesamten UDT in TEMP kopieren und am Ende des FB wieder zurück.

Harald
 
Doch "Zykluszeitkiller": Gerade deshalb weil nur eine Referenz übergeben wird, muß zur Laufzeit bei jedem Zugriff auf den UDT erst aufwändige Adressberechnung ausgeführt werden.
Mögliche Abhilfe: zu Beginn des FB den gesamten UDT in TEMP kopieren und am Ende des FB wieder zurück.

Harald

Genau. Eine einfache Abfrage wird mit der ganzen Adressberechnung des UDT ein vielfaches der Befehlszeit kosten.

Ich habe noch keine TIA Steuerung gehabt. Sind die Dinger so schnell und ist die Zykluszeit noch ein Thema?
Werden die Adressen durch optimierte DB anders berechnet?
 
Zuletzt bearbeitet:
kann das nicht überprüfen, bin mir aber sicher, dass das kein Zyklusfresser ist. Zudem sollte die Schnittstelle InOut verwendet werden, um unnötiges Kopieren in den Baustein zu vermeiden und um Speicher zu sparen.

Werden die Adressen durch optimierte DB anders berechnet?

Du siehst in optimierten Bausteinen keine Adressen! TIA sortiert für sich intern die Variablen im Baustein der Größe nach.
Und für die S7-1500 erfolgt der Zugriff in 4Byte in der Little Endian Reihenfolge...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi

Thema Zeitfresser ... mein Lieblingsthema

Das TIA-Portal kann auch mit den alten 300/400 und da bleibt auch alles beim Alten. Strukturen im InOut können nur recht zäh zugegriffen werden.
Das TIA-Portal muss für die neuen 1200/1500 verwendet werden. Und im Bereich UDT ist alles anders. Strukturen im InOut werden wie shutdown_TIA12 sagt als Referenz übergeben und können ohne irgendwelche Akrobatik schneller als jemals zuvor zugegriffen werden. Da steckt sogar eine 1214 ein 416 in die Tasche.

ABER

was die 1500 und vermutlich auch die 1200 gar nicht mag, ist wenn du optimierte DB mit nicht optimierten Bausteinen mischt. Das wird dann wieder so zäh, also ob du so wie PN/DP vorschlägst eine Kopie im TEMP anlegst. Meine 1516 ist dann aber immer noch schneller als meine 317. Trotzdem immer alles auf optimiert lassen -- sowohl Code als auch Datenbaustein -- dann sind die neuen CPU Generationen deutlich schneller als die alten.

Wer jedoch mit irgendwelchen Fremdgeräten TSEND/TRCV machen muss, der sollte die dann wahrscheinlich notwendigen nicht optimierten Bausteine für die Kommunikation bündeln und auch nur dann aufrufen, wenn neuen Daten da sind und darin möglichst nur einmal die Daten von nicht optimiert auf optimiert rangieren.


'n schön' Tach auch
HB
 
Egal wie optimiert die Parameterübergabe und Struktur-Adressberechnung auch sein mag, ein Zugriff auf einen per Referenz übergebenen Parameter wird immer mindestens 1 Speicherzugriff mehr benötigen als ein als Wert übergebener Parameter.

Harald
 
Egal wie optimiert die Parameterübergabe und Struktur-Adressberechnung auch sein mag, ein Zugriff auf einen per Referenz übergebenen Parameter wird immer mindestens 1 Speicherzugriff mehr benötigen als ein als Wert übergebener Parameter.

Bei einem InOut muss aber der Parameter erst auf die bausteininterne Variable (bzw. Parameterstack oder wie man das auch nennen will) umkopiert, und nachher wieder zurückgeschrieben werden. So pauschal kann man also nicht sagen welche Variante von beiden schneller, speichersparender oder was auch immer ist.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi

wir haben leider keine Ahnung wie groß eine Referenz bei der 1200/1500 ist. Bei der 300/400 war das entweder 6 (pointer) oder 10 (any) Bytes. Bei den neuen bekommt man leider keine, nicht mal indirekte - über den Platz im DB, Hinweise darauf wie viele Bytes das kostet. Der DB tut so als würden die Referenzen gar nix kosten. Das glaub ich nicht, denn S. muss ja immer noch das nehmen, was MIPS, Intel, ARM, Renesas oder sonstwer in die µ-Controller einbaut. Aber genau darin, liegt vermutlich der Vorteil. Wenn man uns nicht verrät was es ist, dann können die das bei der 1214 anders wie bei der 1215 anders wie bei der 1513 und bei der 1518 nochmal anders machen. Irgendwie wird ein Zeiger übergeben. Der ist dann vermutlich eben nur 4 Bytes groß. Und nun kann man ohne das Gehampel mit DB und Adressregister einfach mit dem was die Hardware hergibt indirekt zugreifen. Das ist auf jeden Fall schneller als bei der 400. Problematisch ist nun jedoch der Aufbau eines Zeigers. Hier muss irgendwie immer noch geprüft werden. Aber eben nur einmal, draußen, bei der Parameterübergabe. Wenn ich mir das so überlege, ist das vermutlich der Grund für das mitunter so lästige konsistente Download.

Ich hab mal heute ein bisschen gemessen. Wie viele Calls schafft die 1516 in 100ms? Sehr schwierig raus zu finden. Die Ergebnisse schwanken wie verrückt. Auf dem weg nach Hause - im Stau :-( kam mir dann der Gedanke, dass das vermutlich daran liegt, dass ich das im OB1, der ja mit einer recht niedrigen Priorität läuft, liegt und durch alles mögliche unterbrochen wird, gemessen habe. Trotzdem, was kommt so bei raus?

Der Bausteinaufruf eines leeren Bausteins CALL FC1() oder CALL FB1, DB1() braucht rund 700µs.
Der direkte Zugriff auf ein Element in einem globalen DB DB2.bool1 braucht rund 30ns.
Der direkte Zugriff auf ein Element in der Instanz #s_int1 braucht rund 35ns.
Dann habe ich mir einen UDT aus bool, int, real gemacht und dieses im DB2 verwendet und in der stat des FB verwendet.
Der direkte Zugriff auf ein Element in einem globalen DB DB2.udt1.bool1 braucht rund 30ns. Der Datentyp scheint keine große Rolle zu spielen -- war alles innerhalb der Schwankung.
Der direkte Zugriff auf ein Element in der Instanz #s_udt1.int1 braucht rund 35ns.
Nun habe ich die Schnittstelle erweitert und den DB2.udt1 an einen io Parameter des FB1 übergeben ohne darauf zuzugreifen.
Die Übergabe des DB2.udt1 braucht rund 15µs. Manchmal aber auch nur 4µs -- das verstehe ich nicht.
Und als nächstes ein Zugriff auf diesen Parameter #io_udt1.int1 braucht rund 35ns.
Nun habe ich die Schnittstelle erweitert und den DB2.udt1 an einen i Parameter des FB1 übergeben ohne darauf zuzugreifen.
Die Übergabe des DB2.udt1 braucht rund 120ns -- hier wird der udt aus dem DB2 in den DB1 kopiert, wie man in einer Beobachtungstabelle sehen kann.
Und als nächstes ein Zugriff auf diesen Parameter #i_udt1.int1 braucht rund 35ns.

Also globale Zugriffe sind einen Tick schneller wie lokale Zugriffe.
Der Aufbau des Zeigers dauert.
Kopieren dauert aber auch.
Wenn der UDT 1000 Elemente hat, dann sollte das 30*1000 also vielleicht 30µs dauern -- kommt beim Nachmessen aber nicht raus, sondern nur 12µs.
Bei andere Gelegenheit ist mir schon mal aufgefallen, dass die Zahl der Verschachtelungen eine Rolle spielt, jedoch war das für mich nicht nachvollziehbar -- jede Messung bringt da neue Überraschungen.

Die Zugriffe auf die Referenzen sind sehr schnell. Deutlich schneller als ich das im Verhältnis zur 317 oder 416 kenne.
Wenn also viele Daten über die Schnittstelle eine Bausteins müssen, dann ist die Referenz überlegen.
Bei wenig Daten spielt es eh keine Rolle.

Alle genannten Zahlen sind wie bereits erwähnt mit einem Fehler verbunden -- von 30% bis 300%. Jede Messung bringt da neue Überraschungen. Reproduzierbar ist was anderes :-(
Genug Licht auf den Schatten geworfen?


'n schön' Tach auch
HB

PS: Ich habe ja gehofft, dass uns die V13 zum Thema VARIANT was bringt, aber ... nei nei nei .. ich bin sehr enttäuscht und noch am Messen. Was VariantPut und VariantGet wirklich machen ist noch viel schleierhafter wie die Parameterübergabe. Das dauert noch ein paar Wochen.
 
Die ganze Zeitmesserei ist bei den heutigen CPUs (also auch den neueren 300er und 400er; bei der 1200 und 1500er sowieso) ein Riesenproblem, da auch hier Cachingmechanismen Einzug gehalten haben. Einige der in den SPSen zum Einsatz kommenden Prozessoren setzen auch Branch-Prediction ein - somit ergeben sich teils sehr unterschiedliche Zeiten für Schleifendurchläufe. Auch bemerkbar machen können sich durchaus, wie auch schon oben angesprochen, höherpriore OBs.
Auffällig werden diese Mechanismen, wenn man sich mal anschaut, wie sich die Abarbeitungszeiten ändern in Abhängigkeit davon, ob man einen Baustein beobachtet oder nicht. Unterschiede gab es bei den früheren CPUs auch - diese waren prozentual aber erheblich geringer.

Ich kann nur dringend davon abraten bei den aktuellen CPUs von einer stabilen Zykluszeit auszugehen - aber das hat guter Programmierstil ja schon immer berücksichtigt :ROFLMAO:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich hab FB's mit einige an UDT gemischt aus Bool word und REAL darin. Auch verschachtelt. Hab die aber nur im OUT Schnittstelle. hab das Programm mit TIA_V13 Projektiert und ist noch nicht fertig. Lauf momentan in ein 315 2PN/DP CPU . Mit ungefähr 15 FB aufrufe lauft die CPU im 4 mS Cycluszeit.

@ Hellebarde

Wie hast du die Zeiten so genau rausbekommen? Nur das Testprogramm und dann Cycluszeit beobachten ?

Versuche dann etwas genauer zu analysieren.

Bram


Anhang anzeigen 24137 Anhang anzeigen 24138

Hab diese PDF's gefunden wo etwas steht Über Zeiten.
 
Zuletzt bearbeitet:
Ich hab mal heute ein bisschen gemessen.
Leider scheint bei den vielen Messungen kein Vergleich der hier angefragten Übergabe ByRef vs. ByVal dabei zu sein.

Vielleicht kann ja mal jemand zum Vergleich die Ausführungszeit inklusive dem CALL von folgendem Programmbeispiel messen:
- Übergabe ByRef: FB21 bekommt einen UDT als IN_OUT übergeben
- Übergabe ByVal: FB22 bekommt alle Variablen des UDT einzeln als IN_OUT übergeben (egal ob im FB nur lesen oder nur schreiben)

Der UDT:
Code:
TYPE UDT 20
  STRUCT 	
   Enable : BOOL ;	
   Wert_1 : REAL ;	
   Wert_2 : REAL ;	
   Wert_3 : REAL ;	
   Ergebnis : REAL ;	
  END_STRUCT ;	
END_TYPE

DATA_BLOCK "DB20"
  STRUCT 	
   MyUdtVar : UDT 20;	
  END_STRUCT ;	
BEGIN
   MyUdtVar.Enable := FALSE; 
   MyUdtVar.Wert_1 := 0.000000e+000; 
   MyUdtVar.Wert_2 := 0.000000e+000; 
   MyUdtVar.Wert_3 := 0.000000e+000; 
   MyUdtVar.Ergebnis := 0.000000e+000; 
END_DATA_BLOCK

FB21 mit Übergabe der Struktur als UDT an IN_OUT ByReference:
Code:
FUNCTION_BLOCK FB 21
VAR_IN_OUT
  IO_UDT : UDT 20;	
END_VAR
BEGIN
NETWORK

      U     #IO_UDT.Enable; 
      SPBN  M1; 
      L     #IO_UDT.Wert_1; 
      L     #IO_UDT.Wert_2; 
      *R    ; 
      L     #IO_UDT.Wert_3; 
      *R    ; 
M1:   SET   ; 

      U     #IO_UDT.Enable; 
      SPBN  M2; 
      L     #IO_UDT.Wert_1; 
      L     #IO_UDT.Wert_2; 
      +R    ; 
      L     #IO_UDT.Wert_3; 
      +R    ; 
M2:   SET   ; 

      U     #IO_UDT.Enable; 
      SPBN  M3; 
      L     #IO_UDT.Wert_1; 
      L     #IO_UDT.Wert_2; 
      *R    ; 
      L     #IO_UDT.Wert_3; 
      +R    ; 
      T     #IO_UDT.Ergebnis; 
M3:   SET   ; 

END_FUNCTION_BLOCK

FB22 mit Übergabe der Struktur als Einzelvariablen an IN_OUT ByValue:
Code:
FUNCTION_BLOCK FB 22
VAR_IN_OUT
  IO_Enable : BOOL ;	
  IO_Wert_1 : REAL ;	
  IO_Wert_2 : REAL ;	
  IO_Wert_3 : REAL ;	
  IO_Ergebnis : REAL ;	
END_VAR
BEGIN
NETWORK

      U     #IO_Enable; 
      SPBN  M1; 
      L     #IO_Wert_1; 
      L     #IO_Wert_2; 
      *R    ; 
      L     #IO_Wert_3; 
      *R    ; 
M1:   SET   ; 

      U     #IO_Enable; 
      SPBN  M2; 
      L     #IO_Wert_1; 
      L     #IO_Wert_2; 
      +R    ; 
      L     #IO_Wert_3; 
      +R    ; 
M2:   SET   ; 

      U     #IO_Enable; 
      SPBN  M3; 
      L     #IO_Wert_1; 
      L     #IO_Wert_2; 
      *R    ; 
      L     #IO_Wert_3; 
      +R    ; 
      T     #IO_Ergebnis; 
M3:   SET   ; 

END_FUNCTION_BLOCK

Der Aufruf der beiden FB zum Vergleich:
Code:
FUNCTION FC 21 : VOID
BEGIN
NETWORK
[COLOR="#008000"]//ByRef IN_OUT UDT       : Call des FB:  50 Byte + FB21: 614 Byte = 664 Byte[/COLOR]
      CALL FB    21 , DB    21 (
           IO_UDT                   := "DB20".MyUdtVar);

[COLOR="#008000"]//ByVal IN_OUT Variablen : Call des FB: 192 Byte + FB22: 120 Byte = 312 Byte[/COLOR]
      CALL FB    22 , DB    22 (
           IO_Enable                := "DB20".MyUdtVar.Enable,
           IO_Wert_1                := "DB20".MyUdtVar.Wert_1,
           IO_Wert_2                := "DB20".MyUdtVar.Wert_2,
           IO_Wert_3                := "DB20".MyUdtVar.Wert_3,
           IO_Ergebnis              := "DB20".MyUdtVar.Ergebnis);

END_FUNCTION

Harald
 
Zurück
Oben