DPV1-Daten empfangen
DPV1-Dienste sind neben der zyklischen Verbindung der azyklische Datenaustausch. Dieser wird immer vom PROFIBUS-Master initiiert und kann Daten über diesen Kanal lesen oder schreiben. Dieser Dienst ist sinnvoll, um Daten zu transportieren, die nur selten benötigt werden oder sich selten ändern. In der Regel sind dies z. B. Geräte, die zur Laufzeit parametriert werden können, diese Parameter kann der Anwender dann lesen oder schreiben.
Beim CX7031 steht dieser Kanal offen zur Verfügung und kann somit vom Anwender parallel zu den zyklischen Daten genutzt werden.
Der Empfang erfolgt beim CX7031 über die ADS-Notification-Bausteine in der SPS. Wird ein DPV1-Telegramm empfangen, so wird dieses in die ADS-Notification eingetragen und an die SPS weitergeleitet und kann dann mit den entsprechenden Bausteinen z. B. ADSWRITEIND bei einem Schreibtelegramm ausgewertet werden. Der Anwender muss auch die Antwort generieren und kann auch bei fehlerhaften DPV1-Daten einen entsprechenden Fehler zurückgeben, z. B., dass die Datenlänge nicht stimmt oder der DPV1-Index falsch ist.
Der Ablauf ist der Aufruf des Bausteins ADSWRITEIND, ist der VALID gesetzt, wurden Daten empfangen, da evtl. auch andere Dienste die ADS-Indication verwenden, empfiehlt es sich die AMS NetID zu prüfen, woher die ADS-Indication kommt. Im Falle des CX7031 ist dies die AMS NetID des CX7031 Device. Dann wird der Port überprüft, da im CX7031 evtl. 4 Slaves vorhanden sind, kann über die Portnummer festgestellt werden, von welchem Slave der DPV1-Dienst empfangen wurde. Der Port ist immer 1000 + Slave-Adresse. Man muss also einfach den Wert des Ports minus 1000 berechnen und erhält die PROFIBUS-Adresse des Slaves. Als nächstes kommt der IDXGRP, hier ist immer das Bit 31 gesetzt, zusätzlich ist hier die DPV1 Slot Nummer codiert. Der IDXOFFS enthält die DPV1 Indexnummer. Ist der DPV1-Dienst korrekt, so quittieren sie anschließend mit der entsprechenden ADS-Response und dem Ergebnis = 0. Ist der DPV1 ungültig, so muss die Response <> 0 sein und wird je nach Fehler beantwortet, hier sind die Fehlercodes der PROFIBUS-Norm zu beachten, diese sind in der folgenden Tabelle DP- V1 Error Response beschrieben.
Um die Weiterleitung an die SPS zu ermöglichen, muss in TwinCAT der Port der SPS angegeben werden, dies ist in der Regel „851“. Der Standardwert ist „0“, d. h. dieser Dienst ist deaktiviert.
DPV1-Read
Eine DPV1-MSAC_C1-Read-Indication wird wie folgt auf eine ADS-Read-Indication abgebildet:
ADS-Read-Indication-Parameter | Bedeutung |
---|---|
Source-Net-ID (NETID) | Net-ID des Slaves (s. Karteireiter ADS des Devices) |
Source-Port (PORT) | 1000+Slave-Adresse |
Invoke-ID (INVOKEID) | eindeutige Nummer, muss für den Response verwendet werden |
IndexGroup (IDXGRP) | Slot-Number (DPV1-Parameter) (0x01...0xFC) |
IndexOffset (IDXOFFS) | Index (DPV1-Parameter) (0x00...0xFF) |
Length (LENGTH) | Länge der auszulesenden Daten (max. 236 Byte) |
Eine ADS-Read-Response wird wie folgt auf eine DPV1-MSAC_C1-Read-Response abgebildet:
ADS-Read-Response-Parameter | Bedeutung |
---|---|
Destination-Net-ID (NETID) | Net-ID des Slaves (s. Karteireiter ADS des Devices) |
Destination-Port (PORT) | 1000+Slave-Adresse |
Invoke-ID (INVOKEID) | eindeutige Nummer, wie bei Indication |
Result (RESULT) | Siehe DPV1-Fehler-Response |
Length (LENGTH) | Länge der gelesenen Daten |
Data (DATAADDR) | gelesene Daten |
DPV1-Write
Eine DPV1-MSAC_C1-Write-Indication wird wie folgt auf eine ADS-Write-Indication abgebildet:
ADS-Write-Indication-Parameter | Bedeutung |
---|---|
Source-Net-ID (NETID) | Net-ID des Slaves (s. Karteireiter ADS des Devices) |
Source-Port (PORT) | 1000+Slave-Adresse |
Invoke-ID (INVOKEID) | eindeutige Nummer, muss für den Response verwendet werden |
IndexGroup (IDXGRP) | Slot-Number (DPV1-Parameter) (0x01...0xFC) |
IndexOffset (IDXOFFS) | Index (DPV1-Parameter) (0x00...0xFF) |
Length (LENGTH) | Länge der zu schreibenden Daten (max. 240 Byte) |
Data (DATAADDR) | zu schreibende Daten |
Eine ADS-Read-Response wird wie folgt auf eine DPV1-MSAC_C1-Read-Response abgebildet:
ADS-Read-Response-Parameter | Bedeutung |
---|---|
Destination-Net-ID (NETID) | Net-ID des Slaves (s. Karteireiter ADS des Devices) |
Destination-Port (PORT) | 1000+Slave-Adresse |
Invoke-ID (INVOKEID) | eindeutige Nummer, wie bei Indication |
Result (RESULT) | Siehe DPV1-Fehler-Response |
Length (LENGTH) | Länge der gelesenen Daten |
DPV1-Fehler-Response:
Response: = 16#aaaa_yyxz (yy Fehler Code 2 Herstellerspezifisch, x Error Class, z Error Code)
„aaaa“ optional, ADS-Fehlercode, wir empfehlen hier den Fehlercode 0x0700 zu verwenden.
Error-Class (x= Bit 4-7) | Error-Code (z=Bit 0-3) | Bedeutung |
---|---|---|
0xA | 0x0 | Application, Read Error |
0x1 | Application, Write Error | |
0x2 | Application, Module Failure | |
0x8 | Application, Version Conflict | |
0x9 | Application, Feature Not Supported | |
0xB | 0x0 | Access, Invalid Index |
0x1 | Access, Write Length Error | |
0x2 | Access, Invalid Slot | |
0x3 | Access, Type Conflict | |
0x4 | Access, Invalid Area | |
0x5 | Access, State Conflict | |
0x6 | Access, Access Denied | |
0x7 | Access, Invalid Range | |
0x8 | Access, Invalid Parameter | |
0x9 | Access, Invalid Type | |
0xC | 0x0 | Resource, Read Constrain Conflict |
0x1 | Resource, Write Constrain Conflict | |
0x2 | Resource, Busy | |
0x3 | Resource, Unavailable |
Beispiel sie wollen mitteilen das der DPV1-Index falsch ist so antworten sie im Response mit einer 0x0000_00B0.
Code-Beispiel:
VAR_GLOBAL
CX7031_NetID AT %I*:AMSNETID ; //Link to the AMS Net ID from the CX7031 PROFIBUS Interface
END_VAR
PROGRAM pro_DPV1_Service
VAR
i : INT; //Count up if something is coming in the Write Indication
sNetId :STRING;
nPort :UINT;
udInvokeId :UDINT;
iCase :INT;
udGroup :UDINT;
udOffset :UDINT;
udLenght :UDINT;
ptrByte :POINTER TO BYTE; (* Datenpointer *)
fbAdsWriteInd :ADSWRITEIND;
fbAdsWriteRes :ADSWRITERES;
bExecute :BOOL;
result :UDINT; //Bit 0..7 Error Code 1: Class/Code, Bit 8..15 Error Code 2, Bit 16..31 not necessary
nResult :UDINT;
DPV1_Write :ARRAY[0..255] OF BYTE;
ADSREADINDEX :ADSREADINDEX;
ADSREADRES :ADSREADRES;
sNetId_READ :STRING(23);
nPort_READ :UINT;
udInvokeId_READ :UDINT;
udGroup_READ :DWORD;
udOffset_READ :UDINT;
udLenght_READ :UDINT;
ptrByte_READ :Tc2_System.UXINT;
DATAADDR :INT;
DataRead :ARRAY[0..199] OF BYTE;
iRead :INT;
END_VAR
//Read a DPV1 Write here we receive DV1 write data
fbAdsWriteInd(
CLEAR :=,
VALID =>,
NETID =>,
PORT =>,
INVOKEID =>,
IDXGRP =>,
IDXOFFS =>,
LENGTH =>,
DATAADDR =>);
IF fbAdsWriteInd.VALID THEN //wait until valid is True then we receive data
(* You have to check the NETID and port number if you use ADS notification also for other things *)
i := i + 1; //Count up if something is coming in the Indication
sNetId := fbAdsWriteInd.NETID; //AMD Net ID from device (CX7031)
nPort := fbAdsWriteInd.PORT; //PB Adresse + 1000 dec
udInvokeId := fbAdsWriteInd.INVOKEID; //for the Response
udGroup := UDINT_TO_DWORD(fbAdsWriteInd.IDXGRP);
udOffset := fbAdsWriteInd.IDXOFFS; //Prm or Cfg Data or DPV1 Services
udLenght := fbAdsWriteInd.LENGTH; //Len of the data
ptrByte := fbAdsWriteInd.DATAADDR; //Pointer from the data
//Result without Error, if you want so send an Error use "1"
IF F_CreateAmsNetId(nIds:=gvl.CX7031_NetID )=sNetId THEN //Check the AMSNet ID from CX7031 -> IF sNetId='5.23.4.5.4.1' THEN ....
IF nPort=1000 + 22 THEN //Slave with Adress 22
CASE udOffset OF //Offset which kind of data is comming
20: //For DP-V1 Services (20 is here the PB Index)
MEMCPY(ADR(DPV1_Write),ptrByte,udLenght);
fbAdsWriteRes.RESULT := 0;
ELSE
fbAdsWriteRes.RESULT := 16#0700_00B0 ; //Access, Invalid Index;
END_CASE
END_IF
IF iCase = 0 THEN
iCase := 10; //Send response
END_IF
END_IF
END_IF
CASE iCase OF
0: (* WAIT *);
10: (* send ADS-Response *)
fbAdsWriteRes(
NETID := sNetId,
PORT := nPort,
INVOKEID := udInvokeId,
RESULT := ,
RESPOND := TRUE);
fbAdsWriteInd(CLEAR:=TRUE); (* confirm ADS-Indication *)
iCase := 20;
20: fbAdsWriteRes(RESPOND:=FALSE);
fbAdsWriteInd(CLEAR:=FALSE);
iCase := 0;
END_CASE
//READ Indication for DPV1 Read Data
ADSREADINDEX(
CLEAR:= ,
MINIDXGRP:= ,
VALID=> ,
NETID=> ,
PORT=> ,
INVOKEID=> ,
IDXGRP=> ,
IDXOFFS=> ,
LENGTH=> );
IF ADSREADINDEX.VALID THEN
sNetId_READ :=ADSREADINDEX.NETID; //AMD Net ID from device (CX7031)
nPort_READ :=ADSREADINDEX.PORT; //PB Adresse + 1000 dec
udInvokeId_READ :=ADSREADINDEX.INVOKEID; //for the Response
udGroup_READ :=UDINT_TO_DWORD(ADSREADINDEX.IDXGRP);
udOffset_READ :=ADSREADINDEX.IDXOFFS; //Prm or Cfg Data
udLenght_READ :=ADSREADINDEX.LENGTH; //Len of the data
iRead:=10;
IF F_CreateAmsNetId(nIds:=gvl.CX7031_NetID )=sNetId_READ THEN //Check the AMSNet ID from CX7031 -> IF sNetId='5.23.4.5.4.1' THEN ....
IF nPort_READ =1022 THEN //Check Slave Addr 22
IF (16#FF AND udGroup_READ) = 2 THEN //Check Slot Number
IF udOffset_READ=16#15 THEN //Check Index Number, here is the Index 21=0x15
DataRead[0]:=DPV1_Write[0]+1;
ADSREADRES.RESULT:=0; //no Error
ELSE
ADSREADRES.RESULT :=16#0700_00B0 ; //Access, Invalid Index
END_IF
ELSE
ADSREADRES.RESULT :=16#0700_00B2 ; //Access, Invalid Slot
END_IF
END_IF
END_IF
END_IF
CASE iRead OF
0: //Wait
10:
ADSREADRES(
NETID:=sNetId_READ ,
PORT:=nPort_READ ,
INVOKEID:=udInvokeId_READ ,
RESULT:= ,
LEN:=20 ,
DATAADDR:=ADR(DataRead) ,
RESPOND:=TRUE );
ADSREADINDEX(Clear:=TRUE);
iRead:=20;
20: ADSREADRES(RESPOND:=FALSE);
ADSREADINDEX(Clear:=FALSE);
iRead:=0;
END_CASE