EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析

EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析

  • 一 ethercatconfig.h/c文件功能预览:
  • 二 ethercatconfig.h/c 中主要函数的作用:
    • 2.1.1 `ec_config_init(uint8 usetable)` 和 `ecx_config_init(ecx_contextt *context, uint8 usetable)`:
    • 2.1.2 `ec_config_map(void *pIOmap)`:
    • 2.1.3 `ec_config_overlap_map(void *pIOmap)` 和 `ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)`:
    • 2.1.4 `ec_config(uint8 usetable, void *pIOmap)` 和 `ecx_config_init(ecx_contextt *context, uint8 usetable)`:
    • 2.1.5 `ec_recover_slave(uint16 slave, int timeout)` 和 `ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)`:
    • 2.1.6 `ec_reconfig_slave(uint16 slave, int timeout)` 和 `ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)`:
  • 三 ethercatconfig.c 原文件的中文注释
  • 该文档修改记录:
  • 总结

一 ethercatconfig.h/c文件功能预览:

函数 功能和用途 使用场景
ec_config_init (仅适用于EC_VER1) 初始化EtherCAT主站配置,用于主站初始化。 应用程序启动时进行EtherCAT主站的初始化。
ecx_config_init 初始化EtherCAT主站配置,用于主站初始化。 应用程序启动时进行EtherCAT主站的初始化。
ec_config_map_group (仅适用于EC_VER1) 映射EtherCAT从站数据到主站,配置数据映射到指定组。 初始化后,配置数据映射到指定的数据组。
ecx_config_map_group 映射EtherCAT从站数据到主站,配置数据映射到指定组。 初始化后,配置数据映射到指定的数据组。
ec_config_overlap_map_group (仅适用于EC_VER1) 映射EtherCAT从站数据到主站,处理重叠数据映射。 处理重叠数据映射配置时,将多个数据组映射到主站。
ecx_config_overlap_map_group 映射EtherCAT从站数据到主站,处理重叠数据映射。 处理重叠数据映射配置时,将多个数据组映射到主站。
ec_recover_slave (仅适用于EC_VER1) 尝试从站的恢复,用于从站恢复。 当从站失去连接或通信出现问题时,尝试从站的恢复。
ecx_recover_slave 尝试从站的恢复,用于从站恢复。 当从站失去连接或通信出现问题时,尝试从站的恢复。
ec_reconfig_slave (仅适用于EC_VER1) 重新配置从站,用于运行时更改从站配置。 应用程序需要在运行时更改从站配置时使用。
ecx_reconfig_slave 重新配置从站,用于运行时更改从站配置。 应用程序需要在运行时更改从站配置时使用。

二 ethercatconfig.h/c 中主要函数的作用:

ethercatconfig.h 文件通常包含了用户或开发者定义的配置参数和宏定义,用于指定 EtherCAT 网络的特性,如周期时间、数据对象的大小、网络拓扑结构等。开发者可以根据其应用程序的需求,在这个文件中进行定制。以下是一些可能包含在 ethercatconfig.h 文件中的配置选项:

  • 通信周期:定义 EtherCAT 网络的通信周期,这对于实时性能至关重要。

  • 从站数量:指定在网络中的 EtherCAT 从站数量。

  • 从站配置:每个从站的配置参数,包括地址、数据对象大小等。

  • 网络拓扑:定义网络中不同从站的连接方式和物理拓扑。

  • 数据对象定义:定义 EtherCAT 数据对象(PDO)的结构和映射。

ethercatconfig.c 文件通常包含 SOEM 库的初始化代码,它使用 ethercatconfig.h 中的配置参数来初始化 EtherCAT 主站并设置网络。这个文件负责执行以下操作:

  • 初始化 EtherCAT 主站:配置主站并初始化必要的数据结构。

  • 从站配置:为每个从站设置相应的参数,以确保网络中的从站能够正常工作。

  • 网络初始化:设置 EtherCAT 网络的物理连接、通信周期等参数。

  • 启动 EtherCAT 通信:启动 EtherCAT 通信,使网络中的从站可以开始交换数据。

总之,ethercatconfig.h 和 ethercatconfig.c 文件是 SOEM 库中用于配置和初始化 EtherCAT 网络的关键文件。


2.1.1 ec_config_init(uint8 usetable)ecx_config_init(ecx_contextt *context, uint8 usetable)

  • 这些函数用于初始化EtherCAT主站的配置。
  • 参数 usetable 是一个标志,用于指示是否使用表格(table)来配置主站。如果设置为1,表示使用表格;如果设置为0,表示手动配置。
  • 在初始化过程中,这些函数通常会设置网络接口、配置Slave设备的数量、数据映射表等。如果使用表格,则初始化会包括加载表格信息。

2.1.2 ec_config_map(void *pIOmap)

  • 此函数用于将Slave设备的输入和输出数据映射到主站的过程数据区域。
  • 参数 pIOmap 是指向数据映射的指针,它包含了关于Slave设备数据映射的信息。
  • 主站需要知道如何读取和写入每个Slave设备的数据,这是通过数据映射来实现的。这个函数负责确保正确的数据映射。

2.1.3 ec_config_overlap_map(void *pIOmap)ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)

  • 这些函数用于处理数据映射中的重叠问题,以确保数据传输不会发生冲突。
  • 参数 pIOmap 是指向数据映射的指针。
  • 参数 group 可用于指定数据映射到特定的数据组。在某些情况下,数据映射可能会重叠,这可能导致数据混淆。这些函数处理这些重叠,以确保数据正常传输。

2.1.4 ec_config(uint8 usetable, void *pIOmap)ecx_config_init(ecx_contextt *context, uint8 usetable)

  • 这些函数将配置的初始化和数据映射组合在一起,以便主站的整体配置。
  • 参数 usetable 指示是否使用表格(table)来配置主站。
  • 参数 pIOmap 是指向数据映射的指针。
  • 这些函数在主站应用程序中通常用于一次性配置整个EtherCAT主站,包括初始化和数据映射。

2.1.5 ec_recover_slave(uint16 slave, int timeout)ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)

  • 这些函数用于尝试从错误状态中恢复指定的Slave设备。
  • 参数 slave 指定要恢复的Slave设备。
  • 参数 timeout 指定恢复操作的超时时间。如果Slave设备发生错误状态,这些函数可以尝试将其恢复到正常状态。

2.1.6 ec_reconfig_slave(uint16 slave, int timeout)ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)

  • 这些函数用于重新配置指定的Slave设备,通常在网络配置发生更改时使用。
  • 参数 slave 指定要重新配置的Slave设备。
  • 参数 timeout 指定重新配置操作的超时时间。当需要更改Slave设备的配置时,这些函数可用于重新配置它们,确保网络正常运行。

三 ethercatconfig.c 原文件的中文注释

/*
 * 根据带有异常的GNU通用公共许可证第2版许可。详细的许可信息请参见项目根目录中的LICENSE文件
 */

/** \file
 * \brief
 * 用于EtherCAT主站的配置模块。
 *
 * 在成功使用ec_init()或ec_init_redundant()进行初始化后,可以使用此模块自动配置从站。
 */

#include 
#include 
#include "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatcoe.h"
#include "ethercatsoe.h"
#include "ethercatconfig.h"

typedef struct
{
   int thread_n;
   int running;
   ecx_contextt *context;
   uint16 slave;
} ecx_mapt_t;

ecx_mapt_t ecx_mapt[EC_MAX_MAPT];
#if EC_MAX_MAPT > 1
OSAL_THREAD_HANDLE ecx_threadh[EC_MAX_MAPT];
#endif

#ifdef EC_VER1
/** 从站配置结构 */
typedef const struct
{
   /** 从站制造商代码 */
   uint32           man;
   /** 从站ID */
   uint32           id;
   /** 可读的名称 */
   char             name[EC_MAXNAME + 1];
   /** 数据类型 */
   uint8            Dtype;
   /** 输入位数 */
   uint16            Ibits;
   /** 输出位数 */
   uint16           Obits;
   /** SyncManager 2地址 */
   uint16           SM2a;
   /** SyncManager 2标志 */
   uint32           SM2f;
   /** SyncManager 3地址 */
   uint16           SM3a;
   /** SyncManager 3标志 */
   uint32           SM3f;
   /** FMMU 0激活 */
   uint8            FM0ac;
   /** FMMU 1激活 */
   uint8            FM1ac;
} ec_configlist_t;

#include "ethercatconfiglist.h"
#endif

