2 Word zu einem Real verbinden

fischerp

Level-2
Beiträge
45
Reaktionspunkte
4
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
ich lese mit einer WAGO-Steuerung ein Modbusgerät aus (Modbus TCP). Die Werte sind teilweise 32bit. Leider kann Codesys da offensichtlich nur Bit und word händeln. Jetzt suche ich eine Lösung, um die 32bit-Werte zu verarbeiten. Es stehen - was auch sonst - immer in zwei aufeinanderfolgenden word die 4 byte aus denen der Wert besteht. Wenn ich die Register mit Modbuspoll auslese, dann sehe ich in der Darstellung als word die selben Werte, wie in Codesys, als float sehe ich die Werte, wie ich sie erwarten würde. Also muss ich die beiden Word "verheiraten". Der eleganteste Weg scheint mir die Union-Funktion zu sein. Trotz recht umfangreicher Recherche sowohl hier im Forum, als auch allgemein im Netz, komme ich damit nicht klar. Ich will jetzt kein fertig gestricktes Programm, das mir mein Problem löst, aber vllt gibt es etwas empfehlenswerte Literatur bzw Links zum Thema. Oder einen Programmausschnitt, der zeigt, wie das grundsätzlich funktioniert. Ich glaube, hoffe, dass das wenn man einmal das Prinzip durchschaut hat, völlig problemlos ist.
Letztendlich soll, wenn das überhaupt machbar ist, ein FB rauskommen, bei dem ich über zwei Input-Variablen meine gelesenen Word anlege und am Ausgang mein Wert steht.
 
Welche Codesys Version, 2 oder 3?

so umfangreich kann die Suche hier im Forum nicht gewesen sein, Pointer oder Union:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Der eleganteste Weg scheint mir die Union-Funktion zu sein. Trotz recht umfangreicher Recherche sowohl hier im Forum, als auch allgemein im Netz,
Benutze einfach die Forumssuche mit den Suchworten "word real union", da werden zumindest bei mir relevante Beiträge aufgelistet, z.B. als zweite Stelle:
Nimm das Bitmuster des DWORD und speichere es in eine REAL-Variable bzw. speichere es auf einen 32-Bit-Speicherplatz und hole es als REAL-Wert wieder raus.
DWord-Bitmuster in REAL-Variable speichern:
Code:
VAR
  dwVar : DWORD;            //Eingang DWORD
  pReal : POINTER TO REAL;
  rReal : REAL;             //Ausgabe REAL
END_VAR

dwVar := MyDWord;     //Bitmuster in das DWord speichern
pReal := ADR(dwVar);  //Pointer auf das DWord setzen
rReal := pReal^;      //REAL-Wert aus dem DWord in Real-Variable speichern
Oder den REAL-Wert via UNION aus Words oder Bytes zusammenbasteln:
Code:
TYPE U_REALWORDS :
  UNION
    uReal  : REAL;
    uaWord : ARRAY[0..1] OF WORD;
  END_UNION
END_TYPE

tempU : U_REALWORDS;


tempU.uaWord[0] := dasEineWord;
tempU.uaWord[1] := dasAndereWord;
rMyReal := tempU.uReal;
Code:
TYPE U_REALBYTES :
  UNION
    uReal  : REAL;
    uaByte : ARRAY[0..3] OF BYTE;
  END_UNION
END_TYPE

tempU : U_REALBYTES;


tempU.uaByte[0] := Byte_1;
tempU.uaByte[1] := Byte_2;
tempU.uaByte[2] := Byte_3;
tempU.uaByte[3] := Byte_4;
rMyReal := tempU.uReal;

Ich kann mir auch vorstellen, dass man bei ChatGPT eine brauchbare Antwort bekommt.
 
