Beispiel: Kommunikation BC/BX<->PC/CX (F_SwapRealEx)

Das Beispiel zeigt die Verwendung der F_SwapRealEx-Funktion. Dieses Beispiel beinhaltet zwei Komponenten: TwinCAT 2.xx BC/BX (Busklemmen Controller)-Applikation and TwinCAT 3.xx PC/CX (x86)-Applikation. Die PC/CX Applikation liest/schreibt eine Strukturvariable aus/in den Merkerbereich des BC's/BX's. Die Strukturvariable beinhaltet REAL-Elemente. Diese müssen vor der Verwendung auf dem PC/CX oder vor dem Versand zum BC/BX in das richtige Format konvertiert werden.

Hier können Sie die kompletten Sourcen entpacken:

TwinCAT 2.xx - BC/BX (Busklemmen Controller) Applikation/Projektdatei: DataExBC.zip

TwinCAT 3.xx - PC/CX (x86, x64, ARM) Applikation/Archivdatei: DataExPC.zip

Systemanforderungen:

Projekt herunterladen

Benutzen Sie die TwinCAT 2.xx PLC um die BC/BX-Applikation in das Laufzeitsystem eines Busklemmen Controllers runterzuladen (z.B. BC9000). Erstellen Sie ein Bootprojekt und starten Sie die SPS. Im nächsten Schritt benutzen Sie TwinCAT 3.xx und erstellen ein neues XAE-Projekt. Importieren Sie die Archivdatei in TwinCAT XAE mit einem rechten Mausklick auf den PLC- Knoten und dann auf Add existing item..

Damit auf den BC/BX (Busklemmen Controller) über ADS zugegriffen werden kann muss dieser als Gerät in die Liste der TwinCAT AMS Route-Verbindungen (routes) eingetragen werden. Erstellen Sie bitte eine neue statische Route (folgen Sie den Schritten im Bild). Die AmsNetId und IP Adresse des BC/BX muss passend konfiguriert werden (vergessen Sie bitte nicht die AmsNetID im SPS-Programcode).

Beispiel: Kommunikation BC/BX<->PC/CX (F_SwapRealEx) 1:

Zu beachten!

Der BC/BX (Busklemmen Controller) und PC/CX (x86, x64, ARM) hat eine unterschiedliche Speicherausrichtung (data alignment). Definieren Sie bitte Strukturen mit 8-Byte Speicherausrichtung, wenn Sie diese für den Datenaustausch BC/BX <-> PC/CX verwenden wollen.

Die verwendete Strukturvariablendefinition auf beiden Systemen:

(* 8 byte aligned structure, byte size := 152 byte *)
TYPE ST_FrameData :
STRUCT
    nFrameSize: DWORD;(*Frame byte size, member byte size := 4 byte*)
    nTxFrames : DWORD;(*Tx frame number, member byte size := 4 byte*)
    nRxFrames : DWORD;(*Rx frame number, member byte size := 4 byte*)
    nCounter  : DWORD;(*Number value, member byte size := 4 byte*)
    fU        : REAL;(*Floating point number, member byte size := 4 byte*)
    fV        : REAL;(*Floating point number, member byte size := 4 byte*)
    fW        : REAL;(*Floating point number, member byte size := 4 byte*)
    aFloats   : ARRAY[0..9] OF REAL;(* Array of floating point numbers, array byte size := 40 byte*)
    sMsg      : STRING;(*String variable, member byte size := 81 byte incl. String null delimiter*)
    bEnable   : BOOL;(*Boolean flag, member byte size := 1 byte*)
    nRsv0     : BYTE;(*Reserved byte to meet the 8 byte alignment, member byte size := 1 byte*)
    nCRC      : BYTE;(*CRC checksum byte, member byte size := 1 byte*)
END_STRUCT
END_TYPE

BC/BX (Busklemmen Controller) Applikation

Nach jedem Schreibzugriff vom PC/CX wird die Datenlänge und Checksumme überprüft. Danach werden neue Zufallswerte für den Lesezugriff generiert die zusätzlich mit einer einfachen Checksumme versehen werden.

PROGRAM MAIN
VAR
    stRxFrame AT%MB500   : ST_FrameData;(* Data transported from PC/CX (x86) to BC/BX (Bus Terminal Controller) *)
    stTxFrame AT%MB0     : ST_FrameData;(* Data transported from BC/BX (Bus Terminal Controller) to PC/CX (x86) *)
    nReceivedFrame       : UDINT;
    i                    : INT;
    nRxErrors            : UDINT;