/** 用于邮箱从站的标准SM0标志配置 */
#define EC_DEFAULTMBXSM0  0x00010026
/** 用于邮箱从站的标准SM1标志配置 */
#define EC_DEFAULTMBXSM1  0x00010022
/** 用于数字输出从站的标准SM0标志配置 */
#define EC_DEFAULTDOSM0   0x00010044

#ifdef EC_VER1
/** 在标准配置列表ec_configlist[]中查找从站
 *
 * @param[in] man      = 制造商
 * @param[in] id       = ID
 * @return 在ec_configlist[]中找到时的索引,否则返回0
 */
int ec_findconfig( uint32 man, uint32 id)
{
   int i = 0;

   do
   {
      i++;
   } while ( (ec_configlist[i].man != EC_CONFIGEND) &&
           ((ec_configlist[i].man != man) || (ec_configlist[i].id != id)) );
   if (ec_configlist[i].man == EC_CONFIGEND)
   {
      i = 0;
   }
   return i;
}
#endif

void ecx_init_context(ecx_contextt *context)
{
   int lp;
   *(context->slavecount) = 0;
   /* 清除ec_slave数组 */
   memset(context->slavelist, 0x00, sizeof(ec_slavet) * context->maxslave);
   memset(context->grouplist, 0x00, sizeof(ec_groupt) * context->maxgroup);
   /* 清除从站EEPROM缓存,实际上不会读取任何EEPROM */
   ecx_siigetbyte(context, 0, EC_MAXEEPBUF);
   for(lp = 0; lp < context->maxgroup; lp++)
   {
      /* 每个组条目的默认起始地址 */
      context->grouplist[lp].logstartaddr = lp << EC_LOGGROUPOFFSET;
   }
}

int ecx_detect_slaves(ecx_contextt *context)
{
   uint8  b;
   uint16 w;
   int    wkc;

   /* 进行特殊的预初始化寄存器写操作,以启用老netX100从站的MAC[1]本地管理位设置 */
   b = 0x00;
   ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS, sizeof(b), &b, EC_TIMEOUTRET3);     /* 忽略Alias寄存器 */
   b = EC_STATE_INIT | EC_STATE_ACK;
   ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3);       /* 将所有从站重置为Init状态 */
   /* 现在netX100应该正常工作 */
   ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3);       /* 将所有从站重置为Init状态 */
   wkc = ecx_BRD(context->port, 0x0000, ECT_REG_TYPE, sizeof(w), &w, EC_TIMEOUTSAFE);  /* 检测从站数量 */
   if (wkc > 0)
   {
      /* 这是严格的“小于”关系,因为主站是“从站0” */
      if (wkc < EC_MAXSLAVE)
      {
         *(context->slavecount) = wkc;
      }
      else
      {
         EC_PRINT("错误:网络上从站太多:num_slaves=%d,EC_MAXSLAVE=%d\n",
               wkc, EC_MAXSLAVE);
         return EC_SLAVECOUNTEXCEEDED;
      }
   }
   return wkc;
}


static void ecx_set_slaves_to_default(ecx_contextt *context)
{
   uint8 b;
   uint16 w;
   uint8 zbuf[64];
   memset(&zbuf, 0x00, sizeof(zbuf));
   b = 0x00;
   ecx_BWR(context->port, 0x0000, ECT_REG_DLPORT      , sizeof(b) , &b, EC_TIMEOUTRET3);     /* 停用循环手动 */
   w = htoes(0x0004);
   ecx_BWR(context->port, 0x0000, ECT_REG_IRQMASK     , sizeof(w) , &w, EC_TIMEOUTRET3);     /* 设置中断掩码 */
   ecx_BWR(context->port, 0x0000, ECT_REG_RXERR       , 8         , &zbuf, EC_TIMEOUTRET3);  /* 重置CRC计数器 */
   ecx_BWR(context->port, 0x0000, ECT_REG_FMMU0       , 16 * 3    , &zbuf, EC_TIMEOUTRET3);  /* 重置FMMU */
   ecx_BWR(context->port, 0x0000, ECT_REG_SM0         , 8 * 4     , &zbuf, EC_TIMEOUTRET3);  /* 重置SyncM */
   b = 0x00;
   ecx_BWR(context->port, 0x0000, ECT_REG_DCSYNCACT   , sizeof(b) , &b, EC_TIMEOUTRET3);     /* 重置激活寄存器 */
   ecx_BWR(context->port, 0x0000, ECT_REG_DCSYSTIME   , 4         , &zbuf, EC_TIMEOUTRET3);  /* 重置系统时间+偏移 */
   w = htoes(0x1000);
   ecx_BWR(context->port, 0x0000, ECT_REG_DCSPEEDCNT  , sizeof(w) , &w, EC_TIMEOUTRET3);     /* DC速度启动 */
   w = htoes(0x0c00);
   ecx_BWR(context->port, 0x0000, ECT_REG_DCTIMEFILT  , sizeof(w) , &w, EC_TIMEOUTRET3);     /* DC滤波表达式 */
   b = 0x00;
   ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS     , sizeof(b) , &b, EC_TIMEOUTRET3);     /* 忽略Alias寄存器 */
   b = EC_STATE_INIT | EC_STATE_ACK;
   ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL       , sizeof(b) , &b, EC_TIMEOUTRET3);     /* 将所有从站重置为Init */
   b = 2;
   ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG      , sizeof(b) , &b, EC_TIMEOUTRET3);     /* 强制从PDI读取EEPROM */
   b = 0;
   ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG      , sizeof(b) , &b, EC_TIMEOUTRET3);     /* 将EEPROM设置为主站 */
}


#ifdef EC_VER1
static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
{
   int cindex;
   ec_slavet *csl;

   csl = &(context->slavelist[slave]);
   cindex = ec_findconfig( csl->eep_man, csl->eep_id );
   csl->configindex= cindex;
   /* slave found in configuration table ? */
   if (cindex)
   {
      csl->Dtype = ec_configlist[cindex].Dtype;
      strcpy(csl->name ,ec_configlist[cindex].name);
      csl->Ibits = ec_configlist[cindex].Ibits;
      csl->Obits = ec_configlist[cindex].Obits;
      if (csl->Obits)
      {
         csl->FMMU0func = 1;
      }
      if (csl->Ibits)
      {
         csl->FMMU1func = 2;
      }
      csl->FMMU[0].FMMUactive = ec_configlist[cindex].FM0ac;
      csl->FMMU[1].FMMUactive = ec_configlist[cindex].FM1ac;
      csl->SM[2].StartAddr = htoes(ec_configlist[cindex].SM2a);
      csl->SM[2].SMflags = htoel(ec_configlist[cindex].SM2f);
      /* simple (no mailbox) output slave found ? */
      if (csl->Obits && !csl->SM[2].StartAddr)
      {
         csl->SM[0].StartAddr = htoes(0x0f00);
         csl->SM[0].SMlength = htoes((csl->Obits + 7) / 8);
         csl->SM[0].SMflags = htoel(EC_DEFAULTDOSM0);
         csl->FMMU[0].FMMUactive = 1;
         csl->FMMU[0].FMMUtype = 2;
         csl->SMtype[0] = 3;
      }
      /* complex output slave */
      else
      {
         csl->SM[2].SMlength = htoes((csl->Obits + 7) / 8);
         csl->SMtype[2] = 3;
      }
      csl->SM[3].StartAddr = htoes(ec_configlist[cindex].SM3a);
      csl->SM[3].SMflags = htoel(ec_configlist[cindex].SM3f);
      /* simple (no mailbox) input slave found ? */
      if (csl->Ibits && !csl->SM[3].StartAddr)
      {
         csl->SM[1].StartAddr = htoes(0x1000);
         csl->SM[1].SMlength = htoes((csl->Ibits + 7) / 8);
         csl->SM[1].SMflags = htoel(0x00000000);
         csl->FMMU[1].FMMUactive = 1;
         csl->FMMU[1].FMMUtype = 1;
         csl->SMtype[1] = 4;
      }
      /* complex input slave */
      else
      {
         csl->SM[3].SMlength = htoes((csl->Ibits + 7) / 8);
         csl->SMtype[3] = 4;
      }
   }
   return cindex;
}
#else
static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
{
   (void)context;
   (void)slave;
   return 0;
}
#endif

/* If slave has SII and same slave ID done before, use previous data.
 * This is safe because SII is constant for same slave ID.
 */
