Kommunikationsvariablen (DWORD) richtig in andere Datentypen aufsplitten TwinCAT3

el-nino

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

Ich habe eine Frage zur Adressvergabe für Kommunikationsvariablen. Ich kommuniziere via Profinet mit einer Siemens und habe dazu je 32 DWORD vergeben welche ich auch so mit der Hardware verlinke:
Code:
Input AT %IB600: ARRAY[0..31] OF DWORD;
Output AT %QB600: ARRAY[0..31] OF DWORD;

Diese DWORD werden verschieden verwendet. Teilweise als einzelne Bits, oder aber auch als Zahlenwerte (zb DINT). Um die DWORDs aufzuschlüsseln habe ich eine STRUCT definiert, die dann etwa so aussieht und einfach auf dieselben absoluten Adressen geht:
Code:
TYPE strInput :
STRUCT
    iLifeBit         AT %IX600.0    : BOOL; 
    iLaeuft         AT %IX600.1    : BOOL;
    iBetriebsbereit    AT %IX600.2    : BOOL;
    .....
    iWert1        AT %IB616    : DINT; 
    iWert2            AT %IB620    : DINT; 
END_STRUCT
END_TYPE

Verwendet wird das Ganze dann so:
Code:
 Input AT %IB600: ARRAY[0..31] OF DWORD;
 Output AT %QB600: ARRAY[0..31] OF DWORD;
 VonGeraet : strInput;
 AnGeraet : strOutput;

Das funktioniert soweit eigentlich ganz gut. Nur habe ich ein Problem bei vergleichen, auch wenn iWert1 = iWert2 ist, das Resultat ist immer FALSE:
IMG_6702.JPG
wenn im Bild oben die Variablen test1 und test2 TRUE sind, müsste zwangweise test3 auch TRUE sein, ist es aber nicht. Wenn ich die Werte auf eine andere Variable kopiere, funktioniert der Vergleich dann.

Die Frage ist nun: was ist hier Best Practice? Es sollte ja nichts spezielles sein, dass ich zur Kommunikation Blöcke mit demselben Datentyp verwende (zb DWORDs) und diese dann in einzelne Datentypen aufsplitten muss.
 
An welcher Stelle im Programm setzt du denn wann die Variable "Test3" auf FALSE ?
Ich bin mir nicht sicher, dass TwinCat dir den Wert einer Variablen anzeigt, wie er exakt in der Programmzeile ist - ich denke eher, dass du auch dort das Ergebnis vom Programmende angezeigt bekommst ...

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
test3 wird nirgends auf FALSE gesetzt, ich setzte die test-variablen jeweils manuell wieder zurück. Um das Problem weiter einzugrenzen habe ich diese ganze IF-Struktur auch noch mit einem Trigger versehen, so dass sie genau einen SPS-Zyklus lang durchlaufen wird. Es ist effektiv so, dass die SPS in dieser Situation "komisch" reagiert...

Ich hatte auch schon mit Beckhoff kontakt. Die sehen das Problem in der doppelten Adressierung (Input[4] belegt dieselbe Adresse wie iWert1), warum das einen Einfluss auf Vergleiche haben soll kann mir aber niemand erklären. Der Vorschlag von Beckhoff ist die Daten in einem FB auf lokale Variablen zu kopieren. Ich finde das etwas umständlich und frage mich daher, was in diesem Fall Best Practice ist. Das sollte ja eigentlich etwas "alltägliches" sein, dass man mit einem anderen Datentyp kommuniziert als man dann effektiv braucht.
 
Also ich würde bei der Struktur gar keine Adressen verwenden. Erstell die Struktur so wie die Daten reinkommen, bzw. rausgehen sollen und mach ein memcpy, fertig. Außerdem musst Du in der Struktur noch das Pragma {attribute 'pack_mode' := '1'} einfügen, damit keine Lücken entstehen.
Ansonsten würde ich zum Testen bei der IF-Abfrage mal einen Breakpoint setzen um zu sehen, was zu diesem Zeitpunkt tatsächlich in der Variable steht. Natürlich nur, wenn Dir dabei nicht die ganze Anlage um die Ohren fliegt.
 
