Skip to end of banner
Go to start of banner

PNS Isochronous Example

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 17 Next »

1. Example project

Basis for this exampe is the Extended Config Example netX 90 - PROFINET IO Device - extendedConfig V2.2.0.0, because an acyclic handling will be necessary to get the isochron data parameter.

Example for netX90:

netXStudio_PNSV5_extendedConfig Isochron - V1.0.0.2.zip

2. Changes for this example

There are many changes necessary. An interrupt function for sync0 signal is necessary which shall start timers to do the IO data update at the correct timepoint. For this the following things have changed:

  1. The GSDML is changed to support isochron mode and the output module gets the option to let it run isochron.
  2. The IO data handler function is splitted in 2 IO data handler. One for inputs and one for outputs. Each handler has its own timer which runs in RT and IRT connection with 1ms. Only in IRT isochron connection the timer timings will be changed.
  3. The set OEM parameter service with parameter 6 is called up before channel init is called up to map the Profinet cycle counter in the input data (plc to device).
  4. The set trigger type request is called up before channel init to disable freerun mode.
  5. The hardware configuration maps the mmio to xtrigger0/Sync0 and the main function now calls up some functions to activate an interrupt service routine for xtrigger0.
  6. In the function for write record indication the parameter with index 0x8030 has been added.
  7. In the function for parameter end indication the values from index 0x8030 are used to calculate new timer settings.

3. Index 0x8030

As soon as a submodule supports isochron mode, the synchron mode for this submodule can be activated in the plc. The plc will write down the parameter with index 0x8030 in the connection establishment. The fuction for write record indication is necessary to receive the parameter with the index 0x8030 from the plc.

For each isochron submodule a parameter with Index 0x8030 (Isochronus Mode Data) will be sent from the plc to the device. The parameter with Index 0x8030 (isochronus mode data) consists of these informations:

typedef __HIL_PACKED_PRE struct ISOCHRONOUS_DATA_Ttag

{
  uint16_t        usType;
  uint16_t        usLen;
  uint8_t         bMajor;
  uint8_t         bMinor;
  uint8_t         abPadding1[2];
  uint16_t     usSlot;
  uint16_t     usSubSlot;
  uint16_t     usCACF;
  uint16_t     usDataCycle;
  uint32_t     ulIO_Input;
  uint32_t     ulIO_Output;
  uint32_t     ulIO_InputValid;
  uint32_t     ulIO_OutputValid;
}__HIL_PACKED_POST PNS_IF_PDU_ISOCHRONOUS_DATA_T;


Very important are the slot and the subslot information. With this the application knows which IO data are requested to run as isochron.

The values for Datacycle, IO_Input, IO_Output and CACF need to be saved in the application. After receiving all 0x8030 parameter over the function write record indication, the application needs to calculate timings in the parameter end indication.

4. Calculate Time for call of Output Handler

The ulIO_Output from 0x8030 is the T_IO_Output time in this picture:

T_IO_Output_Min + Spare Time in the picture is the time the device needs to get outputs and apply them. In the GSDML T_IO_Output_Min + Spare Time is added like this:

<IsochroneMode IsochroneModeRequired="false" T_DC_Base="8" T_DC_Max="16" T_DC_Min="1" T_IO_Base="1000" T_IO_InputMin="125" T_IO_OutputMin="60" />

T_IO_Output_Min + Spare is an application specific timing. Start with setting this value to a high value. Later this value can be measured and reduced in the GSDML.

T_IO_Output - T_IO_Output_Min = This is the time the plc has calculated for the network and other timeoffsets to achieve that all isochron devices apply data at the same point of time. This time can be diffent in each network setup.


What does the application developer need to do now?

The application gets in the parameter with Index 0x8030 the time for T_IO_Output. T_IO_Output starts with Sync0 signal and the application needs to apply data if timepoint of T_IO_Output is reached. The application needs to start a timer with sync0 signal. Now the question ist, what time shall be used to configurate the timer? Here are some options:

Option 1: The timer is set to T_IO_Output - T_IO_Output_Min. The application calls xChannelIORead and processes the data and applies it to the outputs.

There is a lot Jitter possible, because the time for processing the data can be different. 

Option 2:

It is better to call up xChannelIORead and data processing as soon as possible and to save the data for apply outputs until the correct point of time has come. Measure only the application specific time to apply outputs. T_AO = time apply outputs which will be a short time.

Set the timer to T_IO_Output - T_AO. Then the application only needs to apply outputs as soon as the timerinterrupt is reached and there will be less Jitter.


Parameter end indciation:

In this example the calculation for the times which are used for the output data handler timer is done in the parameter end indication.

T_IO_Output_Event = s_ab_RecordIsochronusData_Slot2_Subslot1.ulIO_Output -T_AO; //in nano seconds