END_VAR
(* New frame from PC/CX received? *)
IF stRxFrame.nTxFrames <> nReceivedFrame THEN
    (* Frame length OK? *)
    IF stRxFrame.nFrameSize = SIZEOF( stRxFrame) THEN
        (* Checksum OK? *)
        IF stRxFrame.nCRC = F_CheckSum( ADR( stRxFrame), SIZEOF( stRxFrame) - 1 ) THEN (* => OK *)
            (* Create/modify the tx data *)
            stTxFrame.nFrameSize  := SIZEOF( stTxFrame);(* Set frame byte size *)
            stTxFrame.nTxFrames   := stTxFrame.nTxFrames + 1;(* Increment the send frame number *)
            stTxFrame.nRxFrames   := stRxFrame.nTxFrames;(* Report the received frame number *)
            stTxFrame.bEnable     := NOT stRxFrame.bEnable;(* Toggle bool flag *)
            stTxFrame.nCounter    := stTxFrame.nCounter + 1;(* Send some counter value *)
            stTxFrame.sMsg        := CONCAT( 'Message from BC/BX, counter:', DWORD_TO_STRING( stTxFrame.nCounter ) );(* Create any string message *)
            stTxFrame.fU          := stRxFrame.fU + 10.0;(* Modify some floating point values *)
            stTxFrame.fV          := stRxFrame.fV + 100.0;
            stTxFrame.fW          := stRxFrame.fW + 1000.0;
            FOR i:= 0 TO 9 DO
                stTxFrame.aFloats[i] := stTxFrame.aFloats[i] + i + 3.141592;
            END_FOR
            stTxFrame.nCRC        := F_CheckSum( ADR( stTxFrame), SIZEOF( stTxFrame) - 1 );(* Create checksum *)
        ELSE(* => Checksum error *)
            nRxErrors := nRxErrors + 1;
        END_IF
    ELSE(* => Invalid frame length *)
        nRxErrors := nRxErrors + 1;
    END_IF
    nReceivedFrame := stRxFrame.nTxFrames;
END_IF

PC/CX (x86, x64, ARM) Applikation

Eine steigende Flanke am bWrite startet den Schreibvorgang. Die REAL elemente werden vor dem Schreibvorgang in den BC/BX-Format konvertiert. Die Datenlänge und Checksumme wird ermittelt und gesetzt. Eine steigende Flanke am bRead startet den Lesevorgang. Nach erfolgreichem Lesevorgang wird zuerst die Länge der Daten dann die eine einfache Checksumme überprüft. Danach werden die REAL-Elemente in das PC/CX-Format konvertiert.