static int ecx_lookup_prev_sii(ecx_contextt *context, uint16 slave)
{
   int i, nSM;
   if ((slave > 1) && (*(context->slavecount) > 0))
   {
      i = 1;
      while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||
             (context->slavelist[i].eep_id  != context->slavelist[slave].eep_id ) ||
             (context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&
            (i < slave))
      {
         i++;
      }
      if(i < slave)
      {
         context->slavelist[slave].CoEdetails = context->slavelist[i].CoEdetails;
         context->slavelist[slave].FoEdetails = context->slavelist[i].FoEdetails;
         context->slavelist[slave].EoEdetails = context->slavelist[i].EoEdetails;
         context->slavelist[slave].SoEdetails = context->slavelist[i].SoEdetails;
         if(context->slavelist[i].blockLRW > 0)
         {
            context->slavelist[slave].blockLRW = 1;
            context->slavelist[0].blockLRW++;
         }
         context->slavelist[slave].Ebuscurrent = context->slavelist[i].Ebuscurrent;
         context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
         memcpy(context->slavelist[slave].name, context->slavelist[i].name, EC_MAXNAME + 1);
         for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
         {
            context->slavelist[slave].SM[nSM].StartAddr = context->slavelist[i].SM[nSM].StartAddr;
            context->slavelist[slave].SM[nSM].SMlength  = context->slavelist[i].SM[nSM].SMlength;
            context->slavelist[slave].SM[nSM].SMflags   = context->slavelist[i].SM[nSM].SMflags;
         }
         context->slavelist[slave].FMMU0func = context->slavelist[i].FMMU0func;
         context->slavelist[slave].FMMU1func = context->slavelist[i].FMMU1func;
         context->slavelist[slave].FMMU2func = context->slavelist[i].FMMU2func;
         context->slavelist[slave].FMMU3func = context->slavelist[i].FMMU3func;
         EC_PRINT("Copy SII slave %d from %d.\n", slave, i);
         return 1;
      }
   }
   return 0;
}

/** 枚举和初始化所有从站。
 *
 * @param[in] context      = 上下文结构
 * @param[in] usetable     = 使用配置表来初始化从站时为TRUE,否则为FALSE
 * @return 从站发现数据报的工作计数 = 找到的从站数量
 */
int ecx_config_init(ecx_contextt *context, uint8 usetable)
{
   uint16 slave, ADPh, configadr, ssigen;
   uint16 topology, estat;
   int16 topoc, slavec, aliasadr;
   uint8 b, h;
   uint8 SMc;
   uint32 eedat;
   int wkc, cindex, nSM;
   uint16 val16;

   EC_PRINT("ec_config_init %d\n", usetable);
   ecx_init_context(context);
   wkc = ecx_detect_slaves(context);
   if (wkc > 0)
   {
      ecx_set_slaves_to_default(context);
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         ADPh = (uint16)(1 - slave);
         val16 = ecx_APRDw(context->port, ADPh, ECT_REG_PDICTL, EC_TIMEOUTRET3); /* 读取从站的接口类型 */
         context->slavelist[slave].Itype = etohs(val16);
         /* 为了提高网络帧的可读性,使用节点偏移 */
         /* 这不会影响可寻址从站的数量(自动循环) */
         ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(slave + EC_NODEOFFSET), EC_TIMEOUTRET3); /* 设置从站的节点地址 */
         if (slave == 1)
         {
            b = 1; /* 对于第一个从站,禁用非EtherCAT帧 */
         }
         else
         {
            b = 0; /* 对于后续从站,允许所有帧通过 */
         }
         ecx_APWRw(context->port, ADPh, ECT_REG_DLCTL, htoes(b), EC_TIMEOUTRET3); /* 设置非EtherCAT帧的行为 */
         configadr = ecx_APRDw(context->port, ADPh, ECT_REG_STADR, EC_TIMEOUTRET3);
         configadr = etohs(configadr);
         context->slavelist[slave].configadr = configadr;
         ecx_FPRD(context->port, configadr, ECT_REG_ALIAS, sizeof(aliasadr), &aliasadr, EC_TIMEOUTRET3);
         context->slavelist[slave].aliasadr = etohs(aliasadr);
         ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET3);
         estat = etohs(estat);
         if (estat & EC_ESTAT_R64) /* 检查从站是否支持读取8字节数据块 */
         {
            context->slavelist[slave].eep_8byte = 1;
         }
         ecx_readeeprom1(context, slave, ECT_SII_MANUF); /* Manuf */
      }
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* Manuf */
         context->slavelist[slave].eep_man = etohl(eedat);
         ecx_readeeprom1(context, slave, ECT_SII_ID); /* ID */
      }
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* ID */
         context->slavelist[slave].eep_id = etohl(eedat);
         ecx_readeeprom1(context, slave, ECT_SII_REV); /* revision */
      }
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* revision */
         context->slavelist[slave].eep_rev = etohl(eedat);
         ecx_readeeprom1(context, slave, ECT_SII_RXMBXADR); /* write mailbox address + mailboxsize */
      }
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* write mailbox address and mailboxsize */
         context->slavelist[slave].mbx_wo = (uint16)LO_WORD(etohl(eedat));
         context->slavelist[slave].mbx_l = (uint16)HI_WORD(etohl(eedat));
         if (context->slavelist[slave].mbx_l > 0)
         {
            ecx_readeeprom1(context, slave, ECT_SII_TXMBXADR); /* read mailbox offset */
         }
      }
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         if (context->slavelist[slave].mbx_l > 0)
         {
            eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* read mailbox offset */
            context->slavelist[slave].mbx_ro = (uint16)LO_WORD(etohl(eedat)); /* read mailbox offset */
            context->slavelist[slave].mbx_rl = (uint16)HI_WORD(etohl(eedat)); /* read mailbox length */
            if (context->slavelist[slave].mbx_rl == 0)
            {
               context->slavelist[slave].mbx_rl = context->slavelist[slave].mbx_l;
            }
            ecx_readeeprom1(context, slave, ECT_SII_MBXPROTO);
         }
         configadr = context->slavelist[slave].configadr;
         val16 = ecx_FPRDw(context->port, configadr, ECT_REG_ESCSUP, EC_TIMEOUTRET3);
         if ((etohs(val16) & 0x04) > 0)  /* 支持DC? */
         {
            context->slavelist[slave].hasdc = TRUE;
         }
         else
         {
            context->slavelist[slave].hasdc = FALSE;
         }
         topology = ecx_FPRDw(context->port, configadr, ECT_REG_DLSTAT, EC_TIMEOUTRET3); /* 从DL状态中提取拓扑信息 */
         topology = etohs(topology);
         h = 0;
         b = 0;
         if ((topology & 0x0300) == 0x0200) /* 端口0打开并已建立通信 */
         {
            h++;
            b |= 0x01;
         }
         if ((topology & 0x0c00) == 0x0800) /* 端口1打开并已建立通信 */
         {
            h++;
            b |= 0x02;
         }
         if ((topology & 0x3000) == 0x2000) /* 端口2打

         {
            h++;
            b |= 0x04;
         }
         if ((topology & 0xc000) == 0x8000) /* 端口3打开并已建立通信 */
         {
            h++;
            b |= 0x08;
         }
         /* ptype = 物理类型 */
         val16 = ecx_FPRDw(context->port, configadr, ECT_REG_PORTDES, EC_TIMEOUTRET3);
         context->slavelist[slave].ptype = LO_BYTE(etohs(val16));
         context->slavelist[slave].topology = h;
         context->slavelist[slave].activeports = b;
         /* 0=没有链接,不可能             */
         /* 1=1个链接,线尾              */
         /* 2=2个链接,前后各一个         */
         /* 3=3个链接,分割点             */
         /* 4=4个链接,交叉点             */
         /* 搜索父节点 */
         context->slavelist[slave].parent = 0; /* 父节点是主站 */
         if (slave > 1)
         {
            topoc = 0;
            slavec = slave - 1;
            do
            {
               topology = context->slavelist[slavec].topology;
               if (topology == 1)
               {
                  topoc--; /* 找到终端 */
               }
               if (topology == 3)
               {
                  topoc++; /* 找到分割点 */
               }
               if (topology == 4)
               {
                  topoc += 2; /* 找到交叉点 */
               }
               if (((topoc >= 0) && (topology > 1)) ||
                   (slavec == 1)) /* 找到父节点 */
               {
                  context->slavelist[slave].parent = slavec;
                  slavec = 1;
               }
               slavec--;
            }
            while (slavec > 0);
         }
         (void)ecx_statecheck(context, slave, EC_STATE_INIT,  EC_TIMEOUTSTATE); /* 检查状态变化Init */

         /* 如果从站具有邮箱,设置默认邮箱配置 */
         if (context->slavelist[slave].mbx_l > 0)
         {
            context->slavelist[slave].SMtype[0] = 1;
            context->slavelist[slave].SMtype[1] = 2;
            context->slavelist[slave].SMtype[2] = 3;
            context->slavelist[slave].SMtype[3] = 4;
            context->slavelist[slave].SM[0].StartAddr = htoes(context->slavelist[slave].mbx_wo);
            context->slavelist[slave].SM[0].SMlength = htoes(context->slavelist[slave].mbx_l);
            context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);
            context->slavelist[slave].SM[1].StartAddr = htoes(context->slavelist[slave].mbx_ro);
            context->slavelist[slave].SM[1].SMlength = htoes(context->slavelist[slave].mbx_rl);
            context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);
            eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP);
            context->slavelist[slave].mbx_proto = (uint16)etohl(eedat);
         }
         cindex = 0;
         /* 使用配置表吗? */
         if (usetable == 1)
         {
            cindex = ecx_config_from_table(context, slave);
         }
         /* 如果从站不在配置表中,则通过SII查找 */
         if (!cindex && !ecx_lookup_prev_sii(context, slave))
         {
            ssigen = ecx_siifind(context, slave, ECT_SII_GENERAL);
            /* SII通用部分 */
            if (ssigen)
            {
               context->slavelist[slave].CoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x07);
               context->slavelist[slave].FoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x08);
               context->slavelist[slave].EoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x09);
               context->slavelist[slave].SoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x0a);
               if ((ecx_siigetbyte(context, slave, ssigen + 0x0d) & 0x02) > 0)
               {
                  context->slavelist[slave].blockLRW = 1;
                  context->slavelist[0].blockLRW++;
               }
               context->slavelist[slave].Ebuscurrent = ecx_siigetbyte(context, slave, ssigen + 0x0e);
               context->slavelist[slave].Ebuscurrent += ecx_siigetbyte(context, slave, ssigen + 0x0f) << 8;
               context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
            }
            /* SII字符串部分 */
            if (ecx_siifind(context, slave, ECT_SII_STRING) > 0)
            {
               ecx_siistring(context, context->slavelist[slave].name, slave, 1);
            }
            /* 没有找到从站的名称,使用构造的名称 */
            else
            {
               sprintf(context->slavelist[slave].name, "? M:%8.8x I:%8.8x",
                       (unsigned int)context->slavelist[slave].eep_man,
                       (unsigned int)context->slavelist[slave].eep_id);
            }
            /* SII SM部分 */
            nSM = ecx_siiSM(context, slave, context->eepSM);
            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++;
               }
            }
            /* SII FMMU部分 */
            if (ecx_siiFMMU(context, slave, context->eepFMMU))
            {
               if (context->eepFMMU->FMMU0 !=0xff)
               {
                  context->slavelist[slave].FMMU0func = context->eepFMMU->FMMU0;
               }
               if (context->eepFMMU->FMMU1 !=0xff)
               {
                  context->slavelist[slave].FMMU1func = context->eepFMMU->FMMU1;
               }
               if (context->eepFMMU->FMMU2 !=0xff)
               {
                  context->slavelist[slave].FMMU2func = context->eepFMMU->FMMU2;
               }
               if (context->eepFMMU->FMMU3 !=0xff)
               {
                  context->slavelist[slave].FMMU3func = context->eepFMMU->FMMU3;
               }
            }
         }

         if (context->slavelist[slave].mbx_l > 0)
         {
            if (context->slavelist[slave].SM[0].StartAddr == 0x0000) /* 这不应该发生 */
            {
               EC_PRINT("从站 %d 在配置中没有正确的邮箱,尝试默认值。\n", slave);
               context->slavelist[slave].SM[0].StartAddr = htoes(0x1000);
               context->slavelist[slave].SM[0].SMlength = htoes(0x0080);
               context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);
               context->slavelist[slave].SMtype[0] = 1;
            }
            if (context->slavelist[slave].SM[1].StartAddr == 0x0000) /* 这不应该发生 */
            {
               EC_PRINT("从站 %d 在配置中没有正确的邮箱,尝试默认值。\n", slave);
               context->slavelist[slave].SM[1].StartAddr = htoes(0x1080);
               context->slavelist[slave].SM[1].SMlength = htoes(0x0080);
               context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);
               context->slavelist[slave].SMtype[1] = 2;
            }
            /* 编程SM0邮箱接收和SM1邮箱发送 */
            /* 在一个数据报中同时写入两个SM可以解决旧NETX中的时序问题 */
            ecx_FPWR(context->port, configadr, ECT_REG_SM0, sizeof(ec_smt) * 2,
               &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);
         }
         /* 一些从站需要在init->preop过渡中将eeprom可用于PDI */
         ecx_eeprom2pdi(context, slave);
         /* 用户可以覆盖自动状态更改 */
         if (context->manualstatechange == 0)
         {
            /* 请求从站的pre_op状态 */
            ecx_FPWRw(context->port,
               configadr,
               ECT_REG_ALCTL,
               htoes(EC_STATE_PRE_OP | EC_STATE_ACK),
               EC_TIMEOUTRET3); /* 设置preop状态 */
         }
      }
   }
   return wkc;
}

