Modbus RTU mit TwinCAT 3 – Frequenz wird nicht auf Slave-Gerät geschrieben, Output Start/Stop funktioniert

Ashrafhj

Level-2
Beiträge
9
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,





ich habe aktuell ein Projekt, bei dem ich ein Slave-Gerät der DF-C 61XXX-Serie über Modbus RTU mit TwinCAT 3 steuern möchte. Dabei benutze ich eine Beckhoff EL6002 (RS232-Klemme). Mein Ziel ist es, die Frequenz auf das Slave-Gerät zu schreiben und andere Werte wie Spannung oder Strom auszulesen.





Projektbeschreibung:


• Hardware:


• Beckhoff CX5020 mit EL6002-Klemme (RS232)


• Modbus-RTU-fähiges Slave-Gerät: DF-C 61XXX-Serie


• Kommunikation:


• Modbus RTU


• Baudrate: 9600


• Datenbits: 8


• Stopbits: 2


• Parität: N


• Slave-Adresse: 100


• Register:


• Frequenz schreiben: 0x0007 (Frequency Setting, R/W, Einheit: 0.1 Hz)


• Output Start/Stop: 0x0009 (Control Command, W, Werte: 0x0000 für Stop, 0x0001 für Start)





Was funktioniert bereits:


1. Output Start/Stop:


• Schreiben von 0x0000 (Stop) und 0x0001 (Start) in das Register 0x0009 funktioniert einwandfrei. Das Slave-Gerät reagiert korrekt.


2. Kommunikation:


• Es treten keine Fehler während der Kommunikation auf. Das Slave-Gerät bestätigt die Befehle mit korrekten Antworten.





Problem:


• Beim Schreiben eines Frequenzwerts (z. B. 62.0 Hz → 620 in 0.1 Hz) in das Register 0x0007 gibt es keine sichtbare Reaktion am Slave-Gerät.


• Der Schreibvorgang wird als erfolgreich gemeldet (kein Fehlercode), aber der Wert wird nicht angezeigt oder übernommen.





Verwendeter Code:

FUNCTION_BLOCK Modbus_rtu



VAR_ INPUT

newVarInput: UINT;

freqValue : UINT;// Frequency value (e.g., 62.0 Hz -> 620 in 0.1Hz resolution)

voltValue : UINT;// Voltage value (e.g., 120.0V -> 1200 in 0.1V resolution)

outputCommand: UINT: // Command to control output (e.g., 1 for start output)

enable: BOOL;

END_VAR

VAR OUTPUT

bSuccess : BOOL; // Indicates if the operation was successful

bBusy: BOOL; // Indicates if the operation is in progress

bError : BOOL; // Indicates if an error occurred uiErrorID: UINT; // Error ID in case of failure

END VAR

VAR

fregRegisterAddress : UINT :=7; // Register address for frequency

voltRegisterAddress : UINT := 8; // Register address for voltage

outputRegisterAddress : WORD:= 9; // Register address for output control

wqauntity: WORD:=1;

uid: BYTE:=100;

data _write: WORD:=16#0001;// Outputstart

data_writefreq: WORD:=16#0258 // Example: Data returned as 0x0258 HEX 0x0258 = Value 600, the output frequency of device is 60.0Hz



ModbusRtu: ModbusRtuMasterV2_RL6x22B;

Execute: BOOL;

END VAR





ModbusRtu.WriteSingleRegister



(UnitID:-uid,

Quantity:=wqauntity,

MBAddr: =outputRegisterAddress,

cbLength:=SIZEOF (data_write) .

pMemoryAddr:=ADR(data_write) ,

Execute:-enable,

);



ModbusRtu.WriteSingleRegister



(UnitID:-uid,

Quantity:=wqauntity,

MBAddr: = fregRegisterAddress,

cbLength:=SIZEOF (data_writefreq) .

pMemoryAddr:=ADR(data_writefreq) ,

Execute:-enable,

);


Analyse bisher:


1. Register-Adresse: Die Adresse 0x0007 für Frequency Setting ist laut Dokumentation korrekt.


2. Skalierung: Der Frequenzwert wird in 0.1 Hz gesendet (z. B. 62.0 Hz → 620).