with T_AO = 0

The application developer needs to set a value for T_AO. He can take, for example, the same value like in the GSDML T_IO_OutputMin="60". The value T_AO must be in nano seconds.

5. Calculate Time for call of Input Handler 

The ulIO_Input from 0x8030 is the T_IO_Input time in this picture:


T_IO_Input_Min time in the picture is the time the device needs for the inputs. In the GSDML T_IO_Input_Min is added like this:

<IsochroneMode IsochroneModeRequired="false" T_DC_Base="8" T_DC_Max="16" T_DC_Min="1" T_IO_Base="1000" T_IO_InputMin="125" T_IO_OutputMin="60" />

T_IO_Input_Min is an application specific timing. In the development process it is good to start with setting this value to a high value. Later this value can be measured and reduced in the GSDML.


What does the application developer need to do now?

The application gets in the parameter with Index 0x8030 the time for T_IO_Input. But the application needs the time from beginning with Sync0 signal. With the cycletime the application needs to calculate the correct time for the timer.

This calculation will be done in the parameter end indciation function:

T_IO_Input_Event = ((((uint32_t) s_ab_RecordIsochronusData_Slot2_Subslot1.usDataCycle) * 3125)*10) - T_IO_Input; //in nano second

6. Some more changes in GSDML

In the DAP:

Change in the GSDML the line like this:<CertificationInfo ApplicationClass="Isochronous" ConformanceClass="C" NetloadClass="II"/>

Add "IsochroneModeInRT_Classes="RT_CLASS_3" in the InterfaceSubmoduleItem.

Add IsochroneMode in the modul, that shall support isochrone mode. Put it behind </ModuleInfo>

<IsochroneMode IsochroneModeRequired="false" T_DC_Base="8" T_DC_Max="16" T_DC_Min="1" T_IO_Base="1000" T_IO_InputMin="125" T_IO_OutputMin="60" />

The Timings T_IO_InputMin="125" T_IO_OutputMin="60" are device specific and need to be measured from the developer.

7. Interrupt HIF Command and Timer

Map mmio to the xc_trigger0

and add in main.c the interrupt fuction for XCTIGGER_IRQHandler.

The XCTIGGER_IRQHandler starts 2 timers for the 2 IO data handler.

void XCTRIGGER0_IRQHandler(void)
{
  ((trigger_irq_app_Type*) trigger_irq_app_BASE)->trigger_irq_raw_b.xc_trigger_out_edge=1u;

   if (Get_Timer_Configuration() == 1)
   {
    // Start timer for Input and Output update functions
    HOSTAL_Callbacks_Enable();
    HOSTAL_Callbacks_Enable2();
   }

  DRV_NVIC_ClearPendingIRQ(trigger_out_edge0_IRQn);
}

int main()
{
  int32_t lRet = CIFX_NO_ERROR;

  libcInit();

  if(CIFX_NO_ERROR == lRet)
  {
    lRet = InitializeToolkit();
  }

  if(CIFX_NO_ERROR == lRet)
  {
    // activate XCTRIGGER0_IRQHandler(); Set MMIO3 in Hardware Configuration file to xtrigger0
    Set_Timer_Configuration_false();
    ((trigger_irq_app_Type*) trigger_irq_app_BASE)->trigger_irq_cfg_b.xc_trigger_out_polarity = 1u; // 1 = falling edge
    ((trigger_irq_app_Type*) trigger_irq_app_BASE)->trigger_irq_msk_set_b.xc_trigger_out_edge = 1u;
    DRV_NVIC_EnableIRQ(trigger_out_edge0_IRQn);

    lRet = App_CifXApplicationDemo();
  }

  while(1);  /** do not return from main() */
}

7. Use set oem service parameter 6 and set trigger type request before channel init

//Deactivate Freerun
void AppPNS_HandleSetTriggerTypeReq( CIFX_PACKET* ptPkt)
{
HIL_SET_TRIGGER_TYPE_REQ_T *tSetTriggerReq= (HIL_SET_TRIGGER_TYPE_REQ_T *) ptPkt;
  memset(tSetTriggerReq,0,sizeof(HIL_SET_TRIGGER_TYPE_REQ_T));

  tSetTriggerReq->tHead.ulCmd=HIL_SET_TRIGGER_TYPE_REQ;
  tSetTriggerReq->tHead.ulLen=sizeof(HIL_SET_TRIGGER_TYPE_REQ_DATA_T);
  tSetTriggerReq->tHead.ulDest=HIL_PACKET_DEST_DEFAULT_CHANNEL;
  tSetTriggerReq->tData.usPdInHskTriggerType=HIL_TRIGGER_TYPE_PDIN_TIMED_ACTIVATION;
  tSetTriggerReq->tData.usPdOutHskTriggerType=HIL_TRIGGER_TYPE_PDOUT_TIMED_LATCH;
  tSetTriggerReq->tData.usSyncHskTriggerType=HIL_TRIGGER_TYPE_SYNC_NONE;
}

