ECS CustomOD Example exercise

1. ECS Setup.

Necessary hardware and sotware for this training

The same like in ECS SimpleConfig Example exercise. Follow the steps 1 till 3 and use here now netX 90 - EtherCAT Slave - customOD V3.0.0.1

2. Create an Object with a Subobject

go to the file: AppECS_DemoObjectDictionary.h

In the array g_tObjects[] all custom objects are defined. Go to object 0x4000 and copy this. Create a new object. Here the example is 0x4001:

  {
    .usIndex = 0x4000,
	...
  },
  {
    .usIndex = 0x4001,
    .bMaxNumOfSubObjs = ARRCNT(g_tSiObj_4001) - 1,
    .ulMaxFieldUnits = 1,
    .bObjectCode = ODV3_OBJCODE_RECORD,
    .usAccessFlags = 0,
    .bIndicationFlags = 0,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,
    .usAccessRights = ECAT_OD_READ_ALL,
    .pszName = "Hello World Object",
    .ptSi00 = &g_tSiObj_4001[0],
    .ptSiBreak = &g_tSiObj_4001[ ARRCNT(g_tSiObj_4001) ],
  },
};

and now create the subobjects for the object 0x4001 in the subobject structure g_tSiObj_4001

static const uint8_t s_b4001_NumElements;

SUBOBJECT_DESCRIPTION_T g_tSiObj_4001[] =
{
  {
    .bSubIndex = 0,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,
    .ulMaxFieldUnits = 1,
    .pszName = "Number of elements",
    .pvInitialValue = &s_b4001_NumElements,
    .ulInitialValueLength = sizeof(s_b4000_NumElements),
  },
  {
   .bSubIndex = 1,
   .bIndicationFlags = ODV3_INDICATION_FLAGS_ALLOWED_ON_SUBOBJ,
   .usAccessRights = ECAT_OD_ACCESS_ALL,
   .usDatatype = ECAT_OD_DTYPE_BOOLEAN,
   .ulMaxFieldUnits = 1,
   .pszName = "Flag 1",
  },
};

static const uint8_t s_b4001_NumElements = ARRCNT(g_tSiObj_4001) - 1; /* SI 00 does not counts */

As soon as the application starts, it will do this steps:

The application calls in the file AppECS_DemoApplicationFunctions.c the function AppECS_OD_SendCreateObjectReq.

  /* initialize channel to use the configured data */
  App_SysPkt_AssembleChannelInitReq(ptPacket);
  ulRet = Pkt_SendReceivePacket(ptAppData, ECS_DEMO_CHANNEL_INDEX, ptPacket, TXRX_TIMEOUT);
  if( ulRet != CIFX_NO_ERROR )
      return ulRet;

  /* Create Objects */
  AppECS_OD_SendCreateObjectReq(ptAppData, &g_customOd);
  //ulRet = Pkt_SendReceivePacket( ptAppData->hChannel, &ptAppData->tPacket, TXRX_TIMEOUT );
    if( ulRet != CIFX_NO_ERROR )
      return ulRet;

the function AppECS_OD_SendCreateObjectReq takes all objects from the array g_tObjects[] and creates these objects in the ECS firmware.

After successfull creating of an object the application calls AppECS_OD_SendCreateSubObjectReq to create the subobjects.

The flag ODV3_INDICATION_FLAGS_ALLOWED_ON_SUBOBJ means here, that a wirte or read on this subobject from a EtherCAT master will be

forwarded to the application as Indication.

In the file AppECS_DemoApplicationFuctions.c is the function AppECS_Read_ObjectInd and AppECS_Write_ObjectInd.

Add here now a switch case for the object 0x4001:

First creatre

static uint8_t Object_4001_1=0x01;

AppECS_Read_ObjectInd:

    case 0x4001:
      switch ( ptInd->tData.bSubIndex )
       {
       case 0x0001:
          ptRes->tHead.ulLen = 9+1;
          ptRes->tData.ulTotalDataBytes=1;
          ptRes->tData.abData[0]= Object_4001_1;
          break;
       default:
          ptRes->tHead.ulLen = 9;
          ptRes->tData.ulTotalDataBytes=0;
          break;
       }
      break

