FC510x - PCI Cards for CANopen

SDO communication with FC510x

CANopen SDO (Service Data Object) communication is used to read or write any parameters in the CANopen bus node's object directory. The FC5101CANopen PCI card uses SDO communication to configure the communication parameters when starting up. Two types of application-specific SDO communication are additionally possible:

 

1. Downloading Application-Specific Parameters when Starting Up

The appropriate parameters are to be entered here in the System Manager for the corresponding node under "SDO". The objects that result from the configuration under CAN node appear in square brackets. Any desired number of object directory entries can then be added.

 
Edit SDO entry

The card expects a positive acknowledgement of the parameter download from the relevant bus device. If it was not possible to write a parameter (the bus device has aborted the SDO) the card then attempts to read the corresponding value back and to compare it with the value that was to be written. This is because it could, for instance, be a read-only value, and therefore already correctly configured within the bus device. If they agree with one another, the card moves onto the next parameter entry.

 
 

2. Upload and download at runtime via ADS

It is possible to perform SDO accesses to the bus devices' object directories using Beckhoff's ADS communication when the system is running. This is also possible from the PLC, from the NC, from the OPC server, from ActiveX controls or from any other ADS device.

The whole SDO protocol is handled by the card. Using the ADS Write or ADS Read functions the parameters are transferred to the card, and the data is transferred (write) or fetched (read). The "IDXGRP" parameter here corresponds to the 16 bit index in the CANopen object directory, while "IDXOFFS" corresponds to the 8 bit subindex in the CANopen object directory. Details about the ADS function blocks can be found in the TwinCAT documentation (Beckhoff Information System).

The ADS function block parameters are represented as follows in the SDO parameters:

 
 

ADSREAD / ADSWRITE

 
Parameter
Description
NETID
The NetID is a string, 23 bytes in length, and is formed by default from the IP address of the computer with an additional two bytes. It addresses the FC5101 card and can be found under "ADS" in the System Manager.
PORT
Contains the ADS device's port number - this is the port number of the CANopen bus device that is to be addressed.
IDXGRP
Corresponds to the 16 bit index in the CANopen object directory.
IDXOFFS
Corresponds to the 8 bit subindex in the CANopen object directory.
LEN
The length of the parameter that is to be read or written, in bytes.
DESTADDR
(only ADSREAD)
Contains the address of the buffer which is to receive the data that has been read. The programmer is himself responsible for dimensioning the buffer to a size that can accept 'LEN' bytes. The buffer can be a single variable, an array or a structure, whose address can be found with the ADR operator.
SRCADDR
(only ADSWRITE)
Contains the address of the buffer from which the data to be written is to be fetched. The programmer is himself responsible for dimensioning the buffer to such a size that 'LEN' bytes can be taken from it. The buffer can be a single variable, an array or a structure, whose address can be found with the ADR operator.
READ
The ADS command is triggered by a rising edge at this input
TIMEOUT
States the time before the function is canceled
BUSY
This output remains TRUE until the function block has executed a command, but at the longest for the duration supplied to the 'Timeout' input. While Busy = TRUE, no new command will be accepted at the inputs. Please note that it is not the execution of the service but its acceptance whose time is monitored.
ERR
This output is switched to TRUE if an error occurs during the execution of the command.
ERRID
Contains the command-specific error code of the most recently executed command. Is reset to 0 by the execution of a command at the inputs.

The ERRID is a 32 bit value. The Low word (bits 0...15) contains the general ADS ERROR CODES, while the High word (bits 16...31) returns SDO-specific error codes:

 
MSB
LSB
Bit 31
Bits 30...24
Bits 23...20
Bits 19...16
Bit 15...0
1
Bits 6..0 of the SDO error code
Bits 19...16 of the SDO error code
Bits 27...24 of the SDO error code
ADS ERROR code, see chapter Error handling and diagnostics for meaning

If one of the values SDO Additional Code, SDO Error Code or SDO Error Class is larger than the available data width (hidden bits set), then the value 0x2115 is returned in the High word (bits 16...31).

 
 

Example: SDO Read via ADS

In the following sample program (structured text) for the use of ADS services for SDO communication, object 0x1000, subindex 0, from the node with port number 0x1001 is read. The DeviceType is CANopen. This is coded as UnSigned32, and is therefore 4 bytes long.

 
SDO Read via ADS
SDO_READ(
        StartReading :=ReadStart,
        CO_Index :=16#1000,
        CO_SubIndex :=16#0,
        DataLength := 4,
        PortNr := 16#1001,
       ADSNetID:='192.168.10.11.2.1'
        );
IF SDO_READ.ReadDataAvailable THEN
        ReadStart :=FALSE;
        ReadError :=SDO_READ.Error;
        ReadData :=SDO_READ.ReadData;
END_IF

The SDO_READ function block that has been called in turn calls the ADSREAD function a number of times. It looks like this (starting with the variable declaration):

FUNCTION_BLOCK SDO_READ
VAR_INPUT
    ADSNetID:STRING(23); (* The AMSNetID addresses the FC5101 card. Can be empty if only one local single channel card is present*)
   PortNr:WORD;         (*This Port No. addresses the CANopen Node (see System Manager)*)
    CO_Index:DWORD;      (*This is the Index of the CANopen Object Dictionary Entry*)
    CO_SubIndex:DWORD;   (* This is the Sub-Index of the CANopen Object Dictionary Entry*)
    DataLength:DWORD;    (* This is the Length of the CANopen Object Dictionary Entry*)
    StartReading:BOOL;   (* only reset to FALSE after ReadDataAvailable=TRUE*)
