TwinCat: Modbus Register Lesen und Schreiben

Beckhoff_SPS

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

ich kann mit meinem Programm mit TwinCat 3 sowohl über Modbus Register lesen, als auch Register schreiben. Nun würde ich aber gerne erst ein Register lesen und anschließend ein Register schreiben. Wie bekomme ich dies hin? Ist das im selben PRG möglich? Wenn ja, wie?

Danke für die Hilfe
 
Das ist mein Code:

Variablenteil:

PROGRAM ModbusRTU_Master_EL6021

VAR
ModbusRTU_Master: ModbusRtuMasterV2_KL6x22B;

Test: WORD := 1300;

TestZwei: WORD;

bRead: BOOL := 0;

bBusy: BOOL;
bError: BOOL;
nErrID: Tc2_ModbusRTU.MODBUS_ERRORS;
cbRead: UINT;

fbTON1: TON;
Nummer: INT := 1;

END_VAR



Programmteil:
if (ModbusRTU_Master.BUSY = False and Nummer=1) then
ModbusRTU_Master.WriteSingleRegister(
UnitID:= 1,
Quantity:= 1,
MBAddr:= 16#4001, //Frequenz
cbLength:= SIZEOF(Test),
pMemoryAddr:= ADR(Test),
AuxQuantity:= ,
AuxMBAddr:= ,
AuxcbLength:= ,
pAuxMemoryAddr:= ,
Execute:= bRead,
Timeout:= T#5S,
BUSY=> bBusy,
Error=> bError,
ErrorId=> nErrID,
cbRead=> cbRead);
nummer :=2;
END_IF


if (ModbusRTU_Master.BUSY = False and Nummer=2) then
ModbusRTU_Master.ReadRegs(
UnitID:= 1,
Quantity:= 1,
MBAddr:= 16#0D10, //Modultemperatur
cbLength:= SIZEOF(TestZwei),
pMemoryAddr:= ADR(TestZwei),
AuxQuantity:= ,
AuxMBAddr:= ,
AuxcbLength:= ,
pAuxMemoryAddr:= ,
Execute:= bRead,
Timeout:= T#5S,
BUSY=> bBusy,
Error=> bError,
ErrorId=> nErrID,
cbRead=> cbRead);
nummer :=2;

END_IF