AppECS_Write_ObjectInd:

      case 0x4001:
        switch ( ptInd->tData.bSubIndex )
         {
         case 0x0001:
            Object_4001_1 = ptInd->tData.abData[0] ;
            break;
         default:
            break;
         }
        break

Compile the netXStudio projekct with the changes and download the new application to the netX90.

Now start TwinCAT and create a new project.

Go to the netX90 EtherCAT slave and check the object list:

The object 0x4001 is now avaiable.

Doubleclick on 4001:01 Flag 1 and change the value for the subobject from ECS master.

Set a breakpoint in the function AppECS_Write_ObjectIInd for the object 0x4001. As soon as the change from ECM is done, the indication function will be called.

double click on advanced and force a read of all objects

set a breakpoint in AppECS_Read_ObjectInd for the object 0x4001

Changed Sources:

AppECS_DemoApplicationFunctions.c

AppECS_DemoObjectDictionary.h

3. Create a PDO

In the set configuration packet the flag ECAT_SET_CONFIG_COEFLAGS_USE_CUSTOM_OD is used. This allows to create a custom object dictionary.

This chepter describes how to create a new processdata object (PDO).

Go in the array g_tObjects[] to object 0x1600 and copy it and create object 0x1601

  {
    .usIndex = 0x1600,
	...
  },
  {
    .usIndex = 0x1601,
    .bMaxNumOfSubObjs = ARRCNT(s_ab1601_Elements),
    .bObjectCode = ODV3_OBJCODE_RECORD,
    .usAccessFlags = 0,
    .bIndicationFlags = 0,
    .usDatatype = ECAT_OD_DTYPE_PDO_MAPPING,
    .usAccessRights = ECAT_OD_ACCESS_ALL,
    /* no SimpleVar, therefore no ulMaxFieldUnits value */
    .pszName = "1. RxPDO",
    /* no SimpleVar, therefore no initial value */
    .ptSi00 = &g_tSiObj_1601[0],
    .ptSiBreak = &g_tSiObj_1601[ ARRCNT(g_tSiObj_1601) ],
  },

and now create the subobjects for the object 0x1601 in the subobject structure g_tSiObj_1601

static const uint32_t s_ab1601_Elements[] =
{
  PDOMAPPING(0x2001, 1, 8),
};

static const uint8_t s_b1601_NumElements = ARRCNT(s_ab1601_Elements);

SUBOBJECT_DESCRIPTION_T g_tSiObj_1601[] =
{
  {
    .bSubIndex = 0,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,
    .ulMaxFieldUnits = 1,
    .pszName = "Number of elements",
    .pvInitialValue = &s_b1601_NumElements,
    .ulInitialValueLength = sizeof(s_b1601_NumElements),
  },
  {
    .bSubIndex = 1,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED32,
    .ulMaxFieldUnits = 1,
    .pszName = 0,
    .pvInitialValue = &s_ab1601_Elements[0],
    .ulInitialValueLength = sizeof(s_ab1601_Elements[0]),
  },
};

and link this with the SynManager object 0x1C12

static const uint16_t s_aus1C12_Entries[] = { 0x1600 , 0x1601 };
static const uint8_t s_b1C12_NumElements = ARRCNT(s_aus1C12_Entries);

SUBOBJECT_DESCRIPTION_T tSiObj_1C12[] =
{
  {
    .bSubIndex = 0,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,
    .ulMaxFieldUnits = 1,
    .pszName = "Number of elements",
    .pvInitialValue = &s_b1C12_NumElements,
    .ulInitialValueLength = sizeof(s_b1C12_NumElements),
  },
  {
    .bSubIndex = 1,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED16,
    .ulMaxFieldUnits = 1,
    .pszName = 0,
    .pvInitialValue = &s_aus1C12_Entries[0],
    .ulInitialValueLength = sizeof(s_aus1C12_Entries[0]),
  },
  {
    .bSubIndex = 2,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED16,
    .ulMaxFieldUnits = 1,
    .pszName = 0,
    .pvInitialValue = &s_aus1C12_Entries[1],
    .ulInitialValueLength = sizeof(s_aus1C12_Entries[1]),
  },
};

