126 lines
4.5 KiB
Python
126 lines
4.5 KiB
Python
#!/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()
|