Files
DCU_ECAT_Master/ecat_flash_eeprom.py
T
cbb d594b8fd94 init: DCU LAN9252 EtherCAT 主站工具集
现有工具纳入版本管理:
- ecat_tool.py: 一体化命令行主站(status/flash/eeprom/sdo/send)
- ecat_server.py + ecat.html/css: 网页/桌面控制台(端口 8090)
- ecat_scan.py / ecat_esc_regs.py / ecat_sniff.py: bring-up 扫描、
  网络侧 ESC 寄存器诊断、裸帧抓包对照
- ecat_flash_eeprom.py: 经网线烧写 SII 镜像 + 逐字校验
- *.bat / *.vbs 启动入口

新增 .gitignore(排除 venv/ 与 __pycache__)、.gitattributes(不改写换行符)、
requirements.txt(锁定 pysoem 1.1.13 等依赖)、README.md。
2026-09-03 01:56:29 +08:00

126 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()