Guten Morgen,
erst mal, es geht um Codesys 3.5
Die hier empfohlenen Posts und vieles, was ich mit der Suche mit den genannten Suchbegriffen suche, habe ich tatsächlich schon gesehen, aber nicht wirklich verstanden. Das mag sicher daran liegen, dass ich bislang - auch aus Faulheit - fast nur in CFC programmiert habe. Ausserdem durchschaue ich grundsätzlich nicht, wie dieses Union-Konstrukt aufgebaut ist. Ich habe mir mal ein Beispiel in ChatGPT heruntergeladen, das hat mich aber auch nicht wesentlich schlauer gemacht.
Was ich glaube verstanden zu habe ist:
  • irgendwo, vermutlich in der Variablendeklaration müssen die "Teilnehmer" der Union benannt werden. wenn ich das richtig verstanden habe so:
TYPE MyUnion :
UNION
Word1 : WORD;
Word2 : WORD;
Dword : DWORD;
END_UNION;
END_TYPE

VAR
uData : MyUnion;
END_VAR

  • Jetzt müssen den Variablen Word1 und Word2 Werte zugeordnet werden. Das findet im Programmfenster statt


    uData.Word1 := word1UL1; // auf diese Variable habe ich die Modbusregister gemappt
    uData.Word2 := word2UL2; //auf diese Variable habe ich die Modbusregister gemappt

  • jetzt werden die beiden Word in die Variable uData geschrieben

    // Zugriff auf den Dword-Wert (kombinierte Werte von Word1 und Word2)
    uData.Dword := (uData.Word2 * 16#10000) + uData.Word1;
  • die folgende Zeile verstehe ich überhaupt nicht. Jetzt sollte doch in uData.Dword bereits mein Wert stehen

    // Ausgabe des Dword-Werts
    WriteLog('Dword: ' + INT_TO_STRING(uData.Dword));
Ich verstehe natürlich, dass es mühsam ist, jemandem, der mit wenig Grundwissen daher kommt, die Welt zu erklären. Das geht mir bei den Themen, von denen ich Ahnung habe im umgekehrten Fall genauso, aber vllt hat jemand trotzdem Lust, mir hier unter die Arme zu greifen.
 
Was ich glaube verstanden zu habe ist:
  • irgendwo, vermutlich in der Variablendeklaration müssen die "Teilnehmer" der Union benannt werden. wenn ich das richtig verstanden habe so:
TYPE MyUnion :
UNION
Word1 : WORD;
Word2 : WORD;
Dword : DWORD;
END_UNION;
END_TYPE
Union = mehrere Variablen vereinigt auf demselben Speicherplatz, die Variablen können unterschiedliche Datentypen haben - das ist die hauptsächliche Anwendung der Union
Mit TYPE wird die Struktur eines selbst definierten Datentyps deklariert. Bei dir: Datentyp "MyUnion", der aus einer UNION von 3 Variablen besteht, und 4 Byte groß ist, weil die größte enthaltene Variable ein DWORD ist.
Alle Variablen, die bei UNION angegeben werden, erhalten dieselbe (Anfangs-)Adresse = liegen auf derselben Speicheradresse. Speichert man etwas in eine Variable, dann ändert sich logischerweise auch der Inhalt aller anderen Variablen, die auf derselben Adresse liegen.
Sollen mehrere Variablen hintereinander in der Union liegen, dann ist das mit Struct oder Array möglich. siehe meinen Beispielcode in Beitrag #3 - da liegen 2 Words hintereinander, weil in einem Array:
Code:
TYPE U_REALWORDS :
  UNION
    uReal  : REAL;
    uaWord : ARRAY[0..1] OF WORD;
  END_UNION
END_TYPE

tempU : U_REALWORDS;


tempU.uaWord[0] := dasEineWord;
tempU.uaWord[1] := dasAndereWord;
rMyReal := tempU.uReal;

Bei dir liegt Word1 und Word2 auf derseben Adresse! Sobald Word2 etwas zugewiesen wird, wird damit auch der Speicherplatz von Word1 überschrieben!
 
Union = mehrere Variablen vereinigt auf demselben Speicherplatz, die Variablen können unterschiedliche Datentypen haben - das ist die hauptsächliche Anwendung der Union
Und zwar: dem Compiler mitzuteilen, dass man als Programmierer weiß was man tut, damit der Compiler nicht womöglich "freundlicherweise" irgendwelche Konvertierungen im Hintergrund hinzufügt. Beispiel:
Code:
TYPE U_REALDW :
  UNION
    uReal  : REAL;
    uDWord : DWORD;
  END_UNION
END_TYPE

tempU : U_REALDW;
MyDWord : DWORD;
MyReal : REAL;


//mit UNION
tempU.uDWord := MyDWord; //den Wert des DWord 1:1 in die Union hinein kopieren
MyReal := tempU.uReal;   //den Wert aus der Union 1:1 als Real heraus kopieren

//ohne UNION
MyReal := MyDWord; //den Wert des DWord als Ganzzahl interpretieren und in Real konvertieren
Weist man der DWord-Variable in der Union den Wert einer DWord-Variable zu, dann passt der Datentyp zueinander und der Compiler kopiert einfach den Wert ohne Konvertierung.
Kopiert man aus der Real-Variable der Union in eine Real-Variable, dann passt der Datentyp zueinander und der Compiler kopiert einfach den Wert ohne Konvertierung.
Weist man den Wert einer DWord-Variable an eine Real-Variable zu, dann passt der Datentyp nicht zueinander und der Compiler kopiert nicht einfach, sondern konvertiert den Wert von Ganzzahl zu REAL.

In Codesys geht dieses Kopieren ohne Konvertieren übrigens nicht direkt, sondern man muss zwingend über einen Zwischenspeicher umkopieren. Mit Hilfe einer UNION oder über getypte Pointer (siehe Beitrag #3).

Ich verstehe natürlich, dass es mühsam ist, jemandem, der mit wenig Grundwissen daher kommt, die Welt zu erklären. (...) vllt hat jemand trotzdem Lust, mir hier unter die Arme zu greifen.
Waren meine Erklärungen in den letzten 3 Beiträgen nun ausführlich und verständlich genug?
 
Die Erklärungen waren ausführlich und verständlich, allerdings habe ich vermutlich noch immer ein Brett vor dem Kopf. Ich zeige mal, was ich gemacht habe:
Irgendwo habe ich gelesen, dass die Typdefinition in einem DUT stehen muss, also habe ich ein DUT vom Datentyp Union an meine Applikation angehängt. Da habe ich den auf meine Verhältnisse angepassten Code aus Beitrag #3 reingeschrieben. (word1UL1 und word2UL2 sind tatsächlich exitierende word-Variablen, die auch einen sinnvollen Inhalt haben)

  1. Create a DUT og type UNION with a Word array of size 2 and a REAL value.
  2. Copy the two modbus words (per real value) into the unions array.
  3. Read the REAL value out of the UNION
1741463387871.png

Das lässt sich fehlerfrei kompilieren und ich kann es auch auf die Steuerung laden. Jetzt hätte ich erwartet, dass ich in rMyReal etwas sehe.
Wenn ich zur Laufzeit auf das DUT schaue scheint da überhaupt nichts zu laufen.
Wenn ich im Programm die Variablen verwenden möchte, finde ich die nirgends.
Muss ich das noch irgendwie starten?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Du brauchst mindestens drei Dinge:
- die Deklaration des Unions (->DUT)
- eine Variablendeklaration mit dem Typ des Unions
- Code zum Beschreiben des einen Datentyps und Auslesen des anderen Datentyps

Punkt 2 und 3 kannst du innerhalb eines FC abhandeln. Der hat dann beispielsweise die zwei Word als Eingangsparameter und das Real als Rückgabewert.
 
  1. Create a DUT og type UNION with a Word array of size 2 and a REAL value.
  2. Copy the two modbus words (per real value) into the unions array.
  3. Read the REAL value out of the UNION
Hast du diese Anleitung von einer KI? Da fehlt zwischen 1 und 2 noch der Schritt, dass eine Variable von dem UNION-Datentyp angelegt/deklariert werden muss.

Anhang anzeigen 85873
Das lässt sich fehlerfrei kompilieren und ich kann es auch auf die Steuerung laden.
Wie kann das fehlerfrei kompiliert werden, wo doch noch einiges rot unterstrichen ist??
Oder passt/gehört das Bild nicht zu deinen Erklärungen?

In meinem Code in #3 hatte ich aus Platzgründen und/oder Faulheit die Zeilen VAR und END_VAR weggelassen. Übliche ;) Codesys-Programmierer sehen, dass die Zeile mit dem Doppelpunkt eine Variablen-Deklaration ist und verschieben die Zeile in einen passenden Variablen-Deklarations-Abschnitt.

Nochmal ganz ausführlich:
1. Create a DUT og type UNION with a Word array of size 2 and a REAL value.
so:
Code:
TYPE U_REALWORDS :
  UNION
    uReal  : REAL;
    uaWord : ARRAY[0..1] OF WORD;
  END_UNION
END_TYPE
Das ist die Deklaration des Datentyps (bei Wago heißt das DUT ?) Eventuell gibt es im Wago-Programmiersystem eine zentrale Stelle, wo der Datentyp deklariert werden muss? (ich kenne das Wago-Programmierystem nicht)

Jetzt suche dir einen Codeabschnitt, wo du den Code programmieren willst, z.B. einen FUNCTION_BLOCK (professioneller: eine FUNCTION erstellen mit 2 Words IN und REAL Rückgabe).
In dem Code zunächst die Union-Variable deklarieren (eine "Instanz" erstellen):
Code:
VAR
  tempU : U_REALWORDS;
END_VAR

Nun der Programmcode
2. Copy the two modbus words (per real value) into the unions array.
3. Read the REAL value out of the UNION
Code:
tempU.uaWord[0] := dasEineWord;
tempU.uaWord[1] := dasAndereWord;
rMyReal := tempU.uReal;

Nun noch den Code im Programm aufrufen, compilieren, in die Steuerung laden, und du solltest den Code und das Ergebnis beobachten können.

Wenn ich im Programm die Variablen verwenden möchte, finde ich die nirgends.
??? Welche Variablen meinst du?
 
Hast du diese Anleitung von einer KI? Da fehlt zwischen 1 und 2 noch der Schritt, dass eine Variable von dem UNION-Datentyp angelegt/deklariert werden muss.
das habe ich aus der englischsprachigen wagocommunity https://www.wago.community/t/how-to-read-real-value-on-modbus-for-codesys-v3-5/551
Wie kann das fehlerfrei kompiliert werden, wo doch noch einiges rot unterstrichen ist??
Oder passt/gehört das Bild nicht zu deinen Erklärungen?
Genau das kann ich problemlos hochladen (grünes Zahnrad -> einloggen) dann wird es zuerst übersetzt, dann hochgeladen. Jetzt sind auch die roten Unterstreichungen weg. Ich kann das Projekt starten und alles andere, was ich bis dahin programmiert habe, läuft.

Jetzt habe ich das, wie von dir beschrieben, gemacht, und es geht. (Ist wohl so, wie Du das in deiner Signatur erwähnst.) Ich bilde mir ein, dass ich das Prinzip erkannt habe. Zum testen habe ich das in ein POU geschrieben und da zum Beschreiben des Arrays meine tatsächlich existierenden Word verwendet.
Wenn ich dich oben richtig verstanden habe, sollte es aber möglich und sinnvoll sein, den Code in einen FB, oder besser in eine Function (mit Rückgabetyp REAL) zu packen. Da natürlich die (in meinem Fall 2) word als Var in. Sehe ich das richtig?

Und jetzt bleibt mir nur noch, dir zu danken und, das muss ich einfach sagen, wenn ich ehrlich bin, ich hätte bei so viel Begriffsstutzigkeit meinerseits die Krise bekommen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Jetzt habe ich noch ein Problem, das mir aber vergleichsweise gering erscheint.
Ich habe eine Funtion mit genau dem selben Code, der als Programm funktioniert, geschrieben. Die beiden Werte, die ins Array geschrieben werden sollen, habe ich als VAR_INPUT definiert.
1741513575285.png
Das ganze rufe ich im PLC_PRG auf
1741513646516.png
Jetzt bekomme ich diesen Fehler:
------ Übersetzungslauf gestartet: Applikation: Device.Application -------
Code typisieren...
Code erzeugen...
[FEHLER] cc100_20250309_zwischenstand: PLC_PRG [Device: SPS-Logik: Application](Zeile 2, Spalte 1 (Impl)): C0040: Funktion 'word2_in_real' benötigt genau '2' Eingänge
Übersetzung abgeschlossen -- 1 Fehler, 0 Warnungen : Kein Download möglich


Ich habe genau zwei Eingänge, word1 und word2. Was übersehe ich?
 
Du musst ihnen noch etwas zuweisen z.b. :
Code:
fMyRealFromModbus := word2_in_real(word1 := ModbusWord1, word2 := ModbusWord2);
 
In deiner Function musst du den REAL-Wert nicht an eine lokale Variable zuweisen, sondern zurückgeben, indem du den Wert dem Funktionsname zuweist:
Code:
word2_in_real := tempU.uResponse;
Dann kann der Rückgabewert der Function beim Aufruf der Function an die Zielvariable zugewiesen werden, siehe den Code von samus in #13: fMyRealFromModbus := word2_in_real(...

Grrr.... Smartphone Autokorrektur :oops:
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
dann sieht das jetzt so aus:
1741526385294.png
allerdings erzeugt es nach wie vor den selben Fehler. Funktion 'word2_in_real' benötigt genau '2' Eingänge

was hat es damit auf sich?
fMyRealFromModbus := word2_in_real(word1 := ModbusWord1, word2 := ModbusWord2);
muss ich im PLC_PRG -Aufruf etwas in der Richtung eingeben:
fMyRealFromModbus := word2_in_real(inword1 :=tempU.uaInword[0] , inword2 :=tempU.uaInword[1] );
(bislang steht da: word2_in_real(inword1 := , inword2 := );
oder worauf bezieht sich das?
 
Ja genau. Dieser Code gehört in das PLC_PRG.
Damit rufst du deine Funktion auf. Damit die Funktion weiss welche Werte du gerne übersetzt haben willst, musst du der Funktion beim Aufruf die Werte mitgeben. Damit du das Resultat der Funktion siehst, musst du das Resultat der Funktion auf eine Variable schreiben. In diesem Fall fMyRealFromModbus.
 
Ich habe diese Zuweisung jetzt im PLC_PRG, da kommen dann Fehler.
  • fMyRealFromModbus nicht definiert
    • habe eine Variable vom Typ real angelegt, jetzt ist der Fehler weg.
  • 'Unbekannter Typ: 'tempU.uaInword[0]'' kann nicht in Typ 'WORD' konvertiert werden
    'Unbekannter Typ: 'tempU.uaInword[1]'' kann nicht in Typ 'WORD' konvertiert werden
    • In der Function ist tempU als Variable Typ U_Realwords definiert. Das ist meine Union
    • In der Union definiere ich eine Variable uaInword als Array [0..1] of word
    • also sollte es tempU.uaInword[0] geben
... und ich hatte geglaubt, dass ich das mit der Union langsam kapiert habe.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Zeig mal einen Screenshot deiner Union.

Edit: Achte darauf, dass das Array[0..1] OF WORD richtig geschrieben ist. Mit Semikolon am schluss etc. Er erkennt die Deklaration nicht. Und es darf kein Code sonst in der Union sein.
 
Zuletzt bearbeitet:
dann mache ich das jetzt der Reihe nach

Die Union
1741535871518.png

Die Function
1741535919452.png
Der Aufruf im PLC_PRG
1741535985648.png

Mehr gibt es da nicht. Wenn jetzt noch was fehlt, dann habe ich es vergessen, nicht kapiert, whatever
 
Ich kenne Codesys nicht
Aber ich glaube du hast zwischen Beitrag 1 und jetzt dein Ziel aus den Augen verloren.

Du wolltest Daten von Modbus in Real wandeln. Das solltest du nun auch machen.

Das rote untersrichene wird heissen, Variable nicht definiert. Also in deinem Programmabschnitt ist tempU nicht definiert.
Braucht du auch nicht du solltest ja die Modbus Word Daten verwenden.
 
Zurück
Oben