//Activate stack cycle counter to get it with xChannelIORead at DPM offset 6.
void AppPNS_SetOEM6( CIFX_PACKET* ptPkt )
{

  PNS_IF_SET_OEM_PARAMETERS_REQ_T *ptSetOEMReq = ( PNS_IF_SET_OEM_PARAMETERS_REQ_T* )ptPkt;
  memset( ptSetOEMReq, 0, sizeof(PNS_IF_SET_OEM_PARAMETERS_REQ_T));

  ptSetOEMReq->tHead.ulDest = HIL_PACKET_DEST_DEFAULT_CHANNEL;
  ptSetOEMReq->tHead.ulCmd  = PNS_IF_SET_OEM_PARAMETERS_REQ;
  ptSetOEMReq->tHead.ulLen  = 4 + sizeof(PNS_IF_SET_OEM_PARAMETERS_TYPE_6_T);
  ptSetOEMReq->tData.ulParameterType=PNS_IF_SET_OEM_PARAMETERS_TYPE_6;
  ptSetOEMReq->tParam.tType6Param.usIRTCycleCounterOffset= 0x6;
}

8. Write record indication for parameter 0x8030

To receive the parameter with index 0x8030 in the function write record indication add the following code:

else if (ptWriteRecordInd->tData.ulApi == 0 && ptWriteRecordInd->tData.ulSlot == 2 && ptWriteRecordInd->tData.ulSubslot == 1 &&
      ptWriteRecordInd->tData.ulIndex == 0x8030)  //Isochronous Mode Data
    {
      if (ptWriteRecordInd->tData.hDeviceHandle == 0 )  //PNS_IF_DEVICE_HANDLE_UNSPEC_OR_SUPERV_DA
      { /* access is denied for non io/io-supervisor ars */
        ptWriteRecordRes->tData.ulPnio = 0xDF80B600;
      }
      else if (ptWriteRecordInd->tData.ulLenToWrite != sizeof ( PNS_IF_PDU_ISOCHRONOUS_DATA_T ))
      { /* bad length */
      ptWriteRecordRes->tData.ulPnio = 0xDF80B100;
      }
      else
      { /* record data ok and will be used */
        value1=*(ptWriteRecordInd->tData.abRecordData+12);
        value2=*(ptWriteRecordInd->tData.abRecordData+13);
        CACF= ( uint16_t ) value1 << 8 | ( uint16_t )value2;

        value1=*(ptWriteRecordInd->tData.abRecordData+14);
        value2=*(ptWriteRecordInd->tData.abRecordData+15);
        s_ab_RecordIsochronusData_Slot2_Subslot1.usDataCycle= ( uint16_t ) value1 << 8 | ( uint16_t )value2;

        value1=*(ptWriteRecordInd->tData.abRecordData+16);
        value2=*(ptWriteRecordInd->tData.abRecordData+17);
        value3=*(ptWriteRecordInd->tData.abRecordData+18);
        value4=*(ptWriteRecordInd->tData.abRecordData+19);
        T_IO_Input= ( uint32_t ) value1 << 24 | ( uint32_t )value2 <<16 | ( uint32_t )value3 <<8 | ( uint32_t ) value4;
        s_ab_RecordIsochronusData_Slot2_Subslot1.ulIO_Input= T_IO_Input;

        value1=*(ptWriteRecordInd->tData.abRecordData+20);
        value2=*(ptWriteRecordInd->tData.abRecordData+21);
        value3=*(ptWriteRecordInd->tData.abRecordData+22);
        value4=*(ptWriteRecordInd->tData.abRecordData+23);
        T_IO_Output= ( uint32_t ) value1 << 24 | ( uint32_t )value2 <<16 | ( uint32_t )value3 <<8 | ( uint32_t ) value4;
        s_ab_RecordIsochronusData_Slot2_Subslot1.ulIO_Output= T_IO_Output;

        Set_Isochron_Data_available_true();

        ptWriteRecordRes->tData.ulPnio = 0;
        ptWriteRecordRes->tData.ulWriteLen = sizeof ( PNS_IF_PDU_ISOCHRONOUS_DATA_T );
      }
    }

9. Parameter end indication

Disable the 1ms Input data handler and 1ms output data handler. Calculate the new timings for IO data handler timers and reconfigurate the timers. The timers will be startet with sny0/xtrigger0 signal in the interrupt service XCTRIGGER0_IRQHandler.