Ich hatte auch schon mit Beckhoff kontakt. Die sehen das Problem in der doppelten Adressierung (Input[4] belegt dieselbe Adresse wie iWert1), warum das einen Einfluss auf Vergleiche haben soll kann mir aber niemand erklären. Der Vorschlag von Beckhoff ist die Daten in einem FB auf lokale Variablen zu kopieren. Ich finde das etwas umständlich
Kann es sein, daß die Zugriffe auf "AT %IX"-Variablen auf die Peripherie zugreifen und nicht in ein Prozessabbild der Eingänge? Dann macht das einmalige Umkopieren auf andere Variablen mehr als Sinn bzw. ist Pflicht, weil bei jedem Zugriff auf die Peripherie ein anderer Wert geliefert werden könnte, wenn sich der Status der Peripherieeingänge in der Zwischenzeit geändert hat.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Also ich würde bei der Struktur gar keine Adressen verwenden. Erstell die Struktur so wie die Daten reinkommen, bzw. rausgehen sollen und mach ein memcpy, fertig. Außerdem musst Du in der Struktur noch das Pragma {attribute 'pack_mode' := '1'} einfügen, damit keine Lücken entstehen.
Ansonsten würde ich zum Testen bei der IF-Abfrage mal einen Breakpoint setzen um zu sehen, was zu diesem Zeitpunkt tatsächlich in der Variable steht. Natürlich nur, wenn Dir dabei nicht die ganze Anlage um die Ohren fliegt.

Das mit dem Breakpoint habe ich schon gemacht, es steht wirklich bei beiden 6000 drin. Auch wenn ich das ganze Bit für Bit analysiere steht in beiden derselbe Wert drin. Wieso dass der Vergleich gegen 6000 beide Male TRUE ergibt aber gegeneinander FALSE konnte mir noch niemand erklären...

Kannst du mir ein Beispiel zu memcpy und diesem pack_mode posten?

Kann es sein, daß die Zugriffe auf "AT %IX"-Variablen auf die Peripherie zugreifen und nicht in ein Prozessabbild der Eingänge? Dann macht das einmalige Umkopieren auf andere Variablen mehr als Sinn bzw. ist Pflicht, weil bei jedem Zugriff auf die Peripherie ein anderer Wert geliefert werden könnte, wenn sich der Status der Peripherieeingänge in der Zwischenzeit geändert hat.

Der Status der Peripherieeingänge ist zu diesem Moment statisch. Das Ganze ist während einer Inbetriebnahme aufgetaucht, entsprechend konnte ich es auch soweit isolieren.

Hier noch ein weiterer Test wo ich die Werte auf Variablen kopiere und vergleiche. Das Ganze soweit isoliert dass es genau 1 SPS-Zyklus lang durchlaufen wird (R_TRIG):
1.jpg
 
Zuletzt bearbeitet:
Kein Problem, die Anlage läuft ja soweit mit dem Umkopieren. Es geht vorallem um die Zukunft, best practice und so.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Kannst du mir ein Beispiel zu memcpy und diesem pack_mode posten?
Vor gerade 5 Tagen hatten wir hier schon quasi die gleiche Frage, wo es darum ging ein Struct über ein Profinet-Array zu legen, da kam auch schon der Tip mit dem MEMCPY, was man benutzen kann, wenn man keine Bytes drehen muß. pragma {attribute 'pack_mode'}
Am Ende hat der Frager wegen der besseren Verständlichkeit seine Aufgabe mit einer UNION gelöst.
INT / DINT in Array of Byte kopieren

Sag mal, muß man bei Twincat hinter END_IF keine Semikolons ; setzen? Ändert sich was, wenn Du Semikolons setzt?
Wie/wo ist "test3" deklariert? Gibt es vielleicht eine zweite (globale/lokale?) Variable mit dem selben Name und die verschatten sich irgendwie gegenüber dem ? Nimm mal statt "test3" eine neue Variable mit anderem Name wie z.B. "test1234" ;)

