ECAT_Main函数在MainLoop中轮询,下面是ECAT_Main中的运行流程。
MBX_Main();
在MBX_Main中,获取sMbxReceiveQueue中的数据,包括COE的SDO和FOE。对SDO数据进行处理,然后回复Master。
if ( bMbxRunning )
{
HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
sm1Activate = SWAPWORD(sm1Activate);
}
bMbxRunning 在成功进入Bootstrap或者Pre-OP状态的时候变为TRUE。这里读取0x80E寄存器的值。SWAPWORD用于大小端的转换,ESC是小端芯片,如果从机的MCU是大端的话需要进行转换,这里用的小端的MCU,所以SWAPWORD没什么作用。
ALEventReg = HW_GetALEventRegister();
ALEventReg = SWAPWORD(ALEventReg);
读取AL事件请求寄存器0x220的值
if ((ALEventReg & AL_CONTROL_EVENT) && !bEcatWaitForAlControlRes)
{
HW_EscReadWord( EscAlControl, ESC_AL_CONTROL_OFFSET);
EscAlControl = SWAPWORD(EscAlControl);
ALEventReg &= ~((AL_CONTROL_EVENT) | (SM_CHANGE_EVENT));
AL_ControlInd((UINT8)EscAlControl, 0);
}
需要满足的判断条件有两个:
- 0x220的bit0置1,也就是产生了主站状态请求事件;
- bEcatWaitForAlControlRes标志位为FALSE。bEcatWaitForAlControlRes在一般状态下是FALSE的,只有在safeop到OP状态的转换的时候,会先将bEcatWaitForAlControlRes标记为TRUE,然后等待OP条件满足,例如:DC模式下,需要200ms内稳定收到PDO数据,或者其他模式下需要收到一帧PDO数据,如果nPdOutputSize为0,则不需要PDO接收PDO数据。在条件满足后,从站会将bEcatWaitForAlControlRes标记为FALSE,然后设置状态为OP。
进入这个判断后:
- 读取0x120寄存器的值,这个值就是主站请求的状态,
- 将ALEventReg的bit0和bit4清0。这里的清0主要是避免后续重复调用AL_ControlInd函数。这里清0只是清除变量里的值,若要寄存器清0,对0x120读操作,0x220的bit0清0;对0x806进行读操作,0x220的bit4清0。
- 调用AL_ControlInd设置状态
if ( (ALEventReg & SM_CHANGE_EVENT) && !bEcatWaitForAlControlRes && (nAlStatus & STATE_CHANGE) == 0 && (nAlStatus & ~STATE_CHANGE) != STATE_INIT )
{
ALEventReg &= ~(SM_CHANGE_EVENT);
AL_ControlInd(nAlStatus & STATE_MASK, 0);
}
需要满足的判断条件有四个:
- 0x220的bit4置1,也就是SM激活寄存器(SM配置寄存器+0x6)产生了变化
- bEcatWaitForAlControlRes标志位为FALSE
- 当前从站的状态未报错
- 当前从站的状态不是INIT
进入这个判断以后:
- 将ALEventReg的bit4清0
- 调用AL_ControlInd设置状态,这里的目的不是为了改变状态,因为填入的请求状态参数是当前的状态值。这里主要目的是当SM激活寄存器发生改变后,使用AL_ControlInd函数中的CheckSmSettings来检查SM的配置。这也解释了为什么在上一步会将ALEventReg的bit0和bit4都清0了。因为在上一步调用AL_ControlInd已经检查过了,不需要重复检查了。
if(bEcatWaitForAlControlRes)
{
AL_ControlRes();
}
bEcatWaitForAlControlRes是在safeop转换OP的时候标记未TRUE的,AL_ControlRes主要是判断OP条件是否满足,当条件满足后会将从站状态改变为OP,并标记bEcatWaitForAlControlRes为FALSE。
if (!(sm1Activate & SM_SETTING_ENABLE_VALUE))
{
AL_ControlInd(nAlStatus & STATE_MASK, 0);
}
这里应该还是对邮箱配置进行检查,但是因为我的邮箱一直都是激活状态,所以全程没有进入到这个条件中去。
if ( ALEventReg & (MAILBOX_READ_EVENT) )
{
u16dummy = 0;
HW_EscWriteWord(u16dummy,u16EscAddrSendMbx);
ALEventReg &= ~(MAILBOX_READ_EVENT);
MBX_MailboxReadInd();
}
需要满足的判断条件一个:
- SM1通道有事件发生,主站读取了SM1中的数据;
进入这个判断后:
- 写取SM1的第一个字节,用于清除0x220中的SM1事件的置位。
- 清除变量中相应位置的置位。
- MBX_MailboxReadInd查看发送队列中是否还有待发送的数据,如果有,将数据写入SM1的缓存中
if ( ( (sm1Activate & SM_SETTING_REPAET_REQ_MASK) && !bMbxRepeatToggle )
||( !(sm1Activate & SM_SETTING_REPAET_REQ_MASK) && bMbxRepeatToggle ))
{
MBX_MailboxRepeatReq();
if (bMbxRepeatToggle)
{
sm1Activate |= SM_SETTING_REPEAT_ACK;
}
else
{
sm1Activate &= ~SM_SETTING_REPEAT_ACK;
}
sm1Activate = SWAPWORD(sm1Activate);
HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
}
判断条件一个:
- 重复请求位(SM配置寄存器+0x6 的bit1)是否翻转,bMbxRepeatToggle是上一次bit1的值。
进入这个判断后:
- 调用MBX_MailboxRepeatReq将之前保存的上一包数据重新写入邮箱之中,并且更新bMbxRepeatToggle的值。
- 写重复请求应答位(SM配置寄存器+0x7 的bit1),该位与重复请求位相同时,表示PDI对前面设置的重复请求应答。其实也可以理解为主站翻转+0x6的bit1来告诉从站需要重传,然后主站一直检测+0x7的bit1,发现翻转后,说明重传数据已经放入邮箱了,主站再去读从站邮箱里的数据。
ALEventReg = HW_GetALEventRegister();
ALEventReg = SWAPWORD(ALEventReg);
if ( ALEventReg & (MAILBOX_WRITE_EVENT) )
{
ALEventReg &= ~(MAILBOX_WRITE_EVENT);
MBX_CheckAndCopyMailbox();
}
重新读取0x220的寄存器的值,或者这个值已经变了。
判断SM0事件是否发生了,主机是否有数据写进来了
如果SM0数据发生了,那么先将变量ALEventReg相应的位清0,然后调用MBX_CheckAndCopyMailbox将有效数据写入 sMbxReceiveQueue中。sMbxReceiveQueue会在MBX_Main()里面进行处理。