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.
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
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:
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(
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