目录
一、简介
1、 FMMU
2、SM
(1) 缓冲模式
(2)邮箱模式
3、FMMU将物理存储器映射到逻辑过程数据映射的配置原理
二、FMMU和SM在EtherCAT从站控制器的存储空间分配
三、FMMU和SM部分寄存器描述(LAN9253)
1、FMMU
2、SM
四、FMMU和SM的数据结构(soem主站)
(1) FMMU数据结构
(2) SM数据结构
五、FMMU和SM映射关系代码参考(soem)
六、其他相关链接
一、简介
1、 FMMU
Fieldbus Memory Management Unit,现场总线存储器管理单元, 通过内部地址映射将逻辑地址转换为物理地址。
FMMU允许跨越多个从设备的数据段使用逻辑地址;一个数据报寻址几个任意分布的EtherCAT从站控制器内的数据。
每个FMMU通道将一个连续的逻辑地址空间映射到从站的一个连续物理地址空间。
EtherCAT从站控制器的FMMU支持逐位映射,支持的FMMU数量取决于EtherCAT从站控制器。
FMMU支持的访问类型可配置为读、写或读/写。
2、SM
Sync Manager, 同步管理;
直接用EtherCAT从站控制器的存储器实现EtherCAT主站和本地应用程序之间交换数据,没有任何限制,这种直接通过内存通信存在缺点。所以需要SM来同步管理。SM可在EtherCAT主站和本地应用程序之间实现一致且安全的数据交换,并生成中断来通知双方发生数据更改。
SM管理DPRAM,保证了应用数据的一致性和安全性。
SM由EtherCAT主站配置。
SM支持两种通信模式。
(1) 缓冲模式
缓冲模式允许EtherCAT主站和本地应用程序随时访问通信缓冲区。
缓冲模式通常应用与循环过程数据。
(2)邮箱模式
邮箱模式以握手机制实现数据交换,不会丢数据。
邮箱模式通常用于应用程序层协议。
3、FMMU将物理存储器映射到逻辑过程数据映射的配置原理
二、FMMU和SM在EtherCAT从站控制器的存储空间分配
三、FMMU和SM部分寄存器描述(LAN9253)
1、FMMU
2、SM
四、FMMU和SM的数据结构(soem主站)
(1) FMMU数据结构
/** record for FMMU */
typedef __packed struct
{
uint32 LogStart;
uint16 LogLength;
uint8 LogStartbit;
uint8 LogEndbit;
uint16 PhysStart;
uint8 PhysStartBit;
uint8 FMMUtype;
uint8 FMMUactive;
uint8 unused1;
uint16 unused2;
} ec_fmmut;
(2) SM数据结构
/** record for sync manager */
typedef __packed struct
{
uint16 StartAddr;
uint16 SMlength;
uint32 SMflag;
} ec_smt;
五、FMMU和SM映射关系代码参考(soem)
/** Map all PDOs in one group of slaves to IOmap.
*
* @param[in] context = context struct
* @param[out] pIOmap = pointer to IOmap
* @param[in] group = group to map, 0 = all groups
* @return IOmap size
*/
int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
{
uint16 slave, configadr;
int Isize, Osize, BitCount, ByteCount, FMMUsize, FMMUdone;
uint16 SMlength, EndAddr;
uint8 BitPos;
uint8 SMc, FMMUc;
uint32 LogAddr = 0;
uint32 oLogAddr = 0;
uint32 diff;
int nSM, rval;
ec_eepromPDOt eepPDO;
uint16 currentsegment = 0;
uint32 segmentsize = 0;
if ((*(context->slavecount) > 0) && (group < context->maxgroup))
{
EC_PRINT("ec_config_map_group IOmap:%p group:%d\n \r", 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;
/* find output mapping of slave and program FMMU */
for (slave = 1; slave <= *(context->slavecount); slave++)
{
configadr = context->slavelist[slave].configadr;
ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* check state change pre-op */
EC_PRINT(" >Slave %d, configadr %x, state %2.2x\n \r",
slave, context->slavelist[slave].configadr, context->slavelist[slave].state);
/* execute special slave configuration hook Pre-Op to Safe-OP */
if(context->slavelist[slave].PO2SOconfig) /* only if registered */
{
context->slavelist[slave].PO2SOconfig(slave);
}
if(context->slavelist[slave].proc_init) /* only if registered */
{
context->slavelist[slave].proc_init(context,slave);
}
if (!group || (group == context->slavelist[slave].group))
{
/* if slave not found in configlist find IO mapping in slave self */
if (!context->slavelist[slave].configindex)
{
Isize = 0;
Osize = 0;
if (context->slavelist[slave].mbx_proto & ECT_MBXPROT_COE) /* has CoE */
{
rval = 0;
if (context->slavelist[slave].CoEdetails & ECT_COEDET_SDOCA) /* has Complete Access */
/* read PDO mapping via CoE and use Complete Access */
{
rval = ecx_readPDOmapCA(context, slave, &Osize, &Isize);
}
if (!rval) /* CA not available or not succeeded */
{
/* read PDO mapping via CoE */
rval = ecx_readPDOmap(context, slave, &Osize, &Isize);
}
EC_PRINT(" CoE Osize:%d Isize:%d\n \r", Osize, Isize);
}
if ((!Isize && !Osize) && (context->slavelist[slave].mbx_proto & ECT_MBXPROT_SOE)) /* has SoE */
{
/* read AT / MDT mapping via SoE */
rval = ecx_readIDNmap(context, slave, &Osize, &Isize);
context->slavelist[slave].SM[2].SMlength = htoes((Osize + 7) / 8);
context->slavelist[slave].SM[3].SMlength = htoes((Isize + 7) / 8);
EC_PRINT(" SoE Osize:%d Isize:%d\n \r", Osize, Isize);
}
if (!Isize && !Osize) /* find PDO mapping by SII */
{
memset(&eepPDO, 0, sizeof(eepPDO));
Isize = (int)ecx_siiPDO(context, slave, &eepPDO, 0);
EC_PRINT(" SII Isize:%d\n \r", 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 length %d\n \r", nSM, eepPDO.SMbitsize[nSM]);
}
}
Osize = (int)ecx_siiPDO(context, slave, &eepPDO, 1);
EC_PRINT(" SII Osize:%d\n \r", 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 length %d\n \r", nSM, eepPDO.SMbitsize[nSM]);
}
}
}
context->slavelist[slave].Obits = Osize;
context->slavelist[slave].Ibits = Isize;
EC_PRINT(" ISIZE:%d %d OSIZE:%d\n \r",
context->slavelist[slave].Ibits, Isize,context->slavelist[slave].Obits);
}
EC_PRINT(" SM programming\n \r");
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 Type:%d StartAddr:%4.4x Flags:%8.8x\n \r",
context->slavelist[slave].SMtype[0],
context->slavelist[slave].SM[0].StartAddr,
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 Type:%d StartAddr:%4.4x Flags:%8.8x\n \r",
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 \r", 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;
}
if (context->slavelist[slave].Obits > 7)
{
context->slavelist[slave].Obytes = (context->slavelist[slave].Obits + 7) / 8;
}
FMMUc = context->slavelist[slave].FMMUunused;
SMc = 0;
BitCount = 0;
ByteCount = 0;
EndAddr = 0;
FMMUsize = 0;
FMMUdone = 0;
/* create output mapping */
if (context->slavelist[slave].Obits)
{
EC_PRINT(" OUTPUT MAPPING\n \r");
/* search for SM that contribute to the output mapping */
while ( (SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Obits + 7) / 8)))
{
EC_PRINT(" FMMU %d\n \r", FMMUc);
while ( (SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3)) SMc++;
EC_PRINT(" SM%d\n \r", 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)) ) /* more SM for output */
{
SMc++;
while ( (SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3)) SMc++;
/* if addresses from more SM connect use one FMMU otherwise break up in mutiple FMMU */
if ( etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr )
{
break;
}
EC_PRINT(" SM%d\n \r", SMc);
SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
ByteCount += SMlength;
BitCount += SMlength * 8;
EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
}
/* bit oriented slave */
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++;
BitPos -= 8;
}
FMMUsize = 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 ++;
if (BitPos > 7)
{
LogAddr++;
BitPos -= 8;
}
}
/* byte oriented slave */
else
{
if (BitPos)
{
LogAddr++;
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 = 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;
context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;
context->slavelist[slave].FMMU[FMMUc].FMMUtype = 2;
context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;
/* program FMMU for output */
ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
context->grouplist[group].outputsWKC++;
if (!context->slavelist[slave].outputs)
{
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(" slave %d Outputs %p startbit %d\n \r",
slave,
context->slavelist[slave].outputs,
context->slavelist[slave].Ostartbit);
}
FMMUc++;
}
context->slavelist[slave].FMMUunused = FMMUc;
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].nsegments = currentsegment + 1;
context->grouplist[group].Isegment = currentsegment;
context->grouplist[group].Ioffset = segmentsize;
if (!group)
{
context->slavelist[0].outputs = pIOmap;
context->slavelist[0].Obytes = LogAddr; /* store output bytes in master record */
}
/* do input mapping of slave and program FMMUs */
for (slave = 1; slave <= *(context->slavecount); slave++)
{
configadr = context->slavelist[slave].configadr;
FMMUc = context->slavelist[slave].FMMUunused;
if (context->slavelist[slave].Obits) /* find free FMMU */
{
while ( context->slavelist[slave].FMMU[FMMUc].LogStart ) FMMUc++;
}
SMc = 0;
BitCount = 0;
ByteCount = 0;
EndAddr = 0;
FMMUsize = 0;
FMMUdone = 0;
/* create input mapping */
if (context->slavelist[slave].Ibits)
{
EC_PRINT(" =Slave %d, INPUT MAPPING\n \r", slave);
/* search for SM that contribute to the input mapping */
while ( (SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Ibits + 7) / 8)))
{
EC_PRINT(" FMMU %d\n \r", FMMUc);
while ( (SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4)) SMc++;
EC_PRINT(" SM%d\n \r", 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)) ) /* more SM for input */
{
SMc++;
while ( (SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4)) SMc++;
/* if addresses from more SM connect use one FMMU otherwise break up in mutiple FMMU */
if ( etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr )
{
break;
}
EC_PRINT(" SM%d\n \r", SMc);
SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
ByteCount += SMlength;
BitCount += SMlength * 8;
EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
}
/* bit oriented slave */
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++;
BitPos -= 8;
}
FMMUsize = 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 ++;
if (BitPos > 7)
{
LogAddr++;
BitPos -= 8;
}
}
/* byte oriented slave */
else
{
if (BitPos)
{
LogAddr++;
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 = 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;
/* program FMMU for input */
ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
/* add one for an input FMMU */
context->grouplist[group].inputsWKC++;
}
if (!context->slavelist[slave].inputs)
{
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 \r",
context->slavelist[slave].inputs,
context->slavelist[slave].Istartbit);
}
FMMUc++;
}
context->slavelist[slave].FMMUunused = FMMUc;
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); /* set Eeprom control to PDI */
ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , EC_TIMEOUTRET3); /* set safeop status */
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].Obytes;
if (!group)
{
context->slavelist[0].inputs = (uint8 *)(pIOmap) + context->slavelist[0].Obytes;
context->slavelist[0].Ibytes = LogAddr - context->slavelist[0].Obytes; /* store input bytes in master record */
}
EC_PRINT("IOmapSize %d\n \r", LogAddr - context->grouplist[group].logstartaddr);
return (LogAddr - context->grouplist[group].logstartaddr);
}
return 0;
}
六、其他相关链接
EtherCAT从站开发要点-CSDN博客
【EtherCAT】COE对象字典与PDO映射简介-CSDN博客