EtherCAT WatchDog
目录
- 前言
- DC看门狗
- 如何触发OP to SafeOP的
- 如何设置门限值的
- 如何喂狗的
- 本地看门狗
- 如何触发OP to SafeOP的
- 如何设置门限值的
- 如何喂狗的
前言
- EtherCAT设置看门狗的目的是,执行周期不正常的时候,将状态机由OP返回到SafeOP。比如轮询模式下APP部分拖了太长的时间,中断模式下协议栈之外的某个中断卡死导致过程数据更新周期异常
- EtherCAT协议栈中的看门狗是软件实现的。门限值参数由ESC寄存器中提供,判断超限和回到SafeOP都要协议栈实现。
协议栈里有两种看门狗,分别是DC看门狗和本地看门狗。如果使用了DC的话协议栈会生成SM相关的看门狗代码,此时不需要再做本地看门狗了。(要手动修改ESC_SM_WD_SUPPORTED为1,才不会生成相关代码)
如果没有使用DC的话,手动修改ESC_SM_WD_SUPPORTED为0,协议栈会生成一个本地看门狗的代码
DC看门狗
如何触发OP to SafeOP的
变量定义:
Sync0WdCounter:sync0看门狗计数器
Sync0WdValue:sync0看门狗门限值
Sync1WdCounter:sync1看门狗计数器
Sync1WdValue:sync1看门狗门限值
EtherCAT要求提供一个1ms的定时器,对接函数如下
//此函数文件来自用户对接
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim == &htim3){ECAT_CheckTimer();}
}
ECAT_CheckTimer会调用DC_CheckWatchdog, DC_CheckWatchdog内容如下
简单来说就是判断Sync0WdCounter 是否超过 Sync0WdValue 门限值,超过了设置 bDcRunning = TRUE;,否则计数器自增
//ecatslv.c -> DC_CheckWatchdog()
void DC_CheckWatchdog(void)
{.../*If Sync0 watchdog is enabled and expired*/if((Sync0WdValue > 0) && (Sync0WdCounter >= Sync0WdValue)){/*Sync0 watchdog expired*/bDcRunning = FALSE; }else{if(Sync0WdCounter < Sync0WdValue){Sync0WdCounter ++;}bDcRunning = TRUE;}.../*Check the Sync1 cycle if Sync1 Wd is enabled*/if(Sync1WdValue > 0){if(Sync1WdCounter < Sync1WdValue){Sync1WdCounter ++;}else{/*Sync1 watchdog expired*/bDcRunning = FALSE;}}}
为什么设置了bDcRunning = FALSE;就能让EtherCAT状态机回到SAFEOP呢?
MainLoop -> CheckIfEcatError中有如下代码
//ecatslv.c -> CheckIfEcatError
void CheckIfEcatError(void)
{...if(bDcSyncActive){if(bEcatOutputUpdateRunning){/*Slave is in OP state*/if(!bDcRunning){AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_FATALSYNCERROR);return;}else if(!bSmSyncSequenceValid){AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_SYNCERROR);return;}}}}
如何设置门限值的
cycleTimeSync0, shiftTimeSync1 分别从寄存器ESC_DC_SYNC0_CYCLETIME, ESC_DC_SYNC1_CYCLETIME中读取获得
Sync0WdValue 的门限值设置规则是
- Sync0WdValue 是cycleTimeSync0 的二倍
- Sync0WdValue 至少为1ms
Sync1WdValue的门限值设置规则是
- 如果shiftTimeSync1 比cycleTimeSync0短,那么Sync1WdValue=Sync0WdValue
- Sync1Cycle 是cycleTimeSync0和shiftTimeSync1 之和,Sync1WdValue是Sync1Cycle 的二倍
- Sync1WdValue至少1ms
- Sync1WdValue要在上述计算值上增加Sync0WdValue的一半。
建议结合代码理解,上面的描述感觉不准确,比如最后的加一半MCU里可能会丢小数。
//ecatslv.c -> StartInputHandler()
UINT16 StartInputHandler(void)
{....// Cycle time for Sync0HW_EscReadDWord(cycleTimeSync0, ESC_DC_SYNC0_CYCLETIME_OFFSET);cycleTimeSync0 = SWAPDWORD(cycleTimeSync0);// Cycle time for Sync1HW_EscReadDWord(shiftTimeSync1, ESC_DC_SYNC1_CYCLETIME_OFFSET);shiftTimeSync1 = SWAPDWORD(shiftTimeSync1);..../*calculate the Sync0 Watchdog counter value the minimum value is 1 msif the sync0 cycle is greater 500us the Sync0 Wd value is 2*Sycn0 cycle */if(cycleTimeSync0 == 0){Sync0WdValue = 0;}else{UINT32 Sync0Cycle = cycleTimeSync0/100000;if(Sync0Cycle < 5){/*Sync0 cycle less than 500us*/Sync0WdValue = 1;}else{Sync0WdValue = (UINT16)(Sync0Cycle*2)/10;}}/* Calculate also the watchdog time for Sync1*/if ( (dcControl & ESC_DC_SYNC1_ACTIVE_MASK) != 0 ){if(shiftTimeSync1 < cycleTimeSync0){/* Sync 1 has the same cycle time than Sync0 (maybe with a shift (shiftTimeSync1 > 0))*/Sync1WdValue = Sync0WdValue;}else{/* Sync1 cycle is larger than Sync0 (e.g. subordinated Sync0 cycles) */UINT32 Sync1Cycle = (shiftTimeSync1 + cycleTimeSync0 )/100000;if(Sync1Cycle < 5){/*Sync0 cycle less than 500us*/Sync1WdValue = 1;}else{Sync1WdValue = (UINT16)((Sync1Cycle*2)/10);}/* add one Sync0 cycle because the Sync1 cycle starts on the next Sync0 after the Sync1 signal */Sync1WdValue += Sync0WdValue/2;}
}
如何喂狗的
//ecatappl.c -> Sync0_Isr
void Sync0_Isr(void)
{Sync0WdCounter = 0;...
}void Sync1_Isr(void)
{Sync1WdCounter = 0;...
}
DC大概就这样
本地看门狗
如何触发OP to SafeOP的
变量定义:
EcatWdCounter :本地看门狗计数值
EcatWdValue :本地看门狗门限值
//此函数文件来自用户对接
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim == &htim3){ECAT_CheckTimer();}
}
//ecatappl.c -> ECAT_CheckTimer
void ECAT_CheckTimer(void)
{...ECAT_CheckWatchdog()
}
判断的套路与上面DC的一致,检查到超限就设置回到SafeOP
//ecatslv.c -> ECAT_CheckWatchdog
void ECAT_CheckWatchdog(void)
{if ( EcatWdValue != 0 ){// Watchdog is activeEcatWdCounter++;if ( EcatWdCounter >= EcatWdValue ){// Watchdog is expiredEcatWdCounter = 0;if (nPdOutputSize > 0){//handle only the expired PD watchdog if outputs are configuredif (bEcatOutputUpdateRunning){/* watchdog expired in OP */AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_SMWATCHDOG);}else{/* watchdog expired in SAFE-OP */bEcatFirstOutputsReceived = FALSE;}}}}
}
如何设置门限值的
wd:来自ESC_PD_WD_TIME寄存器,看门狗门限
wdiv:来自ESC_WD_DIVIDER_OFFSET,分频器
EcatWdValue = (wd * wdiv),再做一次圆整(没分析具体圆整到多少)
ecatslv.c -> StartInputHandler()中有下面内容
//ecatslv.c -> StartInputHandler()
UINT16 StartInputHandler(void)
{..../*get the watchdog time (register 0x420). if value is > 0 watchdog is active*/HW_EscReadWord(wd, ESC_PD_WD_TIME);wd = SWAPWORD(wd);if (nPdOutputSize > 0 && wd != 0 ){/*get watchdog divider (register 0x400)*/HW_EscReadWord(wdiv, ESC_WD_DIVIDER_OFFSET);wdiv = SWAPWORD(wdiv);if ( wdiv != 0 ){/* the ESC subtracts 2 in register 0x400 so it has to be added here */UINT32 d = wdiv+2;d *= wd;/* store watchdog in ms in variable EcatWdValue *//* watchdog value has to be rounded up */d = (INT32)(d + 24999);d /= 25000;EcatWdValue = (UINT16) d;}else{wd = 0;/* wd value has to be set to zero, if the wd is 0 */EcatWdValue = 0;}/* reset watchdog counter */EcatWdCounter = 0;}else{/* the watchdog is deactivated or slave has no output process data*/wdiv = 0;EcatWdValue = 0;}
}
如何喂狗的
void PDI_Isr(void)
{...EcatWdCounter = 0;}
除了PDI ISR,mainloop中也有,不赘述了