From 3bfa240dfa5d22948a93a7599c20e223b3114f30 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Thu, 3 Sep 2026 21:42:45 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=E5=9B=9B=E4=B8=AA?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=E6=80=A7=E9=82=AE=E7=AE=B1=E6=A5=94=E6=AD=BB?= =?UTF-8?q?=E6=8E=A2=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _probe_wedge/_probe_wedge2/_probe_stuck/_probe_scan 已完成使命: 根因查明为 SSC 的 u16EscAddrSendMbx 错位 —— 响应帧写到 SM1 start+4/+8 且逐会话累积 (断电才清零), 主站从固定 0x1200 读永远对不上帧头; SM 寄存器本身恒正确, 所以"SM0/SM1 配置正常却持续失败"就是该错位的判据。 结论已沉淀进 ecat_server.py config_map 失败分支的 SM 现场转储(扫全 4 个 SM, 0x0800+8n)与注释, 固件 1.5.52+ 已加守护自动写回恢复。探针不再需要。 --- _probe_scan.py | 69 ------------------------------ _probe_stuck.py | 77 ---------------------------------- _probe_wedge.py | 107 ----------------------------------------------- _probe_wedge2.py | 93 ---------------------------------------- 4 files changed, 346 deletions(-) delete mode 100644 _probe_scan.py delete mode 100644 _probe_stuck.py delete mode 100644 _probe_wedge.py delete mode 100644 _probe_wedge2.py diff --git a/_probe_scan.py b/_probe_scan.py deleted file mode 100644 index 34af8e9..0000000 --- a/_probe_scan.py +++ /dev/null @@ -1,69 +0,0 @@ -"""_probe_scan.py — 精确定位响应帧头在 SM1 RAM 的偏移 + SM0 请求侧同步 dump。""" -import struct -import sys -import time - -import ecat_server as es -import pysoem - -r = es.api_open({"substr": "I226"}) -if not r.get("ok"): - 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 sm_reg(addr): - d = fprd(addr, 8) - if d is None: - return None - start, length = struct.unpack("> 3 & 1})", flush=True) - -# 同步看 SM0 侧(请求是否被读走) -st0 = sm_reg(0x0800) -print(f"SM0 st=0x{st0['st']:02X}(bit3={st0['st'] >> 3 & 1})", flush=True) - -es.api_close({}) -print("DONE", flush=True) diff --git a/_probe_stuck.py b/_probe_stuck.py deleted file mode 100644 index 1d25aa8..0000000 --- a/_probe_stuck.py +++ /dev/null @@ -1,77 +0,0 @@ -"""_probe_stuck.py — 验证"SM1 响应滞留 → 邮箱死锁"假说。 -板上现状: SM1 挂着一个未读走的响应(上一探针注入)。实验: - 1. SOEM SDO 读 → 预期失败(复现楔死现象) - 2. 手动读走 SM1 全长(清 bit3) → 模拟"主站把滞留响应读掉" - 3. SOEM SDO 再读 → 是否恢复? -若恢复 = 根治方向明确:邮箱死锁的物理本质是响应滞留,主站侧可自愈,从站侧可加超时复位。 -""" -import struct -import sys - -import ecat_server as es -import pysoem - -r = es.api_open({"substr": "I226"}) -if not r.get("ok"): - 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 sm1_state(tag): - d = fprd(0x0808, 8) - if d is None: - print(f"[{tag}] SM1 读失败", flush=True) - return None - start, length = struct.unpack("> 3 & 1} 读邮箱满)", flush=True) - return {"start": start, "len": length, "st": st} - - -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() -print("PREOP:", "OK" if m.state_check(pysoem.PREOP_STATE, 1_000_000) == pysoem.PREOP_STATE else "FAIL", flush=True) - -# --- 1. 现状: SM1 挂着滞留响应 --- -sm1_state("实验前") - -print("\n--- SOEM SDO #1(预期失败) ---", flush=True) -try: - d = bytes(s.sdo_read(0x1000, 0, 8)) - print(f" 成功?! = {d.hex().upper()}", flush=True) -except Exception as e: - print(f" 失败: {type(e).__name__}: {e}", flush=True) - -sm1_state("SDO#1 后") - -# --- 2. 读走 SM1 滞留响应(全长度读取,读到末字节才清 bit3) --- -sm1 = sm1_state("读滞留前") -if sm1 and (sm1["st"] & 0x08): - d = fprd(sm1["start"], sm1["len"]) - print(f" 读走 SM1 {sm1['len']}B: {d[:16].hex().upper()}...", flush=True) - sm1_state("读走后") - -# --- 3. SOEM SDO 再试 --- -print("\n--- SOEM SDO #2(读走滞留后) ---", flush=True) -try: - d = bytes(s.sdo_read(0x1000, 0, 8)) - print(f" 成功! = {d.hex().upper()}", flush=True) - print(">>> 假说成立:响应滞留是死锁根源,读走即恢复!", flush=True) -except Exception as e: - print(f" 仍失败: {type(e).__name__}: {e}", flush=True) - sm1_state("SDO#2 后") - -es.api_close({}) -print("DONE", flush=True) diff --git a/_probe_wedge.py b/_probe_wedge.py deleted file mode 100644 index 52f3895..0000000 --- a/_probe_wedge.py +++ /dev/null @@ -1,107 +0,0 @@ -"""_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) diff --git a/_probe_wedge2.py b/_probe_wedge2.py deleted file mode 100644 index 8d54a5d..0000000 --- a/_probe_wedge2.py +++ /dev/null @@ -1,93 +0,0 @@ -"""_probe_wedge2.py — 按真实邮箱协议探针: 检查写邮箱空 → 写满 256B → 查事件/响应。""" -import struct -import sys -import time - -import ecat_server as es -import pysoem - -r = es.api_open({"substr": "I226"}) -if not r.get("ok"): - 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 sm_reg(addr): - d = fprd(addr, 8) - if d is None: - return None - start, length = struct.unpack("> 3 & 1}写满 SM1.bit3={sm1['st'] >> 3 & 1}读满)", - flush=True) - return sm0, sm1, ev_v - - -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() -print("PREOP:", "OK" if m.state_check(pysoem.PREOP_STATE, 1_000_000) == pysoem.PREOP_STATE else "FAIL", flush=True) - -sm0, sm1, ev = show("稳态") - -# 合法 CoE SDO 读 0x1000:00(expedited), padded 到整个邮箱长度 256B -req_hdr = bytes([0x0A, 0x00, 0x00, 0x00, 0x00, 0x03, # len=10, addr=0, pri=0, type=CoE(cnt=0) - 0x10, 0x00, # CANOpen = 0x0010 (SDO req<<12 | 0x1000? 不对, 服务号 0x10?) —— 用标准 SOEM 格式 - 0x00, 0x10, # Command = 0x40(up req, exp) - 0x00, 0x10, # Index = 0x1000 - 0x00, 0x00]) # sub=0, +pad -# SOEM ecx_SDOread 的帧: CANOpen = (ECT_COES_SDOREQ<<12)=0x1000 | number; Command=0x40; Index; SubIndex -req = bytes([0x0A, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x10, 0x40, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00]) -mbx = (req + bytes(sm0["len"]))[:sm0["len"]] -print(f"\n写满 SM0 RAM {sm0['len']}B(合法 CoE 读 0x1000:00)...", flush=True) -try: - s._fpwr(sm0["start"], mbx) - print(" 写入 OK (WKC=1)", flush=True) -except Exception as e: - print(f" 写入失败: {e}", flush=True) - -sm0b, sm1b, ev2 = show("写入后立即") -time.sleep(0.2) -sm0c, sm1c, ev3 = show("写入后 200ms") - -if sm1c and (sm1c["st"] & 0x08): - d = fprd(sm1c["start"], 32) - print(f"*** SSC 响应了! SM1 RAM = {d.hex().upper() if d else 'ERR'}", flush=True) - print(">>> ESC+SSC 完全正常,楔死另有其因", flush=True) -else: - print("\nSSC 无响应。检查 0x220 bit0 事件是否置位过...", flush=True) - if sm0c and (sm0c["st"] & 0x08): - print(f"SM0.bit3(写满)={sm0c['st'] >> 3 & 1} —— 写满标志挂着(主站侧协议已触发)", flush=True) - # 模拟 SSC 解锁: 读 SM0 首 word + 末 word - fprd(sm0c["start"], 2) - fprd(sm0c["start"] + sm0c["len"] - 2, 2) - sm0d, sm1d, ev4 = show("模拟 SSC 解锁后") - if not (sm0d["st"] & 0x08): - print("解锁有效(SM0.bit3 清零) —— SM 状态机活着", flush=True) - else: - print(f"SM0.bit3(写满)={sm0c['st'] >> 3 & 1 if sm0c else '?'} —— 写满标志都没置!写到末字节也没触发 mailbox full", flush=True) - -es.api_close({}) -print("DONE", flush=True)