/* 如果从站具有SII映射并且以前执行相同从站ID,则使用先前的映射。
 * 这是安全的,因为对于相同的从站ID,SII映射是恒定的。
 */
static int ecx_lookup_mapping(ecx_contextt *context, uint16 slave, uint32 *Osize, uint32 *Isize)
{
   int i, nSM;
   if ((slave > 1) && (*(context->slavecount) > 0))
   {
      i = 1;
      while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||
             (context->slavelist[i].eep_id  != context->slavelist[slave].eep_id ) ||
             (context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&
            (i < slave))
      {
         i++;
      }
      if(i < slave)
      {
         for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
         {
            context->slavelist[slave].SM[nSM].SMlength = context->slavelist[i].SM[nSM].SMlength;
            context->slavelist[slave].SMtype[nSM] = context->slavelist[i].SMtype[nSM];
         }
         *Osize = context->slavelist[i].Obits;
         *Isize = context->slavelist[i].Ibits;
         context->slavelist[slave].Obits = (uint16)*Osize;
         context->slavelist[slave].Ibits = (uint16)*Isize;
         EC_PRINT("从站 %d 从 %d 复制映射。\n", slave, i);
         return 1;
      }
   }
   return 0;
}

static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, int thread_n)
{
   uint32 Isize, Osize;
   int rval;

   ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* 检查状态是否变为Pre-op */

   EC_PRINT(" >从站 %d, 配置地址 %x, 状态 %2.2x\n",
            slave, context->slavelist[slave].configadr, context->slavelist[slave].state);

   /* 执行特殊的从站配置挂接 Pre-Op 到 Safe-OP */
   if(context->slavelist[slave].PO2SOconfig) /* 仅在已注册时执行 */
   {
      context->slavelist[slave].PO2SOconfig(slave);
   }
   if (context->slavelist[slave].PO2SOconfigx) /* 仅在已注册时执行 */
   {
      context->slavelist[slave].PO2SOconfigx(context, slave);
   }
   /* 如果在配置列表中未找到从站,则在从站本身中查找IO映射 */
   if (!context->slavelist[slave].configindex)
   {
      Isize = 0;
      Osize = 0;
      if (context->slavelist[slave].mbx_proto & ECT_MBXPROT_COE) /* 具有CoE */
      {
         rval = 0;
         if (context->slavelist[slave].CoEdetails & ECT_COEDET_SDOCA) /* 具有Complete Access */
         {
            /* 通过CoE读取PDO映射并使用Complete Access */
            rval = ecx_readPDOmapCA(context, slave, thread_n, &Osize, &Isize);
         }
         if (!rval) /* CA不可用或未成功 */
         {
            /* 通过CoE读取PDO映射 */
            rval = ecx_readPDOmap(context, slave, &Osize, &Isize);
         }
         EC_PRINT("  CoE 输出大小:%u 输入大小:%u\n", Osize, Isize);
      }
      if ((!Isize && !Osize) && (context->slavelist[slave].mbx_proto & ECT_MBXPROT_SOE)) /* 具有SoE */
      {
         /* 通过SoE读取AT/MDT映射 */
         rval = ecx_readIDNmap(context, slave, &Osize, &Isize);
         context->slavelist[slave].SM[2].SMlength = htoes((uint16)((Osize + 7) / 8));
         context->slavelist[slave].SM[3].SMlength = htoes((uint16)((Isize + 7) / 8));
         EC_PRINT("  SoE 输出大小:%u 输入大小:%u\n", Osize, Isize);
      }
      context->slavelist[slave].Obits = (uint16)Osize;
      context->slavelist[slave].Ibits = (uint16)Isize;
   }

   return 1;
}

