SOEM源码解析——ecx_writeeeprom(写EEPROM)

0 工具准备

1.SOEM-master-1.4.0源码

1 ecx_writeeeprom函数总览

/** Write EEPROM to slave bypassing cache.:绕过从站缓存写EEPROM数据
 * @param[in] context   = context struct 句柄
 * @param[in] slave     = Slave number 从站序号
 * @param[in] eeproma   = (WORD) Address in the EEPROM EEPROM地址(以字为单位)
 * @param[in] data      = 16bit data 16bit数据
 * @param[in] timeout   = Timeout in us.
 * @return >0 if OK
 */
int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout)
{
   uint16 configadr;
   /* set eeprom control to master */
   /* 将EEPROM控制权设置为主站 */
   ecx_eeprom2master(context, slave);
   configadr = context->slavelist[slave].configadr;
   return (ecx_writeeepromFP(context, configadr, eeproma, data, timeout));
}

从以上代码可以看到,SOEM主站写从站EEPROM的操作可以分成2块:
(1)通过ecx_eeprom2master函数夺取EEPROM访问控制权
(2)通过ecx_writeeepromFP函数写入数据到从站EEPROM

1.1 ecx_eeprom2master函数解析

ECT_REG_EEPCFG      = 0x0500, // EEPROM控制寄存器
/** Set eeprom control to master. Only if set to PDI.:设置EEPROM访问控制权为主站,仅当它设置为PDI时
 * @param[in] context   = context struct 句柄
 * @param[in] slave     = Slave number 从站序号
 * @return >0 if OK
 */
int ecx_eeprom2master(ecx_contextt *context, uint16 slave)
{
   int wkc = 1, cnt = 0;
   uint16 configadr;
   uint8 eepctl;

   if ( context->slavelist[slave].eep_pdi )
   {
      configadr = context->slavelist[slave].configadr;
      eepctl = 2;
      do
      {
         /* force Eeprom from PDI */
         /* 强制PDI操作释放,复位0x0501.0为0,将EEPROM访问控制权分配给主站 */
         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET);
      }
      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
      eepctl = 0;
      cnt = 0;
      do
      {
         /* set Eeprom to master */
         /* 设置EEPRO访问权限分配给主站 */
         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET);
      }
      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
      context->slavelist[slave].eep_pdi = 0;
   }

   return wkc;
}

该函数实际上就是通过设置ESC的EEPROM控制寄存器来夺取EEPROM访问控制权,涉及的寄存器功能描述如下:
SOEM源码解析——ecx_writeeeprom(写EEPROM)_第1张图片
主站首先发送FPWR配置写报文设置0x0500.1=1,强制PDI操作释放,将EEPROM的访问控制权分配给主站,相关语句如下:

wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET);

然后发送FPWR配置写报文设置0x0500.0=0,将EEPROM访问权限分配给主站,完成对EEPROM访问控制权的夺取,相关语句如下:

wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET);

1.2 ecx_eeprom2master函数解析

/** Write EEPROM to slave bypassing cache. FPWR method.:绕过从站缓存写EEPROM,FPWR方法
 * @param[in]  context        = context struct 句柄
 * @param[in] configadr   = configured address of slave 从站配置地址
 * @param[in] eeproma     = (WORD) Address in the EEPROM EEPROM地址(以字为单位)
 * @param[in] data        = 16bit data 16bit数据
 * @param[in] timeout     = Timeout in us.
 * @return >0 if OK
 */
int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout)
{
   uint16 estat;
   ec_eepromt ed;
   int wkc, rval = 0, cnt = 0, nackcnt = 0;

   if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
   {
      /* error bits are set */
      /* 如果EEPROM错误标志置位 */
      if (estat & EC_ESTAT_EMASK) 
      {
         /* clear error bits */
         /* 清除错误标志 */
         estat = htoes(EC_ECMD_NOP);
         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
      }
      do
      {
         cnt = 0;
         do
         {
            /* 将待写入数据写入到0x0508EEPROM数据寄存器 */
            wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
         }
         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
         ed.comm = EC_ECMD_WRITE;
         ed.addr = eeproma;
         ed.d2   = 0x0000;
         cnt = 0;
         do
         {
            /* 发送主站写命令及写地址 */
            wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
         }
         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
         if (wkc)
         {
            osal_usleep(EC_LOCALDELAY * 2);
            estat = 0x0000;
            /* 等待EEPROM接口空闲,完成写入操作 */
            if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
            {
               if (estat & EC_ESTAT_NACK)
               {
                  nackcnt++;
                  osal_usleep(EC_LOCALDELAY * 5);
               }
               else
               {
                  nackcnt = 0;
                  rval = 1;
               }
            }
         }
      }
      while ((nackcnt > 0) && (nackcnt < 3));
   }

   return rval;
}

