industrialNETworXnetx

amazue

amazue

| 02.07.2009 | 18:07 | 9 replies

EtherCAT Slave issues

Hi,

We are trying to use a CifX board as an EtherCAT Slave. Our EtherCAT Master is a software stack running on a dedicated machine. This stack works perfectly with our EtherCAT drive.

We use a CifX board to be bus-independant because we planned to use ProfiNet soon.

At this time, we can successfully exchange data -both reading and writing- using the default PDOs and PDOs entries from "hilscher cifx re ecs v2.0.x.xml".
Nevertheless had two warning messages in my Master stack "Slave 0 does not support changing the PDO mapping!" and "Slave 0 does not support assigning PDOs!".
This is quite surprising because :
1- The mapping and PDO/PDO entries match to the slave default values
2- As mentioned in documentation (slave protocol api, p50), the EtherCAT master
should be able to configure the PDO mapping.
Is this problem due to the fact there is only one PDO available?

Now, the next steps are :
1- Use our own PDO mapping and entries in the Slave
2- Use non-cyclic communication

I expected to provide a configuration file that could be read by the slave when starting. This file would contain PDO entries definition,
position in PDM and PDO assignement.
It appears that such mechanism does not exist -am I wrong?- and we have to configure the slave by code.
Do we have to write this code inside the netX?
Can we write the code in the application using the toolkit mailbox functionnalities -in this case we would loss the bus-independant feature- ? If so, how to know the task handle?

My last point concerns the limitations :
What is the maximum number of entries in one PDO ?
What is the maximum number of PDO in a sync manager ?
Our goal is to map the entire DPM (400 bytes) in the process data.

Thanks.

Andreas Jacob

Andreas Jacob

Hilscher Gesellschaft fuer Systemautomation mbH

| 03.07.2009 | 09:38

Hi amazure,

dynamic PDO mapping (not supported on LFW, but can be implemented on host side):

you cannot simply check the checkboxes "PDO Assignment" and "PDO Configuration". The application software using the cifX must implement PDO mapping on its side in that case.

The default mapping stuff inside the cifX firmware is fixed since it is a static PDO mapping.
The LFW does not implement dynamic PDO mapping on its own.

By having dynamic PDO mapping, it starts to become related to the application and fieldbus as those objects like those defined in DS402 cannot be predicted by the firmware what the application does support. Therefore, it would always be fieldbus-dependant on that. Some fieldbusses may share some protocol definitions but not all do.

If you do not need any application profile, the firmware versions since V2.2 provide a more convenient way of configuring the process data. With these firmwares, the master configurators have the possibility to read out the actual configuration. Whether this is supported depends on the configurator.


Accessing the DPM mailboxes:

Please consult the DPM User Interface manual for knowing the handling of the mailboxes in general.
There is a default address on a channel addressing the stack: 0x20.

I/O Data Sizes:

You cannot map 400 byte on each direction. The stack can only handle a maximum of 400 bytes on netX100/500 for both directions together. To have flexible configuration you have to preallocate that amount to each direction. The XML file delivered by us provides a 200 / 200 byte allocation.



Since the LFW does not handle PDO mapping internally, the following limits do not apply:
- What is the maximum number of entries in one PDO ?
- What is the maximum number of PDO in a sync manager ?

amazue

amazue

| 06.07.2009 | 16:06

Hello AJ,

Thank you for your reply.

In order to achieve the process data transfer, I created 50 entries (both input and output) of uint32 using ObjAccess = OD2_OBJ_ACCESS_PDOMAP.
The master access to these entries using the default PDO/SM assignement. This works well, but :
- when I try to create different subobjects, i have an error "TLR_E_OD2_SUBOBJECT_ALREADY_EXISTS", so i am forced to create multiple object/index, all using subindex 1. Do i need to do something special about subobjects creation ?
- i didn't find how to explicitly assign an object to an address in the dpm. My objects are mapped from 0 to the end of 200 bytes DPM in the creation order. Is there a way to explicitly specify this mapping?

In order to achieve acyclic transfer, I created object with ObjAccess = OD2_OBJ_ACCESS_INDEXED, then I sent a notify_register packet.
When the master tries to read SDO, this works well : i receive a notify_read_ind message, reply with a value which is sent to the master.
Unfortunatly, this doesn't work when I try to write SDO. I don't receive any notify_write_ind message. Is there any restriction about Master->Slave SDO ?

NB : all this code is written using the provided linux driver based on the toolkit.

Regards.

Andreas Jacob

Andreas Jacob

Hilscher Gesellschaft fuer Systemautomation mbH

| 07.07.2009 | 13:30

Hi amazue,

it is hard to say, what is going wrong without your project.

