Analysis of SOEM source code of EtherCAT master station -- SM configuration of synchronization manager

Keywords: PDO Programming

This article describes how SOEM(Simple Open Source Master) configures the SM(SyncManager) register of the slave.
Based on SOEM-1.3.1.   

1. SM register

SM configuration register starts from 0x800, and each channel uses 8 bytes, as shown in the following figure:

The physical start address and control register use the value in the slave EEPROM, and the length is calculated according to the data length mapped in PDO.

2. Register initialization

During initialization, the SOEM will read the SM information in the slave EEPROM through the SII interface (the classification information type is 41), and assign it to the corresponding structure,

The specific code is in the function ECX of / soem/EthercatConfig.c_ config_ In init():

            nSM = ecx_siiSM(context, slave, context->eepSM); //Read SM configuration information in slave EEPROM
            if (nSM>0)
            {   
               context->slavelist[slave].SM[0].StartAddr = htoes(context->eepSM->PhStart);
               context->slavelist[slave].SM[0].SMlength = htoes(context->eepSM->Plength);
               context->slavelist[slave].SM[0].SMflags = 
                  htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));
               SMc = 1;
               while ((SMc < EC_MAXSM) &&  ecx_siiSMnext(context, slave, context->eepSM, SMc))
               {
                  context->slavelist[slave].SM[SMc].StartAddr = htoes(context->eepSM->PhStart);
                  context->slavelist[slave].SM[SMc].SMlength = htoes(context->eepSM->Plength);
                  context->slavelist[slave].SM[SMc].SMflags = 
                     htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));
                  SMc++;
               }   
            } 

3. Update data length register

In function ECX_ map_ Update SM data length register in SII:
static int ecx_map_sii(ecx_contextt *context, uint16 slave)
{
   int Isize, Osize;
   int nSM;
   ec_eepromPDOt eepPDO;

   Osize = context->slavelist[slave].Obits;
   Isize = context->slavelist[slave].Ibits;

   if (!Isize && !Osize) /* find PDO in previous slave with same ID */
   {
      (void)ecx_lookup_mapping(context, slave, &Osize, &Isize);
   }
   if (!Isize && !Osize) /* find PDO mapping by SII */
   {
      memset(&eepPDO, 0, sizeof(eepPDO));
      Isize = (int)ecx_siiPDO(context, slave, &eepPDO, 0);  //TxPDO corresponding to slave station
      EC_PRINT("  SII Isize:%d\n", Isize);               
      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
      {   
         //If TxPDO specifies the SM, the SM type is 4, i.e. process data input. SOEM does not care about the SM configuration in EEPROM
         //The data length and type of SM have been changed. The physical address and control flag adopt the value of EEPROM.
         if (eepPDO.SMbitsize[nSM] > 0)   
         {   
            context->slavelist[slave].SM[nSM].SMlength =  htoes((eepPDO.SMbitsize[nSM] + 7) / 8);
            context->slavelist[slave].SMtype[nSM] = 4;
            EC_PRINT("    SM%d length %d\n", nSM, eepPDO.SMbitsize[nSM]);
         }   
      }   
      Osize = (int)ecx_siiPDO(context, slave, &eepPDO, 1);   //RxPDO corresponding to slave station
      EC_PRINT("  SII Osize:%d\n", Osize);               
      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ ) //If the SM is specified by RxPDO, the SM type is 3, i.e. process data output
      {   
         if (eepPDO.SMbitsize[nSM] > 0)
         {   
            //Round up, SM data length, overwrite initialization value
            context->slavelist[slave].SM[nSM].SMlength =  htoes((eepPDO.SMbitsize[nSM] + 7) / 8);
            context->slavelist[slave].SMtype[nSM] = 3;
            EC_PRINT("    SM%d length %d\n", nSM, eepPDO.SMbitsize[nSM]);
         }   
      }   
   }
   context->slavelist[slave].Obits = Osize;
   context->slavelist[slave].Ibits = Isize;
   EC_PRINT("     ISIZE:%d %d OSIZE:%d\n", 
      context->slavelist[slave].Ibits, Isize,context->slavelist[slave].Obits);    

   return 1;
}

4. Write register

At ECX_ map_ In the sm() function, write the value to the corresponding register of the slave station through the FPWR command:

   static int ecx_map_sm(ecx_contextt *context, uint16 slave)
{
   uint16 configadr;
   int nSM;

   configadr = context->slavelist[slave].configadr;

   EC_PRINT("  SM programming\n");  
   if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[0].StartAddr)  //SM0 is not used as mailbox communication
   {
      ecx_FPWR(context->port, configadr, ECT_REG_SM0, 
         sizeof(ec_smt), &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);
      EC_PRINT("    SM0 Type:%d StartAddr:%4.4x Flags:%8.8x\n", 
          context->slavelist[slave].SMtype[0],            //1:MAIL OUT 2: MAIL IN    3: Process Out   4:Process IN
          context->slavelist[slave].SM[0].StartAddr, 
          context->slavelist[slave].SM[0].SMflags);   
   }
   if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[1].StartAddr) //SM1 is not used as a mailbox communication
   {
      ecx_FPWR(context->port, configadr, ECT_REG_SM1, 
         sizeof(ec_smt), &context->slavelist[slave].SM[1], EC_TIMEOUTRET3);
      EC_PRINT("    SM1 Type:%d StartAddr:%4.4x Flags:%8.8x\n", 
          context->slavelist[slave].SMtype[1], 
          context->slavelist[slave].SM[1].StartAddr, 
          context->slavelist[slave].SM[1].SMflags);   
   }
   /* program SM2 to SMx */
   for( nSM = 2 ; nSM < EC_MAXSM ; nSM++ )
   {   
      if (context->slavelist[slave].SM[nSM].StartAddr)
      {
         /* check if SM length is zero -> clear enable flag */
         if( context->slavelist[slave].SM[nSM].SMlength == 0) 
         {
            context->slavelist[slave].SM[nSM].SMflags = 
               htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) & EC_SMENABLEMASK);
         }
         ecx_FPWR(context->port, configadr, ECT_REG_SM0 + (nSM * sizeof(ec_smt)),
            sizeof(ec_smt), &context->slavelist[slave].SM[nSM], EC_TIMEOUTRET3);
         EC_PRINT("    SM%d Type:%d StartAddr:%4.4x Flags:%8.8x\n", nSM,
             context->slavelist[slave].SMtype[nSM], 
             context->slavelist[slave].SM[nSM].StartAddr, 
             context->slavelist[slave].SM[nSM].SMflags);   
      }
   }
   if (context->slavelist[slave].Ibits > 7)
   {
      context->slavelist[slave].Ibytes = (context->slavelist[slave].Ibits + 7) / 8;  //Round up, for example, Ibits=13, resulting in Ibytes=2
   }
   if (context->slavelist[slave].Obits > 7)
   {
      context->slavelist[slave].Obytes = (context->slavelist[slave].Obits + 7) / 8;
   }

   return 1;
}

Attachment: register details

The meaning of each control register is as follows:

The meaning of each status register is as follows:

Each meaning of the activation control register is:

The meanings of PDI control register are as follows:

Posted by shseraj on Thu, 04 Jun 2020 10:22:10 -0700