本文介绍SOEM(Simple Open Source Master)如何配置从站的SM(SyncManager)寄存器。
基于SOEM-1.3.1。
SM配置寄存器从0x800开始,每个通道使用8个字节,如下图所示:
其中,物理起始地址和控制寄存器使用从站EEPROM中的值,长度根据PDO中映射的数据长度计算。
初始化过程中,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++;
}
}
在函数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;
}
在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控制寄存器各位含义为: