EtherCAT主站SOEM源码解析----同步管理器SM配置

本文介绍SOEM(Simple Open Source Master)如何配置从站的SM(SyncManager)寄存器。
基于SOEM-1.3.1

1、SM寄存器

SM配置寄存器从0x800开始,每个通道使用8个字节,如下图所示:
这里写图片描述

其中,物理起始地址和控制寄存器使用从站EEPROM中的值,长度根据PDO中映射的数据长度计算。

2、寄存器初始化

初始化过程中,SOEM会通过SII接口读取从站EEPROM中SM信息(分类信息类型为41),并赋给相应的结构体,

具体代码在/soem/EthercatConfig.c 的函数ecx_config_init()中:

            nSM = ecx_siiSM(context, slave, context->eepSM); //读取从站EEPROM中SM配置信息
            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、更新数据长度寄存器

在函数ecx_map_sii中更新SM数据长度寄存器:
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
      EC_PRINT("  SII Isize:%d\n", Isize);               
      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
      {   
         //如果有TxPDO指定了该SM,则该SM类型为4,即过程数据输入,SOEM不care EEPROM中的SM配置
         //更改了SM的数据长度和类型,物理地址和控制标志采用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
      EC_PRINT("  SII Osize:%d\n", Osize);               
      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ ) //如果有RxPDO指定了该SM,则该SM类型为3,即过程数据输出
      {   
         if (eepPDO.SMbitsize[nSM] > 0)
         {   
            //向上取整,SM数据长度,覆盖初始化时的值
            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、写入寄存器

在ecx_map_sm()函数中通过FPWR命令将值写入从站对应寄存器:

   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没有被用作邮箱通信
   {
      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没有被用作邮箱通信
   {
      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;  //向上取整,  例如Ibits=13, 结果Ibytes=2
   }
   if (context->slavelist[slave].Obits > 7)
   {
      context->slavelist[slave].Obytes = (context->slavelist[slave].Obits + 7) / 8;
   }

   return 1;
}

附:寄存器详情

其中控制寄存器各位的含义如下:
这里写图片描述

状态寄存器各位的含义如下:
这里写图片描述

激活控制寄存器各位含义为:
这里写图片描述

PDI控制寄存器各位含义为:
这里写图片描述

你可能感兴趣的:(EtherCAT主站SOEM源码解析----同步管理器SM配置)