"""_probe_wedge.py — 楔死现场逐环节定位(纠正之前 0x0810/0x0818=SM2/SM3 的标号错误,真邮箱 SM 是 0x0800/0x0808)。 环节定位: A. FPWR 写 SM0 RAM 的 WKC —— 写都进不去 = SM0 硬件层被拒(disabled/错址) B. 写入后 0x220 bit0(SM0 写事件)是否置位 —— ESC 没置事件 = ESC 硬件楔死 C. 200ms 后 bit0 是否被 SSC 清掉 + SM1 status bit3(读邮箱满)是否置位 —— 没处理 = SSC 软件死了 D. SM1 RAM 里有没有滞留的旧响应(bSendMbxIsFull 死锁证据) """ import struct import sys import time import ecat_server as es import pysoem r = es.api_open({"substr": "I226"}) if not r.get("ok"): print("open 失败,板子可能已断电", flush=True) sys.exit(1) m = es.g_master s = m.slaves[0] def fprd(addr, size): try: return bytes(s._fprd(addr, size, 20000)) except Exception: return None def dump(tag): al = fprd(0x0130, 8) ev = fprd(0x0220, 2) am = fprd(0x0204, 2) sm0 = fprd(0x0800, 8) sm1 = fprd(0x0808, 8) def fmt(d): if d is None: return "ERR" start, length = struct.unpack("> 1) & 1}) 0x204=0x{am_v:04X}", flush=True) print(f" SM0(0x0800): {fmt(sm0)}", flush=True) print(f" SM1(0x0808): {fmt(sm1)}", flush=True) return sm0, sm1, ev_v # --- 阶段 1: PREOP 下探(邮箱 SM 应处于使能态) --- m.state = pysoem.INIT_STATE | pysoem.STATE_ACK m.write_state() m.state_check(pysoem.INIT_STATE, 1_000_000) m.config_init() m.state = pysoem.PREOP_STATE | pysoem.STATE_ACK m.write_state() st = m.state_check(pysoem.PREOP_STATE, 1_000_000) print(f"PREOP state_check = 0x{st & 0x0F:02X}", flush=True) sm0, sm1, ev = dump("PREOP 稳态") if sm0 is None or sm1 is None: print("SM0/SM1 读取失败", flush=True) es.api_close({}) sys.exit(1) # SM0/SM1 RAM 内容(滞留响应证据) mbx0_addr, mbx0_len = struct.unpack("> 3 & 1},应=1,响应就绪)", flush=True) if ev3 is not None and (ev3 & 1): print(" >>> bit0 仍挂起:SSC 轮询没处理邮箱(环节C死点:MainLoop/ECAT_Main 邮箱分支死了)", flush=True) elif sm1_st & 0x08: d = fprd(mbx1_addr, 16) print(f" 阶段D: SM1 响应 RAM = {d.hex().upper() if d else 'ERR'}", flush=True) print(" >>> SSC 活着!邮箱处理正常 —— 楔死只在特定条件下", flush=True) else: print(" >>> bit0 被清了但 SM1 无响应:SSC 收到了但没回(处理链中途死)", flush=True) es.api_close({}) print("DONE", flush=True)