该函数涉及的寄存器如下:
SOEM源码解析——ecx_writeeeprom(写EEPROM)_第2张图片
SOEM源码解析——ecx_writeeeprom(写EEPROM)_第3张图片
SOEM源码解析——ecx_writeeeprom(写EEPROM)_第4张图片在这里插入图片描述
(1)主站发送FPRD配置读报文检查0x0502寄存器bit15是否为0,为0则表示EEPROM接口空闲,相关语句和函数如下:

if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
uint16 ecx_eeprom_waitnotbusyFP(ecx_contextt *context, uint16 configadr,uint16 *estat, int timeout)
{
   int wkc, cnt = 0, retval = 0;
   osal_timert timer;

   osal_timer_start(&timer, timeout);
   do
   {
      if (cnt++)
      {
         osal_usleep(EC_LOCALDELAY);
      }
      *estat = 0;
      wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(*estat), estat, EC_TIMEOUTRET);
      *estat = etohs(*estat);
   }
   while (((wkc <= 0) || ((*estat & EC_ESTAT_BUSY) > 0)) && (osal_timer_is_expired(&timer) == FALSE)); /* wait for eeprom ready */
   if ((*estat & EC_ESTAT_BUSY) == 0)
   {
      retval = 1;
   }

   return retval;
}

在确认EEPROM为空闲状态时,需要检查EEPROM错误标志位,如果错误标志置位需要首先清除错误标志,使用FPWR配置地址写报文设置0x0502寄存器为0x0:

/* error bits are set */
      /* 如果EEPROM错误标志置位 */
      if (estat & EC_ESTAT_EMASK) 
      {
         /* clear error bits */
         /* 清除错误标志 */
         estat = htoes(EC_ECMD_NOP);
         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
      }

(2)将待写入数据写入到EEPROM数据寄存器
主站发送FPWR配置写报文设置0x0508EEPROM数据寄存器,将16bit的数据写入到该寄存器,相关语句如下:

do
         {
            /* 将待写入数据写入到0x0508EEPROM数据寄存器 */
            wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
         }

(3)发送主站写命令及写地址
主站发送FPWR配置写报文设置0x0502寄存器bit0和bit9为1表示写命令,这2bit的操作必须通过一个EtherCAT报文完成。同时该报文设置0x0504-0x0507EEPROM地址寄存器值为目标地址(以字为单位),相关语句如下:

ed.comm = EC_ECMD_WRITE;
         ed.addr = eeproma;
         ed.d2   = 0x0000;
         cnt = 0;
         do
         {
            /* 发送主站写命令及写地址 */
            wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
         }
         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));

(4)等待ESC完成EEPROM写入工作
主站发送FPRD报文读取0x0502寄存器的bit15,等待bit15为0也就是EEPROM接口空闲,这时去读取bit13命令应答位,查看命令是否正确执行(bit13=0表示无错误,bit13=1表示EEPROM无应答或命令无效)。相关语句如下:

if (wkc)
         {
            osal_usleep(EC_LOCALDELAY * 2);
            estat = 0x0000;
            /* 等待EEPROM接口空闲,完成写入操作 */
            if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
            {
               if (estat & EC_ESTAT_NACK)
               {
                  nackcnt++;
                  osal_usleep(EC_LOCALDELAY * 5);
               }
               else
               {
                  nackcnt = 0;
                  rval = 1;
               }
            }
         }

值得注意的是,上述(3)(4)步骤在一个while循环里执行,在(4)中出现了命令没有正确执行情况时会执行重复(3)(4)步骤最多3次。

2 总结

ecx_writeeeprom函数写从站EEPROM操作可以分为以下2个大块:
(1)通过ecx_eeprom2master函数夺取EEPROM访问控制权
(2)通过ecx_writeeepromFP函数写入数据到从站EEPROM

你可能感兴趣的:(工业以太网,EtherCAT,ecx_writeeeprom,SOEM,工业以太网)