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访问控制权,涉及的寄存器功能描述如下:
主站首先发送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;
}
该函数涉及的寄存器如下:
(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