static int ecx_map_sii(ecx_contextt *context, uint16 slave)
{
   uint32 Isize, Osize;
   int nSM;
   ec_eepromPDOt eepPDO;

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

   if (!Isize && !Osize) /* 在具有相同ID的先前从站中查找PDO */
   {
      (void)ecx_lookup_mapping(context, slave, &Osize, &Isize);
   }
   if (!Isize && !Osize) /* 通过SII查找PDO映射 */
   {
      memset(&eepPDO, 0, sizeof(eepPDO));
      Isize = ecx_siiPDO(context, slave, &eepPDO, 0);
      EC_PRINT("  SII Isize:%u\n", Isize);
      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
      {
         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 长度 %d\n", nSM, eepPDO.SMbitsize[nSM]);
         }
      }
      Osize = ecx_siiPDO(context, slave, &eepPDO, 1);
      EC_PRINT("  SII Osize:%u\n", Osize);
      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
      {
         if (eepPDO.SMbitsize[nSM] > 0)
         {
            context->slavelist[slave].SM[nSM].SMlength =  htoes((eepPDO.SMbitsize[nSM] + 7) / 8);
            context->slavelist[slave].SMtype[nSM] = 3;
            EC_PRINT("    SM%d 长度 %d\n", nSM, eepPDO.SMbitsize[nSM]);
         }
      }
   }
   context->slavelist[slave].Obits = (uint16)Osize;
   context->slavelist[slave].Ibits = (uint16)Isize;
   EC_PRINT("     输入大小:%d %d 输出大小:%d\n",
      context->slavelist[slave].Ibits, Isize, context->slavelist[slave].Obits);

   return 1;
}

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

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

   EC_PRINT("  SM编程\n");
   if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[0].StartAddr)
   {
      ecx_FPWR(context->port, configadr, ECT_REG_SM0,
         sizeof(ec_smt), &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);
      EC_PRINT("    SM0 类型:%d 起始地址:%4.4x 标志:%8.8x\n",
          context->slavelist[slave].SMtype[0],
          etohs(context->slavelist[slave].SM[0].StartAddr),
          etohl(context->slavelist[slave].SM[0].SMflags));
   }
   if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[1].StartAddr)
   {
      ecx_FPWR(context->port, configadr, ECT_REG_SM1,
         sizeof(ec_smt), &context->slavelist[slave].SM[1], EC_TIMEOUTRET3);
      EC_PRINT("    SM1 类型:%d 起始地址:%4.4x 标志:%8.8x\n",
          context->slavelist[slave].SMtype[1],
          etohs(context->slavelist[slave].SM[1].StartAddr),
          etohl(context->slavelist[slave].SM[1].SMflags));
   }
   /* 编程SM2到SMx */
   for( nSM = 2 ; nSM < EC_MAXSM ; nSM++ )
   {
      if (context->slavelist[slave].SM[nSM].StartAddr)
      {
         /* 检查SM长度是否为零 -> 清除启用标志 */
         if( context->slavelist[slave].SM[nSM].SMlength == 0)
         {
            context->slavelist[slave].SM[nSM].SMflags =
               htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) & EC_SMENABLEMASK);
         }
         /* 如果SM长度为非零,始终设置启用标志 */
         else
         {
            context->slavelist[slave].SM[nSM].SMflags =
               htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) | ~EC_SMENABLEMASK);
         }
         ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt)),
            sizeof(ec_smt), &context->slavelist[slave].SM[nSM], EC_TIMEOUTRET3);
         EC_PRINT("    SM%d 类型:%d 起始地址:%4.4x 标志:%8.8x\n", nSM,
             context->slavelist[slave].SMtype[nSM],
             etohs(context->slavelist[slave].SM[nSM].StartAddr),
             etohl(context->slavelist[slave].SM[nSM].SMflags));
      }
   }
   if (context->slavelist[slave].Ibits > 7)
   {
      context->slavelist[slave].Ibytes = (context->slavelist[slave].Ibits + 7) / 8;
   }
   if (context->slavelist[slave].Obits > 7)
   {
      context->slavelist[slave].Obytes = (context->slavelist[slave].Obits + 7) / 8;
   }

   return 1;
}


#if EC_MAX_MAPT > 1
OSAL_THREAD_FUNC ecx_mapper_thread(void *param)
{
   ecx_mapt_t *maptp;
   maptp = param;
   ecx_map_coe_soe(maptp->context, maptp->slave, maptp->thread_n);
   maptp->running = 0;
}

static int ecx_find_mapt(void)
{
   int p;
   p = 0;
   while((p < EC_MAX_MAPT) && ecx_mapt[p].running)
   {
      p++;
   }
   if(p < EC_MAX_MAPT)
   {
      return p;
   }
   else
   {
      return -1;
   }
}
#endif

static int ecx_get_threadcount(void)
{
   int thrc, thrn;
   thrc = 0;
   for(thrn = 0 ; thrn < EC_MAX_MAPT ; thrn++)
   {
      thrc += ecx_mapt[thrn].running;
   }
   return thrc;
}


static void ecx_config_find_mappings(ecx_contextt *context, uint8 group)
{
   int thrn, thrc;
   uint16 slave;

   for (thrn = 0; thrn < EC_MAX_MAPT; thrn++)
   {
      ecx_mapt[thrn].running = 0;
   }
   /* 查找多线程中的从站的CoE和SoE映射 */
   for (slave = 1; slave <= *(context->slavecount); slave++)
   {
      if (!group || (group == context->slavelist[slave].group))
      {
#if EC_MAX_MAPT > 1
            /* 多线程版本 */
            while ((thrn = ecx_find_mapt()) < 0)
            {
               osal_usleep(1000);
            }
            ecx_mapt[thrn].context = context;
            ecx_mapt[thrn].slave = slave;
            ecx_mapt[thrn].thread_n = thrn;
            ecx_mapt[thrn].running = 1;
            osal_thread_create(&(ecx_threadh[thrn]), 128000,
               &ecx_mapper_thread, &(ecx_mapt[thrn]));
#else
            /* 串行版本 */
            ecx_map_coe_soe(context, slave, 0);
#endif
      }
   }
   /* 等待所有线程完成 */
   do
   {
      thrc = ecx_get_threadcount();
      if (thrc)
      {
         osal_usleep(1000);
      }
   } while (thrc);
   /* 查找从站的SII映射并编程SM */
   for (slave = 1; slave <= *(context->slavecount); slave++)
   {
      if (!group || (group == context->slavelist[slave].group))
      {
         ecx_map_sii(context, slave);
         ecx_map_sm(context, slave);
      }
   }
}
static void ecx_config_create_input_mappings(ecx_contextt *context, void *pIOmap, 
   uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
{
   int BitCount = 0;
   int FMMUdone = 0;
   int AddToInputsWKC = 0;
   uint16 ByteCount = 0;
   uint16 FMMUsize = 0;
   uint8 SMc = 0;
   uint16 EndAddr;
   uint16 SMlength;
   uint16 configadr;
   uint8 FMMUc;

   EC_PRINT(" =Slave %d, INPUT MAPPING\n", slave);

   configadr = context->slavelist[slave].configadr;
   FMMUc = context->slavelist[slave].FMMUunused;
   if (context->slavelist[slave].Obits) /* 查找空闲的FMMU */
   {
      while (context->slavelist[slave].FMMU[FMMUc].LogStart)
      {
         FMMUc++;
      }
   }
   /* 搜索为输入映射做出贡献的SM */
   while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Ibits + 7) / 8)))
   {
      EC_PRINT("    FMMU %d\n", FMMUc);
      while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4))
      {
         SMc++;
      }
      EC_PRINT("      SM%d\n", SMc);
      context->slavelist[slave].FMMU[FMMUc].PhysStart =
         context->slavelist[slave].SM[SMc].StartAddr;
      SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
      ByteCount += SMlength;
      BitCount += SMlength * 8;
      EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
      while ((BitCount < context->slavelist[slave].Ibits) && (SMc < (EC_MAXSM - 1))) /* 更多的SM用于输入 */
      {
         SMc++;
         while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4))
         {
            SMc++;
         }
         /* 如果来自多个SM的地址连接在一起,使用一个FMMU,否则分成多个FMMU */
         if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr)
         {
            break;
         }
         EC_PRINT("      SM%d\n", SMc);
         SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
         ByteCount += SMlength;
         BitCount += SMlength * 8;
         EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
      }

      /* 位定向从站 */
      if (!context->slavelist[slave].Ibytes)
      {
         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
         *BitPos += context->slavelist[slave].Ibits - 1;
         if (*BitPos > 7)
         {
            *LogAddr += 1;
            *BitPos -= 8;
         }
         FMMUsize = (uint16)(*LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1);
         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
         *BitPos += 1;
         if (*BitPos > 7)
         {
            *LogAddr += 1;
            *BitPos -= 8;
         }
      }
      /* 字节定向从站 */
      else
      {
         if (*BitPos)
         {
            *LogAddr += 1;
            *BitPos = 0;
         }
         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
         *BitPos = 7;
         FMMUsize = ByteCount;
         if ((FMMUsize + FMMUdone) > (int)context->slavelist[slave].Ibytes)
         {
            FMMUsize = (uint16)(context->slavelist[slave].Ibytes - FMMUdone);
         }
         *LogAddr += FMMUsize;
         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
         *BitPos = 0;
      }
      FMMUdone += FMMUsize;
      if (context->slavelist[slave].FMMU[FMMUc].LogLength)
      {
         context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;
         context->slavelist[slave].FMMU[FMMUc].FMMUtype = 1;
         context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;
         /* 为输入编程FMMU */
         ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
            sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
         /* 设置标志以添加一个输入FMMU,单个ESC只能贡献一次 */
         AddToInputsWKC = 1;
      }
      if (!context->slavelist[slave].inputs)
      {
         if (group)
         {
            context->slavelist[slave].inputs =
               (uint8 *)(pIOmap) + 
               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) - 
               context->grouplist[group].logstartaddr;
         }
         else
         {
            context->slavelist[slave].inputs =
               (uint8 *)(pIOmap) +
               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);
         }
         context->slavelist[slave].Istartbit =
            context->slavelist[slave].FMMU[FMMUc].LogStartbit;
         EC_PRINT("    Inputs %p startbit %d\n",
            context->slavelist[slave].inputs,
            context->slavelist[slave].Istartbit);
      }
      FMMUc++;
   }
   context->slavelist[slave].FMMUunused = FMMUc;

   /* 如果标志为true,则添加一个WKC用于输入 */
   if (AddToInputsWKC)
      context->grouplist[group].inputsWKC++;
}



