Hallo Wu Fu,
ich habe mit schon mal eine Verbindung zu zwei Danfoss FC300 über Modbus RTU (RS485) aufgebaut. Allerdings nur zum Test auf dem Schreibtisch. Ich habe mir damals zwei Bausteine geschrieben:
Der erste Baustein wird in einer zyklischen Task aufgerufen und ist für die Kommunikation verantwortlich.
Der zweite Baustein wird irgendwo im Programm aufgerufen und stellt die Status- und Steuerbits zur Verfügung.
Hier mal der Code:
Baustein in Zyklischer Task (bei mir 10ms):
PROGRAM Com_Danfoss
VAR
ModbusMaster: MODBUSMASTER_RTU;
i: BYTE := 1; (*Schleifenzähler*)
Slaveadresse: BYTE; (*Slaveadresse in der Schleife*)
Aktion_RW: BYTE; (*Aktion lesen=0 / schreiben=1 / nächster Slave=2*)
FunctionCode: BYTE;
StartAddress: WORD;
NumberOfPoints: WORD;
StartFunction: BOOL;
Fehler: BYTE;
SlaveError: ARRAY [1..SlaveAnzahl] OF enumMB_ERROR;
SlaveData: ARRAY [1..SlaveAnzahl] OF SlaveData;
FU_Data: ARRAY [1..SlaveAnzahl] OF FU_Data;
xxB: POINTER TO BYTE;
test: BOOL :=FALSE;
END_VAR
VAR CONSTANT
SlaveAnzahl: BYTE := 7; (*wenn Änderung, dann Initalwerte anpassen*)
SlaveAdr: ARRAY [1..SlaveAnzahl] OF BYTE := 1,2,3,4,5,6,7;
SlaveParameter: ARRAY [1..SlaveAnzahl] OF AktParRW := 7(( Lesen:=( FunctionCode:=1,
StartAddress:=32,
NumberOfPoints:=32),
Schreiben:=( FunctionCode:=15,
StartAddress:=0,
NumberOfPoints:=32)));
(*Schnittstelle für Danfoss FU*)
ComPort: BYTE := 2;
ComBaudrate: COM_BAUDRATE := BAUD_19200;
ComParity: COM_PARITY := PARITY_EVEN;
ComStopbits : COM_STOPBITS := STOPBITS_1;
ComBytesize : COM_BYTESIZE := BS_8;
ComFlowControl: COM_FLOW_CONTROL := HALFDUPLEX;
ComTimeOut: TIME := t#20ms;
END_VAR
CASE Aktion_RW OF
(*Daten vom Slave lesen*)
0: Slaveadresse := SlaveAdr[i]; (*Slaveadresse*)
FunctionCode := SlaveParameter[i].Lesen.FunctionCode; (*Funktionscode für lesen*)
StartAddress := SlaveParameter[i].Lesen.StartAddress; (*Startadresse Statuswort*)
NumberOfPoints := SlaveParameter[i].Lesen.NumberOfPoints; (*Bereichslänge*)
IF NOT StartFunction THEN (*Warte auf Antwort vom Slave*)
IF SlaveData[i].Lesen.Data[1] = FunctionCode (*Funktionscode in Antwort gleich*)
AND SlaveData[i].Lesen.Data[0] = Slaveadresse (*Slaveadresse in Antwort gleich*)
AND NOT BYTE_TO_BOOL(Fehler) (*kein Fehler*)
THEN
(*Zustandswort eintragen*)
xxB := ADR(FU_Data[i].Zustandswort); (*Pointer für Zielbits*)
xxB^ := SlaveData[i].Lesen.Data[3];
xxB := xxB + SIZEOF(SlaveData[i].Lesen.Data[3]);
xxB^ := SlaveData[i].Lesen.Data[4];
(*Istwert eintragen*)
xxB := ADR(FU_Data[i].Istwert); (*Pointer für Zielbits*)
xxB^ := SlaveData[i].Lesen.Data[5];
xxB := xxB + SIZEOF(SlaveData[i].Lesen.Data[5]);
xxB^ := SlaveData[i].Lesen.Data[6];
Aktion_RW := 1; (*dann schreiben*)
(* StartFunction := 1;*)
test := 0;
RETURN;
ELSE
IF BYTE_TO_BOOL(Fehler) AND test
THEN SlaveError[i] := Fehler;
Aktion_RW := 2;
END_IF;
END_IF;
StartFunction := 1;
test := 1;
END_IF;
(*Daten zum Slave schreiben*)
1: Slaveadresse := SlaveAdr[i]; (*Slaveadresse*)
FunctionCode := SlaveParameter[i].Schreiben.FunctionCode; (*Funktionscode für schreiben*)
StartAddress := SlaveParameter[i].Schreiben.StartAddress; (*Startadresse Steuerwort*)
NumberOfPoints := SlaveParameter[i].Schreiben.NumberOfPoints; (*Bereichslänge*)
IF SlaveData[i].Lesen.Data[1] = FunctionCode (*Funktionscode in Antwort gleich*)
AND SlaveData[i].Lesen.Data[0] = Slaveadresse (*Slaveadresse in Antwort gleich*)
AND NOT BYTE_TO_BOOL(Fehler) (*kein Fehler*)
THEN
(*Steuerwort eintragen*)
xxB := ADR(FU_Data[i].Steuerwort); (*Pointer für Zielbits*)
SlaveData[i].Schreiben.Data[0] := xxB^;
xxB := xxB + SIZEOF(SlaveData[i].Schreiben.Data[0]);
SlaveData[i].Schreiben.Data[1] := xxB^;
(*Sollwert eintragen*)
xxB := ADR(FU_Data[i].Sollwert); (*Pointer für Zielbits*)
SlaveData[i].Schreiben.Data[2] := xxB^;
xxB := xxB + SIZEOF(SlaveData[i].Schreiben.Data[2]);
SlaveData[i].Schreiben.Data[3] := xxB^;
Aktion_RW := 2; (*dann schreiben*)
(* StartFunction := 1;*)
RETURN;
ELSE
IF BYTE_TO_BOOL(Fehler)
THEN SlaveError[i] := Fehler;
Aktion_RW := 2;
END_IF;
StartFunction := 1;
END_IF;
(*Nächster Slave*)
2: IF NOT BYTE_TO_BOOL(Fehler)
THEN SlaveError[i] := Fehler;
Fehler := 0;
END_IF;
IF NOT StartFunction
THEN IF i >= SlaveAnzahl (*wenn letzter Slave erreicht*)
THEN i := 1; (*dann wieder erster Slave*)
ELSE i := i + 1; (*sonst nächster Slave*)
END_IF;
Aktion_RW := 0; (*danach lesen*)
test := 0;
END_IF;
END_CASE;
(*Modbusmaster für die Kommunikation mit den Slaves*)
ModbusMaster(
SlaveAddress:= Slaveadresse,
FunctionCode:= FunctionCode,
StartAddress:= StartAddress,
NumberOfPoints:= NumberOfPoints,
bCOM_PORT:= ComPort, (*VAR CONSTANT*)
cbCOM_BAUDRATE:= ComBaudrate, (*VAR CONSTANT*)
cpCOM_PARITY:= ComParity, (*VAR CONSTANT*)
csCOM_STOPBITS:= ComStopbits, (*VAR CONSTANT*)
cbsCOM_BYTESIZE:= ComBytesize, (*VAR CONSTANT*)
cfCOM_FLOW_CONTROL:= ComFlowControl, (*VAR CONSTANT*)
TimeOut:= ComTimeOut, (*VAR CONSTANT*)
StartFunction:= StartFunction,
ReceiveBuffer:= SlaveData[i].Lesen,
SendData:= SlaveData[i].Schreiben,
Error=> Fehler);
Baustein für jeden Fu aufrufen:
FUNCTION_BLOCK Schnittstelle_Danfoss
VAR_INPUT
(* Festsollwertanwahl_LSB: BOOL;
Festsollwertanwahl_MSB: BOOL;
Keine_DC_Bremse: BOOL;
Kein_Freilaufstopp: BOOL;*)
Kein_Schnellstopp: BOOL;
(* Keine_Freq_speichern: BOOL;*)
Start: BOOL;
Reset: BOOL;
JOG: BOOL;
(* Rampe_2: BOOL;
Daten_gueltig: BOOL;
Relais_1: BOOL;
Relais_2: BOOL;
Parametersatzwahl_LSB: BOOL;
Parametersatzwahl_MSB: BOOL;*)
Reversierung: BOOL;
rSollwert: REAL;
END_VAR
VAR_OUTPUT
Regler_bereit: BOOL;
Frequenzumrichter_betriebsbereit: BOOL;
Sicherheitsverriegelung: BOOL;
Alarm: BOOL;
Warnung: BOOL;
Ist_gleich_Soll: BOOL;
Autobetrieb: BOOL;
In_Freq_Bereich: BOOL;
Motor_ein: BOOL;
Spannungswarnung: BOOL;
Stromgrenze: BOOL;
Warnung_Uebertemp: BOOL;
rIstwert: REAL;
END_VAR
VAR
rSoll: REAL;
END_VAR
VAR_IN_OUT
FU_Data: FU_Data;
END_VAR
(*## Daten zu FU schreiben ##*)
FU_Data.Steuerwort.0 := FALSE; (*Festsollwertanwahl_LSB*)
FU_Data.Steuerwort.1 := FALSE; (*Festsollwertanwahl_MSB*)
FU_Data.Steuerwort.2 := TRUE; (*Keine_DC_Bremse*)
FU_Data.Steuerwort.3 := TRUE; (*Kein_Freilaufstopp*)
FU_Data.Steuerwort.4 := Kein_Schnellstopp;
FU_Data.Steuerwort.5 := TRUE; (*Keine_Freq_speichern*)
FU_Data.Steuerwort.6 := Start;
FU_Data.Steuerwort.7 := Reset;
FU_Data.Steuerwort.8 := JOG;
FU_Data.Steuerwort.9 := FALSE; (*Rampe_2*)
FU_Data.Steuerwort.10 := TRUE; (*Daten_gueltig*)
FU_Data.Steuerwort.11 := FALSE; (*Relais_1*)
FU_Data.Steuerwort.12 := FALSE; (*Relais_2*)
FU_Data.Steuerwort.13 := FALSE; (*Parametersatzwahl_LSB*)
FU_Data.Steuerwort.14 := FALSE; (*Parametersatzwahl_MSB*)
FU_Data.Steuerwort.15 := Reversierung;
(*Engangsignal auf Grenzwerte begrenzen*)
IF rSollwert > 100.0
THEN rSoll := 100.0;
ELSE IF rSollwert < 0.0
THEN rSoll := 0.0;
ELSE rSoll := rSollwert;
END_IF;
END_IF;
(*FU_Sollwert berechnen*)
FU_Data.Sollwert := REAL_TO_WORD(rSoll*163.8375);
(*## Daten von FU lesen ##*)
Regler_bereit := FU_Data.Zustandswort.0;
Frequenzumrichter_betriebsbereit := FU_Data.Zustandswort.1;
Sicherheitsverriegelung := FU_Data.Zustandswort.2;
Alarm := FU_Data.Zustandswort.3;
Warnung := FU_Data.Zustandswort.7;
Ist_gleich_Soll := FU_Data.Zustandswort.8;
Autobetrieb := FU_Data.Zustandswort.9;
In_Freq_Bereich := FU_Data.Zustandswort.10;
Motor_ein := FU_Data.Zustandswort.11;
Spannungswarnung := FU_Data.Zustandswort.13;
Stromgrenze := FU_Data.Zustandswort.14;
Warnung_Uebertemp := FU_Data.Zustandswort.15;
(*FU_Istwert berechnen*)
rIstwert := FU_Data.Istwert/163.8375;
Der Code beider Bausteine ist noch nicht ausgereift, aber vielleich hilft er ja jemandem.
Ich bin für Kritik, Verbesserungen und Anregungen offen
Grüße
Tobi