Files
DCU_ECAT_Master/ecat_flash_eeprom.py
T

126 lines
4.5 KiB
Python
Raw Normal View History

2026-09-03 01:56:29 +08:00
#!/usr/bin/env python3
"""Flash the SII EEPROM image (generated by gen_eeprom.py) into the LAN9252
over the EtherCAT cable — no external programmer, no HBI needed.
Usage:
ecat_flash_eeprom.py [adapter-substr] (default: I226)
Steps: load gen_eeprom.py in-process (its asserts validate the image),
probe the EEPROM byte order with a scratch word, write every non-0xFFFF
word with immediate read-back verify, then restore the scratch word.
Afterwards POWER CYCLE the board so the ESC reloads its config.
"""
import contextlib
import importlib.util
import io
import struct
import sys
import pysoem
GEN = r"G:\Robot_v2\DCU\DCU_APP\GD32E508_DCU_APP\comm\ssc\port\gen_eeprom.py"
SCRATCH_WORD = 0x0200 # inside the 0xFF filler, far beyond the categories
def load_image():
spec = importlib.util.spec_from_file_location("gen_eeprom", GEN)
mod = importlib.util.module_from_spec(spec)
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
spec.loader.exec_module(mod) # runs its own self-check asserts
img = bytes(mod.eeprom)
print(f"镜像加载成功: {len(img)} 字节, checksum=0x{img[14]:02X}, "
f"VendorID=0x{struct.unpack_from('<I', img, 16)[0]:08X}, "
f"ProductCode=0x{struct.unpack_from('<I', img, 20)[0]:08X}")
return img
def find_adapter(substr):
for a in pysoem.find_adapters():
desc = a.desc.decode() if isinstance(a.desc, bytes) else a.desc
if substr.lower() in desc.lower():
return a
return None
def open_slave(substr):
adapter = find_adapter(substr)
if adapter is None:
raise SystemExit(f"没有找到描述包含 {substr!r} 的适配器")
master = pysoem.Master()
master.open(adapter.name)
try:
n = master.config_init()
except Exception as e:
raise SystemExit(f"总线上没有发现从站({type(e).__name__}: {e})")
if not n:
raise SystemExit("总线上没有发现从站:请检查板子供电和网线")
print(f"从站应答正常({n} 个)")
return master.slaves[0]
def probe_word_layout(slave):
"""Write 0x1234 at the scratch word and find where it lands in the
4-byte read-back, so verification slices the right two bytes."""
test = b"\x34\x12"
slave.eeprom_write(SCRATCH_WORD, test)
rb = bytes(slave.eeprom_read(SCRATCH_WORD))
off = rb.find(test)
if off < 0:
raise SystemExit(f"写入探针失败: 读回 {rb.hex().upper()}")
slave.eeprom_write(SCRATCH_WORD, b"\xff\xff") # restore erase state
rb2 = bytes(slave.eeprom_read(SCRATCH_WORD))
if rb2[off:off + 2] != b"\xff\xff":
raise SystemExit(f"探针字恢复失败: {rb2.hex().upper()}")
print(f"字节序/读回偏移探测: 写入 {test.hex()} 落在读回偏移 {off},恢复 OK")
return off
def main():
substr = sys.argv[1] if len(sys.argv) > 1 else "I226"
img = load_image()
slave = open_slave(substr)
verify_off = probe_word_layout(slave)
total = len(img) // 2
n_written = n_fail = 0
fails = []
for word in range(total):
val = struct.unpack_from("<H", img, word * 2)[0]
if val == 0xFFFF: # erase state, nothing to write
continue
data = struct.pack("<H", val)
try:
slave.eeprom_write(word, data)
except Exception as e:
fails.append((word, data.hex(), f"WRITE EXC {type(e).__name__}: {e}"))
n_fail += 1
continue
rb = bytes(slave.eeprom_read(word))[verify_off:verify_off + 2]
n_written += 1
if rb != data:
fails.append((word, data.hex(), rb.hex()))
n_fail += 1
if n_written % 32 == 0:
print(f" ... 已写 {n_written} 字")
print(f"\n刷写完成: 成功 {n_written} 字, 失败 {n_fail} 字 "
f"(跳过 0xFF 填充 {total - n_written - n_fail} 字)")
if fails:
print("失败明细 (word, 期望, 实际):")
for w, exp, act in fails[:20]:
print(f" word 0x{w:04X}: {exp.upper()} != {act.upper()}")
sys.exit(3)
# summary of key words
for name, w in (("PDI Control", 0), ("Checksum", 7), ("VendorID lo", 8),
("ProductCode lo", 0x0A), ("Size", 0x3E), ("Version", 0x3F)):
val = struct.unpack_from("<H", img, w * 2)[0]
print(f" word 0x{w:02X} {name} = 0x{val:04X}")
print("\n>>> 请给板子断电重新上电(ESC 重新加载 EEPROM 配置),")
print(">>> 然后运行 run_scan.bat I226 验证身份字。")
if __name__ == "__main__":
main()