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-Daten empfangen 1:
DPV1-Daten empfangen: Aktivierung der Kommunikation unter TwinCAT.

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