3. Datenformat: Es wird ein UINT-Wert im Big-Endian-Format gesendet (High Byte zuerst).


4. Slave reagiert nicht: Obwohl der Schreibvorgang erfolgreich ist, zeigt das Slave-Gerät den Wert nicht an.





Fragen:


1. Was könnte die Ursache dafür sein, dass das Slave-Gerät den Frequenzwert nicht anzeigt, obwohl die Kommunikation erfolgreich ist?


2. Gibt es spezielle Anforderungen beim Schreiben in Register, die sowohl Lesen als auch Schreiben (R/W) erlauben?


3. Muss ich den Wert über ein weiteres Command bestätigen?


4. Hat jemand Erfahrung mit der DF-C 61XXX-Serie oder ähnlichen Geräten?





Ich freue mich über eure Tipps und Rückmeldungen! Vielen Dank vorab.





Beste Grüße,
 
Hallo,

zunächst einmal, Code bitte in Code Tags packen, zwecks besserer Lesbarkeit

Code:
FUNCTION_BLOCK Modbus_rtu

VAR_ INPUT
newVarInput: UINT;
freqValue : UINT;// Frequency value (e.g., 62.0 Hz -> 620 in 0.1Hz resolution)
voltValue : UINT;// Voltage value (e.g., 120.0V -> 1200 in 0.1V resolution)
outputCommand: UINT: // Command to control output (e.g., 1 for start output)
enable: BOOL;
END_VAR

VAR OUTPUT
bSuccess : BOOL; // Indicates if the operation was successful
bBusy: BOOL; // Indicates if the operation is in progress
bError : BOOL; // Indicates if an error occurred uiErrorID: UINT; // Error ID in case of failure
END VAR

VAR
fregRegisterAddress : UINT :=7; // Register address for frequency
voltRegisterAddress : UINT := 8; // Register address for voltage
outputRegisterAddress : WORD:= 9; // Register address for output control
wqauntity: WORD:=1;
uid: BYTE:=100;
data _write: WORD:=16#0001;// Outputstart
data_writefreq: WORD:=16#0258 // Example: Data returned as 0x0258 HEX 0x0258 = Value 600, the output frequency of device is 60.0Hz

ModbusRtu: ModbusRtuMasterV2_RL6x22B;

Execute: BOOL;

END VAR


ModbusRtu.WriteSingleRegister
(UnitID:-uid,
Quantity:=wqauntity,
MBAddr: =outputRegisterAddress,
cbLength:=SIZEOF (data_write) .
pMemoryAddr:=ADR(data_write) ,
Execute:-enable,
);



ModbusRtu.WriteSingleRegister
(UnitID:-uid,
Quantity:=wqauntity,
MBAddr: = fregRegisterAddress,
cbLength:=SIZEOF (data_writefreq) .
pMemoryAddr:=ADR(data_writefreq) ,
Execute:-enable,
);

Hast Du mal versucht nur die Frequenz zu schreiben?

Normalerweiser sind die Befehle nicht in einem Zyklus abgearbeitet, so daß nur ein Auftrag zur Zeit anliegen darf. D.h. erst dan eine Kommando senden, warten bis der Auftrag abgearbeitet ist (Busy Flag), dann den nächsten Auftrag.

Du mußt die beiden Kommandos, oder mehr, also gegeneinander verriegeln, z.B. mit einer state machine

Gruß
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich kenne deine Geräte und das ModbusRtu.WriteSingleRegister nicht, aber: bei Modbus können nicht mehrere Kommunikations-Aufträge gleichzeitig aktiv sein. Bevor ein weiterer Schreib/Lese-Auftrag aktiv wird, muss der vorherige Auftrag komplett beendet sein, inklusive Response oder Timeout vom RTU-Slave. Du musst die Kommunikation in Schrittketten implementieren mit Auswertung/Weiterschalten anhand der Rückmeldungen.
 
Hallo und vielen Dank für deine Anmerkung bezüglich Code in Code-Tags packen!
Ich habe versucht, nur die Frequenz zu schreiben, aber leider wird dieser Wert nicht auf meinem Slave-Gerät angezeigt. Die Werte, die ich zu Beginn auf meinem Slave-Gerät eingestellt habe, bleiben unverändert.
Könntest du mir bitte genauer erklären, wie ich die beiden Kommandos gegeneinander verriegeln kann? Ein Beispiel wäre ebenfalls sehr hilfreich!

