[EtherCAT]一种竞争态引起从站报错0x2C的分析
从站0x2C错误码逻辑解释
根据《[EtherCAT]从站运行过程中报错(错误码:0x1A\0x1B\0x2C)的代码分析》,0x2C错误码是利用1ms软件定时器监控的SYNC0中断服务函数的执行情况,其核心是DC_CheckWatchdog()函数和Sync0_Isr()/Sync1_Isr()的执行。
在DC_CheckWatchdog()函数中对变量Sync0WdCounter进行递增,在Sync0_Isr()函数中对变量Sync0WdCounter进行清零(相当于喂狗)。Sync0_Isr()正常执行时,变量Sync0WdCounter不会超出阈值,当变量Sync0WdCounter超出阈值时就会触发从站报出0x2C错误码。
竞争态情况分析
由于在RV32平台上,Sync0WdCounter++指令不是单条机器指令,而是多条机器指令,按照读-改-写三个阶段完成变量更新。当Sync0_Isr()恰巧打断Sync0WdCounter++, 就会造成Sync0_Isr()中的变量清零操作Sync0WdCounter = 0被抹掉了,没有实现变量清零,后续变量计数就会超出阈值。

为什么会出现竞争态
-
SSC设置参数ECAT_TIMER_INT = 0 生成协议栈代码
此时DC_CheckWatchdog()在mainloop中被调用,而Sync0_Isr()/Sync1_Isr()依赖系统DC时间产生,两者是不同步的,存在竞争的可能。
-
SSC设置参数ECAT_TIMER_INT = 1 生成协议栈代码
此时DC_CheckWatchdog()改为在从站本地的1ms tick中断中被调用。
EtherCAT系统默认第一个DC从站的DC时间作为整个系统的参考时间,即主时钟。其他DC从站的DC时间是从时钟,会被周期性的补偿,保证其同步于主时钟。
对于第一个从站,其SYNC0/SYNC1中断依赖的DC时间和1ms Timer时间都是基于相同的本地的晶振产生,两者频率锁定,相位偏移固定不变,不会产生竞争。
对于第二个从站,其DC时间由ESC跟着系统主时钟调整,而 1 ms Timer仍按本地晶振走。两个时基的频率差就是两端晶振的频率差(典型 ±20~±50 ppm),这个频差会让 Sync0_Isr 与 DC_CheckWatchdog 的相对相位随时间缓慢扫过整个 1 ms 周期,最终一定会扫到 Sync0_Isr 恰好抢占 DC_CheckWatchdog 在 R-M-W 序列中间的临界窗口,从而触发竞态。
解决办法
为了避免出现DC_CheckWatchdog()函数与Sync0_Isr()/Sync1_Isr()函数之间的竞争,有两种方式可以解决。
-
SSC设置参数ECAT_TIMER_INT = 0 生成协议栈代码
生成的代码中DC_CheckWatchdog()中会有先DISABLE_ESC_INT()后ENABLE_ESC_INT()的保护操作, 保证
Sync0WdCounter++不会被Sync0_Isr()/Sync1_Isr()打断。注意: 该条件下生成的SSC代码DC_CheckWatchdog()是在mainloop中调用,存在执行DC_CheckWatchdog()的DISABLE_ESC_INT()后被一个低优先级且耗时的其他中断打断,造成ESC中断长时间未开启,相应中断无法执行的情况。 应予以避免。
-
SSC设置参数ECAT_TIMER_INT = 1 生成协议栈代码
生成的代码中DC_CheckWatchdog()中没有先DISABLE_ESC_INT()后ENABLE_ESC_INT()的保护操作。DC_CheckWatchdog()改为在1ms tick的中断函数中被调用。 需要设置1ms tick的中断优先级 >= SYNC0/SYNC1的中断优先级, 避免
Sync0WdCounter++不会被Sync0_Isr()/Sync1_Isr()打断。