static void ecx_config_create_output_mappings(ecx_contextt *context, void *pIOmap,
   uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
{
   int BitCount = 0;
   int FMMUdone = 0;
   int AddToOutputsWKC = 0;
   uint16 ByteCount = 0;
   uint16 FMMUsize = 0;
   uint8 SMc = 0;
   uint16 EndAddr;
   uint16 SMlength;
   uint16 configadr;
   uint8 FMMUc;

   EC_PRINT("  输出映射\n");

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

   /* 搜索对输出映射有贡献的SM */
   while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Obits + 7) / 8)))
   {
      EC_PRINT("    FMMU %d\n", FMMUc);
      while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3))
      {
         SMc++;
      }
      EC_PRINT("      SM%d\n", SMc);
      context->slavelist[slave].FMMU[FMMUc].PhysStart =
         context->slavelist[slave].SM[SMc].StartAddr;
      SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
      ByteCount += SMlength;
      BitCount += SMlength * 8;
      EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
      while ((BitCount < context->slavelist[slave].Obits) && (SMc < (EC_MAXSM - 1))) /* 更多的SM用于输出 */
      {
         SMc++;
         while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3))
         {
            SMc++;
         }
         /* 如果来自多个SM的地址连接在一起,使用一个FMMU,否则分成多个FMMU */
         if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr)
         {
            break;
         }
         EC_PRINT("      SM%d\n", SMc);
         SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
         ByteCount += SMlength;
         BitCount += SMlength * 8;
         EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
      }

      /* 位定向从站 */
      if (!context->slavelist[slave].Obytes)
      {
         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
         *BitPos += context->slavelist[slave].Obits - 1;
         if (*BitPos > 7)
         {
            *LogAddr += 1;
            *BitPos -= 8;
         }
         FMMUsize = (uint16)(*LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1);
         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
         *BitPos += 1;
         if (*BitPos > 7)
         {
            *LogAddr += 1;
            *BitPos -= 8;
         }
      }
      /* 字节定向从站 */
      else
      {
         if (*BitPos)
         {
            *LogAddr += 1;
            *BitPos = 0;
         }
         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
         *BitPos = 7;
         FMMUsize = ByteCount;
         if ((FMMUsize + FMMUdone)> (int)context->slavelist[slave].Obytes)
         {
            FMMUsize = (uint16)(context->slavelist[slave].Obytes - FMMUdone);
         }
         *LogAddr += FMMUsize;
         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
         *BitPos = 0;
      }
      FMMUdone += FMMUsize;
      if (context->slavelist[slave].FMMU[FMMUc].LogLength)
      {
         context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;
         context->slavelist[slave].FMMU[FMMUc].FMMUtype = 2;
         context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;
         /* 为输出程序FMMU */
         ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
            sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
         /* 设置标志以添加一个输出FMMU,一个单独的ESC只能贡献一次 */
         AddToOutputsWKC = 1;
      }
      if (!context->slavelist[slave].outputs)
      {
         if (group)
         {
            context->slavelist[slave].outputs =
               (uint8 *)(pIOmap) +
               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) -
               context->grouplist[group].logstartaddr;
         }
         else
         {
            context->slavelist[slave].outputs =
               (uint8 *)(pIOmap) +
               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);
         }
         context->slavelist[slave].Ostartbit =
            context->slavelist[slave].FMMU[FMMUc].LogStartbit;
         EC_PRINT("    从站 %d 输出 %p 开始位 %d\n",
            slave,
            context->slavelist[slave].outputs,
            context->slavelist[slave].Ostartbit);
      }
      FMMUc++;
   }
   context->slavelist[slave].FMMUunused = FMMUc;
   /* 如果标志为真,则为输出添加一个WKC */
   if (AddToOutputsWKC)
      context->grouplist[group].outputsWKC++;
}

/** 将一组从站中的所有PDO映射到IOmap,按顺序排列(传统SOEM方式)。

 *
 * @param[in]  context    = 上下文结构
 * @param[out] pIOmap     = 指向IOmap的指针
 * @param[in]  group      = 要映射的组,0表示所有组
 * @return IOmap大小
 */