Beste Grüße
 
Ich kenne deine Geräte und das ModbusRtu.WriteSingleRegister nicht, aber: bei Modbus können nicht mehrere Kommunikations-Aufträge gleichzeitig aktiv sein. Bevor ein weiterer Schreib/Lese-Auftrag aktiv wird, muss der vorherige Auftrag komplett beendet sein, inklusive Response oder Timeout vom RTU-Slave. Du musst die Kommunikation in Schrittketten implementieren mit Auswertung/Weiterschalten anhand der Rückmeldungen.
Hallo,

vielen Dank für deine Antwort und den Hinweis, dass bei Modbus nicht mehrere Kommunikationsaufträge gleichzeitig aktiv sein dürfen. Das erklärt wahrscheinlich die Probleme, die ich bisher hatte.

Du hast vollkommen recht, dass die vorherigen Aufträge zuerst beendet werden müssen, bevor ein neuer Auftrag gestartet wird. Ich verstehe, dass dies am besten mit einer Schrittketten-Logik umgesetzt wird, die anhand der Rückmeldungen entscheidet, wann der nächste Auftrag ausgeführt werden darf.

Es wäre sehr nett von dir, wenn du mir ein Beispiel geben oder mich genauer orientieren könntest, wie ich sicherstellen kann, dass der vorherige Auftrag vollständig abgeschlossen ist, bevor ich einen weiteren schalte. Besonders interessiert mich, wie ich die Rückmeldungen (Busy, Done, Error) in einer solchen Schrittfolge richtig verarbeite.



Vielen Dank im Voraus für deine Unterstützung!





Beste Grüße,
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

hier findest Du schon einmal einen Anfang für eine Schrittkette.

Ich würde aber den Abschnitt ganz unten innerhalb der If Abfrage iStepReading mit in den Schritt 1 packen.

Danach kann man mit dem Schema weitermachen oder aber in Schritt 1 zwischen verschiedenen Daten wählen.

Gruß
 
hier findest Du schon einmal einen Anfang für eine Schrittkette.

Ich würde aber den Abschnitt ganz unten innerhalb der If Abfrage iStepReading mit in den Schritt 1 packen.

Danach kann man mit dem Schema weitermachen oder aber in Schritt 1 zwischen verschiedenen Daten wählen.

Gruß
Hallo,
vielen Dank für die vorherigen Erklärungen und Hinweise. Ich habe nun einen weiteren Versuch unternommen, bei dem ich zunächst nur die Frequenz an das Modbus-Slave-Gerät geschrieben habe, und das hat funktioniert. wenn möchte ich mehrere Schreibaufträge nacheinander ausführen (Frequenz und Spannung), habe dafür meinen Code angepasst, stoße jedoch auf ein Problem. Die Zustandsmaschine bleibt bei iStepWriting = 2 hängen. Es scheint, dass der Zustand nicht wie erwartet wechselt.
Was könnte die Ursache sein, dass iStepWriting nicht über Zustand 2 hinaus wechselt?
Gruß
Code:
FUNCTION_BLOCK Modbus_rtu

VAR_ INPUT

newVarInput: UINT;

freqValue : UINT;// Frequency value (e.g., 62.0 Hz -> 620 in 0.1Hz resolution)

voltValue : UINT;// Voltage value (e.g., 120.0V -> 1200 in 0.1V resolution)

outputCommand: UINT: // Command to control output (e.g., 1 for start output)

enable: BOOL;

END_VAR



VAR OUTPUT

bSuccess : BOOL; // Indicates if the operation was successful

bBusy: BOOL; // Indicates if the operation is in progress

bError : BOOL; // Indicates if an error occurred uiErrorID: UINT; // Error ID in case of failure

END VAR



VAR

fregRegisterAddress : UINT :=7; // Register address for frequency

voltRegisterAddress : UINT := 8; // Register address for voltage

outputRegisterAddress : WORD:= 9; // Register address for output control

wqauntity: WORD:=1;

uid: BYTE:=100;

data _write: WORD:=16#0001;// Outputstart





ModbusRtu: ModbusRtuMasterV2_RL6x22B;

