feat(ethercat): 添加LAN9252睡眠挂起机制防止HBI访问冲突

添加了s_pmSuspended状态标志来管理LAN9252的电源管理模式,在芯片进入D1-D3
睡眠状态时阻止HBI访问以避免唤醒问题。同步修改了ethercat_sync中断处理程序,
在睡眠挂起期间跳过HBI相关操作确保芯片能够正常维持睡眠状态。

fix(spi): 提升SPI CS EXTI抢占优先级避免帧丢失

将SPI CS EXTI中断优先级从1提升至0,使其能够抢占PDI/SYNC1等关键中断,确
保SPI帧释放处理能够在微秒级完成,避免因延时导致的ESP32 CS间隔超时和帧
丢失问题。

feat(ssc): 实现OP输入新鲜度不变量监控机制

添加了输入映射序列号监控机制,当PDI路径中断时能够检测到输入数据冻结并
自动触发强制刷新,解决结构洞问题导致的输入数据停止更新现象。

chore(pdo): 更新PDO输入字节注释说明布局变更历史
This commit is contained in:
cbb
2026-09-13 18:59:57 +08:00
parent 6afaacd03a
commit 6136c2363f
12 changed files with 524 additions and 56 deletions
+91
View File
@@ -149,6 +149,16 @@ extern volatile UINT32 g_dbg_appCyc;
/* [DIFF-STATS] 差分写统计窗口值(comm/ethercat_diff_stats.cpp),out[0..10] 对应 sub13..23 */
void EcatDiffStats_GetWindow(UINT32 *out);
/* 1.6.33:OP 输入新鲜度 — APPL_InputMapping 每次成功刷新就自增 */
static volatile UINT32 s_inMapSeq = 0U;
static UINT32 s_inMapSeqSeen = 0U;
static UINT32 s_inStaleBaseCyc = 0U;
static UINT32 s_opFreshRescues = 0U;
#include <stdint.h>
/* DFP 的 SystemCoreClock 是 uint32_t(=unsigned int);SSC 的 UINT32 是 unsigned long,
* 不能用 UINT32 重复声明。 */
extern uint32_t SystemCoreClock;
/* 过程数据收发模块 (comm/ethercat_pdo.cpp 的 extern "C" shim, 1.5.54)。
* 1.5.53 时这里直接调 EcatCan_* 转发桥;1.5.54 把"一个 EtherCAT 周期怎么收发"
* 收拢进 EthercatPdo 类,本文件退回纯 SSC 移植层:只留对象字典 + 三行转发。 */
@@ -437,6 +447,9 @@ void APPL_InputMapping(UINT16 *pInputData)
* SDO 读 0x6000 时看到与过程数据一致的内容。 */
EcatPdo_WriteInputs((UINT8 *)&s_inputObj.u64Data[0]);
memcpy(pInputData, &s_inputObj.u64Data[0], ECAT_PDO_IN_BYTES);
/* 1.6.33:OP 输入新鲜度不变量的活性计数。每次真正走到输入映射就自增,
* 供 EcatDbg_OpInputFreshness 判断 PDI 路径是否还活着。 */
++s_inMapSeq;
}
/*---------------------------------------------------------------------------
@@ -632,3 +645,81 @@ UINT32 EcatDbg_GuardAleventMask(void)
}
return actual;
}
/*---------------------------------------------------------------------------
* OP 输入新鲜度不变量 — 1.6.33
*
* 结构洞(用户实锤:1ms 也复现,复位 GD32 APP 才好):
* SM-Synchron 稳态下 MainLoop 的 free-run 分支被
* (!bEscIntEnabled || !bEcatFirstOutputsReceived) && !bDcSyncActive
* 整段跳过,输入 PDO **只**由 PDI_Isr 的 PDO_InputMapping 刷新。
* PDI 通路一停(门控/掩码/EXTI),ESC 仍答 WKC、AL 仍 OP,主站读到的是
* 冻住的旧缓冲 —— 网页绿灯+高 Hz、CPU/CAN 全像没解析。既有 1.5.60 只救
* "门关着",救不了"门开着但 EXTI 不来"。
*
* 根治:把「OP ⇒ 输入必须在 T ms 内被重写」做成不变量。
* - 正常路径:PDI_Isr 每拍自增 s_inMapSeq
* - 异常路径:seq 停涨超过阈值 → 主循环强制 PDO_InputMapping
* (屏中断防双写)+ 重开门/应答 SM2,主站侧 gseq/rx 立刻恢复前进
* 不依赖主站重拉、不依赖人工复位。MainLoop 若本身死锁则本函数也停 ——
* 那是另一类故障(心跳会停),不在本不变量覆盖范围。
*---------------------------------------------------------------------------*/
void EcatDbg_OpInputFreshness(void)
{
const UINT8 al = (UINT8)(nAlStatus & 0x0FU);
const UINT32 now = EcatDbg_Dwt();
const UINT32 seq = s_inMapSeq;
if (al != 8U || nPdInputSize == 0U) {
s_inMapSeqSeen = seq;
s_inStaleBaseCyc = now;
return;
}
if (seq != s_inMapSeqSeen) {
s_inMapSeqSeen = seq;
s_inStaleBaseCyc = now;
return;
}
/* seq 未推进:等满阈值再介入,避开两拍之间的正常空隙。
* 5ms @1ms 周期 = 连丢 ≥5 拍;@4ms 周期 = 连丢 1~2 拍,仍足够判死。 */
{
const UINT32 thresh = (SystemCoreClock / 1000U) * 5U;
if ((UINT32)(now - s_inStaleBaseCyc) < thresh) {
return;
}
}
/* 强制刷新:先重开门/首输出标志(门关着时 PDI 永远不会回来),
* 再在关中断窗口内做一次输入映射 —— 此路径极低频,关中断可接受。 */
{
UINT16 dummy = 0U;
const UINT16 evt = HW_GetALEventRegister();
if (!bEscIntEnabled) {
bEscIntEnabled = TRUE;
}
if (!bEcatFirstOutputsReceived) {
bEcatFirstOutputsReceived = TRUE;
HW_EscReadWord(dummy, nEscAddrOutputData);
HW_EscReadWord(dummy, (UINT16)(nEscAddrOutputData + nPdOutputSize - 2U));
}
__asm volatile ("cpsid i" ::: "memory");
PDO_InputMapping();
__asm volatile ("cpsie i" ::: "memory");
++s_opFreshRescues;
s_inMapSeqSeen = s_inMapSeq;
s_inStaleBaseCyc = EcatDbg_Dwt();
printf("[ECAT][FRESH] WARN: OP input frozen (seq=%lu) evt=0x%04X "
"(gate=%u first=%u dc=%u) -> forced PDO_InputMapping #%lu\r\n",
(unsigned long)seq, (unsigned)evt,
(unsigned)(bEscIntEnabled ? 1U : 0U),
(unsigned)(bEcatFirstOutputsReceived ? 1U : 0U),
(unsigned)(bDcSyncActive ? 1U : 0U),
(unsigned long)s_opFreshRescues);
}
}
UINT32 EcatDbg_OpFreshRescues(void)
{
return s_opFreshRescues;
}
+4
View File
@@ -32,6 +32,10 @@ void APPL_InputMapping(UINT16 *pInputData);
void APPL_OutputMapping(UINT16 *pOutputData);
void APPL_Application(void);
/* 1.6.33:OP 输入新鲜度不变量(task_runner 每圈调用;内部廉价,仅冻结时强制重写) */
void EcatDbg_OpInputFreshness(void);
UINT32 EcatDbg_OpFreshRescues(void);
#ifdef __cplusplus
}
#endif