and change

  {
    .usIndex = 0x1C12,
    .bMaxNumOfSubObjs = ARRCNT(tSiObj_1C12) - 1,
    .bObjectCode = ODV3_OBJCODE_ARRAY,
    .usAccessFlags = 0,
    .bIndicationFlags = 0,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED16,/**< \todo what exactly shall be used here */
    .usAccessRights = ECAT_OD_ACCESS_ALL,
    /* no SimpleVar, therefore no ulMaxFieldUnits value */
    .pszName = "Sync Manager 1 PDO Assignment",
    /* no SimpleVar, therefore no initial value */
    .ptSi00 = &tSiObj_1C12[0],
    .ptSiBreak = &tSiObj_1C12[ ARRCNT(tSiObj_1C12) ],
  },  

and now create the custom specific opject 0x2001 in the array g_tObjects[]

  {
    .usIndex = 0x2000,
	...
  },
  {
    .usIndex = 0x2001,
    .bMaxNumOfSubObjs = ARRCNT(g_tSiObj_2001) - 1,
    .bObjectCode = ODV3_OBJCODE_RECORD,
    .usAccessFlags = ODV3_ACCESS_FLAGS_RXPDO_MAPPABLE,
    .bIndicationFlags = 0,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,/**< \todo what exactly shall be used here */
    .usAccessRights = ECAT_OD_READ_ALL,
    /* no SimpleVar, therefore no ulMaxFieldUnits value */
    .pszName = "Outputs",
    /* no SimpleVar, therefore no initial value */
    .ptSi00 = &g_tSiObj_2001[0],
    .ptSiBreak = &g_tSiObj_2001[ ARRCNT(g_tSiObj_2001) ],
  },

and now create the subobjects for the object 0x2001 in the subobject structure g_tSiObj_2001

static const uint8_t s_b2001_NumElements;

SUBOBJECT_DESCRIPTION_T g_tSiObj_2001[] =
{
  {
    .bSubIndex = 0,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,
    .ulMaxFieldUnits = 1,
    .pszName = "Number of elements",
    .pvInitialValue = &s_b2001_NumElements,
    .ulInitialValueLength = sizeof(s_b2001_NumElements),
  },
  {
    .bSubIndex = 1,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_ACCESS_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,
    .ulMaxFieldUnits = 1,
    .pszName = "Outputdata0",
    .pvInitialValue = 0,
    .ulInitialValueLength = 0,
  },
};

static const uint8_t s_b2001_NumElements = ARRCNT(g_tSiObj_2001) - 1; /* SI 00 does not counts */

The last step is to change the outsize in the configuration packet

ptConfigReq->tData.tBasicCfg.ulProcessDataOutputSize = sizeof(APP_PROCESS_DATA_INPUT_T)+1;

Compile the netXStudio projekct with the changes and download the new application to the netX90.

Now start TwinCAT and create a new project.

Go to the netX90 EtherCAT slave and check the object list:

and under process data the new PDO is available.

Changed Sources:

AppECS_DemoObjectDictionary.h

AppECS_DemoApplicationFunctions.c


Do some last changes for a 4 byte size submodul.

Change the outsize in the configuration packet to + 4;

ptConfigReq->tData.tBasicCfg.ulProcessDataOutputSize = sizeof(APP_PROCESS_DATA_INPUT_T)+4;

And now change the subobjet 2001:1 from ECAT_OD_DTYPE_UNSIGNED8 to ECAT_OD_DTYPE_UNSIGNED32

SUBOBJECT_DESCRIPTION_T g_tSiObj_2001[] =
{
  {
    .bSubIndex = 0,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_READ_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED8,
    .ulMaxFieldUnits = 1,
    .pszName = "Number of elements",
    .pvInitialValue = &s_b2001_NumElements,
    .ulInitialValueLength = sizeof(s_b2001_NumElements),
  },
  {
    .bSubIndex = 1,
    .bIndicationFlags = 0,
    .usAccessRights = ECAT_OD_ACCESS_ALL,
    .usDatatype = ECAT_OD_DTYPE_UNSIGNED32,
    .ulMaxFieldUnits = 1,
    .pszName = "Outputdata0",
    .pvInitialValue = 0,
    .ulInitialValueLength = 0,
  },
};

and change the mapping to

static const uint32_t s_ab1601_Elements[] =
{
  PDOMAPPING(0x2001, 1, 32),
};

Go to TwinCAT. Here is the last information.

After click on Load PDO info from device TwinCAT reads the latest informations about the PDOs from the device.

The subobject 2001:1 has no a size of 4 byte