OD2_OBJ_ACCESS_INDEXED does not control acyclic transfer. It selects whether an object is a simple variable (flag is not set) or a record/array (flag is set).

amazue

amazue

| 07.07.2009 | 17:07

Hi AJ,

Here is my code for SDO communication.
The Slave to Master works, although the Master to Slave does not (no notify packet received). The problem does not come from the master as it works with other slaves.

/////////////////////////////////
// Slave -> Master (Acyclic)
/////////////////////////////////
tSendPktCreateObject.tHead.ulDest = 0x20;
tSendPktCreateObject.tHead.ulSrc = 0;
tSendPktCreateObject.tHead.ulDestId = 0;
tSendPktCreateObject.tHead.ulSrcId = 0;
tSendPktCreateObject.tHead.ulLen = 10;
tSendPktCreateObject.tHead.ulId = id_packet++;
tSendPktCreateObject.tHead.ulSta = 0;
tSendPktCreateObject.tHead.ulCmd = 0x1B00;
tSendPktCreateObject.tHead.ulExt = 0;
tSendPktCreateObject.tHead.ulRout = 0;
tSendPktCreateObject.tData.usIndex = 0x4100;
tSendPktCreateObject.tData.bNumOfSubObjs = 1;
tSendPktCreateObject.tData.bMaxSubObjs = 1;
tSendPktCreateObject.tData.usObjAccess = 0; // see p. 64
tSendPktCreateObject.tData.bObjectCode = 0x0007; // COE_OBJCODE_VAR;
tSendPktCreateObject.tData.usDatatype = 7; // UNIT32
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktCreateObject), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktCreateObject), (CIFX_PACKET*)(&tRecvPktCreateObject), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}

tSendPktCreateSubObject.tHead.ulDest = 0x20;
tSendPktCreateSubObject.tHead.ulSrc = 0;
tSendPktCreateSubObject.tHead.ulDestId = 0;
tSendPktCreateSubObject.tHead.ulSrcId = 0;
tSendPktCreateSubObject.tHead.ulLen = 19;
tSendPktCreateSubObject.tHead.ulId = id_packet++;
tSendPktCreateSubObject.tHead.ulSta = 0;
tSendPktCreateSubObject.tHead.ulCmd = 0x1B02;
tSendPktCreateSubObject.tHead.ulExt = 0;
tSendPktCreateSubObject.tHead.ulRout = 0;
tSendPktCreateSubObject.tData.ulMode = 0; // MODE_STORAGE
tSendPktCreateSubObject.tData.usIndex = 0x4100;
tSendPktCreateSubObject.tData.bSubIdx = 1;
tSendPktCreateSubObject.tData.usDirection = 1; // DIRECTION_INPUT
tSendPktCreateSubObject.tData.usSubObjAccess = 0x0007; // see p. 64
tSendPktCreateSubObject.tData.usDatatype = 7; // UNIT32
tSendPktCreateSubObject.tData.usFieldLen = 1;
tSendPktCreateSubObject.tData.uRelativeAddress = 0;
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktCreateSubObject), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktCreateSubObject), (CIFX_PACKET*)(&tRecvPktCreateSubObject), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}

tSendPktNotifyRegister.tHead.ulDest = 0x20;
tSendPktNotifyRegister.tHead.ulSrc = 0;
tSendPktNotifyRegister.tHead.ulDestId = 0;
tSendPktNotifyRegister.tHead.ulSrcId = 0;
tSendPktNotifyRegister.tHead.ulLen = 10;
tSendPktNotifyRegister.tHead.ulId = id_packet++;
tSendPktNotifyRegister.tHead.ulSta = 0;
tSendPktNotifyRegister.tHead.ulCmd = 0x1B10;
tSendPktNotifyRegister.tHead.ulExt = 0;
tSendPktNotifyRegister.tHead.ulRout = 0;
tSendPktNotifyRegister.tData.usIndex = 0x4100;
tSendPktNotifyRegister.tData.fReadNotify = TLR_TRUE;
tSendPktNotifyRegister.tData.fWriteNotify = TLR_FALSE;
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktNotifyRegister), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktNotifyRegister), (CIFX_PACKET*)(&tRecvPktNotifyRegister), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}