Harald
 
Zuletzt bearbeitet:
So, dann will ich mal liefern.
Ich habe zwei Strukturen mit eigentlich gleichem Inhalt angelegt, nur eine mit "pack_mode" Pragma und eine ohne.
Code:
{attribute 'pack_mode' := '1'}
TYPE ST_WithPragma :
STRUCT
 wWord1  : WORD;
 byByte1  : BYTE;
 dwDWord1 : DWORD;
 byByte2  : BYTE;
 wWord2  : WORD;
 iInteger1 : INT;
END_STRUCT
END_TYPE

TYPE ST_WithoutPragma :
STRUCT
 wWord1  : WORD;
 byByte1  : BYTE;
 dwDWord1 : DWORD;
 byByte2  : BYTE;
 wWord2  : WORD;
 iInteger1 : INT;
END_STRUCT
END_TYPE

Um eine Datenquelle zu simulieren habe ich ein Array of Byte mit 12 Elementen erstellt und initialisiert.
Code:
abyTestArray : ARRAY[1..12] OF BYTE := [16#F0, 16#01, 16#50, 16#30, 16#D0, 16#A1, 16#FE, 16#DE, 16#FC, 16#00, 16#00, 16#80];

Sowohl das Array als auch die beiden Strukturen haben auf den ersten Blick die selbe Größe, dazu jedoch später mehr. Soviel sei verraten, es wird lustig.

Diese Daten kopiere ich nun mit memcpy in jeweils eine Instanz der obigen Strukturen, was in folgendem Code erfolgt.
Code:
memcpy(ADR(stWithPragma), ADR(abyTestArray), SIZEOF(abyTestArray));
memcpy(ADR(stWithoutPragma), ADR(abyTestArray), SIZEOF(abyTestArray));

Die Daseinsberechtigung des Pragmas wird deutlich, wenn man sich die Strukturen Online anschaut. Hier zunächst in dezimaler Darstellung. Bei gleichen Strukturen wäre ein gleiches Ergebniss zu erwarten.
PragmaProblem01.png
Hm, das sieht jetzt aber nur teilweise gleich aus, was ist denn da passiert? Bei verschiedenen Programmiersprachen gibt es (meist einstellbare) sogenannte Speicherausrichtungen, das heißt Variablen schließen, je nach Ihrer Größe nicht unbedingt im Speicher direkt aneinander an. Angenommen Variablen beginnen immer an einer geraden Adresse, dann würde der Compiler nach einem Byte eine Lücke lassen und genau das macht TwinCAT/Visual Studio wenn das Pragma nicht verwendet wird. Die Funktion memcpy weiß davon aber nichts und kopiert munter jeden Eintrag des Arrays nacheinander in die Struktur. "Wie, da gibt's Lücken?! Ist mir doch egal!"
Aus diesem Grunde gibt es zwischen den beiden Strukturen eine Verschiebung von insgesamt zwei Bytes, nämlich jeweils um ein Byte nach jeder Byte-Variable in der Struktur. Gleichzeitig muss beachtet werden, dass Werte mit mehr als einem Byte in der als "Little-Endian" bezeichneten Bytereihenfolge abgespeichert werden, also das niederwertigste Byte zuerst. In hexadezimaler Darstellung sieht das Ganze dann so aus und die angesprochene Verschiebung wird deutlich.
PragmaProblem02.png
Der Erste Wert ist ein Word mit dem Wert 01F0Hex, bzw. 496Dez was im Array den Positionen 1 (Least Significant Byte = F0) und 2 (Most Significant Byte= 01) entspricht. Der nächste Wert passt auch noch 50Hex entsprechend 80Dez, stimmt soweit. Aber dann nimmt das Unglück, wie erwähnt, bei der Struktur ohne Pragma seinen Lauf. Das LSB (30Hex) vom DWord geht flöten, stattdessen drängelt sich der eigentlich für das nächste Byte vorgesehene Wert (DEHex) als neues MSB rein.
 
Zuletzt bearbeitet:
Zurück
Oben