void AppPNS_HandleParameterEndInd( APP_DATA_T* ptAppData)
{
  if (Get_Isochron_Data_available() == 1)
  {
    //calculate Timongs for isocron application
    T_IO_Input_Event = ((((uint32_t) s_ab_RecordIsochronusData_Slot2_Subslot1.usDataCycle) * 3125)*10) - T_IO_Input; //in nano seconds
    T_IO_Output_Event = s_ab_RecordIsochronusData_Slot2_Subslot1.ulIO_Output -T_AO; //in nano seconds
    HOSTAL_Callbacks_Disable(); // Disable 1ms timer
    HOSTAL_Callbacks_Disable2(); // Disable 1ms timer
    HOSTAL_Init_Isochron_Input_Timer( T_IO_Input_Event); // set Timer with new timing
    HOSTAL_Init_Isochron_Output_Timer( T_IO_Output_Event); // set Timer with new timing
    Set_Timer_Configuration_true();
  }

  PNS_IF_PARAM_END_RSP_T* ptParameterEndRes=( PNS_IF_PARAM_END_RSP_T*) &(ptAppData->tPacket);

  ptParameterEndRes->tHead.ulCmd |= 0x01; //PNS_IF_PARAM_END_RES;
  ptParameterEndRes->tHead.ulLen  = 8;
  ptParameterEndRes->tHead.ulSta  = SUCCESS_HIL_OK;
  ptParameterEndRes->tData.fSendApplicationReady = 1;

  (void)Pkt_SendPacket(ptAppData->hChannel[0], &ptAppData->tPacket, TX_TIMEOUT);


  return;
}

10. IO data hanlders

In the input IO data handler not much has changed. The input data handler now reads the input data and the stack cycle counter:

App_stackcyclecounter=ptAppData→tInputData.stackcyclecounter;

Another change of this input data handler is, that in case of isochron connection, the timer will be disabled, because the time shall restart with XCTRIGGER0_IRQHandler.

The output data handler is different. In case of isochron connection, the application needs to check if the correct cycle is reached. For that the Index 0x8030 has the CACF value. If this value is not 1, the application needs to check for the correct cycle like this: if((App_stackcyclecounter % (Get_Isochron_DataCycle() * Get_CACF()))==0)

In the output data handler, in case of isochron connection, the timer will be disabled, because the time shall restart with XCTRIGGER0_IRQHandler.

// Do update of RT modules isochron modules in the if cycle counter matches to CACF
    if((App_stackcyclecounter % (Get_Isochron_DataCycle() * Get_CACF()))==0)
    {
      HOSTAL_Sensor_GetData(0, &ptAppData->tOutputData.usSensor_1_Output);
      ptAppData->tOutputData.bSensor_1_State = HOSTAL_Sensor_GetState(0, &ptAppData->tAcyclicData.usSensor1_StatusCode);

      HOSTAL_Sensor_GetData(1, &ptAppData->tOutputData.usSensor_2_Output);
      ptAppData->tOutputData.bSensor_2_State = HOSTAL_Sensor_GetState(1, &ptAppData->tAcyclicData.usSensor2_StatusCode);

      ptAppData->tOutputData.bActuator_1_State = HOSTAL_Actuator_GetState(0, &ptAppData->tAcyclicData.usActuator1_StatusCode);
      ptAppData->tOutputData.bActuator_2_State = HOSTAL_Actuator_GetState(1, &ptAppData->tAcyclicData.usActuator2_StatusCode);

      lRet = xChannelIOWrite(ptAppData->hChannel[0], 0, 0, sizeof(ptAppData->tOutputData), &ptAppData->tOutputData, 0);
      if(lRet != CIFX_NO_ERROR)
      {
        /** Something failed?
         * Reason for error could be:
         * 1) netX is not "ready" yet. May happen during startup.
         * 2) netX is not "running" yet. May happen during startup in case the netX is not fully configured yet.
         * 3) netX has not yet established an IO connection. */
      }
    }
    else
    {
      // If an only RT module exists, do the update of this data. This example does not have 2 modules.
    }
    HOSTAL_Callbacks_Disable2(); // Disable Timer. Timer will be restarted as soon as Syn0/XCTRIGGER0_IRQHandler() comes

11. Sycon cifxCard and netHost

Follow these steps:

PNS Configure Profinet IO Controller in IRT mode

and for isochron only activate the isochron mode:

12. TIA Portal

Create first a Synchronous Cycle OB.

Go to the device and set it to IRT mode. Under domain settings it is possbile to set the send clock for the plc.

Go to the isochronous mode. Activate isochronous mode and the modules that shall run isochron. Here the plc send clock is set to 500µs and the applicaton cycle is 1ms. That will lead to a CACF = 2.

Go to the output submodul and connect it with the Synchronous Cycle OB.






  • No labels