int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
{
   uint16 slave, configadr;
   uint8 BitPos;
   uint32 LogAddr = 0;
   uint32 oLogAddr = 0;
   uint32 diff;
   uint16 currentsegment = 0;
   uint32 segmentsize = 0;

   if ((*(context->slavecount) > 0) && (group < context->maxgroup))
   {
      EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);
      LogAddr = context->grouplist[group].logstartaddr;
      oLogAddr = LogAddr;
      BitPos = 0;
      context->grouplist[group].nsegments = 0;
      context->grouplist[group].outputsWKC = 0;
      context->grouplist[group].inputsWKC = 0;

      /* 查找映射并设置同步管理器 */
      ecx_config_find_mappings(context, group);

      /* 对从站进行输出映射并设置FMMUs */
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         configadr = context->slavelist[slave].configadr;

         if (!group || (group == context->slavelist[slave].group))
         {
            /* 创建输出映射 */
            if (context->slavelist[slave].Obits)
            {
               ecx_config_create_output_mappings (context, pIOmap, group, slave, &LogAddr, &BitPos);
               diff = LogAddr - oLogAddr;
               oLogAddr = LogAddr;
               if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
               {
                  context->grouplist[group].IOsegment[currentsegment] = segmentsize;
                  if (currentsegment < (EC_MAXIOSEGMENTS - 1))
                  {
                     currentsegment++;
                     segmentsize = diff;
                  }
               }
               else
               {
                  segmentsize += diff;
               }
            }
         }
      }
      if (BitPos)
      {
         LogAddr++;
         oLogAddr = LogAddr;
         BitPos = 0;
         if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
         {
            context->grouplist[group].IOsegment[currentsegment] = segmentsize;
            if currentsegment < (EC_MAXIOSEGMENTS - 1))
            {
               currentsegment++;
               segmentsize = 1;
            }
         }
         else
         {
            segmentsize += 1;
         }
      }
      context->grouplist[group].outputs = pIOmap;
      context->grouplist[group].Obytes = LogAddr - context->grouplist[group].logstartaddr;
      context->grouplist[group].nsegments = currentsegment + 1;
      context->grouplist[group].Isegment = currentsegment;
      context->grouplist[group].Ioffset = (uint16)segmentsize;
      if (!group)
      {
         context->slavelist[0].outputs = pIOmap;
         context->slavelist[0].Obytes = LogAddr - 
            context->grouplist[group].logstartaddr; /* 在主记录中存储输出字节 */
      }

      /* 对从站进行输入映射并设置FMMUs */
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         configadr = context->slavelist[slave].configadr;
         if (!group || (group == context->slavelist[slave].group))
         {
            /* 创建输入映射 */
            if (context->slavelist[slave].Ibits)
            {
               ecx_config_create_input_mappings(context, pIOmap, group, slave, &LogAddr, &BitPos);
               diff = LogAddr - oLogAddr;
               oLogAddr = LogAddr;
               if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
               {
                  context->grouplist[group].IOsegment[currentsegment] = segmentsize;
                  if (currentsegment < (EC_MAXIOSEGMENTS - 1))
                  {
                     currentsegment++;
                     segmentsize = diff;
                  }
               }
               else
               {
                  segmentsize += diff;
               }
            }

            ecx_eeprom2pdi(context, slave); /* 将EEPROM控制设置为PDI */
            /* 用户可以覆盖自动状态更改 */
            if (context->manualstatechange == 0)
            {
               /* 请求从站进入安全操作状态 */
               ecx_FPWRw(context->port,
                  configadr,
                  ECT_REG_ALCTL,
                  htoes(EC_STATE_SAFE_OP),
                  EC_TIMEOUTRET3); /* 设置安全操作状态 */
            }
            if (context->slavelist[slave].blockLRW)
            {
               context->grouplist[group].blockLRW++;
            }
            context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
         }
      }
      if (BitPos)
      {
         LogAddr++;
         oLogAddr = LogAddr;
         BitPos = 0;
         if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
         {
            context->grouplist[group].IOsegment[currentsegment] = segmentsize;
            if (currentsegment < (EC_MAXIOSEGMENTS - 1))
            {
               currentsegment++;
               segmentsize = 1;
            }
         }
         else
         {
            segmentsize += 1;
         }
      }
      context->grouplist[group].IOsegment[currentsegment] = segmentsize;
      context->grouplist[group].nsegments = currentsegment + 1;
      context->grouplist[group].inputs = (uint8 *)(pIOmap) + context->grouplist[group].Obytes;
      context->grouplist[group].Ibytes = LogAddr - 
         context->grouplist[group].logstartaddr - 
         context->grouplist[group].Obytes;
      if (!group)
      {
         context->slavelist[0].inputs = (uint8 *)(pIOmap) + context->slavelist[0].Obytes;
         context->slavelist[0].Ibytes = LogAddr - 
            context->grouplist[group].logstartaddr - 
            context->slavelist[0].Obytes; /* 在主记录中存储输入字节 */
      }

      EC_PRINT("IOmapSize %d\n", LogAddr - context->grouplist[group].logstartaddr);

      return (LogAddr - context->grouplist[group].logstartaddr);
   }

   return 0;
}

/** 将一组从站中的所有PDO映射到IOmap,包括输出和输入,具有重叠。注意:在使用LRW时必须使用这个功能。

 *
 * @param[in]  context    = 上下文结构
 * @param[out] pIOmap     = 指向IOmap的指针
 * @param[in]  group      = 要映射的组,0表示所有组
 * @return IOmap大小
 */
int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
{
   uint16 slave, configadr;
   uint8 BitPos;
   uint32 mLogAddr = 0;
   uint32 siLogAddr = 0;
   uint32 soLogAddr = 0;
   uint32 tempLogAddr;
   uint32 diff;
   uint16 currentsegment = 0;
   uint32 segmentsize = 0;

   if ((*(context->slavecount) > 0) && (group < context->maxgroup))
   {
      EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);
      mLogAddr = context->grouplist[group].logstartaddr;
      siLogAddr = mLogAddr;
      soLogAddr = mLogAddr;
      BitPos = 0;
      context->grouplist[group].nsegments = 0;
      context->grouplist[group].outputsWKC = 0;
      context->grouplist[group].inputsWKC = 0;

      /* 查找映射并设置同步管理器 */
      ecx_config_find_mappings(context, group);
      
      /* 对从站进行IO映射并设置FMMUs */
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         configadr = context->slavelist[slave].configadr;
         siLogAddr = soLogAddr = mLogAddr;

         if (!group || (group == context->slavelist[slave].group))
         {
            /* 创建输出映射 */
            if (context->slavelist[slave].Obits)
            {
               ecx_config_create_output_mappings(context, pIOmap, group, 
                  slave, &soLogAddr, &BitPos);
               if (BitPos)
               {
                  soLogAddr++;
                  BitPos = 0;
               }
            }

            /* 创建输入映射 */
            if (context->slavelist[slave].Ibits)
            {
               ecx_config_create_input_mappings(context, pIOmap, group, 
                  slave, &siLogAddr, &BitPos);
               if (BitPos)
               {
                  siLogAddr++;
                  BitPos = 0;
               }
            }

            tempLogAddr = (siLogAddr > soLogAddr) ?  siLogAddr : soLogAddr;
            diff = tempLogAddr - mLogAddr;
            mLogAddr = tempLogAddr;

            if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
            {
               context->grouplist[group].IOsegment[currentsegment] = segmentsize;
               if (currentsegment < (EC_MAXIOSEGMENTS - 1))
               {
                  currentsegment++;
                  segmentsize = diff;
               }
            }
            else
            {
               segmentsize += diff;
            }

            ecx_eeprom2pdi(context, slave); /* 将EEPROM控制设置为PDI */
            /* 用户可以覆盖自动状态更改 */
            if (context->manualstatechange == 0)
            {
               /* 请求从站进入安全操作状态 */
               ecx_FPWRw(context->port,
                  configadr,
                  ECT_REG_ALCTL,
                  htoes(EC_STATE_SAFE_OP),
                  EC_TIMEOUTRET3);
            }
            if (context->slavelist[slave].blockLRW)
            {
               context->grouplist[group].blockLRW++;
            }
            context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
         }
      }

      context->grouplist[group].IOsegment[currentsegment] = segmentsize;
      context->grouplist[group].nsegments = currentsegment + 1;
      context->grouplist[group].Isegment = 0;
      context->grouplist[group].Ioffset = 0;

      context->grouplist[group].Obytes = soLogAddr - context->grouplist[group].logstartaddr;
      context->grouplist[group].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;
      context->grouplist[group].outputs = pIOmap;
      context->grouplist[group].inputs = (uint8 *)pIOmap + context->grouplist[group].Obytes;

      /* 将计算的输入数据移动到带有OBytes偏移的位置 */
      for (slave = 1; slave <= *(context->slavecount); slave++)
      {
         if (!group || (group == context->slavelist[slave].group))
         {
            if(context->slavelist[slave].Ibits > 0)
            {
               context->slavelist[slave].inputs += context->grouplist[group].Obytes;
            }
         }
      }

      if (!group)
      {
         /* 在主记录中存储输出字节 */
         context->slavelist[0].outputs = pIOmap;
         context->slavelist[0].Obytes = soLogAddr - context->grouplist[group].logstartaddr; 
         context->slavelist[0].inputs = (uint8 *)pIOmap + context->slavelist[0].Obytes;
         context->slavelist[0].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;
      }

      EC_PRINT("IOmapSize %d\n", context->grouplist[group].Obytes + context->grouplist[group].Ibytes);

      return (context->grouplist[group].Obytes + context->grouplist[group].Ibytes);
   }

   return 0;
}

/** 恢复从站。
 *
 * @param[in] context = 上下文结构
 * @param[in] slave   = 要恢复的从站
 * @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3
 * @return >0 表示成功
 */