END_VAR
VAR_OUTPUT
    ReadData:ARRAY[0..255] OF BYTE;
    ReadDataAvailable:BOOL;
    Error:DWORD;
END_VAR
VAR
    state:BYTE := 0;
    ADSREAD:ADSREAD;
END_VAR
 
CASE
state OF
    0:
        IF StartReading THEN
           ReadDataAvailable := FALSE;
           Error := 0;
           ADSRead(
               NETID:= ADSNetID,
               PORT:= PortNr,
               IDXGRP:= CO_Index,
               IDXOFFS:= CO_SubIndex,
               LEN:= DataLength,
               DESTADDR:= ADR(ReadData),
               READ:= TRUE,
               TMOUT := T#1s
               );
           IF ADSRead.err THEN
               state := 2;
               ReadDataAvailable := TRUE;
               Error := ADSRead.ErrId;
           ELSE
               state := 1;
           END_IF
        ELSE
           ADSRead(
               NETID:= ADSNetID,
               PORT:= PortNr,
               IDXGRP:= CO_Index,
               IDXOFFS:= CO_SubIndex,
               LEN:= DataLength,
               DESTADDR:= ADR(ReadData),
               READ:= FALSE,
               TMOUT := T#1s
           );
        END_IF
    1:
        ADSRead(READ:=FALSE);
        IF ADSRead.err THEN
           state := 2;
           ReadDataAvailable := TRUE;
           Error := ADSRead.ErrId;
        ELSE
           IF NOT ADSRead.busy THEN
               state := 2;
               ReadDataAvailable := TRUE;
           END_IF
        END_IF
    2:
        ADSRead(READ:=FALSE);
        state := 0;
END_CASE
 
 

Sample: SDO Write via ADS

In the following sample program (structured text) for the use of ADS services for SDO communication, object 0x6200, Subindex3, from the node with port number 0x1001 is written. It concerns digital outputs to an I/O node.

(* Data to be written *)
WriteData[0] := 16#55;

(* write Object *)
    SDO_WRITE(
    StartWriting := WriteStart,
    CO_Index := 16#6200,
    CO_SubIndex := 3,
    DataLength := 1,
    PortNr := 16#1001,
    WriteData := WriteData,
    ADSNetID:='192.168.10.11.2.1'
    );
IF SDO_WRITE.WriteDataFinished THEN
    WriteStart := FALSE;
    WriteError := SDO_WRITE.Error;
END_IF

 

The SDO_WRITE function block that has been called in turn calls the ADSWRITE function a number of times. It looks like this (starting with the variable declaration):

FUNCTION_BLOCK SDO_WRITE
VAR_INPUT
    ADSNetID:STRING(23);   (* The AMSNetID addresses the FC5101 card. Can be empty if only one local single
channel card is present*)
   PortNr:WORD;          (* The Port No. addresses the CANopen Node (see System Manager)*)
   CO_Index:DWORD;        (* This is the Index of the CANopen Object Dictionary Entry*)
    CO_SubIndex:DWORD;     (*This is the Sub-Index of the CANopen Object Dictionary Entry*)
    DataLength:DWORD;     (* This is the Length of the CANopen Object Dictionary Entry*)
    StartWriting:BOOL;     (*only reset to FALSE after WriteDataFinished=TRUE*)
    WriteData:ARRAY[0..255] OF BYTE; (*This array contains the data to be written to the CANopen Object Dictionary*)
END_VAR
VAR_OUTPUT
    WriteDataFinished:BOOL;
    Error:DWORD;
END_VAR
VAR
    state:BYTE := 0;
    ADSWRITE:ADSWRITE;
END_VAR
 
CASE
state OF
    0:
        IF StartWriting THEN
           WriteDataFinished := FALSE;
           Error := 0;
           ADSWrite(
               NETID:= ADSNetID,
               PORT:= PortNr,
               IDXGRP:= CO_Index,
               IDXOFFS:= CO_SubIndex,
               LEN:= DataLength,
               SRCADDR:= ADR(WriteData),
               WRITE:= TRUE,
               TMOUT := T#1s
               );
           IF ADSWrite.err THEN
               state := 2;
               WriteDataFinished := TRUE;
               Error := ADSWrite.ErrId;
           ELSE
               state := 1;
           END_IF
        ELSE
           ADSWrite(
               NETID:= '',
               PORT:= PortNr,
               IDXGRP:= CO_Index,
               IDXOFFS:= CO_SubIndex,
               LEN:= DataLength,
               SRCADDR:= ADR(WriteData),
               WRITE:= FALSE,
               TMOUT := T#1s
               );
        END_IF
    1:
        ADSWrite(WRITE:=FALSE);
        IF ADSWrite.err THEN
           state := 2;
           WriteDataFinished := TRUE;
           Error := ADSWrite.ErrId;
        ELSE
           IF NOT ADSWrite.busy THEN
               state := 2;
               WriteDataFinished := TRUE;
           END_IF
        END_IF
    2:
        ADSWrite(WRITE:=FALSE);
        state := 0;
END_CASE