iStepwriting : INT:=0;

Execute: BOOL;



END VAR

//Programmteil

CASE iStepwriting OF

0:

ModbusRtu.Execute:=TRUE;

iStepwriting:=1;



1:

IF NOT ModbusRtu.BUSY THEN

ModbusRtu.Execute:=FALSE;

iStepwriting:=2;

END_IF



2:

ModbusRtu.WriteSingleRegister

(UnitID:-uid,

Quantity:=wqauntity,

MBAddr: =outputRegisterAddress,

cbLength:=SIZEOF (data_write) .

pMemoryAddr:=ADR(data_write) ,

Execute:=TRUE,

);

iStepwriting:=3;



3:

IF NOT Modbus.BUSY THEN

ModbusRtu.Execute:=FALSE;

iStepwriting:=4;

END_IF

4:



ModbusRtu.WriteSingleRegister

(UnitID:-uid,

Quantity:=wqauntity,

MBAddr: = fregRegisterAddress,

cbLength:=SIZEOF (freqValue) .

pMemoryAddr:=ADR(freqValue) ,

Execute:=TRUE,

);

iStepwriting:=5;



5:

IF NOT Modbus.BUSY THEN

ModbusRtu.Execute:=FALSE;

iStepwriting:=6;

END_IF

6:

ModbusRtu.WriteSingleRegister

(UnitID:-uid,

Quantity:=wqauntity,

MBAddr: = fregRegisterAddress,

cbLength:=SIZEOF (voltValue) .

pMemoryAddr:=ADR(voltValue) ,

Execute:=TRUE,

);

iStepwriting:=7;

IF NOT Modbus.BUSY THEN

ModbusRtu.Execute:=FALSE;

iStepwriting:=0;

END_IF





END_CASE
 
Hallo,

habe gerade erst gesehen, daß in dem verlinkten Beispiel auch noch kleine Fehler sind.

Deine Lösung ist schon fast richtig, nur kann sich das Busyflag nicht ändern, da der Baustein nicht mehr aufgerufen wird in Schritt 3. Da müßte die Schrittkette hängen bleiben, nicht Schritt 2.

Hier mal die Anpassung:

Code:
2:
  ModbusRtu.WriteSingleRegister
    (UnitID:-uid,
    Quantity:=wqauntity,
    MBAddr: =outputRegisterAddress,
    cbLength:=SIZEOF (data_write) .
    pMemoryAddr:=ADR(data_write) ,
    Execute:=TRUE,
    );

  IF NOT Modbus.BUSY THEN
    iStepwriting:=3;
  END_IF

3:
  ModbusRtu.WriteSingleRegister(Execute:=FALSE);
  iStepwriting:=4;
  
4:
  ModbusRtu.WriteSingleRegister
  (UnitID:-uid,
  Quantity:=wqauntity,
  MBAddr: = fregRegisterAddress,
  cbLength:=SIZEOF (freqValue) .
  pMemoryAddr:=ADR(freqValue) ,
  Execute:=TRUE,
  );
  
  IF NOT Modbus.BUSY THEN
    iStepwriting:=5;
  END_IF

5:
  ModbusRtu.WriteSingleRegister(Execute:=FALSE);
  iStepwriting:=6;
  
6:
  ModbusRtu.WriteSingleRegister
    (UnitID:-uid,
    Quantity:=wqauntity,
    MBAddr: = fregRegisterAddress,
    cbLength:=SIZEOF (voltValue) .
    pMemoryAddr:=ADR(voltValue) ,
    Execute:=TRUE,
    );

  IF NOT Modbus.BUSY THEN
    iStepwriting:=7;
  END_IF

7:
  ModbusRtu.WriteSingleRegister(Execute:=FALSE);
  iStepwriting:=0;

Man könnte die Funktion ModbusRtu.WriteSingleRegister() auch ohne weitere Argumente ausserhalb der Case Anweisung ausführen. Dann müßte man sonst nur die einzelnen Eingangswerte zuweisen. Diese Form der Schrittkette hier eignet sich dagenen, wenn man verschiedene Funktionen, z.b. lesen und schreiben, durchführen möchte.

Kann das hier nicht testen. Sollte aber größtenteils richtig sein.

Gruß
 
Zurück
Oben