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
+23
View File
@@ -228,6 +228,7 @@ bool EscLan9252::s_busReady = false;
bool EscLan9252::s_coreReady = false;
bool EscLan9252::s_resetDone = false;
bool EscLan9252::s_pmPhyDown = false;
bool EscLan9252::s_pmSuspended = false;
void EscLan9252::hardwareReset()
{
@@ -254,6 +255,8 @@ void EscLan9252::hardwareReset()
printf("[ECAT][ESC] hardwareReset: RST# low %ums -> released, pin reads %s\r\n",
(unsigned)CONFIG_ESC_NRST_ASSERT_MS,
pinHigh ? "HIGH" : "*** LOW (net shorted to GND / pin damaged) ***");
/* RST# 复位清掉 PM 睡眠态(芯片回 D0),挂起门控随之解除(1.6.27) */
s_pmSuspended = false;
}
uint8_t EscLan9252::nrstDrive(uint8_t action)
@@ -266,6 +269,8 @@ uint8_t EscLan9252::nrstDrive(uint8_t action)
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
CONFIG_ESC_NRST_PIN);
gpio_bit_set(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN);
/* 复位释放后芯片重走上电流程回 D0,睡眠挂起随之解除(1.6.27) */
s_pmSuspended = false;
break;
case 1: /* 持续拉低(手动保持复位) */
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
@@ -275,6 +280,7 @@ uint8_t EscLan9252::nrstDrive(uint8_t action)
case 3: /* 释放:浮空输入,网络交给 R44 10k 上拉 */
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_IN_FLOATING, 0,
CONFIG_ESC_NRST_PIN);
s_pmSuspended = false;
break;
case 4: /* 只读回,不改驱动 */
break;
@@ -315,6 +321,12 @@ uint8_t EscLan9252::pmDrive(uint8_t op)
* 本操作的语义 = "读状态(若在睡眠则主动打破睡眠)",非无侵入观测。 */
const uint32_t pmt = readReg32(LAN9252_REG_PMT_CTRL);
status = pmPackStatus(pmt);
/* 读到 D0(SLEEP_EN=0):芯片不在睡眠,若门控仍挂起(如 op4 唤醒超时后
* 芯片延迟自醒)则自愈解除,恢复主循环/ISR 的 HBI 访问(1.6.27)。 */
if ((status & 0x02U) == 0U && s_pmSuspended) {
s_pmSuspended = false;
printf("[ECAT][PM] status: chip in D0, pm suspend gate released\r\n");
}
printf("[ECAT][PM] status: PMT_CTRL=0x%08lX READY=%u SLEEP_EN=%u MODE=D%u\r\n",
(unsigned long)pmt,
(unsigned)((status >> 0) & 1U), (unsigned)((status >> 1) & 1U),
@@ -332,6 +344,11 @@ uint8_t EscLan9252::pmDrive(uint8_t op)
/* PM_MODE 与 PM_SLEEP_EN 必须分两次写(DS 6.3.5.1 注:同一写中改 MODE 置 EN 无效)。 */
writeReg32(LAN9252_REG_PMT_CTRL, v);
writeReg32(LAN9252_REG_PMT_CTRL, v | PMT_CTRL_PM_SLEEP_EN);
/* 1.6.27:立即挂起全部 HBI 消费者(task_runner 停 MainLoop/守护/心跳,
* ethercat_sync 三 ISR 清 pending 后直接返回)——本函数运行于 CS EXTI ISR,
* 退出后主循环第一圈 ECAT_Main 读 0x220 就会把刚睡着的芯片叫醒;
* 此前置位窗口内本 ISR 是唯一运行者(maskEnter 已屏 EXTI0/1),无竞态。 */
s_pmSuspended = true;
/* 1.6.24 实测:进入睡眠后任何 HBI 访问(含读!)都会立即触发唤醒——
* 手册 6.3.4.2 只说 HBI "write" 唤醒,但芯片实际按主机 CS 活动判定,
* 写入后的读回验证会在 <50ms 内把芯片叫回 D0(板级复现:进 D1 后
@@ -372,6 +389,12 @@ uint8_t EscLan9252::pmDrive(uint8_t op)
printf("[ECAT][PM] wake: PHY power-up %s (autoneg re-negotiating)\r\n",
restored ? "OK" : "FAIL (retry action 34)");
}
if (awake) {
/* 唤醒成功:解除 HBI 挂起,主循环/ISR 恢复运行(1.6.27)。
* 超时则保持挂起——芯片大概率仍睡着;若实际已醒,下一次 op0
* 状态查询读到 D0 会自愈解除。 */
s_pmSuspended = false;
}
status = awake ? pmPackStatus(readReg32(LAN9252_REG_PMT_CTRL)) : 0U;
printf("[ECAT][PM] wake: %s -> status=0x%02X (READY=%u SLEEP_EN=%u)\r\n",
awake ? "OK" : "TIMEOUT (10ms, poll action 30 / retry 34)",