/////////////////////////////////
// Master -> Slave (Acyclic)
/////////////////////////////////
tSendPktCreateObject.tHead.ulDest = 0x20;
tSendPktCreateObject.tHead.ulSrc = 0;
tSendPktCreateObject.tHead.ulDestId = 0;
tSendPktCreateObject.tHead.ulSrcId = 0;
tSendPktCreateObject.tHead.ulLen = 10;
tSendPktCreateObject.tHead.ulId = id_packet++;
tSendPktCreateObject.tHead.ulSta = 0;
tSendPktCreateObject.tHead.ulCmd = 0x1B00;
tSendPktCreateObject.tHead.ulExt = 0;
tSendPktCreateObject.tHead.ulRout = 0;
tSendPktCreateObject.tData.usIndex = 0x4200;
tSendPktCreateObject.tData.bNumOfSubObjs = 1;
tSendPktCreateObject.tData.bMaxSubObjs = 1;
tSendPktCreateObject.tData.usObjAccess = 0; // see p. 64
tSendPktCreateObject.tData.bObjectCode = 0x0007; // COE_OBJCODE_VAR;
tSendPktCreateObject.tData.usDatatype = 7; // UNIT32
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktCreateObject), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktCreateObject), (CIFX_PACKET*)(&tRecvPktCreateObject), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}

tSendPktCreateSubObject.tHead.ulDest = 0x20;
tSendPktCreateSubObject.tHead.ulSrc = 0;
tSendPktCreateSubObject.tHead.ulDestId = 0;
tSendPktCreateSubObject.tHead.ulSrcId = 0;
tSendPktCreateSubObject.tHead.ulLen = 19;
tSendPktCreateSubObject.tHead.ulId = id_packet++;
tSendPktCreateSubObject.tHead.ulSta = 0;
tSendPktCreateSubObject.tHead.ulCmd = 0x1B02;
tSendPktCreateSubObject.tHead.ulExt = 0;
tSendPktCreateSubObject.tHead.ulRout = 0;
tSendPktCreateSubObject.tData.ulMode = 0; // MODE_STORAGE
tSendPktCreateSubObject.tData.usIndex = 0x4200;
tSendPktCreateSubObject.tData.bSubIdx = 1;
tSendPktCreateSubObject.tData.usDirection = 2; // DIRECTION_OUTPUT
tSendPktCreateSubObject.tData.usSubObjAccess = 0x0028; // see p. 64
tSendPktCreateSubObject.tData.usDatatype = 7; // UNIT32
tSendPktCreateSubObject.tData.usFieldLen = 1;
tSendPktCreateSubObject.tData.uRelativeAddress = 0;
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktCreateSubObject), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktCreateSubObject), (CIFX_PACKET*)(&tRecvPktCreateSubObject), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}

tSendPktNotifyRegister.tHead.ulDest = 0x20;
tSendPktNotifyRegister.tHead.ulSrc = 0;
tSendPktNotifyRegister.tHead.ulDestId = 0;
tSendPktNotifyRegister.tHead.ulSrcId = 0;
tSendPktNotifyRegister.tHead.ulLen = 10;
tSendPktNotifyRegister.tHead.ulId = id_packet++;
tSendPktNotifyRegister.tHead.ulSta = 0;
tSendPktNotifyRegister.tHead.ulCmd = 0x1B10;
tSendPktNotifyRegister.tHead.ulExt = 0;
tSendPktNotifyRegister.tHead.ulRout = 0;
tSendPktNotifyRegister.tData.usIndex = 0x4200;
tSendPktNotifyRegister.tData.fReadNotify = TLR_FALSE;
tSendPktNotifyRegister.tData.fWriteNotify = TLR_TRUE;
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktNotifyRegister), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktNotifyRegister), (CIFX_PACKET*)(&tRecvPktNotifyRegister), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}

amazue

amazue

| 07.07.2009 | 17:12

Here is the code concerning the cyclic communication.
I created my objects. They are automatically mapped in DPM from offset 0 to 196. Is there a way to explicitly specify the mapping of my objects in DPM ?