PROGRAM MAIN
VAR
    bWrite        : BOOL;(* Rising edge at this variable writes data to the BC/BX (Bus Terminal Controller) *)
    bRead         : BOOL;(* Rising edge at this variable reads data from BC/BX (Bus Terminal Controller) *)
    stTxFrame     : ST_FrameData; (* Data transported from PC/CX (x86) to BC/BX (Bus Terminal Contoroller) *)
    stRxFrame     : ST_FrameData; (* Data transported from BC/BX (Bus Terminal Controller) to PC/CX (x86) *)
    fbWrite       : ADSWRITE :=  ( NETID := '172.17.61.50.1.1', PORT := 800, IDXGRP := 16#4020, IDXOFFS := 500, TMOUT := DEFAULT_ADS_TIMEOUT );
    fbRead        : ADSREAD :=  ( NETID := '172.17.61.50.1.1', PORT := 800, IDXGRP := 16#4020, IDXOFFS := 0, TMOUT := DEFAULT_ADS_TIMEOUT );
    (* Temporary used variables *)
    stTxToBC      : ST_FrameData;
    stRxFromBC    : ST_FrameData;
    i             : INT;
    nTxState      : UDINT;
    nRxState      : UDINT;
    nTxErrors     : UDINT;
    nRxErrors     : UDINT;
END_VAR
(*##########################################################################################*)
CASE nTxState OF
    0:
        IF bWrite THEN(* Write BC/BX data *)
            bWrite := FALSE;

            (* Prepare/modify tx data *)
            stTxFrame.nFrameSize    := SIZEOF( stTxFrame );(* Set frame byte size *)
            stTxFrame.nTxFrames    := stTxFrame.nTxFrames + 1;(* Increment the send frame number *)
            stTxFrame.nRxFrames     := stRxFrame.nTxFrames;(* Report the received frame number *)
            stTxFrame.bEnable    := NOT stTxFrame.bEnable;(* Toggle bool flag *)
            stTxFrame.nCounter     := stTxFrame.nCounter + 1;(* Increment counter value *)
            stTxFrame.sMsg         := CONCAT( 'Message from PC/CX, counter: ', DWORD_TO_STRING( stTxFrame.nCounter ) );(* Create some string message *)
            stTxFrame.fU            := stTxFrame.fU + 1.2;(* Modify some floating point values *)
            stTxFrame.fV            := stTxFrame.fV + 3.4;
            stTxFrame.fW            := stTxFrame.fW + 5.6;
            FOR i:= 0 TO 9 DO
                stTxFrame.aFloats[i] := stTxFrame.aFloats[i] + i;
            END_FOR
            stTxFrame.nCRC         := 0;

            (* Create temporary copy of tx data *)
            stTxToBC := stTxFrame;

            (* Swap REAL variables to BC/BX (Bus Terminal Controller) format *)
            F_SwapRealEx( stTxToBC .fU );
            F_SwapRealEx( stTxToBC .fV );
            F_SwapRealEx( stTxToBC .fW );
            FOR i:= 0 TO 9 DO
                F_SwapRealEx( stTxToBC .aFloats[i] );
            END_FOR
    
            (* Create CRC check number *)
            stTxToBC .nCRC     := F_CheckSum( ADR( stTxToBC ), SIZEOF( stTxToBC ) - 1 );
    
            (* Send *)
            fbWrite( WRITE := FALSE );
            fbWrite( LEN := SIZEOF( stTxToBC ), SRCADDR := ADR( stTxToBC ), WRITE := TRUE );
            nTxState := 1;
        END_IF

    1:(* Wait until ads write command not busy *)
        fbWrite( WRITE := FALSE );
        IF NOT fbWrite.BUSY THEN
            IF NOT fbWrite.ERR THEN
                nTxState := 0;
            ELSE(* Ads error *)
                nTxState := 100;
            END_IF
        END_IF

    100: (* TODO: Error state, add error handling *)
        nTxErrors := nTxErrors + 1;
        nTxState := 0;

END_CASE

(*##########################################################################################*)
CASE nRxState OF
    0:
        IF bRead THEN(* Read BC/BX data *)
            bRead := FALSE;

            fbRead( READ := FALSE );
            fbRead( LEN := SIZEOF( stRxFromBC ), DESTADDR := ADR( stRxFromBC ), READ := TRUE );
            nRxState := 1;
        END_IF

    1:(* Wait until ads read command not busy *)
        fbRead( READ := FALSE );
        IF NOT fbRead.BUSY THEN
            IF NOT fbRead.ERR THEN
                (* Perform simple frame length check *)
                IF stRxFromBC.nFrameSize = SIZEOF( stRxFromBC ) THEN (* Check frame length *)
                    (* Perform simple CRC check *)
                    IF stRxFromBC.nCRC = F_CheckSum( ADR( stRxFromBC ), SIZEOF( stRxFromBC ) - 1 ) THEN

                        (* Swap REAL variables to PC/CX (x86) format *)
                        F_SwapRealEx( stRxFromBC.fU );
                        F_SwapRealEx( stRxFromBC.fV );
                        F_SwapRealEx( stRxFromBC.fW );
                        FOR i:= 0 TO 9 DO
                            F_SwapRealEx( stRxFromBC.aFloats[i] );
                        END_FOR

                        stRxFrame := stRxFromBC;
                        nRxState     := 0;

                    ELSE(* => Checksum error *)
                        nRxState := 100;
                    END_IF
                ELSE(* => Invalid frame length *)
                    nRxState := 100;
                END_IF
            ELSE(* => Ads error *)
                nRxState := 100;
            END_IF
        END_IF

    100: (* TODO: Error state, add error handling *)
        nRxErrors := nRxErrors + 1;
        nRxState := 0;

END_CASE

Applikationstest

Öffnen Sie die PC/CX-Applikation und schreiben TRUE in die bWrite-Variable. Im nächsten Schritt schreiben Sie TRUE in die bRead-Variable.

Voraussetzungen

Entwicklungsumgebung

Zielplattform

Einzubindende SPS-Bibliotheken (Kategoriegruppe)

TwinCAT v3.1.0

PC oder CX (x86, x64, Arm®)

Tc2_Utilities (System)