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.

Receiving DPV1 data 1:
Receiving DPV1 data: Activation of communication under TwinCAT.

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