/////////////////////////////
// Slave -> Master
/////////////////////////////
for (i = 0 ; i < nb_entries_in ; ++i)
{
tSendPktCreateObject.tHead.ulDest = 0x20;
tSendPktCreateObject.tHead.ulSrc = 0;
tSendPktCreateObject.tHead.ulDestId = 0;
tSendPktCreateObject.tHead.ulSrcId = 0;
tSendPktCreateObject.tHead.ulLen = 10;
tSendPktCreateObject.tHead.ulId = id_packet++;
tSendPktCreateObject.tHead.ulSta = 0;
tSendPktCreateObject.tHead.ulCmd = 0x1B00;
tSendPktCreateObject.tHead.ulExt = 0;
tSendPktCreateObject.tHead.ulRout = 0;
tSendPktCreateObject.tData.usIndex = 0x2800 + i;
tSendPktCreateObject.tData.bNumOfSubObjs = 1;
tSendPktCreateObject.tData.bMaxSubObjs = 1;
tSendPktCreateObject.tData.usObjAccess = 1; // see p. 64
tSendPktCreateObject.tData.bObjectCode = 0x0007; // COE_OBJCODE_VAR;
tSendPktCreateObject.tData.usDatatype = 7; // UNIT32
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktCreateObject), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktCreateObject), (CIFX_PACKET*)(&tRecvPktCreateObject), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}
tSendPktCreateSubObject.tHead.ulDest = 0x20;
tSendPktCreateSubObject.tHead.ulSrc = 0;
tSendPktCreateSubObject.tHead.ulDestId = 0;
tSendPktCreateSubObject.tHead.ulSrcId = 0;
tSendPktCreateSubObject.tHead.ulLen = 19;
tSendPktCreateSubObject.tHead.ulId = id_packet++;
tSendPktCreateSubObject.tHead.ulSta = 0;
tSendPktCreateSubObject.tHead.ulCmd = 0x1B02;
tSendPktCreateSubObject.tHead.ulExt = 0;
tSendPktCreateSubObject.tHead.ulRout = 0;
tSendPktCreateSubObject.tData.ulMode = 0; // MODE_STORAGE
tSendPktCreateSubObject.tData.usIndex = 0x2800 + i;
tSendPktCreateSubObject.tData.bSubIdx = 1;
tSendPktCreateSubObject.tData.usDirection = 1; // DIRECTION_INPUT
tSendPktCreateSubObject.tData.usSubObjAccess = 0x0007; // see p. 64
tSendPktCreateSubObject.tData.usDatatype = 7; // UNIT32
tSendPktCreateSubObject.tData.usFieldLen = 1;
tSendPktCreateSubObject.tData.uRelativeAddress = 0;
if(CIFX_NO_ERROR != (lRet = xChannelPutPacket(hChannel, (CIFX_PACKET*)(&tSendPktCreateSubObject), 10)))
{
printf("xChannelPutPacket failed!\n");
}
else
{
if(CIFX_NO_ERROR != (lRet = xChannelGetPacket(hChannel, sizeof(tRecvPktCreateSubObject), (CIFX_PACKET*)(&tRecvPktCreateSubObject), 20)) )
{
printf("xChannelGetPacket failed!\n");
}
}
}

amazue

amazue

| 07.07.2009 | 17:18

My last problem concerns the ECAT_OD_SDOINFO_REGISTER_REQ_T packet.

I didn't find the ulCmd value of this packet, so I can't register sdoinfo.

Thanks for your help.

Andreas Jacob

Andreas Jacob

Hilscher Gesellschaft fuer Systemautomation mbH

| 09.07.2009 | 08:05

General packet handling:

After registration for indication be aware that there might be indications instead of the actual confirmation of command coming before.

So, if you happen to execute such code sequence during the normal run it might be you did not evaluate the packet.


Sub object access flags:
tSendPktCreateSubObject.tData.usSubObjAccess = 0x0028; // see p. 64

means

Not readable (i.e. Write Only) (Bit 0-2 are zero)

The following definitions are the base for the flags:

#define ECAT_OD_READ_PREOP          (0x0001)
#define ECAT_OD_READ_SAFEOP         (0x0002)
#define ECAT_OD_READ_OPERATIONAL    (0x0004)
#define ECAT_OD_WRITE_PREOP         (0x0008)
#define ECAT_OD_WRITE_SAFEOP        (0x0010)
#define ECAT_OD_WRITE_OPERATIONAL   (0x0020)


You set:
ECAT_OD_WRITE_PREOP and ECAT_OD_WRITE_OPERATIONAL

This means it is a write-only object which is only writable at PreOp and Op. It cannot be read and therefore TwinCAT will output Attempted to read a write-only object.

So, if you happen to be in SafeOp, you cannot write the object.



ECAT_OD_SDOINFO_REGISTER_REQ_T:

The actual command is ECAT_OD_SDOINFO_REGISTER_REQ. There is no sense in using that without using ECAT_OD_UNDEFINED_NOTIFY_REGISTER_REQ.

amazue

amazue

| 09.07.2009 | 18:03

Hi AJ,

I've solved my problem with sdo write packets : I just used a firmware 2.1.0 build2 (on comx + pci adpater) in place of firmware 2.0.0 build 10 (on cifx)! I will try to update my cifx board, but it seems to be a firmware bug.

May be I was not clear about one point : I don't have any header file. I have just the EtherCAT Slave Protocol API.pdf to help me. That's why I need a numeric value for ECAT_OD_SDOINFO_REGISTER_REQ and ECAT_OD_UNDEFINED_NOTIFY_REGISTER_REQ.

Also I noticed something strange. Only PDO used by the master are mapped in the DPM of the slave. Is this behaviour normal ?

Regards.

Andreas Jacob

Andreas Jacob

Hilscher Gesellschaft fuer Systemautomation mbH

| 10.07.2009 | 06:31

Hi, good to hear, that you fixed your problem.

If you really miss the needed header files, you should get in contact with the Hilscher support.

Login