fbTON1(IN:= NOT fbTON1.Q, PT:= T#1S, Q=> bRead, ET=> );


Hier wird das Schreiben zwar ausgeführt, allerdings wird nicht gelesen. Wo liegt mein Denkfehler?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Der Fehler von Dir ist ein Klassiker und hier im Forum schon oft diskutiert worden.
Schau Dir mal an, wie die FBs für Modbus funktionieren und aufgerufen werden müssen, vielleicht kommst Du selber drauf, ansonsten helfe ich gerne weiter.
Dein Konstrukt wie Du das Execute setzt solltest Du übrigens auch überdenken.

Gesendet von meinem SM-G973F mit Tapatalk
 
Zuletzt bearbeitet:
Danke für die Antwort, ich stehe aber leider auf dem Schlauch und finde den Fehler nicht. Kannst du mir vielleicht weiterhelfen?
 
Dann werde ich Dich mal ein wenig schubsen, hoffentlich in die richtige Richtung.
Es gibt Funktionsbausteine die benötigen zur Bearbeitung mehr als einen Zyklus. Bei einigen ist dies prinzipbedingt und damit offensichtlich, bei anderen eher nicht oder nicht so direkt. Zur ersteren Gruppe gehören z.B. Flankenauswertungen und Timer, zur letzteren z.B. Bausteine zur Datenübertragung, wie eben auch die Modbusbausteine. Alle diese Bausteine werden aber nicht einfach irgendwann gestartet, laufen dann fröhlich im Hintergrund weiter und irgendwann ändern sich wie von Geisterhand deren Ausgänge, sondern die Bausteine müssen in jedem Zyklus aufgerufen werden. Diesen Fehler haben schon viele vor Dir gemacht und es werden auch noch viele nach Dir diesen Fehler machen, hier im Forum liest man davon immer wieder.
Erstmal denke ich hast Du bei Deinem Beitrag einen Tippfehler, denn bei beiden IF-Abfragen wird nummer auf 2 gesetzt und dann würde nach dem ersten Zyklus nur noch die IF-Abfrage zum Lesen ausgeführt, ich denke mal bei der zweiten IF-Abfrage sollte es eine 1 sein. Übrigens nutze bitte zum Einfügen von Code den Code-Tag, das ist der Button mit dem #.
Dein Timer Konstrukt ist auch etwas seltsam, was willst Du damit erreichen? Ich mag mich irren, aber am besten man wartet das Senden erst ab ehe man etwas empfängt, aber hier kann jemand anderes vielleicht noch etwas zu beisteuern.
Doch nun zu Deinem anderen Fehler. Wie erwähnt müssen die Bausteine in jedem Zyklus aufgerufen werden, was bei Dir jedoch nicht der Fall ist. Sobald einer der beiden Bausteine innerhalb der IF-Abfrage aufgerufen wurde geht dessen BUSY auf TRUE und keine IF-Abfrage ist mehr erfüllt und der Baustein wird nicht mehr ausgeführt und wird nie fertig. Innerhalb einer Abfrage, einer CASE-Anweisung, usw. sollte man nie die Aufrufe solcher FBs platzieren, sondern nur deren Eingänge bearbeiten und deren Ausgänge auswerten, der Aufruf sollte immer außerhalb erfolgen.
Du könntest z.B. eine IF/ELSIF Reihe erstellen. Wenn nummer = 1 und BUSY des Empfangsbausteins = FALSE setze Execute des Empfangsbausteins auf FALSE, alle Daten für das Senden setzen und Execute des Sendebausteins auf TRUE setzen und setze nummer auf 2. Dann im ELSIF auf nummer gleich 2 und BUSY des Sendebausteins auf FALSE abfragen,
Execute des
Sendebausteins auf FALSE setzen und alle Daten für das Empfangen setzen, Execute des Empfangsbausteins auf TRUE setzen und nummer auf 1, fertig. Aber dann sendest Du halt ständig das Selbe.
Übrigens solltest Du auch noch Fehlerfälle auswerten.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke für deine Hilfe! Ich bin nun ein ganzes Stück weiter, allerdings scheint der Code immernoch nicht ganz korrekt zu sein. Die BUSY Variable wechselt leider nie auf true und dementsprechend passiert auch leider nichts. Ich habe auch versucht eine Fehlerabfrage einzubauen.
Hier mein Code:

Code:
CASE nFunction OF
    
3:
                               ModbusRTU_Master(
                                            UnitID:= 1, 
                                            Quantity:= 1, 
                                             MBAddr:= 16#4001,    //Modultemperatur: 16#0D10  Ausgangsfrequenz: 16#0D00  Sollfrequenz:  16#0D02
                                            cbLength:= SIZEOF(TestZwei), 
                                            pMemoryAddr:= ADR(TestZwei), 
                                            AuxQuantity:= , 
                                            AuxMBAddr:= , 
                                            AuxcbLength:= , 
                                            pAuxMemoryAddr:= , 
                                            //Execute := TRUE, 
                                            Timeout:= T#5S, 
                                            BUSY=> bBusy, 
                                            Error=> bError, 
                                            ErrorId=> nErrID, 
                                            cbRead=> cbRead); 
                                ModbusRTU_Master.ReadRegs(Execute := TRUE);
                               IF NOT ModbusRTU_Master.BUSY THEN
                                               ModbusRTU_Master(Execute := FALSE);
                                               IF NOT ModbusRTU_Master.Error THEN
                                                               nErrID := 0;
                                                               nFunction := 6;
                                               ELSE
                                                               bError := TRUE;
                                                               nErrorcase := 3;
                                                               nErrID := ModbusRTU_Master.ErrorId;
                                               END_IF
                               END_IF
         
6:
                               ModbusRTU_Master(
                                            UnitID:= 1, 
                                            Quantity:= 1, 
                                             MBAddr:= 16#4001,    //Modultemperatur: 16#0D10  Ausgangsfrequenz: 16#0D00  Sollfrequenz:  16#0D02
                                            cbLength:= SIZEOF(Test), 
                                            pMemoryAddr:= ADR(Test), 
                                            AuxQuantity:= , 
                                            AuxMBAddr:= , 
                                            AuxcbLength:= , 
                                            pAuxMemoryAddr:= , 
                                            //Execute := TRUE, 
                                            Timeout:= T#5S, 
                                            BUSY=> bBusy, 
                                            Error=> bError, 
                                            ErrorId=> nErrID, 
                                            cbRead=> cbRead);
                                ModbusRTU_Master.WriteSingleRegister(Execute := TRUE);
                               IF NOT ModbusRTU_Master.BUSY THEN
                                               ModbusRTU_Master(Execute := FALSE);
                                               IF NOT ModbusRTU_Master.Error THEN
                                                               nErrID := 0;
                                                               nFunction := 3;
                                               ELSE
                                                               bError := TRUE;
                                                               nErrorcase := 6;
                                                               nErrID := ModbusRTU_Master.ErrorId;
                                               END_IF
                               END_IF

END_CASE;

Kann es sein, dass ich execute nicht korrekt setze und der Baustein so keine steigende Flanke am Eingang sieht? Oder wo liegt mein Fehler?
 
Zuletzt bearbeitet:
Erstmal führst Du den FB noch immer innerhalb der CASE-Anweisung aus. Wie schon geschrieben sollte der Aufruf bestimmter FBs außerhalb von IF-Anweisungen und CASE-Anweisungen erfolgen und innerhalb von IF und CASE nur die Eingänge gesetzt werden. Dann rufst Du den FB falsch auf, wie in der Anleitung zu lesen ist, erfolgt der Aufruf über Aktionen und nur über diese, das heißt nur über ModbusRTU_Master.ReadRegs oder ModbusRTU_Master.WriteSingleRegister und nicht auch über ModbusRTU_Master und schon gar nicht parallel.
 
Zuletzt bearbeitet:
Anbei mal ein kondensierter Code Ausschnitt auf von einem Programm von mir, in dem ich ModBus RTU verwende. Ich habe eine ganze Menge code gelöscht, der nichts direkt mit der Modbus Kommunikation zu tun hat. Ich hoffe das noch alle relevanten Anweisungen im Schnipsel enthalten sind ....

Code:
VAR
      fbModBusRtuMaster: ModbusRtuMaster_KL6x22B;
      arrRxData     :ARRAY[0..10] OF WORD ;    
      nRegister :WORD;
      nCount : WORD;
END_VAR


fbModBusRtuMaster.UnitID := nStationAddress;
fbModBusRtuMaster.Timeout := T#5S;

CASE iStepReading OF 
    0:
        IF bReadData AND iStepWriting = 0 AND NOT bWriteData THEN
            iStepReading := 1;
        END_IF

    1:
        fbModBusRtuMaster.Execute := FALSE;
        iStepReading := 2;
       
    2:
        fbModBusRtuMaster.MBAddr := nRegister;
        fbModBusRtuMaster.Quantity := nCount;
        fbModBusRtuMaster.Execute := TRUE;           
        
        IF fbModBusRtuMaster.BUSY = TRUE THEN iStepReading := 3; END_IF
        
    3:
        IF NOT fbModBusRtuMaster.BUSY THEN
            IF fbModBusRtuMaster.Error = TRUE THEN
                iStepReading := 66;
            ELSE
                iErrorCounter := 0;
                fbModBusRtuMaster.Execute := FALSE;  
                iStepReading := 0;
            END_IF    
        END_IF
        
    66:
        iErrorCounter := iErrorCounter +1;
        iStepReading := 0;
        
END_CASE


IF iStepReading > 0 THEN    
    fbModBusRtuMaster.cbLength := SIZEOF(arrRxData);
    fbModBusRtuMaster.pMemoryAddr := ADR(arrRxData);    
    fbModBusRtuMaster.ReadRegs();
END_IF
 
Zurück
Oben