int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)
{
   int rval;
   int wkc;
   uint16 ADPh, configadr, readadr;

   rval = 0;
   configadr = context->slavelist[slave].configadr;
   ADPh = (uint16)(1 - slave);
   /* 检查是否找到了与请求的从站不同的从站 */
   readadr = 0xfffe;
   wkc = ecx_APRD(context->port, ADPh, ECT_REG_STADR, sizeof(readadr), &readadr, timeout);
   /* 找到正确的从站,完成 */
   if(readadr == configadr)
   {
       return 1;
   }
   /* 仅在没有配置地址时尝试 */
   if( (wkc > 0) && (readadr == 0))
   {
      /* 清除可能的从站地址EC_TEMPNODE */
      ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);
      /* 设置从站的临时节点地址 */
      if(ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(EC_TEMPNODE) , timeout) <= 0)
      {
         ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);
         return 0; /* 从站未响应 */
      }

      context->slavelist[slave].configadr = EC_TEMPNODE; /* 临时配置地址 */
      ecx_eeprom2master(context, slave); /* 设置EEPROM控制为主站 */

      /* 检查从站是否与之前配置的相同 */
      if ((ecx_FPRDw(context->port, EC_TEMPNODE, ECT_REG_ALIAS, timeout) ==
             htoes(context->slavelist[slave].aliasadr)) &&
          (ecx_readeeprom(context, slave, ECT_SII_ID, EC_TIMEOUTEEP) ==
             htoel(context->slavelist[slave].eep_id)) &&
          (ecx_readeeprom(context, slave, ECT_SII_MANUF, EC_TIMEOUTEEP) ==
             htoel(context->slavelist[slave].eep_man)) &&
          (ecx_readeeprom(context, slave, ECT_SII_REV, EC_TIMEOUTEEP) ==
             htoel(context->slavelist[slave].eep_rev)))
      {
         rval = ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(configadr) , timeout);
         context->slavelist[slave].configadr = configadr;
      }
      else
      {
         /* 从站与预期的不同,移除配置地址 */
         ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , timeout);
         context->slavelist[slave].configadr = configadr;
      }
   }

   return rval;
}

/** 重新配置从站。
 *
 * @param[in] context = 上下文结构
 * @param[in] slave   = 要重新配置的从站
 * @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3
 * @return 从站状态
 */
int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)
{
   int state, nSM, FMMUc;
   uint16 configadr;

   configadr = context->slavelist[slave].configadr;
   if (ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_INIT) , timeout) <= 0)
   {
      return 0;
   }
   state = 0;
   ecx_eeprom2pdi(context, slave); /* 设置EEPROM控制为PDI */
   /* 检查状态变化是否初始化 */
   state = ecx_statecheck(context, slave, EC_STATE_INIT, EC_TIMEOUTSTATE);
   if(state == EC_STATE_INIT)
   {
      /* 程序所有已启用的SM */
      for( nSM = 0 ; nSM < EC_MAXSM ; nSM++ )
      {
         if (context->slavelist[slave].SM[nSM].StartAddr)
         {
            ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt)),
               sizeof(ec_smt), &context->slavelist[slave].SM[nSM], timeout);
         }
      }
      ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_PRE_OP) , timeout);
      state = ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* 检查状态变化到Pre-op */
      if( state == EC_STATE_PRE_OP)
      {
         /* 执行特殊的从站配置钩子,从Pre-Op到Safe-Op */
         if(context->slavelist[slave].PO2SOconfig) /* 仅在注册时执行 */
         {
            context->slavelist[slave].PO2SOconfig(slave);
         }
         if (context->slavelist[slave].PO2SOconfigx) /* 仅在注册时执行 */
         {
            context->slavelist[slave].PO2SOconfigx(context, slave);
         }         
         ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , timeout); /* 设置Safe-Op状态 */
         state = ecx_statecheck(context, slave, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE); /* 检查状态变化到Safe-Op */
         /* 程序已配置的FMMU */
         for( FMMUc = 0 ; FMMUc < context->slavelist[slave].FMMUunused ; FMMUc++ )
         {
            ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
               sizeof(ec_fmmut), &context->slavelist[slave].FMMU[FMMUc], timeout);
         }
      }
   }

   return state;
}

#ifdef EC_VER1
/** 枚举并初始化所有从站。
 *
 * @param[in] usetable     = TRUE表示使用配置表来初始化从站,FALSE表示不使用配置表
 * @return 所发现的从站数据报的工作计数 = 找到的从站数量
 * @see ecx_config_init
 */
int ec_config_init(uint8 usetable)
{
   return ecx_config_init(&ecx_context, usetable);
}

/** 将一个组中的所有从站的所有PDOs按照顺序映射到IOmap中,遵循传统SOEM方式。
 *
 * @param[out] pIOmap     = 指向IOmap的指针
 * @param[in]  group      = 要映射的组,0表示所有组
 * @return IOmap的大小
 * @see ecx_config_map_group
 */
int ec_config_map_group(void *pIOmap, uint8 group)
{
   return ecx_config_map_group(&ecx_context, pIOmap, group);
}

/** 将一个组中的所有从站的所有PDOs映射到IOmap中,允许输出和输入重叠。注意:使用LRW时必须使用此方式,适用于TI ESC。
 *
 * @param[out] pIOmap     = 指向IOmap的指针
 * @param[in]  group      = 要映射的组,0表示所有组
 * @return IOmap的大小
 * @see ecx_config_overlap_map_group
 */
int ec_config_overlap_map_group(void *pIOmap, uint8 group)
{
   return ecx_config_overlap_map_group(&ecx_context, pIOmap, group);
}

/** 将所有从站的所有PDOs按照顺序映射到IOmap中,遵循传统SOEM方式。
 *
 * @param[out] pIOmap     = 指向IOmap的指针
 * @return IOmap的大小
 */
int ec_config_map(void *pIOmap)
{
   return ec_config_map_group(pIOmap, 0);
}

/** 将所有从站的所有PDOs映射到IOmap中,允许输出和输入重叠。注意:使用LRW时必须使用此方式,适用于TI ESC。
 *
 * @param[out] pIOmap     = 指向IOmap的指针
 * @return IOmap的大小
 */
int ec_config_overlap_map(void *pIOmap)
{
   return ec_config_overlap_map_group(pIOmap, 0);
}

/** 枚举、映射并初始化所有从站。
 *
 * @param[in] usetable    = TRUE表示使用配置表来初始化从站,FALSE表示不使用配置表
 * @param[out] pIOmap     = 指向IOmap的指针
 * @return 所发现的从站数据报的工作计数 = 找到的从站数量
 */
int ec_config(uint8 usetable, void *pIOmap)
{
   int wkc;
   wkc = ec_config_init(usetable);
   if (wkc)
   {
      ec_config_map(pIOmap);
   }
   return wkc;
}

/** 枚举、映射并初始化所有从站。
 *
 * @param[in] usetable    = TRUE表示使用配置表来初始化从站,FALSE表示不使用配置表
 * @param[out] pIOmap     = 指向IOmap的指针
 * @return 所发现的从站数据报的工作计数 = 找到的从站数量
 */
int ec_config_overlap(uint8 usetable, void *pIOmap)
{
   int wkc;
   wkc = ec_config_init(usetable);
   if (wkc)
   {
      ec_config_overlap_map(pIOmap);
   }
   return wkc;
}

/** 恢复从站。
 *
 * @param[in] slave   = 要恢复的从站
 * @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3
 * @return 如果成功则返回大于0
 * @see ecx_recover_slave
 */
int ec_recover_slave(uint16 slave, int timeout)
{
   return ecx_recover_slave(&ecx_context, slave, timeout);
}

/** 重新配置从站。
 *
 * @param[in] slave   = 要重新配置的从站
 * @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3
 * @return 从站状态
 * @see ecx_reconfig_slave
 */
int ec_reconfig_slave(uint16 slave, int timeout)
{
   return ecx_reconfig_slave(&ecx_context, slave, timeout);
}
#endif



该文档修改记录:

修改时间 修改说明
2023年11月4日 EtherCAT主站SOEM – 3 – SOEM之ethercatconfig.h/c文件解析

总结

以上就是EtherCAT主站SOEM – 3 --SOEM之ethercatconfig.h/c文件解析的内容。
有不明白的地方欢迎留言;有建议欢迎留言,我后面编写文档好改进。
创作不容,如果文档对您有帮助,记得给个赞。

你可能感兴趣的:(EtherCAT主站-SOEM,c语言,SOEM,EtherCAT,ethercatconfig,工业总线,经验分享,EtherCAT主站)