Receiving DPV1 data
DPV1 services are, in addition to the cyclic connection, the acyclic data exchange. This is always initiated by the PROFIBUS master and can read or write data via this channel. This service is useful for transporting data that are rarely required or rarely change. As a rule, these are devices that can be parameterized at runtime, for example, and the user can then read or write these parameters.
With the CX7031, this channel is open and can therefore be used by the user in parallel with the cyclic data.
With the CX7031, reception takes place via the ADS notification function blocks in the PLC. If a DPV1 telegram is received, it is entered in the ADS notification and forwarded to the PLC and can then be evaluated with the corresponding function blocks, e.g. ADSWRITEIND for a write telegram. The user must also generate the response and can also return a corresponding error if the DPV1 data is incorrect, e.g. that the data length is incorrect or the DPV1 index is wrong.
The procedure is to call the ADSWRITEIND function block, if the VALID is set, data has been received, as other services may also use the ADS indication, it is recommended to check the AMS NetID where the ADS indication comes from. In the case of the CX7031, this is the AMS NetID of the CX7031 device. The port is then checked, as there may be 4 slaves in the CX7031, the port number can be used to determine from which slave the DPV1 service was received. The port is always 1000 + slave address. So you simply have to calculate the value of the port minus 1000 to get the PROFIBUS address of the slave. Next comes the IDXGRP, bit 31 is always set here, and the DPV1 slot number is also coded here. The IDXOFFS contains the DPV1 index number. If the DP-V1 service is correct, they then acknowledge with the corresponding ADS response and the result = 0. If the DP-V1 is invalid, the response must be <> 0 and is answered depending on the error; the error codes of the PROFIBUS standard must be observed here; these are described in the following table DP- V1 Error Response.
To enable forwarding to the PLC, the port of the PLC must be specified in TwinCAT; this is usually "851". The default value is "0", i.e. this service is disabled.
DPV1 Read
A DPV1-MSAC_C1 read indication is mapped in an ADS read indication as follows:
ADS read indication parameter | Meaning |
---|---|
Source-Net-ID (NETID) | Net-ID of the slave (see the device’s ADS tab) |
Source-Port (PORT) | 1000+slave address |
Invoke-ID (INVOKEID) | unique number, must be used for the response |
IndexGroup (IDXGRP) | Slot number (DPV1 parameter) (0x01...0xFC) |
IndexOffset (IDXOFFS) | Index (DPV1 parameter) (0x00...0xFF) |
Length (LENGTH) | Length of the data to be read (max. 236 bytes) |
An ADS read response is mapped in a DPV1-MSAC_C1 read response as follows:
ADS read response parameter | Meaning |
---|---|
Destination-Net-ID (NETID) | Net-ID of the slave (see the device’s ADS tab) |
Destination-Port (PORT) | 1000+slave address |
Invoke-ID (INVOKEID) | unique number, as with Indication |
Result (RESULT) | See DPV1 error response |
Length (LENGTH) | Length of data read |
Data (DATAADDR) | Read data |
DPV1-Write
A DPV1-MSAC_C1 write indication is mapped in an ADS write indication as follows:
ADS write indication parameter | Meaning |
---|---|
Source-Net-ID (NETID) | Net-ID of the slave (see the device’s ADS tab) |
Source-Port (PORT) | 1000+slave address |
Invoke-ID (INVOKEID) | unique number, must be used for the response |
IndexGroup (IDXGRP) | Slot number (DPV1 parameter) (0x01...0xFC) |
IndexOffset (IDXOFFS) | Index (DPV1 parameter) (0x00...0xFF) |
Length (LENGTH) | Length of the data to be written (max. 240 bytes) |
Data (DATAADDR) | Data to be written |
An ADS read response is mapped in a DPV1-MSAC_C1 read response as follows:
ADS read response parameter | Meaning |
---|---|
Destination-Net-ID (NETID) | Net-ID of the slave (see the device’s ADS tab) |
Destination-Port (PORT) | 1000+slave address |
Invoke-ID (INVOKEID) | unique number, as with Indication |
Result (RESULT) | See DPV1 error response |
Length (LENGTH) | Length of data read |
DPV1 error response:
Response: = 16#aaaa_yyxz (yy error code 2 manufacturer-specific, x error class, z error code)
"aaaa" optional, ADS error code, we recommend using the error code 0x0700 here.
Error-Class (x= Bit 4-7) | Error-Code (z=Bit 0-3) | Meaning |
---|---|---|
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 |
For example, if you want to report that the DPV1 index is incorrect, answer with 0x0000_00B0 in the response.
Code sample:
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